Skip to content

feat: add atmos pro commit for server-side commits via GitHub App - #2298

Merged
Andriy Knysh (aknysh) merged 56 commits into
mainfrom
osterman/pro-commit-cmd
Apr 12, 2026
Merged

Andriy Knysh (aknysh) merged 56 commits into
mainfrom
osterman/pro-commit-cmd

Conversation

@osterman

@osterman Erik Osterman (Cloud Posse) (osterman) commented Apr 7, 2026 •

Copy link
Copy Markdown
Member

what

  • Add new atmos pro commit CLI command that sends changed files to Atmos Pro, which creates commits server-side using its GitHub App installation — ensuring commits trigger CI workflows (unlike GITHUB_TOKEN commits)
  • Flexible staging control: --add "*.tf" for patterns, --all/-A for everything, or commit whatever is already staged
  • Built-in infinite loop prevention: automatically detects when running as atmos-pro[bot] and exits early
  • Client-side validation: path safety (.github/ rejection, traversal prevention), file size limits (2 MiB), max 200 changed files
  • Reuses existing OIDC authentication flow from pkg/pro/api_client.go
  • Introduces new atmos-pro blog tag and retroactively tags 3 existing Atmos Pro changelog entries
  • Full CLI docs, blog post announcement, and roadmap entry

why

  • Commits made with GITHUB_TOKEN in GitHub Actions don't trigger subsequent workflow runs — this is a deliberate GitHub limitation that blocks autofix patterns (e.g., terraform fmt + commit)
  • Teams previously needed third-party services like autofix.ci to work around this
  • Atmos Pro's GitHub App can create commits that trigger CI, and this command provides the CLI interface for that capability
  • The workflow never receives a write token — Atmos Pro controls exactly what gets committed

references

  • Replaces autofix.ci for autocommit workflows
  • Uses existing OIDC auth from pkg/pro/api_client.go (NewAtmosProAPIClientFromEnv)
  • API endpoint: POST /api/v1/git/commit

Summary by CodeRabbit

  • New Features

    • Added atmos pro commit for server-side GitHub commits with staging flags (--message, --comment, --add, --all), loop prevention, branch checks, file-size and changed-files safety limits, and stdout commit SHA.
  • Documentation

    • New CLI docs, blog post, roadmap entry, and tags with usage, GitHub Actions examples, flags, and safety guidance.
  • Usability

    • Improved input validation and clearer user-facing errors for commit and API failures.
  • Tests

    • Added unit and integration tests for commit flow, API client, DTO JSON, and validations.
  • Chores

    • CI workflow updated to run formatting fixes and invoke pro commit.

Add new `atmos pro commit` command that sends changed files to Atmos Pro,
which creates commits server-side using its GitHub App installation. This
ensures commits trigger CI workflows (unlike GITHUB_TOKEN commits).

Features:
- Flexible staging: --add "*.tf" for patterns, --all/-A for everything,
  or commit whatever is already staged
- Built-in loop prevention via GITHUB_ACTOR detection
- Client-side validation: path safety, file size limits, change count
- OIDC authentication reusing existing Atmos Pro auth flow

Also introduces `atmos-pro` blog tag and retroactively tags existing
Atmos Pro changelog entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added the size/l Large size PR label Apr 7, 2026
@github-actions

github-actions Bot commented Apr 7, 2026 •

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 2dddf1f.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

  • .github/workflows/autofix.yml

@coderabbitai

coderabbitai Bot commented Apr 7, 2026 •

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an atmos pro commit CLI command and server-side commit flow: CLI wiring and flags, input/branch/path/size validation, optional git staging and staged-change detection, file collection/base64, new DTOs and Pro API client with retries, sentinel errors, schema/env binding, tests, and docs.

Changes

