name: Build and deploy on: pull_request: push: branches: [main] workflow_dispatch: jobs: validate: name: Validate source runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Run fast regression checks run: | python3 -m py_compile local/*.py deploy/*.py python3 -m unittest \ local.test_dependency_lock \ local.test_staging_readiness \ deploy.test_production_preflight \ local.test_pricing \ local.test_secrets -v sh -n local/lock_dependencies.sh integration: name: Integration suite on a real stack needs: validate runs-on: ubuntu-latest timeout-minutes: 45 env: SITE_PORT: "8080" KANBAN_PORT: "8081" API_PORT: "8000" COMPOSE: docker compose -f compose.local.yaml steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # py_compile cannot see an unresolved name, and the four unit tests above # never start the application. A missing import in local/auth.py therefore # reached production and returned 500 on every session, login and # registration. These suites exercise the running stack and would have # failed on it immediately. - name: Start the stack run: | $COMPOSE up --build -d --wait --wait-timeout 600 $COMPOSE ps - name: API and workflow regressions run: | python3 -m local.smoke_test python3 -m local.workflow_test python3 -m local.security_test python3 -m local.scanning_test - name: Runtime and retention regressions run: | $COMPOSE exec -T api python -m local.retention_test $COMPOSE exec -T api python -m local.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. - 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 echo "Using $CHROME_BIN" node local/artwork_browser_test.mjs node local/browser_test.mjs - name: Diagnostics on failure if: failure() run: | $COMPOSE ps || true $COMPOSE logs --tail 200 api worker site kanban || true - name: Tear down if: always() run: $COMPOSE down -v || true publish-and-deploy: name: Publish images and notify Portainer needs: [validate, integration] if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main' runs-on: ubuntu-latest timeout-minutes: 45 steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Sign in to the Gitea Container Registry env: REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} run: | test -n "$REGISTRY_USERNAME" test -n "$REGISTRY_TOKEN" echo "$REGISTRY_TOKEN" | docker login gitea.blyzer.com.br \ --username "$REGISTRY_USERNAME" --password-stdin - name: Build and publish API run: | image="gitea.blyzer.com.br/blyzer/dtf-api" docker build --pull --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 run: | image="gitea.blyzer.com.br/blyzer/dtf-web" docker build --pull --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 }}" - 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"