Skip to content

2.5.0

2.5.0 #62

Workflow file for this run

name: Release
on:
push:
tags:
- "app-v*"
workflow_dispatch:
inputs:
tag:
description: "Release tag to build, for example app-v0.1.0"
required: true
type: string
permissions:
contents: write
env:
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
jobs:
release:
name: Build ${{ matrix.label }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- label: macOS Intel
os: macos-latest
target: x86_64-apple-darwin
args: --target x86_64-apple-darwin
stableAsset: codex-usage-desktop-macos-x64.dmg
updaterAsset: codex-usage-desktop-macos-x64.app.tar.gz
- label: macOS Apple Silicon
os: macos-latest
target: aarch64-apple-darwin
args: --target aarch64-apple-darwin
stableAsset: codex-usage-desktop-macos-arm64.dmg
updaterAsset: codex-usage-desktop-macos-arm64.app.tar.gz
- label: Windows x64
os: windows-latest
target: x86_64-pc-windows-msvc
args: --target x86_64-pc-windows-msvc
stableAsset: codex-usage-desktop-windows-x64-setup.exe
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ env.RELEASE_TAG }}
fetch-depth: 0
- name: Validate release tag
shell: bash
run: |
set -euo pipefail
version="$(node -p "require('./src-tauri/tauri.conf.json').version")"
expected="app-v${version}"
if [[ "${RELEASE_TAG}" != "${expected}" ]]; then
echo "Release tag ${RELEASE_TAG} does not match src-tauri/tauri.conf.json version ${version}."
echo "Expected tag: ${expected}"
exit 1
fi
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache Rust
uses: swatinem/rust-cache@v2
with:
workspaces: src-tauri -> target
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test frontend
run: pnpm test
- name: Typecheck frontend
run: pnpm typecheck
- name: Test Rust backend
run: cargo test --manifest-path src-tauri/Cargo.toml
- name: Generate release notes
id: release_notes
shell: bash
run: |
set -euo pipefail
previous_tag="$(git describe --tags --match 'app-v*' --abbrev=0 "${RELEASE_TAG}^" 2>/dev/null || true)"
if [[ -n "${previous_tag}" ]]; then
range="${previous_tag}..${RELEASE_TAG}"
else
range="${RELEASE_TAG}"
fi
{
echo "## What's Changed"
echo
has_changes=false
version_bump_regex='^chore(\([^)]*\))?:[[:space:]]*[0-9]+\.[0-9]+\.[0-9]+$'
while IFS= read -r subject; do
[[ -z "${subject}" ]] && continue
if [[ "${subject}" =~ ${version_bump_regex} ]]; then
continue
fi
has_changes=true
echo "- ${subject}"
done < <(git log --format=%s --reverse "${range}")
if [[ "${has_changes}" == "false" ]]; then
echo "- No notable commit changes."
fi
echo
if [[ -n "${previous_tag}" ]]; then
echo "**Full Changelog**: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/compare/${previous_tag}...${RELEASE_TAG}"
else
echo "**Full Changelog**: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commits/${RELEASE_TAG}"
fi
} > release-notes.md
{
echo "body<<EOF"
cat release-notes.md
echo "EOF"
} >> "${GITHUB_OUTPUT}"
- name: Build and publish Tauri app
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
with:
tagName: ${{ env.RELEASE_TAG }}
releaseName: Codex Usage Desktop ${{ env.RELEASE_TAG }}
releaseBody: ${{ steps.release_notes.outputs.body }}
releaseDraft: true
prerelease: false
args: ${{ matrix.args }}
- name: Prepare stable DMG asset
if: runner.os == 'macOS'
shell: bash
run: |
set -euo pipefail
search_dirs=()
for dir in \
"src-tauri/target/${{ matrix.target }}/release/bundle/dmg" \
"src-tauri/target/release/bundle/dmg"
do
if [[ -d "${dir}" ]]; then
search_dirs+=("${dir}")
fi
done
if [[ "${#search_dirs[@]}" -eq 0 ]]; then
echo "No Tauri DMG bundle directories were found."
exit 1
fi
dmg="$(find "${search_dirs[@]}" -maxdepth 1 -type f -name '*.dmg' | head -n 1)"
updater="$(find "src-tauri/target/${{ matrix.target }}/release/bundle/macos" -maxdepth 1 -type f -name '*.app.tar.gz' | head -n 1)"
signature="$(find "src-tauri/target/${{ matrix.target }}/release/bundle/macos" -maxdepth 1 -type f -name '*.app.tar.gz.sig' | head -n 1)"
if [[ -z "${dmg}" || -z "${updater}" || -z "${signature}" ]]; then
echo "The DMG, updater archive, or updater signature is missing."
exit 1
fi
mkdir -p release-assets
cp "${dmg}" "release-assets/${{ matrix.stableAsset }}"
cp "${updater}" "release-assets/${{ matrix.updaterAsset }}"
cp "${signature}" "release-assets/${{ matrix.updaterAsset }}.sig"
- name: Upload stable DMG asset
if: runner.os == 'macOS'
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ env.RELEASE_TAG }}
draft: true
files: release-assets/*
- name: Prepare and validate Windows assets
if: runner.os == 'Windows'
shell: bash
run: |
set -euo pipefail
search_dirs=()
for dir in \
"src-tauri/target/${{ matrix.target }}/release/bundle/nsis" \
"src-tauri/target/release/bundle/nsis"
do
if [[ -d "${dir}" ]]; then
search_dirs+=("${dir}")
fi
done
if [[ "${#search_dirs[@]}" -eq 0 ]]; then
echo "No Tauri NSIS bundle directories were found."
exit 1
fi
setup="$(find "${search_dirs[@]}" -maxdepth 1 -type f -name '*-setup.exe' | head -n 1)"
if [[ -z "${setup}" ]]; then
echo "The setup executable is missing."
exit 1
fi
signature="${setup}.sig"
if [[ ! -f "${signature}" ]]; then
echo "The setup executable signature is missing: ${signature}"
exit 1
fi
mkdir -p release-assets
cp "${setup}" "release-assets/${{ matrix.stableAsset }}"
cp "${signature}" "release-assets/${{ matrix.stableAsset }}.sig"
- name: Upload Windows assets
if: runner.os == 'Windows'
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ env.RELEASE_TAG }}
draft: true
files: release-assets/*
updater-manifest:
name: Publish updater manifest
needs: release
runs-on: ubuntu-latest
steps:
- name: Download stable updater assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
mkdir updater-assets
gh release download "${RELEASE_TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--dir updater-assets \
--pattern 'codex-usage-desktop-macos-*.app.tar.gz' \
--pattern 'codex-usage-desktop-macos-*.app.tar.gz.sig' \
--pattern 'codex-usage-desktop-windows-x64-setup.exe' \
--pattern 'codex-usage-desktop-windows-x64-setup.exe.sig'
- name: Generate complete updater manifest
shell: bash
run: |
set -euo pipefail
version="${RELEASE_TAG#app-v}"
base_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}"
published_at="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
mac_x64_sig="$(cat updater-assets/codex-usage-desktop-macos-x64.app.tar.gz.sig)"
mac_arm64_sig="$(cat updater-assets/codex-usage-desktop-macos-arm64.app.tar.gz.sig)"
windows_x64_sig="$(cat updater-assets/codex-usage-desktop-windows-x64-setup.exe.sig)"
jq -n \
--arg version "${version}" \
--arg notes "See the GitHub release for changes in ${RELEASE_TAG}." \
--arg pub_date "${published_at}" \
--arg mac_x64_sig "${mac_x64_sig}" \
--arg mac_arm64_sig "${mac_arm64_sig}" \
--arg windows_x64_sig "${windows_x64_sig}" \
--arg mac_x64_url "${base_url}/codex-usage-desktop-macos-x64.app.tar.gz" \
--arg mac_arm64_url "${base_url}/codex-usage-desktop-macos-arm64.app.tar.gz" \
--arg windows_x64_url "${base_url}/codex-usage-desktop-windows-x64-setup.exe" \
'{
version: $version,
notes: $notes,
pub_date: $pub_date,
platforms: {
"darwin-x86_64": {signature: $mac_x64_sig, url: $mac_x64_url},
"darwin-aarch64": {signature: $mac_arm64_sig, url: $mac_arm64_url},
"windows-x86_64": {signature: $windows_x64_sig, url: $windows_x64_url}
}
}' > latest.json
- name: Upload complete updater manifest
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ env.RELEASE_TAG }}
draft: true
files: latest.json
- name: Publish completed release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release edit "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --draft=false --latest