Cohort / File(s) Summary
CLI Command
cmd/pro_commit.go
New pro commit subcommand; requires --message, optional --comment, --add, --all; validates mutual-exclusion and delegates to pro.ExecuteCommit.
Orchestrator & Business Logic
pkg/pro/commit.go, pkg/pro/commit_test.go
Adds ExecuteCommit and AtmosProBotActor: loop-prevention, message/comment/branch validation, optional git add, staged change detection, path filtering, file-size limit (2 MiB), 200-file limit, builds DTO and submits commit; comprehensive unit and git-backed tests.
API Client
pkg/pro/api_client_commit.go, pkg/pro/api_client_commit_test.go
Adds AtmosProAPIClient.CreateCommit and helpers: JSON POST to /git/commit, retry wrapper (refresh OIDC on 401), response parsing, API error mapping; tests for success, nil DTO, HTTP errors, and network failures.
DTOs
pkg/pro/dtos/commit.go, pkg/pro/dtos/commit_test.go
New types: CommitRequest, CommitResponse, CommitFileAddition, CommitFileDeletion, CommitChanges; JSON marshal/unmarshal tests including optional comment omission.
Errors
errors/errors.go
Adds Pro commit sentinel errors (missing/too-long message/comment, branch/path/file limits, staging/commit failures, staging-flag conflict) and reorders an existing sentinel.
Config & Schema
pkg/config/load.go, pkg/schema/pro.go
Binds settings.pro.github_head_ref to GITHUB_HEAD_REF; adds GitHubHeadRef to ProSettings (mapstructure-only field).
Docs & Website
website/docs/cli/commands/pro/pro-commit.mdx, website/blog/2026-04-07-pro-commit.mdx, website/blog/..., website/src/data/roadmap.js, website/blog/tags.yml
Adds CLI docs, blog post and roadmap entry for pro commit, new atmos-pro blog tag, example workflows and constraints documented.
CI Workflows
.github/workflows/atmos-pro.yaml, .github/workflows/autofix.yml
Adds new atmos-pro workflow that runs formatting and invokes pro commit; simplifies/replaces prior autofix.yml steps with a placeholder echo.

Sequence Diagram(s)

sequenceDiagram
    participant User as CLI User
    participant Cmd as Cmd\n(pro commit)
    participant Exec as ExecuteCommit\n(Orchestrator)
    participant Git as Git\n(Local)
    participant API as AtmosProAPIClient
    participant GH as GitHub

    User->>Cmd: atmos pro commit --message "..." --add "*.tf"
    Cmd->>Cmd: validate flags & init config
    Cmd->>Exec: ExecuteCommit(cfg, message, ...)
    Exec->>Exec: loop-prevention (atmos-pro[bot])
    Exec->>Exec: validate message/comment/branch
    Exec->>Git: git add (if requested)
    Exec->>Git: git diff --cached --name-only --diff-filter=AM/D
    Exec->>Exec: filter & validate paths (no .github/, no .., no absolute)
    Exec->>Git: read files, base64-encode (skip >2MiB)
    Exec->>Exec: build CommitRequest DTO
    Exec->>API: CreateCommit(request)
    API->>API: retry & refresh OIDC on 401
    API->>GH: POST /git/commit (OIDC auth)
    GH->>API: 2xx CommitResponse { data.sha }
    API->>Exec: return SHA
    Exec->>User: write SHA to stdout
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • aknysh
  • milldr
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a new atmos pro commit CLI command for server-side commits via GitHub App, which is the primary feature across the changeset.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch osterman/pro-commit-cmd

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (4)
pkg/pro/dtos/commit_test.go (1)

67-98: Prefer table-driven cases for the response variants.

Current coverage is good; a table-driven shape will trim duplication and make future API-field additions easier to maintain.

