Conversation
Co-Authored-By: Virgil <[email protected]>
Co-Authored-By: Virgil <[email protected]>
Co-Authored-By: Virgil <[email protected]>
Reads repos.yaml dependency graph via TopologicalOrder(), bumps patch version bottom-up, runs GOWORK=off go get -u ./... and go mod tidy per repo, commits go.mod/go.sum, creates annotated tags, and pushes. Supports --dry-run to preview the plan and --force to skip confirmation. Co-Authored-By: Virgil <[email protected]>
…h framework equivalents Replace os.ReadFile with coreio.Local.Read for consistent filesystem abstraction. Replace fmt.Errorf/errors.New with log.E() from go-log for structured error context. Co-Authored-By: Virgil <[email protected]>
Co-Authored-By: Virgil <[email protected]>
281 translation keys covering dev (health, work, commit, push, pull, tag, impact, issues, reviews, ci, apply, workflow, vm), deploy, docs, git, and setup commands. Co-Authored-By: Virgil <[email protected]>
Locales auto-load when cmd/dev is imported via init(). Co-Authored-By: Virgil <[email protected]>
Clean init(): cli.RegisterCommands(AddDevCommands, locales.FS) No more i18n.AddLoader — CLI handles locale loading automatically. Co-Authored-By: Virgil <[email protected]>
Package-level var declarations run at import time, before i18n is initialised. Move Short/Long assignment to AddCommands functions which run after Core startup. Co-Authored-By: Virgil <[email protected]>
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe changes integrate internationalization support throughout the CLI by adding locale files and refactoring command initialization. Error handling is standardised to use a logging wrapper (log.E) instead of direct error constructors. A new tag subcommand is introduced under the dev command to manage repository versioning and git operations. Dependencies are updated to newer versions. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as CLI Tag Command
participant Registry as Tag Registry
participant GitRepo as Git Repository
participant GoModules as Go Modules
User->>CLI: Execute tag command (--registry, --dry-run, --force)
CLI->>Registry: Load tag registry
Registry-->>CLI: Registry with dependency graph
CLI->>CLI: Compute dependency-ordered plan
CLI-->>User: Display tagging plan per repo
alt Dry-run mode
CLI-->>User: Exit (plan only)
else Normal mode
alt Not forced
CLI->>User: Prompt for confirmation
User-->>CLI: Confirm
end
loop For each repository in dependency order
CLI->>GitRepo: Check for go.mod
alt go.mod exists
CLI->>GoModules: Run go get -u ./...
GoModules-->>CLI: Dependencies updated
CLI->>GoModules: Run go mod tidy
GoModules-->>CLI: Modules tidied
CLI->>GitRepo: Commit go.mod/go.sum
GitRepo-->>CLI: Committed
end
CLI->>GitRepo: Create annotated git tag (next version)
GitRepo-->>CLI: Tag created
CLI->>GitRepo: Push commits and tags (--follow-tags)
GitRepo-->>CLI: Pushed to remote
end
CLI-->>User: Tagging complete
end
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📝 Coding Plan
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
deploy/coolify/client.go (1)
84-93:⚠️ Potential issue | 🟠 MajorAvoid logging raw API output in error messages.
Line 92 embeds the full API response in the error message. This could expose sensitive data (credentials, PII, server configurations) in logs if the Coolify API returns such information in a malformed response.
Consider truncating the output or logging it at a debug level separately rather than embedding it in the error.
🔒 Proposed fix to limit output exposure
- return nil, log.E("coolify", fmt.Sprintf("failed to parse response (output: %s)", output), err) + // Truncate output to avoid leaking sensitive data in logs + preview := output + if len(preview) > 100 { + preview = preview[:100] + "..." + } + return nil, log.E("coolify", fmt.Sprintf("failed to parse response (output: %s)", preview), err)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@deploy/coolify/client.go` around lines 84 - 93, The error return in the JSON parsing block currently embeds the full API response via the output variable in log.E("coolify", ...), which can leak sensitive data; change the failure path in the json.Unmarshal error branch (the block that also tries arrResult) to avoid including raw output: construct a truncated or redacted representation (e.g., first N characters + "... [truncated]" or sanitize sensitive patterns) and use that in the error message, and if you still want full output for debugging, log the full output at debug level (not error) using the existing logger; update the log.E call to reference the truncated/redacted string instead of output.cmd/setup/cmd_registry.go (1)
215-215:⚠️ Potential issue | 🟡 MinorIncorrect i18n.T template argument format.
The call
i18n.T("i18n.count.failed", failed)passes an integer directly, but the template expects a map with aCountkey:"{{.Count}} failed". This will likely result in incorrect string interpolation.🐛 Proposed fix
- fmt.Printf(", %s", errorStyle.Render(i18n.T("i18n.count.failed", failed))) + fmt.Printf(", %s", errorStyle.Render(i18n.T("i18n.count.failed", map[string]any{"Count": failed})))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/setup/cmd_registry.go` at line 215, The i18n call currently passes the integer directly to i18n.T which expects a map with a Count key; update the call in cmd_registry.go where i18n.T("i18n.count.failed", failed) is used (inside the fmt.Printf/errorStyle.Render expression) to pass a map with the Count field (e.g., map[string]interface{}{"Count": failed}) so the template "{{.Count}} failed" can interpolate correctly.cmd/setup/cmd_setup.go (1)
35-40:⚠️ Potential issue | 🟠 MajorMissing i18n initialisation for setup command.
The
ShortandLongfields were removed fromsetupCmd, but unlike other commands (e.g.,setDocsI18nincmd/docs/cmd_docs.go,setDeployI18nincmd/deploy/cmd_deploy.go), there is no correspondingsetSetupI18n()function defined or invoked. This will leave the setup command with empty descriptions in help output.Proposed fix
Add a
setSetupI18nfunction and call it inAddSetupCommand:+func setSetupI18n() { + setupCmd.Short = i18n.T("cmd.setup.short") + setupCmd.Long = i18n.T("cmd.setup.long") +} + // AddSetupCommand adds the 'setup' command to the given parent command. func AddSetupCommand(root *cli.Command) { + setSetupI18n() initSetupFlags() addGitHubCommand(setupCmd) root.AddCommand(setupCmd) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/setup/cmd_setup.go` around lines 35 - 40, The setup command lost its localized descriptions; add a setSetupI18n function that sets setupCmd.Short and setupCmd.Long (mirroring setDocsI18n/setDeployI18n patterns) and call setSetupI18n() inside AddSetupCommand before the command is registered; update the setSetupI18n implementation to use the same i18n/string resources and keys as other commands so help text appears correctly for setupCmd.
🧹 Nitpick comments (1)
cmd/dev/cmd_tag.go (1)
101-129: A few runtime messages still bypass i18n.The command metadata is localised, but the plan heading, dry-run notice, confirm prompt, and step labels in this block are still hard-coded English. Routing them through
i18n.T(...)would keepdev tagaligned with the rest of the locale work in this PR.Also applies to: 145-179
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/dev/cmd_tag.go` around lines 101 - 129, Wrap all user-visible literal strings in this block (and the later block around lines 145-179) through the i18n system: replace the title passed to cli.TitleStyle.Render ("Tag plan (dependency order)"), the arrow/step labels and the dry-run message ("Dry run — no changes made."), and the confirmation prompt ("Tag and push %d repos?") with i18n.T(...) calls; for formatted strings use fmt.Sprintf(i18n.T("..."), len(plans)) or equivalent. Update usages in the rendering loop (cli.Print/cli.Sprintf, repoNameStyle.Render, dimStyle.Render, aheadStyle.Render), the dryRun branch (cli.Text), and the confirmation branch (cli.Confirm) so all displayed text is produced via i18n.T.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/dev/cmd_tag.go`:
- Around line 144-177: The loop currently swallows per-repo failures and still
returns nil at the end; change the control flow so any error from goGetUpdate,
goModTidy, commitGoMod, createTag or pushWithTags causes the command to return a
non-nil error immediately instead of continuing the batch. Specifically, after
any failing call to goGetUpdate, goModTidy or commitGoMod, stop processing and
return the error; if the failure occurs after createTag (i.e., createTag
succeeded but a later step fails), delete the locally created tag for p.Next in
that repository before returning the error to avoid leaving a partial state.
Ensure the return uses the original error from the failing function so
callers/CI see failure.
- Around line 142-163: Before performing any modifications (the block guarded by
p.IsGoMod that calls goGetUpdate, goModTidy, and commitGoMod), add a preflight
"clean tree" check that runs "git status --porcelain" in p.Path and aborts with
an error if the output is non-empty; do the same for the other code path that
also calls commitGoMod (the section mentioned around lines 245–281).
Specifically, detect the repository state for the repo represented by p.Path,
and if any staged or uncommitted changes exist, print a clear error and
skip/bail instead of proceeding to goGetUpdate/goModTidy/commitGoMod so
commitGoMod never picks up unrelated staged changes. Ensure messages reference
the package (p.Path or p.Next) so the user knows which repo failed the
preflight.
- Around line 74-77: The code defaults to "v0.0.0" whenever latestTag(ctx,
repo.Path) returns an error or empty, but latestTag uses local refs only and can
return errors for stale/fresh clones; first fetch remote tags and preflight for
any tags before falling back. Update the call site (where current is set from
latestTag) to run a git fetch --tags (or equivalent fetchTags(ctx, repo.Path))
before calling latestTag, and modify latestTag to differentiate "no tags found"
(use git tag --list or git describe --always) from other git errors so you only
set current="v0.0.0" when there truly are no tags; reference the latestTag
function and the variable current/Next calculation to locate and fix the logic.
- Around line 247-265: The git add currently always includes both "go.mod" and
"go.sum" which fails when go.sum doesn't exist; change the add step (where
addCmd is constructed) to build the args list dynamically and only include
"go.mod" and "go.sum" when they actually exist or are tracked (use the existing
checks modChanged and untrackedSum or os.Stat/git ls-files) and then run
exec.CommandContext(ctx, "git", "add", <filtered args>) so addCmd only tries to
stage files that are present/tracked in repoPath.
In `@cmd/setup/cmd_registry.go`:
- Line 231: The i18n.T call is passing a raw string but the template expects a
map with a Name key; update the call at the log.E invocation so
i18n.T("i18n.fail.run", ...) receives a map with Name set to "build" (i.e.,
replace the second argument with a map[string]string or equivalent
{"Name":"build"}) so the template {{.Name}} can be populated; the change should
be made where log.E("setup.registry", i18n.T(...), err) is returned in
cmd_registry.go.
In `@locales/en.json`:
- Line 415: The localization key "conflicting_flags" currently reads "Cannot use
--check with modification flags" but the actual validation in cmd_github.go
compares ghRepo != "" && ghAll (conflict between --repo and --all); update the
value for the "conflicting_flags" key in locales/en.json to accurately describe
that conflict (e.g., "Cannot use --repo with --all" or similar) so the message
matches the check performed in cmd_github.go.
---
Outside diff comments:
In `@cmd/setup/cmd_registry.go`:
- Line 215: The i18n call currently passes the integer directly to i18n.T which
expects a map with a Count key; update the call in cmd_registry.go where
i18n.T("i18n.count.failed", failed) is used (inside the
fmt.Printf/errorStyle.Render expression) to pass a map with the Count field
(e.g., map[string]interface{}{"Count": failed}) so the template "{{.Count}}
failed" can interpolate correctly.
In `@cmd/setup/cmd_setup.go`:
- Around line 35-40: The setup command lost its localized descriptions; add a
setSetupI18n function that sets setupCmd.Short and setupCmd.Long (mirroring
setDocsI18n/setDeployI18n patterns) and call setSetupI18n() inside
AddSetupCommand before the command is registered; update the setSetupI18n
implementation to use the same i18n/string resources and keys as other commands
so help text appears correctly for setupCmd.
In `@deploy/coolify/client.go`:
- Around line 84-93: The error return in the JSON parsing block currently embeds
the full API response via the output variable in log.E("coolify", ...), which
can leak sensitive data; change the failure path in the json.Unmarshal error
branch (the block that also tries arrResult) to avoid including raw output:
construct a truncated or redacted representation (e.g., first N characters +
"... [truncated]" or sanitize sensitive patterns) and use that in the error
message, and if you still want full output for debugging, log the full output at
debug level (not error) using the existing logger; update the log.E call to
reference the truncated/redacted string instead of output.
---
Nitpick comments:
In `@cmd/dev/cmd_tag.go`:
- Around line 101-129: Wrap all user-visible literal strings in this block (and
the later block around lines 145-179) through the i18n system: replace the title
passed to cli.TitleStyle.Render ("Tag plan (dependency order)"), the arrow/step
labels and the dry-run message ("Dry run — no changes made."), and the
confirmation prompt ("Tag and push %d repos?") with i18n.T(...) calls; for
formatted strings use fmt.Sprintf(i18n.T("..."), len(plans)) or equivalent.
Update usages in the rendering loop (cli.Print/cli.Sprintf,
repoNameStyle.Render, dimStyle.Render, aheadStyle.Render), the dryRun branch
(cli.Text), and the confirmation branch (cli.Confirm) so all displayed text is
produced via i18n.T.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9a670936-3a60-4383-933e-b24cf1201066
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (26)
cmd/deploy/cmd_commands.gocmd/deploy/cmd_deploy.gocmd/dev/cmd_dev.gocmd/dev/cmd_impact.gocmd/dev/cmd_tag.gocmd/dev/cmd_vm.gocmd/dev/forge_client.gocmd/docs/cmd_commands.gocmd/docs/cmd_docs.gocmd/docs/cmd_list.gocmd/docs/cmd_sync.gocmd/setup/cmd_bootstrap.gocmd/setup/cmd_commands.gocmd/setup/cmd_github.gocmd/setup/cmd_registry.gocmd/setup/cmd_repo.gocmd/setup/cmd_setup.gocmd/setup/github_config.gocmd/setup/github_protection.gocmd/setup/github_security.gocmd/setup/github_webhooks.godeploy/coolify/client.gogo.modlocales/embed.golocales/en.jsonsnapshot/snapshot.go
| current, err := latestTag(ctx, repo.Path) | ||
| if err != nil || current == "" { | ||
| current = "v0.0.0" | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
According to the official Git documentation, how does git describe --tags --abbrev=0 --match "v*" behave, and how can tooling distinguish "no matching tags" from other command failures?
💡 Result:
Behavior of git describe --tags --abbrev=0 --match "v*" (per official docs)
git describe“finds the most recent tag that is reachable from a commit” (default:HEAD). If the commit is exactly tagged, it prints just the tag; otherwise it normally appends-<n>-g<abbrev>suffixes. With--abbrev=0, it “suppresses long format, only showing the closest tag.” [1]--tagsmakes it consider any tag inrefs/tags/(including lightweight tags). [1]--match "v*"restricts candidates to tags whose names match the given glob pattern (matched without therefs/tags/prefix). [1]- If multiple tags are possible, the search strategy prefers: exact-match first; otherwise it walks history to find tagged ancestors; it chooses the tag with the fewest commits between it and the target commit, and prefers annotated tags over lightweight tags (and newer tag dates over older) when applicable. [1]
Distinguishing “no matching tags” vs other failures
- Git’s fatal-error path (
die()) exits with status 128. [2] So, exit status alone cannot reliably distinguish “no matching tags” from other fatal failures (both are typically 128).
Tooling patterns that do distinguish it:
-
Avoid the fatal case by using a defined fallback
Add--always, which “show[s a] uniquely abbreviated commit object as fallback.” [1]
Then tooling can interpret:- output matching your tag naming scheme (e.g., starts with
v) ⇒ found a matching tag - otherwise ⇒ “no matching tags” (but still a successful command invocation)
- output matching your tag naming scheme (e.g., starts with
-
Preflight-check whether any matching tags exist (and are reachable), then decide whether to call
describe
Usegit tag --listwith a pattern (shell wildcard) to test for candidates, and optionally--merged <commit>to restrict to tags reachable fromHEAD(or another commit). [3]
If that precheck returns no tags, treat it as “no matching tags” without ever invokinggit describe; otherwise rungit describe ...and treat nonzero exit as a real failure.
Sources:
[1] git-describe documentation (options, examples, and search strategy) https://git-scm.com/docs/git-describe/2.33.0.html
[2] api-error-handling (fatal die() exits 128) https://git-scm.com/docs/api-error-handling/2.32.0
[3] git-tag documentation (--list with patterns; --merged) https://git-scm.com/docs/git-tag
🏁 Script executed:
# First, check if the file exists and get its overall structure
cd cmd/dev && wc -l cmd_tag.goRepository: dAppCore/go-devops
Length of output: 76
🏁 Script executed:
# Read the section around lines 74-77
sed -n '70,80p' cmd/dev/cmd_tag.goRepository: dAppCore/go-devops
Length of output: 279
🏁 Script executed:
# Read the latestTag function implementation
sed -n '1,300p' cmd/dev/cmd_tag.go | grep -n "latestTag" -A 20Repository: dAppCore/go-devops
Length of output: 1473
Fetch tags before determining version bumps to avoid stale or incorrect tag state.
latestTag (line 196) uses git describe on local refs only, without fetching. Lines 75–76 collapse all errors and empty results into v0.0.0, meaning a stale clone, fresh clone, or locally-created-but-unpushed tags will yield wrong Current/Next pairs. Per Git documentation, exit codes cannot reliably distinguish "no matching tags" from other failures—both exit 128. Fetch tags before planning, then only fall back when Git explicitly finds no matching tags (using git tag --list to preflight-check, or git describe --always to avoid fatal failures).
Also applies to: lines 195–203
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cmd/dev/cmd_tag.go` around lines 74 - 77, The code defaults to "v0.0.0"
whenever latestTag(ctx, repo.Path) returns an error or empty, but latestTag uses
local refs only and can return errors for stale/fresh clones; first fetch remote
tags and preflight for any tags before falling back. Update the call site (where
current is set from latestTag) to run a git fetch --tags (or equivalent
fetchTags(ctx, repo.Path)) before calling latestTag, and modify latestTag to
differentiate "no tags found" (use git tag --list or git describe --always) from
other git errors so you only set current="v0.0.0" when there truly are no tags;
reference the latestTag function and the variable current/Next calculation to
locate and fix the logic.
| if p.IsGoMod { | ||
| // Step 1: GOWORK=off go get -u ./... | ||
| if err := goGetUpdate(ctx, p.Path); err != nil { | ||
| cli.Print(" %s go get -u: %s\n", errorStyle.Render("x"), err) | ||
| failed++ | ||
| continue | ||
| } | ||
|
|
||
| // Step 2: GOWORK=off go mod tidy | ||
| if err := goModTidy(ctx, p.Path); err != nil { | ||
| cli.Print(" %s go mod tidy: %s\n", errorStyle.Render("x"), err) | ||
| failed++ | ||
| continue | ||
| } | ||
|
|
||
| // Step 3: Commit go.mod/go.sum if changed | ||
| if err := commitGoMod(ctx, p.Path, p.Next); err != nil { | ||
| cli.Print(" %s commit: %s\n", errorStyle.Render("x"), err) | ||
| failed++ | ||
| continue | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
According to the official Git documentation, does git commit -m ... include every currently staged file, even if only some paths were newly added in the same script?
💡 Result:
Yes. The official git commit documentation says that, when run without any pathnames, git commit “create[s] a new commit containing the current contents of the index” (the staging area). That means git commit -m "..." records everything that is currently staged, regardless of whether some paths were newly added earlier in the same script. [1]
By contrast, if you run git commit with path arguments, it records only those paths’ current working-tree contents and can ignore other staged changes. [1]
[1] Git official docs: git-commit (git-scm.com)
🏁 Script executed:
# First, let's find and examine the cmd/dev/cmd_tag.go file
find . -name "cmd_tag.go" -type fRepository: dAppCore/go-devops
Length of output: 82
🏁 Script executed:
# Let's examine the full file to understand context
wc -l ./cmd/dev/cmd_tag.goRepository: dAppCore/go-devops
Length of output: 86
🏁 Script executed:
# Read the entire file
cat -n ./cmd/dev/cmd_tag.goRepository: dAppCore/go-devops
Length of output: 10604
Add a clean-tree preflight check before modifying repositories.
The commitGoMod function at line 277 runs git commit without path arguments, which commits everything currently staged. If the working tree has pre-existing staged files before the script starts, they will be included in the auto-generated commit, making those changes indistinguishable from the go.mod/go.sum updates. Add a check before line 142 to verify the repository is clean—i.e., git status --porcelain is empty—and bail out if not.
Also applies to: lines 245–281
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cmd/dev/cmd_tag.go` around lines 142 - 163, Before performing any
modifications (the block guarded by p.IsGoMod that calls goGetUpdate, goModTidy,
and commitGoMod), add a preflight "clean tree" check that runs "git status
--porcelain" in p.Path and aborts with an error if the output is non-empty; do
the same for the other code path that also calls commitGoMod (the section
mentioned around lines 245–281). Specifically, detect the repository state for
the repo represented by p.Path, and if any staged or uncommitted changes exist,
print a clear error and skip/bail instead of proceeding to
goGetUpdate/goModTidy/commitGoMod so commitGoMod never picks up unrelated staged
changes. Ensure messages reference the package (p.Path or p.Next) so the user
knows which repo failed the preflight.
| if err := goGetUpdate(ctx, p.Path); err != nil { | ||
| cli.Print(" %s go get -u: %s\n", errorStyle.Render("x"), err) | ||
| failed++ | ||
| continue | ||
| } | ||
|
|
||
| // Step 2: GOWORK=off go mod tidy | ||
| if err := goModTidy(ctx, p.Path); err != nil { | ||
| cli.Print(" %s go mod tidy: %s\n", errorStyle.Render("x"), err) | ||
| failed++ | ||
| continue | ||
| } | ||
|
|
||
| // Step 3: Commit go.mod/go.sum if changed | ||
| if err := commitGoMod(ctx, p.Path, p.Next); err != nil { | ||
| cli.Print(" %s commit: %s\n", errorStyle.Render("x"), err) | ||
| failed++ | ||
| continue | ||
| } | ||
| } | ||
|
|
||
| // Step 4: Create annotated tag | ||
| if err := createTag(ctx, p.Path, p.Next); err != nil { | ||
| cli.Print(" %s tag: %s\n", errorStyle.Render("x"), err) | ||
| failed++ | ||
| continue | ||
| } | ||
|
|
||
| // Step 5: Push commits and tags | ||
| if err := pushWithTags(ctx, p.Path); err != nil { | ||
| cli.Print(" %s push: %s\n", errorStyle.Render("x"), err) | ||
| failed++ | ||
| continue | ||
| } |
There was a problem hiding this comment.
Do not report success after a partial batch failure.
This loop carries on after every repo error, and Line 191 still returns nil. In a dependency-ordered batch that can leave downstream repos working from an incomplete state while CI/scripts see success, so the command should return a non-nil error once any repo fails; if the failure happens after Line 166, clean up the just-created local tag before exiting.
Also applies to: 183-191
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cmd/dev/cmd_tag.go` around lines 144 - 177, The loop currently swallows
per-repo failures and still returns nil at the end; change the control flow so
any error from goGetUpdate, goModTidy, commitGoMod, createTag or pushWithTags
causes the command to return a non-nil error immediately instead of continuing
the batch. Specifically, after any failing call to goGetUpdate, goModTidy or
commitGoMod, stop processing and return the error; if the failure occurs after
createTag (i.e., createTag succeeded but a later step fails), delete the locally
created tag for p.Next in that repository before returning the error to avoid
leaving a partial state. Ensure the return uses the original error from the
failing function so callers/CI see failure.
| diffCmd := exec.CommandContext(ctx, "git", "diff", "--quiet", "go.mod", "go.sum") | ||
| diffCmd.Dir = repoPath | ||
| modChanged := diffCmd.Run() != nil | ||
|
|
||
| // Also check for untracked go.sum | ||
| lsCmd := exec.CommandContext(ctx, "git", "ls-files", "--others", "--exclude-standard", "go.sum") | ||
| lsCmd.Dir = repoPath | ||
| lsOut, _ := lsCmd.Output() | ||
| untrackedSum := strings.TrimSpace(string(lsOut)) != "" | ||
|
|
||
| if !modChanged && !untrackedSum { | ||
| return nil // No changes | ||
| } | ||
|
|
||
| // Stage go.mod and go.sum | ||
| addCmd := exec.CommandContext(ctx, "git", "add", "go.mod", "go.sum") | ||
| addCmd.Dir = repoPath | ||
| if out, err := addCmd.CombinedOutput(); err != nil { | ||
| return log.E("dev.tag", "git add: "+strings.TrimSpace(string(out)), err) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
According to the official Git documentation, what happens when git add go.mod go.sumis run andgo.sum does not exist or is not tracked?
💡 Result:
git add go.mod go.sum“updates the index using the current content found in the working tree” for the given pathspecs, i.e., it stages those files for the next commit. (git-scm.com)- If
go.sumexists in your working tree but is untracked,git addwill add it (stage it) like any other “new … file[]”. (git-scm.com) - If
go.sumdoes not exist (so the pathspec matches nothing), Git errors with:error: pathspec 'go.sum' did not match any file(s) known to git.(git-scm.com) - The
--ignore-missingoption does not apply here because, per the docs, it “can only be used together with --dry-run”. (git-scm.com)
Citations:
- 1: https://git-scm.com/docs/git-add/2.46.0.html
- 2: https://git-scm.com/docs/git-add/2.46.0.html
- 3: https://git-scm.com/docs/user-manual/2.19.0?utm_source=openai
- 4: https://git-scm.com/docs/git-add/2.46.0.html
Stage only files that exist or are already tracked.
Line 262 unconditionally stages both go.mod and go.sum, but valid Go modules can legitimately have no go.sum. When go.sum doesn't exist, git add fails with error: pathspec 'go.sum' did not match any file(s) known to git, breaking the tag flow even when only go.mod changed.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cmd/dev/cmd_tag.go` around lines 247 - 265, The git add currently always
includes both "go.mod" and "go.sum" which fails when go.sum doesn't exist;
change the add step (where addCmd is constructed) to build the args list
dynamically and only include "go.mod" and "go.sum" when they actually exist or
are tracked (use the existing checks modChanged and untrackedSum or os.Stat/git
ls-files) and then run exec.CommandContext(ctx, "git", "add", <filtered args>)
so addCmd only tries to stage files that are present/tracked in repoPath.
| buildCmd.Stderr = os.Stderr | ||
| if err := buildCmd.Run(); err != nil { | ||
| return fmt.Errorf("%s: %w", i18n.T("i18n.fail.run", "build"), err) | ||
| return log.E("setup.registry", i18n.T("i18n.fail.run", "build"), err) |
There was a problem hiding this comment.
Incorrect i18n.T template argument format.
The call i18n.T("i18n.fail.run", "build") passes a string directly, but the template "Failed to run {{.Name}}" expects a map with a Name key.
🐛 Proposed fix
- return log.E("setup.registry", i18n.T("i18n.fail.run", "build"), err)
+ return log.E("setup.registry", i18n.T("i18n.fail.run", map[string]any{"Name": "build"}), err)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return log.E("setup.registry", i18n.T("i18n.fail.run", "build"), err) | |
| return log.E("setup.registry", i18n.T("i18n.fail.run", map[string]any{"Name": "build"}), err) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cmd/setup/cmd_registry.go` at line 231, The i18n.T call is passing a raw
string but the template expects a map with a Name key; update the call at the
log.E invocation so i18n.T("i18n.fail.run", ...) receives a map with Name set to
"build" (i.e., replace the second argument with a map[string]string or
equivalent {"Name":"build"}) so the template {{.Name}} can be populated; the
change should be made where log.E("setup.registry", i18n.T(...), err) is
returned in cmd_registry.go.
| "error": { | ||
| "not_authenticated": "GitHub CLI (gh) is not authenticated. Run 'gh auth login' first.", | ||
| "config_not_found": "GitHub config file not found", | ||
| "conflicting_flags": "Cannot use --check with modification flags" |
There was a problem hiding this comment.
Error message may not match the validation logic.
The message "conflicting_flags": "Cannot use --check with modification flags" doesn't match the actual validation in cmd_github.go (line 120-121), which checks for ghRepo != "" && ghAll (using --repo with --all together). Consider updating the message to accurately describe the conflict:
- "conflicting_flags": "Cannot use --check with modification flags"
+ "conflicting_flags": "Cannot use --repo with --all"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "conflicting_flags": "Cannot use --check with modification flags" | |
| "conflicting_flags": "Cannot use --repo with --all" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@locales/en.json` at line 415, The localization key "conflicting_flags"
currently reads "Cannot use --check with modification flags" but the actual
validation in cmd_github.go compares ghRepo != "" && ghAll (conflict between
--repo and --all); update the value for the "conflicting_flags" key in
locales/en.json to accurately describe that conflict (e.g., "Cannot use --repo
with --all" or similar) so the message matches the check performed in
cmd_github.go.
Forge → GitHub Sync
Commits: 10
Files changed: 27
Automated sync from Forge (forge.lthn.ai) to GitHub mirror.
Co-Authored-By: Virgil [email protected]
Summary by CodeRabbit
Release Notes
New Features
tagsubcommand underdevfor managing repository versioning with support for dry-run and force modes, enabling automated tagging with dependency-ordered execution.Bug Fixes & Improvements
Dependencies