Repository navigation
Add a Coolify deploy workflow, gated on green CI #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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 |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| name: deploy | ||
|
|
||
| # Deploy for the foaf-protocol tiers. Each tier is a Coolify app on the Contabo | ||
| # box (144.126.145.4) tracking this repo's `main`; a deploy asks Coolify to | ||
| # pull latest and rebuild. | ||
| # | ||
| # prod api.foaf.io (manual dispatch, needs confirm=deploy-prod-protocol) | ||
| # demo dpi.foaf.io (auto, after CI goes green on main) | ||
| # beta bpi.foaf.io (manual dispatch) | ||
| # | ||
| # Secret: COOLIFY_API_TOKEN (Coolify API bearer). | ||
| # | ||
| # THIS WORKFLOW IS THE ONLY DEPLOY PATH, AND THAT IS LOAD-BEARING. | ||
| # Coolify has a per-app "auto deploy on git push" setting that is invisible | ||
| # from this repo. It was ON for all three tiers until 2026-09-26, which meant | ||
| # every merge to `main` rebuilt PRODUCTION immediately, with no gate anywhere | ||
| # in the repo and no way to tell from the code. It is now OFF for prod, demo | ||
| # and beta, which is what makes the prod confirm below real rather than | ||
| # decorative. The absence of a deploy workflow was never the absence of a | ||
| # deploy trigger. | ||
| # | ||
| # If prod ever starts deploying on merge again, check that setting first: | ||
| # curl -s https://coolify.rheo.ca/api/v1/applications/<uuid> \ | ||
| # -H "Authorization: Bearer $COOLIFY_API_TOKEN" \ | ||
| # | python3 -c 'import sys,json;print(json.load(sys.stdin)["settings"]["is_auto_deploy_enabled"])' | ||
| # Turn it back off with a PATCH of {"is_auto_deploy_enabled": false}. | ||
| # | ||
| # Talks to the Coolify API directly over HTTPS at coolify.rheo.ca, which is | ||
| # published through Traefik, so no SSH key is needed on the runner. | ||
|
|
||
| on: | ||
| # Demo deploys only after the suite is green on main. A push trigger would | ||
| # deploy red commits, which is the failure CI exists to prevent. | ||
| workflow_run: | ||
| workflows: ["CI"] | ||
| types: [completed] | ||
| branches: [main] | ||
| workflow_dispatch: | ||
| inputs: | ||
| tier: | ||
| description: "Tier to deploy" | ||
| required: true | ||
| type: choice | ||
| options: [demo, beta, prod] | ||
| confirm: | ||
| description: "Type deploy-prod-protocol to allow a prod deploy" | ||
| required: false | ||
| type: string | ||
|
|
||
| concurrency: | ||
| group: deploy-${{ github.event.inputs.tier || 'demo' }} | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| deploy: | ||
| runs-on: ubuntu-latest | ||
| if: >- | ||
| github.event_name == 'workflow_dispatch' || | ||
| github.event.workflow_run.conclusion == 'success' | ||
| steps: | ||
| - name: Resolve tier and guard prod | ||
| id: tier | ||
| run: | | ||
| set -euo pipefail | ||
| TIER="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tier || 'demo' }}" | ||
| if [ "$TIER" = "prod" ] && [ "${{ github.event.inputs.confirm }}" != "deploy-prod-protocol" ]; then | ||
|
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 | 🛡️ Detected with Advanced Tier | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: cat -n .github/workflows/deploy.yml
ls .github/workflows; sed -n '1,30p' .github/workflows/ci.yml
rg -n 'environment|CODEOWNERS|permissions' .github || trueRepository: FOAF-Foundation/foaf-protocol-ruby Length of output: 5948 🌐 Web query:
💡 Result: Injection Reachability: External Pass
A user with write access can already push a branch containing a modified copy of this workflow and dispatch that ref. Same-repository branch runs can receive repository secrets. The remaining risk is the production-confirmation bypass and token exposure when branch or environment protections would otherwise restrict that dispatcher. Apply the environment-variable fix id: tier
+ env:
+ CONFIRM: ${{ github.event.inputs.confirm }}
run: |
set -euo pipefail
TIER="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tier || 'demo' }}"
- if [ "$TIER" = "prod" ] && [ "${{ github.event.inputs.confirm }}" != "deploy-prod-protocol" ]; then
+ if [ "$TIER" = "prod" ] && [ "$CONFIRM" != "deploy-prod-protocol" ]; then🧰 Tools🪛 zizmor (1.30.0)[warning] 55-103: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block (excessive-permissions) [error] 66-66: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) 🤖 Prompt for AI Agents |
||
| echo "::error::Refusing prod deploy without confirm=deploy-prod-protocol" | ||
| exit 1 | ||
| fi | ||
| case "$TIER" in | ||
| prod) UUID="kvnksiwym19qm62iaygggfhs" ;; | ||
| demo) UUID="zu1wmibhfvizact7cwh7dfm6" ;; | ||
| beta) UUID="kwsr62yqupxe4w4u22n0xipp" ;; | ||
| *) echo "::error::Unknown tier $TIER"; exit 1 ;; | ||
| esac | ||
| echo "tier=$TIER" >> "$GITHUB_OUTPUT" | ||
| echo "uuid=$UUID" >> "$GITHUB_OUTPUT" | ||
| echo "Deploying tier=$TIER uuid=$UUID" >> "$GITHUB_STEP_SUMMARY" | ||
|
|
||
| - name: Trigger Coolify deploy and wait | ||
| env: | ||
| CT: ${{ secrets.COOLIFY_API_TOKEN }} | ||
| TIER: ${{ steps.tier.outputs.tier }} | ||
| UUID: ${{ steps.tier.outputs.uuid }} | ||
| run: | | ||
| set -euo pipefail | ||
| api() { curl -sf -m 30 -X "$1" "https://coolify.rheo.ca/api/v1/$2" -H "Authorization: Bearer $CT"; } | ||
| RESP=$(api POST "deploy?uuid=$UUID") | ||
|
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. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Deploy the commit that passed CI. The API request asks Coolify to pull the latest 🧰 Tools🪛 zizmor (1.30.0)[warning] 55-103: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block (excessive-permissions) 🤖 Prompt for AI Agents |
||
| echo "$RESP" | ||
| DEP=$(echo "$RESP" | python3 -c "import sys,json;print(json.load(sys.stdin)['deployments'][0]['deployment_uuid'])") | ||
| echo "deployment=$DEP" | ||
| for i in $(seq 1 90); do | ||
| sleep 10 | ||
| DS=$(api GET "deployments/$DEP" | python3 -c "import sys,json;print(json.load(sys.stdin).get('status','?'))" 2>/dev/null || echo "poll-failed") | ||
| echo "[$i] status=$DS" | ||
| case "$DS" in | ||
| finished) echo "✅ $TIER deployed ($DEP)" >> "$GITHUB_STEP_SUMMARY"; exit 0 ;; | ||
| failed|cancelled*) echo "::error::Deployment $DS — see Coolify deployment $DEP"; exit 1 ;; | ||
| esac | ||
| done | ||
| echo "::error::Timed out after 15 minutes waiting on deployment $DEP" | ||
| exit 1 | ||
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.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Require a push-triggered CI run for automatic demo deployment.
CIalso runs for pull requests. A successful pull-request run whose head branch matchesmaincan satisfy the branch filter and this success check, causing an unintended demo deployment. Requiregithub.event.workflow_run.event == 'push'in this condition. GitHub identifies the triggering event in the workflow-run payload. (docs.github.com)🧰 Tools
🪛 zizmor (1.30.0)
[warning] 55-103: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
🤖 Prompt for AI Agents