ci: require manual gated releases from main
All checks were successful
Build and deploy / Validate source (push) Successful in 1m28s
Build and deploy / Integration suite on a real stack (push) Successful in 4m3s
Build and deploy / Secret scan and release gate (push) Successful in 11s
Build and deploy / Publish images and notify Portainer (push) Has been skipped
All checks were successful
Build and deploy / Validate source (push) Successful in 1m28s
Build and deploy / Integration suite on a real stack (push) Successful in 4m3s
Build and deploy / Secret scan and release gate (push) Successful in 11s
Build and deploy / Publish images and notify Portainer (push) Has been skipped
This commit is contained in:
@@ -48,6 +48,9 @@ jobs:
|
||||
# by whoever follows them. The suites run inside the network, so it has to
|
||||
# be the service name, not a published port on the host.
|
||||
S3_PUBLIC_ENDPOINT: http://storage:9000
|
||||
PUBLIC_ORIGIN: http://site
|
||||
ALLOWED_HOSTS: localhost,127.0.0.1,site,kanban
|
||||
ALLOWED_ORIGINS: http://site,http://kanban,http://localhost:28080,http://localhost:28081
|
||||
COMPOSE: docker compose -f compose.local.yaml
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -70,7 +73,7 @@ jobs:
|
||||
# one a developer exercises on localhost.
|
||||
- name: API and workflow regressions
|
||||
run: |
|
||||
for suite in smoke_test workflow_test security_test scanning_test payment_test; do
|
||||
for suite in smoke_test workflow_test security_test scanning_test payment_test quote_pagination_test; do
|
||||
echo "--- $suite"
|
||||
$COMPOSE exec -T \
|
||||
-e SITE_BASE_URL=http://site \
|
||||
@@ -83,35 +86,13 @@ jobs:
|
||||
$COMPOSE exec -T api python -m tests.retention_test
|
||||
$COMPOSE exec -T api python -m tests.runtime_security_test
|
||||
|
||||
# These need a real Chrome. They are the only coverage for the artwork
|
||||
# editor and the full customer journey, so install google-chrome-stable
|
||||
# (or set CHROME_BIN) on the runner to make them gate deployments. The
|
||||
# suites above stay hard gates either way.
|
||||
# Run Chrome on the Compose network. It must resolve the same storage:9000
|
||||
# hostname used in presigned URLs, and absence of Chrome must fail CI.
|
||||
- name: Browser regressions
|
||||
run: |
|
||||
for candidate in "$CHROME_BIN" /usr/bin/google-chrome-stable \
|
||||
/usr/bin/google-chrome /usr/bin/chromium /usr/bin/chromium-browser; do
|
||||
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
|
||||
export CHROME_BIN="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ ! -x "${CHROME_BIN:-}" ]; then
|
||||
echo "::warning::No Chrome on this runner; browser regressions were NOT run."
|
||||
echo "Install google-chrome-stable or set CHROME_BIN to gate on them."
|
||||
exit 0
|
||||
fi
|
||||
# Chrome runs here, in the runner container, and reaches the stack only
|
||||
# through ports published on the host. When the runner is itself a
|
||||
# container those are in another namespace, so check before running
|
||||
# rather than failing with a bare connection error. See ROADMAP 5.10.
|
||||
if ! wget -q -T 5 -O /dev/null "http://localhost:${SITE_PORT}/health"; then
|
||||
echo "::warning::Stack not reachable from the runner; browser regressions were NOT run."
|
||||
exit 0
|
||||
fi
|
||||
echo "Using $CHROME_BIN"
|
||||
node tests/artwork_browser_test.mjs
|
||||
node tests/browser_test.mjs
|
||||
$COMPOSE build browser-tests
|
||||
$COMPOSE run --rm --no-deps browser-tests sh -ec \
|
||||
'node tests/artwork_browser_test.mjs && node tests/browser_test.mjs'
|
||||
|
||||
- name: Diagnostics on failure
|
||||
if: failure()
|
||||
@@ -145,13 +126,9 @@ jobs:
|
||||
fs --scanners secret --exit-code 1 --severity HIGH,CRITICAL \
|
||||
--no-progress /src
|
||||
|
||||
# docs/PORTAINER.md described this as blocking publication. It never ran at
|
||||
# all, and turning it on unconditionally would block every deploy: the
|
||||
# source preflight refuses a release while the payment and messaging
|
||||
# adapters are fake, which is the deliberate state the stack runs in
|
||||
# today. So its verdict is always printed, and enforcement is opt-in.
|
||||
# Set the repository variable ENFORCE_PRODUCTION_PREFLIGHT to "true" once
|
||||
# real adapters land, and this becomes the gate the documentation claims.
|
||||
# Keep push feedback advisory while the provider adapters are fake.
|
||||
# The manual release job enforces the source preflight unconditionally.
|
||||
# ENFORCE_PRODUCTION_PREFLIGHT can make push checks fail on blockers too.
|
||||
- name: Production source preflight
|
||||
run: |
|
||||
set +e
|
||||
@@ -171,7 +148,7 @@ jobs:
|
||||
publish-and-deploy:
|
||||
name: Publish images and notify Portainer
|
||||
needs: [validate, integration, scan]
|
||||
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
||||
if: gitea.event_name == 'workflow_dispatch' && gitea.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
@@ -181,6 +158,12 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
- name: Require production readiness
|
||||
env:
|
||||
PORTAINER_WEBHOOK: ${{ secrets.PORTAINER_WEBHOOK }}
|
||||
run: |
|
||||
python3 deploy/production_preflight.py --source-only
|
||||
test -n "$PORTAINER_WEBHOOK"
|
||||
- name: Sign in to the Gitea Container Registry
|
||||
env:
|
||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||
@@ -190,7 +173,7 @@ jobs:
|
||||
test -n "$REGISTRY_TOKEN"
|
||||
echo "$REGISTRY_TOKEN" | docker login gitea.blyzer.com.br \
|
||||
--username "$REGISTRY_USERNAME" --password-stdin
|
||||
- name: Build and publish API
|
||||
- name: Build API
|
||||
run: |
|
||||
image="gitea.blyzer.com.br/blyzer/dtf-api"
|
||||
# The Dockerfiles pin digests themselves; these variables let a base be
|
||||
@@ -201,9 +184,7 @@ jobs:
|
||||
docker build --file deploy/Dockerfile.api "$@" \
|
||||
--build-arg VCS_REF="${{ gitea.sha }}" \
|
||||
--tag "$image:latest" --tag "$image:${{ gitea.sha }}" .
|
||||
docker push "$image:latest"
|
||||
docker push "$image:${{ gitea.sha }}"
|
||||
- name: Build and publish web
|
||||
- name: Build web
|
||||
run: |
|
||||
image="gitea.blyzer.com.br/blyzer/dtf-web"
|
||||
set --
|
||||
@@ -212,8 +193,6 @@ jobs:
|
||||
docker build --file deploy/Dockerfile.web "$@" \
|
||||
--build-arg VCS_REF="${{ gitea.sha }}" \
|
||||
--tag "$image:latest" --tag "$image:${{ gitea.sha }}" .
|
||||
docker push "$image:latest"
|
||||
docker push "$image:${{ gitea.sha }}"
|
||||
# CRITICAL blocks, HIGH is reported. Both images carry zero CRITICAL after
|
||||
# the base pinning and OS upgrades, so this gate holds the line already
|
||||
# reached. The remaining HIGH findings have no upstream fix, so failing on
|
||||
@@ -227,25 +206,29 @@ jobs:
|
||||
"gitea.blyzer.com.br/blyzer/dtf-web:${{ gitea.sha }}"; do
|
||||
echo "--- $target (HIGH, reported)"
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock "$image" \
|
||||
image --scanners vuln --severity HIGH --no-progress \
|
||||
image --image-src docker --scanners vuln --severity HIGH --no-progress \
|
||||
--format table --exit-code 0 "$target" ||
|
||||
echo "::warning::Could not scan $target for HIGH findings"
|
||||
echo "--- $target (CRITICAL, blocking)"
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock "$image" \
|
||||
image --scanners vuln --severity CRITICAL --no-progress \
|
||||
image --image-src docker --scanners vuln --severity CRITICAL --no-progress \
|
||||
--format table --exit-code 1 "$target" || failed=1
|
||||
done
|
||||
if [ "$failed" -ne 0 ]; then
|
||||
echo "::error::A CRITICAL vulnerability was found in a published image."
|
||||
echo "::error::A CRITICAL vulnerability was found in a release image."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Publish validated images
|
||||
run: |
|
||||
for name in dtf-api dtf-web; do
|
||||
image="gitea.blyzer.com.br/blyzer/$name"
|
||||
docker push "$image:${{ gitea.sha }}"
|
||||
docker push "$image:latest"
|
||||
done
|
||||
|
||||
- name: Trigger Portainer redeployment
|
||||
env:
|
||||
PORTAINER_WEBHOOK: ${{ secrets.PORTAINER_WEBHOOK }}
|
||||
run: |
|
||||
if [ -z "$PORTAINER_WEBHOOK" ]; then
|
||||
echo "PORTAINER_WEBHOOK is not configured; images were published but deployment was skipped."
|
||||
exit 0
|
||||
fi
|
||||
curl --fail --silent --show-error --max-time 30 --request POST "$PORTAINER_WEBHOOK"
|
||||
|
||||
Reference in New Issue
Block a user