Files
goodbuddy/.github/workflows/packages.yml
T
mesalogo 607bb33249 feat: add managed SSH execution spaces
Add managed SSH Hosts and projects with signed side-by-side GoodBuddy Agent and OpenCode Runtime installation, Ask and Execute workspace access, a Main-owned model bridge, detached Agent lifecycle, durable ACP output recovery, and project activation UI.

Use protocol v2 with bounded FIFO frames over SSH instead of custom credit, priority, batching, or model-message chunking. Mismatched Host Agents now upgrade automatically, an exactly verified Node runtime is reused on-host, and local Portable builds can embed strictly verified Agent artifacts.

Release note: 新增托管 SSH 远程项目,可自动安装或升级签名 Agent/OpenCode Runtime,并在 SSH 断线后继续 Agent 工作和同步未确认输出。
2026-08-24 23:02:18 +08:00

677 lines
24 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.19.0
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.ref_type == 'tag' ||
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
uses: actions/upload-artifact@v7
with:
name: goodbuddy-production-bundle
path: out
if-no-files-found: error
retention-days: 1
authorize-agent-signing:
name: Authorize production Agent signing
if: >-
github.ref_type == 'tag' ||
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
needs: validate
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
authorized: ${{ steps.policy.outputs.authorized }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Verify immutable release ref policy
id: policy
shell: bash
run: |
set -euo pipefail
git fetch --no-tags origin \
"+refs/heads/main:refs/remotes/origin/main"
git merge-base --is-ancestor \
"$GITHUB_SHA" refs/remotes/origin/main
case "$GITHUB_EVENT_NAME" in
push)
test "$GITHUB_REF_TYPE" = "tag"
test "$(node -p "String(require(process.env.GITHUB_EVENT_PATH).created)")" = "true"
test "$(node -p "String(require(process.env.GITHUB_EVENT_PATH).forced)")" = "false"
test "$(node -p "String(require(process.env.GITHUB_EVENT_PATH).deleted)")" = "false"
expected="v$(node -p "require('./package.json').version")"
test "$GITHUB_REF" = "refs/tags/$expected"
test "$GITHUB_REF_NAME" = "$expected"
git fetch --no-tags origin \
"+refs/tags/${GITHUB_REF_NAME}:refs/tags/${GITHUB_REF_NAME}"
test "$(git cat-file -t "refs/tags/$GITHUB_REF_NAME")" = "tag"
test "$(git rev-parse "refs/tags/$GITHUB_REF_NAME^{commit}")" = "$GITHUB_SHA"
;;
workflow_dispatch)
test "$GITHUB_REF" = "refs/heads/main"
test "$GITHUB_REF_TYPE" = "branch"
test "$GITHUB_SHA" = "$(git rev-parse refs/remotes/origin/main)"
;;
*)
echo "Production Agent signing is limited to immutable version-tag pushes or a manual run of refs/heads/main." >&2
exit 1
;;
esac
echo "authorized=true" >> "$GITHUB_OUTPUT"
agent-runtime:
name: Acquire Linux ${{ matrix.arch }} Agent runtime
if: >-
needs.authorize-agent-signing.result == 'success' &&
needs.authorize-agent-signing.outputs.authorized == 'true'
needs:
- validate
- authorize-agent-signing
strategy:
fail-fast: false
matrix:
include:
- arch: x64
node-url: https://nodejs.org/dist/v24.19.0/node-v24.19.0-linux-x64.tar.gz
node-archive: node-v24.19.0-linux-x64.tar.gz
node-sha256: f625d97cd707df4ff96254916fbc5ff014f09c09effe5a1e0ca8f6d41a8789d4
- arch: arm64
node-url: https://nodejs.org/dist/v24.19.0/node-v24.19.0-linux-arm64.tar.gz
node-archive: node-v24.19.0-linux-arm64.tar.gz
node-sha256: d28c8a5bf0a808f0ed434a1dce8c54ae98f0371c0bd86ac58abc613f73e6643f
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
- name: Verify locked Node runtime metadata
env:
AGENT_ARCH: ${{ matrix.arch }}
NODE_ARCHIVE: ${{ matrix.node-archive }}
NODE_SHA256: ${{ matrix.node-sha256 }}
NODE_URL: ${{ matrix.node-url }}
run: |
node - <<'NODE'
const lock = require('./agent-runtime-lock.json')
const input = lock.node?.targets?.[`linux-${process.env.AGENT_ARCH}`]
const expectedUrl = `${lock.node?.source ?? ''}${input?.archive ?? ''}`
if (
input?.archive !== process.env.NODE_ARCHIVE ||
input?.sha256 !== process.env.NODE_SHA256 ||
expectedUrl !== process.env.NODE_URL ||
!expectedUrl.startsWith('https://')
) {
throw new Error('Agent runtime acquisition metadata does not match agent-runtime-lock.json')
}
NODE
- name: Restore verified Node runtime cache
id: node-runtime-cache
uses: actions/cache/restore@v6
with:
path: ${{ runner.temp }}/goodbuddy-agent-node
key: goodbuddy-agent-node-${{ matrix.arch }}-${{ matrix.node-sha256 }}
- name: Download locked official Node runtime
if: steps.node-runtime-cache.outputs.cache-hit != 'true'
shell: bash
env:
NODE_ARCHIVE: ${{ matrix.node-archive }}
NODE_URL: ${{ matrix.node-url }}
run: |
set -euo pipefail
directory="$RUNNER_TEMP/goodbuddy-agent-node"
archive="$directory/$NODE_ARCHIVE"
mkdir -p "$directory"
curl --proto '=https' --tlsv1.2 \
--fail --silent --show-error --location \
--retry 3 --retry-all-errors \
--connect-timeout 15 --max-time 300 \
--max-filesize 104857600 \
"$NODE_URL" --output "$archive.partial"
mv "$archive.partial" "$archive"
- name: Verify locked Node runtime digest
shell: bash
env:
NODE_ARCHIVE: ${{ matrix.node-archive }}
NODE_SHA256: ${{ matrix.node-sha256 }}
run: |
set -euo pipefail
archive="$RUNNER_TEMP/goodbuddy-agent-node/$NODE_ARCHIVE"
test -f "$archive"
printf '%s %s\n' "$NODE_SHA256" "$archive" |
sha256sum --check --strict -
- name: Save verified Node runtime cache
if: steps.node-runtime-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: ${{ runner.temp }}/goodbuddy-agent-node
key: goodbuddy-agent-node-${{ matrix.arch }}-${{ matrix.node-sha256 }}
- name: Upload verified Node runtime input
uses: actions/upload-artifact@v7
with:
name: goodbuddy-agent-node-${{ matrix.arch }}
path: ${{ runner.temp }}/goodbuddy-agent-node/${{ matrix.node-archive }}
if-no-files-found: error
compression-level: 0
retention-days: 1
agent-signing:
name: Linux ${{ matrix.arch }} signed Agent bundle
if: >-
needs.authorize-agent-signing.result == 'success' &&
needs.authorize-agent-signing.outputs.authorized == 'true'
needs:
- validate
- authorize-agent-signing
- agent-runtime
strategy:
fail-fast: false
matrix:
include:
- arch: x64
runner: ubuntu-24.04
node-archive: node-v24.19.0-linux-x64.tar.gz
node-sha256: f625d97cd707df4ff96254916fbc5ff014f09c09effe5a1e0ca8f6d41a8789d4
- arch: arm64
runner: ubuntu-24.04-arm
node-archive: node-v24.19.0-linux-arm64.tar.gz
node-sha256: d28c8a5bf0a808f0ed434a1dce8c54ae98f0371c0bd86ac58abc613f73e6643f
runs-on: ${{ matrix.runner }}
timeout-minutes: 20
environment:
name: agent-signing
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-node@v7
with:
node-version: 24.19.0
- name: Install dependencies
run: npm ci
- name: Preflight production Agent signing registry
env:
GOODBUDDY_AGENT_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_AGENT_SIGNING_KEY_ID }}
run: node build/agent-bundle.cjs preflight
- name: Download verified official Node runtime input
uses: actions/download-artifact@v8
with:
name: goodbuddy-agent-node-${{ matrix.arch }}
path: ${{ runner.temp }}/goodbuddy-agent-node
- name: Build and sign Agent bundle
shell: bash
env:
GOODBUDDY_AGENT_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_AGENT_SIGNING_KEY_ID }}
GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY }}
run: |
set -euo pipefail
set +x
node build/agent-bundle.cjs build \
--arch "${{ matrix.arch }}" \
--runtime-archive "$RUNNER_TEMP/goodbuddy-agent-node/${{ matrix.node-archive }}" \
--archive "dist/agent-bundles/goodbuddy-agent-linux-${{ matrix.arch }}.tar"
- name: Upload signed Agent bundle
uses: actions/upload-artifact@v7
with:
name: goodbuddy-agent-linux-${{ matrix.arch }}
path: dist/agent-bundles/goodbuddy-agent-linux-${{ matrix.arch }}.tar
if-no-files-found: error
compression-level: 0
retention-days: 1
package:
name: ${{ matrix.platform }} ${{ matrix.arch }}
if: >-
needs.authorize-agent-signing.result == 'success' &&
needs.authorize-agent-signing.outputs.authorized == 'true'
needs:
- validate
- authorize-agent-signing
- agent-signing
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.19.0
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: Download signed Agent bundles
uses: actions/download-artifact@v8
with:
pattern: goodbuddy-agent-linux-*
path: dist/agent-downloads
merge-multiple: true
- name: Import and verify both Agent bundles
shell: bash
run: |
set -euo pipefail
node build/agent-bundle.cjs import \
--arch x64 \
--archive dist/agent-downloads/goodbuddy-agent-linux-x64.tar
node build/agent-bundle.cjs import \
--arch arm64 \
--archive dist/agent-downloads/goodbuddy-agent-linux-arm64.tar
- name: Resolve macOS signing mode
id: macos-signing
if: matrix.platform == 'macos'
shell: bash
env:
MACOS_CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE_BASE64 }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: |
set -euo pipefail
names=(
MACOS_CERTIFICATE_BASE64
MACOS_CERTIFICATE_PASSWORD
APPLE_API_KEY_BASE64
APPLE_API_KEY_ID
APPLE_API_ISSUER
)
configured=0
missing=()
for name in "${names[@]}"; do
if [[ -n "${!name}" ]]; then
configured=$((configured + 1))
else
missing+=("$name")
fi
done
if [[ "$configured" -eq 0 ]]; then
echo "enabled=false" >> "$GITHUB_OUTPUT"
echo "::warning title=Unsigned macOS packages::Apple signing credentials are not configured. The macOS DMG and ZIP will be unsigned and unnotarized."
{
echo "### macOS signing"
echo
echo "Apple signing credentials are not configured. This target produces unsigned and unnotarized packages that Gatekeeper may block on first launch."
} >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
if [[ "$configured" -ne "${#names[@]}" ]]; then
missing_names="$(IFS=,; echo "${missing[*]}")"
echo "::error title=Incomplete macOS signing credentials::Missing: $missing_names"
exit 1
fi
echo "enabled=true" >> "$GITHUB_OUTPUT"
printf '%s' "$MACOS_CERTIFICATE_BASE64" | base64 -D > "$RUNNER_TEMP/goodbuddy-developer-id.p12"
printf '%s' "$APPLE_API_KEY_BASE64" | base64 -D > "$RUNNER_TEMP/AuthKey.p8"
test -s "$RUNNER_TEMP/goodbuddy-developer-id.p12"
test -s "$RUNNER_TEMP/AuthKey.p8"
chmod 600 "$RUNNER_TEMP/goodbuddy-developer-id.p12" "$RUNNER_TEMP/AuthKey.p8"
{
echo "### macOS signing"
echo
echo "Complete Apple signing credentials were detected. This target will be signed, notarized, and verified."
} >> "$GITHUB_STEP_SUMMARY"
- name: Build, sign and notarize macOS release packages
if: matrix.platform == 'macos' && steps.macos-signing.outputs.enabled == 'true'
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
CSC_LINK: ${{ runner.temp }}/goodbuddy-developer-id.p12
CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
CSC_IDENTITY_AUTO_DISCOVERY: 'true'
APPLE_API_KEY: ${{ runner.temp }}/AuthKey.p8
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
- name: Build unsigned macOS release packages
if: matrix.platform == 'macos' && steps.macos-signing.outputs.enabled == 'false'
run: npm run release:package -- --platform ${{ matrix.platform }} --arch ${{ matrix.arch }} --skip-build --unsigned
env:
ELECTRON_CACHE: ${{ runner.temp }}/electron
ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/electron-builder
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
- name: Verify macOS signature and notarization ticket
if: matrix.platform == 'macos' && steps.macos-signing.outputs.enabled == 'true'
shell: bash
run: |
set -euo pipefail
dmg="$(find "dist/release/macos-${{ matrix.arch }}" -maxdepth 1 -type f -name '*.dmg' -print -quit)"
test -n "$dmg"
mount_point="$RUNNER_TEMP/goodbuddy-dmg"
mkdir "$mount_point"
cleanup() {
hdiutil detach "$mount_point" -quiet || true
}
trap cleanup EXIT
hdiutil attach "$dmg" -nobrowse -readonly -mountpoint "$mount_point" -quiet
app="$(find "$mount_point" -maxdepth 1 -type d -name '*.app' -print -quit)"
test -n "$app"
codesign --verify --deep --strict --verbose=2 "$app"
spctl --assess --type execute --verbose=4 "$app"
xcrun stapler validate "$app"
- name: Build and verify non-macOS release packages
if: matrix.platform != 'macos'
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.authorize-agent-signing.outputs.authorized == 'true'
needs:
- authorize-agent-signing
- 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.19.0
- 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
goodbuddy) ;;
*) echo "OSS Bucket 必须与应用内置镜像地址一致" >&2; exit 1 ;;
esac
case "$OSS_ENDPOINT" in
https://oss-cn-beijing.aliyuncs.com) ;;
*) echo "OSS Endpoint 必须与应用内置镜像地址一致" >&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://}"
oss_region="${endpoint_host#oss-}"
oss_region="${oss_region%.aliyuncs.com}"
case "$oss_region" in
*[!a-z0-9-]*|'') echo "无法从 OSS Endpoint 推导 Region" >&2; exit 1 ;;
esac
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"
echo "region=$oss_region" >> "$GITHUB_OUTPUT"
- name: Upload immutable release assets to OSS
shell: bash
env:
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
OSS_REGION: ${{ steps.oss-release.outputs.region }}
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"
test -n "$OSS_REGION"
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" \
--region "$OSS_REGION" \
--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 }}
OSS_REGION: ${{ steps.oss-release.outputs.region }}
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_REGION"
ossutil cp dist/site-release.json \
"oss://${OSS_BUCKET}/releases/latest.json" \
--endpoint "$OSS_ENDPOINT" \
--region "$OSS_REGION" \
--force