Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
pullbase | 8afafa3 | Commit Preview URL Branch Preview URL |
Jan 15 2026, 09:09 PM |
📝 Walkthrough🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
server/pkg/configvalidate/validate.go (1)
92-109: Syntax error: Missing closing brace for the Decode error check.The structure appears broken. Line 93 opens an
if err != nil {block, but there's no closing brace before the error handling logic at line 94. The existing error handling code (lines 94-107) should be nested inside line 93's block, but the indentation and brace structure is incorrect.The code should be structured as:
🐛 Proposed fix
var config ServerConfig if len(rootNode.Content) > 0 { if err := rootNode.Content[0].Decode(&config); err != nil { - if yamlErr, ok := err.(*yaml.TypeError); ok { - for _, e := range yamlErr.Errors { - errors = append(errors, ValidationError{ - Field: "", - Message: e, - }) + if yamlErr, ok := err.(*yaml.TypeError); ok { + for _, e := range yamlErr.Errors { + errors = append(errors, ValidationError{ + Field: "", + Message: e, + }) + } + } else { + errors = append(errors, ValidationError{ + Field: "", + Message: fmt.Sprintf("YAML parsing error: %v", err), + }) + } + return errors } - } else { - errors = append(errors, ValidationError{ - Field: "", - Message: fmt.Sprintf("YAML parsing error: %v", err), - }) - } - return errors - } }server/docs/swagger.json (1)
9-13: Same missingsecurityDefinitionsissue as swagger.yaml.Multiple endpoints reference
AgentAuthandBearerAuthsecurity schemes that are no longer defined. If this file is auto-generated from annotations or swagger.yaml, ensure the security definitions are restored in the source.Also applies to: 67-70, 113-116
🤖 Fix all issues with AI agents
In `@public/pullbase-website/src/pages/index.astro`:
- Around line 58-59: Anchor element for "[ GET_STARTED ]" includes rel="noopener
noreferrer" but lacks target="_blank"; either remove the rel attribute if you
want same-tab navigation or add target="_blank" to open in a new tab and keep
rel for security – update the <a> element that contains the text "[ GET_STARTED
]" in index.astro accordingly so rel and target attributes are consistent.
- Around line 35-37: The anchor with text "GET_STARTED" includes rel="noopener
noreferrer" but no target, causing inconsistency; update the <a> element (the
GET_STARTED link) to either remove the rel attribute if you intend same-tab
navigation, or—preferred for external docs—add target="_blank" so rel="noopener
noreferrer" is effective and matches other external links; ensure you update the
GET_STARTED link attributes accordingly.
In `@server/migrations/sqlite/000002_add_auto_reconcile.down.sql`:
- Around line 1-2: The migration uses "ALTER TABLE servers DROP COLUMN
auto_reconcile" which requires SQLite >= 3.35.0; either update the migration to
be compatible with older SQLite by performing the safe alter-table pattern
(create a new servers_temp table without auto_reconcile, copy data from servers,
drop servers, rename servers_temp to servers, and recreate indexes/constraints)
in 000002_add_auto_reconcile.down.sql (and the other listed migrations), or
alternatively document/enforce the minimum SQLite version (>= 3.35.0) for the
project and add a startup/version check; reference the migration name
"000002_add_auto_reconcile.down.sql" and the SQL statement "ALTER TABLE servers
DROP COLUMN auto_reconcile" when making the change.
In `@server/migrations/sqlite/000003_add_soft_delete.down.sql`:
- Around line 1-5: Add a note to CONTRIBUTING.md documenting the minimum SQLite
version required for migrations that use ALTER TABLE ... DROP COLUMN;
specifically add "SQLite 3.35.0+ (for development)" to the
prerequisites/dependencies section so readers know migrations like those
referencing DROP COLUMN (e.g., the migration that drops idx_servers_deleted_at
and uses ALTER TABLE servers DROP COLUMN deleted_at) require SQLite 3.35.0 or
later.
In `@server/migrations/sqlite/011_add_deployed_commit.down.sql`:
- Around line 1-3: Add a note to the migrations README stating the project
requires SQLite 3.35.0 or later because several migrations use the "ALTER TABLE
... DROP COLUMN" syntax; update server/migrations/README.md to include a brief
"Minimum SQLite version" section (or a line in the intro) that names 3.35.0+ and
references that migrations such as 000002, 000003, 013 and
011_add_deployed_commit.down.sql rely on DROP COLUMN so developers must run at
least that SQLite version.
In `@server/migrations/sqlite/017_replace_pat_with_github_app.up.sql`:
- Around line 6-7: The migration currently executes "ALTER TABLE environments
DROP COLUMN token" which permanently removes the token column and data; before
dropping it, add an explicit pre-migration step that exports/archives existing
environments.token values (e.g., to a backup table or external storage) and
implement a validation step that checks every environment has been migrated to
GitHub App auth (fail the migration and surface a clear warning if any
environments still rely on token), and update the down migration and release
notes to document the recovery/rollback path and communicate the required
upgrade steps to users.
In `@server/migrations/sqlite/021_add_notification_webhook.down.sql`:
- Line 1: The down migration uses "ALTER TABLE environments DROP COLUMN
notification_webhook_url;" which lacks the IF EXISTS defensive clause; update
the SQL to drop the column with IF EXISTS (i.e., ALTER TABLE environments DROP
COLUMN IF EXISTS notification_webhook_url) so it matches the PostgreSQL pattern
and avoids errors during partial rollbacks while still noting SQLite 3.35.0+ is
required.
In `@server/pkg/config/config_test.go`:
- Around line 89-92: The t.Cleanup closure captures the loop variable key by
reference so only the last env var gets unset; fix by capturing key (and/or
value) by value inside the loop before registering cleanup (e.g. create a new
local variable like k := key or use the shadow-variable pattern) and call
t.Cleanup(func() { os.Unsetenv(k) }) so each registered cleanup unsets the
correct environment variable that was set with os.Setenv.
In `@server/pkg/configvalidate/validate.go`:
- Around line 245-247: The error message in validate.go is misleading: the check
uses if mode > 0777 allowing 0777, but the returned error says "must be less
than 0777"; update the error text in the return fmt.Errorf call that references
modeStr so it correctly reflects the condition (e.g., "must be less than or
equal to 0777" or "must be at most 0777") while leaving the existing mode > 0777
check unchanged.
🧹 Nitpick comments (22)
server/migrations/sqlite/000001_create_initial_schema.up.sql (2)
13-22: Consider adding a UNIQUE constraint on server name or (name, repo_url).If server names should be unique within the system (or unique per repository), adding a constraint would prevent duplicate entries at the database level. This is optional depending on your business requirements.
25-35: Consider adding an index on(server_id, agent_timestamp DESC)as a composite index.You have separate indexes on
server_idandagent_timestamp, but queries filtering byserver_idand ordering byagent_timestamp(e.g., getting the latest status for a server) would benefit from a composite index.♻️ Suggested composite index
CREATE INDEX IF NOT EXISTS idx_agent_status_server_id ON agent_status(server_id); -CREATE INDEX IF NOT EXISTS idx_agent_status_timestamp ON agent_status(agent_timestamp DESC); +CREATE INDEX IF NOT EXISTS idx_agent_status_server_timestamp ON agent_status(server_id, agent_timestamp DESC);server/migrations/sqlite/015_add_agent_tokens.up.sql (1)
14-15: Redundant index ontoken_hash.The
UNIQUEconstraint ontoken_hash(line 4) already creates an implicit unique index in SQLite. The explicit indexidx_agent_tokens_token_hashis redundant and adds unnecessary overhead during writes.♻️ Suggested fix
-- Create index for efficient token lookups -CREATE INDEX idx_agent_tokens_token_hash ON agent_tokens(token_hash); CREATE INDEX idx_agent_tokens_server_id ON agent_tokens(server_id); CREATE INDEX idx_agent_tokens_active ON agent_tokens(is_active) WHERE is_active = 1;If you remove this index from the up migration, also remove
DROP INDEX IF EXISTS idx_agent_tokens_token_hash;from the down migration.CONTRIBUTING.md (3)
31-33: LGTM! Clear PR guidelines with helpful cross-references.The updated guidance properly references the new Testing section and clarifies documentation expectations.
Optional: Consider wrapping the bare URL on Line 32 in angle brackets per Markdown best practices:
<https://github.com/pullbase/docs>
58-77: Excellent addition! Comprehensive testing guidance.The new Testing section clearly distinguishes between sanity, full, and integration tests, helping contributors choose the appropriate test level.
Optional: Line 66 has a minor grammar quirk. Consider rewording for clarity:
📝 Suggested grammar improvement
-Full tests (requires built UI assets for server without stubs): +Full tests (requires UI assets to be built for server without stubs):Or more concisely:
-Full tests (requires built UI assets for server without stubs): +Full tests (need built UI assets, no stubs):
79-85: Great addition! Clear guidance for documentation and API changes.The new section provides essential instructions for keeping documentation and Swagger artifacts up-to-date.
Optional: Line 81 has a bare URL. Consider wrapping it in angle brackets per Markdown conventions:
<https://github.com/pullbase/docs>server/cmd/pullbasectl/status.go (1)
125-138: Good signal handling pattern.The context-based cancellation with signal notification is well implemented. The buffered channel prevents the signal from being lost if not immediately received.
Minor note:
signal.Stop(sigCh)could be called in the deferred cleanup for completeness, though it's not critical for a CLI tool that exits after this function returns.♻️ Optional: Add signal cleanup
ctx, cancel := context.WithCancel(context.Background()) defer cancel() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + defer signal.Stop(sigCh) go func() { <-sigCh cancel() }()server/migrations/sqlite/014_add_auto_reconcile_to_environments.up.sql (1)
5-5: Consider addingIF NOT EXISTSfor idempotent index creation.Other migrations in this PR (e.g.,
010_create_events.up.sql) useIF NOT EXISTSfor table creation. For consistency and to allow safe re-runs, consider adding the same guard to the index creation.Additionally, an index on a low-cardinality boolean column (0/1) typically provides minimal query optimization benefit unless you're frequently querying for rare values.
Suggested improvement
--- Create index for auto_reconcile column for performance -CREATE INDEX idx_environments_auto_reconcile ON environments(auto_reconcile); +-- Create index for auto_reconcile column for performance +CREATE INDEX IF NOT EXISTS idx_environments_auto_reconcile ON environments(auto_reconcile);server/migrations/sqlite/010_create_events.up.sql (1)
12-16: Index creation missingIF NOT EXISTSguard.The table creation uses
IF NOT EXISTSfor idempotency, but the index creation statements do not. If this migration is partially applied or re-run, the indexes could fail to create or cause errors.Suggested fix for idempotent index creation
-- Create indexes for efficient querying -CREATE INDEX idx_events_environment_id ON events(environment_id); -CREATE INDEX idx_events_server_id ON events(server_id); -CREATE INDEX idx_events_event_type ON events(event_type); -CREATE INDEX idx_events_timestamp ON events(timestamp DESC); +CREATE INDEX IF NOT EXISTS idx_events_environment_id ON events(environment_id); +CREATE INDEX IF NOT EXISTS idx_events_server_id ON events(server_id); +CREATE INDEX IF NOT EXISTS idx_events_event_type ON events(event_type); +CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp DESC);server/migrations/sqlite/008_create_environments.up.sql (1)
18-18: Low-value index onprovidercolumn.Since the CHECK constraint enforces
provider = 'github'(single allowed value), this index will have extremely low cardinality and provide no query benefit. Consider removing it unless you plan to support multiple providers in the future.♻️ Suggested removal
CREATE INDEX idx_environments_repo_url ON environments(repo_url); -CREATE INDEX idx_environments_provider ON environments(provider); CREATE INDEX idx_environments_status ON environments(status);server/migrations/sqlite/021_add_notification_webhook.up.sql (1)
1-1: LGTM!Adding a nullable
TEXTcolumn for webhook URLs is safe and appropriate.Minor note: Other migrations in this PR (e.g.,
011_add_deployed_commit.up.sql) include a descriptive comment header. Consider adding one for consistency, though this is optional.server/migrations/sqlite/009_add_rollback_events.up.sql (1)
17-20: Consider addingIF NOT EXISTSto index creation for idempotency.The table uses
CREATE TABLE IF NOT EXISTSfor safe re-runs, but the indexes use plainCREATE INDEX. If the migration partially succeeds (table created, some indexes created) and is retried, it will fail on the already-created indexes.♻️ Suggested fix
-- Create indexes for efficient querying -CREATE INDEX idx_rollback_events_environment_id ON rollback_events(environment_id); -CREATE INDEX idx_rollback_events_status ON rollback_events(status); -CREATE INDEX idx_rollback_events_created_at ON rollback_events(created_at DESC); +CREATE INDEX IF NOT EXISTS idx_rollback_events_environment_id ON rollback_events(environment_id); +CREATE INDEX IF NOT EXISTS idx_rollback_events_status ON rollback_events(status); +CREATE INDEX IF NOT EXISTS idx_rollback_events_created_at ON rollback_events(created_at DESC);server/migrations/sqlite/012_remove_polling_support.down.sql (1)
1-3: Clarify that data changes are also not reverted.The comment only mentions the CHECK constraint limitation, but doesn't clarify that the
status = 'fallback'→'error'data change from the up migration is also not reversed. This could mislead developers.📝 Suggested improvement
-- Migration: Restore polling support and fallback status -- Note: SQLite cannot modify CHECK constraints after table creation. --- This is a no-op for SQLite. +-- This is a no-op for SQLite. The data change (fallback -> error) from the +-- up migration is not reverted as we cannot reliably identify which 'error' +-- statuses were originally 'fallback'.server/pkg/testutil/testutil.go (1)
156-166: Consider documenting the path assumption.The function traverses up 3 directory levels from
testutil.goto reachserver/migrations/. This works correctly for the current structure but could break if the test utility file is moved.A brief comment documenting the expected path relationship would aid future maintainers:
📝 Suggested documentation
func defaultMigrationPath() string { + // Navigate from server/pkg/testutil/testutil.go up to server/migrations/ _, currentFile, _, ok := runtime.Caller(0) if !ok { return "file://migrations" } serverDir := filepath.Dir(filepath.Dir(filepath.Dir(currentFile))) migrationsDir := filepath.Join(serverDir, "migrations") return "file://" + migrationsDir }server/pkg/apierrors/errors.go (1)
300-301: Consider handling JSON encoding errors.The
json.NewEncoder(w).Encode(resp)error is silently ignored. While this is common practice for response writing (since headers are already sent), consider logging encoding failures for debugging purposes.♻️ Optional: Log encoding errors
- json.NewEncoder(w).Encode(resp) + if err := json.NewEncoder(w).Encode(resp); err != nil { + // Log but don't return - headers already sent + // Consider adding: log.Warn("failed to encode error response", "error", err) + }server/pkg/rollback/service_test.go (1)
201-201: Consider usingio.Discardinstead ofos.Stdoutfor quieter test output.The logger is initialized with
os.Stdout, which will produce output during test runs. Other test files in this PR (e.g.,webhook_handlers_integration_test.go) useio.Discardto suppress log output. Consider aligning for consistency and cleaner test output.Suggested change
+import "io" + -logger := logging.NewLogger(logging.Options{Format: "text", Output: os.Stdout}) +logger := logging.NewLogger(logging.Options{Format: "text", Output: io.Discard})Also applies to: 337-337, 364-364, 400-400
server/pkg/notifications/webhook_test.go (1)
127-148: Test correctly validates context timeout during retry backoff.The test flow is accurate: first request returns 500 → retry backoff begins (50ms) → context times out (25ms) →
ctx.Err()returned via the select statement. The implementation properly checksctx.Done()during the backoff wait, so cancellation is correctly handled.The timing margins are intentionally tight (25ms timeout vs 50ms backoff) to ensure the context expires during the wait period rather than before. This is a valid test scenario. However, in loaded CI environments where the initial HTTP request itself may take measurable time, the timing could be tighter than intended, potentially causing the test to be sensitive to execution speed.
Consider either increasing the context timeout slightly (e.g., 40ms) while keeping it below the 50ms backoff, or reducing the backoff delay to create a clearer separation and more predictable behavior across environments.
server/pkg/server/handlers_agent.go (2)
241-247: Movedefer r.Body.Close()after reading the body.The
defer r.Body.Close()is placed beforejson.NewDecoder(r.Body).Decode(). While this will technically work (defer executes at function return), it's unconventional and could cause issues if the code is refactored to return early. The standard pattern is to close the body after you're done reading it.♻️ Suggested improvement
func (api *API) UpdateAgentStatusHandler(w http.ResponseWriter, r *http.Request) { claims, ok := GetUserClaims(r.Context()) if !ok || claims == nil { writeAPIError(w, apierrors.Unauthorized("Authorization header or session cookie required")) return } serverID := chi.URLParam(r, "serverID") var payload AgentStatusPayload - defer r.Body.Close() if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { + r.Body.Close() api.log().Error("bad request for status update", "server_id", serverID, "error", err) writeAPIError(w, apierrors.BadRequestf("Invalid status payload: %s", err.Error())) return } + r.Body.Close()Alternatively, keep defer but place it immediately after acquiring the body or at function start:
func (api *API) UpdateAgentStatusHandler(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() claims, ok := GetUserClaims(r.Context())
527-533: Same issue: movedefer r.Body.Close()to function start.Same pattern issue as
UpdateAgentStatusHandler. Consider moving the defer to the beginning of the function for clarity.♻️ Suggested improvement
func (api *API) AgentUpdateStatusHandler(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() agentToken, ok := GetAgentToken(r.Context()) if !ok || agentToken == nil { writeAPIError(w, apierrors.Unauthorized("Agent token required")) return } serverID := agentToken.ServerID var payload AgentStatusPayload - defer r.Body.Close()server/cmd/server/main.go (1)
280-284: Consider consolidating duplicate logger instances.Two separate logger instances are created with identical options (
Format: "text", Output: os.Stdout). Consider creating a single logger instance and reusing it.♻️ Suggested consolidation
+ logger := logging.NewLogger(logging.Options{Format: "text", Output: os.Stdout}) + var environmentMonitor *gitmonitor.EnvironmentMonitor var rollbackGitMonitor rollback.GitMonitor var installationTokenProvider gitmonitor.InstallationTokenProvider if cfg.Git.Enabled { // ... encryption key and GitHub client setup ... - logger := logging.NewLogger(logging.Options{Format: "text", Output: os.Stdout}) webhookRouter := gitmonitor.NewWebhookRouter(logger, nil) webhookManager := gitmonitor.NewWebhookManager(webhookRouter, logger, envRepo) // ... } - logger := logging.NewLogger(logging.Options{Format: "text", Output: os.Stdout}) rollbackService := rollback.NewService(repo, rollbackGitMonitor, logger)This would require moving the logger declaration before the
if cfg.Git.Enabledblock and ensuring it's in scope for both use cases.Also applies to: 302-303
server/docs/swagger.yaml (1)
481-482: Stripped API metadata may impact documentation quality.The
infosection has been reduced to justcontact: {}. While this compiles, consider retaining essential metadata liketitle,version, anddescriptionfor better API documentation and client SDK generation.server/docs/swagger.json (1)
3-5: Stripped API metadata.Similar to swagger.yaml, the
infoblock has minimal content. If this is auto-generated, the source annotations should include proper API metadata.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
server/go.sumis excluded by!**/*.sum
📒 Files selected for processing (117)
CONTRIBUTING.mdagent/main.gopublic/pullbase-website/src/pages/index.astroserver/cmd/pullbasectl/auth.goserver/cmd/pullbasectl/bootstrap.goserver/cmd/pullbasectl/environments.goserver/cmd/pullbasectl/globals.goserver/cmd/pullbasectl/servers.goserver/cmd/pullbasectl/status.goserver/cmd/pullbasectl/users.goserver/cmd/pullbasectl/validate_config_test.goserver/cmd/server/main.goserver/cmd/server/main_test.goserver/docs/docs.goserver/docs/swagger.jsonserver/docs/swagger.yamlserver/go.modserver/migrations/postgres/000001_create_initial_schema.down.sqlserver/migrations/postgres/000001_create_initial_schema.up.sqlserver/migrations/postgres/000002_add_auto_reconcile.down.sqlserver/migrations/postgres/000002_add_auto_reconcile.up.sqlserver/migrations/postgres/000003_add_soft_delete.down.sqlserver/migrations/postgres/000003_add_soft_delete.up.sqlserver/migrations/postgres/008_create_environments.down.sqlserver/migrations/postgres/008_create_environments.up.sqlserver/migrations/postgres/009_add_rollback_events.down.sqlserver/migrations/postgres/009_add_rollback_events.up.sqlserver/migrations/postgres/010_create_events.down.sqlserver/migrations/postgres/010_create_events.up.sqlserver/migrations/postgres/011_add_deployed_commit.down.sqlserver/migrations/postgres/011_add_deployed_commit.up.sqlserver/migrations/postgres/012_remove_polling_support.down.sqlserver/migrations/postgres/012_remove_polling_support.up.sqlserver/migrations/postgres/013_add_environment_to_servers.down.sqlserver/migrations/postgres/013_add_environment_to_servers.up.sqlserver/migrations/postgres/014_add_auto_reconcile_to_environments.down.sqlserver/migrations/postgres/014_add_auto_reconcile_to_environments.up.sqlserver/migrations/postgres/015_add_agent_tokens.down.sqlserver/migrations/postgres/015_add_agent_tokens.up.sqlserver/migrations/postgres/016_remove_server_git_fields.down.sqlserver/migrations/postgres/016_remove_server_git_fields.up.sqlserver/migrations/postgres/017_replace_pat_with_github_app.down.sqlserver/migrations/postgres/017_replace_pat_with_github_app.up.sqlserver/migrations/postgres/018_restrict_providers_to_github.down.sqlserver/migrations/postgres/018_restrict_providers_to_github.up.sqlserver/migrations/postgres/019_add_branch_and_deploy_path_to_environments.down.sqlserver/migrations/postgres/019_add_branch_and_deploy_path_to_environments.up.sqlserver/migrations/postgres/020_add_agent_version.down.sqlserver/migrations/postgres/020_add_agent_version.up.sqlserver/migrations/postgres/021_add_notification_webhook.down.sqlserver/migrations/postgres/021_add_notification_webhook.up.sqlserver/migrations/postgres/022_add_drift_details.down.sqlserver/migrations/postgres/022_add_drift_details.up.sqlserver/migrations/sqlite/000001_create_initial_schema.down.sqlserver/migrations/sqlite/000001_create_initial_schema.up.sqlserver/migrations/sqlite/000002_add_auto_reconcile.down.sqlserver/migrations/sqlite/000002_add_auto_reconcile.up.sqlserver/migrations/sqlite/000003_add_soft_delete.down.sqlserver/migrations/sqlite/000003_add_soft_delete.up.sqlserver/migrations/sqlite/008_create_environments.down.sqlserver/migrations/sqlite/008_create_environments.up.sqlserver/migrations/sqlite/009_add_rollback_events.down.sqlserver/migrations/sqlite/009_add_rollback_events.up.sqlserver/migrations/sqlite/010_create_events.down.sqlserver/migrations/sqlite/010_create_events.up.sqlserver/migrations/sqlite/011_add_deployed_commit.down.sqlserver/migrations/sqlite/011_add_deployed_commit.up.sqlserver/migrations/sqlite/012_remove_polling_support.down.sqlserver/migrations/sqlite/012_remove_polling_support.up.sqlserver/migrations/sqlite/013_add_environment_to_servers.down.sqlserver/migrations/sqlite/013_add_environment_to_servers.up.sqlserver/migrations/sqlite/014_add_auto_reconcile_to_environments.down.sqlserver/migrations/sqlite/014_add_auto_reconcile_to_environments.up.sqlserver/migrations/sqlite/015_add_agent_tokens.down.sqlserver/migrations/sqlite/015_add_agent_tokens.up.sqlserver/migrations/sqlite/016_remove_server_git_fields.down.sqlserver/migrations/sqlite/016_remove_server_git_fields.up.sqlserver/migrations/sqlite/017_replace_pat_with_github_app.down.sqlserver/migrations/sqlite/017_replace_pat_with_github_app.up.sqlserver/migrations/sqlite/018_restrict_providers_to_github.down.sqlserver/migrations/sqlite/018_restrict_providers_to_github.up.sqlserver/migrations/sqlite/019_add_branch_and_deploy_path_to_environments.down.sqlserver/migrations/sqlite/019_add_branch_and_deploy_path_to_environments.up.sqlserver/migrations/sqlite/020_add_agent_version.down.sqlserver/migrations/sqlite/020_add_agent_version.up.sqlserver/migrations/sqlite/021_add_notification_webhook.down.sqlserver/migrations/sqlite/021_add_notification_webhook.up.sqlserver/migrations/sqlite/022_add_drift_details.down.sqlserver/migrations/sqlite/022_add_drift_details.up.sqlserver/pkg/apierrors/errors.goserver/pkg/auth/auth.goserver/pkg/config/config.goserver/pkg/config/config_test.goserver/pkg/configvalidate/validate.goserver/pkg/database/database.goserver/pkg/database/repository.goserver/pkg/database/repository_test.goserver/pkg/gitmonitor/environment_monitor.goserver/pkg/gitmonitor/environment_monitor_test.goserver/pkg/gitmonitor/webhook_router.goserver/pkg/logging/logger.goserver/pkg/models/models.goserver/pkg/notifications/webhook.goserver/pkg/notifications/webhook_test.goserver/pkg/rollback/integration_test.goserver/pkg/rollback/service.goserver/pkg/rollback/service_test.goserver/pkg/server/handlers.goserver/pkg/server/handlers_agent.goserver/pkg/server/handlers_auth.goserver/pkg/server/handlers_config.goserver/pkg/server/handlers_environments.goserver/pkg/server/handlers_servers.goserver/pkg/server/handlers_test.goserver/pkg/server/webhook_handlers.goserver/pkg/server/webhook_handlers_integration_test.goserver/pkg/testutil/testutil.go
💤 Files with no reviewable changes (1)
- server/cmd/pullbasectl/globals.go
🧰 Additional context used
🧬 Code graph analysis (19)
server/pkg/server/webhook_handlers_integration_test.go (1)
server/pkg/logging/logger.go (2)
NewLogger(29-47)Options(23-27)
server/cmd/pullbasectl/auth.go (1)
server/pkg/auth/auth.go (1)
BootstrapPasswordMinLength(15-15)
server/pkg/server/webhook_handlers.go (2)
server/pkg/gitmonitor/environment_monitor.go (1)
EnvironmentMonitor(21-32)server/pkg/logging/logger.go (1)
Logger(11-11)
server/pkg/notifications/webhook_test.go (2)
server/pkg/notifications/webhook.go (2)
NewServiceWithConfig(59-68)BuildTestPayload(201-209)server/pkg/logging/logger.go (1)
Fatal(104-107)
server/cmd/pullbasectl/users.go (1)
server/pkg/auth/auth.go (1)
BootstrapPasswordMinLength(15-15)
server/pkg/server/handlers_servers.go (1)
server/pkg/models/models.go (1)
DriftDetails(74-79)
server/pkg/config/config.go (1)
server/pkg/logging/logger.go (2)
Info(92-94)Warn(96-98)
server/pkg/rollback/integration_test.go (1)
server/pkg/logging/logger.go (2)
NewLogger(29-47)Options(23-27)
server/pkg/gitmonitor/webhook_router.go (3)
server/pkg/models/models.go (1)
GitProvider(144-144)server/pkg/logging/logger.go (1)
Logger(11-11)server/pkg/gitmonitor/environment_monitor.go (1)
EnvironmentMonitor(21-32)
server/pkg/database/repository.go (1)
server/pkg/logging/logger.go (3)
Info(92-94)Warn(96-98)Debug(88-90)
server/pkg/rollback/service.go (1)
server/pkg/logging/logger.go (1)
Logger(11-11)
server/pkg/database/database.go (1)
server/pkg/logging/logger.go (1)
Info(92-94)
server/pkg/rollback/service_test.go (1)
server/pkg/logging/logger.go (2)
NewLogger(29-47)Options(23-27)
server/pkg/gitmonitor/environment_monitor.go (1)
server/pkg/logging/logger.go (1)
Logger(11-11)
server/pkg/server/handlers.go (1)
server/pkg/logging/logger.go (3)
Logger(11-11)Default(64-66)Warn(96-98)
server/pkg/server/handlers_auth.go (5)
server/pkg/apierrors/errors.go (9)
Error(86-91)Gone(244-249)Validationf(188-193)Forbidden(167-172)BadRequest(195-200)Internal(223-228)Is(328-334)NotFoundf(146-151)Unauthorized(153-158)server/pkg/auth/auth.go (1)
BootstrapPasswordMinLength(15-15)server/pkg/database/repository.go (1)
ErrNotFound(20-20)server/pkg/models/models.go (1)
RoleAdmin(184-184)server/pkg/logging/logger.go (1)
Warn(96-98)
server/cmd/server/main.go (1)
server/pkg/logging/logger.go (4)
Warn(96-98)Info(92-94)NewLogger(29-47)Options(23-27)
server/pkg/notifications/webhook.go (1)
server/pkg/logging/logger.go (3)
Logger(11-11)NewLogger(29-47)Options(23-27)
server/pkg/testutil/testutil.go (1)
server/pkg/logging/logger.go (1)
Warn(96-98)
🪛 Checkov (3.2.334)
server/docs/swagger.yaml
[medium] 74-78: Ensure that arrays have a maximum number of items
(CKV_OPENAPI_21)
🪛 LanguageTool
CONTRIBUTING.md
[style] ~66-~66: The double modal “requires built” is nonstandard (only accepted in certain dialects). Consider “to be built”.
Context: ...gs=test ./... ``` Full tests (requires built UI assets for server without stubs): `...
(NEEDS_FIXED)
🪛 markdownlint-cli2 (0.18.1)
CONTRIBUTING.md
32-32: Bare URL used
(MD034, no-bare-urls)
81-81: Bare URL used
(MD034, no-bare-urls)
⏰ 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: unit-tests
- GitHub Check: Workers Builds: pullbase
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@CONTRIBUTING.md`:
- Around line 81-88: Update the swag init command in CONTRIBUTING.md to use the
correct project paths: replace the generator entry flag value `-g
cmd/server/main.go` with `-g server/cmd/server/main.go` and replace the output
directory flag `-o docs` with `-o server/docs` so the command matches the actual
entry point and docs location used by the project.
- Around line 60-79: In the "Testing" section fix the grammar of the quick test
description by updating the line "Quick sanity (uses stubs to avoid UI embed):"
to a clearer phrase such as "Quick sanity check (uses stubs to avoid embedding
the UI):" (locate the "Testing" header and that sentence to make the change).
🧹 Nitpick comments (5)
CONTRIBUTING.md (1)
32-32: Consider wrapping bare URLs in angle brackets.Static analysis flagged bare URLs at lines 32 and 83. For markdown best practices, consider wrapping them in angle brackets.
📝 Proposed formatting improvements
Line 32:
-5. Update documentation if the change alters behavior or adds features (docs live at https://github.com/pullbase/docs) +5. Update documentation if the change alters behavior or adds features (docs live at <https://github.com/pullbase/docs>)Line 83:
-- Update relevant docs in https://github.com/pullbase/docs when features or behavior change. +- Update relevant docs in <https://github.com/pullbase/docs> when features or behavior change.Also applies to: 83-83
server/pkg/server/handlers.go (1)
268-278: Consider using instance logger for consistency.The
requireRolefunction uses package-levellogging.Errorandlogging.Warn, which write to the default logger. However, other methods in this file (e.g.,RecordAuditLogat lines 297, 312) useapi.log()to respect a custom logger if configured.Since
requireRoledoesn't have access to the API instance, this is acceptable, but worth noting that log output from role checks will always go to the default logger regardless ofAPI.Loggerconfiguration.server/cmd/server/main.go (1)
279-283: Consider reusing the logger instance.A logger is created here at line 279, and another identical one is created at line 301. Both use the same options (
Format: "text", Output: os.Stdout). Consider creating the logger once earlier and reusing it.♻️ Suggested refactor
Create the logger once before the
if cfg.Git.Enabledblock:+ logger := logging.NewLogger(logging.Options{Format: "text", Output: os.Stdout}) + var environmentMonitor *gitmonitor.EnvironmentMonitor var rollbackGitMonitor rollback.GitMonitor var installationTokenProvider gitmonitor.InstallationTokenProvider if cfg.Git.Enabled { // ... existing code ... - logger := logging.NewLogger(logging.Options{Format: "text", Output: os.Stdout}) webhookRouter := gitmonitor.NewWebhookRouter(logger, nil)And remove the duplicate creation at line 301.
server/pkg/testutil/testutil.go (2)
155-165: Path resolution may be fragile in some test execution contexts.
defaultMigrationPath()usesruntime.Caller(0)to locate the source file and navigates up three directories to findmigrations. This works when running tests from the standard Go toolchain but may fail if:
- Tests are run from a different working directory
- The binary is compiled and run separately from source
The fallback to
"file://migrations"at line 158-159 provides a safety net. Consider documenting this behavior or adding a test to verify the path resolution.
334-340: Hardcoded migration version may require manual updates.The fallback schema inserts migration version 22 as the current version. This needs to be updated whenever new migrations are added, which is easy to forget.
Consider extracting this as a constant or documenting the maintenance requirement.
♻️ Suggested refactor
+const fallbackSchemaVersion = 22 // Update when adding new migrations // In setupFallbackSchema(): - INSERT INTO schema_migrations (version, dirty) VALUES (22, false) ON CONFLICT DO NOTHING; + INSERT INTO schema_migrations (version, dirty) VALUES (%d, false) ON CONFLICT DO NOTHING;Or add a comment near the INSERT statement:
-- NOTE: Update this version when adding new migrations INSERT INTO schema_migrations (version, dirty) VALUES (22, false) ON CONFLICT DO NOTHING;
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
CONTRIBUTING.mdserver/cmd/server/main.goserver/migrations/sqlite/012_remove_polling_support.up.sqlserver/pkg/configvalidate/validate.goserver/pkg/csrf/csrf.goserver/pkg/database/database.goserver/pkg/server/handlers.goserver/pkg/server/handlers_auth.goserver/pkg/server/routes.goserver/pkg/testutil/testutil.go
💤 Files with no reviewable changes (1)
- server/pkg/csrf/csrf.go
🚧 Files skipped from review as they are similar to previous changes (3)
- server/pkg/configvalidate/validate.go
- server/pkg/server/handlers_auth.go
- server/migrations/sqlite/012_remove_polling_support.up.sql
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-15T20:03:09.042Z
Learnt from: hacktivist123
Repo: pullbase/pullbase PR: 1
File: server/pkg/config/config_test.go:89-92
Timestamp: 2026-01-15T20:03:09.042Z
Learning: Go 1.22+ fixes: range loop variables are captured per iteration, so closures (including t.Cleanup) generally don’t require shadowing or explicit capture. Since server/go.mod specifies Go 1.24.0, reviewers can assume this behavior across Go files in the repo and avoid shadowing for range variables in tests and implementation. If there’s code intended for older Go versions, consider explicit capture to preserve pre-1.22 semantics.
Applied to files:
server/pkg/server/routes.goserver/pkg/database/database.goserver/pkg/testutil/testutil.goserver/pkg/server/handlers.goserver/cmd/server/main.go
🧬 Code graph analysis (4)
server/pkg/database/database.go (1)
server/pkg/logging/logger.go (3)
Info(92-94)Warn(96-98)Debug(88-90)
server/pkg/testutil/testutil.go (2)
server/pkg/logging/logger.go (1)
Warn(96-98)server/pkg/database/repository.go (1)
NewRepository(39-44)
server/pkg/server/handlers.go (1)
server/pkg/logging/logger.go (3)
Logger(11-11)Default(64-66)Warn(96-98)
server/cmd/server/main.go (1)
server/pkg/logging/logger.go (4)
Warn(96-98)Info(92-94)NewLogger(29-47)Options(23-27)
🪛 LanguageTool
CONTRIBUTING.md
[style] ~68-~68: The double modal “requires built” is nonstandard (only accepted in certain dialects). Consider “to be built”.
Context: ...gs=test ./... ``` Full tests (requires built UI assets for server without stubs): `...
(NEEDS_FIXED)
🪛 markdownlint-cli2 (0.18.1)
CONTRIBUTING.md
32-32: Bare URL used
(MD034, no-bare-urls)
83-83: Bare URL used
(MD034, no-bare-urls)
⏰ 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: integration-tests
- GitHub Check: Workers Builds: pullbase
🔇 Additional comments (31)
CONTRIBUTING.md (3)
31-33: Enhanced PR workflow looks good!The additions explicitly require test verification and documentation updates, which improves contributor guidance and ensures quality standards.
42-44: Clear SQLite version requirement with good rationale.The specific version requirement (3.35.0+) is well-justified by the note explaining the need for
ALTER TABLE ... DROP COLUMNsupport in down migrations.
56-58: Agent build instructions are helpful.The addition of agent build steps provides complete build guidance for contributors.
server/pkg/server/handlers.go (3)
16-16: LGTM!Import updated to use the internal logging package, aligning with the PR-wide logging migration.
70-70: LGTM!Logger field type updated to
*logging.Logger. Sincelogging.Loggeris an alias forslog.Logger(perserver/pkg/logging/logger.goline 10), this maintains compatibility while centralizing the logging abstraction.
83-88: LGTM!The
log()helper correctly returns the internal logging type and falls back tologging.Default()when no logger is configured.server/pkg/server/routes.go (1)
70-70: LGTM!
X-CSRF-Tokencorrectly removed fromAllowedHeaders, consistent with the CSRF removal across the codebase. The remaining headers (Accept,Authorization,Content-Type) cover standard API needs.server/pkg/database/database.go (9)
8-12: LGTM!Imports updated appropriately for the new logging package and
stringsfor version parsing.
76-77: LGTM!Logging updated to use the internal logging package.
111-116: LGTM!Good addition of SQLite version validation. Logging a warning rather than failing allows flexibility while alerting operators to potential migration issues.
139-155: LGTM!Version parsing handles the common
major.minor.patchformat correctly. Silently ignoring patch parse errors on line 152 is acceptable since patch version isn't used in the minimum version comparison.
163-171: LGTM!
InitSchemacorrectly delegates tomigrationPathForDialectbefore passing to dialect-specific initializers.
174-188: LGTM!
migrationPathForDialectgracefully handles thefile://prefix and falls back to the original path if no dialect-specific subdirectory exists. Theos.Statcheck ensures the directory exists before modifying the path.
205-206: LGTM!Logging updated to use the internal logging package.
224-225: LGTM!Logging updated to use the internal logging package.
119-137: SQLite version requirement is correctly enforced.The check for SQLite >= 3.35.0 is accurate. ALTER TABLE DROP COLUMN support was introduced in SQLite 3.35.0 (released March 12, 2021), making this the appropriate minimum version requirement for the migration system.
server/cmd/server/main.go (7)
32-32: LGTM!Import updated to use the internal logging package.
65-65: LGTM!Logging call updated to use the internal logging package.
186-197: LGTM!Development certificate generation path logs appropriately with useful metadata (paths, validity, expiration).
314-322: LGTM!API struct initialization correctly uses the logger and removes CSRF field (as per AI summary). All required fields are properly initialized.
421-448: LGTM!Enhanced TLS certificate handling with clear error messages. The auto-generation of self-signed certificates when both are missing provides a good developer experience, while properly failing when only one file is present (indicating a configuration issue).
455-461: LGTM!Helpful logging metadata added for TLS startup, including certificate modification times which aids debugging certificate rotation issues.
464-474: LGTM!Graceful shutdown handling with appropriate logging.
server/pkg/testutil/testutil.go (8)
7-8: LGTM!Imports added for the new functionality:
filepathandruntimefor path computation,databaseandloggingpackages.Also applies to: 14-15
24-24: LGTM!
DefaultMigrationPathupdated to a simpler relative path. The actual path resolution is handled bydefaultMigrationPath().
31-38: LGTM!
TestDBstruct expanded withConfigandDialectfields, enabling tests to access database configuration details. The unexportedcleanupandtfields properly encapsulate test lifecycle management.
74-77: LGTM!Using
wait.ForListeningPort("5432/tcp")is more reliable than log-based waiting for PostgreSQL container readiness.
115-117: LGTM!Logging updated to use the internal logging package for container termination warnings.
140-153: LGTM!
MustMigratenow falls back todefaultMigrationPath()when empty and logs the migration path being used, aiding test debugging.
392-401: LGTM!Using
t.Cleanup(cancel)for context cancellation is the correct pattern for Go 1.24.0, ensuring contexts are properly cleaned up when tests complete. Based on learnings, Go 1.22+ handles closure captures correctly.
356-360: No action needed.WaitForDBConnectionis properly defined as a method onTestDBinserver/pkg/testutil/helpers.go:323and is correctly accessible from the call site intestutil.go.Likely an incorrect or invalid review comment.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@server/cmd/server/main.go`:
- Around line 433-434: After generating the self-signed certs, the os.Stat calls
for certFile and keyFile ignore errors and may leave certInfo or keyInfo nil;
update the code around the os.Stat(certFile) and os.Stat(keyFile) calls to check
and handle the returned errors (e.g., if err != nil) before later using
certInfo.ModTime() or keyInfo.ModTime(): log the stat error with context
(including certFile/keyFile), and return or exit the function with an error
instead of proceeding, ensuring no nil pointer dereference occurs.
♻️ Duplicate comments (1)
CONTRIBUTING.md (1)
68-68: Minor grammar issue: "requires built" is nonstandard.Consider rewording to "requires UI assets to be built" or simply "requires built UI assets".
🧹 Nitpick comments (6)
CONTRIBUTING.md (1)
81-88: LGTM - swag paths are now correct.The swag init command now correctly references
server/cmd/server/main.goand outputs toserver/docs, matching the actual project structure.Static analysis flags bare URLs on lines 32 and 83. Consider using markdown link syntax for consistency:
[pullbase/docs](https://github.com/pullbase/docs)server/pkg/testutil/testutil.go (3)
29-32: Consider documenting which migration version corresponds to FallbackSchemaVersion.The comment says "Update this when adding new migrations" but doesn't indicate what schema state version 22 represents. Consider adding a reference to the migration file name or date for easier maintenance.
144-157: Silently falling back to manual schema may hide migration issues.When
InitSchemafails, the code logs and falls back tosetupFallbackSchema(). This could mask real migration bugs in CI. Consider making the fallback opt-in or failing loudly in CI environments.💡 Suggested approach
func (tdb *TestDB) MustMigrate(migrationPath string) { tdb.t.Helper() if migrationPath == "" { migrationPath = defaultMigrationPath() } tdb.t.Logf("using migration path: %s", migrationPath) ctx := context.Background() if err := database.InitSchema(ctx, tdb.DB, tdb.Dialect, migrationPath); err != nil { - tdb.t.Logf("Migration failed: %v - falling back to manual schema", err) - tdb.setupFallbackSchema() + if os.Getenv("CI") != "" { + tdb.t.Fatalf("Migration failed in CI: %v", err) + } + tdb.t.Logf("Migration failed: %v - falling back to manual schema (disable with CI=true)", err) + tdb.setupFallbackSchema() } }
159-169: Fragile path resolution usingruntime.Callerwith hardcoded directory depth.The triple
filepath.Dircall assumes the file is always atserver/pkg/testutil/testutil.go. If the file moves or the directory structure changes, this silently returns an incorrect path.💡 Suggested improvement
Add validation that the computed path exists:
func defaultMigrationPath() string { _, currentFile, _, ok := runtime.Caller(0) if !ok { return "file://migrations" } serverDir := filepath.Dir(filepath.Dir(filepath.Dir(currentFile))) migrationsDir := filepath.Join(serverDir, "migrations") + if _, err := os.Stat(migrationsDir); os.IsNotExist(err) { + // Fall back if computed path doesn't exist + return "file://migrations" + } + return "file://" + migrationsDir }server/cmd/server/main.go (2)
249-250: Consider explicitly setting the log level.The
logging.Optionsstruct accepts aLevelfield (per the relevant snippets), but it's not specified here. While there's likely a sensible default, consider making this configurable viacfgto allow operators to control verbosity.💡 Suggested improvement
- logger := logging.NewLogger(logging.Options{Format: "text", Output: os.Stdout}) + logger := logging.NewLogger(logging.Options{ + Level: cfg.Logging.Level, // or a sensible default like "info" + Format: "text", + Output: os.Stdout, + })
410-416: Consider channel-based error handling for server startup failures.Calling
os.Exit(1)inside goroutines (lines 414, 459) bypasses deferred cleanup likedb.Close(). While startup failures typically warrant immediate exit, a channel-based approach would allow graceful cleanup.This is a minor concern since startup failures are fatal anyway, but for completeness:
errChan := make(chan error, 1) go func() { if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { errChan <- err } }() // Then select on errChan and stopChan in main
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
CONTRIBUTING.mdserver/cmd/server/main.goserver/pkg/testutil/testutil.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-15T20:03:09.042Z
Learnt from: hacktivist123
Repo: pullbase/pullbase PR: 1
File: server/pkg/config/config_test.go:89-92
Timestamp: 2026-01-15T20:03:09.042Z
Learning: Go 1.22+ fixes: range loop variables are captured per iteration, so closures (including t.Cleanup) generally don’t require shadowing or explicit capture. Since server/go.mod specifies Go 1.24.0, reviewers can assume this behavior across Go files in the repo and avoid shadowing for range variables in tests and implementation. If there’s code intended for older Go versions, consider explicit capture to preserve pre-1.22 semantics.
Applied to files:
server/cmd/server/main.goserver/pkg/testutil/testutil.go
🧬 Code graph analysis (2)
server/cmd/server/main.go (2)
server/pkg/logging/logger.go (4)
Warn(96-98)Info(92-94)NewLogger(29-47)Options(23-27)server/pkg/gitmonitor/environment_monitor.go (1)
EnvironmentMonitor(21-32)
server/pkg/testutil/testutil.go (2)
server/pkg/logging/logger.go (1)
Warn(96-98)server/pkg/database/repository.go (1)
NewRepository(39-44)
🪛 LanguageTool
CONTRIBUTING.md
[style] ~68-~68: The double modal “requires built” is nonstandard (only accepted in certain dialects). Consider “to be built”.
Context: ...gs=test ./... ``` Full tests (requires built UI assets for server without stubs): `...
(NEEDS_FIXED)
🪛 markdownlint-cli2 (0.18.1)
CONTRIBUTING.md
32-32: Bare URL used
(MD034, no-bare-urls)
83-83: Bare URL used
(MD034, no-bare-urls)
⏰ 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: integration-tests
- GitHub Check: Workers Builds: pullbase
🔇 Additional comments (18)
CONTRIBUTING.md (4)
31-33: LGTM!The expanded PR workflow steps provide clearer guidance for contributors, appropriately separating testing, documentation updates, and PR submission.
42-44: LGTM!Good addition explaining the SQLite 3.35.0+ requirement with the rationale about
ALTER TABLE ... DROP COLUMNsupport for down migrations.
60-67: LGTM!Clear testing guidance with the quick sanity check using stubs.
70-79: LGTM!Good addition of full tests and integration tests guidance with clear prerequisites noted.
server/pkg/testutil/testutil.go (7)
7-18: LGTM!Import additions are appropriate:
filepathandruntimesupport the newdefaultMigrationPath()helper, andloggingreplaces directslogusage per the project's centralized logging refactor.
35-42: LGTM!Adding
ConfigandDialectfields toTestDBprovides useful context for tests that need to inspect connection details or dialect-specific behavior.
119-121: LGTM!Correctly updated to use the project's centralized
logging.Warnhelper instead of directslogusage.
347-354: LGTM - Schema version tracking.Correctly inserts the fallback schema version into
schema_migrationsto maintain consistency with the migration system.
402-412: LGTM - Proper context cleanup.Attaching
canceltot.Cleanupensures context cancellation happens automatically when the test completes, preventing resource leaks. Based on learnings, Go 1.22+ handles closure captures correctly, so this pattern is safe.
366-392: BothWaitForDBConnectionandPollOptionsare properly defined in the same package (server/pkg/testutil/).PollOptionsis defined atserver/pkg/testutil/helpers.go:14and theWaitForDBConnectionmethod is defined atserver/pkg/testutil/helpers.go:323. They are fully accessible and visible within the package scope.Likely an incorrect or invalid review comment.
171-243: The fallback schema only supports PostgreSQL and is correct for its context.The
setupFallbackSchema()method is only invoked with PostgreSQL containers. BothSetupTestDB()andSetupTestDBWithConfig()explicitly callStartPostgresContainer()withDialect: database.DialectPostgreshardcoded. No code path creates a TestDB with SQLite dialect. While the codebase supports multiple dialects for the main application, the testutil package'sTestDBis PostgreSQL-only, making the PostgreSQL-specific syntax (SERIAL, JSONB, etc.) appropriate for its exclusive use case.Likely an incorrect or invalid review comment.
server/cmd/server/main.go (7)
32-32: LGTM on the logging import and bootstrap secret file handling.The transition to the internal logging package is clean, and the
writeBootstrapSecretFilefunction has proper error handling with appropriate directory creation, file permissions (0600), and sync before close.Also applies to: 46-79
81-161: Certificate generation implementation is well-designed.Good choices: ECDSA P-256 curve, appropriate file permissions (0600 for key, 0644 for cert), and comprehensive SAN coverage (localhost DNS + IPv4/IPv6 loopback). The 1-year validity is reasonable for development certificates.
455-461: Good operational logging with TLS metadata.Including
cert_modtimeandkey_modtimein the startup log is helpful for operators to verify which certificates are loaded. This aids debugging certificate rotation issues.
333-386: Bootstrap secret lifecycle is well-implemented.Good security practices observed:
- Secret cleared from memory after use (line 375)
- Leftover secret file removed when admin already exists
- Proper error handling for file operations
- Clear logging for each secret source (file, config, generated)
464-475: Clean shutdown sequence with appropriate timeout.The 5-second graceful shutdown timeout is reasonable, and using
Warninstead ofErrorfor shutdown failures is appropriate since in-flight requests may have completed.
186-196: Logging migration is consistent throughout startup flow.All logging calls follow the consistent
logging.Error/Warn/Info(msg, key, value, ...)pattern. Error messages are descriptive and include relevant context (paths, errors, dialect info).Also applies to: 199-235
314-322: CSRF protection is properly implemented through SameSite cookies and token-based authentication.The removal of a dedicated
CSRFfield from the API struct is not a security concern. The codebase usesSameSite: Laxcookies for session-based authentication and Bearer token authentication for API clients. This dual approach provides robust CSRF protection: session cookies are protected via the SameSite attribute (along with HttpOnly and Secure flags), while Bearer token authentication is inherently CSRF-safe since it doesn't rely on cookies for state. No dedicated CSRF middleware is needed for this architecture.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/pkg/testutil/testutil.go (1)
70-76: Wireconfig.StartupTimeoutinto the wait strategy.The
config.StartupTimeout(5 minutes) is configured but not applied topostgres.BasicWaitStrategies(). The wait strategy uses a default 60-second startup timeout instead. Pass the timeout explicitly:Suggested fix
container, err := postgres.Run(ctx, config.Image, postgres.WithDatabase(config.Database), postgres.WithUsername(config.Username), postgres.WithPassword(config.Password), postgres.BasicWaitStrategies().WithStartupTimeout(config.StartupTimeout), )
🤖 Fix all issues with AI agents
In `@server/pkg/testutil/testutil.go`:
- Around line 164-174: The file:// path construction produces backslashes on
Windows; update defaultMigrationPath to normalize the filesystem path to forward
slashes before prefixing with "file://", e.g. call filepath.ToSlash on the
migrationsDir (or its result) so the returned string uses forward slashes, and
also update migrationPathForDialect where file:// + filepath.Join(...) is used
to similarly call filepath.ToSlash on the joined path to ensure Windows paths
become "file:///C:/..." style URLs; reference the functions defaultMigrationPath
and migrationPathForDialect and normalize the path string after filepath.Join
but before adding the "file://" prefix.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
server/pkg/testutil/testutil.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-15T20:03:09.042Z
Learnt from: hacktivist123
Repo: pullbase/pullbase PR: 1
File: server/pkg/config/config_test.go:89-92
Timestamp: 2026-01-15T20:03:09.042Z
Learning: Go 1.22+ fixes: range loop variables are captured per iteration, so closures (including t.Cleanup) generally don’t require shadowing or explicit capture. Since server/go.mod specifies Go 1.24.0, reviewers can assume this behavior across Go files in the repo and avoid shadowing for range variables in tests and implementation. If there’s code intended for older Go versions, consider explicit capture to preserve pre-1.22 semantics.
Applied to files:
server/pkg/testutil/testutil.go
🧬 Code graph analysis (1)
server/pkg/testutil/testutil.go (4)
server/pkg/database/database.go (1)
New(34-52)server/pkg/logging/logger.go (1)
Warn(96-98)server/pkg/testutil/helpers.go (1)
PollOptions(16-20)server/pkg/database/repository.go (2)
Repository(24-27)NewRepository(39-44)
⏰ 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: integration-tests
- GitHub Check: Workers Builds: pullbase
🔇 Additional comments (9)
server/pkg/testutil/testutil.go (9)
7-16: Imports align with new migration-path and logging helpers.
Everything added here is used by the new logic.
23-29: VerifyFallbackSchemaVersionstays aligned with the newest migration.
Hard-coded versions drift easily as migrations grow.
32-36: Exposing Config/Dialect onTestDBis useful.
Clean addition for downstream helpers.
120-126: Warn-level logging on termination failures is helpful.
Good visibility without failing tests.
152-156: Default migration path + logging is clear.
Nice traceability when auto-selecting the path.
191-205: Fallback schema expansion + migration tracking look solid.
The added columns, triggers, andschema_migrationshandling align with the new fallback behavior.Also applies to: 220-223, 240-242, 246-248, 296-298, 338-347, 353-359
371-375: Polling for DB readiness before migrations improves stability.
Good reliability improvement for container startup races.Also applies to: 389-393
408-410: Cleanup-cancelled contexts prevent leaks.
Usingt.Cleanuphere is the right lifecycle hook.Also applies to: 414-416
103-118: Revert this suggestion;database.Newcannot return non-nil DB with error.Both
newPostgresandnewSQLiteexplicitly calldb.Close()before returning an error (lines 72 and 107, respectively). Therefore,database.Newwill either return a valid, open connection witherr == nil, or an error withdb == nil. No connection leak is possible in the retry loop.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.