Skip to content

Conversation

altitude
Copy link
Member

No description provided.

Copy link

coderabbitai bot commented Apr 23, 2025

Walkthrough

CreateLedger was made fully transactional (RunInTx) covering system-store insert, bucket init/migrate/add, and ledger-store creation; many Antithesis Earthfiles, workload binaries, helper scripts, and Justfiles were added; a minor import reorder occurred in a generator tool.

Changes

Cohort / File(s) Summary
Driver: transactional CreateLedger
internal/storage/driver/driver.go
CreateLedger now runs inside RunInTx: system-store insert with constraint mapping, bucket retrieval via bucketFactory, bucket init/migrate or AddLedger with an assert on outdated buckets, and ledger-store creation; doc comment and imports expanded.
Client Earth build
pkg/client/Earthfile
New Earthfile defining a minimal multi-stage artifact build for client sources.
Antithesis image build & entrypoint
test/antithesis/image/Earthfile, test/antithesis/image/entrypoint.sh
New Earthfile with compile+runtime stages (instrumentation, build, runtime image) and an entrypoint that waits for Postgres readiness then runs ledger serve.
Antithesis workload build
test/antithesis/workload/Earthfile
New Earthfile building instrumented workload binaries (sources → compile → build) and pushing the workload image with tagging.
Workload utilities
test/antithesis/workload/internal/utils.go
New helper utilities: client creation with retry/backoff, random data, assertion helpers, ledger create/list/random selection, and server-error classification.
Workload binaries / commands
test/antithesis/workload/bin/init/main.go, .../bin/cmds/first_default_ledger/main.go, .../parallel_driver_ledger_create/main.go, .../parallel_driver_transactions/main.go, .../eventually_correct/main.go, .../anytime_version_upgrade/main.go
Multiple new executables: readiness init loop, create-default ledger, parallel ledger-create, parallel transaction submissions, eventual balances checker, and a version-upgrade placeholder.
Workload datagen
test/antithesis/workload/datagen/main.go
New simple datagen program: Account() returns a random account string; defines Sequence type.
Build orchestration / Justfiles
test/antithesis/Justfile, test/antithesis/config/Justfile
New Justfile targets to build/push manifests and images and to launch Antithesis experiments (run-6min, run-1h, requirements-build, build-manifest, push).
Imports tidy
tools/generator/cmd/root.go
Reordered standard-library imports; no logic changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller as API/Service
  participant Driver as Driver.CreateLedger
  participant DB as DB (RunInTx)
  participant Sys as SystemStore
  participant BF as BucketFactory
  participant B as Bucket
  participant LSF as LedgerStoreFactory

  Caller->>Driver: CreateLedger(ctx, ledger)
  Driver->>DB: RunInTx(ctx, fn)
  alt transaction
    DB->>Sys: CreateLedger(record)
    Sys-->>DB: success / constraint violation
    DB->>BF: Create(bucketName)
    BF-->>DB: bucket instance
    DB->>B: IsInitialized?
    alt initialized
      DB->>B: IsUpToDate?
      alt outdated
        note right of B: assert.AlwaysOrUnreachable -> ErrBucketOutdated
        DB-->>Driver: rollback + error
      else up-to-date
        DB->>B: AddLedger(l)
      end
    else not initialized
      DB->>B: Migrate()
    end
    DB->>LSF: CreateStore(bucket, ledger)
    LSF-->>DB: LedgerStore
    DB-->>Driver: commit with store
  else error
    DB-->>Driver: rollback with error
  end
  Driver-->>Caller: store or resolved error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

build-images

Suggested reviewers

  • Dav-14
  • flemzord

Pre-merge checks (3 warnings)

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The pull request title “feat: add DST setup” does not reflect any of the changes made in this changeset, which include transactional ledger handling, multiple Earthfiles for client and test workloads, and new testing utilities rather than any DST setup. It is misleading and fails to summarize the main objective of the PR. Update the title to clearly and concisely summarize the primary change, for example “feat: wrap CreateLedger in transaction with bucket migration” or another phrase that captures the core feature introduced by this PR.
Description Check ⚠️ Warning The pull request currently has no description to explain the extensive changes to transactional ledger creation, Earthfiles, and test workloads, leaving reviewers without context. As a result, the PR lacks a summary of its purpose and scope. Provide a concise description outlining the high-level objectives and scope of the changes, such as introducing transactional guarantees for CreateLedger, adding new Earthfiles for Antithesis workflows, and supplying test utilities for parallel workloads.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.

Poem

In burrows of bytes I hop and compile,
Ledgers commit in one transactional mile.
Buckets migrate, workloads take flight,
Earthly builds hum through day and night.
A rabbit cheers—no rollback tonight! 🐇✨

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between af6956b and d2d57b7.

⛔ Files ignored due to path filters (2)
  • .github/workflows/antithesis.yml is excluded by !**/*.yml
  • test/antithesis/config/manifests/workload.yaml is excluded by !**/*.yaml
📒 Files selected for processing (1)
  • test/antithesis/workload/bin/cmds/anytime_version_upgrade/main.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/antithesis/workload/bin/cmds/anytime_version_upgrade/main.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tests
  • GitHub Check: Dirty
  • GitHub Check: Push Images to Antithesis
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/dst

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.

Copy link

codecov bot commented Apr 23, 2025

Codecov Report

❌ Patch coverage is 35.29412% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.59%. Comparing base (9c59658) to head (d2d57b7).
⚠️ Report is 13 commits behind head on main.

Files with missing lines Patch % Lines
internal/storage/driver/driver.go 35.29% 11 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #879      +/-   ##
==========================================
- Coverage   80.62%   80.59%   -0.03%     
==========================================
  Files         186      186              
  Lines       10207    10324     +117     
==========================================
+ Hits         8229     8321      +92     
- Misses       1559     1580      +21     
- Partials      419      423       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@altitude altitude marked this pull request as ready for review April 23, 2025 15:01
@altitude altitude requested a review from a team as a code owner April 23, 2025 15:01
Copy link

@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: 9

🧹 Nitpick comments (11)
test/antithesis/workload/bin/init/main.go (1)

28-34: Consider adding a log message for setup completion and shutdown.

Adding log messages would make it easier to understand the program flow, especially when running in containerized environments where logs are a primary debugging tool.

 	lifecycle.SetupComplete(map[string]any{})
+	fmt.Println("Setup complete, waiting for termination signal")
 
 	sigs := make(chan os.Signal, 1)
 	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
 
 	<-sigs
+	fmt.Println("Received termination signal, shutting down")
 }
test/antithesis/image/entrypoint.sh (1)

11-11: Add proper signal handling for container graceful shutdown.

The script should properly handle termination signals to ensure clean shutdown of the ledger service.

 echo "Postgres is ready; serving ledger!"
 
+# Handle termination signals
+trap 'echo "Received termination signal. Shutting down..."; kill -TERM $child_pid 2>/dev/null; exit 0' SIGTERM SIGINT
+
 ledger serve
+child_pid=$!
+wait $child_pid
test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go (3)

69-71: Improve error handling consistency.

The error handling here doesn't provide specific error details in the assertion, unlike other error checks in the code.

