Merge pull request #2165 from zhouyu123666/main #4
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |