[ENG-440] fix-summit-one-click-railway-deployment - #33
Conversation
📝 WalkthroughWalkthroughAdds a health-check API route, updates Dockerfile to copy builder-stage artifacts and install pnpm/tsx, introduces Railway deployment config, and adds a GitHub Actions release workflow for building and publishing Docker images. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant HealthEndpoint as Health Endpoint
participant Database
participant Response
Client->>HealthEndpoint: GET /api/health
HealthEndpoint->>HealthEndpoint: Build health object (status, timestamp, uptime, env)
HealthEndpoint->>Database: Execute "SELECT 1"
alt Database connected
Database-->>HealthEndpoint: Success
HealthEndpoint->>HealthEndpoint: Set database='connected', status='healthy'
else Database error
Database-->>HealthEndpoint: Error
HealthEndpoint->>HealthEndpoint: Set database='disconnected', status='unhealthy'
end
HealthEndpoint->>Response: Return JSON with HTTP 200 or 503
Response-->>Client: Health JSON
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 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
Comment |
PR Check Results✅ Tests PassedTest Output✅ Build PassedBuild Output |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @Dockerfile:
- Around line 101-106: The Dockerfile currently copies the entire node_modules
via the COPY --from=builder --chown=nextjs:nodejs /app/node_modules
./node_modules line which balloons image size and includes dev deps like
drizzle-kit; instead run drizzle migrations during the builder stage (after pnpm
run build) so you can remove that COPY, or introduce a small dedicated stage
that installs/contains only drizzle-kit and its runtime deps and invoke
drizzle-kit there (or use Railway build hooks/init container) so the final
runner stage only receives the Next.js output and production deps; update the
Dockerfile to stop copying /app/node_modules into the runner and either (a) move
migration commands to the builder stage, or (b) add a minimal multi-stage copy
that only transfers the specific drizzle-kit artifacts rather than the whole
node_modules.
🧹 Nitpick comments (2)
src/app/api/health/route.ts (1)
4-26: Add type safety for the health response object.The health object's structure changes dynamically (the
databaseproperty is conditionally added), which can lead to type inconsistencies. Consider defining an explicit interface for the health response.♻️ Suggested type-safe implementation
+interface HealthResponse { + status: 'healthy' | 'unhealthy'; + timestamp: string; + uptime: number; + environment: string; + database?: 'connected' | 'disconnected'; +} + export async function GET(request: NextRequest) { try { // Basic health check - const health = { + const health: HealthResponse = { status: 'healthy', timestamp: new Date().toISOString(), uptime: process.uptime(), environment: process.env.NODE_ENV || 'development', }; // Optional: Check database connectivity try { // Simple query to verify database connection await db.execute('SELECT 1'); health.database = 'connected'; } catch (dbError) { health.database = 'disconnected'; health.status = 'unhealthy'; } return NextResponse.json(health, { status: health.status === 'healthy' ? 200 : 503 }); } catch (error) { return NextResponse.json( { status: 'unhealthy', error: error instanceof Error ? error.message : 'Health check failed', timestamp: new Date().toISOString(), }, { status: 503 } ); } }railway.toml (1)
6-7: Consider reducing the health check timeout.The healthcheckTimeout is set to 300 seconds (5 minutes), which is significantly longer than typical health check timeouts (10-30 seconds). While this provides more tolerance for slow startups, it also delays the detection of genuinely unhealthy deployments.
Consider reducing this to 30-60 seconds unless you have a specific reason for the extended timeout (e.g., cold starts with large database migrations).
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
Dockerfilerailway.tomlsrc/app/api/health/route.ts
🧰 Additional context used
🪛 GitHub Actions: PR Checks and Comments
src/app/api/health/route.ts
[error] 2-2: Module not found: Can't resolve '@/lib/db/client'
🔇 Additional comments (5)
src/app/api/health/route.ts (2)
17-17: The database client API is correct. The codebase uses Drizzle ORM with the postgres.js adapter, and the code properly implementsawait db.execute(sql\SELECT 1`)with thesqltemplate tag imported fromdrizzle-orm`, which is the correct API for this configuration.Likely an incorrect or invalid review comment.
2-2: Correct import path already in place.The health endpoint correctly imports from
@/lib/db(line 3), not@/lib/db/client. The import resolves properly tosrc/lib/db/index.ts, which exports the drizzle database instance. Thedb.execute()call withsqltemplate literal is the correct Drizzle ORM API for executing raw queries.Consider improving type safety by replacing
health: any(line 8) with a proper type definition instead ofany.railway.toml (3)
5-5: Verify that pnpm commands are available at deploy time.The preDeployCommand runs
pnpm run generate && pnpm run push, which are Drizzle ORM commands for generating migrations and pushing schema changes. These scripts are correctly defined in package.json and the drizzle.config.ts is properly configured to use the DATABASE_URL environment variable with validation.For deployment, ensure:
- Database connectivity is available (Railway's PostgreSQL plugin will auto-inject DATABASE_URL)
- This auto-migration approach aligns with your production safety strategy
13-13: No action needed.RAILWAY_STATIC_URLalready includes the HTTPS protocol (https://<service-name>.up.railway.app), soNEXTAUTH_URL = "${RAILWAY_STATIC_URL}"expands to a fully qualified URL as required by NextAuth.Likely an incorrect or invalid review comment.
25-27: Verify the Railway PostgreSQL plugin configuration syntax.The web search indicates Railway typically provisions PostgreSQL through the dashboard or CLI (e.g.,
railway add plugin postgresql@17), but the available documentation doesn't show plugin configuration within railway.toml. Confirm whether[[services.web.plugins]]is the correct syntax for declaring plugins in railway.toml's Config-as-Code schema, as this differs from the typical provisioning approach.
| # Copy source files needed for drizzle-kit generate and push | ||
| COPY --from=builder --chown=nextjs:nodejs /app/src ./src | ||
| # Copy TypeScript config for drizzle | ||
| COPY --from=builder --chown=nextjs:nodejs /app/tsconfig.json ./tsconfig.json | ||
| # Copy all node_modules to run drizzle-kit commands | ||
| COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules |
There was a problem hiding this comment.
Consider the security and performance implications of copying entire node_modules to production.
Copying the complete node_modules directory (including dev dependencies like drizzle-kit) to the production image significantly increases the image size and attack surface. While this supports Railway's preDeployCommand execution, it's not aligned with production image best practices.
Alternative approaches:
- Run migrations during the builder stage after
pnpm run buildand before copying to the runner stage - Use a separate Railway service or init container specifically for running migrations
- Use Railway's build hooks to run migrations in the build phase rather than deploy phase
If you must run drizzle commands at deploy time, consider using a multi-stage approach where drizzle-kit and its dependencies are copied separately and only the minimal required subset is included.
🔍 Script to measure image size impact
#!/bin/bash
# Description: Compare the size of node_modules vs the standalone Next.js output
# to quantify the image size increase
echo "=== Checking node_modules size ==="
if [ -d "node_modules" ]; then
du -sh node_modules
else
echo "node_modules not found in current directory"
fi
echo ""
echo "=== Checking if drizzle-kit is in dependencies vs devDependencies ==="
if [ -f "package.json" ]; then
cat package.json | jq '{dependencies: .dependencies | keys | map(select(. | contains("drizzle"))), devDependencies: .devDependencies | keys | map(select(. | contains("drizzle")))}'
else
echo "package.json not found"
fi🤖 Prompt for AI Agents
In @Dockerfile around lines 101 - 106, The Dockerfile currently copies the
entire node_modules via the COPY --from=builder --chown=nextjs:nodejs
/app/node_modules ./node_modules line which balloons image size and includes dev
deps like drizzle-kit; instead run drizzle migrations during the builder stage
(after pnpm run build) so you can remove that COPY, or introduce a small
dedicated stage that installs/contains only drizzle-kit and its runtime deps and
invoke drizzle-kit there (or use Railway build hooks/init container) so the
final runner stage only receives the Next.js output and production deps; update
the Dockerfile to stop copying /app/node_modules into the runner and either (a)
move migration commands to the builder stage, or (b) add a minimal multi-stage
copy that only transfers the specific drizzle-kit artifacts rather than the
whole node_modules.
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In @.github/workflows/release.yml:
- Around line 91-109: The workflow is missing the build-arg for
NEXT_PUBLIC_DISABLE_SIGNUP used by the Dockerfile (ARG
NEXT_PUBLIC_DISABLE_SIGNUP and later set as an ENV), so add a build-arg entry
named NEXT_PUBLIC_DISABLE_SIGNUP to the build-args block in the release workflow
and set it to the intended value (e.g., "true" or "false") so Next.js
client-side code is correctly embedded at build time; ensure you keep other
NEXT_PUBLIC_* variables as build-args per the Dockerfile requirements.
- Line 81: The workflow uses a branch-based condition on the `latest` tag line
("type=raw,value=latest,enable={{is_default_branch}}") but this job is triggered
by tag pushes so `is_default_branch` will be false and `latest` never applied;
fix by removing the `enable` condition to always emit the `latest` tag, or
replace it with a tag-aware condition such as checking `github.ref_name` (e.g.,
enable when the tag does not contain a hyphen to skip prereleases) so the
`latest` tag is applied only for non-prerelease tags.
- Around line 16-31: The workflow hardcodes sensitive environment variables
(DATABASE_URL, NEXTAUTH_SECRET, CLIENT_AUTH_SECRET, RESEND_API_KEY,
XENDIT_SECRET_KEY, MINIO_* and others) directly in the release.yml and passes
them as Docker build-args; remove these literal values and replace them with
references to GitHub Actions secrets (e.g., use secrets.DATABASE_URL,
secrets.NEXTAUTH_SECRET, etc.) and stop sending them as Docker build-args—pass
only non-sensitive build-time flags and inject secrets at runtime or via GitHub
Secrets with docker run or container runtime environment; ensure Next.js build
steps do not require connecting to the database during build and any build-time
placeholders use safe defaults or env var fallbacks that are supplied from
secrets at runtime.
- Line 158: Update the release step that currently uses "uses:
softprops/action-gh-release@v1" to the v2 action and required config: change the
uses line to "softprops/action-gh-release@v2", remove the "env: GITHUB_TOKEN"
block entirely (v2 expects the token via input), and add "permissions: contents:
write" to the job; also set the action's token input (e.g., add a with: token:
${{ github.token }} under the step if you want to be explicit) so the new action
has the required permission to create/update releases.
In @Dockerfile:
- Around line 105-113: Remove the unnecessary COPY steps that bloat the
production image: delete the COPY of /app/src, /app/tsconfig.json,
/app/node_modules, and /app/pnpm-lock.yaml from the Dockerfile so only the
required runtime artifacts remain; keep the existing COPY lines for the
migrations folder, drizzle.config.ts, and package.json, and if migrations need
to be applied in production move that work to an init container or separate
migration job rather than copying dev/build files into the app image.
🧹 Nitpick comments (2)
Dockerfile (2)
116-116: Redundant directory creation.The
mkdir -p /appis redundant sinceWORKDIR /appat line 84 already creates the directory if it doesn't exist. Thechowncommand is still useful, but you can simplify this to:-RUN mkdir -p /app && chown -R nextjs:nodejs /app +RUN chown -R nextjs:nodejs /app
80-82: Global package installation for Railway pre-deploy commands increases production image size.The
pnpm(via corepack) andtsxinstallation at lines 80-82 is confirmed to be exclusively for Railway's pre-deploy hook (preDeployCommand = "pnpm run generate && pnpm run push"), which runs drizzle-kit database commands.While necessary for these migrations, this approach—combined with copying the entire
node_modules(line 110),src(line 106), and related files—significantly increases the production image size and partially negates the benefits of using Next.js standalone output.Consider:
- Reducing
node_modulessize using tools likenode-pruneafter copying but before the final layer- Running migrations in a separate Railway job/service that doesn't carry the full application runtime
- Documenting this trade-off if maintaining the current approach
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.github/workflows/release.ymlDockerfilesrc/app/api/health/route.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/app/api/health/route.ts
🧰 Additional context used
🪛 actionlint (1.7.10)
.github/workflows/release.yml
158-158: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🪛 Checkov (3.2.334)
.github/workflows/release.yml
[medium] 16-17: Basic Auth Credentials
(CKV_SECRET_4)
🪛 Gitleaks (8.30.0)
.github/workflows/release.yml
[high] 20-20: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
[high] 96-96: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
⏰ 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). (1)
- GitHub Check: test-and-build
| DATABASE_URL: "postgresql://user:password@localhost:5432/summit_build" | ||
| NEXTAUTH_SECRET: "build-time-secret-key-for-github-actions-only" | ||
| NEXTAUTH_URL: "http://localhost:3000" | ||
| CLIENT_AUTH_SECRET: "build-time-client-secret" | ||
| RESEND_API_KEY: "re_123456789" | ||
| SEND_FROM_EMAIL: "no-reply@example.com" | ||
| RESEND_FROM_NAME: "Summit" | ||
| XENDIT_SECRET_KEY: "xnd_development_fake" | ||
| XENDIT_CALLBACK_VERIFICATION_TOKEN: "fake-token" | ||
| CRON_API_KEY: "fake-cron-key" | ||
| MINIO_ENDPOINT: "localhost" | ||
| MINIO_PORT: "9000" | ||
| MINIO_USE_SSL: "false" | ||
| MINIO_ACCESS_KEY: "minioadmin" | ||
| MINIO_SECRET_KEY: "minioadmin" | ||
| MINIO_BUCKET_NAME: "summit" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd /tmp && find . -name "release.yml" -o -name "release.yaml" 2>/dev/null | head -5Repository: kugie-app/summit
Length of output: 42
🏁 Script executed:
git ls-files ".github/workflows/release.yml"Repository: kugie-app/summit
Length of output: 89
🏁 Script executed:
cat -n .github/workflows/release.yml | head -150Repository: kugie-app/summit
Length of output: 5685
🌐 Web query:
Next.js build phase environment variables database connectivity requirements
💡 Result:
Short answer
- NEXT_PUBLIC-prefixed env vars are inlined at next build and become part of client JS; non-prefixed vars are server-only. [1][2]
- If you open DB connections during the build (getStaticProps, getStaticPaths, or other build-time scripts), the build environment must have network access to the DB and the appropriate secrets set (DATABASE_URL, etc.). That means credentials must be present in the build environment and the DB must accept connections from the build host. [1][3]
- If you instead connect at runtime (API routes, getServerSideProps, server components, serverless functions), the DB connectivity and secrets are required at runtime, not at build. This is the recommended approach for secrets and for avoiding build-time DB dependency. [2][3]
Practical implications / checklist
- Decide build-time vs runtime data:
- Build-time (SSG, getStaticProps/getStaticPaths, pre-rendering): build must have DB credentials and network access. Builds are one-off and will freeze any NEXT_PUBLIC inlined values. [1][3]
- Runtime (SSR, API routes, server components): connect from deployed runtime; secrets kept out of client bundles. [2][3]
- Never prefix secrets with NEXT_PUBLIC (that exposes them to clients). Use non-prefixed env vars or provider secrets. [1][2]
- For CI/CD / hosting (Vercel, Docker, etc.): set environment variables in the build environment (or use provider secret settings) and ensure the build runner’s IP or VPC can reach the database if you perform build-time queries. [3]
- If you need runtime-configurable values for client code (not inlined at build), expose them via server endpoints rather than NEXT_PUBLIC. [2]
Sources
- Next.js — Environment Variables (docs on .env, NEXT_PUBLIC and inlining at build time). [Next.js docs]. [1]
- Next.js — App/Pages guidance about runtime vs build-time environment variables and exposing only NEXT_PUBLIC to client. [Next.js docs]. [2]
- Next.js — Notes about build-time inlining and consequences for promoting built artifacts / single Docker image across environments; build host must have env values and DB access if connecting during build. [Next.js docs]. [3]
Would you like a short example showing a safe runtime DB connection pattern (API route / server component) and how to supply env vars in common hosts (Vercel, Docker, CI)?
Avoid hardcoded credentials in CI/CD environment configuration.
While these appear to be dummy values for build-time testing, hardcoding any credentials (even fake ones) in the workflow file is poor practice:
- Visible in workflow logs — Anyone with access to the repo's Actions tab can see these values
- Sets a bad precedent — Makes it easier to accidentally hardcode real secrets later
- Violates least-privilege principle — Credentials should be injected via GitHub Secrets instead
Note: The more critical concern is lines 91–109, where the same credentials are passed as Docker build-args. Build-args are baked into the Docker image history and visible to anyone inspecting image layers—use secrets or runtime configuration for sensitive values instead.
Consider using GitHub Secrets for all credentials, even test ones, and ensure your Next.js build doesn't require database connectivity (database connections should happen at runtime via API routes or server components, not during build).
🧰 Tools
🪛 Checkov (3.2.334)
[medium] 16-17: Basic Auth Credentials
(CKV_SECRET_4)
🪛 Gitleaks (8.30.0)
[high] 20-20: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 Prompt for AI Agents
In @.github/workflows/release.yml around lines 16 - 31, The workflow hardcodes
sensitive environment variables (DATABASE_URL, NEXTAUTH_SECRET,
CLIENT_AUTH_SECRET, RESEND_API_KEY, XENDIT_SECRET_KEY, MINIO_* and others)
directly in the release.yml and passes them as Docker build-args; remove these
literal values and replace them with references to GitHub Actions secrets (e.g.,
use secrets.DATABASE_URL, secrets.NEXTAUTH_SECRET, etc.) and stop sending them
as Docker build-args—pass only non-sensitive build-time flags and inject secrets
at runtime or via GitHub Secrets with docker run or container runtime
environment; ensure Next.js build steps do not require connecting to the
database during build and any build-time placeholders use safe defaults or env
var fallbacks that are supplied from secrets at runtime.
| type=semver,pattern={{version}} | ||
| type=semver,pattern={{major}}.{{minor}} | ||
| type=semver,pattern={{major}} | ||
| type=raw,value=latest,enable={{is_default_branch}} |
There was a problem hiding this comment.
The "latest" tag condition won't work correctly.
Line 81 uses enable={{is_default_branch}} to conditionally apply the latest tag. However, this workflow triggers on tag pushes (line 5-6), not branch pushes. When triggered by a tag, is_default_branch will be false, so the latest tag will never be applied.
Solutions:
- Remove the condition entirely if you want every release to update
latest - Use a condition based on whether the tag is a pre-release:
enable=${{ !contains(github.ref_name, '-') }} - Manually control latest tagging based on your release strategy
💡 Proposed fix
tags: |
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- type=raw,value=latest,enable={{is_default_branch}}
+ type=raw,value=latest,enable=${{ !contains(github.ref_name, '-') }}This will apply latest only to non-prerelease versions (e.g., v1.0.0 but not v1.0.0-beta).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| type=raw,value=latest,enable={{is_default_branch}} | |
| tags: | | |
| type=ref,event=tag | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=raw,value=latest,enable=${{ !contains(github.ref_name, '-') }} |
🤖 Prompt for AI Agents
In @.github/workflows/release.yml at line 81, The workflow uses a branch-based
condition on the `latest` tag line
("type=raw,value=latest,enable={{is_default_branch}}") but this job is triggered
by tag pushes so `is_default_branch` will be false and `latest` never applied;
fix by removing the `enable` condition to always emit the `latest` tag, or
replace it with a tag-aware condition such as checking `github.ref_name` (e.g.,
enable when the tag does not contain a hyphen to skip prereleases) so the
`latest` tag is applied only for non-prerelease tags.
| build-args: | | ||
| DATABASE_URL=postgresql://user:password@localhost:5432/summit_build | ||
| NEXTAUTH_SECRET=build-time-secret-key-for-github-actions-only | ||
| NEXTAUTH_URL=http://localhost:3000 | ||
| CLIENT_AUTH_SECRET=build-time-client-secret | ||
| RESEND_API_KEY=re_123456789 | ||
| SEND_FROM_EMAIL=no-reply@example.com | ||
| RESEND_FROM_NAME=Summit | ||
| XENDIT_SECRET_KEY=xnd_development_fake | ||
| XENDIT_CALLBACK_VERIFICATION_TOKEN=fake-token | ||
| CRON_API_KEY=fake-cron-key | ||
| MINIO_ENDPOINT=localhost | ||
| MINIO_PORT=9000 | ||
| MINIO_USE_SSL=false | ||
| MINIO_ACCESS_KEY=minioadmin | ||
| MINIO_SECRET_KEY=minioadmin | ||
| MINIO_BUCKET_NAME=summit | ||
| NEXT_PUBLIC_URL=http://localhost:3000 | ||
| NEXT_TELEMETRY_DISABLED=1 |
There was a problem hiding this comment.
Missing NEXT_PUBLIC_DISABLE_SIGNUP build-arg.
The Dockerfile declares ARG NEXT_PUBLIC_DISABLE_SIGNUP (line 41) and sets it as an environment variable (line 60), but this workflow doesn't provide it in the build-args. This could result in the variable being undefined in the built image.
✅ Proposed fix
build-args: |
DATABASE_URL=postgresql://user:password@localhost:5432/summit_build
NEXTAUTH_SECRET=build-time-secret-key-for-github-actions-only
NEXTAUTH_URL=http://localhost:3000
CLIENT_AUTH_SECRET=build-time-client-secret
RESEND_API_KEY=re_123456789
SEND_FROM_EMAIL=no-reply@example.com
RESEND_FROM_NAME=Summit
XENDIT_SECRET_KEY=xnd_development_fake
XENDIT_CALLBACK_VERIFICATION_TOKEN=fake-token
CRON_API_KEY=fake-cron-key
MINIO_ENDPOINT=localhost
MINIO_PORT=9000
MINIO_USE_SSL=false
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
MINIO_BUCKET_NAME=summit
NEXT_PUBLIC_URL=http://localhost:3000
NEXT_TELEMETRY_DISABLED=1
+ NEXT_PUBLIC_DISABLE_SIGNUP=falseNote: However, if you implement the fix from my previous comment to remove sensitive build-args, you should keep NEXT_PUBLIC_* variables as they need to be embedded at build time for Next.js client-side code.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| build-args: | | |
| DATABASE_URL=postgresql://user:password@localhost:5432/summit_build | |
| NEXTAUTH_SECRET=build-time-secret-key-for-github-actions-only | |
| NEXTAUTH_URL=http://localhost:3000 | |
| CLIENT_AUTH_SECRET=build-time-client-secret | |
| RESEND_API_KEY=re_123456789 | |
| SEND_FROM_EMAIL=no-reply@example.com | |
| RESEND_FROM_NAME=Summit | |
| XENDIT_SECRET_KEY=xnd_development_fake | |
| XENDIT_CALLBACK_VERIFICATION_TOKEN=fake-token | |
| CRON_API_KEY=fake-cron-key | |
| MINIO_ENDPOINT=localhost | |
| MINIO_PORT=9000 | |
| MINIO_USE_SSL=false | |
| MINIO_ACCESS_KEY=minioadmin | |
| MINIO_SECRET_KEY=minioadmin | |
| MINIO_BUCKET_NAME=summit | |
| NEXT_PUBLIC_URL=http://localhost:3000 | |
| NEXT_TELEMETRY_DISABLED=1 | |
| build-args: | | |
| DATABASE_URL=postgresql://user:password@localhost:5432/summit_build | |
| NEXTAUTH_SECRET=build-time-secret-key-for-github-actions-only | |
| NEXTAUTH_URL=http://localhost:3000 | |
| CLIENT_AUTH_SECRET=build-time-client-secret | |
| RESEND_API_KEY=re_123456789 | |
| SEND_FROM_EMAIL=no-reply@example.com | |
| RESEND_FROM_NAME=Summit | |
| XENDIT_SECRET_KEY=xnd_development_fake | |
| XENDIT_CALLBACK_VERIFICATION_TOKEN=fake-token | |
| CRON_API_KEY=fake-cron-key | |
| MINIO_ENDPOINT=localhost | |
| MINIO_PORT=9000 | |
| MINIO_USE_SSL=false | |
| MINIO_ACCESS_KEY=minioadmin | |
| MINIO_SECRET_KEY=minioadmin | |
| MINIO_BUCKET_NAME=summit | |
| NEXT_PUBLIC_URL=http://localhost:3000 | |
| NEXT_TELEMETRY_DISABLED=1 | |
| NEXT_PUBLIC_DISABLE_SIGNUP=false |
🧰 Tools
🪛 Gitleaks (8.30.0)
[high] 96-96: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 Prompt for AI Agents
In @.github/workflows/release.yml around lines 91 - 109, The workflow is missing
the build-arg for NEXT_PUBLIC_DISABLE_SIGNUP used by the Dockerfile (ARG
NEXT_PUBLIC_DISABLE_SIGNUP and later set as an ENV), so add a build-arg entry
named NEXT_PUBLIC_DISABLE_SIGNUP to the build-args block in the release workflow
and set it to the intended value (e.g., "true" or "false") so Next.js
client-side code is correctly embedded at build time; ensure you keep other
NEXT_PUBLIC_* variables as build-args per the Dockerfile requirements.
Critical: Build-args expose sensitive data in Docker image history.
Docker build-args are baked into the image metadata and can be inspected by anyone who has access to the image using docker history. Even though these appear to be dummy values, this pattern is problematic because:
- Build-args are NOT secret - they're visible in image layers
- Anyone pulling the image can see them - use
docker history kugieapp/summit:latest - This creates a security vulnerability if real credentials are accidentally used
Solutions:
- Runtime environment variables should be provided at deployment time (Railway environment variables), not baked into the image
- Next.js builds don't need actual credentials - only
NEXT_PUBLIC_*variables need to be embedded - Remove all non-
NEXT_PUBLIC_*build-args and provide them at runtime instead
🔒 Recommended fix: Remove sensitive build-args
build-args: |
- DATABASE_URL=postgresql://user:password@localhost:5432/summit_build
- NEXTAUTH_SECRET=build-time-secret-key-for-github-actions-only
- NEXTAUTH_URL=http://localhost:3000
- CLIENT_AUTH_SECRET=build-time-client-secret
- RESEND_API_KEY=re_123456789
- SEND_FROM_EMAIL=no-reply@example.com
- RESEND_FROM_NAME=Summit
- XENDIT_SECRET_KEY=xnd_development_fake
- XENDIT_CALLBACK_VERIFICATION_TOKEN=fake-token
- CRON_API_KEY=fake-cron-key
- MINIO_ENDPOINT=localhost
- MINIO_PORT=9000
- MINIO_USE_SSL=false
- MINIO_ACCESS_KEY=minioadmin
- MINIO_SECRET_KEY=minioadmin
- MINIO_BUCKET_NAME=summit
NEXT_PUBLIC_URL=http://localhost:3000
NEXT_TELEMETRY_DISABLED=1
+ NEXT_PUBLIC_DISABLE_SIGNUP=${{ secrets.NEXT_PUBLIC_DISABLE_SIGNUP || 'false' }}Then provide all runtime secrets via Railway environment variables at deployment time.
🧰 Tools
🪛 Gitleaks (8.30.0)
[high] 96-96: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
| echo "See the [README](README.md) for required environment variables." >> CHANGELOG.md | ||
|
|
||
| - name: Create Release | ||
| uses: softprops/action-gh-release@v1 |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
softprops/action-gh-release v2 breaking changes GITHUB_TOKEN
💡 Result:
Short answer: v2 of softprops/action-gh-release requires the GitHub token to have contents: write permission to create/update releases and upload assets; and because events/actions performed with the repository GITHUB_TOKEN do NOT trigger other workflows (and have repo-scoped, time-limited permissions), you may need to supply a personal access token (PAT) via the action's token input when you need cross-repo operations or to have the release event trigger other workflows. [1][2]
What to change
- Ensure job/workflow permissions include:
permissions:
contents: write - If you need the release to trigger other workflows or operate on a different repo, pass a PAT (stored in secrets) to the action:
with:
token: ${{ secrets.MY_PAT }}
(the action defaults token to ${{ github.token }} if not supplied). [1][2]
Sources
- softprops/action-gh-release README (inputs, token, permissions). [1]
- GitHub docs: GITHUB_TOKEN behavior and when it will not trigger workflows. [2]
🌐 Web query:
softprops/action-gh-release latest version 2024 2025
💡 Result:
The latest release is v2.5.0 — published on December 1, 2025. [1][2]
Sources:
- GitHub Releases: softprops/action-gh-release v2.5.0. [1]
- GitHub Marketplace listing for action-gh-release (usage shows @v2). [2]
Update to latest action version and add required permissions.
softprops/action-gh-release@v2 is the current version (v2.5.0 as of December 2025). Upgrading requires two changes:
- Remove the
env: GITHUB_TOKENblock (v2 uses thetokeninput parameter instead, defaulting to${{ github.token }}) - Add
permissions: contents: writeto the job (v2 requires explicit permission to create/update releases)
📦 Proposed fix
+ permissions:
+ contents: write
- name: Create Release
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
body_path: CHANGELOG.md
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| uses: softprops/action-gh-release@v1 | |
| permissions: | |
| contents: write | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| body_path: CHANGELOG.md | |
| draft: false | |
| prerelease: ${{ contains(github.ref_name, '-') }} |
🧰 Tools
🪛 actionlint (1.7.10)
158-158: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🤖 Prompt for AI Agents
In @.github/workflows/release.yml at line 158, Update the release step that
currently uses "uses: softprops/action-gh-release@v1" to the v2 action and
required config: change the uses line to "softprops/action-gh-release@v2",
remove the "env: GITHUB_TOKEN" block entirely (v2 expects the token via input),
and add "permissions: contents: write" to the job; also set the action's token
input (e.g., add a with: token: ${{ github.token }} under the step if you want
to be explicit) so the new action has the required permission to create/update
releases.
| # Copy source files needed for drizzle-kit generate and push | ||
| COPY --from=builder --chown=nextjs:nodejs /app/src ./src | ||
| # Copy TypeScript config for drizzle | ||
| COPY --from=builder --chown=nextjs:nodejs /app/tsconfig.json ./tsconfig.json | ||
| # Copy all node_modules to run drizzle-kit commands | ||
| COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules | ||
|
|
||
| # Copy pnpm lock file for pnpm commands | ||
| COPY --from=builder --chown=nextjs:nodejs /app/pnpm-lock.yaml ./pnpm-lock.yaml |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and read the Dockerfile to see the full context
fd -t f "Dockerfile" -x wc -l {}Repository: kugie-app/summit
Length of output: 76
🏁 Script executed:
# Read the Dockerfile, focusing on lines around 97-113
fd -t f "Dockerfile" -x cat -n {} | sed -n '85,125p'Repository: kugie-app/summit
Length of output: 1963
🏁 Script executed:
# Find and examine package.json to check drizzle dependencies and scripts
fd -t f "package.json" -x cat {}Repository: kugie-app/summit
Length of output: 2678
🏁 Script executed:
# Check the remaining lines of the Dockerfile
fd -t f "Dockerfile" -x cat -n {} | sed -n '115,126p'Repository: kugie-app/summit
Length of output: 455
🏁 Script executed:
# Also check if there are any startup/entrypoint scripts in src or root
fd -t f -e "sh" -e "js" -e "ts" | grep -E "(startup|entrypoint|start\.sh|migrate)" | head -20Repository: kugie-app/summit
Length of output: 42
🏁 Script executed:
# Check what's in the src directory to understand the structure
fd -t d "src" | head -5Repository: kugie-app/summit
Length of output: 64
🏁 Script executed:
# Look for docker-compose or other orchestration to see how the container runs
fd -t f "docker-compose" -o -name "docker-compose.yml" -o -name "docker-compose.yaml"Repository: kugie-app/summit
Length of output: 290
🏁 Script executed:
# Check if there's a CMD defined later or in a different file
fd -t f "Dockerfile" -x tail -20 {}Repository: kugie-app/summit
Length of output: 722
🏁 Script executed:
# Look for any docker-related files
fd -t f "docker" | head -20Repository: kugie-app/summit
Length of output: 90
🏁 Script executed:
# Check if there's a .dockerignore or other config
fd -t f -e "dockerignore" -o -name "*docker*"Repository: kugie-app/summit
Length of output: 290
🏁 Script executed:
# Check if there are migration files in src/lib/db/migrations
fd -t f "migrations" -x ls -la {}Repository: kugie-app/summit
Length of output: 42
🏁 Script executed:
# Check docker-compose to see how the container is used
cat docker-compose.yamlRepository: kugie-app/summit
Length of output: 2867
🏁 Script executed:
# Check if there's a .next/standalone to see what it contains
fd -t f "next.config" -o -name "next.config.js" -o -name "next.config.ts"Repository: kugie-app/summit
Length of output: 290
🏁 Script executed:
# Let's check the size of key directories to quantify the concern
fd -t f "package.json" -x wc -l {}Repository: kugie-app/summit
Length of output: 77
🏁 Script executed:
# Check what drizzle-kit actually needs - look for any docs or usage in the codebase
rg "drizzle-kit" --type md --type js --type ts -A 2 -B 2Repository: kugie-app/summit
Length of output: 493
🏁 Script executed:
# Check what's in the src directory
fd -t f "src" | head -20Repository: kugie-app/summit
Length of output: 42
🏁 Script executed:
# Let's check the drizzle.config.ts to understand what it needs
cat drizzle.config.tsRepository: kugie-app/summit
Length of output: 667
🏁 Script executed:
# Verify if there's an actual entrypoint script or migration runner
rg "drizzle-kit|migrate" --type sh --type js --type tsRepository: kugie-app/summit
Length of output: 121
Remove unnecessary production files that bloat the image and negate standalone mode benefits.
The production container only executes node server.js (Next.js server), yet copies:
- Entire
srcdirectory (already bundled in standalone output) - Full
node_modules(can be hundreds of MBs) tsconfig.json(unnecessary at runtime)pnpm-lock.yaml(unnecessary in production)
While drizzle-kit scripts exist for migrations, they don't run automatically at startup—they must be invoked manually. This setup wastes image space for optional manual operations.
Recommended fixes:
- Remove lines 106, 108, 110, 113 (entire src, tsconfig.json, node_modules, pnpm-lock.yaml)
- Keep only: migrations folder (line 101), drizzle.config.ts (line 102), package.json (line 103)
- If migrations must run in production, use an init container or separate migration job that runs before the app starts
🤖 Prompt for AI Agents
In @Dockerfile around lines 105 - 113, Remove the unnecessary COPY steps that
bloat the production image: delete the COPY of /app/src, /app/tsconfig.json,
/app/node_modules, and /app/pnpm-lock.yaml from the Dockerfile so only the
required runtime artifacts remain; keep the existing COPY lines for the
migrations folder, drizzle.config.ts, and package.json, and if migrations need
to be applied in production move that work to an init container or separate
migration job rather than copying dev/build files into the app image.
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.