mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-22 19:41:13 +00:00
* feat: AI translations at release, 7 new languages and regional fallback * fix: proper plural forms for count strings and full translations * fix: plural forms for selected, hidden and active counts * fix: synced plugin updates failing on desktop and server-only plugins shown as stopped * fix: restore rounded corners on app icons and favicon * feat: desktop app auto updates with automatic, ask first or never modes * feat: community and custom plugin registries * feat: plugin guide for AI tools in the SDK with a lint check * fix: keep the terminal WebSocket open past nginx's 60s default (#1588) * chore: update changelog * feat: show plugin download counts only and stop CI builds inflating them * feat: hide installed plugins in the browse tab by default * chore: pin bundled plugins to latest releases * chore: update translations * fix: focusing a saved password field and saving no longer erases it * fix: tell desktop users when their 2.8 data needs 2.9 first * fix: point windows update info at the x64 installer * fix: keep desktop client tunnel sockets alive behind proxies * chore: pin bundled plugins to latest releases * fix: cover client tunnel keepalive in the c2s runtime tests * fix: typing after opening a host no longer goes to the host search * fix: accept tab and = separators in SSH config import (#1598) * chore: update changelog * chore: drop plugin versions from core changelog * chore: update translations * chore: lint, format, and bump version to 26.10.1 --------- Co-authored-by: Darnel Hunter <68697450+innotelinc@users.noreply.github.com> Co-authored-by: Iain <Iain1970@protonmail.com>
675 lines
26 KiB
YAML
675 lines
26 KiB
YAML
name: Release
|
|
|
|
# Stable: run by hand from a dev-X.Y.Z branch. Beta: run by hand from a dev
|
|
# branch, or let the Tuesday and Friday schedule snapshot the newest dev
|
|
# branch when it moved since the last beta.
|
|
|
|
on:
|
|
schedule:
|
|
- cron: "0 15 * * 2,5"
|
|
workflow_dispatch:
|
|
inputs:
|
|
channel:
|
|
description: "Stable release or beta"
|
|
required: true
|
|
default: Stable
|
|
type: choice
|
|
options:
|
|
- Stable
|
|
- Beta
|
|
mode:
|
|
description: "Release mode (Overwrite release is stable only)"
|
|
required: true
|
|
default: Everything
|
|
type: choice
|
|
options:
|
|
- Everything
|
|
- Overwrite release
|
|
- Dry run
|
|
- Skip submit
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
concurrency:
|
|
group: release
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
prep:
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
outputs:
|
|
channel: ${{ steps.mode.outputs.channel }}
|
|
mode: ${{ steps.mode.outputs.mode }}
|
|
skip: ${{ steps.beta.outputs.skip || 'false' }}
|
|
version: ${{ steps.info.outputs.version || steps.beta.outputs.version }}
|
|
release_tag: ${{ steps.info.outputs.release_tag || steps.beta.outputs.release_tag }}
|
|
mobile_version: ${{ steps.info.outputs.mobile_version }}
|
|
mobile_tag: ${{ steps.info.outputs.mobile_tag }}
|
|
dev_branch: ${{ steps.info.outputs.dev_branch || steps.beta.outputs.dev_branch }}
|
|
sha: ${{ steps.beta.outputs.sha }}
|
|
prev_ref: ${{ steps.beta.outputs.prev_ref }}
|
|
steps:
|
|
- name: Resolve channel and mode
|
|
id: mode
|
|
run: |
|
|
CHANNEL="${{ inputs.channel || 'Beta' }}"
|
|
MODE="${{ inputs.mode || 'Everything' }}"
|
|
echo "channel=$CHANNEL" >> "$GITHUB_OUTPUT"
|
|
echo "mode=$MODE" >> "$GITHUB_OUTPUT"
|
|
if [ "$CHANNEL" = "Beta" ] && [ "$MODE" = "Overwrite release" ]; then
|
|
echo "Overwrite release is for stable releases only."
|
|
exit 1
|
|
fi
|
|
|
|
- name: Guard branch (release modes)
|
|
if: ${{ github.event_name == 'workflow_dispatch' && steps.mode.outputs.mode != 'Overwrite release' && !startsWith(github.ref, 'refs/heads/dev-') }}
|
|
run: |
|
|
echo "This mode must be run from a dev branch (got ${{ github.ref }})."
|
|
exit 1
|
|
|
|
- name: Guard branch (overwrite mode)
|
|
if: ${{ steps.mode.outputs.mode == 'Overwrite release' && github.ref != 'refs/heads/main' }}
|
|
run: |
|
|
echo "Overwrite release must be run from main (got ${{ github.ref }})."
|
|
exit 1
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 1
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version-file: ".nvmrc"
|
|
|
|
- name: Resolve versions
|
|
id: info
|
|
if: ${{ steps.mode.outputs.channel == 'Stable' }}
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
if [ "${{ steps.mode.outputs.mode }}" = "Overwrite release" ]; then
|
|
DEV_BRANCH=""
|
|
VERSION=$(node -p "require('./package.json').version")
|
|
else
|
|
DEV_BRANCH="${GITHUB_REF#refs/heads/}"
|
|
VERSION=$(node scripts/parse-dev-branch.cjs "$DEV_BRANCH")
|
|
fi
|
|
echo "dev_branch=$DEV_BRANCH" >> "$GITHUB_OUTPUT"
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "release_tag=v$VERSION" >> "$GITHUB_OUTPUT"
|
|
|
|
MOBILE_RAW=$(gh release view -R Termix-SSH/Mobile --json tagName -q .tagName)
|
|
MOBILE_VERSION=$(echo "$MOBILE_RAW" | sed -E 's/^release-//; s/-tag$//; s/^v//')
|
|
if [ -z "$MOBILE_VERSION" ]; then
|
|
echo "Failed to resolve the latest Termix-SSH/Mobile release version."
|
|
exit 1
|
|
fi
|
|
echo "mobile_version=$MOBILE_VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "mobile_tag=$MOBILE_RAW" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Check CHANGELOG.md has notes for this version
|
|
if: ${{ steps.mode.outputs.channel == 'Stable' }}
|
|
run: |
|
|
node scripts/generate-release-body.cjs \
|
|
--version "${{ steps.info.outputs.version }}" \
|
|
--mobile-version "${{ steps.info.outputs.mobile_version }}" \
|
|
--mobile-tag "${{ steps.info.outputs.mobile_tag }}" > /dev/null
|
|
|
|
- name: Resolve beta version
|
|
id: beta
|
|
if: ${{ steps.mode.outputs.channel == 'Beta' }}
|
|
env:
|
|
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "schedule" ]; then
|
|
REFS=$(gh api "repos/${{ github.repository }}/branches" --paginate -q '.[].name')
|
|
if ! DEV_BRANCH=$(printf '%s\n' "$REFS" | node scripts/latest-dev-branch.cjs 2>/dev/null); then
|
|
echo "No dev-X.Y.Z branch is open, so no beta today."
|
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
else
|
|
DEV_BRANCH="${GITHUB_REF#refs/heads/}"
|
|
fi
|
|
|
|
BASE=$(node scripts/parse-dev-branch.cjs "$DEV_BRANCH")
|
|
SHA=$(gh api "repos/${{ github.repository }}/branches/$DEV_BRANCH" -q .commit.sha)
|
|
TAGS=$(gh api "repos/${{ github.repository }}/git/matching-refs/tags/v$BASE-beta." -q '.[].ref' | sed 's#^refs/tags/##')
|
|
LATEST=$(printf '%s\n' "$TAGS" | node scripts/beta-version.cjs "$BASE" --latest)
|
|
VERSION=$(printf '%s\n' "$TAGS" | node scripts/beta-version.cjs "$BASE")
|
|
|
|
if [ -n "$LATEST" ]; then
|
|
LATEST_SHA=$(gh api "repos/${{ github.repository }}/commits/$LATEST" -q .sha)
|
|
if [ "$LATEST_SHA" = "$SHA" ] && [ "${{ github.event_name }}" = "schedule" ]; then
|
|
echo "$DEV_BRANCH has not moved since $LATEST, so no beta today."
|
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
PREV="$LATEST"
|
|
else
|
|
PREV=$(gh release view --repo ${{ github.repository }} --json tagName -q .tagName 2>/dev/null || true)
|
|
fi
|
|
|
|
echo "Beta $VERSION from $DEV_BRANCH @ $SHA, changes since ${PREV:-nothing}"
|
|
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
echo "dev_branch=$DEV_BRANCH" >> "$GITHUB_OUTPUT"
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "release_tag=v$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
|
echo "prev_ref=$PREV" >> "$GITHUB_OUTPUT"
|
|
|
|
# Translates new or changed strings with the Termix LLM server and commits
|
|
# them to the dev branch, so the beta or stable build includes them. If the
|
|
# server is down it warns and the release goes on with what is there.
|
|
translate:
|
|
needs: [prep]
|
|
if: ${{ needs.prep.outputs.skip != 'true' && needs.prep.outputs.mode != 'Overwrite release' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
outputs:
|
|
sha: ${{ steps.commit.outputs.sha }}
|
|
steps:
|
|
- name: Checkout dev branch
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ needs.prep.outputs.sha || needs.prep.outputs.dev_branch }}
|
|
fetch-depth: 1
|
|
token: ${{ secrets.TERMIX_PAT }}
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version-file: ".nvmrc"
|
|
|
|
- name: Translate
|
|
uses: Termix-SSH/Termix-Registry/translate@main
|
|
with:
|
|
source: src/ui/locales/en.json
|
|
out: src/ui/locales/translated
|
|
languages: src/ui/locales/languages.json
|
|
api-key: ${{ secrets.TERMIX_LLM_API_KEY }}
|
|
|
|
- name: Commit translations
|
|
id: commit
|
|
run: |
|
|
git add src/ui/locales
|
|
if git diff --cached --quiet; then
|
|
echo "No translation changes."
|
|
elif [ "${{ needs.prep.outputs.mode }}" = "Dry run" ]; then
|
|
echo "DRY RUN: would commit these translation changes:"
|
|
git --no-pager diff --cached --stat
|
|
else
|
|
git config user.name "LukeGus"
|
|
git config user.email "bugattiguy527@gmail.com"
|
|
git commit -m "chore: update translations"
|
|
git push origin HEAD:"${{ needs.prep.outputs.dev_branch }}"
|
|
fi
|
|
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
|
|
|
verify-beta:
|
|
needs: [prep, translate]
|
|
if: ${{ needs.prep.outputs.channel == 'Beta' && needs.prep.outputs.skip != 'true' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- name: Checkout beta commit
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ needs.translate.outputs.sha || needs.prep.outputs.sha }}
|
|
fetch-depth: 1
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version-file: ".nvmrc"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
# Bundled plugins are counted as downloads in the plugin store, so a
|
|
# build only fetches them when the pins change.
|
|
- name: Cache bundled plugins
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: node_modules/.cache/termix-bundled-plugins
|
|
key: bundled-plugins-${{ hashFiles('docker/bundled-plugins.json') }}
|
|
enableCrossOsArchive: true
|
|
|
|
- name: Build plugin SDK
|
|
run: npm run build:sdk
|
|
|
|
- name: Lint
|
|
run: npm run lint
|
|
|
|
- name: Type check
|
|
run: npm run type-check
|
|
|
|
- name: Build
|
|
run: npm run build
|
|
|
|
- name: Run unit tests
|
|
run: npm run test
|
|
|
|
normalize:
|
|
needs: [prep, translate]
|
|
if: ${{ needs.prep.outputs.channel == 'Stable' && needs.prep.outputs.mode != 'Overwrite release' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- name: Checkout dev branch
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ needs.prep.outputs.dev_branch }}
|
|
fetch-depth: 0
|
|
token: ${{ secrets.TERMIX_PAT }}
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version-file: ".nvmrc"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
# Manifest validation in lint loads the built SDK.
|
|
- name: Build SDK
|
|
run: npm run build:sdk
|
|
|
|
- name: Lint and format
|
|
run: |
|
|
npm run lint:fix
|
|
npm run format
|
|
npm run lint
|
|
|
|
- name: Run unit tests
|
|
run: npm run test
|
|
|
|
- name: Sync version
|
|
run: node scripts/sync-version.cjs --version "${{ needs.prep.outputs.version }}"
|
|
|
|
- name: Commit changes to dev branch
|
|
run: |
|
|
git config user.name "LukeGus"
|
|
git config user.email "bugattiguy527@gmail.com"
|
|
|
|
if git diff --quiet; then
|
|
echo "No lint/format/version changes to commit."
|
|
exit 0
|
|
fi
|
|
|
|
if [ "${{ needs.prep.outputs.mode }}" = "Dry run" ]; then
|
|
echo "DRY RUN: would commit and push the following changes to ${{ needs.prep.outputs.dev_branch }}:"
|
|
git --no-pager diff --stat
|
|
exit 0
|
|
fi
|
|
|
|
git add -A
|
|
git commit -m "chore: lint, format, and bump version to ${{ needs.prep.outputs.version }}"
|
|
git push origin HEAD:"${{ needs.prep.outputs.dev_branch }}"
|
|
|
|
sync-main:
|
|
needs: [prep, normalize]
|
|
if: ${{ needs.prep.outputs.channel == 'Stable' && needs.prep.outputs.mode != 'Dry run' && needs.prep.outputs.mode != 'Overwrite release' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- name: Checkout dev branch
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ needs.prep.outputs.dev_branch }}
|
|
fetch-depth: 0
|
|
token: ${{ secrets.TERMIX_PAT }}
|
|
|
|
- name: Merge main into dev branch
|
|
run: |
|
|
git config user.name "LukeGus"
|
|
git config user.email "bugattiguy527@gmail.com"
|
|
git fetch origin main
|
|
# -X ours leaves modify/delete conflicts, so settle those to the dev side too
|
|
if ! git merge origin/main -X ours --no-edit; then
|
|
git diff --name-only --diff-filter=U | while read -r f; do
|
|
if git cat-file -e "HEAD:$f" 2>/dev/null; then
|
|
git checkout --ours -- "$f" && git add -- "$f"
|
|
else
|
|
git rm -q -- "$f"
|
|
fi
|
|
done
|
|
git commit --no-edit
|
|
fi
|
|
git push origin HEAD:"${{ needs.prep.outputs.dev_branch }}"
|
|
|
|
merge-to-main:
|
|
needs: [prep, translate, normalize, sync-main]
|
|
if: ${{ always() && needs.prep.result == 'success' && needs.prep.outputs.channel == 'Stable' && (needs.translate.result == 'success' || needs.translate.result == 'skipped') && (needs.normalize.result == 'success' || needs.normalize.result == 'skipped') && (needs.sync-main.result == 'success' || needs.sync-main.result == 'skipped') }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
outputs:
|
|
main_sha: ${{ steps.merge.outputs.main_sha }}
|
|
build_ref: ${{ steps.merge.outputs.build_ref }}
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 1
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version-file: ".nvmrc"
|
|
|
|
- name: Generate PR body
|
|
id: body
|
|
run: |
|
|
node scripts/generate-release-body.cjs \
|
|
--version "${{ needs.prep.outputs.version }}" \
|
|
--mobile-version "${{ needs.prep.outputs.mobile_version }}" \
|
|
--mobile-tag "${{ needs.prep.outputs.mobile_tag }}" > PR_BODY.md
|
|
|
|
- name: Open and squash-merge PR to main
|
|
id: merge
|
|
env:
|
|
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
|
|
run: |
|
|
DEV_BRANCH="${{ needs.prep.outputs.dev_branch }}"
|
|
TITLE="v${{ needs.prep.outputs.version }}"
|
|
|
|
if [ "${{ needs.prep.outputs.mode }}" = "Overwrite release" ]; then
|
|
echo "OVERWRITE: no merge; building from main as-is."
|
|
echo "build_ref=main" >> "$GITHUB_OUTPUT"
|
|
echo "main_sha=$(gh api repos/${{ github.repository }}/commits/main -q .sha)" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
|
|
if [ "${{ needs.prep.outputs.mode }}" = "Dry run" ]; then
|
|
echo "DRY RUN: would open and squash-merge a PR from $DEV_BRANCH into main."
|
|
echo "DRY RUN: downstream jobs will build from $DEV_BRANCH instead of main."
|
|
echo "build_ref=$DEV_BRANCH" >> "$GITHUB_OUTPUT"
|
|
echo "main_sha=" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
|
|
PR_NUMBER=$(gh pr list --repo ${{ github.repository }} --head "$DEV_BRANCH" --base main --state open --json number -q '.[0].number' || true)
|
|
if [ -z "$PR_NUMBER" ]; then
|
|
PR_NUMBER=$(gh pr create --repo ${{ github.repository }} \
|
|
--base main --head "$DEV_BRANCH" \
|
|
--title "$TITLE" --body-file PR_BODY.md \
|
|
| grep -oE '[0-9]+$')
|
|
fi
|
|
|
|
gh pr merge "$PR_NUMBER" --repo ${{ github.repository }} --squash --admin
|
|
|
|
STATE=$(gh pr view "$PR_NUMBER" --repo ${{ github.repository }} --json state -q .state)
|
|
if [ "$STATE" != "MERGED" ]; then
|
|
echo "PR #$PR_NUMBER did not merge (state: $STATE)."
|
|
exit 1
|
|
fi
|
|
|
|
MAIN_SHA=$(gh api repos/${{ github.repository }}/commits/main -q .sha)
|
|
echo "main_sha=$MAIN_SHA" >> "$GITHUB_OUTPUT"
|
|
echo "build_ref=$MAIN_SHA" >> "$GITHUB_OUTPUT"
|
|
|
|
docker:
|
|
needs: [prep, translate, verify-beta, merge-to-main]
|
|
if: ${{ always() && needs.prep.result == 'success' && needs.prep.outputs.skip != 'true' && (needs.merge-to-main.result == 'success' || needs.verify-beta.result == 'success') }}
|
|
uses: ./.github/workflows/docker.yml
|
|
with:
|
|
version: ${{ needs.prep.outputs.version }}
|
|
build_type: ${{ needs.prep.outputs.channel == 'Beta' && 'Beta' || 'Production' }}
|
|
dry_run: ${{ needs.prep.outputs.mode == 'Dry run' }}
|
|
source_ref: ${{ needs.merge-to-main.outputs.build_ref || needs.translate.outputs.sha || needs.prep.outputs.sha }}
|
|
secrets: inherit
|
|
|
|
create-release:
|
|
needs: [prep, merge-to-main, docker]
|
|
if: ${{ always() && needs.prep.outputs.channel == 'Stable' && needs.docker.result == 'success' && needs.prep.outputs.mode != 'Dry run' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Checkout main
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: main
|
|
fetch-depth: 1
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version-file: ".nvmrc"
|
|
|
|
- name: Clear existing release artifacts (overwrite mode)
|
|
if: ${{ needs.prep.outputs.mode == 'Overwrite release' }}
|
|
env:
|
|
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
|
|
run: |
|
|
TAG="${{ needs.prep.outputs.release_tag }}"
|
|
if ! gh release view "$TAG" --repo ${{ github.repository }} >/dev/null 2>&1; then
|
|
echo "Overwrite release: no existing release $TAG to overwrite."
|
|
exit 1
|
|
fi
|
|
echo "Clearing existing assets from $TAG..."
|
|
gh release view "$TAG" --repo ${{ github.repository }} --json assets -q '.assets[].name' | while read -r ASSET; do
|
|
[ -n "$ASSET" ] && gh release delete-asset "$TAG" "$ASSET" --repo ${{ github.repository }} --yes || true
|
|
done
|
|
|
|
- name: Generate release body
|
|
if: ${{ needs.prep.outputs.mode != 'Overwrite release' }}
|
|
run: |
|
|
node scripts/generate-release-body.cjs \
|
|
--version "${{ needs.prep.outputs.version }}" \
|
|
--mobile-version "${{ needs.prep.outputs.mobile_version }}" \
|
|
--mobile-tag "${{ needs.prep.outputs.mobile_tag }}" > RELEASE_BODY.md
|
|
|
|
- name: Create or update GitHub release
|
|
if: ${{ needs.prep.outputs.mode != 'Overwrite release' }}
|
|
env:
|
|
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
|
|
run: |
|
|
TAG="${{ needs.prep.outputs.release_tag }}"
|
|
TITLE="Termix v${{ needs.prep.outputs.version }}"
|
|
if gh release view "$TAG" --repo ${{ github.repository }} >/dev/null 2>&1; then
|
|
gh release edit "$TAG" --repo ${{ github.repository }} --title "$TITLE" --notes-file RELEASE_BODY.md
|
|
else
|
|
gh release create "$TAG" --repo ${{ github.repository }} --title "$TITLE" --notes-file RELEASE_BODY.md --target "${{ needs.merge-to-main.outputs.main_sha }}"
|
|
fi
|
|
|
|
create-beta-release:
|
|
needs: [prep, translate, docker]
|
|
if: ${{ always() && needs.prep.outputs.channel == 'Beta' && needs.docker.result == 'success' && needs.prep.outputs.mode != 'Dry run' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Checkout beta commit
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ needs.translate.outputs.sha || needs.prep.outputs.sha }}
|
|
fetch-depth: 0
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version-file: ".nvmrc"
|
|
|
|
- name: Generate beta release body
|
|
run: |
|
|
node scripts/generate-beta-body.cjs \
|
|
--version "${{ needs.prep.outputs.version }}" \
|
|
--branch "${{ needs.prep.outputs.dev_branch }}" \
|
|
--sha "${{ needs.translate.outputs.sha || needs.prep.outputs.sha }}" \
|
|
--prev "${{ needs.prep.outputs.prev_ref }}" > BETA_BODY.md
|
|
|
|
- name: Create beta release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
|
|
run: |
|
|
VERSION="${{ needs.prep.outputs.version }}"
|
|
TITLE="Termix v${VERSION%-beta.*} Beta ${VERSION##*-beta.}"
|
|
TAG="${{ needs.prep.outputs.release_tag }}"
|
|
if gh release view "$TAG" --repo ${{ github.repository }} >/dev/null 2>&1; then
|
|
gh release edit "$TAG" --repo ${{ github.repository }} \
|
|
--title "$TITLE" --notes-file BETA_BODY.md --prerelease
|
|
else
|
|
gh release create "$TAG" --repo ${{ github.repository }} \
|
|
--title "$TITLE" --notes-file BETA_BODY.md \
|
|
--prerelease --target "${{ needs.translate.outputs.sha || needs.prep.outputs.sha }}"
|
|
fi
|
|
|
|
electron-release:
|
|
needs:
|
|
[
|
|
prep,
|
|
translate,
|
|
merge-to-main,
|
|
docker,
|
|
create-release,
|
|
create-beta-release,
|
|
]
|
|
if: ${{ always() && needs.prep.outputs.skip != 'true' && needs.docker.result == 'success' && needs.create-release.result != 'failure' && needs.create-beta-release.result != 'failure' }}
|
|
uses: ./.github/workflows/electron.yml
|
|
with:
|
|
build_type: all
|
|
artifact_destination: ${{ needs.prep.outputs.mode == 'Dry run' && 'file' || 'release' }}
|
|
release_tag: ${{ needs.prep.outputs.release_tag }}
|
|
version_override: ${{ needs.prep.outputs.channel == 'Beta' && needs.prep.outputs.version || '' }}
|
|
source_ref: ${{ needs.merge-to-main.outputs.build_ref || needs.translate.outputs.sha || needs.prep.outputs.sha }}
|
|
secrets: inherit
|
|
|
|
electron-submit:
|
|
needs: [prep, merge-to-main, electron-release]
|
|
if: ${{ always() && needs.electron-release.result == 'success' && needs.prep.outputs.channel == 'Stable' && needs.prep.outputs.mode != 'Dry run' && needs.prep.outputs.mode != 'Skip submit' }}
|
|
uses: ./.github/workflows/electron.yml
|
|
with:
|
|
build_type: all
|
|
artifact_destination: submit
|
|
source_ref: ${{ needs.merge-to-main.outputs.build_ref }}
|
|
secrets: inherit
|
|
|
|
cask-commit-back:
|
|
needs: [prep, electron-release]
|
|
if: ${{ always() && needs.electron-release.result == 'success' && needs.prep.outputs.channel == 'Stable' && needs.prep.outputs.mode != 'Dry run' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Checkout main
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: main
|
|
fetch-depth: 1
|
|
token: ${{ secrets.TERMIX_PAT }}
|
|
|
|
- name: Bump and commit Homebrew cask
|
|
env:
|
|
VERSION: ${{ needs.prep.outputs.version }}
|
|
DMG_SHA256: ${{ needs.electron-release.outputs.macos_universal_dmg_sha256 }}
|
|
run: |
|
|
if [ -z "$DMG_SHA256" ]; then
|
|
echo "No universal DMG checksum available (macOS build unsigned or skipped); leaving cask unchanged."
|
|
exit 0
|
|
fi
|
|
|
|
git config user.name "LukeGus"
|
|
git config user.email "bugattiguy527@gmail.com"
|
|
|
|
git fetch origin main
|
|
git checkout -B main origin/main
|
|
|
|
sed -i "s|version \".*\"|version \"$VERSION\"|g" Casks/termix.rb
|
|
sed -i "s|sha256 \".*\"|sha256 \"$DMG_SHA256\"|g" Casks/termix.rb
|
|
|
|
git add Casks/termix.rb
|
|
if git diff --cached --quiet; then
|
|
echo "Cask already up to date."
|
|
exit 0
|
|
fi
|
|
|
|
git commit -m "chore: bump Homebrew cask to $VERSION"
|
|
git push origin HEAD:main
|
|
|
|
prune-betas:
|
|
needs: [prep, create-release]
|
|
if: ${{ always() && needs.prep.outputs.channel == 'Stable' && needs.create-release.result == 'success' && needs.prep.outputs.mode != 'Overwrite release' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- name: Delete this version's beta releases (tags stay)
|
|
env:
|
|
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
|
|
run: |
|
|
PREFIX="v${{ needs.prep.outputs.version }}-beta."
|
|
gh release list --repo ${{ github.repository }} --limit 200 --json tagName -q '.[].tagName' \
|
|
| grep -F "$PREFIX" | while read -r TAG; do
|
|
echo "Deleting release $TAG"
|
|
gh release delete "$TAG" --repo ${{ github.repository }} --yes || true
|
|
done
|
|
|
|
# The docs site reads the env catalog, API spec and download list from the
|
|
# newest stable release.
|
|
# Docs written for this release live on dev-X.Y.Z in the Docs repo. Squash
|
|
# merge it into main once the release is out. No branch means nothing to do.
|
|
docs-merge:
|
|
needs: [prep, electron-release]
|
|
if: ${{ always() && needs.prep.outputs.channel == 'Stable' && needs.electron-release.result == 'success' && needs.prep.outputs.mode != 'Dry run' && needs.prep.outputs.mode != 'Overwrite release' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- name: Merge the docs dev branch into main
|
|
env:
|
|
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
|
|
REPO: Termix-SSH/Docs
|
|
VERSION: ${{ needs.prep.outputs.version }}
|
|
run: |
|
|
BRANCH="dev-$VERSION"
|
|
if ! gh api "repos/$REPO/branches/$BRANCH" > /dev/null 2>&1; then
|
|
echo "$REPO has no $BRANCH branch, nothing to merge."
|
|
exit 0
|
|
fi
|
|
|
|
AHEAD=$(gh api "repos/$REPO/compare/main...$BRANCH" -q .ahead_by)
|
|
if [ "$AHEAD" = "0" ]; then
|
|
echo "$BRANCH has nothing main doesn't, deleting it."
|
|
gh api -X DELETE "repos/$REPO/git/refs/heads/$BRANCH"
|
|
exit 0
|
|
fi
|
|
|
|
PR_NUMBER=$(gh pr list --repo "$REPO" --head "$BRANCH" --base main --state open --json number -q '.[0].number')
|
|
if [ -z "$PR_NUMBER" ]; then
|
|
PR_NUMBER=$(gh pr create --repo "$REPO" --base main --head "$BRANCH" \
|
|
--title "v$VERSION" --body "Docs for Termix v$VERSION." | grep -oE '[0-9]+$')
|
|
fi
|
|
|
|
gh pr merge "$PR_NUMBER" --repo "$REPO" --squash --admin --delete-branch
|
|
|
|
STATE=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json state -q .state)
|
|
if [ "$STATE" != "MERGED" ]; then
|
|
echo "Docs PR #$PR_NUMBER did not merge (state: $STATE)."
|
|
exit 1
|
|
fi
|
|
|
|
docs-refresh:
|
|
needs: [prep, electron-release, docs-merge]
|
|
if: ${{ always() && needs.prep.outputs.channel == 'Stable' && needs.electron-release.result == 'success' && needs.prep.outputs.mode != 'Dry run' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- name: Tell the docs site
|
|
env:
|
|
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
|
|
run: gh api repos/Termix-SSH/Docs/dispatches -f event_type=core-released
|
|
|
|
cleanup:
|
|
needs: [prep, merge-to-main, electron-release, cask-commit-back]
|
|
if: ${{ always() && needs.prep.outputs.channel == 'Stable' && (needs.prep.outputs.mode == 'Everything' || needs.prep.outputs.mode == 'Skip submit') && needs.merge-to-main.result == 'success' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 1
|
|
persist-credentials: false
|
|
|
|
- name: Delete dev branch in Termix
|
|
env:
|
|
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
|
|
run: |
|
|
git push https://x-access-token:${{ secrets.TERMIX_PAT }}@github.com/${{ github.repository }}.git \
|
|
--delete "${{ needs.prep.outputs.dev_branch }}" || true
|