release: publish T4 Code v0.1.13 (#20) #15
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release app builds | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: Existing version tag to publish | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: release-${{ github.event.inputs.tag || github.ref_name }} | |
| cancel-in-progress: false | |
| env: | |
| RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }} | |
| jobs: | |
| verify: | |
| if: ${{ github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main' }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 30 | |
| outputs: | |
| source_sha: ${{ steps.source.outputs.source_sha }} | |
| version: ${{ steps.source.outputs.version }} | |
| steps: | |
| - name: Check out trusted release-control source | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ github.sha }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Resolve immutable release source | |
| id: source | |
| shell: bash | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| MAIN_SHA: ${{ github.sha }} | |
| run: | | |
| set -euo pipefail | |
| trusted_version=$(node -p "require('./package.json').version") | |
| expected_tag="v${trusted_version}" | |
| if [[ "$RELEASE_TAG" != "$expected_tag" ]]; then | |
| echo "release tag must be the current package tag ${expected_tag}" >&2 | |
| exit 1 | |
| fi | |
| git fetch --force --no-tags origin "refs/heads/main:refs/remotes/origin/main" | |
| git fetch --force origin "refs/tags/${RELEASE_TAG}:refs/tags/${RELEASE_TAG}" | |
| source_sha=$(git rev-parse "${RELEASE_TAG}^{commit}") | |
| tag_version=$(git show "${source_sha}:package.json" | jq -er '.version') | |
| if [[ "$tag_version" != "$trusted_version" ]]; then | |
| echo "release tag package version ${tag_version} does not match trusted source ${trusted_version}" >&2 | |
| exit 1 | |
| fi | |
| if ! git merge-base --is-ancestor "$source_sha" refs/remotes/origin/main; then | |
| echo "release tag source is not reachable from main" >&2 | |
| exit 1 | |
| fi | |
| if [[ "$EVENT_NAME" == "workflow_dispatch" && "$MAIN_SHA" != "$(git rev-parse HEAD)" ]]; then | |
| echo "manual releases must run from the checked-out main commit" >&2 | |
| exit 1 | |
| fi | |
| printf 'source_sha=%s\nversion=%s\n' "$source_sha" "$tag_version" >> "$GITHUB_OUTPUT" | |
| - name: Check out immutable release source | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ steps.source.outputs.source_sha }} | |
| persist-credentials: false | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 | |
| with: | |
| version: 11.10.0 | |
| - name: Install Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 24.13.1 | |
| cache: pnpm | |
| - name: Verify tag, packages, clients, docs, and downloads agree | |
| run: node scripts/check-release-consistency.mjs --tag "$RELEASE_TAG" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Install browser for end-to-end tests | |
| run: pnpm exec playwright install --with-deps chromium | |
| - name: Check source and types | |
| run: pnpm check | |
| - name: Run tooling regression tests | |
| run: pnpm test:tooling | |
| - name: Run workspace tests | |
| run: pnpm test | |
| - name: Run built-app end-to-end tests | |
| run: pnpm test:e2e | |
| - name: Build all workspaces | |
| run: pnpm build | |
| - name: Check packaging contract | |
| run: pnpm test:packaging | |
| build-linux: | |
| needs: verify | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 35 | |
| steps: | |
| - name: Check out verified release source | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ needs.verify.outputs.source_sha }} | |
| persist-credentials: false | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 | |
| with: | |
| version: 11.10.0 | |
| - name: Install Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 24.13.1 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build Linux packages | |
| run: pnpm package:linux | |
| - name: Inspect Linux packages | |
| run: pnpm inspect:package -- release/*.deb release/*.AppImage | |
| - name: Stage Linux artifacts | |
| shell: bash | |
| run: | | |
| mkdir -p artifacts | |
| cp release/T4-Code-*.deb release/T4-Code-*.AppImage artifacts/ | |
| (cd artifacts && sha256sum T4-Code-* > SHA256SUMS-linux.txt) | |
| - name: Upload Linux artifacts | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: linux-release | |
| path: artifacts/* | |
| if-no-files-found: error | |
| retention-days: 7 | |
| build-android: | |
| needs: verify | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 35 | |
| steps: | |
| - name: Check out verified release source | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ needs.verify.outputs.source_sha }} | |
| persist-credentials: false | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 | |
| with: | |
| version: 11.10.0 | |
| - name: Install Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 24.13.1 | |
| cache: pnpm | |
| - name: Install Java | |
| uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 | |
| with: | |
| distribution: temurin | |
| java-version: "21" | |
| cache: gradle | |
| - name: Install Android SDK | |
| uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3 | |
| - name: Install pinned Android platform and build tools | |
| run: sdkmanager --install "platforms;android-36" "build-tools;36.0.0" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Restore and validate Android release signing key | |
| shell: bash | |
| env: | |
| T4_ANDROID_KEYSTORE_BASE64: ${{ secrets.T4_ANDROID_KEYSTORE_BASE64 }} | |
| T4_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.T4_ANDROID_KEYSTORE_PASSWORD }} | |
| T4_ANDROID_KEY_ALIAS: ${{ secrets.T4_ANDROID_KEY_ALIAS }} | |
| T4_ANDROID_KEY_PASSWORD: ${{ secrets.T4_ANDROID_KEY_PASSWORD }} | |
| run: | | |
| set -euo pipefail | |
| for name in \ | |
| T4_ANDROID_KEYSTORE_BASE64 \ | |
| T4_ANDROID_KEYSTORE_PASSWORD \ | |
| T4_ANDROID_KEY_ALIAS \ | |
| T4_ANDROID_KEY_PASSWORD | |
| do | |
| if [[ -z "${!name:-}" ]]; then | |
| echo "required GitHub Actions secret ${name} is not configured" >&2 | |
| exit 1 | |
| fi | |
| done | |
| keystore_path="$RUNNER_TEMP/t4-code-release.jks" | |
| printf '%s' "$T4_ANDROID_KEYSTORE_BASE64" | base64 --decode > "$keystore_path" | |
| chmod 600 "$keystore_path" | |
| keytool -list \ | |
| -keystore "$keystore_path" \ | |
| -storepass "$T4_ANDROID_KEYSTORE_PASSWORD" \ | |
| -alias "$T4_ANDROID_KEY_ALIAS" >/dev/null | |
| - name: Build signed Android APK | |
| env: | |
| T4_ANDROID_KEYSTORE_PATH: ${{ runner.temp }}/t4-code-release.jks | |
| T4_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.T4_ANDROID_KEYSTORE_PASSWORD }} | |
| T4_ANDROID_KEY_ALIAS: ${{ secrets.T4_ANDROID_KEY_ALIAS }} | |
| T4_ANDROID_KEY_PASSWORD: ${{ secrets.T4_ANDROID_KEY_PASSWORD }} | |
| run: | | |
| pnpm build:web | |
| pnpm --filter @t4-code/mobile build:android:release | |
| - name: Inspect signed Android APK | |
| shell: bash | |
| env: | |
| VERSION: ${{ needs.verify.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| apk="apps/mobile/android/app/build/outputs/apk/release/app-release.apk" | |
| test -f "$apk" | |
| build_tools="${ANDROID_SDK_ROOT:-$ANDROID_HOME}/build-tools/36.0.0" | |
| export PATH="$build_tools:$PATH" | |
| apksigner verify --verbose "$apk" | |
| aapt dump badging "$apk" | grep -F "package: name='com.lycaonsolutions.t4code'" | |
| aapt dump badging "$apk" | grep -F "versionName='${VERSION}'" | |
| - name: Stage Android artifact | |
| shell: bash | |
| env: | |
| VERSION: ${{ needs.verify.outputs.version }} | |
| run: | | |
| mkdir -p artifacts | |
| cp \ | |
| apps/mobile/android/app/build/outputs/apk/release/app-release.apk \ | |
| "artifacts/T4-Code-${VERSION}-android.apk" | |
| (cd artifacts && sha256sum T4-Code-* > SHA256SUMS-android.txt) | |
| - name: Upload Android artifact | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: android-release | |
| path: artifacts/* | |
| if-no-files-found: error | |
| retention-days: 7 | |
| build-macos: | |
| needs: verify | |
| runs-on: macos-15 | |
| timeout-minutes: 40 | |
| steps: | |
| - name: Check out verified release source | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ needs.verify.outputs.source_sha }} | |
| persist-credentials: false | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 | |
| with: | |
| version: 11.10.0 | |
| - name: Install Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 24.13.1 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build unsigned macOS packages | |
| run: pnpm package:mac:unsigned | |
| - name: Inspect macOS packages | |
| run: | | |
| pnpm inspect:package -- release/*.zip | |
| pnpm inspect:dmg -- release/*.dmg | |
| - name: Stage macOS artifacts | |
| shell: bash | |
| run: | | |
| mkdir -p artifacts | |
| cp release/T4-Code-*.dmg release/T4-Code-*.zip artifacts/ | |
| (cd artifacts && shasum -a 256 T4-Code-* > SHA256SUMS-macos.txt) | |
| - name: Upload macOS artifacts | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: macos-release | |
| path: artifacts/* | |
| if-no-files-found: error | |
| retention-days: 7 | |
| publish: | |
| needs: [verify, build-android, build-linux, build-macos] | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Check out verified release source | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ needs.verify.outputs.source_sha }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Confirm the release tag still resolves to the verified source | |
| shell: bash | |
| env: | |
| SOURCE_SHA: ${{ needs.verify.outputs.source_sha }} | |
| run: | | |
| set -euo pipefail | |
| git fetch --force origin "refs/tags/${RELEASE_TAG}:refs/tags/${RELEASE_TAG}" | |
| test "$(git rev-parse "${RELEASE_TAG}^{commit}")" = "$SOURCE_SHA" | |
| - name: Download built artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| pattern: "*-release" | |
| path: artifacts | |
| merge-multiple: true | |
| - name: Assemble checksums | |
| shell: bash | |
| run: cat artifacts/SHA256SUMS-android.txt artifacts/SHA256SUMS-linux.txt artifacts/SHA256SUMS-macos.txt | sort -k2 > artifacts/SHA256SUMS.txt | |
| - name: Publish GitHub release | |
| uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 | |
| with: | |
| tag_name: ${{ env.RELEASE_TAG }} | |
| generate_release_notes: true | |
| fail_on_unmatched_files: true | |
| body_path: docs/CURRENT_RELEASE_NOTES.md | |
| files: | | |
| artifacts/T4-Code-*.apk | |
| artifacts/T4-Code-*.deb | |
| artifacts/T4-Code-*.AppImage | |
| artifacts/T4-Code-*.dmg | |
| artifacts/T4-Code-*.zip | |
| artifacts/SHA256SUMS.txt | |
| dispatch-site: | |
| name: Dispatch site deployment after release publication | |
| needs: publish | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| permissions: | |
| actions: write | |
| contents: read | |
| steps: | |
| - name: Dispatch the production workflow from main with exact release source | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| run: >- | |
| gh workflow run deploy-site.yml | |
| --ref main | |
| -f release_tag="$RELEASE_TAG" |