Release #113
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 | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| release_type: | |
| description: "Release type" | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| - nightly | |
| - custom | |
| default: patch | |
| custom_version: | |
| description: "Custom semver (used only when release_type=custom)" | |
| required: false | |
| type: string | |
| prerelease: | |
| description: "Mark as prerelease" | |
| required: false | |
| default: false | |
| type: boolean | |
| schedule: | |
| - cron: "0 7 * * *" | |
| jobs: | |
| check-changes: | |
| runs-on: ubuntu-22.04 | |
| outputs: | |
| has_changes: ${{ steps.check.outputs.has_changes }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for same-day commits (UTC) | |
| id: check | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo "Manual dispatch — bypassing change-detection, proceeding with release." | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| TODAY_UTC=$(date -u +%Y-%m-%dT00:00:00Z) | |
| COMMIT_COUNT=$(git log --oneline --after="$TODAY_UTC" | wc -l | tr -d ' ') | |
| if [[ "$COMMIT_COUNT" -gt 0 ]]; then | |
| echo "Found ${COMMIT_COUNT} commit(s) since ${TODAY_UTC} — proceeding with nightly release." | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "No commits since ${TODAY_UTC} — skipping nightly release (no same-day changes)." | |
| echo "has_changes=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| release: | |
| needs: check-changes | |
| if: needs.check-changes.outputs.has_changes == 'true' | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: [ubuntu-22.04, macos-15] | |
| runs-on: ${{ matrix.platform }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Compute release version | |
| id: version | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| CURRENT_VERSION=$(node -p "require('./package.json').version") | |
| if [[ "${{ github.event_name }}" == "schedule" ]]; then | |
| RELEASE_TYPE="nightly" | |
| PRERELEASE="true" | |
| else | |
| RELEASE_TYPE="${{ github.event.inputs.release_type }}" | |
| PRERELEASE="${{ github.event.inputs.prerelease }}" | |
| fi | |
| if [[ "$RELEASE_TYPE" == "custom" ]]; then | |
| VERSION="${{ github.event.inputs.custom_version }}" | |
| if [[ -z "$VERSION" ]]; then | |
| echo "custom_version is required when release_type=custom" | |
| exit 1 | |
| fi | |
| elif [[ "$RELEASE_TYPE" == "nightly" ]]; then | |
| MAJOR=$(echo "$CURRENT_VERSION" | cut -d. -f1) | |
| MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f2) | |
| PATCH=$(echo "$CURRENT_VERSION" | cut -d. -f3) | |
| NIGHTLY_DATE=$(date -u +%Y%m%d) | |
| VERSION="${MAJOR}.${MINOR}.${PATCH}-nightly.${NIGHTLY_DATE}.${GITHUB_RUN_NUMBER}" | |
| else | |
| IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" | |
| if [[ -z "${MAJOR:-}" || -z "${MINOR:-}" || -z "${PATCH:-}" ]]; then | |
| echo "Current version must be semver core x.y.z, got: $CURRENT_VERSION" | |
| exit 1 | |
| fi | |
| if ! [[ "$MAJOR" =~ ^[0-9]+$ && "$MINOR" =~ ^[0-9]+$ && "$PATCH" =~ ^[0-9]+$ ]]; then | |
| echo "Current version must be numeric semver core x.y.z, got: $CURRENT_VERSION" | |
| exit 1 | |
| fi | |
| case "$RELEASE_TYPE" in | |
| patch) VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))" ;; | |
| minor) VERSION="${MAJOR}.$((MINOR + 1)).0" ;; | |
| major) VERSION="$((MAJOR + 1)).0.0" ;; | |
| *) | |
| echo "Unsupported release_type: $RELEASE_TYPE" | |
| exit 1 | |
| ;; | |
| esac | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "prerelease=$PRERELEASE" >> "$GITHUB_OUTPUT" | |
| echo "release_type=$RELEASE_TYPE" >> "$GITHUB_OUTPUT" | |
| - name: Apply version across project files | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| VERSION='${{ steps.version.outputs.version }}' | |
| node -e " | |
| const fs = require('node:fs'); | |
| const version = process.argv[1]; | |
| const packageJsonPath = 'package.json'; | |
| const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); | |
| packageJson.version = version; | |
| fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\\n'); | |
| const tauriConfigPath = 'src-tauri/tauri.conf.json'; | |
| const tauriConfig = JSON.parse(fs.readFileSync(tauriConfigPath, 'utf8')); | |
| tauriConfig.version = version; | |
| fs.writeFileSync(tauriConfigPath, JSON.stringify(tauriConfig, null, 2) + '\\n'); | |
| " "$VERSION" | |
| perl -0pi -e "s/^version = \"[^\"]+\"/version = \"$VERSION\"/m" src-tauri/Cargo.toml | |
| - uses: oven-sh/setup-bun@v1 | |
| with: | |
| bun-version: latest | |
| - run: rustup toolchain install stable --profile minimal | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| prefix-key: "v1-release-rust" | |
| workspaces: "src-tauri" | |
| - name: (Ubuntu only) Install dependencies | |
| if: matrix.platform == 'ubuntu-22.04' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf | |
| - name: Install node packages | |
| run: bun install | |
| - name: (macOS only) Prepare Apple notarization key | |
| if: matrix.platform == 'macos-15' | |
| shell: bash | |
| env: | |
| APPLE_API_KEY_SECRET: ${{ secrets.APPLE_API_KEY }} | |
| run: | | |
| set -euo pipefail | |
| if [[ -z "${APPLE_API_KEY_SECRET}" ]]; then | |
| echo "Missing required secret: APPLE_API_KEY" | |
| exit 1 | |
| fi | |
| KEY_FILE="$RUNNER_TEMP/apple_api_key.p8" | |
| # Support APPLE_API_KEY being stored either as raw .p8 contents | |
| # or base64-encoded .p8 contents. | |
| if echo "${APPLE_API_KEY_SECRET}" | base64 --decode > "$KEY_FILE" 2>/dev/null \ | |
| && grep -q "BEGIN PRIVATE KEY" "$KEY_FILE"; then | |
| echo "Using base64-decoded APPLE_API_KEY secret." | |
| else | |
| printf "%s" "${APPLE_API_KEY_SECRET}" > "$KEY_FILE" | |
| if ! grep -q "BEGIN PRIVATE KEY" "$KEY_FILE"; then | |
| echo "APPLE_API_KEY is neither valid base64 .p8 nor raw .p8 content." | |
| exit 1 | |
| fi | |
| echo "Using raw APPLE_API_KEY secret." | |
| fi | |
| { | |
| echo "APPLE_API_KEY_PATH=$KEY_FILE" | |
| } >> "$GITHUB_ENV" | |
| - name: Build and publish release assets | |
| uses: tauri-apps/tauri-action@v0 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.CITADEL_RELEASE_TOKEN }} | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} | |
| APPLE_API_KEY: ${{ secrets.APPLE_API_KEY_ID }} | |
| APPLE_API_KEY_PATH: ${{ env.APPLE_API_KEY_PATH }} | |
| APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | |
| with: | |
| tauriScript: bun tauri | |
| args: --config '{"bundle":{"createUpdaterArtifacts":true}}' | |
| tagName: v${{ steps.version.outputs.version }} | |
| releaseName: Citadel v${{ steps.version.outputs.version }} | |
| releaseBody: | | |
| Automated ${{ steps.version.outputs.release_type }} release. | |
| Includes updater metadata (`latest.json`) for Tauri updater clients. | |
| prerelease: ${{ steps.version.outputs.prerelease == 'true' }} | |
| generateReleaseNotes: true | |
| # Must run after the build: create-pull-request resets the working tree | |
| # to the base branch, which would strip the applied version bump and | |
| # produce artifacts built with the old version. | |
| - name: Open version bump pull request | |
| if: github.event_name == 'workflow_dispatch' && matrix.platform == 'ubuntu-22.04' && steps.version.outputs.release_type != 'nightly' | |
| uses: peter-evans/create-pull-request@v7 | |
| with: | |
| token: ${{ secrets.CITADEL_RELEASE_TOKEN }} | |
| commit-message: "chore(release): bump version to v${{ steps.version.outputs.version }}" | |
| branch: chore/release-bump-v${{ steps.version.outputs.version }} | |
| delete-branch: true | |
| title: "chore(release): bump version to v${{ steps.version.outputs.version }}" | |
| body: | | |
| Automated release version bump generated by the Release workflow. | |
| Files updated: | |
| - package.json | |
| - src-tauri/Cargo.toml | |
| - src-tauri/tauri.conf.json | |
| add-paths: | | |
| package.json | |
| src-tauri/Cargo.toml | |
| src-tauri/tauri.conf.json |