Skip to content

Develop - #12

Merged
davidcreated merged 5 commits into
mainfrom
develop
Jul 23, 2026
Merged

Develop#12
davidcreated merged 5 commits into
mainfrom
develop

Conversation

@davidcreated

@davidcreated davidcreated commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Type of change

  • Bug fix
  • New feature
  • Docs
  • Internal / refactor

Checklist

  • This repo's checks pass locally (format, analyze, tests — see CONTRIBUTING)
  • Added / updated tests where it makes sense
  • Added / updated docs and the CHANGELOG.md (if the repo has one)
  • PR targets the develop branch

Summary by CodeRabbit

  • New Features

    • Added project detection for Flutter, React Native, Android, iOS, Kotlin, Swift, and Dart projects.
    • Added anvil detect, including table and JSON output with configurable scan depth.
    • Added anvil build with target, flavor, release, signing, dry-run, and plain-output options.
    • Added release signing setup for Android and iOS.
    • Added guided progress display and plain terminal output.
    • Added build, test, analysis, dependency, and artifact handling across supported stacks.
  • Documentation

    • Updated onboarding, architecture, roadmap, changelog, and project guidance.

Add internal/detect with marker-file detectors for Flutter (vs pure Dart; app/module/plugin), React Native (bare, Expo managed, Expo prebuild), native Android (app vs library, KMP flag), and native iOS (Xcode/SPM/Podfile). A depth-limited prune-on-detect scanner with skip lists and a containment sweep attributes android/ios folders to their Flutter or RN parent and surfaces each monorepo project once. Add the Cobra-based anvil detect command (table and --json, with --path and --depth). 16 fixture-tree tests cover the edge cases (node_modules exclusion, monorepo, KMP, plugin example pruning). Also apply the no-emojis, no-dash-connectors, minimal-comments style across docs and record it in CLAUDE.md.
feat(detect): stack detection engine and anvil detect
* feat(build): guided build lifecycle and anvil build

Add the driver contract (internal/driver) and drivers for Flutter, React Native, native Android, native iOS, Swift (SPM), and Kotlin/JVM, with --flavor threaded into build and test steps. Add the runner (internal/pipeline) that streams combined output, captures exit codes, classifies results, collects artifacts, and fail-fasts. Add internal/tui with a Bubble Tea view and a plain non-TTY renderer, and the anvil build command (--path/--target/--flavor/--release/--dry-run/--plain). Refine detection so Gradle is not always Android and Package.swift is Swift, adding Swift and Kotlin stacks. Tests cover driver steps, the runner via a real subprocess, and the plain renderer.

* fix: remove unused dirExists helper (staticcheck U1000)
Add a Sign phase and the anvil sign command plus anvil build --sign. internal/sign generates a PKCS12 keystore with keytool, writes key.properties, wires Gradle signingConfigs, writes an iOS ExportOptions.plist, and gitignores the secrets. iOS sign steps live on the Flutter (flutter build ipa), React Native, and native iOS (xcodebuild archive + exportArchive) drivers; Android signs at build time via the wired Gradle config. Passwords come from prompts (huh) or environment, never the repo, and --dry-run makes no changes. Tests cover live keystore generation (keytool), Gradle wiring idempotence, gitignore, ExportOptions, and step argv.
@davidcreated davidcreated self-assigned this Jul 23, 2026
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds Cobra-based detect, build, sign, and version commands; filesystem stack detection; platform drivers; fail-fast pipeline execution; terminal renderers; Android and iOS signing setup; tests; dependencies; and updated project documentation.

Changes

Anvil lifecycle

