name: Deploy on: schedule: - cron: '0 * * * *' # every hour on the hour workflow_dispatch: jobs: test-android-firebase: name: Android Instrumented Tests (Firebase Test Lab) runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Check runner tools run: | command -v dagger >/dev/null 2>&1 || { echo "ERROR: dagger is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } command -v task >/dev/null 2>&1 || { echo "ERROR: task is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } dpkg -s stunnel4 netcat-openbsd >/dev/null 2>&1 || { echo "ERROR: stunnel4/netcat-openbsd are not installed in the runner image. Add them to .forgejo/Dockerfile."; exit 1; } - name: Setup Dagger Remote Engine (via stunnel) env: DAGGER_STUNNEL_URL: ${{ secrets.DAGGER_STUNNEL_URL }} DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} run: scripts/setup_dagger_remote.sh - name: Run Android Tests on Firebase Test Lab env: FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY }} FIREBASE_PROJECT_ID: ${{ vars.FIREBASE_PROJECT_ID }} DAGGER_NO_NAG: "1" run: task test-android-firebase - name: Cleanup TLS credentials if: always() run: rm -rf /tmp/dagger-tls /tmp/stunnel-dagger.conf /tmp/stunnel.pid deploy-playstore: name: Build & Deploy to Play Store runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Check runner tools run: | command -v dagger >/dev/null 2>&1 || { echo "ERROR: dagger is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } command -v task >/dev/null 2>&1 || { echo "ERROR: task is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } dpkg -s stunnel4 netcat-openbsd >/dev/null 2>&1 || { echo "ERROR: stunnel4/netcat-openbsd are not installed in the runner image. Add them to .forgejo/Dockerfile."; exit 1; } - name: Setup Dagger Remote Engine (via stunnel) env: DAGGER_STUNNEL_URL: ${{ secrets.DAGGER_STUNNEL_URL }} DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} run: scripts/setup_dagger_remote.sh - name: Publish Android to Play Store env: ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} PLAY_STORE_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG_JSON }} DAGGER_NO_NAG: "1" run: task publish-android - name: Cleanup TLS credentials if: always() run: rm -rf /tmp/dagger-tls /tmp/stunnel-dagger.conf /tmp/stunnel.pid deploy-apk: name: Build & Deploy APK to Server runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Check runner tools run: | command -v dagger >/dev/null 2>&1 || { echo "ERROR: dagger is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } command -v task >/dev/null 2>&1 || { echo "ERROR: task is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } dpkg -s stunnel4 netcat-openbsd >/dev/null 2>&1 || { echo "ERROR: stunnel4/netcat-openbsd are not installed in the runner image. Add them to .forgejo/Dockerfile."; exit 1; } - name: Setup Dagger Remote Engine (via stunnel) env: DAGGER_STUNNEL_URL: ${{ secrets.DAGGER_STUNNEL_URL }} DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} run: scripts/setup_dagger_remote.sh - name: Build & Deploy APK to server if: ${{ secrets.SSH_PRIVATE_KEY != '' }} env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} SSH_USER: ${{ secrets.SSH_USER }} SSH_HOST: ${{ secrets.SSH_HOST }} ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} DAGGER_NO_NAG: "1" run: task deploy-apk - name: Cleanup TLS credentials if: always() run: rm -rf /tmp/dagger-tls /tmp/stunnel-dagger.conf /tmp/stunnel.pid build-linux: name: Build Linux Release runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Check runner tools run: | command -v dagger >/dev/null 2>&1 || { echo "ERROR: dagger is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } command -v task >/dev/null 2>&1 || { echo "ERROR: task is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } dpkg -s stunnel4 netcat-openbsd >/dev/null 2>&1 || { echo "ERROR: stunnel4/netcat-openbsd are not installed in the runner image. Add them to .forgejo/Dockerfile."; exit 1; } - name: Setup Dagger Remote Engine (via stunnel) env: DAGGER_STUNNEL_URL: ${{ secrets.DAGGER_STUNNEL_URL }} DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} run: scripts/setup_dagger_remote.sh - name: Build & Deploy Linux to server if: ${{ secrets.SSH_PRIVATE_KEY != '' }} env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} SSH_USER: ${{ secrets.SSH_USER }} SSH_HOST: ${{ secrets.SSH_HOST }} DAGGER_NO_NAG: "1" run: task deploy-linux - name: Cleanup TLS credentials if: always() run: rm -rf /tmp/dagger-tls /tmp/stunnel-dagger.conf /tmp/stunnel.pid publish-website: name: Publish Website Build History runs-on: ubuntu-latest needs: [build-linux, deploy-playstore, deploy-apk] if: | always() && (needs.build-linux.result == 'success' || needs.deploy-playstore.result == 'success' || needs.deploy-apk.result == 'success') timeout-minutes: 60 steps: - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Check runner tools run: | command -v dagger >/dev/null 2>&1 || { echo "ERROR: dagger is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } command -v task >/dev/null 2>&1 || { echo "ERROR: task is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } dpkg -s stunnel4 netcat-openbsd >/dev/null 2>&1 || { echo "ERROR: stunnel4/netcat-openbsd are not installed in the runner image. Add them to .forgejo/Dockerfile."; exit 1; } - name: Setup Dagger Remote Engine (via stunnel) env: DAGGER_STUNNEL_URL: ${{ secrets.DAGGER_STUNNEL_URL }} DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} run: scripts/setup_dagger_remote.sh - name: Generate build history and deploy website if: ${{ secrets.SSH_PRIVATE_KEY != '' }} env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} SSH_USER: ${{ secrets.SSH_USER }} SSH_HOST: ${{ secrets.SSH_HOST }} DAGGER_NO_NAG: "1" run: task publish-website - name: Cleanup TLS credentials if: always() run: rm -rf /tmp/dagger-tls /tmp/stunnel-dagger.conf /tmp/stunnel.pid label-deploy-health: name: Update Deploy Health Label runs-on: ubuntu-latest needs: [test-android-firebase, deploy-playstore, deploy-apk, build-linux] if: always() && vars.DEPLOY_HEALTH_ISSUE != '' timeout-minutes: 5 steps: - name: Set CI/Full-Pass or CI/Full-Fail label on tracking issue env: FORGEJO_TOKEN: ${{ github.token }} FORGEJO_URL: ${{ github.server_url }} DEPLOY_HEALTH_ISSUE: ${{ vars.DEPLOY_HEALTH_ISSUE }} ALL_SUCCEEDED: ${{ needs.test-android-firebase.result == 'success' && needs.deploy-playstore.result == 'success' && needs.deploy-apk.result == 'success' && needs.build-linux.result == 'success' }} run: | python3 - << 'PYEOF' import os, json, urllib.request, urllib.error issue = os.environ.get("DEPLOY_HEALTH_ISSUE", "").strip() if not issue: print("DEPLOY_HEALTH_ISSUE not set; skipping") raise SystemExit(0) token = os.environ["FORGEJO_TOKEN"] url_base = os.environ["FORGEJO_URL"].rstrip("/") succeeded = os.environ.get("ALL_SUCCEEDED", "false").lower() == "true" add_label = "CI/Full-Pass" if succeeded else "CI/Full-Fail" remove_label = "CI/Full-Fail" if succeeded else "CI/Full-Pass" headers = {"Authorization": f"token {token}", "Content-Type": "application/json"} api = f"{url_base}/api/v1/repos/guettli/sharedinbox" def api_get(path): req = urllib.request.Request(f"{api}{path}", headers=headers) with urllib.request.urlopen(req) as r: return json.loads(r.read()) def api_put(path, body): data = json.dumps(body).encode() req = urllib.request.Request(f"{api}{path}", data=data, headers=headers, method="PUT") try: with urllib.request.urlopen(req) as r: return json.loads(r.read()) except urllib.error.HTTPError as e: print(f"PUT {path} failed: {e.read().decode()}") raise repo_labels = api_get("/labels") label_map = {l["name"]: l["id"] for l in repo_labels} if add_label not in label_map: print(f"Label '{add_label}' not found in repo — create it first") raise SystemExit(1) current = api_get(f"/issues/{issue}/labels") keep_ids = [l["id"] for l in current if l["name"] not in ("CI/Full-Pass", "CI/Full-Fail")] keep_ids.append(label_map[add_label]) api_put(f"/issues/{issue}/labels", {"labels": keep_ids}) print(f"Set '{add_label}' on issue #{issue}") PYEOF