Skip to content

feat: capture additional git information when creating a new version/deployment#3830

Merged
chronark merged 15 commits intomainfrom
ENG-1992-rich-git-commit-data
Aug 28, 2025
Merged

feat: capture additional git information when creating a new version/deployment#3830
chronark merged 15 commits intomainfrom
ENG-1992-rich-git-commit-data

Conversation

@mcstepp
Copy link
Collaborator

@mcstepp mcstepp commented Aug 21, 2025

What does this PR do?

Fixes #3791

Database Schema Changes

Added the following fields to the deployments table:

  • git_commit_message (text) - Full commit message
  • git_commit_author_name (varchar(256)) - Author's full name
  • git_commit_author_username (varchar(256)) - GitHub username
  • git_commit_author_avatar_url (varchar(512)) - GitHub avatar URL
  • git_commit_timestamp (bigint) - Commit timestamp in Unix milliseconds

Protobuf API Updates

Updated both CreateVersionRequest and Version messages in
/go/proto/ctrl/v1/version.proto to include:

  • git_commit_message
  • git_commit_author_name
  • git_commit_author_username
  • git_commit_author_avatar_url
  • git_commit_timestamp

SQL Query Updates

Modified SQL queries in /go/pkg/db/queries/:

  • deployment_insert.sql - Updated INSERT to include all new git fields
  • deployment_find_by_id.sql - Updated SELECT to return all new git fields

Service Implementation Updates

Enhanced both services in /go/apps/ctrl/services/deployment/:

  • create_version.go:65-85 - Updated to populate new git fields from request
  • get_version.go:22-70 - Updated to return all git information in API responses

Key Benefits

  1. Rich Git Context: Captures complete commit information including author
    details and timestamps
  2. GitHub Integration Ready: Avatar URLs and usernames enable GitHub profile
    integration - ACTUAL FETCHING FROM GITHUB API TODO
  3. Audit Trail: Full commit messages and timestamps provide comprehensive
    deployment history
  4. Backward Compatible: All new fields are optional and don't break existing
    functionality

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  1. API testing - PLEASE NOTE THAT THE GITHUB WEBHOOK ISN'T FULLY INTEGRATED YET. You'll have to manually populate the fields until we integrate the github hook that retrieves it from the associated repos.
  • Use a tool like curl or Postman to call the CreateVersion endpoint with the
    new git fields populated
  • Verify the deployment record is created with all git information stored
    correctly
  • Call GetVersion endpoint and confirm all git fields are returned in the
    response
  • Test with both populated and empty git fields to ensure null handling works
    properly
  1. Database Verification
  • Query the deployments table directly after creating a version to inspect the
    stored git data
  • Verify that nullable fields properly store NULL when not provided
  • Check that timestamps are stored in the expected Unix millisecond format
  • Validate that text fields like commit messages handle special characters and
    encoding correctly

Unit tests included:

  • Git field mappings between protobuf and database
  • Special character handling (unicode, quotes, newlines)
  • NULL value handling for optional fields
  • Timestamp conversion validation

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

  • New Features

    • Deployment versions now capture and return Git metadata (commit message, author name/email/username/avatar URL, and commit timestamp). Fields are normalized, truncated to safe lengths, and timestamps validated (milliseconds, not seconds, not >1h future). Rootfs Image ID and latest Build ID are included when available; workflow start failures are logged without failing creation.
  • Tests

    • Added comprehensive tests for new Git fields, null/special-character handling, mapping, and timestamp boundaries/validation.
  • Chores

    • Updated generated artifacts and bulk/insert/query paths to carry the new Git metadata; dependency/tooling version bumps and a new make target for tools.

@linear
Copy link

linear bot commented Aug 21, 2025

@changeset-bot
Copy link

changeset-bot bot commented Aug 21, 2025

⚠️ No Changeset found

Latest commit: cf89a8d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Aug 21, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
dashboard Ignored Ignored Preview Aug 28, 2025 7:03pm
engineering Ignored Ignored Preview Aug 28, 2025 7:03pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 21, 2025

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

42 files out of 149 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Adds storage, validation, normalization, and retrieval of six Git commit metadata fields (message, author name/email/username/avatar URL, timestamp ms). CreateVersion validates/limits inputs and persists them; GetVersion populates proto fields; DB schema and bulk insert updated; extensive unit tests added; many sqlc headers bumped.

Changes

Cohort / File(s) Summary
Deployment service: create version
go/apps/ctrl/services/deployment/create_version.go
Add validation (timestamp must be ms and ≤ now+1h), trimming, lowercase email, and rune-limits; use helper limitString; populate new InsertDeploymentParams Git fields and insert deployment with pending status; start workflow (errors logged, non-fatal).
Deployment service: get version
go/apps/ctrl/services/deployment/get_version.go
Populate six new Git fields into ctrlv1.Version when DB values valid; map RootfsImageIDRootfsImageId; fetch latest build and deployment steps/hostnames non-fatally.
Deployment service tests
go/apps/ctrl/services/deployment/create_version_simple_test.go
New unit tests covering Git field preservation, normalization/null handling, rune limits, timestamp ms vs s validation, boundary cases, and request→DB param mapping.
DB: deployment queries/models & bulk insert
go/pkg/db/queries/deployment_insert.sql, go/pkg/db/queries/deployment_find_by_id.sql, go/pkg/db/deployment_insert.sql_generated.go, go/pkg/db/deployment_find_by_id.sql_generated.go, go/pkg/db/models_generated.go, go/pkg/db/bulk_deployment_insert.sql.go, go/pkg/db/deployment_insert.sql_generated.go, go/pkg/db/models_generated.go
Add six git metadata columns to INSERT/SELECT and generated types: git_commit_message, git_commit_author_name, git_commit_author_email, git_commit_author_username, git_commit_author_avatar_url, git_commit_timestamp (ms). Update InsertDeploymentParams, Deployment model, and bulk insert placeholders/args.
DB: sqlc regen (headers, docs)
go/pkg/db/.../*.sql_generated.go
Multiple generated files updated with sqlc version header bumps and doc comments; most are header-only.
Misc: tooling & modules
go/Makefile, go/go.mod
Add make tools target installing CLI tools; bump multiple Go module dependencies (including sqlc/tooling and test deps).
Misc: retry util
go/pkg/db/retry.go
Small whitespace/formatting trim; no behavioral change.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Ctrl as Ctrl Deployment Service
  participant DB
  participant WF as Workflow Engine

  rect rgb(245,248,255)
  note over Client,Ctrl: CreateVersion
  Client->>Ctrl: CreateVersion(req with git metadata)
  Ctrl->>Ctrl: validate/normalize git fields (trim, lower email, rune limits, timestamp ms & ≤ now+1h)
  Ctrl->>DB: InsertDeployment(...git_* fields..., status=pending)
  DB-->>Ctrl: deployment_id
  Ctrl->>WF: StartDeploymentWorkflow(deployment_id)
  alt workflow start fails
    WF-->>Ctrl: error (logged, non-fatal)
  else success
    WF-->>Ctrl: ok
  end
  Ctrl-->>Client: Version{Status=pending, VersionId=deployment_id}
  end

  rect rgb(245,255,245)
  note over Client,Ctrl: GetVersion
  Client->>Ctrl: GetVersion(version_id)
  Ctrl->>DB: FindDeploymentById(version_id)
  DB-->>Ctrl: Deployment (incl. git_* fields, rootfs_image_id)
  Ctrl->>DB: FindLatestBuildByDeploymentId(version_id)
  DB-->>Ctrl: Build (optional)
  Ctrl-->>Client: Version{git_* fields, RootfsImageId, BuildId?}
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Store and expose rich Git commit data (message, author, timestamp, branch) [#3791]
Validate and persist millisecond commit timestamps; reject seconds-based inputs [#3791]
Update UI components (ProjectCard) to display commit message, author, avatar, human-readable timestamp, truncation, and commit links [#3791] Backend adds data only; no frontend changes or link generation implemented.

Out-of-scope changes

Code Change Explanation
sqlc-generated header bumps in many files (e.g. go/pkg/db/api_find_by_id.sql_generated.go) Metadata-only version/header updates from sqlc regen; not related to Git metadata feature.
Go module dependency bumps in go/go.mod Broad dependency updates unrelated to implementing or validating Git metadata storage/retrieval.
make tools addition in go/Makefile Tooling convenience target; unrelated to feature objectives.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ENG-1992-rich-git-commit-data

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link
Contributor

github-actions bot commented Aug 21, 2025

Thank you for following the naming conventions for pull request titles! 🙏

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 53

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (15)
go/pkg/db/key_delete_by_id.sql_generated.go (1)

1-10: Ensure all SQLC‐generated files are on v1.29.0

It looks like three generated files were missed in the bump and still carry v1.27.0, which will lead to CI drift unless they’re regenerated:

  • go/pkg/db/key_update_credits_decrement.sql_generated.go
  • go/pkg/db/key_update_credits_increment.sql_generated.go
  • go/pkg/db/key_update_credits_set.sql_generated.go

→ Regenerate these with sqlc v1.29.0 so every *.sql_generated.go is consistent.

Additionally, I spotted two DBTX interface definitions (one in go/pkg/db/interface.go, one in go/pkg/hydra/store/db.go). If the Hydra‐specific one is outdated or unneeded, please reconcile or remove it.

Good news: the DeleteKeyByID(ctx, db DBTX, id string) error signature hasn’t changed, and all call sites (e.g. in querier_generated.go and your HTTP handler) will continue to work unchanged.

go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (1)

18-21: Nit: initialism casing (“IDs”) for consistency

If you ever customize sqlc naming, consider RoleIDs to follow Go initialism conventions. Not a blocker, and acceptable for generated code as-is.

go/pkg/db/api_insert.sql_generated.go (1)

1-78: Regenerate outdated SQLC files and pin SQLC version in config

Three generated files remain at sqlc v1.27.0 and must be regenerated with v1.29.0:

  • go/pkg/db/key_update_credits_increment.sql_generated.go (// sqlc v1.27.0)
  • go/pkg/db/key_update_credits_set.sql_generated.go (// sqlc v1.27.0)
  • go/pkg/db/key_update_credits_decrement.sql_generated.go (// sqlc v1.27.0)

Your sqlc.json configs currently lack any explicit SQLC version pinning. To prevent drift, add a version constraint—either by:

  • Specifying the SQLC CLI version in a lockfile or toolchain file (e.g., sqlc.version: "1.29.0"),
  • Or pinning the SQLC version in your CI/development workflow (e.g., install sqlc@v1.29.0 via npm or brew install sqlc@1.29.0).

Finally, the migrations timestamp check didn’t run because no migrations/ or db/ directories were found. Ensure your migrations path is correct (or adjust the grep patterns) so you can verify that all timestamp columns (e.g., created_at_m, updated_at_m, git_commit_timestamp) follow the intended naming and units conventions.

go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go (1)

14-35: Consider ordering permissions inside JSON_ARRAYAGG for deterministic output.
The subselect feeding JSON_ARRAYAGG lacks ORDER BY, so response order can vary. If clients diff or cache by value, this can cause noise. Adjust the source SQL (not this generated file) to include an ORDER BY (e.g., by permission.name or permission.id).

Example source SQL change (apply in go/pkg/db/queries/role_find_many_by_id_or_name_with_perms.sql):

-         FROM (SELECT name, id, slug, description
-               FROM roles_permissions rp
-                        JOIN permissions p ON p.id = rp.permission_id
-               WHERE rp.role_id = r.id) as permission),
+         FROM (SELECT name, id, slug, description
+               FROM roles_permissions rp
+               JOIN permissions p ON p.id = rp.permission_id
+               WHERE rp.role_id = r.id
+               ORDER BY p.name ASC, p.id ASC) as permission),
go/pkg/db/ratelimit_override_update.sql_generated.go (1)

13-21: Consider naming bind parameters in source SQL to improve generated param names.

The generated param struct uses Windowlimit/Now due to anonymous “?” placeholders and a reserved word column. To improve readability and future diffs, annotate placeholders with names in the SQL source and re-generate. Example:

-UPDATE `ratelimit_overrides`
-SET
-    `limit` = ?,
-    duration = ?,
-    async = ?,
-    updated_at_m= ?
-WHERE id = ?
+UPDATE `ratelimit_overrides`
+SET
+    `limit`       = /*limit*/?,
+    duration      = /*duration*/?,
+    async         = /*async*/?,
+    updated_at_m  = /*now*/?
+WHERE id = /*id*/?

This keeps the generated struct fields intuitive (Limit, Duration, Async, Now, ID) and avoids awkward names like Windowlimit.

Also applies to: 23-31

go/pkg/db/key_list_by_key_auth_id.sql_generated.go (1)

15-25: Duplicated column alias ‘identity_id’ (from k.identity_id and i.id) is confusing

The SELECT projects k.identity_id and i.id AS identity_id. While the scan order is correct, the duplicated alias reduces readability and increases maintenance risk. Consider aliasing the joined identity id distinctly in the source SQL (not the generated file), then regenerate.

Example change in go/pkg/db/queries/key_list_by_key_auth_id.sql (then re-run sqlc):

-  i.id as identity_id,
+  i.id as joined_identity_id,

Callers can then reference the generated field (e.g., JoinedIdentityID) unambiguously after regeneration.

go/pkg/db/ratelimit_delete_many_by_identity_id.sql_generated.go (1)

20-22: Ensure callers never pass a NULL identity_id

The function accepts sql.NullString. If Valid=false, the statement becomes WHERE identity_id = NULL, which matches no rows (likely a silent no-op). If deletion-by-null is never intended, enforce a non-null string at call sites or in the source SQL signature, and consider asserting Valid in the service layer.

go/pkg/db/identity_find.sql_generated.go (1)

12-18: Confirm index coverage for WHERE clause

Query filters by (workspace_id, external_id OR id, deleted). Ensure composite indexes exist to keep this hot path fast at scale (e.g., (workspace_id, external_id, deleted) and (workspace_id, id, deleted)) if not already present. Not a blocker for this PR, but worth verifying.

go/pkg/db/permission_list_by_role_id.sql_generated.go (1)

12-18: Ordering by slug: confirm desired collation

Ordering by p.slug is deterministic. If slugs can differ only by case or locale, confirm DB collation matches desired ordering (case-insensitive vs sensitive). Non-blocking.

go/pkg/db/role_find_by_name_and_workspace_id.sql_generated.go (1)

12-18: Uniqueness assumptions and LIMIT 1

Using LIMIT 1 implies (workspace_id, name) should be unique. If not enforced already, consider a unique index to prevent duplicate roles and guarantee deterministic selection.

go/pkg/db/key_update_credits_refill.sql_generated.go (1)

26-28: Idempotency note: safe to retry; keep contrast with decrement ops

This UPDATE sets columns to explicit values (idempotent), so wrapping with a generic retry helper is safe. Contrast with decrement-style updates flagged in prior learnings (double-charge risk). Keep this distinction documented near call sites.

go/pkg/db/ratelimit_override_insert.sql_generated.go (1)

41-50: Minor: struct tags vs column names

The db tags use created_at/updated_at while columns are created_at_m/updated_at_m. It’s harmless for Exec order, but can confuse readers and tools that rely on tags. If desired, align via sqlc config or column aliases in SQL.

go/pkg/db/ratelimit_namespace_insert.sql_generated.go (1)

12-31: Insert defaults: explicit NULLs OK; consider server defaults

Explicit NULLs for updated_at_m and deleted_at_m are fine. If the schema supports DEFAULT NULL already, you could omit those columns for slightly cleaner SQL in the source file; otherwise, no action.

go/pkg/db/ratelimit_delete_many_by_ids.sql_generated.go (1)

23-31: Large IN lists: consider chunking to avoid driver limits

Dynamic placeholder expansion is correct and safe. For very large id slices, some drivers/DBs hit parameter or packet limits. Provide a non-generated wrapper that chunks into batches (e.g., 500–1000 ids) and calls this method per chunk.

For example, add a helper outside generated code:

// in go/pkg/db/ratelimit_helpers.go (non-generated)
func (q *Queries) DeleteRatelimitsByIDsChunked(ctx context.Context, db DBTX, ids []string, chunk int) error {
    for i := 0; i < len(ids); i += chunk {
        end := i + chunk
        if end > len(ids) { end = len(ids) }
        if err := q.DeleteManyRatelimitsByIDs(ctx, db, ids[i:end]); err != nil {
            return err
        }
    }
    return nil
}
go/apps/ctrl/services/deployment/get_version.go (1)

15-19: Differentiate NotFound from Internal errors.

Returning CodeNotFound for any DB error will mask real failures. Use the db.IsNotFound helper to set the correct Connect error code.

-	deployment, err := db.Query.FindDeploymentById(ctx, s.db.RO(), req.Msg.GetVersionId())
-	if err != nil {
-		return nil, connect.NewError(connect.CodeNotFound, err)
-	}
+	deployment, err := db.Query.FindDeploymentById(ctx, s.db.RO(), req.Msg.GetVersionId())
+	if err != nil {
+		if db.IsNotFound(err) {
+			return nil, connect.NewError(connect.CodeNotFound, err)
+		}
+		return nil, connect.NewError(connect.CodeInternal, err)
+	}
♻️ Duplicate comments (1)
go/pkg/db/keyring_insert.sql_generated.go (1)

3-3: sqlc v1.29.0 header bump confirmed. Insert SQL unchanged.

Note: The hardcoded zero values for size_approx and size_last_updated_at remain intentional per prior guidance; no change required.

Run the consistency check script to ensure all generated DB files were regenerated with v1.29.0.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • 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 f66a269 and a851ef8.

