Skip to content

feat: add attachment image push support to CLI and control plane#104

Merged
bnema merged 6 commits into
mainfrom
feat/attachment-image-push-support
Mar 14, 2026
Merged

feat: add attachment image push support to CLI and control plane#104
bnema merged 6 commits into
mainfrom
feat/attachment-image-push-support

Conversation

@bnema
Copy link
Copy Markdown
Owner

@bnema bnema commented Mar 14, 2026

Summary

Closes #103

  • Adds gordon attachments push <image> command for building, tagging, and pushing attachment images to the Gordon registry
  • Adds FindAttachmentTargetsByImage config lookup with shared image normalization (reuses route-matching rules)
  • Exposes attachment-by-image lookup via GET /admin/attachments/by-image/{image} admin API endpoint
  • Wires the lookup through the control plane for local/remote parity
  • Updates gordon push error messaging to suggest attachments push for non-route images
  • Updates CLI docs: attachments, push, bootstrap, and index

Why

Attachment-based deploys fail when the attachment image does not exist in the registry. Gordon had no native way to push attachment images — gordon push only works for route images. This adds that missing path so first deploys with custom attachments (e.g. pitlane-pgsql:v18) work end-to-end.

Test coverage

  • Config lookup: exact tag, bare name, registry-qualified, multi-target, no match
  • Admin handler: one target, multiple targets, empty, missing path, permissions, URL encoding
  • Remote client: standard and slash-containing image names
  • CLI command: target resolution, not-configured error, no-deploy behavior, tagged input normalization, namespace preservation
  • Full suite: 1906 tests pass across 48 packages

Summary by CodeRabbit

  • New Features

    • Added gordon attachments push to build/tag/push custom attachment images prior to deployment
    • Admin API to list attachment targets for a given image
  • Documentation

    • Full docs for the attachments push subcommand with examples and options
    • New "First Deploy with Custom Attachment Image" workflow added across guides
    • Updated push/bootstrap docs to show attachment-focused workflows and guidance
  • UX

    • Error messaging extended to suggest the attachments workflow when appropriate
  • Tests

    • Added tests covering attachment CLI, HTTP endpoint, and client behaviors

bnema added 2 commits March 14, 2026 06:16
…recent

Replace single-file .bak overwrite with timestamped backups (.bak.<unix>)
and automatic cleanup to prevent config directory pollution. Restore
falls back to legacy .bak for backward compatibility.
…nd precision

- Backup suffix now validated with strconv.ParseInt to reject non-numeric files
- Sort by parsed timestamp instead of lexicographic string comparison
- Use UnixNano() instead of Unix() to prevent collisions on rapid saves
- Test asserts exact backup count (5) instead of permissive range
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 14, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fcca8f20-104c-4860-86b5-fe3408dbdb22

📥 Commits

Reviewing files that changed from the base of the PR and between 85044d9 and 2d1105e.

📒 Files selected for processing (1)
  • internal/adapters/in/cli/attachments_push.go

📝 Walkthrough

Walkthrough

Adds a new CLI command gordon attachments push, control-plane and HTTP APIs to lookup attachment targets by image, config-service image-matching and attachment resolution, DTOs and tests, docs updates, and rotated backup/cleanup behavior in the config service.

Changes

