name: Release on: push: tags: - 'v*' jobs: linux: name: Linux AppImage runs-on: ubuntu-latest env: # Public-facing repo URL for assets users will download. # `github.server_url` resolves to the runner's internal Gitea # endpoint (e.g. http://gitea:3000), which works for API calls # the runner makes itself but not for URLs baked into latest.json # or the AppImage's embedded --update-info — those are read by # end-user machines that can only reach Gitea via its public URL. PUBLIC_REPO_URL: 'https://src.bundespruefstelle.ch/magdev/php-qml' steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # need tag history for release notes - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.4' extensions: curl, json, mbstring tools: composer:v2 coverage: none - name: Install bundle dependencies working-directory: framework/php run: composer install --no-interaction --prefer-dist - name: Setup Python (for install-qt-action's aqtinstall) uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install build + AppImage tools run: sudo apt-get install -y cmake ninja-build rsync file libfuse2 desktop-file-utils - name: Setup Qt 6 uses: jurplel/install-qt-action@v4 with: version: '6.5.*' dir: ${{ github.workspace }}/qt cache: true setup-python: false - name: Install FrankenPHP run: | curl -fsSL -o /usr/local/bin/frankenphp \ https://github.com/php/frankenphp/releases/download/v1.12.2/frankenphp-linux-x86_64 chmod +x /usr/local/bin/frankenphp - name: Build the todo example working-directory: examples/todo run: | make install make build - name: Build AppImage (with embedded update-info) working-directory: examples/todo env: APPIMAGE_EXTRACT_AND_RUN: '1' FRANKENPHP: /usr/local/bin/frankenphp # AppImageUpdate sidecar will fetch this .zsync URL; it must # point at the asset we're about to upload to this Release. APPIMAGE_UPDATE_INFO: | zsync|${{ env.PUBLIC_REPO_URL }}/releases/download/${{ github.ref_name }}/Todo-x86_64.AppImage.zsync run: make appimage - name: Install zsync + Xvfb run: | sudo apt-get update -qq sudo apt-get install -y zsync xvfb - name: Performance smoke (PLAN.md §11 budgets) working-directory: examples/todo # CI runner overrides — strict bare-metal numbers stay in # perfsmoke.sh defaults for `make perf` runs. # # Cold start: shared runners legitimately need 4-6s for AppImage # extract + xvfb + Qt init + Symfony bootstrap. 10s = 5x baseline. # # Idle memory: Qt under xvfb falls back to Mesa llvmpipe (no GPU); # llvmpipe + LLVM 20 libs add ~30-50 MB per process, and perfsmoke # sums VmRSS across host + children (double-counts shared pages). # 600 MB = 3x baseline; still catches order-of-magnitude regressions. # # Bundle-size budget stays strict (environment-independent). env: PERF_COLD_START_MS: '10000' PERF_HEALTHZ_DEADLINE_MS: '15000' PERF_IDLE_MEM_MB: '600' run: ./tests/perfsmoke.sh build/Todo-x86_64.AppImage - name: Generate zsync metadata working-directory: examples/todo/build run: zsyncmake Todo-x86_64.AppImage -u Todo-x86_64.AppImage - name: Generate latest.json appcast working-directory: examples/todo/build env: TAG: ${{ github.ref_name }} run: | SIZE=$(stat -c %s Todo-x86_64.AppImage) SHA=$(sha256sum Todo-x86_64.AppImage | awk '{print $1}') URL_BASE="${PUBLIC_REPO_URL}/releases/download/${TAG}" jq -n \ --arg version "$TAG" \ --arg url "$URL_BASE/Todo-x86_64.AppImage" \ --arg sha256 "$SHA" \ --arg zsync "$URL_BASE/Todo-x86_64.AppImage.zsync" \ --argjson size "$SIZE" \ --arg released "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ '{version:$version, released_at:$released, appimage:{url:$url, sha256:$sha256, size:$size, zsync:$zsync}}' \ > latest.json cat latest.json - name: Compute SHA256SUMS working-directory: examples/todo/build run: | sha256sum Todo-x86_64.AppImage Todo-x86_64.AppImage.zsync latest.json \ > SHA256SUMS cat SHA256SUMS - name: Import GPG signing key if: ${{ env.GPG_KEY != '' }} env: GPG_KEY: ${{ secrets.GPG_KEY }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: | echo "$GPG_KEY" | gpg --batch --import # Default key id from the imported keyring (first secret key). KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec/ {print $5; exit}') echo "GPG_KEYID=$KEYID" >> "$GITHUB_ENV" - name: Sign SHA256SUMS if: ${{ env.GPG_KEYID != '' }} working-directory: examples/todo/build env: GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: | gpg --batch --pinentry-mode loopback \ --passphrase "$GPG_PASSPHRASE" \ --local-user "$GPG_KEYID" \ --detach-sign --armor \ -o SHA256SUMS.asc \ SHA256SUMS - name: Create Gitea Release and upload artefacts env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} REPO: ${{ github.repository }} TAG: ${{ github.ref_name }} working-directory: examples/todo/build run: | set -euo pipefail api="${GITHUB_SERVER_URL}/api/v1" # Pull this tag's section out of CHANGELOG.md for the release body. body=$(awk -v ver="${TAG#v}" ' $0 ~ "^## \\[" ver "\\]" { in_section=1; next } in_section && /^## \[/ { exit } in_section && /^\[.*\]:[[:space:]]/ { exit } in_section ' "$GITHUB_WORKSPACE/CHANGELOG.md") # Pre-1.0 tags are prerelease per SemVer convention. case "$TAG" in v0.*) prerelease=true ;; *) prerelease=false ;; esac # Create the release (or get the existing one for this tag) release_json=$(curl -fsSL -X POST "$api/repos/$REPO/releases" \ -H "Authorization: token $GITEA_TOKEN" \ -H 'Content-Type: application/json' \ -d "$(jq -n --arg tag "$TAG" --arg name "$TAG" --arg body "$body" --argjson pre "$prerelease" \ '{tag_name:$tag,name:$name,body:$body,draft:false,prerelease:$pre}')" \ || curl -fsSL "$api/repos/$REPO/releases/tags/$TAG" \ -H "Authorization: token $GITEA_TOKEN") rid=$(echo "$release_json" | jq -r .id) echo "Release id: $rid" # Wipe any pre-existing assets so a re-run (e.g. after a tag # rotation) produces a clean asset list rather than duplicates # accumulating across runs. existing=$(curl -fsSL "$api/repos/$REPO/releases/$rid/assets" \ -H "Authorization: token $GITEA_TOKEN") for aid in $(echo "$existing" | jq -r '.[].id'); do echo " deleting old asset $aid" curl -fsSL -X DELETE \ "$api/repos/$REPO/releases/$rid/assets/$aid" \ -H "Authorization: token $GITEA_TOKEN" done upload() { local f="$1" echo " uploading $f" curl -fsSL -X POST \ "$api/repos/$REPO/releases/$rid/assets?name=$(basename "$f")" \ -H "Authorization: token $GITEA_TOKEN" \ -H 'Content-Type: application/octet-stream' \ --data-binary "@$f" } upload Todo-x86_64.AppImage upload Todo-x86_64.AppImage.zsync upload latest.json upload SHA256SUMS [ -f SHA256SUMS.asc ] && upload SHA256SUMS.asc || true