name: release # GitHub's build. The Gitea build is .gitea/workflows/build.yml. # # Pushing a tag vX.Y.Z builds the Linux and Windows packages and the Quest APK # inside the build-image and attaches them to a GitHub release. A manual run # builds the same files and keeps them as a workflow artifact without # releasing them. # # The build-image (build-image/Dockerfile) is published to GHCR, tagged with a # hash of the files that define it, and only built when that tag does not # exist yet. on: push: tags: - "v*" workflow_dispatch: permissions: contents: read jobs: image: runs-on: ubuntu-latest permissions: contents: read packages: write outputs: name: ${{ steps.name.outputs.name }} steps: - uses: actions/checkout@v4 - name: Name the image after the files that define it id: name run: | set -euo pipefail hash="$(cat build-image/Dockerfile build-image/docker-entrypoint.sh build-image/build-engine.sh | sha256sum | cut -c1-16)" echo "name=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/questshock-builder:${hash}" >> "$GITHUB_OUTPUT" - name: Build and push the image if it does not exist yet env: IMAGE: ${{ steps.name.outputs.name }} REGISTRY_USER: ${{ github.actor }} REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail echo "$REGISTRY_PASSWORD" | docker login ghcr.io -u "$REGISTRY_USER" --password-stdin if docker manifest inspect "$IMAGE" >/dev/null 2>&1; then echo "$IMAGE already exists" exit 0 fi # The image is large and the runner's disk is small. sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL # The label links the package to this repository, so the build job # can pull it with the workflow's token. docker build -f build-image/Dockerfile \ --label "org.opencontainers.image.source=https://github.com/${GITHUB_REPOSITORY}" \ -t "$IMAGE" . docker push "$IMAGE" build: needs: image runs-on: ubuntu-latest permissions: contents: read packages: read defaults: run: shell: bash # The job runs inside the build-image, so `make` compiles directly instead # of starting the image itself (QUESTSHOCK_BUILD_IMAGE is set in it). container: image: ${{ needs.image.outputs.name }} credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: # The version comes from `git describe --tags`. fetch-depth: 0 - name: Build the Linux and Windows packages run: make package # The release key is stored base64-encoded in ANDROID_KEYSTORE_BASE64; see # README section 4.3. A tag build must be signed with it. - name: Prepare release signing env: KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} run: | set -euo pipefail if [ -z "$KEYSTORE_BASE64" ]; then if [[ "$GITHUB_REF" == refs/tags/* ]]; then echo "::error::ANDROID_KEYSTORE_BASE64 is not set: a release APK must be signed" exit 1 fi echo "No signing key: the APK will be a debug build" exit 0 fi echo "$KEYSTORE_BASE64" | base64 -d > "${RUNNER_TEMP:-/tmp}/questshock-release.jks" echo "QUESTSHOCK_KEYSTORE=${RUNNER_TEMP:-/tmp}/questshock-release.jks" >> "$GITHUB_ENV" # Needs network access for Gradle's own dependency resolution. - name: Build the Quest APK env: QUESTSHOCK_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} QUESTSHOCK_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} QUESTSHOCK_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} run: make apk - uses: actions/upload-artifact@v4 with: name: questshock path: | dist/shockolate-*-linux-*.tar.gz dist/shockolate-*-windows-*.zip dist/questshock-*-android-*.apk release: if: startsWith(github.ref, 'refs/tags/') needs: build runs-on: ubuntu-latest permissions: contents: write env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} steps: - uses: actions/download-artifact@v4 with: name: questshock path: dist - name: Create the release, or update its files if it exists run: | set -euo pipefail if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then gh release upload "$GITHUB_REF_NAME" dist/* --clobber else gh release create "$GITHUB_REF_NAME" dist/* \ --title "$GITHUB_REF_NAME" \ --generate-notes \ --notes "Sideload the APK onto a Meta Quest (see README section 5.1). The game data is not included: you need your own copy of System Shock: Enhanced Edition." fi