mirror of
https://github.com/immich-app/immich.git
synced 2026-04-25 10:39:50 -04:00
- prepare-release: add `branch` input; validate branch/bump combination; skip Weblate merge, mobile build, and APK asset when not on main; point checkout, release target, and tag at the selected branch; backport the archived-versions.json entry to main via PR. - build-mobile: gate Android release build and iOS TestFlight upload on `environment == production` instead of the branch name, so patch releases still produce production artifacts if ever re-enabled. - docker: build on pushes to release/**; restrict retag-from-main jobs to PRs and main-branch pushes. - docs-build: build on pushes to release/**; include release/** in the pre-job force-branches list.
249 lines
9.2 KiB
YAML
249 lines
9.2 KiB
YAML
name: Prepare new release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
branch:
|
|
description: 'Branch to release from (must be main or release/*)'
|
|
required: true
|
|
default: 'main'
|
|
type: string
|
|
serverBump:
|
|
description: 'Bump server version (only patch allowed on release/* branches)'
|
|
required: true
|
|
default: 'false'
|
|
type: choice
|
|
options:
|
|
- 'false'
|
|
- major
|
|
- minor
|
|
- patch
|
|
mobileBump:
|
|
description: 'Bump mobile build number'
|
|
required: false
|
|
type: boolean
|
|
skipTranslations:
|
|
description: 'Skip translations'
|
|
required: false
|
|
type: boolean
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}-root
|
|
cancel-in-progress: true
|
|
|
|
permissions: {}
|
|
|
|
jobs:
|
|
validate_inputs:
|
|
runs-on: ubuntu-latest
|
|
permissions: {}
|
|
steps:
|
|
- name: Validate branch and bump combination
|
|
env:
|
|
BRANCH: ${{ inputs.branch }}
|
|
SERVER_BUMP: ${{ inputs.serverBump }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [[ "$BRANCH" != "main" && "$BRANCH" != release/* ]]; then
|
|
echo "::error::branch must be 'main' or start with 'release/' (got '$BRANCH')"
|
|
exit 1
|
|
fi
|
|
if [[ "$BRANCH" != "main" && "$SERVER_BUMP" != "false" && "$SERVER_BUMP" != "patch" ]]; then
|
|
echo "::error::only 'patch' (or 'false') serverBump is allowed on '$BRANCH'"
|
|
exit 1
|
|
fi
|
|
|
|
merge_translations:
|
|
needs: [validate_inputs]
|
|
uses: ./.github/workflows/merge-translations.yml
|
|
with:
|
|
# Weblate tracks main only, so skip translations when releasing from a release/* branch.
|
|
skip: ${{ inputs.skipTranslations || inputs.branch != 'main' }}
|
|
permissions:
|
|
pull-requests: write
|
|
secrets:
|
|
PUSH_O_MATIC_APP_ID: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
|
PUSH_O_MATIC_APP_KEY: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
|
WEBLATE_TOKEN: ${{ secrets.WEBLATE_TOKEN }}
|
|
|
|
bump_version:
|
|
runs-on: ubuntu-latest
|
|
needs: [merge_translations]
|
|
outputs:
|
|
ref: ${{ steps.push-tag.outputs.commit_long_sha }}
|
|
version: ${{ steps.output.outputs.version }}
|
|
permissions: {} # No job-level permissions are needed because it uses the app-token
|
|
steps:
|
|
- name: Generate a token
|
|
id: generate-token
|
|
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
|
with:
|
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
|
|
|
- name: Checkout
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
token: ${{ steps.generate-token.outputs.token }}
|
|
persist-credentials: true
|
|
ref: ${{ inputs.branch }}
|
|
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@08c4be7e2e672a47d11bd04269e27e5f3e8529cb # v6.0.0
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
|
with:
|
|
node-version-file: './server/.nvmrc'
|
|
cache: 'pnpm'
|
|
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
|
|
- name: Bump version
|
|
env:
|
|
SERVER_BUMP: ${{ inputs.serverBump }}
|
|
MOBILE_BUMP: ${{ inputs.mobileBump }}
|
|
run: misc/release/pump-version.sh -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}"
|
|
|
|
- id: output
|
|
run: echo "version=$IMMICH_VERSION" >> $GITHUB_OUTPUT
|
|
|
|
- name: Commit and tag
|
|
id: push-tag
|
|
uses: EndBug/add-and-commit@290ea2c423ad77ca9c62ae0f5b224379612c0321 # v10.0.0
|
|
with:
|
|
default_author: github_actions
|
|
message: 'chore: version ${{ steps.output.outputs.version }}'
|
|
tag: ${{ steps.output.outputs.version }}
|
|
push: true
|
|
|
|
build_mobile:
|
|
# Mobile build numbers are monotonic per store; releasing from a release/* branch
|
|
# would collide with build numbers already shipped from main. Skip mobile on patch
|
|
# releases — handle mobile patches on main instead.
|
|
if: ${{ inputs.branch == 'main' }}
|
|
uses: ./.github/workflows/build-mobile.yml
|
|
needs: bump_version
|
|
permissions:
|
|
contents: read
|
|
secrets:
|
|
KEY_JKS: ${{ secrets.KEY_JKS }}
|
|
ALIAS: ${{ secrets.ALIAS }}
|
|
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
|
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
|
# iOS secrets
|
|
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
|
|
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
|
|
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
|
|
IOS_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }}
|
|
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
|
|
FASTLANE_TEAM_ID: ${{ secrets.FASTLANE_TEAM_ID }}
|
|
|
|
with:
|
|
ref: ${{ needs.bump_version.outputs.ref }}
|
|
environment: production
|
|
|
|
prepare_release:
|
|
runs-on: ubuntu-latest
|
|
needs: [build_mobile, bump_version]
|
|
# Run even when build_mobile is skipped (patch release from release/* branch).
|
|
if: ${{ always() && needs.bump_version.result == 'success' && (needs.build_mobile.result == 'success' || needs.build_mobile.result == 'skipped') }}
|
|
permissions:
|
|
actions: read # To download the app artifact
|
|
# No content permissions are needed because it uses the app-token
|
|
steps:
|
|
- name: Generate a token
|
|
id: generate-token
|
|
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
|
with:
|
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
|
|
|
- name: Checkout
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
token: ${{ steps.generate-token.outputs.token }}
|
|
persist-credentials: false
|
|
ref: ${{ needs.bump_version.outputs.ref }}
|
|
|
|
- name: Download APK
|
|
if: ${{ needs.build_mobile.result == 'success' }}
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: release-apk-signed
|
|
github-token: ${{ steps.generate-token.outputs.token }}
|
|
|
|
- name: Assemble release assets
|
|
id: assets
|
|
env:
|
|
HAS_APK: ${{ needs.build_mobile.result == 'success' }}
|
|
run: |
|
|
{
|
|
echo 'files<<EOF'
|
|
echo 'docker/docker-compose.yml'
|
|
echo 'docker/docker-compose.rootless.yml'
|
|
echo 'docker/example.env'
|
|
echo 'docker/hwaccel.ml.yml'
|
|
echo 'docker/hwaccel.transcoding.yml'
|
|
echo 'docker/prometheus.yml'
|
|
if [[ "$HAS_APK" == "true" ]]; then
|
|
echo '*.apk'
|
|
fi
|
|
echo 'EOF'
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Create draft release
|
|
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
|
with:
|
|
draft: true
|
|
tag_name: ${{ needs.bump_version.outputs.version }}
|
|
target_commitish: ${{ inputs.branch }}
|
|
token: ${{ steps.generate-token.outputs.token }}
|
|
generate_release_notes: true
|
|
body_path: misc/release/notes.tmpl
|
|
files: ${{ steps.assets.outputs.files }}
|
|
|
|
backport_archived_versions:
|
|
# When releasing from a release/* branch, the archived-versions.json update
|
|
# lives on that branch only. Open a PR to mirror the new entry onto main so
|
|
# main's docs keep a complete archive list.
|
|
if: ${{ inputs.branch != 'main' && needs.bump_version.result == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
needs: [bump_version, prepare_release]
|
|
permissions: {} # uses the app token
|
|
steps:
|
|
- name: Generate a token
|
|
id: generate-token
|
|
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
|
with:
|
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
|
|
|
- name: Checkout main
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
token: ${{ steps.generate-token.outputs.token }}
|
|
persist-credentials: false
|
|
ref: main
|
|
|
|
- name: Update archived versions on main
|
|
env:
|
|
VERSION: ${{ needs.bump_version.outputs.version }}
|
|
run: ./misc/release/archive-version.js "${VERSION#v}"
|
|
|
|
- name: Open backport PR
|
|
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
|
with:
|
|
token: ${{ steps.generate-token.outputs.token }}
|
|
branch: backport/archived-versions-${{ needs.bump_version.outputs.version }}
|
|
base: main
|
|
commit-message: 'chore(docs): archive ${{ needs.bump_version.outputs.version }}'
|
|
title: 'chore(docs): archive ${{ needs.bump_version.outputs.version }}'
|
|
body: |
|
|
Backports the `archived-versions.json` entry for ${{ needs.bump_version.outputs.version }},
|
|
released from `${{ inputs.branch }}`, so main's docs archive list stays complete.
|
|
add-paths: docs/static/archived-versions.json
|
|
delete-branch: true
|