Conversation
MikeMcQuaid
left a comment
There was a problem hiding this comment.
Nice work @ooye-sanket! Looks good so far. Please add a very basic integration test and args check for which-entry. Also, please inline functions that are only used once as there's a fair bit of indirection right now.
| rescue JSON::ParserError | ||
| [] |
There was a problem hiding this comment.
Probably worth a opoo warning of the exception in this case?
|
Thanks @MikeMcQuaid! I've pushed the changes:
Let me know if the level of inlining looks right or if you'd like it structured differently! |
MikeMcQuaid
left a comment
There was a problem hiding this comment.
Thanks! Please ensure CI syntax job passing before asking for re-review.
|
|
||
| sig { override.void } | ||
| def run | ||
| db_path = args.output_db&.then { |path| Pathname(path) } |
There was a problem hiding this comment.
Not sure I understand the .then usage here: can you explain?
| formula = Formulary.factory(name) | ||
| line = db_line(formula) | ||
| if db_path | ||
| existing = db_path.exist? ? db_path.readlines(chomp: true).reject(&:empty?) : [] |
There was a problem hiding this comment.
| existing = db_path.exist? ? db_path.readlines(chomp: true).reject(&:empty?) : [] | |
| existing = db_path.readlines(chomp: true).reject(&:blank?) if db_path.exist? | |
| existing ||= [] |
maybe
| line = db_line(formula) | ||
| if db_path | ||
| existing = db_path.exist? ? db_path.readlines(chomp: true).reject(&:empty?) : [] | ||
| lines = existing.reject { |l| l.start_with?("#{formula.full_name}(") } |
There was a problem hiding this comment.
Could maybe drop existing as a variable and just chain this?
| if db_path | ||
| existing = db_path.exist? ? db_path.readlines(chomp: true).reject(&:empty?) : [] | ||
| lines = existing.reject { |l| l.start_with?("#{formula.full_name}(") } | ||
| lines << line if line |
| else | ||
| puts line if line |
There was a problem hiding this comment.
| else | |
| puts line if line | |
| elsif line | |
| puts line |
| lines = db_path.readlines(chomp: true).reject { |l| l.start_with?("#{name}(") } | ||
| db_path.write("#{lines.sort.join("\n")}\n") |
There was a problem hiding this comment.
This logic is repeated, maybe worth turning into a function or restructuring
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
When marking a formula as not installed on request, also set installed_as_dependency to true, to ensure brew bundle dump correctly excludes it. Previously, brew tab --no-installed-on-request had no effect on bundle dump because the dump logic checks both Fixes Homebrew#21751
9e3e0a6 to
00d9e78
Compare
MikeMcQuaid
left a comment
There was a problem hiding this comment.
Thanks, looking good, a few small things!
|
|
||
| sig { override.void } | ||
| def run | ||
| db_path = Pathname(T.unsafe(args).output_db) if T.unsafe(args).output_db |
There was a problem hiding this comment.
This shouldn't be necessary, you may need to run brew typecheck --update to avoid the need for T.unsafe.
| sig { params(db_path: Pathname, name: String, line: T.nilable(String)).void } | ||
| def write_db(db_path, name, line) | ||
| lines = db_path.readlines(chomp: true).compact_blank if db_path.exist? | ||
| lines = (lines || []).reject { |l| l.start_with?("#{name}(") } |
There was a problem hiding this comment.
| lines = (lines || []).reject { |l| l.start_with?("#{name}(") } | |
| lines = Array(lines).reject { |l| l.start_with?("#{name}(") } |
| def executables_from_manifest(formula) | ||
| return [] unless formula.bottled? | ||
|
|
||
| manifest_path = HOMEBREW_CACHE.glob("#{formula.name}_bottle_manifest--*").first | ||
| if manifest_path.blank? | ||
| bottle = formula.bottle | ||
| return [] unless bottle | ||
|
|
||
| bottle.fetch | ||
| manifest_path = HOMEBREW_CACHE.glob("#{formula.name}_bottle_manifest--*").first | ||
| end | ||
| return [] if manifest_path.blank? | ||
|
|
||
| manifest = JSON.parse(manifest_path.read) | ||
| exec_files = manifest.dig("manifests", 0, "annotations", "sh.brew.path_exec_files") | ||
| return [] if exec_files.blank? | ||
|
|
||
| exec_files.split.map { |f| File.basename(f) }.sort | ||
| rescue JSON::ParserError => e | ||
| opoo "Failed to parse bottle manifest for #{formula.name}: #{e.message}" | ||
| [] | ||
| end |
There was a problem hiding this comment.
can this share any logic or constants with the relevant bottle.rb or resource.rb code?
There was a problem hiding this comment.
Sorry for the review delay here. I agree with Mike's feedback, and also have one other thought.
More generally, I'm not sure whether this actually needs to be a brew command at all. I will leave a comment on the other PR with more details
Edit: oh, one more thing, we should remove the existing command-not-found-db-update.yml workflow
| return if formula.disabled? || formula.deprecated? | ||
|
|
||
| exes = executables_from_manifest(formula) | ||
| "#{formula.full_name}(#{formula.pkg_version}):#{exes.join(" ")}" |
There was a problem hiding this comment.
The reason we added pkg_version before was to track formula file changes over time (we only checked the formulae with changed versions for updated exes).
Now that we're going to run this on every merge where we can detect which formulae are updated, I don't think we need the pkg_version anymore
|
hey @Rylan12 The reason for keeping it as a brew command is that it already handles formula loading, bottle manifest caching, disabled/deprecated checks, and DB file management in a tested, typed Ruby context. Doing all that reliably in bash would be fragile. But happy to discuss if there's a simpler scope you have in mind. |
|
Let's keep it as Ruby for now and we can see how it's looking when the other PR is ready. |
|
I don't mind keeping it in Ruby for now, so this is not a blocker.
I think doing it in bash would actually let us bypass these complexities altogether, resulting in a simpler system overall. There would be no need to load formulae at all, and the deprecated/disabled info is available via the same bottle manifest that we already need to use. Fair enough on the DB management, although I think a reasonably well-crafted I haven't attempted to write it out, so I could be wrong |
Adds
brew which-entry <formula>a developer command that generatesa single
executables.txtentry for a formula by readingsh.brew.path_exec_filesfrom its bottle manifest.This is intended to replace the batch
brew which-updatejob with aper-formula update triggered by homebrew/core CI on each merge.
What it does
sh.brew.path_exec_files--output-db=<file>: upserts the formula's line in the DB fileand removes it if the formula is disabled, deprecated, or deleted
--output-db=: prints the DB line to stdoutRelationship to other PRs
The companion homebrew/core PR (Homebrew/homebrew-core#272976) slims
the existing workflow down to just call
brew which-entryper formuladetected by
brew test-bot --only-formulae-detect, replacing thecurrent daily batch job entirely.
@MikeMcQuaid please have a look!
brew lgtm(style, typechecking and tests) with your changes locally?