ci: two-stage deploy — staging automatic, production behind a manual approval #33
@@ -14,6 +14,11 @@ on:
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
# Only one promotion at a time; never cancel an in-flight promotion.
|
||||
concurrency:
|
||||
group: prod-promotion
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
REGISTRY: privaterepo.sitaru.org
|
||||
BACKEND_IMAGE_NAME: ${{ gitea.repository }}-backend
|
||||
@@ -25,18 +30,37 @@ jobs:
|
||||
name: Promote approved commit to Production
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Resolve target SHA
|
||||
- name: Checkout repository (full history for ancestry check)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Resolve and validate target SHA
|
||||
id: resolve
|
||||
# SECURITY: the dispatch input is untrusted — it reaches the shell
|
||||
# only via env (never spliced into `run:` with ${{ }}) and is only
|
||||
# used as a quoted argument. The resolved value is validated as a
|
||||
# 40-hex sha and required to be an ancestor of main before any
|
||||
# later step interpolates it.
|
||||
env:
|
||||
SHA_INPUT: ${{ gitea.event.inputs.sha }}
|
||||
run: |
|
||||
SHA_INPUT="${{ gitea.event.inputs.sha }}"
|
||||
set -euo pipefail
|
||||
case "$SHA_INPUT" in
|
||||
-*) echo "REFUSED: SHA input may not start with '-'." >&2; exit 1 ;;
|
||||
esac
|
||||
if [ -z "$SHA_INPUT" ]; then
|
||||
SHA_INPUT="${{ gitea.sha }}"
|
||||
SHA_INPUT="$(git rev-parse origin/main)"
|
||||
fi
|
||||
FULL_SHA=$(git rev-parse --verify --quiet "${SHA_INPUT}^{commit}") || {
|
||||
echo "REFUSED: not a commit in this repository." >&2
|
||||
exit 1
|
||||
}
|
||||
echo "$FULL_SHA" | grep -Eq '^[0-9a-f]{40}$'
|
||||
if ! git merge-base --is-ancestor "$FULL_SHA" origin/main; then
|
||||
echo "REFUSED: $FULL_SHA is not on main — only main commits are promotable." >&2
|
||||
exit 1
|
||||
fi
|
||||
# Normalise to the full sha via the API so short inputs work
|
||||
FULL_SHA=$(curl -fsS \
|
||||
-H "Authorization: token ${{ secrets.REGISTRY_TOKEN }}" \
|
||||
"https://${REGISTRY}/api/v1/repos/${{ gitea.repository }}/git/commits/${SHA_INPUT}" \
|
||||
| python3 -c "import json,sys; print(json.load(sys.stdin)['sha'])")
|
||||
SHORT_SHA="sha-$(echo "$FULL_SHA" | cut -c1-7)"
|
||||
echo "full=$FULL_SHA" >> "$GITHUB_OUTPUT"
|
||||
echo "short=$SHORT_SHA" >> "$GITHUB_OUTPUT"
|
||||
|
||||
Reference in New Issue
Block a user