Layer / File(s) Summary
Stack detection and detect command
internal/detect/*, cmd/detect.go
Detects Flutter, Dart, React Native, Android, iOS, Kotlin, and Swift projects with depth limits, pruning, containment handling, and table or JSON output.
Driver contract and build execution
internal/driver/*, internal/pipeline/*, internal/tui/*, cmd/build.go, cmd/root.go, cmd/version.go, go.mod
Defines phase and status contracts, adds platform drivers, executes steps with streamed events and fail-fast behavior, renders plain or interactive output, and exposes Cobra build and version commands.
Release signing setup
cmd/sign.go, internal/sign/*
Adds Android keystore and Gradle wiring, iOS export options, secret handling, gitignore updates, dry-run behavior, and signing tests.
Project documentation and operating contract
CHANGELOG.md, CLAUDE.md, README.md, docs/*, tasks/*
Updates architecture, roadmap, onboarding, milestone progress, lessons, changelog entries, and agent working rules.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too vague and does not describe the actual changes in the PR. Use a specific title that summarizes the main change, such as adding the anvil detection, build, and signing workflow.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Comment @coderabbitai help to get the list of available commands.

@davidcreated
davidcreated merged commit 635a012 into main Jul 23, 2026
10 of 11 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (4)
cmd/root.go (1)

20-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the redundant Execute comment.

Line 20 only restates the function behavior. As per coding guidelines, “Avoid unnecessary comments. Use self-explanatory names, and add comments only for non-obvious constraints that cannot be expressed in code.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/root.go` at line 20, Remove the redundant comment immediately preceding
the Execute function in cmd/root.go, leaving the function implementation
unchanged.

Source: Coding guidelines

cmd/sign.go (1)

41-42: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider validating --export-method against the documented values.

The flag help text says only development or ad-hoc are supported, but any string is accepted and written verbatim into ExportOptions.plist (via setupIOSSigning), deferring the error to a later, less obvious xcodebuild failure.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/sign.go` around lines 41 - 42, Validate signMethod from the
--export-method flag before setupIOSSigning writes ExportOptions.plist,
accepting only the documented development and ad-hoc values. Reject unsupported
values with a clear user-facing error and preserve the existing behavior for
valid values.
internal/sign/sign.go (2)

40-40: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Use exec.CommandContext per golangci-lint's noctx rule.

Both keytool invocations use exec.Command without a context, flagged by noctx. Since the repo's ./check wrapper runs lint as part of the gate, this should be fixed at both sites with the same pattern (accept/pass a context.Context, or use context.Background() if no cancellation is currently threaded through).

  • internal/sign/sign.go#L40: change exec.Command("keytool", args...) to exec.CommandContext(ctx, "keytool", args...), threading a context.Context into GenerateKeystore.
  • internal/sign/sign_test.go#L36: change exec.Command("keytool", "-list", ...) to exec.CommandContext(context.Background(), "keytool", "-list", ...).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/sign/sign.go` at line 40, Replace both context-free keytool
invocations with exec.CommandContext: thread a context.Context into
GenerateKeystore in internal/sign/sign.go at line 40, and use
context.Background() in internal/sign/sign_test.go at line 36. Update affected
callers and imports as needed while preserving existing command arguments and
behavior.

Source: Linters/SAST tools


116-116: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Check f.Close() errors per golangci-lint's errcheck rule.

Both WireAndroidGradle and EnsureGitignore open a file with os.OpenFile and defer f.Close() without checking the returned error, which errcheck flags and which the project's ./check gate would fail on.

  • internal/sign/sign.go#L116: capture the close error in WireAndroidGradle (e.g., via a named return or explicit check) instead of defer f.Close().
  • internal/sign/sign.go#L168: same fix in EnsureGitignore.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/sign/sign.go` at line 116, Handle errors returned by deferred file
closes in both WireAndroidGradle and EnsureGitignore instead of discarding
f.Close() results. Update internal/sign/sign.go at lines 116-116 and 168-168,
using named returns or explicit deferred checks, and propagate any close error
appropriately so errcheck passes.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 80-85: Update the “Where things are” section in CLAUDE.md to
document the implemented build and sign commands under cmd/ and the signing
implementation under internal/sign, while preserving the existing root, version,
detect, driver, pipeline, documentation, and task references.

In `@cmd/build.go`:
- Around line 103-105: The output writes in chooseProject must handle errors:
propagate failures from both fmt.Fprintln/Fprintf calls through the function’s
existing error path. In internal/pipeline/pipeline_test.go lines 21-23,
explicitly assert the write result or discard it to satisfy errcheck.

In `@cmd/sign.go`:
- Around line 164-173: Validate teamID after the interactive huh form completes
in the team-ID resolution block, matching resolveSecret’s empty-value behavior.
If the prompt returns an empty value, return an error instructing the user to
set --team-id or ANVIL_TEAM_ID before proceeding to WriteExportOptions.
- Around line 63-72: Update the control flow in the signing command around the
len(extra) check so signDryRun is handled before the empty-extra confirmation
branch. Ensure Android dry-runs call printPlan and return without printing
“Signing configured,” while preserving the existing confirmation for non-dry-run
cases with no extra actions.

In `@docs/ARCHITECTURE.md`:
- Line 52: Align the Driver contract sketch and related phase-selection text in
ARCHITECTURE.md with internal/driver.Driver by removing or revising the
unsupported Capabilities requirement and documenting only the implemented
methods: Name, Steps, Classify, and Artifacts. Keep the documentation consistent
with the registry and existing drivers rather than changing implementation or
tests.
- Around line 8-16: Update the architecture diagram fenced block in
ARCHITECTURE.md to specify the text language identifier, preserving the diagram
content unchanged.

In `@internal/detect/detect.go`:
- Around line 67-69: Update the walk callback in detect to return any non-nil
filesystem traversal err immediately before accessing d.IsDir(), while
preserving the existing behavior of skipping non-directory entries. This ensures
permission and I/O errors propagate instead of being silently ignored.

In `@internal/tui/plain.go`:
- Around line 15-43: Update Plain to track an outputOK flag and capture errors
from every fmt.Fprint/Fprintf call to out, marking outputOK false whenever a
write fails. Return success && outputOK so pipeline.KindDone cannot mask output
failures, while preserving the existing build success handling.

In `@README.md`:
- Around line 7-8: Update the README capability descriptions and
milestone/status sections to match the implementation: describe build and
signing as available, remove or clearly label upload as planned, and add Swift
and Kotlin to the v1 supported stack list. Ensure all related statements remain
consistent across the command overview and referenced milestone sections.

---

Nitpick comments:
In `@cmd/root.go`:
- Line 20: Remove the redundant comment immediately preceding the Execute
function in cmd/root.go, leaving the function implementation unchanged.

In `@cmd/sign.go`:
- Around line 41-42: Validate signMethod from the --export-method flag before
setupIOSSigning writes ExportOptions.plist, accepting only the documented
development and ad-hoc values. Reject unsupported values with a clear
user-facing error and preserve the existing behavior for valid values.

In `@internal/sign/sign.go`:
- Line 40: Replace both context-free keytool invocations with
exec.CommandContext: thread a context.Context into GenerateKeystore in
internal/sign/sign.go at line 40, and use context.Background() in
internal/sign/sign_test.go at line 36. Update affected callers and imports as
needed while preserving existing command arguments and behavior.
- Line 116: Handle errors returned by deferred file closes in both
WireAndroidGradle and EnsureGitignore instead of discarding f.Close() results.
Update internal/sign/sign.go at lines 116-116 and 168-168, using named returns
or explicit deferred checks, and propagate any close error appropriately so
errcheck passes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d826798e-5686-468a-bce9-593e16d4f781

📥 Commits

Reviewing files that changed from the base of the PR and between 522422f and 366e550.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (37)
  • CHANGELOG.md
  • CLAUDE.md
  • README.md
  • cmd/build.go
  • cmd/detect.go
  • cmd/root.go
  • cmd/sign.go
  • cmd/version.go
  • docs/ARCHITECTURE.md
  • docs/ROADMAP.md
  • go.mod
  • internal/detect/android.go
  • internal/detect/detect.go
  • internal/detect/detect_test.go
  • internal/detect/flutter.go
  • internal/detect/ios.go
  • internal/detect/kotlin.go
  • internal/detect/reactnative.go
  • internal/detect/swift.go
  • internal/driver/android.go
  • internal/driver/driver.go
  • internal/driver/driver_test.go
  • internal/driver/flutter.go
  • internal/driver/ios.go
  • internal/driver/kotlin.go
  • internal/driver/reactnative.go
  • internal/driver/registry.go
  • internal/driver/swift.go
  • internal/pipeline/pipeline.go
  • internal/pipeline/pipeline_test.go
  • internal/sign/sign.go
  • internal/sign/sign_test.go
  • internal/tui/plain.go
  • internal/tui/plain_test.go
  • internal/tui/tui.go
  • tasks/lessons.md
  • tasks/todo.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 9

🧹 Nitpick comments (4)
cmd/root.go (1)

20-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the redundant Execute comment.

Line 20 only restates the function behavior. As per coding guidelines, “Avoid unnecessary comments. Use self-explanatory names, and add comments only for non-obvious constraints that cannot be expressed in code.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/root.go` at line 20, Remove the redundant comment immediately preceding
the Execute function in cmd/root.go, leaving the function implementation
unchanged.

Source: Coding guidelines

cmd/sign.go (1)

41-42: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider validating --export-method against the documented values.

The flag help text says only development or ad-hoc are supported, but any string is accepted and written verbatim into ExportOptions.plist (via setupIOSSigning), deferring the error to a later, less obvious xcodebuild failure.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/sign.go` around lines 41 - 42, Validate signMethod from the
--export-method flag before setupIOSSigning writes ExportOptions.plist,
accepting only the documented development and ad-hoc values. Reject unsupported
values with a clear user-facing error and preserve the existing behavior for
valid values.
internal/sign/sign.go (2)

40-40: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Use exec.CommandContext per golangci-lint's noctx rule.

Both keytool invocations use exec.Command without a context, flagged by noctx. Since the repo's ./check wrapper runs lint as part of the gate, this should be fixed at both sites with the same pattern (accept/pass a context.Context, or use context.Background() if no cancellation is currently threaded through).

  • internal/sign/sign.go#L40: change exec.Command("keytool", args...) to exec.CommandContext(ctx, "keytool", args...), threading a context.Context into GenerateKeystore.
  • internal/sign/sign_test.go#L36: change exec.Command("keytool", "-list", ...) to exec.CommandContext(context.Background(), "keytool", "-list", ...).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/sign/sign.go` at line 40, Replace both context-free keytool
invocations with exec.CommandContext: thread a context.Context into
GenerateKeystore in internal/sign/sign.go at line 40, and use
context.Background() in internal/sign/sign_test.go at line 36. Update affected
callers and imports as needed while preserving existing command arguments and
behavior.

Source: Linters/SAST tools


116-116: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Check f.Close() errors per golangci-lint's errcheck rule.

Both WireAndroidGradle and EnsureGitignore open a file with os.OpenFile and defer f.Close() without checking the returned error, which errcheck flags and which the project's ./check gate would fail on.

  • internal/sign/sign.go#L116: capture the close error in WireAndroidGradle (e.g., via a named return or explicit check) instead of defer f.Close().
  • internal/sign/sign.go#L168: same fix in EnsureGitignore.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/sign/sign.go` at line 116, Handle errors returned by deferred file
closes in both WireAndroidGradle and EnsureGitignore instead of discarding
f.Close() results. Update internal/sign/sign.go at lines 116-116 and 168-168,
using named returns or explicit deferred checks, and propagate any close error
appropriately so errcheck passes.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 80-85: Update the “Where things are” section in CLAUDE.md to
document the implemented build and sign commands under cmd/ and the signing
implementation under internal/sign, while preserving the existing root, version,
detect, driver, pipeline, documentation, and task references.

In `@cmd/build.go`:
- Around line 103-105: The output writes in chooseProject must handle errors:
propagate failures from both fmt.Fprintln/Fprintf calls through the function’s
existing error path. In internal/pipeline/pipeline_test.go lines 21-23,
explicitly assert the write result or discard it to satisfy errcheck.

In `@cmd/sign.go`:
- Around line 164-173: Validate teamID after the interactive huh form completes
in the team-ID resolution block, matching resolveSecret’s empty-value behavior.
If the prompt returns an empty value, return an error instructing the user to
set --team-id or ANVIL_TEAM_ID before proceeding to WriteExportOptions.
- Around line 63-72: Update the control flow in the signing command around the
len(extra) check so signDryRun is handled before the empty-extra confirmation
branch. Ensure Android dry-runs call printPlan and return without printing
“Signing configured,” while preserving the existing confirmation for non-dry-run
cases with no extra actions.

In `@docs/ARCHITECTURE.md`:
- Line 52: Align the Driver contract sketch and related phase-selection text in
ARCHITECTURE.md with internal/driver.Driver by removing or revising the
unsupported Capabilities requirement and documenting only the implemented
methods: Name, Steps, Classify, and Artifacts. Keep the documentation consistent
with the registry and existing drivers rather than changing implementation or
tests.
- Around line 8-16: Update the architecture diagram fenced block in
ARCHITECTURE.md to specify the text language identifier, preserving the diagram
content unchanged.

In `@internal/detect/detect.go`:
- Around line 67-69: Update the walk callback in detect to return any non-nil
filesystem traversal err immediately before accessing d.IsDir(), while
preserving the existing behavior of skipping non-directory entries. This ensures
permission and I/O errors propagate instead of being silently ignored.

In `@internal/tui/plain.go`:
- Around line 15-43: Update Plain to track an outputOK flag and capture errors
from every fmt.Fprint/Fprintf call to out, marking outputOK false whenever a
write fails. Return success && outputOK so pipeline.KindDone cannot mask output
failures, while preserving the existing build success handling.

In `@README.md`:
- Around line 7-8: Update the README capability descriptions and
milestone/status sections to match the implementation: describe build and
signing as available, remove or clearly label upload as planned, and add Swift
and Kotlin to the v1 supported stack list. Ensure all related statements remain
consistent across the command overview and referenced milestone sections.

---

Nitpick comments:
In `@cmd/root.go`:
- Line 20: Remove the redundant comment immediately preceding the Execute
function in cmd/root.go, leaving the function implementation unchanged.

In `@cmd/sign.go`:
- Around line 41-42: Validate signMethod from the --export-method flag before
setupIOSSigning writes ExportOptions.plist, accepting only the documented
development and ad-hoc values. Reject unsupported values with a clear
user-facing error and preserve the existing behavior for valid values.

In `@internal/sign/sign.go`:
- Line 40: Replace both context-free keytool invocations with
exec.CommandContext: thread a context.Context into GenerateKeystore in
internal/sign/sign.go at line 40, and use context.Background() in
internal/sign/sign_test.go at line 36. Update affected callers and imports as
needed while preserving existing command arguments and behavior.
- Line 116: Handle errors returned by deferred file closes in both
WireAndroidGradle and EnsureGitignore instead of discarding f.Close() results.
Update internal/sign/sign.go at lines 116-116 and 168-168, using named returns
or explicit deferred checks, and propagate any close error appropriately so
errcheck passes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d826798e-5686-468a-bce9-593e16d4f781

📥 Commits

Reviewing files that changed from the base of the PR and between 522422f and 366e550.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (37)
  • CHANGELOG.md
  • CLAUDE.md
  • README.md
  • cmd/build.go
  • cmd/detect.go
  • cmd/root.go
  • cmd/sign.go
  • cmd/version.go
  • docs/ARCHITECTURE.md
  • docs/ROADMAP.md
  • go.mod
  • internal/detect/android.go
  • internal/detect/detect.go
  • internal/detect/detect_test.go
  • internal/detect/flutter.go
  • internal/detect/ios.go
  • internal/detect/kotlin.go
  • internal/detect/reactnative.go
  • internal/detect/swift.go
  • internal/driver/android.go
  • internal/driver/driver.go
  • internal/driver/driver_test.go
  • internal/driver/flutter.go
  • internal/driver/ios.go
  • internal/driver/kotlin.go
  • internal/driver/reactnative.go
  • internal/driver/registry.go
  • internal/driver/swift.go
  • internal/pipeline/pipeline.go
  • internal/pipeline/pipeline_test.go
  • internal/sign/sign.go
  • internal/sign/sign_test.go
  • internal/tui/plain.go
  • internal/tui/plain_test.go
  • internal/tui/tui.go
  • tasks/lessons.md
  • tasks/todo.md
🛑 Comments failed to post (9)
CLAUDE.md (1)

80-85: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Synchronize the repository map with the implemented CLI.

cmd/ now includes build and sign commands, and signing lives under internal/sign, but this section only documents root, version, and detect. Add the implemented command and package locations so contributors do not miss the current lifecycle and signing code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLAUDE.md` around lines 80 - 85, Update the “Where things are” section in
CLAUDE.md to document the implemented build and sign commands under cmd/ and the
signing implementation under internal/sign, while preserving the existing root,
version, detect, driver, pipeline, documentation, and task references.
cmd/build.go (1)

103-105: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Handle the reported writer errors.

errcheck reports both ignored fmt.Fprintln results, which blocks linting.

  • cmd/build.go#L103-L105: propagate an output-write error from chooseProject.
  • internal/pipeline/pipeline_test.go#L21-L23: assert the write result or explicitly discard it.
🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 103-103: Error return value of fmt.Fprintln is not checked

(errcheck)

📍 Affects 2 files
  • cmd/build.go#L103-L105 (this comment)
  • internal/pipeline/pipeline_test.go#L21-L23
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/build.go` around lines 103 - 105, The output writes in chooseProject must
handle errors: propagate failures from both fmt.Fprintln/Fprintf calls through
the function’s existing error path. In internal/pipeline/pipeline_test.go lines
21-23, explicitly assert the write result or discard it to satisfy errcheck.

Source: Linters/SAST tools

cmd/sign.go (2)

63-72: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Android dry-run prints a false "Signing configured" confirmation.

setupAndroidSigning's dry-run branch (lines 112-123) always returns nil for extra, same as its real-work branch (line 146). Back here, len(extra) == 0 is checked before signDryRun, so for Android the flow always falls into the first branch and prints "Signing configured. Run 'anvil build --release'..." even during --dry-run, right after the "Would set up Android signing:" plan was printed. No changes were actually made, but the message tells the user otherwise.

🐛 Proposed fix: check dry-run before the empty-extra branch
-	if len(extra) == 0 {
-		fmt.Fprintln(cmd.OutOrStdout(), "Signing configured. Run 'anvil build --release' to produce a signed artifact.")
-		return nil
-	}
 	if signDryRun {
+		if len(extra) == 0 {
+			return nil
+		}
 		printPlan(cmd, chosen, d, opts, extra)
 		return nil
 	}
+	if len(extra) == 0 {
+		fmt.Fprintln(cmd.OutOrStdout(), "Signing configured. Run 'anvil build --release' to produce a signed artifact.")
+		return nil
+	}
 	return runPipeline(cmd, chosen.Path, d, opts, extra)
📝 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.

	if signDryRun {
		if len(extra) == 0 {
			return nil
		}
		printPlan(cmd, chosen, d, opts, extra)
		return nil
	}
	if len(extra) == 0 {
		fmt.Fprintln(cmd.OutOrStdout(), "Signing configured. Run 'anvil build --release' to produce a signed artifact.")
		return nil
	}
	return runPipeline(cmd, chosen.Path, d, opts, extra)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/sign.go` around lines 63 - 72, Update the control flow in the signing
command around the len(extra) check so signDryRun is handled before the
empty-extra confirmation branch. Ensure Android dry-runs call printPlan and
return without printing “Signing configured,” while preserving the existing
confirmation for non-dry-run cases with no extra actions.

164-173: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Empty Team ID from the interactive prompt is not validated.

Unlike resolveSecret (lines 196-198), which errors on an empty value, this block accepts whatever the huh form returns without checking for emptiness. If the user submits the form blank, WriteExportOptions silently writes an ExportOptions.plist with an empty teamID.

🐛 Proposed fix
 		if err := huh.NewForm(huh.NewGroup(
 			huh.NewInput().Title("Apple Developer Team ID").Value(&teamID),
 		)).Run(); err != nil {
 			return driver.Signing{}, nil, err
 		}
+		if teamID == "" {
+			return driver.Signing{}, nil, errors.New("Apple Developer Team ID is required")
+		}
 	}
📝 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.

	if teamID == "" {
		if !interactive() {
			return driver.Signing{}, nil, errors.New("set --team-id or ANVIL_TEAM_ID")
		}
		if err := huh.NewForm(huh.NewGroup(
			huh.NewInput().Title("Apple Developer Team ID").Value(&teamID),
		)).Run(); err != nil {
			return driver.Signing{}, nil, err
		}
		if teamID == "" {
			return driver.Signing{}, nil, errors.New("Apple Developer Team ID is required")
		}
	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/sign.go` around lines 164 - 173, Validate teamID after the interactive
huh form completes in the team-ID resolution block, matching resolveSecret’s
empty-value behavior. If the prompt returns an empty value, return an error
instructing the user to set --team-id or ANVIL_TEAM_ID before proceeding to
WriteExportOptions.
docs/ARCHITECTURE.md (2)

8-16: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Specify the diagram fence language.

Markdownlint reports MD040 because this fenced block has no language identifier. Use text for the architecture diagram.

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 8-8: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ARCHITECTURE.md` around lines 8 - 16, Update the architecture diagram
fenced block in ARCHITECTURE.md to specify the text language identifier,
preserving the diagram content unchanged.

Source: Linters/SAST tools


52-52: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Align the documented Driver contract with internal/driver.Driver.

The supplied implementation defines Name, Steps, Classify, and Artifacts, but no Capabilities method. The new pipeline description therefore documents behavior that the actual registry and drivers cannot provide. Update this sketch and the phase-selection text to match internal/driver.Driver, or change the implementation and tests to introduce the documented contract.

Also applies to: 63-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ARCHITECTURE.md` at line 52, Align the Driver contract sketch and
related phase-selection text in ARCHITECTURE.md with internal/driver.Driver by
removing or revising the unsupported Capabilities requirement and documenting
only the implemented methods: Name, Steps, Classify, and Artifacts. Keep the
documentation consistent with the registry and existing drivers rather than
changing implementation or tests.
internal/detect/detect.go (1)

67-69: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Propagate filesystem traversal errors.

Line 68 discards permission and I/O errors, so detect can successfully report a partial result or no stack. Return err before inspecting d.

Proposed fix
 	walk := func(path string, d fs.DirEntry, err error) error {
-		if err != nil || !d.IsDir() {
+		if err != nil {
+			return err
+		}
+		if !d.IsDir() {
 			return nil
 		}
📝 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.

	walk := func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if !d.IsDir() {
			return nil
		}
🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 69-69: error is not nil (line 67) but it returns nil

(nilerr)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/detect/detect.go` around lines 67 - 69, Update the walk callback in
detect to return any non-nil filesystem traversal err immediately before
accessing d.IsDir(), while preserving the existing behavior of skipping
non-directory entries. This ensures permission and I/O errors propagate instead
of being silently ignored.

Source: Linters/SAST tools

internal/tui/plain.go (1)

15-43: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Propagate output write failures.

A broken writer is silently treated as a successful build. Track errors from every write and return success && outputOK, otherwise KindDone can mask failed CI or pipe output.

Proposed fix
 func Plain(events <-chan pipeline.Event, out io.Writer) bool {
 	var items []pipeline.Item
 	success := true
+	outputOK := true
+	write := func(format string, args ...interface{}) {
+		if _, err := fmt.Fprintf(out, format, args...); err != nil {
+			outputOK = false
+		}
+	}
 	for e := range events {
 		switch e.Kind {
 		case pipeline.KindPlan:
 			items = e.Items
 		case pipeline.KindStepStart:
-			fmt.Fprintf(out, "==> %s: %s\n", items[e.Index].Phase, items[e.Index].Name)
+			write("==> %s: %s\n", items[e.Index].Phase, items[e.Index].Name)
 		case pipeline.KindLine:
-			fmt.Fprintf(out, "    %s\n", e.Line)
+			write("    %s\n", e.Line)
 		case pipeline.KindStepDone:
 			switch e.Status {
 			case driver.Skipped:
-				fmt.Fprintf(out, "--- %s: skipped\n", items[e.Index].Phase)
+				write("--- %s: skipped\n", items[e.Index].Phase)
 			case driver.OK:
-				fmt.Fprintln(out, "    ok")
+				write("    ok\n")
 				for _, a := range e.Artifacts {
-					fmt.Fprintf(out, "    artifact: %s\n", a)
+					write("    artifact: %s\n", a)
 				}
 			case driver.Failed:
-				fmt.Fprintf(out, "    failed (exit %d)\n", e.ExitCode)
+				write("    failed (exit %d)\n", e.ExitCode)
 				success = false
 			}
 		case pipeline.KindDone:
 			success = e.Success
 		}
 	}
-	return success
+	return success && outputOK
 }

The CLI consumes this boolean as its build result in cmd/build.go Line 122.

📝 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.

func Plain(events <-chan pipeline.Event, out io.Writer) bool {
	var items []pipeline.Item
	success := true
	outputOK := true
	write := func(format string, args ...interface{}) {
		if _, err := fmt.Fprintf(out, format, args...); err != nil {
			outputOK = false
		}
	}
	for e := range events {
		switch e.Kind {
		case pipeline.KindPlan:
			items = e.Items
		case pipeline.KindStepStart:
			write("==> %s: %s\n", items[e.Index].Phase, items[e.Index].Name)
		case pipeline.KindLine:
			write("    %s\n", e.Line)
		case pipeline.KindStepDone:
			switch e.Status {
			case driver.Skipped:
				write("--- %s: skipped\n", items[e.Index].Phase)
			case driver.OK:
				write("    ok\n")
				for _, a := range e.Artifacts {
					write("    artifact: %s\n", a)
				}
			case driver.Failed:
				write("    failed (exit %d)\n", e.ExitCode)
				success = false
			}
		case pipeline.KindDone:
			success = e.Success
		}
	}
	return success && outputOK
}
🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 23-23: Error return value of fmt.Fprintf is not checked

(errcheck)


[error] 25-25: Error return value of fmt.Fprintf is not checked

(errcheck)


[error] 29-29: Error return value of fmt.Fprintf is not checked

(errcheck)


[error] 31-31: Error return value of fmt.Fprintln is not checked

(errcheck)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/plain.go` around lines 15 - 43, Update Plain to track an
outputOK flag and capture errors from every fmt.Fprint/Fprintf call to out,
marking outputOK false whenever a write fails. Return success && outputOK so
pipeline.KindDone cannot mask output failures, while preserving the existing
build success handling.

Source: Linters/SAST tools

README.md (1)

7-8: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align README capabilities and status with the implemented milestones.

The README says the command signs and uploads, but upload remains planned. It also says build comes next and signing is later, although both are implemented. Finally, the v1 stack list omits Swift and Kotlin. Update these statements, or clearly mark the planned upload and future capabilities.

Also applies to: 20-22, 46-53

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 7 - 8, Update the README capability descriptions and
milestone/status sections to match the implementation: describe build and
signing as available, remove or clearly label upload as planned, and add Swift
and Kotlin to the v1 supported stack list. Ensure all related statements remain
consistent across the command overview and referenced milestone sections.

This was referenced Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant