ci(web): surface pending manual deploys

Annotate green main builds with the exact manual deployment command while preserving the credential-free, approval-gated Cloudflare boundary.
This commit is contained in:
CodeWhale Bot
2026-08-11 15:59:04 -07:00
parent 2ba40aeab8
commit 9935832d9b
4 changed files with 37 additions and 0 deletions
+19
View File
@@ -52,6 +52,25 @@ jobs:
- name: Build production site
run: npm run build
deploy-reminder:
name: Deployment approval needed
runs-on: ubuntu-latest
needs: lint
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Surface the manual deployment gate
env:
REVISION: ${{ github.sha }}
run: |
echo "::notice title=Web deployment approval needed::Revision ${REVISION} passed the web gates but is not deployed. Dispatch web.yml on main to publish it."
{
echo "## Web deployment approval needed"
echo
echo "Revision \`${REVISION}\` passed the web gates but has **not** been deployed."
echo
echo "A maintainer can publish it with \`gh workflow run web.yml --repo Hmbown/CodeWhale --ref main\`."
} >> "$GITHUB_STEP_SUMMARY"
deploy:
name: Deploy to Cloudflare
runs-on: ubuntu-latest
+3
View File
@@ -18,6 +18,9 @@ runs against Pi 0.8.41 and by dogfooding repeated manual compaction.
- `web_search` defaults to Firecrawl Cloud without an API key; keyless requests
are headerless and quota-bounded, while an optional user key raises limits.
- Green web builds on `main` now emit an actionable manual-deploy reminder, so
site changes cannot quietly appear shipped while Cloudflare still serves an
older revision.
- Mistral AI is a first-class provider route, including Codestral models,
first-party reasoning support, authentication, picker entries, and aliases.
- Headless `Bash` can transfer explicitly requested persistent Unix services
+4
View File
@@ -42,6 +42,10 @@ git rev-parse origin/main
gh workflow run web.yml --repo Hmbown/CodeWhale --ref main
```
Every green push to `main` also emits a `Deployment approval needed` workflow
notice with that command. The reminder does not receive Cloudflare credentials
and cannot deploy; it keeps the manual production approval boundary visible.
The manual job records the pre-deploy source drift, builds the OpenNext bundle,
deploys only after the protected Cloudflare inputs pass, and then requires the
public `/api/facts` receipt to report the exact workflow SHA. A credential-free
+11
View File
@@ -127,6 +127,7 @@ function jobBlock(source: string, job: string) {
describe("web workflow deploy trigger contract", () => {
const workflow = readWebWorkflow();
const deploy = jobBlock(workflow, "deploy");
const deployReminder = jobBlock(workflow, "deploy-reminder");
it("still runs lint on pushes and pull requests", () => {
expect(workflow).toContain(" push:\n branches: [master, main]");
@@ -151,6 +152,16 @@ describe("web workflow deploy trigger contract", () => {
expect(deploy).toContain("needs: lint");
});
it("surfaces an actionable deployment reminder after a green main push", () => {
expect(deployReminder).toContain("needs: lint");
expect(deployReminder).toContain(
"github.event_name == 'push' && github.ref == 'refs/heads/main'",
);
expect(deployReminder).toContain("::notice title=Web deployment approval needed::");
expect(deployReminder).toContain("gh workflow run web.yml");
expect(deployReminder).not.toContain("npm run deploy");
});
it("checks out the exact dispatched revision before deploying", () => {
expect(deploy).toContain("ref: ${{ github.sha }}");
expect(deploy).toContain('--expected-revision "$GITHUB_SHA"');