Skip to content

publish

publish #84

Workflow file for this run

name: "publish"
on:
workflow_dispatch:
inputs:
environment:
description: "Release Environment"
type: choice
required: true
options:
- development
- production
- staging
default: production
release_type:
description: "Release Type"
type: choice
# Not generally needed for production releases
required: false
options:
- patch
- minor
- major
default: patch
jobs:
create-release:
permissions:
contents: write
runs-on: ubuntu-latest
outputs:
release_id: ${{ steps.create-release.outputs.result }}
new_version: ${{ steps.handle-rc.outputs.new_version || steps.version.outputs.new_version }}
steps:
- uses: actions/checkout@v5
- name: handle release candidate new version
id: handle-rc
uses: actions/github-script@v8
if: ${{ inputs.environment != 'production' }}
env:
RELEASE_TYPE: ${{ inputs.release_type }}
ENVIRONMENT: ${{ inputs.environment }}
with:
script: |
// Version of current production release
const { version } = require('./src-tauri/tauri.conf.json')
const [major, minor, patch] = version.split('.').map(Number)
let newVersion
if (process.env.RELEASE_TYPE === 'major') {
newVersion = `${major + 1}.0.0`
} else if (process.env.RELEASE_TYPE === 'minor') {
newVersion = `${major}.${minor + 1}.0`
} else {
newVersion = `${major}.${minor}.${patch + 1}`
}
// If previous RC for given release type already exists, increment its RC number
const releases = await github.rest.repos.listReleases({
owner: context.repo.owner,
repo: context.repo.repo,
})
const rcReleases = releases.data.filter(release =>
release.tag_name.startsWith(`${process.env.ENVIRONMENT}/${newVersion}-rc.`)
)
if (rcReleases.length > 0) {
// Get highest RC number
const rcNumbers = rcReleases.map(release => {
const match = release.tag_name.match(/rc\.(\d+)$/)
return match ? parseInt(match[1], 10) : 0
})
const highestRc = Math.max(...rcNumbers)
newVersion += `-rc.${highestRc + 1}`
} else {
newVersion += `-rc.0`
}
core.setOutput('new_version', newVersion)
console.log(`Setting release candidate version to ${newVersion}`)
- name: get version
id: version
uses: actions/github-script@v8
with:
script: |
const { version } = require('./src-tauri/tauri.conf.json')
core.setOutput('version', version)
- name: create release
id: create-release
uses: actions/github-script@v8
env:
ENVIRONMENT: ${{inputs.environment}}
NEW_VERSION: ${{ steps.handle-rc.outputs.new_version || steps.version.outputs.version }}
with:
# TODO: see if release already exists (e.g. re-run of failed workflow), then re-use it
script: |
const { data } = await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: `${process.env.ENVIRONMENT}/${process.env.NEW_VERSION}`,
name: `v${process.env.NEW_VERSION}/${process.env.ENVIRONMENT}`,
body: `This release is for the ${process.env.ENVIRONMENT} environment.`,
draft: true,
prerelease: process.env.ENVIRONMENT != "production"
})
return data.id
build-tauri:
needs: create-release
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- platform: "macos-latest"
target: "aarch64-apple-darwin"
bundles: "dmg,app"
- platform: "macos-latest"
target: "x86_64-apple-darwin"
bundles: "dmg,app"
- platform: "ubuntu-22.04"
bundles: "appimage,updater"
- platform: "ubuntu-22.04"
target: "i686-unknown-linux-gnu"
bundles: "appimage,updater"
# Disabled, until needed
# - platform: "windows-latest"
# config: "src-tauri/tauri.microsoftstore.conf.json"
# bundles: "msi"
- platform: "windows-latest"
bundles: "nsis"
- platform: "windows-latest"
target: "i686-pc-windows-msvc"
bundles: "nsis"
runs-on: ${{ matrix.platform }}
env:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
steps:
- uses: actions/checkout@v5
- name: handle release candidate new version
uses: actions/github-script@v8
if: ${{ inputs.environment != 'production' }}
env:
NEW_VERSION: ${{ needs.create-release.outputs.new_version }}
with:
script: |
const newVersion = process.env.NEW_VERSION
// update tauri.conf.json with new RC version
const fs = require('fs')
const tauriConf = require('./src-tauri/tauri.conf.json')
tauriConf.version = newVersion
fs.writeFileSync('./src-tauri/tauri.conf.json', JSON.stringify(tauriConf, null, 2) + '\n')
// update package.json version
const packageJson = require('./package.json')
packageJson.version = newVersion
fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2) + '\n')
// update Cargo.toml version
const cargoToml = fs.readFileSync('./src-tauri/Cargo.toml', 'utf8')
const updatedCargoToml = cargoToml.replace(/^version = ".*"/m, `version = "${newVersion}"`)
fs.writeFileSync('./src-tauri/Cargo.toml', updatedCargoToml)
- name: setup node
uses: actions/setup-node@v6
with:
node-version: 22.x
- name: Set FREECODECAMP_API based on environment
run: |
if [ "${{ inputs.environment }}" = "development" ]; then
echo "FREECODECAMP_API=http://localhost:3000" >> $GITHUB_ENV
elif [ "${{ inputs.environment }}" = "staging" ]; then
echo "FREECODECAMP_API=https://api.freecodecamp.dev" >> $GITHUB_ENV
elif [ "${{ inputs.environment }}" = "production" ]; then
echo "FREECODECAMP_API=https://api.freecodecamp.org" >> $GITHUB_ENV
fi
shell: bash
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: get version
id: version
uses: actions/github-script@v8
with:
script: |
const { version } = require('./src-tauri/tauri.conf.json')
core.setOutput('version', version)
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target || '' }}
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-22.04' # This must match the platform value defined above.
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: install 32-bit dependencies (ubuntu i686 only)
if: matrix.platform == 'ubuntu-22.04' && matrix.target == 'i686-unknown-linux-gnu'
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib
sudo apt-get install -y libwebkit2gtk-4.1-dev:i386 librsvg2-dev:i386
sudo apt-get install -y libgtk-3-dev:i386 libssl-dev:i386
# Set up pkg-config for cross-compilation
echo "PKG_CONFIG_ALLOW_CROSS=1" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig" >> $GITHUB_ENV
echo "PKG_CONFIG_LIBDIR=/usr/lib/i386-linux-gnu/pkgconfig:/usr/share/pkgconfig" >> $GITHUB_ENV
- name: install frontend dependencies
run: bun install && bun run prisma generate
# The rust build requires the `.env` file to exist, even if none of the variables are used
- name: prep env (non-windows)
if: matrix.platform != 'windows-latest'
run: touch .env
- name: prep env (windows)
if: matrix.platform == 'windows-latest'
run: New-Item -Name ".env" -ItemType File
- name: install Go stable (windows)
if: matrix.platform == 'windows-latest'
uses: actions/setup-go@v4
with:
go-version: "stable"
- name: install relic (windows)
if: matrix.platform == 'windows-latest'
run: |
go install github.com/sassoftware/relic/v8@latest
- name: install codemagic cli tools (macos)
if: matrix.platform == 'macos-latest'
run: pip3 install codemagic-cli-tools --break-system-packages
- name: install apple certificates and provisioning profiles (macos)
if: matrix.platform == 'macos-latest'
env:
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
APPLE_DEVELOPER_ID_CERT: ${{ secrets.APPLE_DEVELOPER_ID_CERT }}
PROVISIONING_PROFILE: ${{ secrets.MAC_APP_DIRECT_PROVISIONING_PROFILE }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
run: |
# create variables
mkdir -p ~/.appstoreconnect/private_keys
CERT_BASE_PATH=/Users/runner/Library/MobileDevice/Certificates
mkdir -p $CERT_BASE_PATH
DEVELOPER_ID_CERT_PATH=$CERT_BASE_PATH/developer_id_certificate.p12
PP_PATH=./src-tauri/embedded.provisionprofile
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
APPLE_API_KEY_PATH=~/.appstoreconnect/private_keys/AuthKey_$APPLE_API_KEY_ID.p8
# import certificate and provisioning profile from secrets
echo -n "$APPLE_DEVELOPER_ID_CERT" | base64 --decode -o $DEVELOPER_ID_CERT_PATH
echo -n "$PROVISIONING_PROFILE" | base64 --decode -o $PP_PATH
echo -n "$APPLE_API_KEY" | base64 --decode -o $APPLE_API_KEY_PATH
# create temporary keychain
keychain initialize --password $KEYCHAIN_PASSWORD --path $KEYCHAIN_PATH --timeout 21600
# import certificate to keychain
keychain add-certificates
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security find-identity -v
- name: Set verbose flag if in debug mode
uses: actions/github-script@v8
env:
ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }}
with:
script: |
const isDebug = process.env.ACTIONS_STEP_DEBUG === 'true';
core.exportVariable('TAURI_VERBOSE_FLAG', isDebug ? '--verbose' : '');
- name: Build args
id: build-args
uses: actions/github-script@v8
with:
script: |
const target = '${{ matrix.target }}';
const bundles = '${{ matrix.bundles }}';
const config = '${{ matrix.config }}';
let args = [];
if (target) args.push(`--target ${target}`);
if (bundles) args.push(`--bundles ${bundles}`);
if (config) args.push(`--config ${config}`);
core.setOutput('args', args.join(' '));
- uses: tauri-apps/tauri-action@v0.6
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 }}
AZURE_VAULT_ID: ${{ secrets.AZURE_VAULT_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
# MacOS specific
APPLE_SIGNING_IDENTITY: "Developer ID Application: Free Code Camp, Inc. (L33K9LWVP9)"
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY_ID }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
FREECODECAMP_API: ${{ env.FREECODECAMP_API }}
ENVIRONMENT: ${{inputs.environment}}
with:
releaseId: ${{ needs.create-release.outputs.release_id }}
args: ${{ steps.build-args.outputs.args }} ${{ env.TAURI_VERBOSE_FLAG }}
includeDebug: ${{ inputs.environment != 'production' }}
includeRelease: ${{inputs.environment == 'production' }}
includeUpdaterJson: true
# tagName is required to prevent all update URLs from pointing to `/latest/`:
# https://github.com/freeCodeCamp/exam-env/issues/54
tagName: ${{ inputs.environment }}/${{ steps.version.outputs.version }}
# As the release is not published yet, this must be set: https://github.com/tauri-apps/tauri-action?tab=readme-ov-file#tips-and-caveats
releaseDraft: true
publish-release:
permissions:
contents: write
runs-on: ubuntu-latest
needs: [create-release, build-tauri]
steps:
- name: publish release
id: publish-release
uses: actions/github-script@v8
env:
release_id: ${{ needs.create-release.outputs.release_id }}
prerelease: ${{ inputs.environment != 'production' }}
REF: ${{ github.ref }}
with:
script: |
github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: process.env.release_id,
draft: false,
prerelease: process.env.prerelease == "true"
})