⛔ Files ignored due to path filters (11)
  • go/gen/proto/ctrl/v1/build.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • go/gen/proto/ctrl/v1/openapi.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • go/gen/proto/ctrl/v1/routing.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • go/gen/proto/ctrl/v1/service.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • go/gen/proto/ctrl/v1/version.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • go/gen/proto/deploy/assetmanagerd/v1/asset.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • go/gen/proto/deploy/billaged/v1/billing.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • go/gen/proto/deploy/builderd/v1/builder.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • go/gen/proto/metal/vmprovisioner/v1/vmprovisioner.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • go/gen/proto/vault/v1/object.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • go/gen/proto/vault/v1/service.pb.go is excluded by !**/*.pb.go, !**/gen/**
📒 Files selected for processing (107)
  • go/apps/ctrl/services/deployment/create_version.go (1 hunks)
  • go/apps/ctrl/services/deployment/create_version_simple_test.go (1 hunks)
  • go/apps/ctrl/services/deployment/get_version.go (2 hunks)
  • go/pkg/db/api_find_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/api_insert.sql_generated.go (1 hunks)
  • go/pkg/db/api_soft_delete.sql_generated.go (1 hunks)
  • go/pkg/db/api_update_delete_protection.sql_generated.go (1 hunks)
  • go/pkg/db/audit_log_find_target_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/audit_log_insert.sql_generated.go (1 hunks)
  • go/pkg/db/audit_log_target_insert.sql_generated.go (1 hunks)
  • go/pkg/db/build_find_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/build_find_latest_by_deployment_id.sql_generated.go (1 hunks)
  • go/pkg/db/build_insert.sql_generated.go (1 hunks)
  • go/pkg/db/build_update_failed.sql_generated.go (1 hunks)
  • go/pkg/db/build_update_status.sql_generated.go (1 hunks)
  • go/pkg/db/build_update_succeeded.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_deployment_insert.sql.go (3 hunks)
  • go/pkg/db/deployment_find_by_id.sql_generated.go (4 hunks)
  • go/pkg/db/deployment_insert.sql_generated.go (6 hunks)
  • go/pkg/db/deployment_queries_test.go (1 hunks)
  • go/pkg/db/deployment_step_find_by_deployment_id.sql_generated.go (1 hunks)
  • go/pkg/db/deployment_step_insert.sql_generated.go (1 hunks)
  • go/pkg/db/deployment_update_openapi_spec.sql_generated.go (1 hunks)
  • go/pkg/db/deployment_update_status.sql_generated.go (1 hunks)
  • go/pkg/db/identity_delete.sql_generated.go (1 hunks)
  • go/pkg/db/identity_delete_old_with_ratelimits.sql_generated.go (1 hunks)
  • go/pkg/db/identity_find.sql_generated.go (1 hunks)
  • go/pkg/db/identity_insert.sql_generated.go (1 hunks)
  • go/pkg/db/identity_insert_ratelimit.sql_generated.go (1 hunks)
  • go/pkg/db/identity_list.sql_generated.go (1 hunks)
  • go/pkg/db/identity_list_ratelimits.sql_generated.go (1 hunks)
  • go/pkg/db/identity_list_ratelimits_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/identity_list_ratelimits_by_ids.sql_generated.go (1 hunks)
  • go/pkg/db/identity_soft_delete.sql_generated.go (1 hunks)
  • go/pkg/db/identity_update.sql_generated.go (1 hunks)
  • go/pkg/db/key_delete_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_encryption_find_by_key_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_encryption_insert.sql_generated.go (1 hunks)
  • go/pkg/db/key_find_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_find_credits.sql_generated.go (1 hunks)
  • go/pkg/db/key_find_for_verification.sql_generated.go (1 hunks)
  • go/pkg/db/key_find_live_by_hash.sql_generated.go (1 hunks)
  • go/pkg/db/key_find_live_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_insert.sql_generated.go (1 hunks)
  • go/pkg/db/key_insert_ratelimit.sql_generated.go (1 hunks)
  • go/pkg/db/key_list_by_key_auth_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_permission_delete_all_by_key_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_permission_delete_by_key_and_permission_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go (1 hunks)
  • go/pkg/db/key_permission_delete_many_by_permission_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_permission_insert.sql_generated.go (1 hunks)
  • go/pkg/db/key_role_delete_all_by_key_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (1 hunks)
  • go/pkg/db/key_role_delete_many_by_key_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_role_delete_many_by_role_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_role_find_by_key_and_role_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_role_insert.sql_generated.go (1 hunks)
  • go/pkg/db/key_soft_delete_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_soft_delete_many_by_key_auth_id.sql_generated.go (1 hunks)
  • go/pkg/db/key_update.sql_generated.go (1 hunks)
  • go/pkg/db/key_update_credits_refill.sql_generated.go (1 hunks)
  • go/pkg/db/keyring_find_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/keyring_insert.sql_generated.go (1 hunks)
  • go/pkg/db/keyring_update_key_encryption.sql_generated.go (1 hunks)
  • go/pkg/db/models_generated.go (2 hunks)
  • go/pkg/db/permission_delete_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/permission_find_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/permission_find_by_id_or_slug.sql_generated.go (1 hunks)
  • go/pkg/db/permission_find_by_name_and_workspace_id.sql_generated.go (1 hunks)
  • go/pkg/db/permission_find_by_slug_and_workspace_id.sql_generated.go (1 hunks)
  • go/pkg/db/permission_find_by_slugs.sql_generated.go (1 hunks)
  • go/pkg/db/permission_insert.sql_generated.go (1 hunks)
  • go/pkg/db/permission_list.sql_generated.go (1 hunks)
  • go/pkg/db/permission_list_by_key_id.sql_generated.go (1 hunks)
  • go/pkg/db/permission_list_by_role_id.sql_generated.go (1 hunks)
  • go/pkg/db/permission_list_direct_by_key_id.sql_generated.go (1 hunks)
  • go/pkg/db/project_find_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/project_find_by_workspace_slug.sql_generated.go (1 hunks)
  • go/pkg/db/project_insert.sql_generated.go (1 hunks)
  • go/pkg/db/querier_generated.go (4 hunks)
  • go/pkg/db/queries/deployment_find_by_id.sql (1 hunks)
  • go/pkg/db/queries/deployment_insert.sql (2 hunks)
  • go/pkg/db/ratelimit_delete.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_delete_many_by_identity_id.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_delete_many_by_ids.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_list_by_key_id.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_list_by_key_ids.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_namespace_delete.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_namespace_find.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_namespace_find_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_namespace_find_by_name.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_namespace_insert.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_namespace_soft_delete.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_override_find_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_override_find_by_identifier.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_override_insert.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_override_list_by_namespace_id.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_override_soft_delete.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_override_update.sql_generated.go (1 hunks)
  • go/pkg/db/ratelimit_update.sql_generated.go (1 hunks)
  • go/pkg/db/role_delete_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/role_find_by_id.sql_generated.go (1 hunks)
  • go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go (1 hunks)
  • go/pkg/db/role_find_by_name_and_workspace_id.sql_generated.go (1 hunks)
  • go/pkg/db/role_find_by_names.sql_generated.go (1 hunks)
  • go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go (1 hunks)
  • go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go (1 hunks)
⛔ Files not processed due to max files limit (18)
  • go/pkg/db/role_insert.sql_generated.go
  • go/pkg/db/role_list.sql_generated.go
  • go/pkg/db/role_list_by_key_id.sql_generated.go
  • go/pkg/db/role_permission_delete_many_by_permission_id.sql_generated.go
  • go/pkg/db/role_permission_delete_many_by_role_id.sql_generated.go
  • go/pkg/db/role_permission_find_by_role_and_permission_id.sql_generated.go
  • go/pkg/db/role_permission_insert.sql_generated.go
  • go/pkg/db/route_find_by_deployment_id.sql_generated.go
  • go/pkg/db/route_insert.sql_generated.go
  • go/pkg/db/schema.sql
  • go/pkg/db/workspace_find_by_id.sql_generated.go
  • go/pkg/db/workspace_hard_delete.sql_generated.go
  • go/pkg/db/workspace_insert.sql_generated.go
  • go/pkg/db/workspace_list.sql_generated.go
  • go/pkg/db/workspace_soft_delete.sql_generated.go
  • go/pkg/db/workspace_update_enabled.sql_generated.go
  • go/pkg/db/workspace_update_plan.sql_generated.go
  • go/proto/ctrl/v1/version.proto
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Follow comprehensive documentation guidelines for Go code as described in go/GO_DOCUMENTATION_GUIDELINES.md
Every public function/type in Go code must be documented
Prefer interfaces for testability in Go code
Use AIDEV-* comments for complex/important code in Go services

Files:

  • go/pkg/db/ratelimit_list_by_key_ids.sql_generated.go
  • go/pkg/db/deployment_update_openapi_spec.sql_generated.go
  • go/pkg/db/ratelimit_update.sql_generated.go
  • go/pkg/db/identity_delete.sql_generated.go
  • go/pkg/db/key_encryption_insert.sql_generated.go
  • go/pkg/db/audit_log_insert.sql_generated.go
  • go/pkg/db/ratelimit_namespace_delete.sql_generated.go
  • go/pkg/db/key_soft_delete_many_by_key_auth_id.sql_generated.go
  • go/pkg/db/key_update.sql_generated.go
  • go/pkg/db/key_role_delete_many_by_key_id.sql_generated.go
  • go/pkg/db/audit_log_target_insert.sql_generated.go
  • go/pkg/db/api_update_delete_protection.sql_generated.go
  • go/pkg/db/key_find_live_by_id.sql_generated.go
  • go/pkg/db/identity_list_ratelimits.sql_generated.go
  • go/pkg/db/project_find_by_workspace_slug.sql_generated.go
  • go/pkg/db/ratelimit_override_find_by_id.sql_generated.go
  • go/pkg/db/key_find_by_id.sql_generated.go
  • go/pkg/db/identity_list_ratelimits_by_id.sql_generated.go
  • go/pkg/db/key_delete_by_id.sql_generated.go
  • go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go
  • go/pkg/db/key_find_for_verification.sql_generated.go
  • go/pkg/db/role_find_by_name_and_workspace_id.sql_generated.go
  • go/pkg/db/build_update_succeeded.sql_generated.go
  • go/pkg/db/permission_list_direct_by_key_id.sql_generated.go
  • go/pkg/db/key_insert_ratelimit.sql_generated.go
  • go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go
  • go/pkg/db/role_delete_by_id.sql_generated.go
  • go/pkg/db/build_find_latest_by_deployment_id.sql_generated.go
  • go/pkg/db/identity_insert_ratelimit.sql_generated.go
  • go/pkg/db/audit_log_find_target_by_id.sql_generated.go
  • go/pkg/db/role_find_by_id.sql_generated.go
  • go/pkg/db/permission_find_by_id.sql_generated.go
  • go/pkg/db/project_find_by_id.sql_generated.go
  • go/pkg/db/key_encryption_find_by_key_id.sql_generated.go
  • go/pkg/db/keyring_find_by_id.sql_generated.go
  • go/pkg/db/keyring_update_key_encryption.sql_generated.go
  • go/pkg/db/ratelimit_delete_many_by_identity_id.sql_generated.go
  • go/pkg/db/deployment_step_insert.sql_generated.go
  • go/pkg/db/ratelimit_override_update.sql_generated.go
  • go/pkg/db/key_find_credits.sql_generated.go
  • go/pkg/db/deployment_update_status.sql_generated.go
  • go/pkg/db/key_soft_delete_by_id.sql_generated.go
  • go/pkg/db/key_role_insert.sql_generated.go
  • go/pkg/db/permission_find_by_slugs.sql_generated.go
  • go/pkg/db/ratelimit_namespace_soft_delete.sql_generated.go
  • go/pkg/db/deployment_step_find_by_deployment_id.sql_generated.go
  • go/pkg/db/key_role_delete_many_by_role_id.sql_generated.go
  • go/pkg/db/build_update_status.sql_generated.go
  • go/pkg/db/role_find_by_names.sql_generated.go
  • go/pkg/db/project_insert.sql_generated.go
  • go/pkg/db/ratelimit_namespace_find.sql_generated.go
  • go/pkg/db/identity_list_ratelimits_by_ids.sql_generated.go
  • go/pkg/db/permission_find_by_name_and_workspace_id.sql_generated.go
  • go/pkg/db/ratelimit_override_insert.sql_generated.go
  • go/pkg/db/permission_list.sql_generated.go
  • go/pkg/db/identity_update.sql_generated.go
  • go/pkg/db/build_insert.sql_generated.go
  • go/pkg/db/permission_list_by_role_id.sql_generated.go
  • go/pkg/db/ratelimit_list_by_key_id.sql_generated.go
  • go/pkg/db/ratelimit_delete_many_by_ids.sql_generated.go
  • go/pkg/db/ratelimit_namespace_insert.sql_generated.go
  • go/pkg/db/ratelimit_delete.sql_generated.go
  • go/pkg/db/ratelimit_override_soft_delete.sql_generated.go
  • go/pkg/db/identity_insert.sql_generated.go
  • go/pkg/db/key_role_delete_all_by_key_id.sql_generated.go
  • go/pkg/db/ratelimit_namespace_find_by_id.sql_generated.go
  • go/pkg/db/permission_insert.sql_generated.go
  • go/pkg/db/key_role_find_by_key_and_role_id.sql_generated.go
  • go/pkg/db/key_list_by_key_auth_id.sql_generated.go
  • go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go
  • go/pkg/db/keyring_insert.sql_generated.go
  • go/pkg/db/key_permission_delete_all_by_key_id.sql_generated.go
  • go/pkg/db/identity_find.sql_generated.go
  • go/pkg/db/api_insert.sql_generated.go
  • go/pkg/db/key_insert.sql_generated.go
  • go/pkg/db/build_update_failed.sql_generated.go
  • go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go
  • go/pkg/db/identity_delete_old_with_ratelimits.sql_generated.go
  • go/pkg/db/permission_list_by_key_id.sql_generated.go
  • go/pkg/db/key_permission_delete_many_by_permission_id.sql_generated.go
  • go/pkg/db/key_update_credits_refill.sql_generated.go
  • go/pkg/db/ratelimit_override_list_by_namespace_id.sql_generated.go
  • go/pkg/db/key_permission_insert.sql_generated.go
  • go/pkg/db/api_soft_delete.sql_generated.go
  • go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go
  • go/pkg/db/identity_soft_delete.sql_generated.go
  • go/pkg/db/permission_find_by_id_or_slug.sql_generated.go
  • go/apps/ctrl/services/deployment/create_version.go
  • go/pkg/db/permission_find_by_slug_and_workspace_id.sql_generated.go
  • go/pkg/db/permission_delete_by_id.sql_generated.go
  • go/pkg/db/api_find_by_id.sql_generated.go
  • go/pkg/db/identity_list.sql_generated.go
  • go/pkg/db/ratelimit_override_find_by_identifier.sql_generated.go
  • go/pkg/db/key_find_live_by_hash.sql_generated.go
  • go/pkg/db/build_find_by_id.sql_generated.go
  • go/pkg/db/deployment_queries_test.go
  • go/pkg/db/ratelimit_namespace_find_by_name.sql_generated.go
  • go/pkg/db/querier_generated.go
  • go/pkg/db/deployment_insert.sql_generated.go
  • go/pkg/db/models_generated.go
  • go/apps/ctrl/services/deployment/create_version_simple_test.go
  • go/pkg/db/deployment_find_by_id.sql_generated.go
  • go/apps/ctrl/services/deployment/get_version.go
  • go/pkg/db/bulk_deployment_insert.sql.go
  • go/pkg/db/key_permission_delete_by_key_and_permission_id.sql_generated.go
**/*.{env,js,ts,go}

📄 CodeRabbit inference engine (CLAUDE.md)

All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME

Files:

  • go/pkg/db/ratelimit_list_by_key_ids.sql_generated.go
  • go/pkg/db/deployment_update_openapi_spec.sql_generated.go
  • go/pkg/db/ratelimit_update.sql_generated.go
  • go/pkg/db/identity_delete.sql_generated.go
  • go/pkg/db/key_encryption_insert.sql_generated.go
  • go/pkg/db/audit_log_insert.sql_generated.go
  • go/pkg/db/ratelimit_namespace_delete.sql_generated.go
  • go/pkg/db/key_soft_delete_many_by_key_auth_id.sql_generated.go
  • go/pkg/db/key_update.sql_generated.go
  • go/pkg/db/key_role_delete_many_by_key_id.sql_generated.go
  • go/pkg/db/audit_log_target_insert.sql_generated.go
  • go/pkg/db/api_update_delete_protection.sql_generated.go
  • go/pkg/db/key_find_live_by_id.sql_generated.go
  • go/pkg/db/identity_list_ratelimits.sql_generated.go
  • go/pkg/db/project_find_by_workspace_slug.sql_generated.go
  • go/pkg/db/ratelimit_override_find_by_id.sql_generated.go
  • go/pkg/db/key_find_by_id.sql_generated.go
  • go/pkg/db/identity_list_ratelimits_by_id.sql_generated.go
  • go/pkg/db/key_delete_by_id.sql_generated.go
  • go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go
  • go/pkg/db/key_find_for_verification.sql_generated.go
  • go/pkg/db/role_find_by_name_and_workspace_id.sql_generated.go
  • go/pkg/db/build_update_succeeded.sql_generated.go
  • go/pkg/db/permission_list_direct_by_key_id.sql_generated.go
  • go/pkg/db/key_insert_ratelimit.sql_generated.go
  • go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go
  • go/pkg/db/role_delete_by_id.sql_generated.go
  • go/pkg/db/build_find_latest_by_deployment_id.sql_generated.go
  • go/pkg/db/identity_insert_ratelimit.sql_generated.go
  • go/pkg/db/audit_log_find_target_by_id.sql_generated.go
  • go/pkg/db/role_find_by_id.sql_generated.go
  • go/pkg/db/permission_find_by_id.sql_generated.go
  • go/pkg/db/project_find_by_id.sql_generated.go
  • go/pkg/db/key_encryption_find_by_key_id.sql_generated.go
  • go/pkg/db/keyring_find_by_id.sql_generated.go
  • go/pkg/db/keyring_update_key_encryption.sql_generated.go
  • go/pkg/db/ratelimit_delete_many_by_identity_id.sql_generated.go
  • go/pkg/db/deployment_step_insert.sql_generated.go
  • go/pkg/db/ratelimit_override_update.sql_generated.go
  • go/pkg/db/key_find_credits.sql_generated.go
  • go/pkg/db/deployment_update_status.sql_generated.go
  • go/pkg/db/key_soft_delete_by_id.sql_generated.go
  • go/pkg/db/key_role_insert.sql_generated.go
  • go/pkg/db/permission_find_by_slugs.sql_generated.go
  • go/pkg/db/ratelimit_namespace_soft_delete.sql_generated.go
  • go/pkg/db/deployment_step_find_by_deployment_id.sql_generated.go
  • go/pkg/db/key_role_delete_many_by_role_id.sql_generated.go
  • go/pkg/db/build_update_status.sql_generated.go
  • go/pkg/db/role_find_by_names.sql_generated.go
  • go/pkg/db/project_insert.sql_generated.go
  • go/pkg/db/ratelimit_namespace_find.sql_generated.go
  • go/pkg/db/identity_list_ratelimits_by_ids.sql_generated.go
  • go/pkg/db/permission_find_by_name_and_workspace_id.sql_generated.go
  • go/pkg/db/ratelimit_override_insert.sql_generated.go
  • go/pkg/db/permission_list.sql_generated.go
  • go/pkg/db/identity_update.sql_generated.go
  • go/pkg/db/build_insert.sql_generated.go
  • go/pkg/db/permission_list_by_role_id.sql_generated.go
  • go/pkg/db/ratelimit_list_by_key_id.sql_generated.go
  • go/pkg/db/ratelimit_delete_many_by_ids.sql_generated.go
  • go/pkg/db/ratelimit_namespace_insert.sql_generated.go
  • go/pkg/db/ratelimit_delete.sql_generated.go
  • go/pkg/db/ratelimit_override_soft_delete.sql_generated.go
  • go/pkg/db/identity_insert.sql_generated.go
  • go/pkg/db/key_role_delete_all_by_key_id.sql_generated.go
  • go/pkg/db/ratelimit_namespace_find_by_id.sql_generated.go
  • go/pkg/db/permission_insert.sql_generated.go
  • go/pkg/db/key_role_find_by_key_and_role_id.sql_generated.go
  • go/pkg/db/key_list_by_key_auth_id.sql_generated.go
  • go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go
  • go/pkg/db/keyring_insert.sql_generated.go
  • go/pkg/db/key_permission_delete_all_by_key_id.sql_generated.go
  • go/pkg/db/identity_find.sql_generated.go
  • go/pkg/db/api_insert.sql_generated.go
  • go/pkg/db/key_insert.sql_generated.go
  • go/pkg/db/build_update_failed.sql_generated.go
  • go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go
  • go/pkg/db/identity_delete_old_with_ratelimits.sql_generated.go
  • go/pkg/db/permission_list_by_key_id.sql_generated.go
  • go/pkg/db/key_permission_delete_many_by_permission_id.sql_generated.go
  • go/pkg/db/key_update_credits_refill.sql_generated.go
  • go/pkg/db/ratelimit_override_list_by_namespace_id.sql_generated.go
  • go/pkg/db/key_permission_insert.sql_generated.go
  • go/pkg/db/api_soft_delete.sql_generated.go
  • go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go
  • go/pkg/db/identity_soft_delete.sql_generated.go
  • go/pkg/db/permission_find_by_id_or_slug.sql_generated.go
  • go/apps/ctrl/services/deployment/create_version.go
  • go/pkg/db/permission_find_by_slug_and_workspace_id.sql_generated.go
  • go/pkg/db/permission_delete_by_id.sql_generated.go
  • go/pkg/db/api_find_by_id.sql_generated.go
  • go/pkg/db/identity_list.sql_generated.go
  • go/pkg/db/ratelimit_override_find_by_identifier.sql_generated.go
  • go/pkg/db/key_find_live_by_hash.sql_generated.go
  • go/pkg/db/build_find_by_id.sql_generated.go
  • go/pkg/db/deployment_queries_test.go
  • go/pkg/db/ratelimit_namespace_find_by_name.sql_generated.go
  • go/pkg/db/querier_generated.go
  • go/pkg/db/deployment_insert.sql_generated.go
  • go/pkg/db/models_generated.go
  • go/apps/ctrl/services/deployment/create_version_simple_test.go
  • go/pkg/db/deployment_find_by_id.sql_generated.go
  • go/apps/ctrl/services/deployment/get_version.go
  • go/pkg/db/bulk_deployment_insert.sql.go
  • go/pkg/db/key_permission_delete_by_key_and_permission_id.sql_generated.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*_test.go: Use table-driven tests in Go
Organize Go integration tests with real dependencies
Organize Go tests by HTTP status codes

Files:

  • go/pkg/db/deployment_queries_test.go
  • go/apps/ctrl/services/deployment/create_version_simple_test.go
🧠 Learnings (4)
📓 Common learnings
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: For repo unkeyed/unkey and PR review workflows: When imeyer comments "issue" on a thread, automatically create a thorough GitHub issue (sections: Summary, Impact, Where, Repro/Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and the specific comment, and assign the issue to imeyer.
📚 Learning: 2025-08-21T15:54:45.183Z
Learnt from: chronark
PR: unkeyed/unkey#3825
File: go/internal/services/usagelimiter/limit.go:38-0
Timestamp: 2025-08-21T15:54:45.183Z
Learning: In go/internal/services/usagelimiter/limit.go, the UpdateKeyCreditsDecrement operation cannot be safely wrapped with db.WithRetry due to the lack of idempotency mechanisms in the current tech stack. Retrying this non-idempotent write operation risks double-charging users if the first attempt commits but the client sees a transient error.

Applied to files:

  • go/pkg/db/ratelimit_list_by_key_ids.sql_generated.go
  • go/pkg/db/ratelimit_list_by_key_id.sql_generated.go
  • go/pkg/db/key_update_credits_refill.sql_generated.go
📚 Learning: 2025-07-17T14:24:20.403Z
Learnt from: Flo4604
PR: unkeyed/unkey#3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Applied to files:

  • go/pkg/db/key_encryption_insert.sql_generated.go
  • go/pkg/db/key_update.sql_generated.go
  • go/pkg/db/key_insert_ratelimit.sql_generated.go
  • go/pkg/db/key_role_delete_all_by_key_id.sql_generated.go
  • go/pkg/db/key_list_by_key_auth_id.sql_generated.go
  • go/pkg/db/keyring_insert.sql_generated.go
  • go/pkg/db/key_insert.sql_generated.go
  • go/pkg/db/key_find_live_by_hash.sql_generated.go
📚 Learning: 2025-07-21T18:05:58.236Z
Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/deploy/{assetmanagerd,billaged,builderd,metald}/**/*.go : When a service's `*.go` code changes significantly, increase the patch-level version number.

Applied to files:

  • go/pkg/db/deployment_find_by_id.sql_generated.go
  • go/apps/ctrl/services/deployment/get_version.go
🧬 Code graph analysis (5)
go/apps/ctrl/services/deployment/create_version.go (1)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/db/deployment_queries_test.go (2)
go/pkg/db/deployment_insert.sql_generated.go (1)
  • InsertDeploymentParams (59-79)
go/pkg/db/models_generated.go (2)
  • DeploymentsEnvironmentPreview (199-199)
  • DeploymentsStatusPending (240-240)
go/pkg/db/deployment_insert.sql_generated.go (1)
go/pkg/db/models_generated.go (2)
  • DeploymentsEnvironment (195-195)
  • DeploymentsStatus (237-237)
go/pkg/db/models_generated.go (1)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/ctrl/services/deployment/create_version_simple_test.go (4)
go/pkg/db/deployment_insert.sql_generated.go (1)
  • InsertDeploymentParams (59-79)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/db/models_generated.go (1)
  • Deployment (655-675)
go/gen/proto/ctrl/v1/version.pb.go (2)
  • SourceType_SOURCE_TYPE_GIT (91-91)
  • VersionStatus_VERSION_STATUS_ACTIVE (32-32)
🪛 golangci-lint (2.2.2)
go/pkg/db/deployment_queries_test.go

18-18: undefined: sqlmock

(typecheck)


113-113: undefined: sqlmock

(typecheck)


116-116: not enough arguments in call to New
have ()
want (Config)

(typecheck)


127-127: undefined: sqlmock

(typecheck)


221-221: undefined: sqlmock

(typecheck)


224-224: not enough arguments in call to New
have ()
want (Config)

(typecheck)


235-235: undefined: sqlmock

(typecheck)


268-268: undefined: sqlmock

(typecheck)


269-269: undefined: sqlmock

(typecheck)


275-275: undefined: sqlmock

(typecheck)

⏰ 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: Test Go API Local / Test
  • GitHub Check: Test API / API Test Local

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review continued from previous batch...

@vercel vercel bot temporarily deployed to Preview – engineering August 21, 2025 17:25 Inactive
@mcstepp mcstepp force-pushed the ENG-1992-rich-git-commit-data branch from 3c539bb to a430d43 Compare August 21, 2025 17:40
@vercel vercel bot temporarily deployed to Preview – dashboard August 21, 2025 17:41 Inactive
@vercel vercel bot temporarily deployed to Preview – engineering August 21, 2025 17:43 Inactive
@vercel vercel bot temporarily deployed to Preview – engineering August 21, 2025 18:25 Inactive
@vercel vercel bot temporarily deployed to Preview – dashboard August 21, 2025 18:26 Inactive
Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

please run go fmt ./...

this PR is mostly formatting changes cause you regenerated without formatting (I fixed that in the make tooling already in another PR)

@mcstepp mcstepp force-pushed the ENG-1992-rich-git-commit-data branch from abf8414 to b3a2aec Compare August 25, 2025 17:29
@vercel vercel bot temporarily deployed to Preview – engineering August 25, 2025 17:30 Inactive
@vercel vercel bot temporarily deployed to Preview – dashboard August 25, 2025 17:33 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 26

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (20)
go/pkg/db/permission_delete_by_id.sql_generated.go (1)

12-23: Optional: return affected row count to distinguish “not found” vs successful delete.

If callers need to know whether a permission actually existed, prefer using :execrows in the source SQL so the generated method returns (int64, error). This avoids silent no-ops when id doesn’t match any row.

Apply in the SQL source (not this generated file):

-- go/pkg/db/permission_delete_by_id.sql
--- name: DeletePermission :exec
+-- name: DeletePermission :execrows
 DELETE FROM permissions
 WHERE id = ?

Expected generated signature after sqlc generate:

// func (q *Queries) DeletePermission(ctx context.Context, db DBTX, permissionID string) (int64, error)

Callers can then assert rows > 0 to confirm deletion.

go/pkg/db/ratelimit_delete_many_by_ids.sql_generated.go (1)

21-33: Call sites already guard empty slices; consider optional chunking for very large deletes

I verified that every call to DeleteManyRatelimitsByIDs is already wrapped in a if len(ids) > 0 check, so there’s no scenario where the generated query executes IN (NULL) in production:

  • go/apps/api/routes/v2_keys_update_key/handler.go: lines 324–327
  • go/apps/api/routes/v2_identities_update_identity/handler.go: lines 249–253

Since the empty-slice case is already handled upstream, no need to short-circuit in generated code. If you anticipate very large ID lists (which can hit query-size or driver limits), you may still want to introduce an optional batch-chunking helper in your service layer:

// Package internal/dbutil
package dbutil

import (
  "context"
  "your/module/go/pkg/db"
)

const defaultChunkSize = 1000 // tune per SQL dialect

// DeleteManyRatelimitsByIDsInBatches breaks a large list into chunks
// to avoid oversized IN() clauses.
func DeleteManyRatelimitsByIDsInBatches(
  ctx context.Context,
  q *db.Queries,
  tx db.DBTX,
  ids []string,
) error {
  for start := 0; start < len(ids); start += defaultChunkSize {
    end := start + defaultChunkSize
    if end > len(ids) {
      end = len(ids)
    }
    if err := q.DeleteManyRatelimitsByIDs(ctx, tx, ids[start:end]); err != nil {
      return err
    }
  }
  return nil
}

Use this helper instead of calling DeleteManyRatelimitsByIDs directly when dealing with potentially large ID slices.

go/pkg/db/key_role_find_by_key_and_role_id.sql_generated.go (1)

12-17: Verified: Unique Constraint on (key_id, role_id) Present

The keys_roles table in go/pkg/db/schema.sql defines both a primary key on (role_id, key_id, workspace_id) and an explicit UNIQUE(key_id, role_id) constraint, ensuring each key–role pair is unique within a workspace. Given this invariant, it’s safe—and more idiomatic—to switch your SQLC query from :many to :one, which will:

  • Prevent unnecessary slice allocations
  • Surface “not found” errors directly instead of requiring post-call length checks

To apply this refactor:

• In go/pkg/db/key_role_find_by_key_and_role_id.sql, change the annotation from :many to :one

--- a/go/pkg/db/key_role_find_by_key_and_role_id.sql
--- name: FindKeyRoleByKeyAndRoleID :many
+-- name: FindKeyRoleByKeyAndRoleID :one
 SELECT key_id, role_id, workspace_id, created_at_m, updated_at_m
 FROM keys_roles
 WHERE key_id = ?
   AND role_id = ?

• Regenerate your SQLC code to produce:

  • FindKeyRoleByKeyAndRoleID(ctx, db, params) (KeysRole, error)

• Update all call sites—most notably:

  • apps/api/routes/v2_permissions_delete_role/200_test.go (switch from []KeysRole to KeysRole and handle “not found” errors appropriately)
  • Any other references in go/pkg/db/querier_generated.go and application code

This change cleanly encodes the uniqueness invariant in your API and simplifies client code.

go/pkg/db/identity_soft_delete.sql_generated.go (1)

12-17: Split SoftDeleteIdentity into two indexable mutations

We’ve confirmed that the current SoftDeleteIdentity query in go/pkg/db/queries/identity_soft_delete.sql (and its generated counterpart in go/pkg/db/identity_soft_delete.sql_generated.go) uses a single parameter against (id = ? OR external_id = ?). This can inadvertently soft-delete multiple rows if one row’s id and another’s external_id share the same value within the same workspace_id, and it prevents either index (the PK on id or the unique index on (workspace_id, external_id, deleted)) from being used efficiently.

Required changes:

  • go/pkg/db/queries/identity_soft_delete.sql
    Replace the single SoftDeleteIdentity :exec statement with two separate mutations:

    -- name: SoftDeleteIdentity :exec
    -UPDATE identities
    -SET deleted = 1
    -WHERE workspace_id = sqlc.arg('workspace_id')
    -  AND (id = sqlc.arg('identity') OR external_id = sqlc.arg('identity'));
    
    +-- name: SoftDeleteIdentityByID :exec
    +UPDATE identities
    +SET deleted = 1
    +WHERE workspace_id = sqlc.arg('workspace_id')
    +  AND id = sqlc.arg('identity');
    +
    +-- name: SoftDeleteIdentityByExternalID :exec
    +UPDATE identities
    +SET deleted = 1
    +WHERE workspace_id = sqlc.arg('workspace_id')
    +  AND external_id = sqlc.arg('identity');
  • go/pkg/db/identity_soft_delete.sql_generated.go
    Regenerate so there are two functions (SoftDeleteIdentityByID and SoftDeleteIdentityByExternalID) instead of one, each binding its own parameter.

  • Call sites
    Update all uses of the old SoftDeleteIdentity method—found in:

    • apps/api/routes/v2_identities_delete_identity/handler.go
    • apps/api/routes/v2_identities_get_identity/404_test.go
    • apps/api/routes/v2_identities_list_identities/200_test.go

    to invoke the appropriate new function based on whether you’re deleting by internal id or by external_id.

Index coverage is already in place via:

  • Primary key on identities(id)
  • Unique index workspace_id_external_id_deleted_idx on (workspace_id, external_id, deleted)

Splitting the queries will restore single-row semantics and allow each predicate to fully leverage its respective index.

go/pkg/db/ratelimit_namespace_insert.sql_generated.go (1)

12-31: Align InsertRatelimitNamespace params with the created_at_m column

The schema for ratelimit_namespaces defines and uses created_at_m (not created_at), yet the generated insert params struct wrongly maps the fourth field to db:"created_at". To avoid confusion and potential breakage when using named‐based utilities, update the SQL/query definitions so sqlc emits a CreatedAtM int64 db:"created_at_m"field instead ofCreatedAt`:

• In go/pkg/db/queries/ratelimit_namespace_insert.sql
– Change the arg mapping from
…VALUES ( ?, ?, ?, sqlc.arg(created_at), …)
to
…VALUES ( ?, ?, ?, sqlc.arg(created_at_m), …)

• Regenerate the code to pick up:
CreatedAtM int64 db:"created_at_m"

Do not hand‐edit the generated file (go/pkg/db/ratelimit_namespace_insert.sql_generated.go). After adjusting the query, run sqlc generate so the struct field and tag are consistent with the schema.

go/pkg/db/key_permission_insert.sql_generated.go (1)

24-33: Ensure CreatedAt and UpdatedAt are populated for InsertKeyPermission
The generated SQL in key_permission_insert.sql_generated.go (lines 49–51) binds both CreatedAt (for created_at_m) and UpdatedAt (for updated_at_m on duplicate keys). When callers omit these fields, they default to zero/NULL, so neither timestamp reflects the real operation time. I’ve identified the following callers that need adjustment:

  • go/pkg/testutil/seed/seed.go (line 174): InsertKeyPermissionParams literal lacks CreatedAt/UpdatedAt.
  • go/apps/api/routes/v2_keys_remove_permissions/200_test.go: calls to InsertKeyPermission omit both timestamp fields.
  • go/apps/api/routes/v2_keys_set_permissions/200_test.go: similarly omits CreatedAt/UpdatedAt.

To fix, update each call (or introduce a shared helper) to include something like:

CreatedAt: time.Now().UnixMilli(),
UpdatedAt: sql.NullInt64{Int64: time.Now().UnixMilli(), Valid: true},

This will ensure created_at_m and updated_at_m accurately track when the record was inserted or upserted.

go/pkg/db/key_update_credits_set.sql_generated.go (1)

13-22: Danger: NULL credits will set remaining_requests to NULL

Update uses sql.NullInt32 for Credits. If Valid=false, MySQL arithmetic/assignment with NULL will set remaining_requests to NULL. That likely breaks metering/accounting.

Two safe options (edit the source SQL in go/pkg/db/queries/key_update_credits_set.sql and regenerate):

Option A (no-op on NULL):

-SET remaining_requests = ?
+SET remaining_requests = COALESCE(?, remaining_requests)

Option B (require non-NULL input; defensive check in code):

  • Validate Credits.Valid before calling this query; reject otherwise.
  • Consider NOT NULL constraint on keys.remaining_requests.

Pick A if caller may intentionally pass NULL; otherwise pick B and add input validation. I can update the .sql and regenerate if you confirm the intended behavior.

Also applies to: 24-31

go/pkg/db/key_update_credits_increment.sql_generated.go (1)

13-22: NULL increment will NULL-out remaining_requests

remaining_requests = remaining_requests + ? with a NULL parameter yields NULL in MySQL. That’s a hard-to-detect data corruption path.

Fix at the source SQL (go/pkg/db/queries/key_update_credits_increment.sql), then regenerate:

-SET remaining_requests = remaining_requests + ?
+SET remaining_requests = remaining_requests + COALESCE(?, 0)

Additionally, validate that Credits.Valid is true at call sites to avoid accidental NULLs. Consider a NOT NULL constraint on remaining_requests if not already present.

Also applies to: 24-31

go/pkg/db/ratelimit_override_update.sql_generated.go (1)

23-29: Naming mismatch: struct field Windowlimit vs column limit

Not introduced by this PR, but the param name Windowlimit can be confusing given the column is limit. If you touch the SQL next time, consider aliasing for clarity so sqlc generates a clearer param name, e.g.:

UPDATE `ratelimit_overrides`
SET
    `limit` = ? /* alias: window_limit */,
    ...

or rename the column reference in SQLC config to yield Limit/WindowLimit consistently. Leave generated code as-is for now.

go/pkg/db/key_find_live_by_id.sql_generated.go (1)

137-141: Optional: prefer json.RawMessage over interface{} for JSON columns

Not part of this change, but mapping JSON aggregations to interface{} loses type info and can complicate downstream unmarshalling. If/when you touch the SQL or sqlc config, consider mapping these to json.RawMessage for stricter typing without runtime cost.

go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (1)

13-41: Avoid executing DELETE with IN(NULL) for empty role list; consider early return and chunking for large lists.

Current behavior replaces an empty slice with IN(NULL), which is a no-op but still incurs a DB round-trip. Also, very large role_ids may hit driver/statement limits. Prefer short-circuiting when len(role_ids) == 0, and consider chunking when the list is large. Note: do not hand-edit generated files—apply the change in the SQL source and regenerate.

Apply this diff conceptually (in the source that generates this code), then regenerate:

 func (q *Queries) DeleteManyKeyRolesByKeyAndRoleIDs(ctx context.Context, db DBTX, arg DeleteManyKeyRolesByKeyAndRoleIDsParams) error {
+    // Fast-path: nothing to delete
+    if len(arg.RoleIds) == 0 {
+        return nil
+    }
     query := deleteManyKeyRolesByKeyAndRoleIDs
     var queryParams []interface{}
     queryParams = append(queryParams, arg.KeyID)
-    if len(arg.RoleIds) > 0 {
-        for _, v := range arg.RoleIds {
-            queryParams = append(queryParams, v)
-        }
-        query = strings.Replace(query, "/*SLICE:role_ids*/?", strings.Repeat(",?", len(arg.RoleIds))[1:], 1)
-    } else {
-        query = strings.Replace(query, "/*SLICE:role_ids*/?", "NULL", 1)
-    }
+    for _, v := range arg.RoleIds {
+        queryParams = append(queryParams, v)
+    }
+    query = strings.Replace(query, "/*SLICE:role_ids*/?", strings.Repeat(",?", len(arg.RoleIds))[1:], 1)
     _, err := db.ExecContext(ctx, query, queryParams...)
     return err
 }

If your inputs can exceed driver placeholder limits, introduce chunking in the caller:

  • Split role_ids into batches (e.g., 1000 each).
  • Execute the generated function per batch within a transaction.
go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go (1)

13-41: Add a unit test for empty Ids and consider batching large slices

  • No existing tests reference DeleteManyKeyPermissionByKeyAndPermissionIDs with an empty Ids slice, so we should add one to assert that passing Ids: [] results in no rows deleted.
  • At the service layer, consider splitting very large Ids slices into batches (e.g. chunks of 1000) to avoid exceeding database parameter limits or generating excessively long IN lists.
go/pkg/db/ratelimit_list_by_key_id.sql_generated.go (1)

42-44: ListRatelimitsByKeyID callers always pass Valid=true

I searched all usages of ListRatelimitsByKeyID and confirmed that every call site—both in handlers and in tests—constructs the sql.NullString with Valid: true. For example:

  • In v2_keys_update_key/handler.go:
    db.Query.ListRatelimitsByKeyID(ctx, tx, sql.NullString{String: key.ID, Valid: true})
  • In v2_keys_delete_key/200_test.go:
    db.Query.ListRatelimitsByKeyID(ctx, h.DB.RO(), sql.NullString{String: hardDeleteKeyID, Valid: true})
  • In v2_keys_update_key/200_test.go (twice):
    sql.NullString{String: keyResponse.KeyID, Valid: true}

Since all callers set Valid=true, there’s no risk of accidentally querying WHERE key_id = NULL and getting no rows.

• Optional refactor: if your database schema guarantees that ratelimits.key_id is defined as NOT NULL, you could simplify the generated code by overriding the column’s Go type to string in your sqlc config. That would eliminate the NullString footgun altogether.

go/pkg/db/ratelimit_override_insert.sql_generated.go (1)

34-39: MySQL VALUES() deprecation heads-up for future-proofing.

MySQL 8.0.20+ deprecates the VALUES() function in ON DUPLICATE KEY UPDATE. While this generated code is fine today, consider updating the source SQL (go/pkg/db/queries/ratelimit_override_insert.sql) to use the INSERT ... AS new ... ON DUPLICATE KEY UPDATE pattern the next time you touch it. Keep updated_at_m as a bound param if you don’t want to include it in the insert column list.

Example for the source SQL (illustrative):

INSERT INTO ratelimit_overrides (
  id, workspace_id, namespace_id, identifier, `limit`, duration, async, created_at_m
) VALUES (?, ?, ?, ?, ?, ?, false, ?)
AS new
ON DUPLICATE KEY UPDATE
  `limit`   = new.`limit`,
  duration  = new.duration,
  async     = new.async,
  updated_at_m = ?

Please verify your target MySQL version and whether a migration off VALUES() is on your roadmap.

go/pkg/db/retry.go (2)

14-22: Add an explicit idempotency warning to prevent misuse.

Per past incidents, wrapping non-idempotent writes (e.g., credit decrements) risks double-charging on partial failures. Add a prominent caution in the docblock to dissuade misuse.

Apply this doc-only diff:

 // WithRetry executes a database operation with optimized retry configuration.
 // It retries transient errors with exponential backoff but skips non-retryable errors
 // like "not found" or "duplicate key" to avoid unnecessary delays.
 //
 // Configuration:
 //   - 3 attempts maximum
 //   - Exponential backoff: 50ms, 100ms, 200ms
 //   - Skips retries for "not found" and "duplicate key" errors
+//
+// AIDEV-NOTE: Only wrap idempotent operations or operations protected by
+// transactional/uniqueness guarantees. Do NOT use for non-idempotent writes
+// (e.g., decrementing credits, charging, emitting external side effects),
+// as retries may apply the mutation multiple times on ambiguous failures.

31-41: Optional: add jitter to backoff to reduce herd effects under contention.

Small randomized jitter reduces synchronized retries after shared failures.

Apply this optional diff:

 import (
 	"time"

 	"github.com/unkeyed/unkey/go/pkg/retry"
 )
@@
 		retry.Backoff(func(n int) time.Duration {
-			// Predefined backoff delays: 50ms, 100ms, 200ms
-			delays := []time.Duration{
-				DefaultBackoff,     // 50ms for attempt 1
-				DefaultBackoff * 2, // 100ms for attempt 2
-				DefaultBackoff * 4, // 200ms for attempt 3
-			}
-			if n <= 0 || n > len(delays) {
-				return DefaultBackoff // fallback to base delay
-			}
-			return delays[n-1]
+			// Predefined backoff delays: 50ms, 100ms, 200ms, with +/- 20% jitter
+			delays := []time.Duration{DefaultBackoff, DefaultBackoff * 2, DefaultBackoff * 4}
+			if n <= 0 || n > len(delays) {
+				return DefaultBackoff
+			}
+			base := delays[n-1]
+			// simple jitter without extra deps
+			jitterNs := time.Now().UnixNano() % int64(base/5) // 20% max
+			if (time.Now().UnixNano()&1) == 0 {
+				return base + time.Duration(jitterNs)
+			}
+			return base - time.Duration(jitterNs)
 		}),
go/pkg/db/bulk_deployment_insert.sql.go (1)

21-55: Prevent oversized statements: chunk large bulk inserts (e.g., 200–500 rows per batch).

Long VALUES lists risk exceeding max packet/SQL limits. Batch the input and execute multiple INSERTs.

Apply this refactor within InsertDeployments:

@@
-  // Build the bulk insert query
-  valueClauses := make([]string, len(args))
-  for i := range args {
-    valueClauses[i] = "( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"
-  }
-  bulkQuery := fmt.Sprintf(bulkInsertDeployment, strings.Join(valueClauses, ", "))
-
-  // Collect all arguments
-  var allArgs []any
-  for _, arg := range args {
-    allArgs = append(allArgs, arg.ID)
-    allArgs = append(allArgs, arg.WorkspaceID)
-    allArgs = append(allArgs, arg.ProjectID)
-    allArgs = append(allArgs, arg.Environment)
-    allArgs = append(allArgs, arg.BuildID)
-    allArgs = append(allArgs, arg.RootfsImageID)
-    allArgs = append(allArgs, arg.GitCommitSha)
-    allArgs = append(allArgs, arg.GitBranch)
-    allArgs = append(allArgs, arg.GitCommitMessage)
-    allArgs = append(allArgs, arg.GitCommitAuthorName)
-    allArgs = append(allArgs, arg.GitCommitAuthorEmail)
-    allArgs = append(allArgs, arg.GitCommitAuthorUsername)
-    allArgs = append(allArgs, arg.GitCommitAuthorAvatarUrl)
-    allArgs = append(allArgs, arg.GitCommitTimestamp)
-    allArgs = append(allArgs, arg.ConfigSnapshot)
-    allArgs = append(allArgs, arg.OpenapiSpec)
-    allArgs = append(allArgs, arg.Status)
-    allArgs = append(allArgs, arg.CreatedAt)
-    allArgs = append(allArgs, arg.UpdatedAt)
-  }
-
-  // Execute the bulk insert
-  _, err := db.ExecContext(ctx, bulkQuery, allArgs...)
-  return err
+  const chunkSize = 500
+  for start := 0; start < len(args); start += chunkSize {
+    end := start + chunkSize
+    if end > len(args) {
+      end = len(args)
+    }
+    batch := args[start:end]
+
+    // Build placeholders for this batch
+    valueClauses := make([]string, len(batch))
+    for i := range batch {
+      valueClauses[i] = "( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"
+    }
+    bulkQuery := fmt.Sprintf(bulkInsertDeployment, strings.Join(valueClauses, ", "))
+
+    // Collect arguments for this batch
+    var allArgs []any
+    for _, arg := range batch {
+      allArgs = append(allArgs,
+        arg.ID,
+        arg.WorkspaceID,
+        arg.ProjectID,
+        arg.Environment,
+        arg.BuildID,
+        arg.RootfsImageID,
+        arg.GitCommitSha,
+        arg.GitBranch,
+        arg.GitCommitMessage,
+        arg.GitCommitAuthorName,
+        arg.GitCommitAuthorEmail,
+        arg.GitCommitAuthorUsername,
+        arg.GitCommitAuthorAvatarUrl,
+        arg.GitCommitTimestamp,
+        arg.ConfigSnapshot,
+        arg.OpenapiSpec,
+        arg.Status,
+        arg.CreatedAt,
+        arg.UpdatedAt,
+      )
+    }
+    if _, err := db.ExecContext(ctx, bulkQuery, allArgs...); err != nil {
+      return err
+    }
+  }
+  return nil
go/apps/ctrl/services/deployment/get_version.go (1)

80-85: Differentiate not-found vs real errors when fetching latest build.

Silently ignoring all errors can hide operational issues. Ignore not-found; log/warn unexpected errors.

- build, err := db.Query.FindLatestBuildByDeploymentId(ctx, s.db.RO(), deployment.ID)
- if err == nil {
-   protoVersion.BuildId = build.ID
- }
+ build, err := db.Query.FindLatestBuildByDeploymentId(ctx, s.db.RO(), deployment.ID)
+ if err == nil {
+   protoVersion.BuildId = build.ID
+ } else if !db.IsNotFound(err) {
+   s.logger.Warn("failed to fetch latest build for deployment", "error", err, "deployment_id", deployment.ID)
+ }
go/apps/ctrl/services/deployment/create_version.go (2)

62-69: Use Valid flag on DefaultBranch instead of reading String unconditionally.

Avoid relying on String when Valid=false (can carry stale values). Fall back only if DefaultBranch is null/empty.

 gitBranch := req.Msg.GetBranch()
 if gitBranch == "" {
-  gitBranch = project.DefaultBranch.String
-  if gitBranch == "" {
+  if project.DefaultBranch.Valid && project.DefaultBranch.String != "" {
+    gitBranch = project.DefaultBranch.String
+  } else {
     gitBranch = "main" // fallback default
-  }
+  }
 }

88-91: Track TODO for environment field in proto.

Happy to open a follow-up issue to add environment to CreateVersionRequest and wire it through.

♻️ Duplicate comments (48)
go/pkg/db/key_find_credits.sql_generated.go (1)

3-3: Unify sqlc header to a single repo-wide version (expected v1.29.0).

This file is on sqlc v1.28.0 while prior guidance and other files target v1.29.0. Please regenerate with the pinned version to avoid churn and mismatched codegen metadata. If regeneration isn’t possible right now, minimally align the header, but prefer regeneration.

You can verify repo-wide consistency with:

#!/bin/bash
set -euo pipefail

echo "sqlc headers by version:"
rg -nP '^//\s+sqlc v1\.\d+\.\d+' -g 'go/**/*.go' | sort | uniq -c

echo -e "\nFiles not on v1.29.0:"
rg -nP '^\s*//\s+sqlc v1\.(?!29\.0)\d+\.\d+' -g 'go/**/*.go' || echo "All good"

# Optional: Check the sqlc version pin in tooling
fd -t f -g 'Makefile|**/*.sh|**/scripts/**|**/Dockerfile|**/sqlc.*.yaml|**/sqlc.yaml' \
  | xargs -I{} rg -n 'sqlc|v1\.29\.0' {}

If you must hotfix the header pending regeneration, this is the minimal diff (again, prefer regenerating):

-//   sqlc v1.28.0
+//   sqlc v1.29.0
go/pkg/db/key_encryption_find_by_key_id.sql_generated.go (1)

3-3: Unify sqlc version across generated files (this file shows v1.28.0).

The PR appears to standardize sqlc output, but this header is v1.28.0. Prior review threads suggested targeting v1.29.0 repo-wide. Please confirm the intended version and regenerate to a single version to avoid churn and subtle diffs.

Run this to verify repository-wide consistency and surface outliers:

#!/bin/bash
set -euo pipefail

echo "Enumerating sqlc versions found in generated headers..."
rg -nP --type=go -g '!**/vendor/**' -g '!**/node_modules/**' -C0 '^\s*//\s+sqlc v[0-9]+\.[0-9]+\.[0-9]+' \
  | sed -E 's/^(.+):.*(sqlc v[0-9]+\.[0-9]+\.[0-9]+).*$/\2 \1/' \
  | sort -u

echo -e "\nChecking for multiple versions..."
VERSIONS=$(rg -nP --glob '**/*_generated.go' -g '!**/vendor/**' '^\s*//\s+sqlc v[0-9]+\.[0-9]+\.[0-9]+' \
  | sed -E 's/^.+sqlc (v[0-9]+\.[0-9]+\.[0-9]+).*$/\1/' | sort -u)
echo "Found versions: ${VERSIONS}"

if [ "$(echo "$VERSIONS" | wc -l | tr -d ' ')" -ne 1 ]; then
  echo "ERROR: Multiple sqlc versions detected. Please regenerate all with the single intended version." >&2
  exit 1
else
  echo "OK: Single sqlc version detected."
fi

# Optional: enforce a specific target (uncomment and set desired version, e.g., v1.29.0)
# TARGET=v1.29.0
# if [ "$VERSIONS" != "$TARGET" ]; then
#   echo "ERROR: Detected sqlc $VERSIONS, expected $TARGET." >&2
#   exit 1
# fi
go/pkg/db/key_update.sql_generated.go (1)

3-3: Repo-wide sqlc version drift: this file is v1.28.0; project guidance is to standardize on v1.29.0

Previous reviews requested pinning/regenerating all sqlc-generated files to v1.29.0 to avoid noisy diffs and mixed outputs. Please confirm target version and regenerate accordingly, then pin it in CI/devcontainer/tooling.

Apply after regenerating (illustrative diff of the header only):

 // versions:
-//   sqlc v1.28.0
+//   sqlc v1.29.0

To find any stragglers:

#!/bin/bash
rg -nP '^//\s+sqlc v(?!1\.29\.0)' -g '!**/vendor/**' -S
go/pkg/db/audit_log_target_insert.sql_generated.go (1)

3-3: Please align sqlc generator header to the pinned version (v1.29.0) across the repo

This file is stamped v1.28.0. For consistency and reproducible builds, regenerate with the pinned sqlc version and commit the results; also ensure CI/devcontainers install that exact version.

Suggested header change after regeneration:

 // versions:
-//   sqlc v1.28.0
+//   sqlc v1.29.0
go/pkg/db/key_update_credits_decrement.sql_generated.go (1)

3-3: Standardize sqlc to v1.29.0

Header shows v1.28.0. Please regenerate with v1.29.0 and pin the tool version to avoid mixed generations.

 // versions:
-//   sqlc v1.28.0
+//   sqlc v1.29.0
go/pkg/db/key_insert_ratelimit.sql_generated.go (1)

3-3: sqlc header version is v1.28.0; align to pinned v1.29.0

Please regenerate with the pinned toolchain and commit. This avoids perpetual churn in generated artifacts.

 // versions:
-//   sqlc v1.28.0
+//   sqlc v1.29.0
go/pkg/db/key_permission_insert.sql_generated.go (1)

3-3: Bump sqlc header to v1.29.0 for consistency

This file shows v1.28.0. Please regenerate with the pinned version and enforce the pin in tooling.

 // versions:
-//   sqlc v1.28.0
+//   sqlc v1.29.0
go/pkg/db/role_find_by_id.sql_generated.go (1)

3-3: Unify sqlc generator version across repo (this file is v1.28.0; others appear at v1.29.0).

Generated headers should be consistent to ensure reproducible builds and avoid churn in future diffs. Please regenerate using a single pinned sqlc version (recommend v1.29.0 if that’s the current standard) and ensure CI/dev tooling is pinned accordingly. Do not hand-edit generated headers.

Run this script from the repo root to audit and enforce consistency:

#!/bin/bash
set -euo pipefail

PINNED_VERSION="${PINNED_VERSION:-v1.29.0}"

echo "All sqlc version headers found (distinct):"
rg -n --glob '!**/node_modules/**' -P '^//\s+sqlc v[0-9]+\.[0-9]+\.[0-9]+' -S | sed -E 's/^.*sqlc (v[0-9]+\.[0-9]+\.[0-9]+).*$/\1/' | sort -u

echo
echo "Files not on ${PINNED_VERSION} (should be empty after regen):"
rg -n --glob '!**/node_modules/**' -P "^//\\s+sqlc (?!${PINNED_VERSION//./\\.})(v[0-9]+\\.[0-9]+\\.[0-9]+)" -S || true

echo
echo "Potential generator config/files referencing sqlc:"
fd -t f -a -E node_modules -E .git 'sqlc.*' || true
rg -n --glob '!**/node_modules/**' -i '\bsqlc\b' || true

cat <<'EOF'

Next steps:
1) Pin sqlc in your tooling (e.g., devcontainer, CI workflow, or go:generate script).
2) Re-generate:
   - sqlc generate
3) Re-run this script and commit regenerated files.

You can override the expected version:
PINNED_VERSION=v1.28.0 bash scripts/check-sqlc-versions.sh
EOF
go/pkg/db/permission_insert.sql_generated.go (1)

3-3: sqlc header drift: v1.28.0 here vs. v1.29.0 elsewhere — regenerate for consistency.

Keep all generated files on a single sqlc version to avoid noisy diffs and mismatched outputs. Re-run generation with the pinned version and commit results. See my repository-wide audit script in go/pkg/db/role_find_by_id.sql_generated.go.

go/pkg/db/keyring_update_key_encryption.sql_generated.go (1)

3-3: Align generator version (v1.28.0) with repo-wide standard.

This header indicates v1.28.0. If the repo standard is v1.29.0 (as other files suggest), please regenerate with that version and pin your CI/dev environment accordingly.

go/pkg/db/permission_find_by_id_or_slug.sql_generated.go (1)

3-3: Mixed sqlc versions detected — normalize to a single pinned version.

Please re-generate this file with the pinned sqlc version used elsewhere (likely v1.29.0) and ensure tooling is pinned to prevent future drift.

go/pkg/db/ratelimit_update.sql_generated.go (1)

3-3: Please regenerate with the repo’s pinned sqlc version.

This file shows v1.28.0. For deterministic builds and clean diffs, keep all generated files on the same sqlc version (e.g., v1.29.0).

go/pkg/db/project_find_by_workspace_slug.sql_generated.go (1)

3-3: sqlc v1.28.0 header bump — OK

Query text and result scanning stable; previous performance considerations are already addressed by the existing UNIQUE(workspace_id, slug) constraint/index.

go/pkg/db/permission_list_direct_by_key_id.sql_generated.go (1)

3-3: Ensure repo-wide sqlc version consistency and pin in CI

This file shows sqlc v1.28.0. To avoid churn from mixed generator versions, please verify all generated files are on the same sqlc version and pin that version in CI/dev setup.

Run this script from repo root to detect any mixed headers and get a version tally:

#!/bin/bash
set -euo pipefail

echo "Distinct sqlc versions in generated files:"
rg -nP --hidden --glob '!**/vendor/**' $'^//\\s*sqlc v\\d+\\.\\d+\\.\\d+\\b' | sed -nE 's@^.+//\\s*(sqlc v[0-9.]+).*@\\1@p' | sort -u

echo
echo "Files not on the most common version (if any):"
# Find the mode version
mode_ver="$(rg -nP --hidden --glob '!**/vendor/**' $'^//\\s*sqlc v\\d+\\.\\d+\\.\\d+\\b' \
  | sed -nE 's@^.+//\\s*(sqlc v[0-9.]+).*@\\1@p' \
  | sort | uniq -c | sort -nr | awk 'NR==1{print $2}')"
echo "Mode version: ${mode_ver:-unknown}"
rg -nP --hidden --glob '!**/vendor/**' $'^//\\s*sqlc v\\d+\\.\\d+\\.\\d+\\b' \
  | awk -v mode="$mode_ver" '
    {
      match($0, /sqlc v[0-9.]+/);
      ver=substr($0, RSTART, RLENGTH);
      if (ver != mode) print $0
    }' || true
go/pkg/db/key_soft_delete_by_id.sql_generated.go (1)

3-3: Repo-wide generator version alignment

Mirror the consistency check across all generated files and pin sqlc in CI to avoid future drift.

Use the same script provided in the first comment to scan the repo.

go/pkg/db/ratelimit_override_update.sql_generated.go (1)

3-3: Pin sqlc version and verify consistency

Please run the repo-wide header scan and pin the sqlc version in CI/dev tooling.

go/pkg/db/key_find_live_by_id.sql_generated.go (1)

3-3: Consistency check for sqlc version

Run the provided script to ensure no mixed generator versions exist.

go/pkg/db/build_update_failed.sql_generated.go (1)

3-3: Standardize sqlc version across repo

Please run the repo-wide scan and pin sqlc in CI/dev tooling.

go/pkg/db/key_role_delete_many_by_key_id.sql_generated.go (1)

3-3: sqlc v1.28.0 header — approve; also re-run the repo-wide verification

This mirrors the prior “version drift” feedback on generated headers; let’s ensure everything now uniformly reads v1.28.0.

Run:

#!/bin/bash
set -euo pipefail
echo "Distinct sqlc versions found:"
rg -nP '^//\s+sqlc v\d+\.\d+\.\d+' -S | awk -F'v' '{print $NF}' | sort -u
echo
echo "Files not on v1.28.0:"
rg -nP '^//\s+sqlc v(?!1\.28\.0)\d+\.\d+\.\d+' -nS || true
go/pkg/db/permission_find_by_name_and_workspace_id.sql_generated.go (1)

3-3: sqlc header bump to v1.28.0: align repo-wide and pin generator version.

Past reviews flagged mixed sqlc versions (v1.27.0 vs v1.29.0). Please decide on a single target (recommend pinning the latest validated version for this repo) and regenerate to avoid churn and dialect drift. Also ensure placeholder style remains ? consistently with your driver.

Run from repo root:

#!/bin/bash
set -euo pipefail

echo "Distinct sqlc versions in generated files:"
rg -n --glob '**/*sql_generated.go' -g '!**/vendor/**' -P '^//\s+sqlc v' \
  | sed -E 's/.*(sqlc v[0-9.]+).*/\1/' | sort -u

echo
echo "Files not at chosen version (adjust 1\.28\.0 or 1\.29\.0 as needed):"
rg -n --glob '**/*sql_generated.go' -g '!**/vendor/**' -P '^//\s+sqlc v(?!1\.(28|29)\.0)'

echo
echo "Placeholder scan (expect '?' for MySQL/SQLite):"
rg -nP '\$\d+' go/pkg/db -g '*_sql_generated.go' || echo "OK: no \$n placeholders"
go/pkg/db/permission_find_by_slug_and_workspace_id.sql_generated.go (1)

3-3: Normalize sqlc generator version across the repo and pin in CI/dev.

Same note as other generated files: pick a single sqlc version (prefer the repo-standard) and pin it to prevent mixed headers. Verify placeholders remain ?.

Use the same script provided in the sibling file comment to confirm consistency.

go/pkg/db/permission_list.sql_generated.go (1)

3-3: Standardize sqlc generator version.

Same note as other generated files: pick and pin a single sqlc version; regenerate to keep headers uniform.

Use the shared script to confirm uniformity and placeholder style.

go/pkg/db/deployment_step_find_by_deployment_id.sql_generated.go (1)

3-3: Dup: ensure repo-wide sqlc version consistency

See my verification script on permission_list_by_role_id.sql_generated.go.

go/pkg/db/ratelimit_override_find_by_id.sql_generated.go (1)

3-3: Dup: verify all generated files match v1.28.0 and pin sqlc

Refer to the repo-wide verification script posted earlier.

go/pkg/db/permission_find_by_id.sql_generated.go (1)

3-3: Dup: repo-wide sqlc version check

Use the previously shared script to confirm single-version codegen across the repo and pin sqlc in CI.

go/pkg/db/audit_log_find_target_by_id.sql_generated.go (1)

3-3: Dup: ensure consistent sqlc version and toolchain pin

Same verification guidance applies; see earlier comment and script.

go/pkg/db/key_list_by_key_auth_id.sql_generated.go (1)

3-3: Repo-wide sqlc version consistency check recommended.
Similar to prior reviews about mixed sqlc versions, please verify and pin one version via sqlc.yaml. Re-run generation if discrepancies remain.

Use the same script shared in my other comment to enumerate versions and flag outliers. Pass your intended target (e.g., 1.28.0) as the first argument to highlight mismatches.

go/pkg/db/identity_delete.sql_generated.go (1)

3-3: Ensure a single pinned sqlc version across generated files.
Please confirm no stragglers remain on older/newer versions and pin via sqlc.yaml.

Reuse the repo scan script from my earlier comment to validate.

go/pkg/db/identity_update.sql_generated.go (1)

3-3: Pin the sqlc tool version; verify consistency.
Keep generated artifacts aligned by enforcing a single sqlc version in the repo.

Please run the previously provided script to confirm uniformity.

go/pkg/db/build_find_latest_by_deployment_id.sql_generated.go (1)

12-12: Non-blocking naming note (generated): prefer ID over Id in identifiers.
The symbol FindLatestBuildByDeploymentId uses “Id” instead of “ID”. Since this is generated, not actionable here; consider a thin hand-written wrapper alias if external API ergonomics matter.

go/pkg/db/identity_list.sql_generated.go (1)

3-3: Ensure a single sqlc version across the repo

Previous review threads flagged mixed sqlc versions. Please verify the entire repo uses one version (ideally pinned), and regenerate any stragglers.

Run this script from repo root to confirm only one sqlc version is present in all generated DB files and to list any outliers:

#!/usr/bin/env bash
set -euo pipefail

echo "Unique sqlc versions found in go/pkg/db/*_generated.go:"
rg -nP '^//\s+sqlc v' go/pkg/db -g '*_generated.go' | awk '{print $NF}' | sort | uniq -c

echo
echo "Outliers (if more than one version exists):"
versions=$(rg -nP '^//\s+sqlc v' go/pkg/db -g '*_generated.go' | awk '{print $NF}' | sort | uniq | wc -l)
if [ "$versions" -gt 1 ]; then
  rg -nP '^//\s+sqlc v' go/pkg/db -g '*_generated.go' | sort
  exit 1
else
  echo "OK: single sqlc version detected."
fi

Optionally pin the version to avoid drift (example):

  • sqlc.yaml
version: "2"
sql:
  - engine: "mysql"
    schema: "go/pkg/db/schema"
    queries: "go/pkg/db/queries"
    gen:
      go:
        package: "db"
        out: "go/pkg/db"
  • Tooling (Makefile)
SQLC_VERSION ?= 1.28.0
sqlc-generate:
	docker run --rm -v $$PWD:/src -w /src sqlc/sqlc:v$(SQLC_VERSION) generate
go/pkg/db/key_permission_delete_by_key_and_permission_id.sql_generated.go (1)

3-3: Follow-up: unify sqlc version across generated files

Mirror of earlier request to avoid mixed generator versions across the codebase; please run the repo-wide check and pin in CI if needed.

go/pkg/db/key_insert.sql_generated.go (1)

3-3: Keep sqlc version consistent across repo

Same consistency check as noted earlier to avoid multiple sqlc versions in generated files.

go/pkg/db/identity_insert_ratelimit.sql_generated.go (1)

3-3: Repo-wide sqlc version consistency

Reiterating the consistency check request to ensure all generated headers match a single version.

go/pkg/db/api_soft_delete.sql_generated.go (1)

3-3: Unify sqlc version across generated files

Please ensure there isn’t a mixture of sqlc versions across go/pkg/db and other generated packages.

go/pkg/db/permission_list_by_key_id.sql_generated.go (1)

3-3: Verify sqlc version consistency across the repo

Ensure all generated files are on the same sqlc version to avoid churn in future regenerations and mixed diffs. The previous discussion in this repo flagged mixed versions; let’s re-check and standardize on v1.28.0 (or intentionally bump all to v1.29.0 if that’s the plan).

Run from repo root:

#!/bin/bash
set -euo pipefail

# Show distinct sqlc versions across generated files
echo "Distinct sqlc versions found:"
rg -nP '^//\s+sqlc v[0-9.]+' go/pkg/db | awk -F'v' '{print $NF}' | sort -u

# List any files not matching v1.28.0 (adjust if targeting a different version)
echo
echo "Files NOT at sqlc v1.28.0 (should be empty if consistent):"
rg -nP '^//\s+sqlc v(?!1\.28\.0\b).*' go/pkg/db -H || true

# Optional: ensure sqlc.yaml (or generation scripts) pins the intended version
fd -a sqlc.yaml | xargs -I{} sh -c 'echo "--- {};"; cat "{}"'

Expected: only v1.28.0 appears; the “NOT at v1.28.0” section is empty. If not, please regenerate those files or align the configured version in CI/tooling.

go/pkg/db/key_permission_delete_many_by_permission_id.sql_generated.go (1)

3-3: LGTM: header bump; reconcile earlier thread expecting v1.29.0

This file targets sqlc v1.28.0. A prior review thread in this repo referenced v1.29.0 and highlighted mixed versions. Please confirm the intended standard (v1.28.0 vs v1.29.0) and resolve that thread to avoid confusion. If v1.28.0 is the target, ensure remaining files are regenerated to match and CI pins the same.

go/pkg/db/queries/deployment_find_by_id.sql (1)

11-16: Add unit hint for git_commit_timestamp and verify migrations exist

  • Nit: add the same “Unix epoch milliseconds” comment used in InsertDeployment for parity and to prevent unit confusion down the stack.
  • Follow-up: ensure there is a forward SQL migration that adds these six nullable columns to the existing deployments table in production.

Apply this diff to annotate the unit:

     git_branch,
-    git_commit_message,
+    git_commit_message,
     git_commit_author_name,
     git_commit_author_email,
     git_commit_author_username,
     git_commit_author_avatar_url,
-    git_commit_timestamp,
+    git_commit_timestamp, -- Unix epoch milliseconds
     config_snapshot,

Run to verify the migration(s) are present and correct:

#!/bin/bash
set -euo pipefail

echo "== Find migrations that alter `deployments` =="
rg -nP -C2 '(?i)alter\s+table\s+`?deployments`?.*(add\s+column|add\s+)' -- **/*migrations*/*.sql || true

echo
echo "== Check each new column appears in migrations with expected types =="
rg -nP '(?i)git_commit_message\s+text|git_commit_author_name\s+varchar|git_commit_author_email\s+varchar|git_commit_author_username\s+varchar|git_commit_author_avatar_url\s+varchar|git_commit_timestamp\s+bigint' -- **/*migrations*/*.sql || true

echo
echo "== Sanity: matching columns in schema.sql (post-regeneration) =="
rg -n 'git_commit_(message|author_name|author_email|author_username|author_avatar_url|timestamp)' go/pkg/db/schema.sql || true
go/pkg/db/deployment_find_by_id.sql_generated.go (2)

73-79: row.Scan adds six nullable git_ fields — verify Deployment uses sql.NullString/NullInt64.*

To avoid empty-string bleed-through, these should be sql.NullString (message/author*) and sql.NullInt64 (timestamp). Past checks indicated that’s already true.


22-27: SELECT extended with six git_ columns — scan/order parity looks correct.*

Column order matches the Scan order below. Ensure the migration added these columns with expected nullability and widths. If recent-deploy lists will sort/filter by commit time, consider a composite index including git_commit_timestamp.

go/pkg/db/bulk_deployment_insert.sql.go (1)

40-46: Args ordering matches column list — keep tests to detect drift.

Please keep/extend the unit test that asserts placeholder count and arg packing order for bulk insert.

go/apps/ctrl/services/deployment/get_version.go (1)

61-63: Remove author email from public response (PII exposure).

Author email isn’t required by acceptance criteria and increases privacy/compliance risk if exposed broadly. Omit for now; reintroduce later with explicit permission checks if needed.

Apply:

- if deployment.GitCommitAuthorEmail.Valid {
-   protoVersion.GitCommitAuthorEmail = deployment.GitCommitAuthorEmail.String
- }
go/apps/ctrl/services/deployment/create_version.go (3)

116-116: Prefer leaving UpdatedAt NULL on creation.

Avoid conflating created vs updated. Initialize UpdatedAt as NULL; set on first real update.

- UpdatedAt:                sql.NullInt64{Int64: now, Valid: true},
+ UpdatedAt:                sql.NullInt64{Valid: false},

17-24: Rune-safe truncation helper is good. Add an AIDEV note.

Document why we bound sizes (DB width, UI). Helps future maintainers.

-// limitString truncates a string to the specified maximum number of runes
+// limitString truncates a string to the specified maximum number of runes.
+// AIDEV-NOTE: Defensive bound to respect DB varchar/text widths and prevent oversized rows.

71-86: Consider normalizing seconds→ms instead of hard-rejecting valid seconds.

Upstreams often send seconds. Auto-normalize when clearly seconds; keep future-time guard.

- // Validate git commit timestamp if provided (must be Unix epoch milliseconds)
+ // Validate/normalize git commit timestamp if provided (store as Unix epoch ms)
  if req.Msg.GetGitCommitTimestamp() != 0 {
-   timestamp := req.Msg.GetGitCommitTimestamp()
-   // Reject timestamps that are clearly in seconds format (< 1_000_000_000_000)
-   // This corresponds to January 1, 2001 in milliseconds
-   if timestamp < 1_000_000_000_000 {
-     return nil, connect.NewError(connect.CodeInvalidArgument,
-       fmt.Errorf("git_commit_timestamp must be Unix epoch milliseconds, got %d (appears to be seconds format)", timestamp))
-   }
+   timestamp := req.Msg.GetGitCommitTimestamp()
+   // Normalize seconds→ms if value looks like seconds.
+   if timestamp > 0 && timestamp < 1_000_000_000_000 {
+     timestamp = timestamp * 1000
+   }
    // Also reject future timestamps more than 1 hour ahead (likely invalid)
    maxValidTimestamp := time.Now().Add(1 * time.Hour).UnixMilli()
    if timestamp > maxValidTimestamp {
      return nil, connect.NewError(connect.CodeInvalidArgument,
        fmt.Errorf("git_commit_timestamp %d is too far in the future (must be Unix epoch milliseconds)", timestamp))
    }
+   // Reassign normalized value back into the request-derived local
+   // for downstream InsertDeploymentParams.
+   // (Or keep a local variable and use it directly in params below.)
+   // AIDEV-NOTE: Timestamp stored in ms consistently.
+   // (follow-up: add a unit test for seconds input)
  }
go/pkg/db/deployment_insert.sql_generated.go (2)

24-30: INSERT includes git_commit_timestamp with millis comment — good clarity.

Comment documents epoch ms; consistent with service validation. Keep proto doc aligned.


68-79: Initialism nit: AvatarUrl → AvatarURL (optional).

Generated field names use Url. If you want to adopt Go initialism conventions, map via sqlc rename rules; otherwise consistency across the codebase is acceptable.

go/pkg/db/models_generated.go (1)

655-674: Git commit metadata fields added correctly; millis semantics already documented in proto.

The six new nullable fields on Deployment align with the PR objectives and types are appropriate (sql.NullString/sql.NullInt64). Also saw the proto comments marking GitCommitTimestamp as Unix epoch milliseconds, which addresses the earlier ask to document units. No code changes needed here.

@mcstepp mcstepp force-pushed the ENG-1992-rich-git-commit-data branch from 5c1df60 to 35805e7 Compare August 26, 2025 19:32
@vercel vercel bot temporarily deployed to Preview – dashboard August 26, 2025 19:33 Inactive
@vercel vercel bot temporarily deployed to Preview – engineering August 26, 2025 19:34 Inactive
@mcstepp mcstepp force-pushed the ENG-1992-rich-git-commit-data branch from eb5225b to 69247f4 Compare August 28, 2025 15:56
@vercel vercel bot temporarily deployed to Preview – engineering August 28, 2025 15:58 Inactive
@vercel vercel bot temporarily deployed to Preview – dashboard August 28, 2025 15:59 Inactive
Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

LGTM

@chronark chronark enabled auto-merge August 28, 2025 19:53
@chronark
Copy link
Collaborator

@perkinsjr @Flo4604 we need a 2nd approval

@graphite-app
Copy link

graphite-app bot commented Aug 28, 2025

TV gif. Steve Irwin the Crocodile Hunter looking down at a body of water, turns around and gives a double thumbs-up, mouthing 'that's good.' (Added via Giphy)

@chronark chronark added this pull request to the merge queue Aug 28, 2025
@graphite-app
Copy link

graphite-app bot commented Aug 28, 2025

Graphite Automations

"Post a GIF when PR approved" took an action on this PR • (08/28/25)

1 gif was posted to this PR based on Andreas Thomas's automation.

Merged via the queue into main with commit ad1877c Aug 28, 2025
17 checks passed
@chronark chronark deleted the ENG-1992-rich-git-commit-data branch August 28, 2025 20:07
@coderabbitai coderabbitai bot mentioned this pull request Sep 30, 2025
18 tasks
This was referenced Oct 21, 2025
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.

Rich Git Commit Information

3 participants