Skip to content

Merge pull request #2165 from zhouyu123666/main #4

Merge pull request #2165 from zhouyu123666/main

Merge pull request #2165 from zhouyu123666/main #4

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*.*.*'
permissions:
contents: read
jobs:
verify:
name: Verify Release Inputs
runs-on: ubuntu-latest
if: github.repository == 'chaterm/Chaterm' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Validate release tag
shell: bash
run: |
VERSION=$(python3 -c "import json; print(json.load(open('package.json'))['version'])")
EXPECTED_TAG="v$VERSION"
if [ "$GITHUB_REF_NAME" != "$EXPECTED_TAG" ]; then
echo "Tag mismatch: expected $EXPECTED_TAG but got $GITHUB_REF_NAME" >&2
exit 1
fi
git fetch --no-tags origin main:refs/remotes/origin/main
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
echo "tag must point to a commit merged into main" >&2
exit 1
fi
build:
name: Build (${{ matrix.label }})
runs-on: ${{ matrix.os }}
needs: verify
if: github.repository == 'chaterm/Chaterm' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
label: Linux
artifact_name: release-linux-x64
build_commands: |
npm run build:linux:cn -- --publish never
npm run build:linux:global -- --publish never
artifact_paths: |
dist/cn/*.AppImage
dist/cn/*.deb
dist/global/*.AppImage
dist/global/*.deb
dist/*/latest-linux.yml
- os: windows-latest
label: Windows
artifact_name: release-windows-x64
build_commands: |
npm run build:win:cn -- --publish never
npm run build:win:global -- --publish never
artifact_paths: |
dist/cn/*setup-*.exe
dist/global/*setup-*.exe
dist/*/latest.yml
steps:
- name: Enable long paths (Windows)
if: runner.os == 'Windows'
run: git config --system core.longpaths true
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Use Node.js 22.x
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.0.0
with:
node-version: 22.x
cache: 'npm'
- name: Fix package-lock.json
run: node scripts/patch-package-lock.js
- name: Install dependencies
run: npm ci --prefer-offline
- name: Build release packages
shell: bash
run: ${{ matrix.build_commands }}
- name: Upload build artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0
with:
name: ${{ matrix.artifact_name }}
compression-level: 0
retention-days: 7
if-no-files-found: error
path: ${{ matrix.artifact_paths }}
build-macos:
name: Build (macOS)
runs-on: macos-latest
needs: verify
if: github.repository == 'chaterm/Chaterm' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
env:
CSC_LINK: ${{ secrets.MAC_CERT_BASE64 }}
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Validate macOS signing secrets
shell: bash
run: |
required_vars=(
CSC_LINK
CSC_KEY_PASSWORD
APPLE_ID
APPLE_APP_SPECIFIC_PASSWORD
APPLE_TEAM_ID
)
missing=()
for var in "${required_vars[@]}"; do
value="$(printf '%s' "${!var:-}" | tr -d '[:space:]')"
if [ -z "$value" ]; then
missing+=("$var")
fi
done
echo "macOS signing secret availability:"
echo " CSC_LINK: $([ -n "$(printf '%s' "${CSC_LINK:-}" | tr -d '[:space:]')" ] && echo true || echo false)"
echo " CSC_KEY_PASSWORD: $([ -n "$(printf '%s' "${CSC_KEY_PASSWORD:-}" | tr -d '[:space:]')" ] && echo true || echo false)"
echo " APPLE_ID: $([ -n "$(printf '%s' "${APPLE_ID:-}" | tr -d '[:space:]')" ] && echo true || echo false)"
echo " APPLE_APP_SPECIFIC_PASSWORD: $([ -n "$(printf '%s' "${APPLE_APP_SPECIFIC_PASSWORD:-}" | tr -d '[:space:]')" ] && echo true || echo false)"
echo " APPLE_TEAM_ID: $([ -n "$(printf '%s' "${APPLE_TEAM_ID:-}" | tr -d '[:space:]')" ] && echo true || echo false)"
if [ "${#missing[@]}" -gt 0 ]; then
echo "Missing required macOS signing secret(s): ${missing[*]}" >&2
exit 1
fi
- name: Use Node.js 22.x
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.0.0
with:
node-version: 22.x
cache: 'npm'
- name: Fix package-lock.json
run: node scripts/patch-package-lock.js
- name: Install dependencies
run: npm ci --prefer-offline
- name: Build release packages
shell: bash
run: |
npm run build:mac:cn -- --publish never
npm run build:mac:global -- --publish never
- name: Upload macOS x64 artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0
with:
name: release-macos-x64
compression-level: 0
retention-days: 7
if-no-files-found: error
path: |
dist/cn/*macos-x64.dmg
dist/cn/*macos-x64.zip
dist/global/*macos-x64.dmg
dist/global/*macos-x64.zip
dist/*/latest-mac.yml
- name: Upload macOS arm64 artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0
with:
name: release-macos-arm64
compression-level: 0
retention-days: 7
if-no-files-found: error
path: |
dist/cn/*macos-arm64.dmg
dist/cn/*macos-arm64.zip
dist/global/*macos-arm64.dmg
dist/global/*macos-arm64.zip
release:
name: Publish Draft Release
runs-on: ubuntu-latest
outputs:
hashes: ${{ steps.sign.outputs.hashes }}
needs:
- build
- build-macos
if: github.repository == 'chaterm/Chaterm' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Download Linux x64 artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-linux-x64
path: release-assets/release-linux-x64
- name: Download Windows x64 artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-windows-x64
path: release-assets/release-windows-x64
- name: Download macOS x64 artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-macos-x64
path: release-assets/release-macos-x64
- name: Download macOS arm64 artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-macos-arm64
path: release-assets/release-macos-arm64
- name: Prepare release upload directory
shell: bash
run: |
mkdir -p release-upload
count=0
while IFS= read -r -d '' file; do
rel_path="${file#release-assets/}"
target_name="$(basename "$file")"
if [[ "$target_name" == latest*.yml && "$rel_path" == *"/cn/"* ]]; then
case "$target_name" in
latest.yml)
target_name="latest-cn.yml"
;;
latest-mac.yml)
target_name="latest-cn-mac.yml"
;;
latest-linux.yml)
target_name="latest-cn-linux.yml"
;;
esac
fi
case "$target_name" in
chaterm-*.dmg|chaterm-*.zip|chaterm-*.AppImage|chaterm-*.deb|chaterm-*-setup-*.exe|latest*.yml)
;;
*)
echo "Skipping non-release asset: $rel_path"
continue
;;
esac
if [ -e "release-upload/$target_name" ]; then
echo "Duplicate release asset filename after filtering: $target_name from $rel_path" >&2
exit 1
fi
cp "$file" "release-upload/$target_name"
count=$((count + 1))
done < <(find release-assets -type f \( \
-name "*.dmg" -o \
-name "*.zip" -o \
-name "*.exe" -o \
-name "*.AppImage" -o \
-name "*.deb" -o \
-name "latest*.yml" \
\) -print0 | sort -z)
if [ "$count" -eq 0 ]; then
echo "No release assets found" >&2
exit 1
fi
echo "Prepared $count asset(s) for upload"
ls -la release-upload
- name: Install cosign
uses: sigstore/cosign-installer@f713795cb21599bc4e5c4b58cbad1da852d7eeb9 # v3
- name: Sign release assets
id: sign
env:
COSIGN_YES: 'true'
ARTIFACT_SOURCE_DIR: 'release-upload'
ARTIFACT_OUTPUT_DIR: 'signed-release-assets'
run: |
chmod +x scripts/sign-release-artifacts.sh
scripts/sign-release-artifacts.sh
- name: Verify SLSA subjects output
shell: bash
env:
HASHES: ${{ steps.sign.outputs.hashes }}
run: |
if [ -z "$HASHES" ]; then
echo "::error::sign step produced empty hashes output; SLSA provenance job would fail with exit 27" >&2
exit 1
fi
len=${#HASHES}
echo "SLSA subjects output length: $len base64 chars"
echo "slsa_subjects_length=$len" >> "$GITHUB_STEP_SUMMARY"
- name: Stage signatures and checksums for upload
shell: bash
run: |
shopt -s nullglob
staged=0
for f in signed-release-assets/*.sig \
signed-release-assets/SHA256SUMS.txt; do
[ -f "$f" ] || continue
cp "$f" "release-upload/$(basename "$f")"
staged=$((staged + 1))
done
if [ "$staged" -eq 0 ]; then
echo "No signature/checksum files staged for upload" >&2
exit 1
fi
echo "Staged $staged signature/checksum file(s) for release upload"
- name: Publish draft GitHub Release
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.4.1
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
draft: true
generate_release_notes: true
files: release-upload/*
overwrite_files: true
fail_on_unmatched_files: true
provenance:
name: Generate SLSA provenance
needs:
- release
if: github.repository == 'chaterm/Chaterm' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
permissions:
actions: read
id-token: write
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
with:
base64-subjects: ${{ needs.release.outputs.hashes }}
upload-assets: true
upload-tag-name: ${{ github.ref_name }}
draft-release: true