Repository navigation
fix(ops): repair the tinystudio.in release lane - stop failing every main merge while the Pages token is missing #111
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,13 +9,19 @@ name: Deploy public site | |
| # fixes (H2-after-H1 on /promptly/support/, JSON-LD on /contact/, real 404s, | ||
| # portfolio-only homepage). | ||
| # | ||
| # Required secrets (fail-closed when absent): | ||
| # Required secrets (fail-closed when present): | ||
| # CLOUDFLARE_API_TOKEN - Cloudflare API token with Cloudflare Pages:Edit | ||
| # on the tinystudio.in account | ||
| # CLOUDFLARE_ACCOUNT_ID - f670a698e17bf160c8e4679823e68916 | ||
| # The fleet Workers token (fleet-console/cf.env) does NOT have Pages:Edit; | ||
| # provisioning a Pages-scoped token is a one-time dashboard step. The lane | ||
| # prints the exact steps when the secret is missing or lacks Pages:Edit. | ||
| # provisioning a Pages-scoped token is a one-time dashboard step. | ||
| # | ||
| # While BOTH secrets are absent the lane runs a seconds-long "dormant" | ||
| # notice step that prints the exact provisioning steps and exits green, so | ||
| # main merges are not blocked by the missing credential. The moment both | ||
| # secrets exist, every main merge runs the full publish-and-verify pipeline | ||
| # and any failure fails the run loudly (fail-closed). The nightly | ||
| # live-site-check.yml is the loud staleness alarm while the site is stale. | ||
|
|
||
| on: | ||
| push: | ||
|
|
@@ -33,24 +39,44 @@ concurrency: | |
| jobs: | ||
| publish: | ||
| runs-on: [self-hosted, linux, x64, vps-verify] | ||
| # Secrets are hoisted to the job env because the `secrets` context is not | ||
| # available in step-level `if:` conditions; the `env` context is. | ||
| env: | ||
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | ||
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | ||
|
Comment on lines
+44
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
workflow=.github/workflows/deploy-public-site.yml
# Expect: raw Cloudflare credentials appear only in the publish step's env map.
rg -n -C 3 'CLOUDFLARE_API_TOKEN|CLOUDFLARE_ACCOUNT_ID|HAS_CLOUDFLARE' "$workflow"Repository: nish3451/tinystudio-in Length of output: 3164 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/deploy-public-site.yml
printf '%s\n' '--- Cloudflare credential references ---'
rg -n -C 4 'CLOUDFLARE_API_TOKEN|CLOUDFLARE_ACCOUNT_ID|secrets\.' .github/workflows scripts package.json
printf '%s\n' '--- publish script environment access ---'
rg -n -C 5 'process\.env|CLOUDFLARE|deploy|prepare-only' scripts/publish-public-site.mjsRepository: nish3451/tinystudio-in Length of output: 19418 🌐 Web query:
💡 Result: In GitHub Actions, environment variables (env) and secrets can be used throughout your workflows using specific syntax and scoping rules [1][2][3]. Env Scope and Precedence You can define environment variables at the workflow, job, or step level [2][4]. When the same variable name is defined at multiple levels, the most specific definition overrides the others [2][5]. - Step-level env overrides job-level and workflow-level env [2][5]. - Job-level env overrides workflow-level env [2][5]. - Workflow-level env acts as a default for all jobs [5][4]. Context Syntax - Env context: Use ${{ env.VAR_NAME }} to access environment variables in your workflow expressions (such as if conditions) [1][6]. - Secrets context: Use ${{ secrets.SECRET_NAME }} to access your repository secrets [1][3]. - Expressions: Use ${{... }} syntax to evaluate expressions [7]. In if conditional clauses, the ${{ }} syntax is optional [7]. If Conditions and Secrets Secrets cannot be directly referenced in if conditional statements for security reasons [3]. To use a secret in a conditional: 1. Map the secret to a job-level environment variable [3]. 2. Reference that environment variable in your step's if conditional using the env context [6][3]. Example: jobs: example-job: runs-on: ubuntu-latest env: # Map secret to a job-level env var MY_SECRET_VAR: ${{ secrets.MY_SECRET }} steps: - name: Conditional step # Access the env var via the env context if: ${{ env.MY_SECRET_VAR!= '' }} run: echo "Secret is available" Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
import json
import re
from pathlib import Path
workflow = Path(".github/workflows/deploy-public-site.yml").read_text()
package = json.loads(Path("package.json").read_text())
for name in ("CLOUDFLARE_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID"):
matches = [
(i + 1, line)
for i, line in enumerate(workflow.splitlines())
if name in line and "secrets." in line
]
print(f"{name}:")
for line_no, line in matches:
print(f" line {line_no}: {line.strip()}")
print("\nRelevant package scripts:")
for name, command in package.get("scripts", {}).items():
if name == "check" or "publish-public-site" in command:
print(f" {name}: {command}")
print("\nCurrent workflow commands:")
for i, line in enumerate(workflow.splitlines(), 1):
if re.search(r"\brun:\s*(npm run check|node scripts/publish-public-site\.mjs)", line):
print(f" line {i}: {line.strip()}")
PYRepository: nish3451/tinystudio-in Length of output: 803 Restrict Cloudflare credentials to the publish step. The job-level 🤖 Prompt for AI Agents |
||
| steps: | ||
| - uses: actions/checkout@v7 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Release lane dormant - Pages token not provisioned yet | ||
| if: env.CLOUDFLARE_API_TOKEN == '' || env.CLOUDFLARE_ACCOUNT_ID == '' | ||
| run: | | ||
| echo "::warning title=Release lane dormant::CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID are not set in repo secrets, so the publish step is skipped. The lane will deploy automatically on the next main merge once both secrets are provisioned; until then the live site stays on its current bundle and the nightly live-site-check.yml reports it." | ||
| echo "One-time provisioning (dashboard, ~2 minutes):" | ||
| echo " 1. https://dash.cloudflare.com/profile/api-tokens -> Create Token" | ||
| echo " 2. Use the 'Cloudflare Pages: Edit' template, scope it to the account (account id f670a698e17bf160c8e4679823e68916), create, copy the token." | ||
| echo " 3. gh secret set CLOUDFLARE_API_TOKEN -R nish3451/tinystudio-in" | ||
| echo " 4. gh secret set CLOUDFLARE_ACCOUNT_ID -R nish3451/tinystudio-in -b f670a698e17bf160c8e4679823e68916" | ||
| echo " 5. Re-run this lane (or wait for the next main merge); no code change needed." | ||
|
|
||
| - uses: actions/setup-node@v7 | ||
| if: env.CLOUDFLARE_API_TOKEN != '' && env.CLOUDFLARE_ACCOUNT_ID != '' | ||
| with: | ||
| node-version: 20 | ||
| cache: npm | ||
|
|
||
| - run: npm ci --ignore-scripts | ||
| if: env.CLOUDFLARE_API_TOKEN != '' && env.CLOUDFLARE_ACCOUNT_ID != '' | ||
|
|
||
| - name: Pre-deploy repository gate | ||
| if: env.CLOUDFLARE_API_TOKEN != '' && env.CLOUDFLARE_ACCOUNT_ID != '' | ||
| run: npm run check | ||
|
|
||
| - name: Prepare the filtered deploy bundle | ||
| if: env.CLOUDFLARE_API_TOKEN != '' && env.CLOUDFLARE_ACCOUNT_ID != '' | ||
| run: node scripts/publish-public-site.mjs --prepare-only --output "$RUNNER_TEMP/tinystudio-public-bundle" | ||
|
|
||
| - name: Publish to Cloudflare Pages and verify live | ||
| env: | ||
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | ||
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | ||
| if: env.CLOUDFLARE_API_TOKEN != '' && env.CLOUDFLARE_ACCOUNT_ID != '' | ||
| run: node scripts/publish-public-site.mjs --deploy --bundle "$RUNNER_TEMP/tinystudio-public-bundle" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the credentials are provisioned, this job-level
envexposes the Pages:Edit token to every action and command in the job—including checkout, setup-node, the repository test suite, and bundle preparation—whereas the previous workflow exposed it only to the publisher. Any compromised action or newly merged test code could read the token and use it to replace the production Pages deployment; derive a non-secret readiness output in a narrowly scoped step, then continue passing the actual credentials only to the publish step.Useful? React with 👍 / 👎.