# Deployments — one workflow for both environments (docs/40-DEPLOYMENT.md §6): # push to Dev → test (https://test-erp.hexdive.com) # push to production → release (https://erp.hexdive.com) # Only the side(s) whose folder changed since the last successful deploy are built and deployed, # backend (and its migrations) always before frontend. Nightly/manual Playwright runs hit test only. name: Deploy on: push: branches: [Dev, production] schedule: - cron: "30 20 * * *" # 02:00 Asia/Colombo. Gitea reads schedules from the default branch (production). workflow_dispatch: inputs: force_backend: description: "Redeploy the backend even if unchanged (e.g. after editing /etc/erpcore/.env)" type: boolean default: false force_frontend: description: "Redeploy the frontend even if unchanged" type: boolean default: false backend_hash: description: "Rollback: backend tree hash from a previous Gitea release (leave empty normally)" type: string default: "" frontend_hash: description: "Rollback: frontend tree hash from a previous Gitea release (leave empty normally)" type: string default: "" run_e2e: description: "Run the Playwright suite against test after this run" type: boolean default: false concurrency: group: deploy-${{ github.ref_name }} cancel-in-progress: false # never interrupt a deploy (or a migration) half-way jobs: detect: name: detect if: github.event_name != 'schedule' runs-on: linux outputs: env: ${{ steps.detect.outputs.env }} backend: ${{ steps.detect.outputs.backend }} frontend: ${{ steps.detect.outputs.frontend }} backend_hash: ${{ steps.detect.outputs.backend_hash }} frontend_hash: ${{ steps.detect.outputs.frontend_hash }} backend_rollback: ${{ steps.detect.outputs.backend_rollback }} frontend_rollback: ${{ steps.detect.outputs.frontend_rollback }} migrations_hash: ${{ steps.detect.outputs.migrations_hash }} migrations_changed: ${{ steps.detect.outputs.migrations_changed }} record_release: ${{ steps.detect.outputs.record_release }} steps: - name: Checkout pushed commit env: ERP_GIT_TOKEN: ${{ secrets.ERPSECRET }} SHA: ${{ github.sha }} REF: ${{ github.ref_name }} run: | basic="$(printf 'oauth2:%s' "$ERP_GIT_TOKEN" | base64 | tr -d '\n')" url="https://gitea.hexdive.com/New_REP_SYSTEM/ERP-core.git" git init -q . git -c "http.extraHeader=Authorization: Basic $basic" fetch -q --depth 1 "$url" "$SHA" \ || git -c "http.extraHeader=Authorization: Basic $basic" fetch -q --depth 50 "$url" "$REF" git checkout -qf "$SHA" && git clean -ffdxq git log -1 --format='checked out %h %s' - name: Decide what to deploy id: detect env: REF_NAME: ${{ github.ref_name }} FORCE_BACKEND: ${{ inputs.force_backend }} FORCE_FRONTEND: ${{ inputs.force_frontend }} BACKEND_HASH_INPUT: ${{ inputs.backend_hash }} FRONTEND_HASH_INPUT: ${{ inputs.frontend_hash }} run: bash deploy/scripts/detect.sh backend: name: backend needs: detect if: needs.detect.outputs.backend == 'true' runs-on: linux outputs: reused: ${{ steps.build.outputs.reused }} steps: - name: Checkout pushed commit env: ERP_GIT_TOKEN: ${{ secrets.ERPSECRET }} SHA: ${{ github.sha }} REF: ${{ github.ref_name }} run: | basic="$(printf 'oauth2:%s' "$ERP_GIT_TOKEN" | base64 | tr -d '\n')" url="https://gitea.hexdive.com/New_REP_SYSTEM/ERP-core.git" git init -q . git -c "http.extraHeader=Authorization: Basic $basic" fetch -q --depth 1 "$url" "$SHA" \ || git -c "http.extraHeader=Authorization: Basic $basic" fetch -q --depth 50 "$url" "$REF" git checkout -qf "$SHA" && git clean -ffdxq - name: Build (or reuse) release id: build env: BACKEND_HASH: ${{ needs.detect.outputs.backend_hash }} run: bash deploy/scripts/build-backend.sh - name: Backup, switch, health check env: ERP_ENV: ${{ needs.detect.outputs.env }} BACKEND_HASH: ${{ needs.detect.outputs.backend_hash }} IS_ROLLBACK: ${{ needs.detect.outputs.backend_rollback }} MIGRATIONS_HASH: ${{ needs.detect.outputs.migrations_hash }} MIGRATIONS_CHANGED: ${{ needs.detect.outputs.migrations_changed }} run: bash deploy/scripts/deploy-backend.sh frontend: name: frontend needs: [detect, backend] # Runs after the backend succeeded or was skipped — a new UI must never meet an old API. if: >- always() && needs.detect.result == 'success' && needs.detect.outputs.frontend == 'true' && needs.backend.result != 'failure' && needs.backend.result != 'cancelled' runs-on: linux outputs: reused: ${{ steps.build.outputs.reused }} steps: - name: Checkout pushed commit env: ERP_GIT_TOKEN: ${{ secrets.ERPSECRET }} SHA: ${{ github.sha }} REF: ${{ github.ref_name }} run: | basic="$(printf 'oauth2:%s' "$ERP_GIT_TOKEN" | base64 | tr -d '\n')" url="https://gitea.hexdive.com/New_REP_SYSTEM/ERP-core.git" git init -q . git -c "http.extraHeader=Authorization: Basic $basic" fetch -q --depth 1 "$url" "$SHA" \ || git -c "http.extraHeader=Authorization: Basic $basic" fetch -q --depth 50 "$url" "$REF" git checkout -qf "$SHA" && git clean -ffdxq - name: Build (or reuse) image id: build env: FRONTEND_HASH: ${{ needs.detect.outputs.frontend_hash }} run: bash deploy/scripts/build-frontend.sh - name: Swap container, HTTP check env: ERP_ENV: ${{ needs.detect.outputs.env }} FRONTEND_HASH: ${{ needs.detect.outputs.frontend_hash }} IS_ROLLBACK: ${{ needs.detect.outputs.frontend_rollback }} run: bash deploy/scripts/deploy-frontend.sh release: name: release needs: [detect, backend, frontend] if: >- always() && needs.detect.outputs.record_release == 'true' && needs.backend.result != 'failure' && needs.backend.result != 'cancelled' && needs.frontend.result != 'failure' && needs.frontend.result != 'cancelled' runs-on: linux steps: - name: Checkout pushed commit env: ERP_GIT_TOKEN: ${{ secrets.ERPSECRET }} SHA: ${{ github.sha }} REF: ${{ github.ref_name }} run: | basic="$(printf 'oauth2:%s' "$ERP_GIT_TOKEN" | base64 | tr -d '\n')" url="https://gitea.hexdive.com/New_REP_SYSTEM/ERP-core.git" git init -q . git -c "http.extraHeader=Authorization: Basic $basic" fetch -q --depth 1 "$url" "$SHA" \ || git -c "http.extraHeader=Authorization: Basic $basic" fetch -q --depth 50 "$url" "$REF" git checkout -qf "$SHA" && git clean -ffdxq - name: Create Gitea release env: ERP_GIT_TOKEN: ${{ secrets.ERPSECRET }} BACKEND_HASH: ${{ needs.detect.outputs.backend_hash }} FRONTEND_HASH: ${{ needs.detect.outputs.frontend_hash }} MIGRATIONS_HASH: ${{ needs.detect.outputs.migrations_hash }} MIGRATIONS_CHANGED: ${{ needs.detect.outputs.migrations_changed }} BACKEND_RESULT: ${{ needs.backend.result }} BACKEND_REUSED: ${{ needs.backend.outputs.reused }} FRONTEND_RESULT: ${{ needs.frontend.result }} FRONTEND_REUSED: ${{ needs.frontend.outputs.reused }} run: bash deploy/scripts/release.sh e2e: name: e2e needs: [backend, frontend] if: >- always() && (github.event_name == 'schedule' || format('{0}', inputs.run_e2e) == 'true') && needs.backend.result != 'failure' && needs.frontend.result != 'failure' runs-on: linux steps: # Specs come from Dev: the test environment runs Dev, even when a schedule fires on production. - name: Checkout Dev env: ERP_GIT_TOKEN: ${{ secrets.ERPSECRET }} run: | basic="$(printf 'oauth2:%s' "$ERP_GIT_TOKEN" | base64 | tr -d '\n')" git init -q . git -c "http.extraHeader=Authorization: Basic $basic" fetch -q --depth 1 \ "https://gitea.hexdive.com/New_REP_SYSTEM/ERP-core.git" Dev git checkout -qf FETCH_HEAD && git clean -ffdxq - name: Playwright against test env: E2E_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL }} E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }} run: bash deploy/scripts/e2e.sh