-	if !internal.AssertAlwaysErrNil(err, "we should be able to query account 'world'", nil) {
+	if !internal.AssertAlwaysErrNil(err, "we should be able to query account 'world'", internal.Details{
+		"error": err,
+		"ledger": ledger,
+	}) {
 		return
 	}

19-31: Consider adding ledger cleanup after test completion.

The code creates a new ledger for each test run but doesn't clean it up afterward, which could lead to resource accumulation over time.

Add a defer statement after the ledger creation:

 	ledger := fmt.Sprintf("ledger-%d", internal.RandomBigInt().Int64())
 
+	// Ensure ledger cleanup after test
+	defer func() {
+		cleanupErr := internal.DeleteLedger(ctx, client, ledger)
+		if cleanupErr != nil {
+			fmt.Printf("Warning: Failed to clean up ledger %s: %v\n", ledger, cleanupErr)
+		}
+	}()
+
 	err := internal.CreateLedger(

Note: This assumes there's a DeleteLedger function in the internal package. If not, you would need to implement it.


58-61: Simplify conditional check.

The cond variable is only used once, making it unnecessary.

-	cond := !hasError.Load()
-	if assert.Always(cond, "all transactions should have been written", internal.Details{}); !cond {
+	if assert.Always(!hasError.Load(), "all transactions should have been written", internal.Details{}); hasError.Load() {
 		return
 	}
test/antithesis/workload/Earthfile (3)

43-45: Add filtering for Go files in the build loop.

The current loop builds all directories in bin/cmds without filtering. If there are non-Go files or directories without main packages, this could cause build failures.

-    FOR file IN $(ls bin/cmds)
-        RUN go build -o /out/cmds/$file ./bin/cmds/$file
+    FOR file IN $(find bin/cmds -type d -not -path "bin/cmds" | xargs -n1 basename)
+        # Check if the directory contains a main.go file before building
+        RUN if [ -f ./bin/cmds/$file/main.go ]; then \
+                go build -o /out/cmds/$file ./bin/cmds/$file; \
+            fi
     END

56-56: Consider providing documentation for the required ANTITHESIS_REPOSITORY argument.

The ANTITHESIS_REPOSITORY argument is required but has no documentation or validation on format.

-    ARG --required ANTITHESIS_REPOSITORY
+    # ANTITHESIS_REPOSITORY should be in the format of "registry.example.com/project"
+    ARG --required ANTITHESIS_REPOSITORY
+    # Validate repository format
+    RUN if ! echo "${ANTITHESIS_REPOSITORY}" | grep -q "^[a-zA-Z0-9][a-zA-Z0-9.-]*\.[a-zA-Z0-9][a-zA-Z0-9.-]*/[a-zA-Z0-9_/-]*$"; then \
+           echo "Invalid repository format: ${ANTITHESIS_REPOSITORY}"; \
+           echo "Expected format: registry.example.com/project"; \
+           exit 1; \
+        fi

15-26: Consider uncommenting the 'tidy' section or add a comment explaining why it's commented out.

The 'tidy' section is commented out but might be useful for maintaining clean go.mod files.

Either uncomment the section if it's meant to be used, or add a comment explaining why it's disabled:

-# tidy:
-#     FROM +sources
-#
-#     CACHE --id go-mod-cache /go/pkg/mod
-#     CACHE --id go-cache /root/.cache/go-build
-#
-#     WORKDIR /src/test/antithesis/workload
-#     RUN go mod tidy
-#
-#     SAVE ARTIFACT go.mod AS LOCAL go.mod
-#     SAVE ARTIFACT go.sum AS LOCAL go.sum
+# The 'tidy' target is currently disabled because module maintenance is handled in the main repository.
+# Uncomment and use this section when updates to go.mod/go.sum are needed:
+# tidy:
+#     FROM +sources
+#
+#     CACHE --id go-mod-cache /go/pkg/mod
+#     CACHE --id go-cache /root/.cache/go-build
+#
+#     WORKDIR /src/test/antithesis/workload
+#     RUN go mod tidy
+#
+#     SAVE ARTIFACT go.mod AS LOCAL go.mod
+#     SAVE ARTIFACT go.sum AS LOCAL go.sum
test/antithesis/Earthfile (1)

7-26: Review experiment parameters

The experiment duration is set to 0.1, which might be very short for meaningful testing. Consider whether this is intended as a minimum value for quick tests or if a longer duration would be more appropriate for thorough testing.

Also verify that the fixed image names (antithesis-config:latest, ledger:latest, workload:latest) match your intended tagging strategy.

test/antithesis/workload/internal/utils.go (2)

40-56: Hardcoded service endpoint

The client is configured with a hardcoded URL http://gateway:8080. Consider making this configurable via environment variables for better flexibility across different environments.

+func NewClientWithURL(serverURL string) *client.Formance {
+	if serverURL == "" {
+		serverURL = "http://gateway:8080" // Default URL
+	}
+	return client.New(
+		client.WithServerURL(serverURL),
+		client.WithClient(&http.Client{
+			Timeout: time.Minute,
+		}),
+		client.WithRetryConfig(retry.Config{
+			Strategy: "backoff",
+			Backoff: &retry.BackoffStrategy{
+				InitialInterval: 200,
+				Exponent:        1.5,
+				MaxElapsedTime:  4000,
+			},
+			RetryConnectionErrors: true,
+		}),
+	)
+}
+
 func NewClient() *client.Formance {
-	return client.New(
-		client.WithServerURL("http://gateway:8080"),
-		client.WithClient(&http.Client{
-			Timeout: time.Minute,
-		}),
-		client.WithRetryConfig(retry.Config{
-			Strategy: "backoff",
-			Backoff: &retry.BackoffStrategy{
-				InitialInterval: 200,
-				Exponent:        1.5,
-				MaxElapsedTime:  4000,
-			},
-			RetryConnectionErrors: true,
-		}),
-	)
+	return NewClientWithURL("")
 }

66-82: Transaction function with hardcoded values

The RunTx function uses hardcoded values for asset ("USD/2") and source account ("world"). Consider making these configurable parameters to support testing different asset types and account structures.

-func RunTx(ctx context.Context, client *client.Formance, amount *big.Int, ledger string) error {
+func RunTx(ctx context.Context, client *client.Formance, amount *big.Int, ledger string, asset string, source string) error {
+	if asset == "" {
+		asset = "USD/2" // Default asset
+	}
+	if source == "" {
+		source = "world" // Default source
+	}
 	orderID := fmt.Sprint(int64(math.Abs(float64(random.GetRandom()))))
 	dest := fmt.Sprintf("orders:%s", orderID)

 	_, err := client.Ledger.V2.CreateTransaction(ctx, operations.V2CreateTransactionRequest{
 		V2PostTransaction: components.V2PostTransaction{
 			Postings: []components.V2Posting{{
 				Amount:      amount,
-				Asset:       "USD/2",
+				Asset:       asset,
 				Destination: dest,
-				Source:      "world",
+				Source:      source,
 			}},
 		},
 		Ledger: ledger,
 	})
 	return err
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b718a3 and 0919823.

⛔ Files ignored due to path filters (6)
  • .github/workflows/antithesis.yml is excluded by !**/*.yml
  • go.mod is excluded by !**/*.mod
  • go.sum is excluded by !**/*.sum, !**/*.sum
  • test/antithesis/config/docker-compose.yml is excluded by !**/*.yml
  • test/antithesis/workload/go.mod is excluded by !**/*.mod
  • test/antithesis/workload/go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (13)
  • internal/controller/system/controller.go (1 hunks)
  • internal/storage/driver/driver.go (4 hunks)
  • pkg/client/Earthfile (1 hunks)
  • test/antithesis/Earthfile (1 hunks)
  • test/antithesis/config/Earthfile (1 hunks)
  • test/antithesis/config/gateway/Caddyfile (1 hunks)
  • test/antithesis/image/Earthfile (1 hunks)
  • test/antithesis/image/entrypoint.sh (1 hunks)
  • test/antithesis/workload/Earthfile (1 hunks)
  • test/antithesis/workload/bin/cmds/eventually_aggregated_volumes_should_be_zeroed/main.go (1 hunks)
  • test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go (1 hunks)
  • test/antithesis/workload/bin/init/main.go (1 hunks)
  • test/antithesis/workload/internal/utils.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
test/antithesis/workload/bin/init/main.go (2)
test/antithesis/workload/internal/utils.go (1)
  • NewClient (40-56)
internal/api/v1/controllers_config.go (1)
  • GetInfo (36-66)
test/antithesis/workload/internal/utils.go (5)
pkg/client/formance.go (3)
  • Formance (70-74)
  • WithClient (108-112)
  • WithRetryConfig (130-134)
pkg/client/models/operations/v2createledger.go (1)
  • V2CreateLedgerRequest (9-13)
pkg/client/models/operations/v2createtransaction.go (1)
  • V2CreateTransactionRequest (9-23)
pkg/client/models/components/v2posttransaction.go (1)
  • V2PostTransaction (29-36)
pkg/client/models/components/v2posting.go (1)
  • V2Posting (10-15)
🪛 GitHub Check: codecov/patch
internal/storage/driver/driver.go

[warning] 84-95: internal/storage/driver/driver.go#L84-L95
Added lines #L84 - L95 were not covered by tests

🔇 Additional comments (15)
internal/controller/system/controller.go (1)

4-7: Import reordering looks good.

The standard library packages reflect and time have been moved to the top of the import block, following common Go style conventions where standard library imports are grouped together at the top.

pkg/client/Earthfile (1)

1-8: Clean Earthfile setup for the client component.

This is a good minimal Earthfile that creates a client artifact from the current directory. Using a scratch image keeps the build artifact lightweight. The golang version (1.22.2) is appropriately recent.

test/antithesis/config/gateway/Caddyfile (1)

1-12: Appropriate reverse proxy configuration for local development.

The Caddy configuration correctly sets up a reverse proxy to ledger services. The auto_https off and debug options are suitable for a development environment. The load balancing policy of "first" will try the first available backend before attempting the second one.

Just note that this configuration is explicitly for development and would need security enhancements (enabling HTTPS, etc.) for any production deployment.

test/antithesis/config/Earthfile (1)

1-11: Straightforward config build setup.

The Earthfile correctly configures a build process to create a config image containing the docker-compose file and gateway directory. The use of --no-manifest-list is appropriate when targeting a specific platform.

test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go (1)

35-43: 🛠️ Refactor suggestion

Fix potential race condition with totalAmount.

There's a potential race condition in how you're calculating the total amount. While the code is adding to totalAmount before submitting the tasks to the pool, this pattern is prone to bugs if the code is modified later.

 	hasError := atomic.NewBool(false)
-	totalAmount := big.NewInt(0)
+	// Track individual amounts for safer aggregation
+	amounts := make([]*big.Int, count)
 
 	pool := pond.New(10, 10e3)
 
 	for i := 0; i < count; i++ {
 		amount := internal.RandomBigInt()
-		totalAmount = totalAmount.Add(totalAmount, amount)
+		amounts[i] = amount
 		pool.Submit(func() {

Then after line 56 (after pool.StopAndWait()):

 	pool.StopAndWait()
 
+	// Calculate total after all tasks are submitted
+	totalAmount := big.NewInt(0)
+	for _, amount := range amounts {
+		totalAmount = totalAmount.Add(totalAmount, amount)
+	}
+
 	cond := !hasError.Load()

Likely an incorrect or invalid review comment.

internal/storage/driver/driver.go (3)

10-10: Great addition of the Antithesis SDK

The import of the Antithesis assert package is appropriate for the new assertion functionality added in this file.


40-53: Excellent documentation improvement

Adding this comprehensive doc comment significantly improves code clarity. The step-by-step explanation of the ledger creation process and transaction handling makes the complex functionality much easier to understand for developers.


57-59: Good inline comments

These explanatory comments help track the progress through the ledger creation process and align with the high-level documentation. This makes the code much more maintainable.

Also applies to: 62-63, 108-109, 114-115, 119-120

test/antithesis/Earthfile (3)

1-6: LGTM - Base configuration setup

The Earthfile version and imports look correct. Using a tagged version of the core Earthly configuration is a good practice for stability.


27-52: Good debugging setup

The debugging target properly handles session parameters and shows them for verification before submission. The approach of creating a temporary JSON file for the parameters is clean and maintainable.


53-58: LGTM - Build dependencies handled correctly

The requirements-build target properly passes arguments to the component build targets and enforces the required ANTITHESIS_REPOSITORY argument.

test/antithesis/image/Earthfile (1)

7-9: Good cache configuration

Using shared caching for Go modules and build artifacts is an excellent practice that will speed up builds.

test/antithesis/workload/internal/utils.go (3)

19-26: Good utility type and random function

The Details type and RandomBigInt function are well-implemented. The random function correctly converts the random value to a big integer for use in financial transactions.


28-38: Helpful assertion wrappers

These wrapper functions around the Antithesis assertion library provide a consistent interface for making assertions throughout the workload code. The return values allow for fluent usage in conditional blocks.


58-64: Simple, focused ledger creation function

This function provides a clean wrapper around the client API for creating ledgers, making the workload code more readable.

Comment on lines 14 to 27
func main() {
ctx := context.Background()
client := internal.NewClient()

for {
time.Sleep(time.Second)

_, err := client.Ledger.GetInfo(ctx)
if err != nil {
continue
}
break
}

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding error logging and timeout to service readiness loop.

The polling mechanism to check for service readiness is a good approach, but it currently lacks error logging and a timeout mechanism. Without these, it could poll indefinitely if the service never becomes available, and debugging could be difficult if there are connection issues.

 func main() {
 	ctx := context.Background()
 	client := internal.NewClient()
+
+	maxAttempts := 60 // 1 minute timeout with 1-second intervals
+	attempt := 0
 
 	for {
+		attempt++
 		time.Sleep(time.Second)
 
 		_, err := client.Ledger.GetInfo(ctx)
 		if err != nil {
+			if attempt >= maxAttempts {
+				panic("Timed out waiting for ledger service to be ready")
+			}
+			fmt.Printf("Waiting for ledger service to be ready (attempt %d/%d): %v\n", attempt, maxAttempts, err)
 			continue
 		}
+		fmt.Println("Ledger service is ready")
 		break
 	}

Don't forget to add the fmt import:

 import (
 	"context"
+	"fmt"
 	"os"
 	"os/signal"
 	"syscall"
 	"time"
📝 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.

Suggested change
func main() {
ctx := context.Background()
client := internal.NewClient()
for {
time.Sleep(time.Second)
_, err := client.Ledger.GetInfo(ctx)
if err != nil {
continue
}
break
}
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
ctx := context.Background()
client := internal.NewClient()
maxAttempts := 60 // 1 minute timeout with 1-second intervals
attempt := 0
for {
attempt++
time.Sleep(time.Second)
_, err := client.Ledger.GetInfo(ctx)
if err != nil {
if attempt >= maxAttempts {
panic("Timed out waiting for ledger service to be ready")
}
fmt.Printf("Waiting for ledger service to be ready (attempt %d/%d): %v\n", attempt, maxAttempts, err)
continue
}
fmt.Println("Ledger service is ready")
break
}
// rest of your shutdown or signal handling logic…
}

Comment on lines 3 to 7
until pg_isready -d ledger -h 10.0.29.16 -U ledger
do
echo "Waiting for postgres at: $POSTGRES_URI"
sleep 2;
done
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Make database connection parameters configurable via environment variables.

The script uses hardcoded database host, name, and user values. This reduces portability and makes configuration changes difficult.

-until pg_isready -d ledger -h 10.0.29.16 -U ledger
+# Extract parameters from POSTGRES_URI or use default values
+PG_HOST=${PG_HOST:-10.0.29.16}
+PG_DB=${PG_DB:-ledger}
+PG_USER=${PG_USER:-ledger}
+
+until pg_isready -d "$PG_DB" -h "$PG_HOST" -U "$PG_USER"
📝 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.

Suggested change
until pg_isready -d ledger -h 10.0.29.16 -U ledger
do
echo "Waiting for postgres at: $POSTGRES_URI"
sleep 2;
done
# Extract parameters from environment or fall back to existing defaults
PG_HOST=${PG_HOST:-10.0.29.16}
PG_DB=${PG_DB:-ledger}
PG_USER=${PG_USER:-ledger}
until pg_isready -d "$PG_DB" -h "$PG_HOST" -U "$PG_USER"
do
echo "Waiting for postgres at: $POSTGRES_URI"
sleep 2;
done

🛠️ Refactor suggestion

Add a timeout to prevent infinite waiting.

The current loop will wait indefinitely if PostgreSQL never becomes available.

+MAX_RETRIES=30
+retry_count=0
+
 until pg_isready -d ledger -h 10.0.29.16 -U ledger
 do
+  retry_count=$((retry_count+1))
+  if [ $retry_count -ge $MAX_RETRIES ]; then
+    echo "ERROR: PostgreSQL not available after $MAX_RETRIES attempts. Exiting."
+    exit 1
+  fi
   echo "Waiting for postgres at: $POSTGRES_URI"
   sleep 2;
 done
📝 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.

Suggested change
until pg_isready -d ledger -h 10.0.29.16 -U ledger
do
echo "Waiting for postgres at: $POSTGRES_URI"
sleep 2;
done
MAX_RETRIES=30
retry_count=0
until pg_isready -d ledger -h 10.0.29.16 -U ledger
do
retry_count=$((retry_count+1))
if [ $retry_count -ge $MAX_RETRIES ]; then
echo "ERROR: PostgreSQL not available after $MAX_RETRIES attempts. Exiting."
exit 1
fi
echo "Waiting for postgres at: $POSTGRES_URI"
sleep 2;
done

Comment on lines 26 to 30
for asset, volumes := range aggregated.V2AggregateBalancesResponse.Data {
assert.Always(volumes.Cmp(new(big.Int)) == 0, "aggregated volumes for asset "+asset+" should be 0", internal.Details{
"error": err,
})
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix incorrect error reference in assertion details.

In the assertion for each asset's volume, you're referencing the err variable that has already been checked to be nil earlier in the code. This is misleading and could cause confusion.

 for asset, volumes := range aggregated.V2AggregateBalancesResponse.Data {
-	assert.Always(volumes.Cmp(new(big.Int)) == 0, "aggregated volumes for asset "+asset+" should be 0", internal.Details{
-		"error": err,
-	})
+	assert.Always(volumes.Cmp(new(big.Int)) == 0, "aggregated volumes for asset "+asset+" should be 0", internal.Details{
+		"asset": asset,
+		"value": volumes.String(),
+	})
 }
📝 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.

Suggested change
for asset, volumes := range aggregated.V2AggregateBalancesResponse.Data {
assert.Always(volumes.Cmp(new(big.Int)) == 0, "aggregated volumes for asset "+asset+" should be 0", internal.Details{
"error": err,
})
}
for asset, volumes := range aggregated.V2AggregateBalancesResponse.Data {
assert.Always(volumes.Cmp(new(big.Int)) == 0, "aggregated volumes for asset "+asset+" should be 0", internal.Details{
"asset": asset,
"value": volumes.String(),
})
}

Comment on lines 12 to 24
func main() {
ctx := context.Background()
client := internal.NewClient()

aggregated, err := client.Ledger.V2.GetBalancesAggregated(ctx, operations.V2GetBalancesAggregatedRequest{
Ledger: "default",
})
if err != nil {
assert.Always(err == nil, "error getting aggregated balances", internal.Details{
"error": err,
})
return
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement retry mechanism for eventual consistency check.

The filename includes "eventually_" which suggests this check should be retried until it succeeds or times out. Currently, it only checks once without any retry logic.

 func main() {
 	ctx := context.Background()
 	client := internal.NewClient()
+
+	// Add retry with timeout for eventual consistency
+	const maxRetries = 30
+	const retryDelayMs = 500
+	
+	var lastErr error
+	for i := 0; i < maxRetries; i++ {
+		success := tryCheck(ctx, client)
+		if success {
+			return // Success case exits cleanly
+		}
+		
+		// Wait before retry
+		time.Sleep(time.Duration(retryDelayMs) * time.Millisecond)
+	}
+	
+	// If we get here, we've exceeded our retry attempts
+	assert.Always(false, "aggregated volumes not zeroed after maximum retries", internal.Details{})
+}
+
+func tryCheck(ctx context.Context, client *internal.Client) bool {

Further down, you would need to modify the end of the function to return a boolean success value instead of just returning:

 	if err != nil {
 		assert.Always(err == nil, "error getting aggregated balances", internal.Details{
 			"error": err,
 		})
-		return
+		return false
 	}
 
 	for asset, volumes := range aggregated.V2AggregateBalancesResponse.Data {
-		assert.Always(volumes.Cmp(new(big.Int)) == 0, "aggregated volumes for asset "+asset+" should be 0", internal.Details{
-			"error": err,
-		})
+		if volumes.Cmp(new(big.Int)) != 0 {
+			return false
+		}
 	}
+	return true
 }

Committable suggestion skipped: line range outside the PR's diff.

CACHE --id go-mod-cache /go/pkg/mod
CACHE --id go-cache /root/.cache/go-build

RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Pin the version of antithesis-go-instrumentor tool.

Using @latest for tools can lead to inconsistent builds and unexpected breaking changes. Consider pinning to a specific version.

-    RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
+    RUN go install github.com/antithesishq/antithesis-sdk-go/tools/[email protected]  # Use appropriate version
📝 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.

Suggested change
RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@v0.5.2 # Use appropriate version

Comment on lines 84 to 95
assert.AlwaysOrUnreachable(
// @todo: replace this with a proper flag detailing wether we're
// operating a new version of the binary or not.
// if we are, we are definitely expecting this to happen.
// if we're not, this should be unreachable.
false,
"Bucket is outdated",
map[string]any{
"bucket": l.Bucket,
},
)

Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Todo indicates temporary assertion pattern

The assertion is using false as the condition, which means it will always trigger as "unreachable". The TODO comments indicate this is a temporary solution, but this code path isn't covered by tests.

Consider:

  1. Adding a proper flag parameter as mentioned in the TODO
  2. Adding tests to cover this code path

🏁 Script executed:

#!/bin/bash
# Check if there are any tests covering this assertion in the driver code
rg -A 5 -B 5 "test.*Bucket.*Outdated" --type go

Length of output: 49


To be certain this assertion path is untested, let’s list any driver test files and search them for AlwaysOrUnreachable:


🏁 Script executed:

#!/bin/bash
# List Go test files in internal/storage/driver
fd -e go --glob '*_test.go' internal/storage/driver

# Search those test files for AlwaysOrUnreachable
rg "AlwaysOrUnreachable" -n --glob "internal/storage/driver/*_test.go"

Length of output: 348


Ensure proper flag control and add tests for the unreachable assertion

The assert.AlwaysOrUnreachable(false, …) at internal/storage/driver/driver.go:84–95 is built to always fire, and our search shows no tests reference AlwaysOrUnreachable to cover this path. To address:

• Introduce the boolean flag referenced in the TODO (e.g. isNewBinaryVersion) instead of hard‑coding false.
• Add unit tests in internal/storage/driver/driver_test.go (or a new test) that cover both cases:

  • Flag = true (assertion bypassed)
  • Flag = false (assertion triggers)

This will satisfy the TODO and ensure the code path is exercised.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 84-95: internal/storage/driver/driver.go#L84-L95
Added lines #L84 - L95 were not covered by tests

Comment on lines 10 to 28
compile:
FROM --platform=linux/amd64 golang:1.23
CACHE --sharing=shared --id go-mod-cache /go/pkg/mod
CACHE --sharing=shared --id go-cache /root/.cache/go-build

COPY ../../..+sources/src /src
RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
WORKDIR /src

RUN go mod download
RUN mkdir -p /ledger_instrumented
RUN /go/bin/antithesis-go-instrumentor . /ledger_instrumented

WORKDIR /ledger_instrumented/customer
RUN go build -race -o ledger

SAVE ARTIFACT /ledger_instrumented/customer/ledger
SAVE ARTIFACT /ledger_instrumented/symbols

Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Check Go version

The compile stage uses golang:1.23, but the latest stable Go version is 1.22.x. This appears to be a non-existent future version.

-    FROM --platform=linux/amd64 golang:1.23
+    FROM --platform=linux/amd64 golang:1.22

🌐 Web query:

What is the latest stable version of Go?

💡 Result:

The latest stable version of Go as of April 2025 is Go 1.24.2, released on April 1, 2025[1][2]. This release includes security fixes to the net/http package as well as various bug fixes to the compiler, runtime, go command, and several libraries such as crypto/tls, go/types, net/http, and testing[2].

Citations:


Bump Go base image to latest stable (1.24)

The compile stage currently uses an outdated Go image (golang:1.23), but the latest stable release as of April 2025 is Go 1.24.2. To include the latest security and bug fixes, please update the Dockerfile:

File: test/antithesis/image/Earthfile
Lines: 10–28

-    FROM --platform=linux/amd64 golang:1.23
+    FROM --platform=linux/amd64 golang:1.24

If you prefer explicit patch‑level pinning, you can use:

FROM --platform=linux/amd64 golang:1.24.2

[1] https://go.dev/dl/
[2] https://go.dev/doc/devel/release

Comment on lines 29 to 44
build:
FROM --platform=linux/amd64 ubuntu:latest
RUN apt-get update -y && apt-get install -y postgresql-client curl

COPY (+compile/ledger) /bin/ledger
COPY (+compile/symbols) /symbols
COPY entrypoint.sh /bin/entrypoint.sh

RUN chmod 777 /bin/entrypoint.sh

ENTRYPOINT ["/bin/entrypoint.sh"]
EXPOSE 8080

ARG --required ANTITHESIS_REPOSITORY

SAVE IMAGE --push --no-manifest-list "${ANTITHESIS_REPOSITORY}/ledger:latest"
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Consider version tagging strategy

The image is currently saved with the tag ledger:latest. While this works for simple workflows, consider adding a versioning strategy (like using git tags, commits, or timestamps) for better traceability in production environments.

Also verify that the entrypoint script has appropriate error handling for graceful failures.


🏁 Script executed:

#!/bin/bash
# Check entrypoint script for error handling patterns
cat test/antithesis/image/entrypoint.sh | grep -E 'set -e|exit [1-9]||| exit [1-9]'

Length of output: 316


Implement image version tagging and enhance entrypoint error handling

To improve traceability and robustness, please make the following updates:

• In test/antithesis/image/Earthfile
– Replace the hard‑coded ledger:latest tag with a parameterized/versioned tag (e.g. from a Git tag or commit SHA).
– Example diff:

 ARG --required ANTITHESIS_REPOSITORY
-  SAVE IMAGE --push --no-manifest-list "${ANTITHESIS_REPOSITORY}/ledger:latest"
+  ARG IMAGE_TAG?=$(git describe --tags --always)
+  SAVE IMAGE --push --no-manifest-list "${ANTITHESIS_REPOSITORY}/ledger:${IMAGE_TAG}"

• In test/antithesis/image/entrypoint.sh
– Add strict error handling so failures inside the script halt the container startup.
– Example diff at top of the file:

#!/bin/sh
+ set -euo pipefail
+ trap 'echo "⛔ Entrypoint failed on line $LINENO"; exit 1' ERR
# make sure pg is ready to accept connections
until pg_isready -d ledger -h 10.0.29.16 -U ledger; do
  echo "Waiting for postgres at: $POSTGRES_URI"

– Ensure any commands (including ledger serve) propagate non‑zero exit codes.

These changes will give you deterministic image tags and guarantee the container fails fast on errors.

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

Suggested change
build:
FROM --platform=linux/amd64 ubuntu:latest
RUN apt-get update -y && apt-get install -y postgresql-client curl
COPY (+compile/ledger) /bin/ledger
COPY (+compile/symbols) /symbols
COPY entrypoint.sh /bin/entrypoint.sh
RUN chmod 777 /bin/entrypoint.sh
ENTRYPOINT ["/bin/entrypoint.sh"]
EXPOSE 8080
ARG --required ANTITHESIS_REPOSITORY
SAVE IMAGE --push --no-manifest-list "${ANTITHESIS_REPOSITORY}/ledger:latest"
# test/antithesis/image/Earthfile
build:
FROM --platform=linux/amd64 ubuntu:latest
RUN apt-get update -y && apt-get install -y postgresql-client curl
COPY (+compile/ledger) /bin/ledger
COPY (+compile/symbols) /symbols
COPY entrypoint.sh /bin/entrypoint.sh
RUN chmod 777 /bin/entrypoint.sh
ENTRYPOINT ["/bin/entrypoint.sh"]
EXPOSE 8080
ARG --required ANTITHESIS_REPOSITORY
ARG IMAGE_TAG?=$(git describe --tags --always)
SAVE IMAGE --push --no-manifest-list "${ANTITHESIS_REPOSITORY}/ledger:${IMAGE_TAG}"
Suggested change
build:
FROM --platform=linux/amd64 ubuntu:latest
RUN apt-get update -y && apt-get install -y postgresql-client curl
COPY (+compile/ledger) /bin/ledger
COPY (+compile/symbols) /symbols
COPY entrypoint.sh /bin/entrypoint.sh
RUN chmod 777 /bin/entrypoint.sh
ENTRYPOINT ["/bin/entrypoint.sh"]
EXPOSE 8080
ARG --required ANTITHESIS_REPOSITORY
SAVE IMAGE --push --no-manifest-list "${ANTITHESIS_REPOSITORY}/ledger:latest"
# test/antithesis/image/entrypoint.sh
#!/bin/sh
set -euo pipefail
trap 'echo "⛔ Entrypoint failed on line $LINENO"; exit 1' ERR
# make sure pg is ready to accept connections
until pg_isready -d ledger -h 10.0.29.16 -U ledger; do
echo "Waiting for postgres at: $POSTGRES_URI"
done
# ... rest of your startup logic (e.g. ledger serve)

if err := systemStore.CreateLedger(ctx, l); err != nil {
// Handle the case where the ledger already exists
Copy link
Contributor

Choose a reason for hiding this comment

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

I know comments are useful, but this kind of comment just rephrase the code ^^

@@ -0,0 +1,8 @@
VERSION 0.7
Copy link
Contributor

@gfyrag gfyrag Apr 24, 2025

Choose a reason for hiding this comment

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

Stop using Earthfile, there is no Earthfile anymore in the repsoitory ;)

Copy link

@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

🧹 Nitpick comments (7)
test/antithesis/workload/bin/cmds/parallel_drive_ledger_create/main.go (2)

12-28: The ledger creation appears correct, but consider adding confirmation logging.

The code properly creates a ledger with a randomized name, but it would be helpful to add a success log message to confirm when the ledger has been created successfully. This would make troubleshooting easier when reviewing test logs.

	if err != nil {
		assert.Always(err == nil, "ledger should have been created properly", internal.Details{
			"error": err,
		})
		return
	}
+	// Print confirmation that ledger was created successfully
+	fmt.Printf("Ledger '%s' created successfully\n", ledger)
}

15-16: Consider checking for overflow when converting to int64.

Using the absolute value of a randomly generated big integer and directly converting to int64 could potentially lead to overflow issues if the value exceeds the maximum int64 value.

-	id := big.NewInt(0).Abs(internal.RandomBigInt()).Int64()
+	// Ensure the ID fits within int64 bounds
+	bigInt := big.NewInt(0).Abs(internal.RandomBigInt())
+	// Use Mod to ensure it's within int64 range
+	maxInt64 := big.NewInt(math.MaxInt64)
+	id := big.NewInt(0).Mod(bigInt, maxInt64).Int64()
	ledger := fmt.Sprintf("ledger-%d", id)
test/antithesis/workload/bin/cmds/first_default_ledger/main.go (1)

10-25: Consider adding confirmation logging.

The code properly creates the default ledger, but lacks any success message that would be helpful for logging and debugging purposes.

	if err != nil {
		assert.Always(err == nil, "ledger should have been created properly", internal.Details{
			"error": err,
		})
		return
	}
+	fmt.Println("Default ledger created successfully")
}

Don't forget to add the fmt import if you implement this change.

test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go (1)

14-24: Add error handling for the edge case of no ledgers.

The program should handle the case where no ledgers are returned, which is possible especially during initial setup.

	ledgers, err := client.Ledger.V2.ListLedgers(ctx, operations.V2ListLedgersRequest{})
	if err != nil {
		assert.Always(err == nil, "error listing ledgers", internal.Details{
			"error": err,
		})
		return
	}

+	// Handle the case of no ledgers found
+	if len(ledgers.V2LedgerListResponse.Cursor.Data) == 0 {
+		fmt.Println("No ledgers found to check volumes")
+		return
+	}
test/antithesis/workload/internal/utils.go (3)

22-27: Add more context and validation to RandomBigInt function.

The function relies on an external random number generator without validation or documentation about what it returns. Consider adding more context to make the function more robust.

func RandomBigInt() *big.Int {
+	// random.GetRandom() returns a 64-bit random integer
	v := random.GetRandom()
	ret := big.NewInt(0)
	ret.SetString(fmt.Sprintf("%d", v), 10)
+	// Ensure the returned value is non-negative as a safeguard
+	if ret.Sign() < 0 {
+		ret.Neg(ret)
+	}
	return ret
}

81-96: Seed the random generator for better randomization.

The rand.Intn() function is used without seeding the random generator, which can lead to predictable sequences if called in quick succession. Consider seeding the random generator for better randomization.

func GetRandomLedger(ctx context.Context, client *client.Formance) (string, error) {
	ledgers, err := ListLedgers(ctx, client)
	if err != nil {
		return "", err
	}

	if len(ledgers) == 0 {
		return "", fmt.Errorf("no ledgers found")
	}

	// Get a random index within the ledgers slice
+	// Seed the random generator with current time to ensure randomness
+	rand.Seed(time.Now().UnixNano())
	randomIndex := rand.Intn(len(ledgers))

	// Return the ledger at the random index
	return ledgers[randomIndex], nil
}

Note: If you're using Go 1.20+, you should use the newer rand.New(rand.NewSource(time.Now().UnixNano())) approach instead, as rand.Seed is deprecated.


98-114: Hardcoded asset type in RunTx function.

The function hardcodes "USD/2" as the asset, which may not be flexible enough for all test scenarios. Consider making the asset type configurable or adding documentation explaining why this specific asset is used.

-func RunTx(ctx context.Context, client *client.Formance, amount *big.Int, ledger string) error {
+// RunTx creates a transaction on the specified ledger with the given amount of the default asset (USD/2)
+// If you need to use a different asset, use the RunTxWithAsset function instead
+func RunTx(ctx context.Context, client *client.Formance, amount *big.Int, ledger string) error {
+	return RunTxWithAsset(ctx, client, amount, "USD/2", ledger)
+}
+
+// RunTxWithAsset creates a transaction on the specified ledger with the given amount and asset
+func RunTxWithAsset(ctx context.Context, client *client.Formance, amount *big.Int, asset string, ledger string) error {
	orderID := fmt.Sprint(int64(math.Abs(float64(random.GetRandom()))))
	dest := fmt.Sprintf("orders:%s", orderID)

	_, err := client.Ledger.V2.CreateTransaction(ctx, operations.V2CreateTransactionRequest{
		V2PostTransaction: components.V2PostTransaction{
			Postings: []components.V2Posting{{
				Amount:      amount,
-				Asset:       "USD/2",
+				Asset:       asset,
				Destination: dest,
				Source:      "world",
			}},
		},
		Ledger: ledger,
	})
	return err
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0919823 and a2351d6.

⛔ Files ignored due to path filters (3)
  • .github/workflows/antithesis.yml is excluded by !**/*.yml
  • test/antithesis/workload/go.mod is excluded by !**/*.mod
  • test/antithesis/workload/go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (6)
  • test/antithesis/workload/bin/cmds/first_default_ledger/main.go (1 hunks)
  • test/antithesis/workload/bin/cmds/parallel_drive_ledger_create/main.go (1 hunks)
  • test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go (1 hunks)
  • test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go (1 hunks)
  • test/antithesis/workload/internal/utils.go (1 hunks)
  • tools/generator/cmd/root.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tools/generator/cmd/root.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go
🧰 Additional context used
🧬 Code Graph Analysis (3)
test/antithesis/workload/bin/cmds/first_default_ledger/main.go (1)
test/antithesis/workload/internal/utils.go (3)
  • NewClient (41-57)
  • CreateLedger (59-65)
  • Details (20-20)
test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go (4)
test/antithesis/workload/internal/utils.go (2)
  • NewClient (41-57)
  • ListLedgers (67-79)
pkg/client/models/operations/v2listledgers.go (1)
  • V2ListLedgersRequest (9-20)
pkg/client/models/operations/v2getbalancesaggregated.go (1)
  • V2GetBalancesAggregatedRequest (11-18)
pkg/client/models/components/v2aggregatebalancesresponse.go (1)
  • V2AggregateBalancesResponse (10-12)
test/antithesis/workload/bin/cmds/parallel_drive_ledger_create/main.go (1)
test/antithesis/workload/internal/utils.go (4)
  • NewClient (41-57)
  • RandomBigInt (22-27)
  • CreateLedger (59-65)
  • Details (20-20)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Tests
  • GitHub Check: Dirty
  • GitHub Check: Push Images to Antithesis

Comment on lines 42 to 50
for asset, volumes := range aggregated.V2AggregateBalancesResponse.Data {
assert.Always(
volumes.Cmp(new(big.Int)) == 0,
fmt.Sprintf("aggregated volumes for asset %s should be 0",
asset,
), internal.Details{
"error": err,
})
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect error variable in assertion details.

The err variable in the assertion details is from the GetBalancesAggregated call, which would be nil at this point. The details should contain relevant information about the volumes instead.

	for asset, volumes := range aggregated.V2AggregateBalancesResponse.Data {
		assert.Always(
			volumes.Cmp(new(big.Int)) == 0,
			fmt.Sprintf("aggregated volumes for asset %s should be 0",
				asset,
			), internal.Details{
-				"error": err,
+				"asset": asset,
+				"volume": volumes.String(),
			})
	}
🤖 Prompt for AI Agents (early access)
In test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go around lines 42 to 50, the assertion details incorrectly include the variable 'err', which is nil here. Replace 'err' with relevant information about the 'volumes' variable to provide meaningful context in the assertion failure details.

Copy link

@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: 0

🧹 Nitpick comments (2)
test/antithesis/workload/bin/cmds/parallel_driver_ledger_create/main.go (2)

20-30: Error handling could be more descriptive.

The error handling is functional but could benefit from a more specific error message that includes the ledger name.

 	if err != nil {
-		assert.Always(err == nil, "ledger should have been created properly", internal.Details{
+		assert.Always(err == nil, fmt.Sprintf("Failed to create ledger %s", ledger), internal.Details{
 			"error": err,
+			"ledger": ledger,
 		})
 		return
 	}

32-33: Consider adding more detailed logging.

The completion message could include the ledger name to make logs more traceable in a parallel environment.

-	log.Println("composer: parallel_driver_ledger_create: done")
+	log.Printf("composer: parallel_driver_ledger_create: done (created ledger: %s)", ledger)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a2351d6 and 58524d7.

📒 Files selected for processing (6)
  • test/antithesis/Earthfile (1 hunks)
  • test/antithesis/workload/Earthfile (1 hunks)
  • test/antithesis/workload/bin/cmds/first_default_ledger/main.go (1 hunks)
  • test/antithesis/workload/bin/cmds/parallel_driver_ledger_create/main.go (1 hunks)
  • test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go (1 hunks)
  • test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go
  • test/antithesis/workload/bin/cmds/first_default_ledger/main.go
  • test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go
  • test/antithesis/Earthfile
  • test/antithesis/workload/Earthfile
🧰 Additional context used
🧬 Code Graph Analysis (1)
test/antithesis/workload/bin/cmds/parallel_driver_ledger_create/main.go (1)
test/antithesis/workload/internal/utils.go (4)
  • NewClient (41-57)
  • RandomBigInt (22-27)
  • CreateLedger (59-65)
  • Details (20-20)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Dirty
  • GitHub Check: Tests
  • GitHub Check: Push Images to Antithesis
🔇 Additional comments (3)
test/antithesis/workload/bin/cmds/parallel_driver_ledger_create/main.go (3)

1-12: Good package structure and imports.

The package structure and imports are well-organized. The code correctly imports the necessary libraries for context, formatting, logging, and big integer operations, as well as the Antithesis assertion library and internal utilities.


13-19: Solid initialization with unique ledger naming.

The initialization logic is sound:

  • Properly sets up a background context
  • Initializes a client using the internal utility
  • Generates a unique ledger name using a random positive ID

Good use of the Abs() method to ensure the randomly generated ID is positive before formatting it into a ledger name.


1-34:

❓ Verification inconclusive

Add cleanup or documentation for created resources.

The program creates a ledger but doesn't have a mechanism to clean it up. While this might be handled elsewhere, it would be beneficial to either:

  1. Add a cleanup step, or
  2. Add documentation explaining why cleanup isn't needed or how it's handled

This helps prevent resource leakage in testing environments.


🏁 Script executed:

#!/bin/bash
# Check if there are any existing cleanup mechanisms for ledgers in the codebase
echo "Searching for ledger cleanup mechanisms..."
rg -i "delete\s+ledger|cleanup\s+ledger|remove\s+ledger" --type go

Length of output: 313


Add cleanup or document resource handling

I searched the repo for ledger‐cleanup routines and found only:

  • internal/storage/system/migrations.go (“Remove ledger state column”)
  • pkg/client/v2.go (DeleteLedgerMetadata) – only deletes metadata, not the ledger itself

There’s currently no API call in this test CLI to delete the created ledger. To avoid resource leakage in your test environment, please either:

  1. Invoke or implement a proper DeleteLedger (or similar) helper after creation, or
  2. Add documentation explaining why ledger cleanup isn’t needed here (e.g. handled by a global teardown or another process)

Copy link

@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: 0

♻️ Duplicate comments (1)
test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go (1)

53-61: Fix incorrect error variable in assertion details.

The err variable in the assertion details is from the GetBalancesAggregated call, which would be nil at this point. The details should contain relevant information about the volumes instead.

	for asset, volumes := range aggregated.V2AggregateBalancesResponse.Data {
		assert.Always(
			volumes.Cmp(new(big.Int)) == 0,
			fmt.Sprintf("aggregated volumes for asset %s should be 0",
				asset,
			), internal.Details{
-				"error": err,
+				"asset": asset,
+				"volume": volumes.String(),
			})
	}
🧹 Nitpick comments (2)
test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go (2)

39-51: Logging improvement suggestion.

Consider adding more detailed logging before making the API call to help with debugging and monitoring the workflow.

func checkVolumes(ctx context.Context, client *client.Formance, ledger string) {
+	log.Printf("composer: parallel_driver_volumes: checking volumes for ledger %s", ledger)
	aggregated, err := client.Ledger.V2.GetBalancesAggregated(ctx, operations.V2GetBalancesAggregatedRequest{
		Ledger: ledger,
	})

17-22: Consider adding a timeout to the context.

For better resource management, consider using a context with timeout to prevent indefinite hanging if an operation gets stuck.

func main() {
	log.Println("composer: parallel_driver_volumes")
-	ctx := context.Background()
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
+	defer cancel()
	client := internal.NewClient()

Don't forget to add the time import:

import (
	"context"
	"fmt"
	"log"
	"math/big"
	"os"
	"sync"
+	"time"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58524d7 and 4ed1eb5.

📒 Files selected for processing (1)
  • test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go (4)
test/antithesis/workload/internal/utils.go (3)
  • NewClient (41-57)
  • ListLedgers (67-79)
  • Details (20-20)
pkg/client/models/operations/v2listledgers.go (1)
  • V2ListLedgersRequest (9-20)
pkg/client/models/operations/v2getbalancesaggregated.go (1)
  • V2GetBalancesAggregatedRequest (11-18)
pkg/client/models/components/v2aggregatebalancesresponse.go (1)
  • V2AggregateBalancesResponse (10-12)
🔇 Additional comments (1)
test/antithesis/workload/bin/cmds/parallel_driver_volumes/main.go (1)

17-37: LGTM: Good concurrency implementation with WaitGroup.

The main function correctly implements the WaitGroup pattern for concurrency, ensuring all goroutines complete before the program exits. This addresses the synchronization issue that was mentioned in past reviews.

Signed-off-by: Clément Salaün <[email protected]>
Copy link

@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

♻️ Duplicate comments (1)
test/antithesis/workload/bin/cmds/eventually_correct/main.go (1)

45-57: ⚠️ Potential issue

Fix error handling to avoid terminating all goroutines.

Using log.Fatalf in a goroutine will terminate the entire program, not just the current goroutine. This means if one ledger check fails, the others won't be completed.

if err != nil {
    if internal.IsServerError(aggregated.GetHTTPMeta()) {
        assert.Always(
            false,
            fmt.Sprintf("error getting aggregated balances for ledger %s: %s", ledger, err),
            internal.Details{
                "error": err,
            },
        )
    } else {
-       log.Fatalf("error getting aggregated balances for ledger %s: %s", ledger, err)
+       log.Printf("error getting aggregated balances for ledger %s: %s", ledger, err)
+       return
    }
}
🧹 Nitpick comments (2)
test/antithesis/workload/bin/cmds/eventually_correct/main.go (2)

18-39: Consider limiting concurrency for large numbers of ledgers.

The current implementation creates a goroutine for each ledger, which could potentially launch too many concurrent HTTP requests if there are many ledgers. Consider implementing a worker pool pattern to limit concurrency.

Here's a potential implementation using a worker pool:

func main() {
	log.Println("composer: eventually_correct")
	ctx := context.Background()
	client := internal.NewClient()

	ledgers, err := client.Ledger.V2.ListLedgers(ctx, operations.V2ListLedgersRequest{})

	if err != nil {
		log.Printf("error listing ledgers: %s", err)
		os.Exit(1)
	}

+	// Limit concurrency to a reasonable number
+	maxConcurrency := 10
+	semaphore := make(chan struct{}, maxConcurrency)
	wg := sync.WaitGroup{}
	for _, ledger := range ledgers.V2LedgerListResponse.Cursor.Data {
		wg.Add(1)
+		// Acquire a slot from the semaphore
+		semaphore <- struct{}{}
		go func(ledger string) {
			defer wg.Done()
+			defer func() {
+				// Release the slot back to the semaphore
+				<-semaphore
+			}()
			checkVolumes(ctx, client, ledger)
		}(ledger.Name)
	}
	wg.Wait()
}

41-42: Add function documentation.

Add documentation comments to describe the purpose of the function and its parameters to improve code maintainability.

+// checkVolumes fetches aggregated balances for the specified ledger and asserts
+// that all asset volumes are zero. It uses Antithesis assert.Always to record
+// any violations while allowing the program to continue checking other assets.
func checkVolumes(ctx context.Context, client *client.Formance, ledger string) {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40c1a39 and f8fa1ba.

📒 Files selected for processing (3)
  • internal/storage/driver/driver.go (4 hunks)
  • test/antithesis/workload/bin/cmds/eventually_correct/main.go (1 hunks)
  • test/antithesis/workload/internal/utils.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/antithesis/workload/internal/utils.go
  • internal/storage/driver/driver.go
🧰 Additional context used
🧬 Code Graph Analysis (1)
test/antithesis/workload/bin/cmds/eventually_correct/main.go (6)
test/antithesis/workload/internal/utils.go (3)
  • NewClient (41-57)
  • ListLedgers (74-86)
  • IsServerError (59-61)
pkg/client/v2.go (1)
  • V2 (19-21)
pkg/client/models/operations/v2listledgers.go (1)
  • V2ListLedgersRequest (9-20)
pkg/client/formance.go (1)
  • Formance (70-74)
pkg/client/models/operations/v2getbalancesaggregated.go (1)
  • V2GetBalancesAggregatedRequest (11-18)
pkg/client/models/components/v2aggregatebalancesresponse.go (1)
  • V2AggregateBalancesResponse (10-12)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Dirty

Comment on lines +72 to +74
func IsServerError(httpMeta components.HTTPMetadata) bool {
return httpMeta.Response.StatusCode >= 400 && httpMeta.Response.StatusCode < 600
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove duplicate IsServerError function.

This function duplicates the functionality already available in internal.IsServerError which is correctly used at line 46. Remove this duplicate implementation to avoid potential inconsistencies.

-func IsServerError(httpMeta components.HTTPMetadata) bool {
-	return httpMeta.Response.StatusCode >= 400 && httpMeta.Response.StatusCode < 600
-}
📝 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.

Suggested change
func IsServerError(httpMeta components.HTTPMetadata) bool {
return httpMeta.Response.StatusCode >= 400 && httpMeta.Response.StatusCode < 600
}
🤖 Prompt for AI Agents (early access)
In test/antithesis/workload/bin/cmds/eventually_correct/main.go around lines 72 to 74, there is a duplicate implementation of the IsServerError function which duplicates the existing internal.IsServerError function used at line 46. Remove this duplicate IsServerError function entirely to avoid inconsistencies and rely on the internal.IsServerError function instead.

Comment on lines +65 to +66
"error": err,
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect error variable in assert details.

The err variable may be nil at this point since this code executes after the error check. Include the actual volume value instead to provide more meaningful assertion details.

assert.Always(
    volumes.Cmp(new(big.Int)) == 0,
    fmt.Sprintf("aggregated volumes for asset %s should be 0",
        asset,
    ), internal.Details{
-       "error": err,
+       "actual_volume": volumes.String(),
+       "asset": asset,
    })
📝 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.

Suggested change
"error": err,
})
assert.Always(
volumes.Cmp(new(big.Int)) == 0,
fmt.Sprintf("aggregated volumes for asset %s should be 0",
asset,
), internal.Details{
"actual_volume": volumes.String(),
"asset": asset,
})
🤖 Prompt for AI Agents (early access)
In test/antithesis/workload/bin/cmds/eventually_correct/main.go at lines 65-66, the assertion details incorrectly include the variable 'err', which may be nil since this code runs after the error check. Replace 'err' with the actual volume value in the assertion details to provide more meaningful and accurate information.

altitude added 2 commits May 6, 2025 17:07
Signed-off-by: Clément Salaün <[email protected]>
Signed-off-by: Clément Salaün <[email protected]>
Copy link

@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: 5

🧹 Nitpick comments (5)
test/antithesis/workload/bin/cmds/anytime_version_upgrade/main.go (1)

5-8: Consider enhancing the placeholder with more context about the planned functionality.

This is currently just a placeholder implementation, which is fine for initial setup. However, consider adding more detailed comments about the intended functionality of this command, particularly around how it will handle version upgrades within the Antithesis testing framework.

According to the PR context, this appears to be part of a suite of workload orchestration tools. It would be helpful to document:

  1. The purpose of this specific command
  2. How it will interact with other components
  3. Expected inputs/outputs when fully implemented
 func main() {
-	// TODO: implement
+	// TODO: implement version upgrade functionality
+	// This command will handle dynamic version upgrades during Antithesis testing.
+	// It should:
+	// - Coordinate with the test orchestrator
+	// - Manage the upgrade process for specific components
+	// - Validate system stability after upgrades
 	log.Println("placeholder command for anytime_version_upgrade")
 }
test/antithesis/workload/datagen/main.go (2)

1-7: Good starting point for the package, but consider improving imports.

The imports look good, but note that math/rand is deprecated in favor of math/rand/v2 in Go 1.23+. Consider using the newer version for improved randomness and future compatibility.

import (
	"fmt"
-	"math/rand"
+	"math/rand/v2"
)

15-17: Add documentation to explain the program's purpose.

The main function works correctly but lacks documentation explaining the program's purpose within the testing framework.

+// main generates a random account number and prints it to stdout
+// This is used for data generation in Antithesis testing workloads
func main() {
	fmt.Println(Account())
}
test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go (2)

72-83: Transaction function always uses the same fixed values.

The Transaction() function always returns a posting with the same fixed values, which may not thoroughly test the system under different scenarios.

func Transaction() []components.V2Posting {
	postings := []components.V2Posting{}

	postings = append(postings, components.V2Posting{
-		Amount:      big.NewInt(100),
+		Amount:      internal.RandomBigInt(), // Use random amounts for better coverage
		Asset:       "USD/2",
-		Destination: "orders:1234",
+		Destination: fmt.Sprintf("orders:%d", rand.Intn(10000)), // Randomize order IDs
		Source:      "world",
	})

	return postings
}

85-93: Unused function in the main flow.

The Sequence() function is defined but never used in the program. Consider either removing it or documenting why it's defined for future use.

-func Sequence() []Postings {
-	postings := []Postings{}
-
-	for i := 0; i < 10; i++ {
-		postings = append(postings, Transaction())
-	}
-
-	return postings
-}

If you need to keep this function for future use, add appropriate documentation:

+// Sequence returns a sequence of 10 transactions
+// Note: Currently not used in the main flow but kept for future expansion
func Sequence() []Postings {
	postings := []Postings{}

	for i := 0; i < 10; i++ {
		postings = append(postings, Transaction())
	}

	return postings
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 979b33a and 49b0414.

⛔ Files ignored due to path filters (1)
  • test/antithesis/workload/datagen/example.json is excluded by !**/*.json
📒 Files selected for processing (6)
  • test/antithesis/workload/bin/cmds/anytime_version_upgrade/main.go (1 hunks)
  • test/antithesis/workload/bin/cmds/first_default_ledger/main.go (1 hunks)
  • test/antithesis/workload/bin/cmds/parallel_driver_ledger_create/main.go (1 hunks)
  • test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go (1 hunks)
  • test/antithesis/workload/datagen/main.go (1 hunks)
  • test/antithesis/workload/internal/utils.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/antithesis/workload/bin/cmds/first_default_ledger/main.go
  • test/antithesis/workload/bin/cmds/parallel_driver_ledger_create/main.go
  • test/antithesis/workload/internal/utils.go
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Dirty
  • GitHub Check: Tests
  • GitHub Check: Push Images to Antithesis
🔇 Additional comments (2)
test/antithesis/workload/bin/cmds/anytime_version_upgrade/main.go (1)

1-4: LGTM: Package declaration and imports look good.

The package declaration and import statement follow Go conventions. Using the standard library's "log" package is appropriate for basic logging functionality.

test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go (1)

3-14: LGTM: Well-organized imports with clear separation.

The import organization is good, with standard library packages first, followed by third-party packages.

Comment on lines +8 to +9
type Sequence struct {
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove or properly implement the unused struct.

The Sequence struct is defined but never used in this file. Either remove it or document its purpose and implement its intended functionality.

-type Sequence struct {
-}
📝 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.

Suggested change
type Sequence struct {
}
🤖 Prompt for AI Agents (early access)
In test/antithesis/workload/datagen/main.go at lines 8 to 9, the Sequence struct
is defined but not used or implemented. Either remove the Sequence struct
entirely if it is unnecessary, or provide a proper implementation and
documentation explaining its purpose and how it fits into the codebase.

Comment on lines +11 to +13
func Account() string {
return fmt.Sprintf("%d", rand.Intn(10e9))
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Account generation is not seeded, which will produce predictable results.

The random number generator is not seeded, which means it will produce the same sequence of "random" numbers on each program run. This could lead to predictable account numbers, which may be problematic for testing.

+func init() {
+	// Seed the random number generator to ensure different account numbers on each run
+	rand.Seed(time.Now().UnixNano())
+}

func Account() string {
	return fmt.Sprintf("%d", rand.Intn(10e9))
}

You'll also need to add "time" to your imports.

🤖 Prompt for AI Agents (early access)
In test/antithesis/workload/datagen/main.go around lines 11 to 13, the Account
function uses rand.Intn without seeding the random number generator, causing
predictable outputs. To fix this, seed the random number generator once at
program startup using rand.Seed with the current time in nanoseconds from
time.Now().UnixNano(). Also, add "time" to the import statements to enable this.
This ensures Account generates different values on each run.

Comment on lines 38 to 45
res, err := RunTx(ctx, client, Transaction(), ledger)
assert.Sometimes(err == nil, "transaction was committed successfully", internal.Details{
"ledger": ledger,
})
assert.Always(!internal.IsServerError(res.GetHTTPMeta()), "no internal server error when committing transaction", internal.Details{
"ledger": ledger,
"error": err,
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling in transaction assertions.

The code uses assertions but doesn't properly handle the case where res could be nil if err is not nil.

			res, err := RunTx(ctx, client, Transaction(), ledger)
			assert.Sometimes(err == nil, "transaction was committed successfully", internal.Details{
				"ledger": ledger,
			})
-			assert.Always(!internal.IsServerError(res.GetHTTPMeta()), "no internal server error when committing transaction", internal.Details{
+			assert.Always(err == nil || !internal.IsServerError(res.GetHTTPMeta()), "no internal server error when committing transaction", internal.Details{
				"ledger": ledger,
				"error":  err,
			})

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents (early access)
In test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go around
lines 38 to 45, the code asserts conditions on res without checking if res is
nil when err is not nil, which can cause a nil pointer dereference. Add a check
to ensure res is not nil before calling res.GetHTTPMeta(), and handle the case
where err is not nil by skipping or adjusting the assertions accordingly to
avoid runtime panics.

Comment on lines 30 to 36
totalAmount := big.NewInt(0)

pool := pond.New(10, 10e3)

for i := 0; i < count; i++ {
amount := internal.RandomBigInt()
totalAmount = totalAmount.Add(totalAmount, amount)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Unused variable tracking.

The totalAmount variable is incremented with random values that are never used in transactions. The amount generated in each loop iteration is also never used for the transaction since Transaction() uses a fixed amount of 100.

-	totalAmount := big.NewInt(0)
-
	pool := pond.New(10, 10e3)

	for i := 0; i < count; i++ {
-		amount := internal.RandomBigInt()
-		totalAmount = totalAmount.Add(totalAmount, amount)
		pool.Submit(func() {
📝 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.

Suggested change
totalAmount := big.NewInt(0)
pool := pond.New(10, 10e3)
for i := 0; i < count; i++ {
amount := internal.RandomBigInt()
totalAmount = totalAmount.Add(totalAmount, amount)
pool := pond.New(10, 10e3)
for i := 0; i < count; i++ {
pool.Submit(func() {
// ...
})
}
🤖 Prompt for AI Agents (early access)
In test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go around
lines 30 to 36, the variable totalAmount is updated with random amounts that are
never used, and the generated amount is not passed to the Transaction function
which uses a fixed value of 100. To fix this, remove the totalAmount variable
and the addition logic since it is unused, and modify the Transaction calls to
use the generated amount instead of the fixed 100 value so that the random
amounts are actually used in the transactions.

Comment on lines 37 to 46
pool.Submit(func() {
res, err := RunTx(ctx, client, Transaction(), ledger)
assert.Sometimes(err == nil, "transaction was committed successfully", internal.Details{
"ledger": ledger,
})
assert.Always(!internal.IsServerError(res.GetHTTPMeta()), "no internal server error when committing transaction", internal.Details{
"ledger": ledger,
"error": err,
})
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential race condition in anonymous function.

The anonymous function is accessing variables from the outer scope (ctx, client, ledger), which could lead to race conditions, especially if these variables change during execution.

pool.Submit(func() {
-			res, err := RunTx(ctx, client, Transaction(), ledger)
+			// Capture values to prevent race conditions
+			localCtx := ctx
+			localClient := client
+			localLedger := ledger
+			res, err := RunTx(localCtx, localClient, Transaction(), localLedger)
			assert.Sometimes(err == nil, "transaction was committed successfully", internal.Details{
-				"ledger": ledger,
+				"ledger": localLedger,
			})
			assert.Always(!internal.IsServerError(res.GetHTTPMeta()), "no internal server error when committing transaction", internal.Details{
-				"ledger": ledger,
+				"ledger": localLedger,
				"error":  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.

Suggested change
pool.Submit(func() {
res, err := RunTx(ctx, client, Transaction(), ledger)
assert.Sometimes(err == nil, "transaction was committed successfully", internal.Details{
"ledger": ledger,
})
assert.Always(!internal.IsServerError(res.GetHTTPMeta()), "no internal server error when committing transaction", internal.Details{
"ledger": ledger,
"error": err,
})
})
pool.Submit(func() {
// Capture values to prevent race conditions
localCtx := ctx
localClient := client
localLedger := ledger
res, err := RunTx(localCtx, localClient, Transaction(), localLedger)
assert.Sometimes(err == nil, "transaction was committed successfully", internal.Details{
"ledger": localLedger,
})
assert.Always(!internal.IsServerError(res.GetHTTPMeta()), "no internal server error when committing transaction", internal.Details{
"ledger": localLedger,
"error": err,
})
})
🤖 Prompt for AI Agents (early access)
In test/antithesis/workload/bin/cmds/parallel_driver_transactions/main.go around
lines 37 to 46, the anonymous function passed to pool.Submit captures outer
variables ctx, client, and ledger directly, which may cause race conditions if
these variables change concurrently. To fix this, pass these variables as
parameters to the anonymous function or create local copies inside the loop
before submitting the function, ensuring each goroutine uses its own independent
copy of the variables.

Copy link

@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: 0

♻️ Duplicate comments (2)
internal/storage/driver/driver.go (2)

58-60: Trim comments that restate the code.

Several inline comments repeat what the code already expresses (prior reviewer noted this too). Consider removing or tightening to keep the function compact.

Also applies to: 63-63, 108-108, 114-114, 119-119


85-95: Hard-coded assertion always flags “unreachable”; parameterize and fix typo.

This reproduces a prior concern: using false guarantees the Unreachable path when a bucket is outdated, which isn’t aligned with the TODO. Make it configurable and correct the “wether” typo. Also add coverage for both expected/unexpected cases.

-				assert.AlwaysOrUnreachable(
-					// @todo: replace this with a proper flag detailing wether we're
+				assert.AlwaysOrUnreachable(
+					// @todo: replace this with a proper flag detailing whether we're
 					// operating a new version of the binary or not.
 					// if we are, we are definitely expecting this to happen.
 					// if we're not, this should be unreachable.
-					false,
+					d.expectOutdatedBuckets,
 					"Bucket is outdated",
 					map[string]any{
 						"bucket": l.Bucket,
 					},
 				)

Support code outside this hunk (example):

// in type Driver
expectOutdatedBuckets bool

// option
func WithExpectOutdatedBuckets(v bool) Option {
	return func(d *Driver) { d.expectOutdatedBuckets = v }
}

Would you like me to add a focused test that stubs Bucket.IsUpToDate to false and asserts the returned error and the assertion path under both flag values?

🧹 Nitpick comments (2)
internal/storage/driver/driver.go (2)

10-10: Decouple Antithesis SDK from production builds.

Importing github.com/antithesishq/antithesis-sdk-go/assert directly couples all builds to the SDK. Consider a thin internal wrapper with build tags to no-op outside Antithesis runs.

Example:

// internal/assertx/assert_antithesis.go
//go:build antithesis
package assertx

import "github.com/antithesishq/antithesis-sdk-go/assert"

func AlwaysOrUnreachable(cond bool, msg string, kv map[string]any) {
	assert.AlwaysOrUnreachable(cond, msg, kv)
}

// internal/assertx/assert_stub.go
//go:build !antithesis
package assertx

func AlwaysOrUnreachable(bool, string, map[string]any) {}

Then replace calls to assert.AlwaysOrUnreachable with assertx.AlwaysOrUnreachable.


63-70: Avoid resolving the same error twice.

Resolve once and branch on that value. This also makes the intent crisper.

-		if err := systemStore.CreateLedger(ctx, l); err != nil {
-			// Handle the case where the ledger already exists
-			if errors.Is(postgres.ResolveError(err), postgres.ErrConstraintsFailed{}) {
-				return systemstore.ErrLedgerAlreadyExists
-			}
-			return postgres.ResolveError(err)
-		}
+		if err := systemStore.CreateLedger(ctx, l); err != nil {
+			rerr := postgres.ResolveError(err)
+			if errors.Is(rerr, postgres.ErrConstraintsFailed{}) {
+				return systemstore.ErrLedgerAlreadyExists
+			}
+			return rerr
+		}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 49b0414 and c3cb880.

⛔ Files ignored due to path filters (3)
  • .github/workflows/antithesis.yml is excluded by !**/*.yml
  • go.mod is excluded by !**/*.mod
  • go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (1)
  • internal/storage/driver/driver.go (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
internal/storage/driver/driver.go (3)
internal/ledger.go (1)
  • Ledger (18-26)
internal/storage/system/store.go (1)
  • Store (20-31)
internal/storage/bucket/bucket.go (1)
  • Bucket (13-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tests
  • GitHub Check: Dirty
  • GitHub Check: Push Images to Antithesis
🔇 Additional comments (1)
internal/storage/driver/driver.go (1)

41-55: Good, clear GoDoc for CreateLedger.

Accurately documents the transactional flow and failure semantics.

Copy link

@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: 11

♻️ Duplicate comments (1)
test/antithesis/workload/Earthfile (1)

19-19: Pin instrumentor version for reproducible builds.

Using @latest risks non-determinism and breakage. Pin a known-good version.

-RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
+RUN go install github.com/antithesishq/antithesis-sdk-go/tools/[email protected]
🧹 Nitpick comments (10)
test/antithesis/workload/bin/cmds/anytime_version_upgrade/main.go (3)

58-65: Consider surfacing richer context on failure and success.

  • Include namespace/name/version in error logs.
  • Optionally log the observed Generation/ResourceVersion on success.

No diff required; informational.


67-67: Nit: Replace placeholder log with actionable message.

E.g., “upgraded Ledger stack0-ledger to version $tag in namespace formance-systems”.


58-58: Parameterize name/namespace.

Hardcoding “stack0-ledger” and “formance-systems” makes reuse harder; read from env vars with sane defaults.

test/antithesis/workload/Earthfile (2)

42-42: Reduce image size and CVE surface after apt-get.

Clean apt lists in the same layer.

-RUN apt-get update && apt-get install -y curl
+RUN apt-get update && apt-get install -y curl \
+ && rm -rf /var/lib/apt/lists/*

31-33: Make the loop robust to spaces and non-files.

If any file names contain spaces or if bin/cmds has non-entries, the ls-based expansion can misbehave. Prefer Earthly’s globbing:

-FOR file IN $(ls bin/cmds)
-    RUN go build -o /out/cmds/$file ./bin/cmds/$file
+FOR file IN bin/cmds/*
+    RUN go build -o /out/cmds/$(basename ${file}) ./bin/cmds/$(basename ${file})
 END
test/antithesis/config/Justfile (1)

17-19: Guard env and use strict mode in push.

 push: build-manifest
-	docker build -f Dockerfile.config -t $ANTITHESIS_REPOSITORY/antithesis-config:k8s_test .
-	docker push $ANTITHESIS_REPOSITORY/antithesis-config:k8s_test
+	set -euo pipefail
+	: "${ANTITHESIS_REPOSITORY:?ANTITHESIS_REPOSITORY is required}"
+	docker build --pull -f Dockerfile.config -t "$ANTITHESIS_REPOSITORY/antithesis-config:k8s_test" .
+	docker push "$ANTITHESIS_REPOSITORY/antithesis-config:k8s_test"
test/antithesis/Justfile (4)

5-11: Safer JSON construction (optional).

If $ANTITHESIS_SLACK_REPORT_RECIPIENT can contain quotes or special chars, prefer generating the payload with a JSON tool to avoid malformed JSON.

Example without changing external deps (uses Python, commonly available):

payload="$(python3 - <<'PY'
import json, os
print(json.dumps({
  "params": {
    "custom.duration": "0.1",
    "antithesis.report.recipients": os.environ["ANTITHESIS_SLACK_REPORT_RECIPIENT"],
    "antithesis.config_image": "antithesis-config:k8s_test",
    "antithesis.images": "workload:latest;docker.io/library/postgres:15-alpine;ghcr.io/formancehq/operator:v2.10.1;ghcr.io/formancehq/operator-utils:v2.0.14;ghcr.io/formancehq/gateway:v2.0.24;ghcr.io/formancehq/ledger-instrumented:k8s_test"
  }
}))
PY
)"
curl ... --data "$payload"

9-9: Pin images for reproducibility; avoid latest/test tags in CI-triggered experiments.

workload:latest and antithesis-config:k8s_test are mutable; runs won’t be reproducible.

Consider parameterizing and/or digest-pinning, e.g.:

-            "antithesis.images": "workload:latest;docker.io/library/postgres:15-alpine;ghcr.io/formancehq/operator:v2.10.1;ghcr.io/formancehq/operator-utils:v2.0.14;ghcr.io/formancehq/gateway:v2.0.24;ghcr.io/formancehq/ledger-instrumented:k8s_test" \
+            "antithesis.images": "workload:${LEDGER_LATEST_TAG};docker.io/library/postgres:15-alpine@${POSTGRES_DIGEST};ghcr.io/formancehq/operator:v2.10.1@${OPERATOR_DIGEST};ghcr.io/formancehq/operator-utils:v2.0.14@${OPERATOR_UTILS_DIGEST};ghcr.io/formancehq/gateway:v2.0.24@${GATEWAY_DIGEST};ghcr.io/formancehq/ledger-instrumented:k8s_test@${LEDGER_INSTRUMENTED_DIGEST}" \

If digests aren’t practical, at least replace latest/k8s_test with immutable versioned tags produced by your pipeline.

Also applies to: 21-21


1-11: DRY the two nearly-identical recipes.

Reduce duplication and the chance of drift by introducing a parameterized recipe.

Example:

run duration:
    # shared preconditions/auth here...
    # build payload using $duration
    # curl ...

run-6min: requirements-build
    just run 0.1

run-1h: requirements-build
    just run 1

Also applies to: 13-23


25-28: Add env prechecks to avoid surprising no-op pushes.

Fail fast if required vars are missing before building/pushing.

 requirements-build:
-    just config/push
+    : "${ANTITHESIS_REPOSITORY:?ANTITHESIS_REPOSITORY is required}"
+    : "${LEDGER_LATEST_TAG:?LEDGER_LATEST_TAG is required}"
+    just config/push
     # earthly --push ./image+build --ANTITHESIS_REPOSITORY="$ANTITHESIS_REPOSITORY"
     earthly --push ./workload+build --ANTITHESIS_REPOSITORY="$ANTITHESIS_REPOSITORY" --LEDGER_LATEST_TAG="$LEDGER_LATEST_TAG"

Also verify your environment supports just subdirectory invocation (just config/push). If not, use just --justfile ./config/Justfile push.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c3cb880 and 1ee5795.

⛔ Files ignored due to path filters (5)
  • .github/workflows/antithesis.yml is excluded by !**/*.yml
  • test/antithesis/config/manifests/ledger.yaml is excluded by !**/*.yaml
  • test/antithesis/config/manifests/stack.yaml is excluded by !**/*.yaml
  • test/antithesis/workload/go.mod is excluded by !**/*.mod
  • test/antithesis/workload/go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (4)
  • test/antithesis/Justfile (1 hunks)
  • test/antithesis/config/Justfile (1 hunks)
  • test/antithesis/workload/Earthfile (1 hunks)
  • test/antithesis/workload/bin/cmds/anytime_version_upgrade/main.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/antithesis/workload/bin/cmds/anytime_version_upgrade/main.go (2)
cmd/root.go (1)
  • Version (23-23)
test/antithesis/workload/internal/utils.go (1)
  • Details (20-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Dirty
  • GitHub Check: Tests
🔇 Additional comments (1)
test/antithesis/config/Justfile (1)

8-9: Double-check CRD availability vs. Ledger CR usage.

If operator CRDs aren’t applied via regions chart, the Ledger manifest may fail. Ensure regions chart version includes the Ledger CRD matching manifests/ledger.yaml.

If uncertain, keep the operator-crds line enabled for safety.

Also applies to: 13-13

Comment on lines +1 to +16
build-manifest:
rm -f -- tmp/resources.yaml
mkdir -p tmp
cat manifests/namespace.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
cat manifests/postgres.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
helm template regions oci://ghcr.io/formancehq/helm/regions --version 2.15.2 --namespace formance-systems >> tmp/resources.yaml
# helm template operator oci://ghcr.io/formancehq/helm/operator-crds --version v2.8.0 --namespace formance-systems >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
cat manifests/stack.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
yq '.spec.version = strenv(LEDGER_PREVIOUS_TAG)' manifests/ledger.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
cat manifests/workload.yaml >> tmp/resources.yaml

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Harden recipes: fail fast and guard required env.

Add strict shell flags and verify LEDGER_PREVIOUS_TAG is set before templating.

 build-manifest:
-	rm -f -- tmp/resources.yaml
+	set -euo pipefail
+	: "${LEDGER_PREVIOUS_TAG:?LEDGER_PREVIOUS_TAG is required}"
+	rm -f -- tmp/resources.yaml
 	mkdir -p tmp
 	cat manifests/namespace.yaml >> tmp/resources.yaml
 	echo "---" >> tmp/resources.yaml
 	cat manifests/postgres.yaml >> tmp/resources.yaml
 	echo "---" >> tmp/resources.yaml
 	helm template regions oci://ghcr.io/formancehq/helm/regions --version 2.15.2 --namespace formance-systems >> tmp/resources.yaml
 	# helm template operator oci://ghcr.io/formancehq/helm/operator-crds --version v2.8.0 --namespace formance-systems >> tmp/resources.yaml
 	echo "---" >> tmp/resources.yaml
 	cat manifests/stack.yaml >> tmp/resources.yaml
 	echo "---" >> tmp/resources.yaml
 	yq '.spec.version = strenv(LEDGER_PREVIOUS_TAG)' manifests/ledger.yaml >> tmp/resources.yaml
 	echo "---" >> tmp/resources.yaml
 	cat manifests/workload.yaml >> tmp/resources.yaml
📝 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.

Suggested change
build-manifest:
rm -f -- tmp/resources.yaml
mkdir -p tmp
cat manifests/namespace.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
cat manifests/postgres.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
helm template regions oci://ghcr.io/formancehq/helm/regions --version 2.15.2 --namespace formance-systems >> tmp/resources.yaml
# helm template operator oci://ghcr.io/formancehq/helm/operator-crds --version v2.8.0 --namespace formance-systems >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
cat manifests/stack.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
yq '.spec.version = strenv(LEDGER_PREVIOUS_TAG)' manifests/ledger.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
cat manifests/workload.yaml >> tmp/resources.yaml
build-manifest:
set -euo pipefail
: "${LEDGER_PREVIOUS_TAG:?LEDGER_PREVIOUS_TAG is required}"
rm -f -- tmp/resources.yaml
mkdir -p tmp
cat manifests/namespace.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
cat manifests/postgres.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
helm template regions oci://ghcr.io/formancehq/helm/regions --version 2.15.2 --namespace formance-systems >> tmp/resources.yaml
# helm template operator oci://ghcr.io/formancehq/helm/operator-crds --version v2.8.0 --namespace formance-systems >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
cat manifests/stack.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
yq '.spec.version = strenv(LEDGER_PREVIOUS_TAG)' manifests/ledger.yaml >> tmp/resources.yaml
echo "---" >> tmp/resources.yaml
cat manifests/workload.yaml >> tmp/resources.yaml
🤖 Prompt for AI Agents
In test/antithesis/config/Justfile around lines 1 to 16, the build-manifest
recipe runs multiple shell commands without strict failure handling or checking
required environment variables; update the recipe to enable strict shell flags
(set -euo pipefail) at the start of the recipe’s shell invocation and add an
explicit guard that verifies LEDGER_PREVIOUS_TAG is set (exit with a clear error
if not) before running the yq templating step so the recipe fails fast and does
not proceed with an unset variable.

Comment on lines +2 to +11
curl --fail \
--user "formance:$ANTITHESIS_PASSWORD" \
-X POST https://formance.antithesis.com/api/v1/launch_experiment/formance-k8s -d '{ \
"params": { \
"custom.duration": "0.1", \
"antithesis.report.recipients": "'"$ANTITHESIS_SLACK_REPORT_RECIPIENT"'", \
"antithesis.config_image": "antithesis-config:k8s_test", \
"antithesis.images": "workload:latest;docker.io/library/postgres:15-alpine;ghcr.io/formancehq/operator:v2.10.1;ghcr.io/formancehq/operator-utils:v2.0.14;ghcr.io/formancehq/gateway:v2.0.24;ghcr.io/formancehq/ledger-instrumented:k8s_test" \
} \
}'
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Stop leaking credentials; switch to netrc, add JSON header, and harden curl.

Passing $ANTITHESIS_PASSWORD via --user exposes it in process listings. Also, set the proper JSON content-type and add basic resiliency.

Apply this diff:

-    curl --fail \
-        --user "formance:$ANTITHESIS_PASSWORD" \
-        -X POST https://formance.antithesis.com/api/v1/launch_experiment/formance-k8s -d '{ \
+    # Preconditions & secure auth (avoid exposing secrets in process list)
+    : "${ANTITHESIS_PASSWORD:?ANTITHESIS_PASSWORD is required}"
+    : "${ANTITHESIS_SLACK_REPORT_RECIPIENT:?ANTITHESIS_SLACK_REPORT_RECIPIENT is required}"
+    tmp_netrc="$(mktemp)"; trap 'rm -f "$tmp_netrc"' EXIT
+    printf 'machine formance.antithesis.com login formance password %s\n' "$ANTITHESIS_PASSWORD" > "$tmp_netrc"
+    chmod 600 "$tmp_netrc"
+    curl --fail --silent --show-error --retry 3 --retry-all-errors \
+        --netrc-file "$tmp_netrc" \
+        -H 'Content-Type: application/json' \
+        -X POST 'https://formance.antithesis.com/api/v1/launch_experiment/formance-k8s' -d '{ \
📝 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.

Suggested change
curl --fail \
--user "formance:$ANTITHESIS_PASSWORD" \
-X POST https://formance.antithesis.com/api/v1/launch_experiment/formance-k8s -d '{ \
"params": { \
"custom.duration": "0.1", \
"antithesis.report.recipients": "'"$ANTITHESIS_SLACK_REPORT_RECIPIENT"'", \
"antithesis.config_image": "antithesis-config:k8s_test", \
"antithesis.images": "workload:latest;docker.io/library/postgres:15-alpine;ghcr.io/formancehq/operator:v2.10.1;ghcr.io/formancehq/operator-utils:v2.0.14;ghcr.io/formancehq/gateway:v2.0.24;ghcr.io/formancehq/ledger-instrumented:k8s_test" \
} \
}'
# Preconditions & secure auth (avoid exposing secrets in process list)
: "${ANTITHESIS_PASSWORD:?ANTITHESIS_PASSWORD is required}"
: "${ANTITHESIS_SLACK_REPORT_RECIPIENT:?ANTITHESIS_SLACK_REPORT_RECIPIENT is required}"
tmp_netrc="$(mktemp)"; trap 'rm -f "$tmp_netrc"' EXIT
printf 'machine formance.antithesis.com login formance password %s\n' "$ANTITHESIS_PASSWORD" > "$tmp_netrc"
chmod 600 "$tmp_netrc"
curl --fail --silent --show-error --retry 3 --retry-all-errors \
--netrc-file "$tmp_netrc" \
-H 'Content-Type: application/json' \
-X POST 'https://formance.antithesis.com/api/v1/launch_experiment/formance-k8s' -d '{ \
"params": { \
"custom.duration": "0.1", \
"antithesis.report.recipients": "'"$ANTITHESIS_SLACK_REPORT_RECIPIENT"'", \
"antithesis.config_image": "antithesis-config:k8s_test", \
"antithesis.images": "workload:latest;docker.io/library/postgres:15-alpine;ghcr.io/formancehq/operator:v2.10.1;ghcr.io/formancehq/operator-utils:v2.0.14;ghcr.io/formancehq/gateway:v2.0.24;ghcr.io/formancehq/ledger-instrumented:k8s_test" \
} \
}'
🤖 Prompt for AI Agents
In test/antithesis/Justfile around lines 2 to 11, the curl invocation leaks the
ANTITHESIS_PASSWORD via --user, lacks a JSON content-type header, and has no
retry/resilience flags; remove the --user usage and instead rely on netrc by
instructing the environment or CI to create a .netrc entry for the host with the
Formance credentials and pass curl the netrc file option, add a Content-Type:
application/json header, and harden curl by adding --silent --show-error --fail
--location plus retry flags (e.g., --retry and --retry-delay) to improve
resiliency; ensure data remains JSON-encoded and that secret handling is
documented so the .netrc is created with correct permissions before running this
command.

Comment on lines +14 to +23
curl --fail \
--user "formance:$ANTITHESIS_PASSWORD" \
-X POST https://formance.antithesis.com/api/v1/launch_experiment/formance-k8s -d '{ \
"params": { \
"custom.duration": "1", \
"antithesis.report.recipients": "'"$ANTITHESIS_SLACK_REPORT_RECIPIENT"'", \
"antithesis.config_image": "antithesis-config:k8s_test", \
"antithesis.images": "workload:latest;docker.io/library/postgres:15-alpine;ghcr.io/formancehq/operator:v2.10.1;ghcr.io/formancehq/operator-utils:v2.0.14;ghcr.io/formancehq/gateway:v2.0.24;ghcr.io/formancehq/ledger-instrumented:k8s_test" \
} \
}'
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Mirror the security and resiliency fixes in run-1h.

Apply the same netrc, JSON header, and curl hardening here.

-    curl --fail \
-        --user "formance:$ANTITHESIS_PASSWORD" \
-        -X POST https://formance.antithesis.com/api/v1/launch_experiment/formance-k8s -d '{ \
+    : "${ANTITHESIS_PASSWORD:?ANTITHESIS_PASSWORD is required}"
+    : "${ANTITHESIS_SLACK_REPORT_RECIPIENT:?ANTITHESIS_SLACK_REPORT_RECIPIENT is required}"
+    tmp_netrc="$(mktemp)"; trap 'rm -f "$tmp_netrc"' EXIT
+    printf 'machine formance.antithesis.com login formance password %s\n' "$ANTITHESIS_PASSWORD" > "$tmp_netrc"
+    chmod 600 "$tmp_netrc"
+    curl --fail --silent --show-error --retry 3 --retry-all-errors \
+        --netrc-file "$tmp_netrc" \
+        -H 'Content-Type: application/json' \
+        -X POST 'https://formance.antithesis.com/api/v1/launch_experiment/formance-k8s' -d '{ \
📝 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.

Suggested change
curl --fail \
--user "formance:$ANTITHESIS_PASSWORD" \
-X POST https://formance.antithesis.com/api/v1/launch_experiment/formance-k8s -d '{ \
"params": { \
"custom.duration": "1", \
"antithesis.report.recipients": "'"$ANTITHESIS_SLACK_REPORT_RECIPIENT"'", \
"antithesis.config_image": "antithesis-config:k8s_test", \
"antithesis.images": "workload:latest;docker.io/library/postgres:15-alpine;ghcr.io/formancehq/operator:v2.10.1;ghcr.io/formancehq/operator-utils:v2.0.14;ghcr.io/formancehq/gateway:v2.0.24;ghcr.io/formancehq/ledger-instrumented:k8s_test" \
} \
}'
: "${ANTITHESIS_PASSWORD:?ANTITHESIS_PASSWORD is required}"
: "${ANTITHESIS_SLACK_REPORT_RECIPIENT:?ANTITHESIS_SLACK_REPORT_RECIPIENT is required}"
tmp_netrc="$(mktemp)"; trap 'rm -f "$tmp_netrc"' EXIT
printf 'machine formance.antithesis.com login formance password %s\n' "$ANTITHESIS_PASSWORD" > "$tmp_netrc"
chmod 600 "$tmp_netrc"
curl --fail --silent --show-error --retry 3 --retry-all-errors \
--netrc-file "$tmp_netrc" \
-H 'Content-Type: application/json' \
-X POST 'https://formance.antithesis.com/api/v1/launch_experiment/formance-k8s' -d '{ \
"params": { \
"custom.duration": "1", \
"antithesis.report.recipients": "'"$ANTITHESIS_SLACK_REPORT_RECIPIENT"'", \
"antithesis.config_image": "antithesis-config:k8s_test", \
"antithesis.images": "workload:latest;docker.io/library/postgres:15-alpine;ghcr.io/formancehq/operator:v2.10.1;ghcr.io/formancehq/operator-utils:v2.0.14;ghcr.io/formancehq/gateway:v2.0.24;ghcr.io/formancehq/ledger-instrumented:k8s_test" \
} \
}'
🤖 Prompt for AI Agents
In test/antithesis/Justfile around lines 14 to 23, the curl call lacks the same
security and resiliency flags used in run-1h; update the invocation to use a
netrc file for credentials (e.g. --netrc-file "$ANTITHESIS_NETRC" or --netrc as
appropriate), add an explicit JSON content-type header (-H "Content-Type:
application/json"), and harden curl by adding standard options such as --silent
--show-error --location --fail --retry N --retry-delay M --connect-timeout S
--max-time T (choose sensible values) so the request uses the netrc credentials,
sends proper JSON headers, follows redirects, surfaces errors, and
retries/timeouts for resilience.

Comment on lines +3 to +17
import (
"context"
"log"
"os"

"github.com/antithesishq/antithesis-sdk-go/assert"
"github.com/formancehq/ledger/test/antithesis/internal"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"

"k8s.io/client-go/tools/clientcmd"
)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add required imports for patching, trimming, and timeouts.

Needed for the Patch and other fixes.

@@
 import (
   "context"
+  "encoding/json"
   "log"
   "os"
+  "strings"
+  "time"
@@
   "k8s.io/apimachinery/pkg/runtime/schema"
+  "k8s.io/apimachinery/pkg/types"
   "k8s.io/client-go/dynamic"
@@
+  "k8s.io/client-go/rest"
   "k8s.io/client-go/tools/clientcmd"
 )
📝 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.

Suggested change
import (
"context"
"log"
"os"
"github.com/antithesishq/antithesis-sdk-go/assert"
"github.com/formancehq/ledger/test/antithesis/internal"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
)
import (
"context"
"encoding/json"
"log"
"os"
"strings"
"time"
"github.com/antithesishq/antithesis-sdk-go/assert"
"github.com/formancehq/ledger/test/antithesis/internal"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
🤖 Prompt for AI Agents
In test/antithesis/workload/bin/cmds/anytime_version_upgrade/main.go around
lines 3 to 17, the import block is missing packages required for
creating/patching resources, trimming strings, and handling timeouts; add the
necessary imports (k8s.io/apimachinery/pkg/types for PatchType, strings for
trimming, and time for timeouts) to the import list so the subsequent patch
operations, string trimming calls, and timeout logic compile and work as
intended.

Comment on lines +21 to +24
latest_tag, err := os.ReadFile("/ledger_latest_tag")
if err != nil {
log.Fatal(err)
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Trim newline from LEDGER_LATEST_TAG and validate non-empty.

echo writes a trailing newline; passing it verbatim can break version parsing.

@@
- latest_tag, err := os.ReadFile("/ledger_latest_tag")
+ b, err := os.ReadFile("/ledger_latest_tag")
   if err != nil {
     log.Fatal(err)
   }
+ tag := strings.TrimSpace(string(b))
+ if tag == "" {
+   log.Fatal("empty LEDGER_LATEST_TAG")
+ }
@@
-       "version": string(latest_tag),
+       "version": tag,

Also applies to: 50-53


#!/bin/bash
# Locate the main.go file for anytime_version_upgrade command
find . -type f -path "*/anytime_version_upgrade/main.go"

#!/bin/bash
# Locate the main.go for the anytime_version_upgrade command
file=$(find . -type f -path "*/anytime_version_upgrade/main.go")
echo "Found: $file"

# Display imports and code around lines 1-60 (to inspect whether strings is imported and the ReadFile usage)
sed -n '1,60p' "$file"
echo "----"
# Display code around lines 45-80 (to see the assignment at 50-53)
sed -n '45,80p' "$file"

#!/bin/bash
# Locate the file under test
filepath=$(find . -type f -path "*/anytime_version_upgrade/main.go")
echo "Found file: $filepath"

# Show the first 60 lines to inspect imports and initial ReadFile usage
sed -n '1,60p' "$filepath"

# Show lines 45–80 to cover the later ReadFile handling (lines 50–53)
sed -n '45,80p' "$filepath"

Trim and validate LEDGER_LATEST_TAG

Ensure the value read from /ledger_latest_tag has no trailing newline and isn’t empty:

@@ lines 21–24
-   latest_tag, err := os.ReadFile("/ledger_latest_tag")
+   b, err := os.ReadFile("/ledger_latest_tag")
    if err != nil {
      log.Fatal(err)
    }
+   tag := strings.TrimSpace(string(b))
+   if tag == "" {
+     log.Fatal("empty LEDGER_LATEST_TAG")
+   }
@@ lines 50–53
-       "version": string(latest_tag),
+       "version": tag,

Apply the same change around lines 50–53.

🤖 Prompt for AI Agents
In test/antithesis/workload/bin/cmds/anytime_version_upgrade/main.go around
lines 21–24, the code reads /ledger_latest_tag without trimming or validating
the contents; trim whitespace/newlines from the read bytes, convert to a string,
and error if the resulting tag is empty; apply the same trimming and non-empty
validation to the similar read at lines 50–53 so neither location uses a raw
file value with trailing newlines or empty content.

@@ -0,0 +1,51 @@
VERSION 0.7

FROM --platform=linux/amd64 golang:1.24.6
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Pin base images to stable tags (ideally digests).

golang:1.24.6 may not exist; ubuntu:latest drifts and can break builds or introduce CVEs. Pin to a known minor and/or digest.

-FROM --platform=linux/amd64 golang:1.24.6
+FROM --platform=linux/amd64 golang:1.22
@@
-    FROM --platform=linux/amd64 ubuntu:latest
+    FROM --platform=linux/amd64 ubuntu:22.04

If you have approved versions, use those or image digests.

Also applies to: 38-38


🌐 Web query:

Does the Docker image tag "golang:1.24.6" exist, and what is the latest stable Go minor release?

💡 Result:

Yes — the golang:1.24.6 tag exists (including variants like 1.24.6-alpine). [1][2]

The latest stable minor release is Go 1.25 (released August 12, 2025). [3]

Sources:

  • Go release dashboard (go1.24.6 entry). [1]
  • Docker golang images (1.24.6-alpine shown in registries/scans). [2]
  • Official Go release notes / download (Go 1.25 release, Aug 12, 2025). [3]

Pin base images to immutable digests
golang:1.24.6 does exist, but relying on unpinned tags risks drift. Upgrade to the current minor release (Go 1.25, released August 12, 2025) and reference its digest—for example:

FROM --platform=linux/amd64 golang:1.25@sha256:<digest>

Likewise, replace ubuntu:latest with a specific LTS version and its digest, e.g.:

FROM --platform=linux/amd64 ubuntu:22.04@sha256:<digest>

This ensures reproducible, stable builds.

🤖 Prompt for AI Agents
In test/antithesis/workload/Earthfile around line 3, the base image is
referenced by an unpinned tag ("golang:1.24.6"), which risks drift; update the
Go base to the current minor release (Go 1.25) and replace the tag with the
immutable image digest (e.g., golang:1.25@sha256:<digest>) and likewise replace
any ubuntu:latest usage with a specific LTS version and its digest (e.g.,
ubuntu:22.04@sha256:<digest>) so builds are reproducible; obtain the correct
sha256 digests from the official image registries and substitute them in the
FROM lines.

COPY +sources/src /src

WORKDIR /src/test/antithesis/workload
COPY --dir ../../../pkg/client+client/client ../../../pkg/client
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify COPY --dir rename semantics; simplify if not required.

The “+client/client” rename is easy to misread and may be unnecessary if the original layout is already pkg/client/client.

If not required, prefer:

-COPY --dir ../../../pkg/client+client/client ../../../pkg/client
+COPY --dir ../../../pkg/client ../../../pkg/client

If the rename is intentional, add a short comment explaining why.


🌐 Web query:

In Earthly, is "COPY --dir <src>+<newname> <dst>" the correct syntax to rename the root folder during copy?

💡 Result:

No. The correct forms put the artifact/dir as the source and the new name as the destination. For local dirs:

  • COPY --dir test newname
    For artifact form:
  • COPY --dir +target/path newname

Earthly's docs show COPY --dir and artifact refs as +target/artifact-path. [1][2]

Sources:
[1] Earthfile COPY docs (options & examples). [2] Importing / artifact reference examples.


Fix COPY --dir syntax
The current line

COPY --dir ../../../pkg/client+client/client ../../../pkg/client  

misuses the + operator—Earthly’s syntax is COPY --dir <src> <dest>, with a leading + only on artifact refs. For a local directory, use for example:

COPY --dir ../../../pkg/client pkg/client  

If you meant to rename the root folder, use:

COPY --dir ../../../pkg/client client  

Or, for an artifact import:

COPY --dir +target/path newname  

Add a comment if any ref or rename is non-obvious.

🤖 Prompt for AI Agents
In test/antithesis/workload/Earthfile around line 24, the COPY --dir invocation
incorrectly uses a '+' inside the source path
("../../../pkg/client+client/client"), violating Earthly syntax; fix by changing
the line to use a plain local source and desired destination (for example COPY
--dir ../../../pkg/client pkg/client or COPY --dir ../../..//pkg/client client
if you intended to rename the root), or if you intended to import an artifact
use the leading plus form (COPY --dir +target/path newname); also add a brief
comment explaining any non-obvious ref or rename.

ARG --required ANTITHESIS_REPOSITORY
ARG --required LEDGER_LATEST_TAG

RUN echo ${LEDGER_LATEST_TAG} > /ledger_latest_tag
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid trailing newline in /ledger_latest_tag.

echo appends “\n”; use printf for exact content.

-RUN echo ${LEDGER_LATEST_TAG} > /ledger_latest_tag
+RUN printf %s "${LEDGER_LATEST_TAG}" > /ledger_latest_tag
📝 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.

Suggested change
RUN echo ${LEDGER_LATEST_TAG} > /ledger_latest_tag
RUN printf %s "${LEDGER_LATEST_TAG}" > /ledger_latest_tag
🤖 Prompt for AI Agents
In test/antithesis/workload/Earthfile around line 49, the current RUN uses echo
which appends a trailing newline to /ledger_latest_tag; replace the echo with a
printf invocation that writes the LEDGER_LATEST_TAG value without adding a
newline (use printf with a %s format and the variable, redirecting output to
/ledger_latest_tag) to ensure the file contains the exact tag and no trailing
newline.


RUN echo ${LEDGER_LATEST_TAG} > /ledger_latest_tag

SAVE IMAGE --push --no-manifest-list ${ANTITHESIS_REPOSITORY}/workload:latest
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Also push a content-addressable tag (e.g., the version).

latest is mutable; add an immutable tag for traceability.

-SAVE IMAGE --push --no-manifest-list ${ANTITHESIS_REPOSITORY}/workload:latest
+SAVE IMAGE --push --no-manifest-list ${ANTITHESIS_REPOSITORY}/workload:latest
+SAVE IMAGE --push --no-manifest-list ${ANTITHESIS_REPOSITORY}/workload:${LEDGER_LATEST_TAG}
📝 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.

Suggested change
SAVE IMAGE --push --no-manifest-list ${ANTITHESIS_REPOSITORY}/workload:latest
SAVE IMAGE --push --no-manifest-list ${ANTITHESIS_REPOSITORY}/workload:latest
SAVE IMAGE --push --no-manifest-list ${ANTITHESIS_REPOSITORY}/workload:${LEDGER_LATEST_TAG}
🤖 Prompt for AI Agents
In test/antithesis/workload/Earthfile around line 51, the image is only pushed
with the mutable tag "latest"; add an additional push of an immutable,
content-addressable tag (for example a version or commit SHA) so the image is
traceable. Modify the Earthfile to produce and push a second tag such as
${ANTITHESIS_REPOSITORY}/workload:${ANTITHESIS_VERSION} (or derive from git
commit SHA) alongside the existing latest push — ensure the image is tagged with
that immutable tag and pushed (or saved) as well so both latest and the
content-addressable tag exist in the registry.

Copy link

@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: 4

♻️ Duplicate comments (1)
test/antithesis/image/Earthfile (1)

42-42: Version the image tag instead of pushing only :latest

Parameterize the tag for traceability (commit SHA, tag, or build number).

-build:
+build:
+    ARG IMAGE_TAG?=latest
 ...
-    SAVE IMAGE --push ghcr.io/formancehq/ledger-instrumented:latest
+    SAVE IMAGE --push ghcr.io/formancehq/ledger-instrumented:${IMAGE_TAG}
🧹 Nitpick comments (3)
test/antithesis/image/Earthfile (3)

24-24: Make build output more deterministic

Add -trimpath and -buildvcs=false; keeps debug info for symbols while removing host paths/VCS metadata.

-    RUN go build -race -o ledger
+    RUN go build -race -trimpath -buildvcs=false -o ledger

29-41: Consider running as non-root in the runtime image

Drop privileges for better isolation.

     COPY (+compile/ledger) /bin/ledger
     COPY (+compile/symbols) /symbols
     COPY entrypoint.sh /bin/entrypoint.sh

-    RUN chmod 777 /bin/entrypoint.sh
+    RUN chmod 0755 /bin/entrypoint.sh \
+     && useradd --system --no-create-home --shell /usr/sbin/nologin ledger
+    USER ledger

7-8: Deduplicate cache directives

You define go caches globally and again in compile; keep one to reduce noise.

-    CACHE --sharing=shared --id go-mod-cache /go/pkg/mod
-    CACHE --sharing=shared --id go-cache /root/.cache/go-build

Also applies to: 12-13

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1ee5795 and af6956b.

⛔ Files ignored due to path filters (1)
  • .github/workflows/antithesis.yml is excluded by !**/*.yml
📒 Files selected for processing (1)
  • test/antithesis/image/Earthfile (1 hunks)
🔇 Additional comments (3)
test/antithesis/image/Earthfile (3)

19-28: LGTM: instrumentation + artifact handoff look coherent

The instrument → build → symbols flow is consistent with Antithesis usage.


15-15: No changes required
The Earthfile at ../../.. defines a sources target that exports /src.


39-41: Ensure ledger serve binds to port 8080
In test/antithesis/image/entrypoint.sh, invoke ledger serve --addr :8080 to match EXPOSE 8080, or update the EXPOSE directive to the actual default listen port.

CACHE --sharing=shared --id go-cache /root/.cache/go-build

compile:
FROM --platform=linux/amd64 golang:1.24.6
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Confirm and pin Go toolchain version

1.24.6 may be behind the latest patch/minor. Please confirm current stable and pin accordingly for reproducibility.


🌐 Web query:

What is the latest stable Go release (minor and patch) as of today?

💡 Result:

The latest stable Go release as of today (September 10, 2025) is Go 1.25.1. [1][2]

Sources:

  • Official Go VERSION file (go1.25.1). [1]
  • Go announcement: "Go 1.25.1 and Go 1.24.7 are released" (Sep 3, 2025). [2]

Pin Go toolchain to latest stable version (1.25.1)

In test/antithesis/image/Earthfile line 11, update:

- FROM --platform=linux/amd64 golang:1.24.6
+ FROM --platform=linux/amd64 golang:1.25.1
📝 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.

Suggested change
FROM --platform=linux/amd64 golang:1.24.6
FROM --platform=linux/amd64 golang:1.25.1
🤖 Prompt for AI Agents
In test/antithesis/image/Earthfile around line 11, the Docker base image is
pinned to golang:1.24.6; update the FROM line to use the latest stable Go
toolchain by replacing golang:1.24.6 with golang:1.25.1 (preserve the
--platform=linux/amd64 flag if needed) so the image builds with Go 1.25.1.

CACHE --sharing=shared --id go-cache /root/.cache/go-build

COPY ../../..+sources/src /src
RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid @latest for the instrumentor; pin the version

Use an ARG and pin to a known tag/commit to keep builds reproducible.

 compile:
+    ARG ANTITHESIS_GO_INSTRUMENTOR_VERSION?=v0.0.0
-    RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
+    RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@${ANTITHESIS_GO_INSTRUMENTOR_VERSION}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In test/antithesis/image/Earthfile around line 16, the RUN uses @latest for
installing the instrumentor which makes builds non-reproducible; add an ARG
(e.g., INSTRUMENTOR_VERSION) with a pinned tag or commit hash (set a sensible
default), then change the RUN line to reference that ARG (e.g.,
github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@${INSTRUMENTOR_VERSION});
commit the Dockerfile/Earthfile change so builds can be reproduced by overriding
the ARG if needed.

Comment on lines +30 to +32
FROM --platform=linux/amd64 ubuntu:latest
RUN apt-get update -y && apt-get install -y postgresql-client curl

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Pin runtime base and reduce image size

Avoid ubuntu:latest; pin LTS and clean apt cache. Also prefer no-install-recommends.

-    FROM --platform=linux/amd64 ubuntu:latest
-    RUN apt-get update -y && apt-get install -y postgresql-client curl
+    FROM --platform=linux/amd64 ubuntu:24.04
+    RUN apt-get update -y \
+     && apt-get install -y --no-install-recommends postgresql-client curl ca-certificates \
+     && rm -rf /var/lib/apt/lists/*
📝 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.

Suggested change
FROM --platform=linux/amd64 ubuntu:latest
RUN apt-get update -y && apt-get install -y postgresql-client curl
FROM --platform=linux/amd64 ubuntu:24.04
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends postgresql-client curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
🤖 Prompt for AI Agents
In test/antithesis/image/Earthfile around lines 30–32, the Dockerfile uses
ubuntu:latest and installs packages without cleaning caches; change the base to
a pinned LTS (e.g., ubuntu:22.04) and install packages with
--no-install-recommends, combine commands, and remove apt lists to reduce image
size: use FROM --platform=linux/amd64 ubuntu:22.04 and replace the RUN with a
single line that runs apt-get update && apt-get install -y
--no-install-recommends postgresql-client curl && apt-get clean && rm -rf
/var/lib/apt/lists/*.

COPY (+compile/symbols) /symbols
COPY entrypoint.sh /bin/entrypoint.sh

RUN chmod 777 /bin/entrypoint.sh
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

chmod 777 is over-permissive

Use 0755 to avoid granting write to group/others.

-    RUN chmod 777 /bin/entrypoint.sh
+    RUN chmod 0755 /bin/entrypoint.sh
📝 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.

Suggested change
RUN chmod 777 /bin/entrypoint.sh
RUN chmod 0755 /bin/entrypoint.sh
🤖 Prompt for AI Agents
In test/antithesis/image/Earthfile around line 37, the Dockerfile uses an
over-permissive file mode (chmod 777) on /bin/entrypoint.sh; change that command
to set the file mode to 0755 so the owner retains write access while
group/others get read+execute only, and keep the rest of the line and file
unchanged.

Copy link

@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: 3

♻️ Duplicate comments (1)
test/antithesis/image/Earthfile (1)

42-42: Version the pushed image tag (avoid :latest)

Use a parameterized, immutable tag for traceability (commit SHA, tag, or timestamp). This was raised earlier; repeating here for this file.

Apply:

-    SAVE IMAGE --push ghcr.io/formancehq/ledger-instrumented:latest
+    ARG IMAGE_TAG?=$(git rev-parse --short HEAD)
+    SAVE IMAGE --push --no-manifest-list ghcr.io/formancehq/ledger-instrumented:${IMAGE_TAG}
🧹 Nitpick comments (2)
test/antithesis/image/Earthfile (2)

7-8: Deduplicate cache declarations

Caches are declared both at top-level and inside the compile target. Keep the per-target ones and remove the top-level to reduce noise.

Apply:

-CACHE --sharing=shared --id go-mod-cache /go/pkg/mod
-CACHE --sharing=shared --id go-cache /root/.cache/go-build

Also applies to: 12-13


39-40: Add a container healthcheck

Expose a simple readiness probe so orchestrators can detect unhealthy containers.

Apply:

     ENTRYPOINT ["/bin/entrypoint.sh"]
     EXPOSE 8080
+    HEALTHCHECK --interval=15s --timeout=3s --retries=5 CMD curl -fsS http://127.0.0.1:8080/_health || exit 1
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1ee5795 and af6956b.

⛔ Files ignored due to path filters (1)
  • .github/workflows/antithesis.yml is excluded by !**/*.yml
📒 Files selected for processing (1)
  • test/antithesis/image/Earthfile (1 hunks)
🔇 Additional comments (2)
test/antithesis/image/Earthfile (2)

11-11: Good upgrade: Go toolchain pinned to 1.24.6

Pinning to a concrete patch release fixes the previous concern about using a non-existent/unstable tag. LGTM.


24-25: Builder/runner libc alignment with -race

-race binaries depend on glibc. Mixing a Debian-based builder with an Ubuntu runtime is usually fine if the runtime glibc is newer, but it can bite in some environments. Consider aligning distro families or explicitly pinning both to known-compatible images.

Would you like me to check the current glibc versions for your selected tags and confirm compatibility?

Also applies to: 30-30

CACHE --sharing=shared --id go-cache /root/.cache/go-build

COPY ../../..+sources/src /src
RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Pin the instrumentor version for reproducible builds

Using @latest makes builds non-deterministic and can break unexpectedly. Pin a tag/commit and expose it as an ARG.

Apply:

 compile:
     FROM --platform=linux/amd64 golang:1.24.6
@@
-    RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
+    ARG INSTRUMENTOR_VERSION=v0.0.0 # set to known-good tag/commit
+    RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@${INSTRUMENTOR_VERSION}
📝 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.

Suggested change
RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
compile:
FROM --platform=linux/amd64 golang:1.24.6
ARG INSTRUMENTOR_VERSION=v0.0.0 # set to known-good tag/commit
RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@${INSTRUMENTOR_VERSION}
🤖 Prompt for AI Agents
In test/antithesis/image/Earthfile around line 16, the Dockerfile uses "go
install ...@latest" which makes builds non-deterministic; change this to accept
a build ARG for the instrumentor version (e.g., INSTRUMENTOR_VERSION) and
replace @latest with that pinned version (defaulting to a specific tag/commit),
so callers can override the ARG but the image builds reproducibly by default;
update the Earthfile to declare the ARG and use it in the RUN line and document
the new ARG in the file comments or build instructions.

Comment on lines +29 to +31
build:
FROM --platform=linux/amd64 ubuntu:latest
RUN apt-get update -y && apt-get install -y postgresql-client curl
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid floating base and improve apt hygiene

Pin the runtime base and harden the apt install to reduce image size and supply CA certs.

Apply:

-build:
-    FROM --platform=linux/amd64 ubuntu:latest
-    RUN apt-get update -y && apt-get install -y postgresql-client curl
+build:
+    FROM --platform=linux/amd64 ubuntu:24.04
+    RUN apt-get update -y \
+     && apt-get install -y --no-install-recommends postgresql-client curl ca-certificates \
+     && rm -rf /var/lib/apt/lists/*
📝 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.

Suggested change
build:
FROM --platform=linux/amd64 ubuntu:latest
RUN apt-get update -y && apt-get install -y postgresql-client curl
build:
FROM --platform=linux/amd64 ubuntu:24.04
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends postgresql-client curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
🤖 Prompt for AI Agents
In test/antithesis/image/Earthfile around lines 29 to 31, the Dockerfile uses a
floating base image and a basic apt install command; update it to pin a specific
Ubuntu LTS tag (e.g., ubuntu:24.04 or another pinned digest/tag) and harden the
apt invocation by installing ca-certificates and postgresql-client with apt-get
install --no-install-recommends, run apt-get update && apt-get install in one
layer, and then clean up apt caches (apt-get clean and remove
/var/lib/apt/lists/*) to reduce image size and ensure CA certs are present.

Comment on lines +37 to +39
RUN chmod 777 /bin/entrypoint.sh

ENTRYPOINT ["/bin/entrypoint.sh"]
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Drop 0777 and run as non-root

0777 is unnecessary and weakens container security. Make the script executable (0755) and run the image as a non-root user.

Apply:

-    RUN chmod 777 /bin/entrypoint.sh
+    RUN chmod 0755 /bin/entrypoint.sh
+    # create unprivileged user
+    RUN useradd -r -u 65532 -g nogroup -d /nonexistent -s /usr/sbin/nologin ledger
+    USER 65532:65532
📝 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.

Suggested change
RUN chmod 777 /bin/entrypoint.sh
ENTRYPOINT ["/bin/entrypoint.sh"]
RUN chmod 0755 /bin/entrypoint.sh
# create unprivileged user
RUN useradd -r -u 65532 -g nogroup -d /nonexistent -s /usr/sbin/nologin ledger
USER 65532:65532
ENTRYPOINT ["/bin/entrypoint.sh"]
🤖 Prompt for AI Agents
In test/antithesis/image/Earthfile around lines 37-39 the Dockerfile currently
sets chmod 777 on /bin/entrypoint.sh and leaves the container running as root;
change the permission to 0755, create a non-root user (e.g., groupadd/useradd or
adduser), chown the entrypoint to that user, and add a USER directive before
ENTRYPOINT so the container runs as that non-root user; ensure the entrypoint
remains executable and any required file ownerships are adjusted for the new
user.

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.

4 participants