Cohort / File(s) Summary
Documentation
docs/cli/attachments.md, docs/cli/bootstrap.md, docs/cli/index.md, docs/cli/push.md
New attachments push docs, examples and bootstrap workflow guidance; links added to related pages.
CLI command & tests
internal/adapters/in/cli/attachments_push.go, internal/adapters/in/cli/attachments_push_test.go, internal/adapters/in/cli/attachments.go
Adds gordon attachments push command, DI hooks for build/tag/push/version resolution, image/target resolution and unit tests; command registered in CLI.
Control plane adapters
internal/adapters/in/cli/controlplane.go, internal/adapters/in/cli/controlplane_local.go, internal/adapters/in/cli/controlplane_remote.go
Extends ControlPlane interface with FindAttachmentTargetsByImage and implements it for local and remote control planes.
HTTP API, remote client & DTOs
internal/adapters/in/http/admin/handler.go, internal/adapters/in/http/admin/handler_test.go, internal/adapters/in/cli/remote/client.go, internal/adapters/in/cli/remote/client_test.go, internal/adapters/dto/admin_attachments.go
Adds GET /admin/attachments/by-image/{image} handler, DTO AttachmentTargetsByImageResponse, remote client method and tests covering URL-encoded image names.
Config service & backups
internal/boundaries/in/config.go, internal/boundaries/in/mocks/mock_config_service.go, internal/usecase/config/service.go, internal/usecase/config/service_test.go
Adds FindAttachmentTargetsByImage to ConfigService and implementation (matchesImageName helper); switches to timestamped backup files, cleanupOldBackups and updated restore logic; tests updated.
CLI push messaging & tests
internal/adapters/in/cli/push.go, internal/adapters/in/cli/push_test.go
Updates push error messaging to hint gordon attachments push for attachment images and extends test mocks to satisfy new interface method.
DTO / test scaffolding
internal/adapters/dto/admin_attachments.go, internal/boundaries/in/mocks/*
Adds DTO and mock support for new API call and expectations used by client and handler tests.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/CLI
    participant CLI as Attachments Push Command
    participant CP as ControlPlane (local/remote)
    participant Config as ConfigService
    participant Builder as Image Builder
    participant Registry as Container Registry

    User->>CLI: gordon attachments push image[:tag] [--build]
    CLI->>CP: FindAttachmentTargetsByImage(imageName)
    alt local control plane
        CP->>Config: FindAttachmentTargetsByImage(imageName)
        Config-->>CP: []targets
    else remote control plane
        CP->>Registry: GET /admin/attachments/by-image/{imageName}
        Registry-->>CP: AttachmentTargetsByImageResponse (targets)
    end
    CP-->>CLI: targets
    CLI->>CLI: determine version, compute refs, validate
    alt --build flag set--
        CLI->>Builder: BuildAndPush(dockerfile, refs, platform, build-args)
        Builder->>Registry: push tags (version & latest)
        Registry-->>Builder: success
    else tag-only
        CLI->>Builder: TagAndPush(sourceImage, refs)
        Builder->>Registry: push tags
        Registry-->>Builder: success
    end
    Builder-->>CLI: result
    CLI-->>User: "Push complete"
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I hopped through code in twilight hush,

Built images, tagged without a rush.
Targets found and pushes done,
Registry lit by setting sun.
Carrot celebration — push complete! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding attachment image push support to both the CLI and control plane.
Linked Issues check ✅ Passed The pull request fully implements the requirements from issue #103: adds CLI support for attachment image push via 'gordon attachments push', provides config lookup and control-plane support for attachment image resolution, exposes admin API endpoint, and updates error messaging.
Out of Scope Changes check ✅ Passed All changes align with the PR objectives. The backup handling improvements in config/service.go support the new attachment lookup feature and are within scope for the configuration management layer.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/attachment-image-push-support
📝 Coding Plan
  • Generate coding plan for human review comments

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

bnema added 3 commits March 14, 2026 10:16
Add gordon attachments push command for building/tagging/pushing attachment
images to the Gordon registry. This closes the gap where attachment-based
deploys fail because the attachment image has no supported push path.

- Add FindAttachmentTargetsByImage config lookup with shared normalization
- Expose attachment-by-image lookup via admin API and control plane
- Add attachments push CLI command with build/tag/push support
- Update push error messaging to suggest attachments push for non-route images
- Update CLI docs for attachments, push, bootstrap, and index

Closes #103
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/adapters/in/cli/attachments_push.go`:
- Around line 50-109: The function runAttachmentsPush exceeds allowed cyclomatic
complexity; extract the output formatting (the cliWritef/cliWriteLine blocks
that print Attachment image, Targets, Image, Also, and Push complete) into a
helper like formatAndPrintAttachmentInfo(out, imageArg, targets, versionRef,
latestRef, version) and extract the push decision logic into another helper like
performAttachmentPush(ctx, build, version, platform, dockerfile, buildArgs,
registry, imageName, versionRef, latestRef) that encapsulates the buildAndPushFn
vs tagAndPushFn branches; replace the inline blocks in runAttachmentsPush with
calls to these two helpers to reduce branching and complexity while keeping
existing behavior.

In `@internal/usecase/config/service.go`:
- Around line 615-621: The cleanupOldBackups call currently returns an error up
the stack which makes Save fail even though the backup write succeeded; change
the cleanup behavior to log the error instead of returning it. Locate the block
with backupPath := fmt.Sprintf(...) and the subsequent
cleanupOldBackups(configFile, 5) call and replace the "return fmt.Errorf(...)"
path with a non-fatal log statement using the service's logger (e.g.,
s.logger.Errorf or similar) or the package logger, including context (configFile
and backupPath) and the error; keep the original error returned only for the
backup write failure, not for cleanup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 811212aa-b528-48d8-a4c0-84ffc27335c1

📥 Commits

Reviewing files that changed from the base of the PR and between fa9f754 and 85044d9.

📒 Files selected for processing (3)
  • internal/adapters/in/cli/attachments_push.go
  • internal/usecase/config/service.go
  • internal/usecase/config/service_test.go

Comment thread internal/adapters/in/cli/attachments_push.go
Comment on lines +615 to +621
backupPath := fmt.Sprintf("%s.bak.%d", configFile, time.Now().UnixNano())
if err := os.WriteFile(backupPath, src, 0600); err != nil {
return fmt.Errorf("failed to write backup config: %w", err)
}
if err := cleanupOldBackups(configFile, 5); err != nil {
return fmt.Errorf("failed to clean up old backups: %w", err)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider logging cleanup errors instead of propagating them.

The backup creation succeeds but returns an error if old backup cleanup fails. This could cause unnecessary failures for the caller (e.g., Save operation) when the primary operation succeeded.

🔧 Suggested approach
 	if err := os.WriteFile(backupPath, src, 0600); err != nil {
 		return fmt.Errorf("failed to write backup config: %w", err)
 	}
-	if err := cleanupOldBackups(configFile, 5); err != nil {
-		return fmt.Errorf("failed to clean up old backups: %w", err)
-	}
+	// Cleanup is best-effort; don't fail the backup operation
+	_ = cleanupOldBackups(configFile, 5)

 	return nil

Alternatively, if cleanup errors should be visible, log them without failing the operation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/usecase/config/service.go` around lines 615 - 621, The
cleanupOldBackups call currently returns an error up the stack which makes Save
fail even though the backup write succeeded; change the cleanup behavior to log
the error instead of returning it. Locate the block with backupPath :=
fmt.Sprintf(...) and the subsequent cleanupOldBackups(configFile, 5) call and
replace the "return fmt.Errorf(...)" path with a non-fatal log statement using
the service's logger (e.g., s.logger.Errorf or similar) or the package logger,
including context (configFile and backupPath) and the error; keep the original
error returned only for the backup write failure, not for cleanup.

@bnema bnema merged commit 354c921 into main Mar 14, 2026
2 of 3 checks passed
@bnema bnema deleted the feat/attachment-image-push-support branch March 14, 2026 09:50
bnema added a commit that referenced this pull request Mar 19, 2026
feat: add attachment image push support to CLI and control plane
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.

add attachment image push support to CLI and control plane

1 participant