♻️ Suggested refactor
 func TestCommitResponse(t *testing.T) {
-	t.Run("successful response deserializes correctly", func(t *testing.T) {
-		body := `{
-			"success": true,
-			"status": 200,
-			"data": { "sha": "abc123def456" }
-		}`
-
-		var resp CommitResponse
-		err := json.Unmarshal([]byte(body), &resp)
-		require.NoError(t, err)
-		assert.True(t, resp.Success)
-		assert.Equal(t, 200, resp.Status)
-		assert.Equal(t, "abc123def456", resp.Data.SHA)
-	})
-
-	t.Run("error response deserializes correctly", func(t *testing.T) {
-		body := `{
-			"success": false,
-			"status": 400,
-			"errorMessage": "validation failed",
-			"traceId": "trace-123"
-		}`
-
-		var resp CommitResponse
-		err := json.Unmarshal([]byte(body), &resp)
-		require.NoError(t, err)
-		assert.False(t, resp.Success)
-		assert.Equal(t, "validation failed", resp.ErrorMessage)
-		assert.Equal(t, "trace-123", resp.TraceID)
-	})
+	cases := []struct {
+		name         string
+		body         string
+		success      bool
+		status       int
+		sha          string
+		errorMessage string
+		traceID      string
+	}{
+		{
+			name:    "success",
+			body:    `{"success":true,"status":200,"data":{"sha":"abc123def456"}}`,
+			success: true, status: 200, sha: "abc123def456",
+		},
+		{
+			name:    "error",
+			body:    `{"success":false,"status":400,"errorMessage":"validation failed","traceId":"trace-123"}`,
+			success: false, status: 400, errorMessage: "validation failed", traceID: "trace-123",
+		},
+	}
+
+	for _, tc := range cases {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			var resp CommitResponse
+			err := json.Unmarshal([]byte(tc.body), &resp)
+			require.NoError(t, err)
+			assert.Equal(t, tc.success, resp.Success)
+			assert.Equal(t, tc.status, resp.Status)
+			assert.Equal(t, tc.sha, resp.Data.SHA)
+			assert.Equal(t, tc.errorMessage, resp.ErrorMessage)
+			assert.Equal(t, tc.traceID, resp.TraceID)
+		})
+	}
 }

As per coding guidelines, **/*_test.go: Use table-driven tests for testing multiple scenarios in Go.

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

In `@pkg/pro/dtos/commit_test.go` around lines 67 - 98, TestCommitResponse
duplicates similar subtests; refactor it into a table-driven test that iterates
over a slice of cases (with fields like name, body, wantSuccess, wantStatus,
wantSHA, wantErrorMessage, wantTraceID), then for each case call
t.Run(case.name, func(t *testing.T) { json.Unmarshal into CommitResponse,
require.NoError, and assert the expected fields (Success, Status, Data.SHA,
ErrorMessage, TraceID) accordingly }); update TestCommitResponse to use this
table so adding new response fields later only requires extending the case
struct.
pkg/pro/api_client_commit.go (3)

67-69: Status code range allows 3xx redirects as success.

The condition resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest treats 3xx (redirects) as successful responses. For a JSON API endpoint, you'd typically want only 2xx as success:

resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices

In practice this is low risk since Go's http.Client follows redirects by default, but the semantics are clearer if restricted to 2xx.

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

In `@pkg/pro/api_client_commit.go` around lines 67 - 69, The success check for
HTTP responses in the commit API is too broad and treats 3xx redirects as
success; update the condition that currently uses resp.StatusCode <
http.StatusOK || resp.StatusCode >= http.StatusBadRequest to instead restrict
success to only 2xx (for example use resp.StatusCode < http.StatusOK ||
resp.StatusCode >= http.StatusMultipleChoices) so that only 2xx responses bypass
buildCommitAPIError; adjust the check near where resp.StatusCode is evaluated
and buildCommitAPIError(resp, body) is returned.

22-47: Consider adding performance tracking.

CreateCommit is a public function performing network I/O. The coding guidelines recommend adding defer perf.Track(nil, "pro.CreateCommit")() for observability.

♻️ Suggested addition after line 22
 func (c *AtmosProAPIClient) CreateCommit(dto *dtos.CommitRequest) (*dtos.CommitResponse, error) {
+	defer perf.Track(nil, "pro.CreateCommit")()
+
 	if dto == nil {

You'll need to import "github.com/cloudposse/atmos/pkg/perf" as well.

As per coding guidelines: "Add defer perf.Track(atmosConfig, "pkg.FuncName")() plus blank line to all public functions. Use nil if no atmosConfig param."

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

In `@pkg/pro/api_client_commit.go` around lines 22 - 47, Add performance tracking
to the public CreateCommit method by inserting a deferred perf.Track call at the
start of the function (use defer perf.Track(nil, "pro.CreateCommit")() and a
blank line after it) and add the import for
"github.com/cloudposse/atmos/pkg/perf"; update the CreateCommit function (method
name CreateCommit on AtmosProAPIClient) to include this defer immediately after
the nil-check so the network I/O is measured.

3-15: Import grouping: logger belongs with other Atmos packages.

The log import from github.com/cloudposse/atmos/pkg/logger is an Atmos package but sits alone in group 2. Per guidelines, all cloudposse/atmos packages should be in group 3 together.

♻️ Suggested fix
 import (
 	"bytes"
 	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
 	"net/http"
 
-	log "github.com/cloudposse/atmos/pkg/logger"
-
 	errUtils "github.com/cloudposse/atmos/errors"
+	log "github.com/cloudposse/atmos/pkg/logger"
 	"github.com/cloudposse/atmos/pkg/pro/dtos"
 )

As per coding guidelines: "Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages."

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

In `@pkg/pro/api_client_commit.go` around lines 3 - 15, Move the log import into
the Atmos package import group so all cloudposse/atmos packages are grouped
together; specifically, reorder the imports so standard library imports (bytes,
encoding/json, errors, fmt, io, net/http) come first, third-party packages
(e.g., any non-cloudposse imports) second, and then the Atmos packages group
contains log ("github.com/cloudposse/atmos/pkg/logger"), errUtils
("github.com/cloudposse/atmos/errors"), and dtos
("github.com/cloudposse/atmos/pkg/pro/dtos") together, alphabetized within each
group.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/pro/api_client_commit.go`:
- Around line 50-60: sendCommitRequest currently calls c.HTTPClient.Do without
guarding for a nil HTTPClient which can panic; update sendCommitRequest to
mirror sendInstancesRequest by selecting a client variable (use c.HTTPClient if
non-nil, otherwise http.DefaultClient) and call client.Do(req) so the method
safely falls back when c.HTTPClient is nil; reference function name
sendCommitRequest and the existing pattern in sendInstancesRequest to implement
the nil-guard/fallback.

---

Nitpick comments:
In `@pkg/pro/api_client_commit.go`:
- Around line 67-69: The success check for HTTP responses in the commit API is
too broad and treats 3xx redirects as success; update the condition that
currently uses resp.StatusCode < http.StatusOK || resp.StatusCode >=
http.StatusBadRequest to instead restrict success to only 2xx (for example use
resp.StatusCode < http.StatusOK || resp.StatusCode >=
http.StatusMultipleChoices) so that only 2xx responses bypass
buildCommitAPIError; adjust the check near where resp.StatusCode is evaluated
and buildCommitAPIError(resp, body) is returned.
- Around line 22-47: Add performance tracking to the public CreateCommit method
by inserting a deferred perf.Track call at the start of the function (use defer
perf.Track(nil, "pro.CreateCommit")() and a blank line after it) and add the
import for "github.com/cloudposse/atmos/pkg/perf"; update the CreateCommit
function (method name CreateCommit on AtmosProAPIClient) to include this defer
immediately after the nil-check so the network I/O is measured.
- Around line 3-15: Move the log import into the Atmos package import group so
all cloudposse/atmos packages are grouped together; specifically, reorder the
imports so standard library imports (bytes, encoding/json, errors, fmt, io,
net/http) come first, third-party packages (e.g., any non-cloudposse imports)
second, and then the Atmos packages group contains log
("github.com/cloudposse/atmos/pkg/logger"), errUtils
("github.com/cloudposse/atmos/errors"), and dtos
("github.com/cloudposse/atmos/pkg/pro/dtos") together, alphabetized within each
group.

In `@pkg/pro/dtos/commit_test.go`:
- Around line 67-98: TestCommitResponse duplicates similar subtests; refactor it
into a table-driven test that iterates over a slice of cases (with fields like
name, body, wantSuccess, wantStatus, wantSHA, wantErrorMessage, wantTraceID),
then for each case call t.Run(case.name, func(t *testing.T) { json.Unmarshal
into CommitResponse, require.NoError, and assert the expected fields (Success,
Status, Data.SHA, ErrorMessage, TraceID) accordingly }); update
TestCommitResponse to use this table so adding new response fields later only
requires extending the case struct.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fa3536d8-18d8-454b-a8e4-ca8ae7cc46de

📥 Commits

Reviewing files that changed from the base of the PR and between 4549d28 and 92d160c.

📒 Files selected for processing (17)
  • cmd/pro_commit.go
  • errors/errors.go
  • pkg/config/load.go
  • pkg/pro/api_client_commit.go
  • pkg/pro/api_client_commit_test.go
  • pkg/pro/commit.go
  • pkg/pro/commit_test.go
  • pkg/pro/dtos/commit.go
  • pkg/pro/dtos/commit_test.go
  • pkg/schema/pro.go
  • website/blog/2025-10-28-pro-instances-api-query-params.md
  • website/blog/2026-03-19-instance-status-upload.mdx
  • website/blog/2026-03-25-chunked-stack-uploads.mdx
  • website/blog/2026-04-07-pro-commit.mdx
  • website/blog/tags.yml
  • website/docs/cli/commands/pro/pro-commit.mdx
  • website/src/data/roadmap.js
👮 Files not reviewed due to content moderation or server errors (8)
  • website/src/data/roadmap.js
  • website/docs/cli/commands/pro/pro-commit.mdx
  • website/blog/2026-04-07-pro-commit.mdx
  • pkg/pro/api_client_commit_test.go
  • cmd/pro_commit.go
  • errors/errors.go
  • pkg/pro/commit_test.go
  • pkg/pro/commit.go

Comment thread pkg/pro/api_client_commit.go
Matches the defensive pattern used in sendInstancesRequest to prevent
nil pointer dereference when HTTPClient is unset.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 7, 2026
Use yaml:"-" json:"-" tags so the runtime-only GITHUB_HEAD_REF env var
doesn't appear in serialized config output, which was breaking golden
snapshot tests in CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 7, 2026
@codecov

codecov Bot commented Apr 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.53097% with 66 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.26%. Comparing base (37b52c8) to head (2dddf1f).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/pro/commit.go 84.61% 16 Missing and 14 partials ⚠️
cmd/pro_commit.go 59.37% 7 Missing and 6 partials ⚠️
pkg/terminal/terminal.go 56.52% 8 Missing and 2 partials ⚠️
pkg/pro/api_client_commit.go 88.46% 3 Missing and 3 partials ⚠️
pkg/ui/formatter.go 71.42% 4 Missing ⚠️
pkg/git/safe_directory.go 85.71% 1 Missing and 1 partial ⚠️
pkg/ui/markdown/renderer.go 87.50% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2298      +/-   ##
==========================================
+ Coverage   77.19%   77.26%   +0.06%     
==========================================
  Files        1070     1074       +4     
  Lines      101556   101888     +332     
==========================================
+ Hits        78398    78719     +321     
+ Misses      18839    18826      -13     
- Partials     4319     4343      +24     
Flag Coverage Δ
unittests 77.26% <80.53%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
errors/errors.go 100.00% <ø> (ø)
pkg/config/load.go 81.49% <100.00%> (+0.02%) ⬆️
pkg/ui/markdown/renderer.go 81.53% <87.50%> (+0.33%) ⬆️
pkg/git/safe_directory.go 85.71% <85.71%> (ø)
pkg/ui/formatter.go 82.85% <71.42%> (+2.49%) ⬆️
pkg/pro/api_client_commit.go 88.46% <88.46%> (ø)
pkg/terminal/terminal.go 84.34% <56.52%> (-3.73%) ⬇️
cmd/pro_commit.go 59.37% <59.37%> (ø)
pkg/pro/commit.go 84.61% <84.61%> (ø)

... and 7 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

filepath.IsAbs("/etc/passwd") returns false on Windows since it expects
drive-letter paths. Add explicit "/" prefix check so the security
validation works cross-platform.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

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 comments (3)
pkg/pro/commit.go (3)

331-340: Consider graceful handling when file disappears between git diff and stat.

If a staged file is deleted after git diff --cached but before os.Stat, the entire commit fails. A warning-and-skip approach (like the size check) would be more resilient.

♻️ Optional: skip missing files gracefully
 info, err := os.Stat(p)
 if err != nil {
-	return nil, fmt.Errorf("failed to stat file %s: %w", p, err)
+	if os.IsNotExist(err) {
+		ui.Warning(fmt.Sprintf("Skipping %s: file no longer exists", p))
+		continue
+	}
+	return nil, fmt.Errorf("%w: %s: %w", errUtils.ErrFailedToReadFile, p, err)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/pro/commit.go` around lines 331 - 340, In the loop that iterates over
paths (where os.Stat is called for each path and compares info.Size() to
maxFileSizeBytes), change the error handling so that if os.Stat returns an
os.IsNotExist error you log a warning via ui.Warning (similar to the size-skip
message) and continue to the next path instead of returning a fatal error; for
other os.Stat errors preserve the current behavior and return the wrapped error.
Ensure you reference the same variables/functions (paths, os.Stat, ui.Warning,
maxFileSizeBytes) so the change is localized to that check.

313-316: Path traversal check is overly broad.

strings.Contains(p, "..") would reject legitimate filenames like foo..bar or config..json. Consider checking for actual path components instead.

♻️ Suggested refinement
 // Reject path traversal.
-if strings.Contains(p, "..") {
+// Check for ".." as a path component (start, middle, or end).
+normalizedPath := filepath.ToSlash(p)
+if strings.HasPrefix(normalizedPath, "../") ||
+	strings.Contains(normalizedPath, "/../") ||
+	strings.HasSuffix(normalizedPath, "/..") ||
+	normalizedPath == ".." {
 	return fmt.Errorf("%w: path traversal not allowed: %s", errUtils.ErrCommitInvalidFilePath, p)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/pro/commit.go` around lines 313 - 316, The current check uses
strings.Contains(p, "..") which falsely rejects valid names like "foo..bar";
instead normalize and inspect path components: call path.Clean (or
filepath.Clean if OS-specific) on p, ensure the cleaned path is not absolute and
then split the cleaned path into components and reject only if any component
equals "..". Update the validation in the same location that returns
errUtils.ErrCommitInvalidFilePath (the block currently using strings.Contains(p,
"..")) to perform these component-level checks and preserve the same error
return when traversal is detected.

331-345: Error wrapping should use static errors per guidelines.

The fmt.Errorf calls here use dynamic error strings without wrapping a static sentinel from errors/errors.go. This deviates from the project's error handling conventions.

♻️ Proposed fix using static errors
 info, err := os.Stat(p)
 if err != nil {
-	return nil, fmt.Errorf("failed to stat file %s: %w", p, err)
+	return nil, fmt.Errorf("%w: %s: %w", errUtils.ErrFailedToReadFile, p, err)
 }

 ...

 contents, err := os.ReadFile(p)
 if err != nil {
-	return nil, fmt.Errorf("failed to read file %s: %w", p, err)
+	return nil, fmt.Errorf("%w: %s: %w", errUtils.ErrFailedToReadFile, p, err)
 }

If ErrFailedToReadFile doesn't exist yet, you'd add it to errors/errors.go. As per coding guidelines, all errors must be wrapped using static errors.

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

In `@pkg/pro/commit.go` around lines 331 - 345, Replace the dynamic fmt.Errorf
usages in the loop that calls os.Stat and os.ReadFile with wrapping of
package-level sentinel errors (e.g. errors.ErrFailedToStatFile and
errors.ErrFailedToReadFile) per project convention: use fmt.Errorf("%w: failed
to stat file %s: %v", errors.ErrFailedToStatFile, p, err) for the os.Stat error
and fmt.Errorf("%w: failed to read file %s: %v", errors.ErrFailedToReadFile, p,
err) for the os.ReadFile error; if those sentinel errors don't exist add them to
errors/errors.go (e.g. var ErrFailedToStatFile = errors.New("failed to stat
file") and var ErrFailedToReadFile = errors.New("failed to read file")) and keep
the ui.Warning unchanged for the maxFileSizeBytes check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/pro/commit.go`:
- Around line 331-340: In the loop that iterates over paths (where os.Stat is
called for each path and compares info.Size() to maxFileSizeBytes), change the
error handling so that if os.Stat returns an os.IsNotExist error you log a
warning via ui.Warning (similar to the size-skip message) and continue to the
next path instead of returning a fatal error; for other os.Stat errors preserve
the current behavior and return the wrapped error. Ensure you reference the same
variables/functions (paths, os.Stat, ui.Warning, maxFileSizeBytes) so the change
is localized to that check.
- Around line 313-316: The current check uses strings.Contains(p, "..") which
falsely rejects valid names like "foo..bar"; instead normalize and inspect path
components: call path.Clean (or filepath.Clean if OS-specific) on p, ensure the
cleaned path is not absolute and then split the cleaned path into components and
reject only if any component equals "..". Update the validation in the same
location that returns errUtils.ErrCommitInvalidFilePath (the block currently
using strings.Contains(p, "..")) to perform these component-level checks and
preserve the same error return when traversal is detected.
- Around line 331-345: Replace the dynamic fmt.Errorf usages in the loop that
calls os.Stat and os.ReadFile with wrapping of package-level sentinel errors
(e.g. errors.ErrFailedToStatFile and errors.ErrFailedToReadFile) per project
convention: use fmt.Errorf("%w: failed to stat file %s: %v",
errors.ErrFailedToStatFile, p, err) for the os.Stat error and fmt.Errorf("%w:
failed to read file %s: %v", errors.ErrFailedToReadFile, p, err) for the
os.ReadFile error; if those sentinel errors don't exist add them to
errors/errors.go (e.g. var ErrFailedToStatFile = errors.New("failed to stat
file") and var ErrFailedToReadFile = errors.New("failed to read file")) and keep
the ui.Warning unchanged for the maxFileSizeBytes check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 04414076-5ea9-46bc-86ac-8119fa0cab0e

📥 Commits

Reviewing files that changed from the base of the PR and between 611f078 and d871abb.

📒 Files selected for processing (1)
  • pkg/pro/commit.go

coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 7, 2026
Add unit tests for previously uncovered commit.go functions:
resolveBranch, validateChangeCount, buildDeletions, stageFiles,
detectChanges, gitDiffCachedNames, and buildChanges. Uses isolated
temp git repos for git-dependent tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@atmos-pro

atmos-pro Bot commented Apr 9, 2026 •

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More.

…ro commit`

Move autofix logic to atmos-pro.yaml, using atmos toolchain for gofumpt
and `atmos pro commit` for server-side commits via GitHub App. Stub
autofix.yml to a no-op pointing to Atmos Pro.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mergify

mergify Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Important

Cloud Posse Engineering Team Review Required

This pull request modifies files that require Cloud Posse's review. Please be patient, and a core maintainer will review your changes.

To expedite this process, reach out to us on Slack in the #pr-reviews channel.

Use ghcr.io/cloudposse/atmos:latest container image so atmos is on PATH.
Install pre-commit via Debian package instead of pip.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Snapshots were stale after log badge padding change (Padding 0,2 → 0,1)
and error H1 box-style rendering update.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The shouldRenderStyled() change enables Glamour markdown rendering
when CI=true, which breaks tests asserting on literal markdown text.
Fix by setting NO_COLOR=1 in affected tests and using the formatter's
own terminal color profile instead of lipgloss's process-wide singleton.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
regex101.com intermittently returns 504 Gateway Timeout in CI,
causing the Check Markdown Links job to fail.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test hardcoded "/tmp/test-workspace" which becomes
"\tmp\test-workspace" on Windows after filepath.Clean, causing
the Contains assertion to fail. Use t.TempDir() instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of regenerating snapshots to match CI-styled output, set
NO_COLOR=1 on all log-level validation tests for consistent plain
text output across environments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aknysh
Andriy Knysh (aknysh) merged commit 54d7318 into main Apr 12, 2026
61 checks passed
@aknysh
Andriy Knysh (aknysh) deleted the osterman/pro-commit-cmd branch April 12, 2026 22:16
@atmos-pro

atmos-pro Bot commented Apr 12, 2026

Copy link
Copy Markdown
Contributor

Note

Atmos Pro  

Waiting for your GitHub Actions workflow to upload affected stacks.
Learn More.

@mergify mergify Bot removed the needs-cloudposse Needs Cloud Posse assistance label Apr 12, 2026
@atmos-pro

atmos-pro Bot commented Apr 12, 2026

Copy link
Copy Markdown
Contributor

Note

Atmos Pro  

Waiting for your GitHub Actions workflow to upload affected stacks.
Learn More.

@github-actions

Copy link
Copy Markdown

Warning

Release Documentation Required

This PR is labeled minor or major and requires documentation updates:

  • Changelog entry - Add a blog post in website/blog/YYYY-MM-DD-feature-name.mdx
  • Roadmap update - Update website/src/data/roadmap.js with the new milestone

Alternatively: If this change doesn't require release documentation, remove the minor or major label.

@github-actions

Copy link
Copy Markdown

These changes were released in v1.215.0-rc.8.

This branch was successfully deployed

1 active deployment
preview — 2dddf1f1 Deployed Apr 12, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor New features that do not break anything size/l Large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants