Version checks and downloads previously depended on GitHub Release. About & Updates now offers GitHub or a validated mirror source, keeps the selector beneath the startup-check switch, and disables it when startup checks are off. The website resolves platform downloads from a bounded OSS release index with a GitHub fallback. Tagged releases publish and verify immutable OSS assets through OIDC before switching the latest-version index; deployment requires the configured Alibaba Cloud environment variables and role. Release note: “关于与更新”新增 GitHub 与镜像节点选择,启动检查、手动检查和下载页使用同一可信来源;官网下载也可按系统、架构和安装包类型直接选择。
312 lines
10 KiB
YAML
312 lines
10 KiB
YAML
name: Cross-platform packages
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
push:
|
|
branches:
|
|
- main
|
|
tags:
|
|
- 'v*'
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: packages-${{ github.ref }}
|
|
cancel-in-progress: ${{ github.ref_type != 'tag' }}
|
|
|
|
jobs:
|
|
validate:
|
|
name: Validate source
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 20
|
|
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
- uses: actions/setup-node@v7
|
|
with:
|
|
node-version: 24
|
|
cache: npm
|
|
|
|
- name: Verify release tag version
|
|
if: github.ref_type == 'tag'
|
|
run: node -e "const p=require('./package.json'); const expected='v'+p.version; if(process.env.GITHUB_REF_NAME!==expected){throw new Error('Expected tag '+expected+', received '+process.env.GITHUB_REF_NAME)}"
|
|
|
|
- name: Verify bilingual release notes
|
|
if: github.ref_type == 'tag'
|
|
run: npm run release:notes:verify
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Run validators
|
|
run: |
|
|
npm test
|
|
npm run typecheck
|
|
npm run lint
|
|
|
|
- name: Build production bundle
|
|
run: npm run build:bundle
|
|
|
|
- name: Upload production bundle
|
|
if: github.event_name == 'workflow_dispatch' || github.ref_type == 'tag'
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: goodbuddy-production-bundle
|
|
path: out
|
|
if-no-files-found: error
|
|
retention-days: 1
|
|
|
|
package:
|
|
name: ${{ matrix.platform }} ${{ matrix.arch }}
|
|
if: github.event_name == 'workflow_dispatch' || github.ref_type == 'tag'
|
|
needs: validate
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- platform: windows
|
|
arch: x64
|
|
runner: windows-2025
|
|
- platform: windows
|
|
arch: arm64
|
|
runner: windows-2025
|
|
- platform: macos
|
|
arch: x64
|
|
runner: macos-15-intel
|
|
- platform: macos
|
|
arch: arm64
|
|
runner: macos-15
|
|
- platform: linux
|
|
arch: x64
|
|
runner: ubuntu-24.04
|
|
- platform: linux
|
|
arch: arm64
|
|
runner: ubuntu-24.04-arm
|
|
runs-on: ${{ matrix.runner }}
|
|
timeout-minutes: 75
|
|
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
- uses: actions/setup-node@v7
|
|
with:
|
|
node-version: 24
|
|
cache: npm
|
|
|
|
- name: Cache packaging toolsets
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: |
|
|
${{ runner.temp }}/electron
|
|
${{ runner.temp }}/electron-builder
|
|
key: packaging-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('package-lock.json') }}
|
|
restore-keys: packaging-${{ runner.os }}-${{ matrix.arch }}-
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
env:
|
|
ELECTRON_CACHE: ${{ runner.temp }}/electron
|
|
|
|
- name: Download production bundle
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: goodbuddy-production-bundle
|
|
path: out
|
|
|
|
- name: Build and verify release packages
|
|
run: npm run release:package -- --platform ${{ matrix.platform }} --arch ${{ matrix.arch }} --skip-build
|
|
env:
|
|
ELECTRON_CACHE: ${{ runner.temp }}/electron
|
|
ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/electron-builder
|
|
|
|
- name: Upload release packages
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: goodbuddy-${{ matrix.platform }}-${{ matrix.arch }}
|
|
path: dist/release/${{ matrix.platform }}-${{ matrix.arch }}
|
|
if-no-files-found: error
|
|
compression-level: 0
|
|
retention-days: 30
|
|
|
|
release:
|
|
name: Publish GitHub and OSS release
|
|
if: github.event_name == 'push' && github.ref_type == 'tag'
|
|
needs: package
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 35
|
|
environment:
|
|
name: aliyun-oss-release
|
|
permissions:
|
|
contents: write
|
|
actions: read
|
|
id-token: write
|
|
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/setup-node@v7
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Verify release tag
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
expected="v$(node -p "require('./package.json').version")"
|
|
test "$GITHUB_REF_NAME" = "$expected"
|
|
test "$(git rev-parse "refs/tags/$GITHUB_REF_NAME^{commit}")" = "$GITHUB_SHA"
|
|
|
|
- name: Prepare bilingual release notes
|
|
run: node build/release-notes.cjs --output release-notes.md
|
|
|
|
- name: Download Windows packages
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: goodbuddy-windows-*
|
|
path: dist/release-downloads
|
|
|
|
- name: Download macOS packages
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: goodbuddy-macos-*
|
|
path: dist/release-downloads
|
|
|
|
- name: Download Linux packages
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: goodbuddy-linux-*
|
|
path: dist/release-downloads
|
|
|
|
- name: Verify and aggregate release assets
|
|
run: node build/aggregate-release.cjs --input dist/release-downloads --output dist/release-upload
|
|
|
|
- name: Verify OSS release configuration
|
|
shell: bash
|
|
env:
|
|
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
|
|
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
|
|
OIDC_PROVIDER_ARN: ${{ vars.ALIYUN_OIDC_PROVIDER_ARN }}
|
|
ROLE_ARN: ${{ vars.ALIYUN_ROLE_ARN }}
|
|
run: |
|
|
set -euo pipefail
|
|
test -n "$OSS_BUCKET"
|
|
test -n "$OSS_ENDPOINT"
|
|
test -n "$OIDC_PROVIDER_ARN"
|
|
test -n "$ROLE_ARN"
|
|
case "$OSS_BUCKET" in
|
|
*[!a-z0-9-]*|'') echo "OSS Bucket 名称无效" >&2; exit 1 ;;
|
|
esac
|
|
case "$OSS_ENDPOINT" in
|
|
https://oss-*.aliyuncs.com) ;;
|
|
*) echo "OSS Endpoint 必须使用标准 HTTPS 地址" >&2; exit 1 ;;
|
|
esac
|
|
case "$OIDC_PROVIDER_ARN" in
|
|
acs:ram::*:oidc-provider/*) ;;
|
|
*) echo "OIDC Provider ARN 无效" >&2; exit 1 ;;
|
|
esac
|
|
case "$ROLE_ARN" in
|
|
acs:ram::*:role/*) ;;
|
|
*) echo "RAM Role ARN 无效" >&2; exit 1 ;;
|
|
esac
|
|
|
|
- name: Authenticate to Alibaba Cloud
|
|
uses: aliyun/configure-aliyun-credentials-action@v1
|
|
with:
|
|
role-to-assume: ${{ vars.ALIYUN_ROLE_ARN }}
|
|
oidc-provider-arn: ${{ vars.ALIYUN_OIDC_PROVIDER_ARN }}
|
|
role-session-name: goodbuddy-release-${{ github.run_id }}
|
|
role-session-expiration: 3600
|
|
audience: sts.aliyuncs.com
|
|
|
|
- name: Install ossutil
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
version="2.3.0"
|
|
archive="$RUNNER_TEMP/ossutil.zip"
|
|
directory="$RUNNER_TEMP/ossutil"
|
|
curl --fail --silent --show-error --location \
|
|
"https://gosspublic.alicdn.com/ossutil/v2/$version/ossutil-$version-linux-amd64.zip" \
|
|
--output "$archive"
|
|
mkdir "$directory"
|
|
unzip -q "$archive" -d "$directory"
|
|
binary="$(find "$directory" -type f -name ossutil -print -quit)"
|
|
test -n "$binary"
|
|
chmod +x "$binary"
|
|
echo "$(dirname "$binary")" >> "$GITHUB_PATH"
|
|
|
|
- name: Prepare OSS website release index
|
|
id: oss-release
|
|
shell: bash
|
|
env:
|
|
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
|
|
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
|
|
run: |
|
|
set -euo pipefail
|
|
endpoint_host="${OSS_ENDPOINT#https://}"
|
|
base_url="https://${OSS_BUCKET}.${endpoint_host}/releases/${GITHUB_REF_NAME}/"
|
|
node build/create-site-release.cjs \
|
|
--manifest dist/release-upload/release-manifest.json \
|
|
--base-url "$base_url" \
|
|
--output dist/site-release.json
|
|
echo "base-url=$base_url" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Upload immutable release assets to OSS
|
|
shell: bash
|
|
env:
|
|
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
|
|
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
|
|
run: |
|
|
set -euo pipefail
|
|
export OSS_ACCESS_KEY_ID="$ALIBABA_CLOUD_ACCESS_KEY_ID"
|
|
export OSS_ACCESS_KEY_SECRET="$ALIBABA_CLOUD_ACCESS_KEY_SECRET"
|
|
export OSS_SESSION_TOKEN="$ALIBABA_CLOUD_SECURITY_TOKEN"
|
|
test -n "$OSS_ACCESS_KEY_ID"
|
|
test -n "$OSS_ACCESS_KEY_SECRET"
|
|
test -n "$OSS_SESSION_TOKEN"
|
|
for file in dist/release-upload/* dist/site-release.json; do
|
|
name="$(basename "$file")"
|
|
ossutil cp "$file" \
|
|
"oss://${OSS_BUCKET}/releases/${GITHUB_REF_NAME}/${name}" \
|
|
--endpoint "$OSS_ENDPOINT" \
|
|
--update
|
|
done
|
|
|
|
- name: Verify public OSS release assets
|
|
run: node build/verify-site-release.cjs --manifest dist/site-release.json
|
|
|
|
- name: Create or update draft GitHub release
|
|
shell: bash
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
set -euo pipefail
|
|
tag="$GITHUB_REF_NAME"
|
|
version="$(node -p "require('./package.json').version")"
|
|
if gh release view "$tag" >/dev/null 2>&1; then
|
|
gh release edit "$tag" --draft --title "GoodBuddy $version" --notes-file release-notes.md
|
|
else
|
|
gh release create "$tag" --draft --verify-tag --title "GoodBuddy $version" --notes-file release-notes.md
|
|
fi
|
|
gh release upload "$tag" dist/release-upload/* --clobber
|
|
gh release edit "$tag" --draft=false --latest
|
|
|
|
- name: Point website to verified OSS release
|
|
shell: bash
|
|
env:
|
|
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
|
|
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
|
|
run: |
|
|
set -euo pipefail
|
|
export OSS_ACCESS_KEY_ID="$ALIBABA_CLOUD_ACCESS_KEY_ID"
|
|
export OSS_ACCESS_KEY_SECRET="$ALIBABA_CLOUD_ACCESS_KEY_SECRET"
|
|
export OSS_SESSION_TOKEN="$ALIBABA_CLOUD_SECURITY_TOKEN"
|
|
ossutil cp dist/site-release.json \
|
|
"oss://${OSS_BUCKET}/releases/latest.json" \
|
|
--endpoint "$OSS_ENDPOINT" \
|
|
--force
|