621 lines
24 KiB
YAML
621 lines
24 KiB
YAML
name: GoodBuddy Agent Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'agent-v*'
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: agent-release-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
authorize:
|
|
name: Authorize Agent release
|
|
if: >-
|
|
github.ref_type == 'tag' &&
|
|
startsWith(github.ref_name, 'agent-v')
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 5
|
|
outputs:
|
|
authorized: ${{ steps.policy.outputs.authorized }}
|
|
version: ${{ steps.policy.outputs.version }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Verify immutable Agent release tag
|
|
id: policy
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
version="$(node -p "require('./agent-runtime-lock.json').agentVersion")"
|
|
expected="agent-v$version"
|
|
test "$GITHUB_REF_NAME" = "$expected"
|
|
test "$(git cat-file -t "refs/tags/$GITHUB_REF_NAME")" = "tag"
|
|
test "$(git rev-parse "refs/tags/$GITHUB_REF_NAME^{commit}")" = "$GITHUB_SHA"
|
|
git fetch --no-tags origin main
|
|
git merge-base --is-ancestor "$GITHUB_SHA" refs/remotes/origin/main
|
|
echo "authorized=true" >> "$GITHUB_OUTPUT"
|
|
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
|
|
build:
|
|
name: Build ${{ matrix.platform }} ${{ matrix.arch }} compound package
|
|
needs: authorize
|
|
if: needs.authorize.outputs.authorized == 'true'
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- platform: linux
|
|
arch: x64
|
|
runner: ubuntu-24.04
|
|
- platform: linux
|
|
arch: arm64
|
|
runner: ubuntu-24.04-arm
|
|
- platform: darwin
|
|
arch: arm64
|
|
runner: macos-15
|
|
runs-on: ${{ matrix.runner }}
|
|
timeout-minutes: 25
|
|
environment:
|
|
name: agent-signing
|
|
permissions:
|
|
contents: read
|
|
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/setup-node@v7
|
|
with:
|
|
node-version: 24.19.0
|
|
cache: npm
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Verify native build target
|
|
run: node -e "if(process.platform !== '${{ matrix.platform }}' || process.arch !== '${{ matrix.arch }}') throw new Error('Native Agent target mismatch')"
|
|
|
|
- name: Resolve locked native inputs
|
|
id: inputs
|
|
shell: bash
|
|
env:
|
|
AGENT_ARCH: ${{ matrix.arch }}
|
|
AGENT_PLATFORM: ${{ matrix.platform }}
|
|
run: |
|
|
set -euo pipefail
|
|
node - <<'NODE'
|
|
const { appendFileSync } = require('node:fs')
|
|
const { createHash } = require('node:crypto')
|
|
const agent = require('./agent-runtime-lock.json')
|
|
const remote = require('./remote-runtime-lock.json')
|
|
const node = agent.node?.targets?.[`${process.env.AGENT_PLATFORM}-${process.env.AGENT_ARCH}`]
|
|
const runtime = remote.runtimes?.opencode
|
|
const runtimeKey = process.env.AGENT_PLATFORM === 'linux'
|
|
? process.env.AGENT_ARCH : `${process.env.AGENT_PLATFORM}-${process.env.AGENT_ARCH}`
|
|
const opencode = runtime?.targets?.[runtimeKey]
|
|
if (
|
|
!node ||
|
|
!opencode ||
|
|
!/^[A-Za-z0-9._-]+\.tar\.gz$/u.test(node.archive) ||
|
|
!/^[a-f0-9]{64}$/u.test(node.sha256) ||
|
|
!/^[a-z0-9][a-z0-9._-]*$/u.test(opencode.package) ||
|
|
!/^sha512-[A-Za-z0-9+/]+={0,2}$/u.test(opencode.integrity)
|
|
) {
|
|
throw new Error('Locked native Agent package inputs are invalid')
|
|
}
|
|
const nodeUrl = new URL(node.archive, agent.node.source)
|
|
if (
|
|
nodeUrl.protocol !== 'https:' ||
|
|
nodeUrl.hostname !== 'nodejs.org' ||
|
|
nodeUrl.username ||
|
|
nodeUrl.password ||
|
|
nodeUrl.search ||
|
|
nodeUrl.hash
|
|
) {
|
|
throw new Error('Agent Node runtime must use the official HTTPS origin')
|
|
}
|
|
const opencodeArchive =
|
|
`${opencode.package}-${runtime.version}.tgz`
|
|
appendFileSync(
|
|
process.env.GITHUB_OUTPUT,
|
|
[
|
|
`node-archive=${node.archive}`,
|
|
`node-sha256=${node.sha256}`,
|
|
`node-url=${nodeUrl.href}`,
|
|
`opencode-package=${opencode.package}`,
|
|
`opencode-version=${runtime.version}`,
|
|
`opencode-integrity=${opencode.integrity}`,
|
|
`opencode-archive=${opencodeArchive}`,
|
|
`cache-key=${createHash('sha256')
|
|
.update(`${node.sha256}:${opencode.integrity}`)
|
|
.digest('hex')}`,
|
|
''
|
|
].join('\n')
|
|
)
|
|
NODE
|
|
|
|
- name: Cache verified native inputs
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: ${{ runner.temp }}/goodbuddy-agent-release-inputs
|
|
key: goodbuddy-agent-release-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.inputs.outputs.cache-key }}
|
|
|
|
- name: Acquire and verify native inputs
|
|
shell: bash
|
|
env:
|
|
NODE_ARCHIVE: ${{ steps.inputs.outputs.node-archive }}
|
|
NODE_SHA256: ${{ steps.inputs.outputs.node-sha256 }}
|
|
NODE_URL: ${{ steps.inputs.outputs.node-url }}
|
|
OPENCODE_ARCHIVE: ${{ steps.inputs.outputs.opencode-archive }}
|
|
OPENCODE_INTEGRITY: ${{ steps.inputs.outputs.opencode-integrity }}
|
|
OPENCODE_PACKAGE: ${{ steps.inputs.outputs.opencode-package }}
|
|
OPENCODE_VERSION: ${{ steps.inputs.outputs.opencode-version }}
|
|
run: |
|
|
set -euo pipefail
|
|
directory="$RUNNER_TEMP/goodbuddy-agent-release-inputs"
|
|
mkdir -p "$directory"
|
|
node_archive="$directory/$NODE_ARCHIVE"
|
|
if [[ ! -f "$node_archive" ]]; then
|
|
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 "$node_archive.partial"
|
|
mv "$node_archive.partial" "$node_archive"
|
|
fi
|
|
node - "$node_archive" "$NODE_SHA256" <<'NODE'
|
|
const { createHash } = require('node:crypto')
|
|
const { readFileSync } = require('node:fs')
|
|
const [archive, expected] = process.argv.slice(2)
|
|
if (createHash('sha256').update(readFileSync(archive)).digest('hex') !== expected) {
|
|
throw new Error('Node runtime archive integrity mismatch')
|
|
}
|
|
NODE
|
|
|
|
opencode_archive="$directory/$OPENCODE_ARCHIVE"
|
|
if [[ ! -f "$opencode_archive" ]]; then
|
|
npm pack \
|
|
--ignore-scripts \
|
|
--registry https://registry.npmjs.org/ \
|
|
--pack-destination "$directory" \
|
|
"$OPENCODE_PACKAGE@$OPENCODE_VERSION"
|
|
fi
|
|
node - "$opencode_archive" "$OPENCODE_INTEGRITY" <<'NODE'
|
|
const { createHash } = require('node:crypto')
|
|
const { readFileSync, statSync } = require('node:fs')
|
|
const [archive, expected] = process.argv.slice(2)
|
|
const size = statSync(archive).size
|
|
if (size <= 0 || size > 536870912) {
|
|
throw new Error('OpenCode Runtime archive exceeds its safety limit')
|
|
}
|
|
const actual = `sha512-${createHash('sha512')
|
|
.update(readFileSync(archive))
|
|
.digest('base64')}`
|
|
if (actual !== expected) {
|
|
throw new Error('OpenCode Runtime archive integrity mismatch')
|
|
}
|
|
NODE
|
|
|
|
- name: Preflight production GoodBuddy signing identity
|
|
shell: bash
|
|
env:
|
|
GOODBUDDY_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_SIGNING_KEY_ID }}
|
|
run: |
|
|
set -euo pipefail
|
|
node build/agent-bundle.cjs preflight
|
|
|
|
- name: Build production compound Agent package
|
|
shell: bash
|
|
env:
|
|
AGENT_ARCH: ${{ matrix.arch }}
|
|
AGENT_PLATFORM: ${{ matrix.platform }}
|
|
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
|
|
GOODBUDDY_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_SIGNING_KEY_ID }}
|
|
GOODBUDDY_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_SIGNING_PRIVATE_KEY }}
|
|
NODE_ARCHIVE: ${{ steps.inputs.outputs.node-archive }}
|
|
OPENCODE_ARCHIVE: ${{ steps.inputs.outputs.opencode-archive }}
|
|
run: |
|
|
set -euo pipefail
|
|
output="$RUNNER_TEMP/goodbuddy-agent-output"
|
|
mkdir -p "$output"
|
|
continue_archive="$(node build/acquire-continue-runtime.cjs "$RUNNER_TEMP/goodbuddy-continue-runtime" "$AGENT_ARCH" "$AGENT_PLATFORM")"
|
|
node build/agent-package.cjs build \
|
|
--platform "$AGENT_PLATFORM" \
|
|
--arch "$AGENT_ARCH" \
|
|
--minimum-desktop-version "$(node -p "require('./package.json').version")" \
|
|
--node-archive "$RUNNER_TEMP/goodbuddy-agent-release-inputs/$NODE_ARCHIVE" \
|
|
--runtime-archive "$RUNNER_TEMP/goodbuddy-agent-release-inputs/$OPENCODE_ARCHIVE" \
|
|
--continue-archive "$continue_archive" \
|
|
--output "$output/goodbuddy-agent-$AGENT_VERSION-$AGENT_PLATFORM-$AGENT_ARCH.gbagent" \
|
|
> "$output/result.json"
|
|
node - "$output/result.json" "$AGENT_ARCH" "$AGENT_VERSION" <<'NODE'
|
|
const { readFileSync } = require('node:fs')
|
|
const [resultPath, architecture, version] = process.argv.slice(2)
|
|
const result = JSON.parse(readFileSync(resultPath, 'utf8'))
|
|
if (
|
|
result.descriptor?.architecture !== architecture ||
|
|
result.descriptor?.platform !== process.env.AGENT_PLATFORM ||
|
|
result.descriptor?.version !== version ||
|
|
result.archive !==
|
|
`goodbuddy-agent-${version}-${process.env.AGENT_PLATFORM}-${architecture}.gbagent` ||
|
|
!/^[a-f0-9]{64}$/u.test(result.sha256)
|
|
) {
|
|
throw new Error('Production Agent package result is invalid')
|
|
}
|
|
NODE
|
|
|
|
- name: Upload verified compound package
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: goodbuddy-agent-package-${{ matrix.platform }}-${{ matrix.arch }}
|
|
path: ${{ runner.temp }}/goodbuddy-agent-output/goodbuddy-agent-*.gbagent
|
|
if-no-files-found: error
|
|
compression-level: 0
|
|
retention-days: 7
|
|
|
|
catalog:
|
|
name: Sign cumulative Agent catalog
|
|
needs:
|
|
- authorize
|
|
- build
|
|
if: needs.authorize.outputs.authorized == 'true'
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 10
|
|
environment:
|
|
name: agent-signing
|
|
permissions:
|
|
contents: read
|
|
|
|
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: Download compound packages
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: goodbuddy-agent-package-*
|
|
path: dist/agent-release
|
|
merge-multiple: true
|
|
|
|
- name: Fetch previous signed catalog when available
|
|
id: previous
|
|
shell: bash
|
|
env:
|
|
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
root="https://goodbuddy.oss-cn-beijing.aliyuncs.com/agent-releases"
|
|
current_catalog_status="$(
|
|
curl --silent --show-error \
|
|
--output dist/agent-release/previous.json \
|
|
--write-out '%{http_code}' \
|
|
"$root/v$AGENT_VERSION/agent-catalog.json"
|
|
)"
|
|
current_signature_status="$(
|
|
curl --silent --show-error \
|
|
--output dist/agent-release/previous.sig \
|
|
--write-out '%{http_code}' \
|
|
"$root/v$AGENT_VERSION/agent-catalog.sig"
|
|
)"
|
|
if [[ "$current_catalog_status" == 200 && "$current_signature_status" == 200 ]]; then
|
|
echo "arguments=--previous-catalog dist/agent-release/previous.json --previous-signature dist/agent-release/previous.sig" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
if [[ "$current_catalog_status" != 404 || "$current_signature_status" != 404 ]]; then
|
|
echo "Current Agent catalog objects are inconsistent: $current_catalog_status / $current_signature_status" >&2
|
|
exit 1
|
|
fi
|
|
rm -f dist/agent-release/previous.json dist/agent-release/previous.sig
|
|
pointer_status="$(
|
|
curl --silent --show-error \
|
|
--output dist/agent-release/previous-pointer.json \
|
|
--write-out '%{http_code}' \
|
|
"$root/latest.json"
|
|
)"
|
|
if [[ "$pointer_status" == 200 ]]; then
|
|
readarray -t paths < <(
|
|
node - dist/agent-release/previous-pointer.json <<'NODE'
|
|
const { readFileSync } = require('node:fs')
|
|
const pointer = JSON.parse(readFileSync(process.argv[2], 'utf8'))
|
|
if (
|
|
pointer?.formatVersion !== 1 ||
|
|
!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(pointer.version) ||
|
|
pointer.catalog !== `v${pointer.version}/agent-catalog.json` ||
|
|
pointer.signature !== `v${pointer.version}/agent-catalog.sig`
|
|
) {
|
|
throw new Error('Previous Agent catalog pointer is invalid')
|
|
}
|
|
process.stdout.write(`${pointer.catalog}\n${pointer.signature}\n`)
|
|
NODE
|
|
)
|
|
curl --fail --silent --show-error \
|
|
"$root/${paths[0]}" \
|
|
--output dist/agent-release/previous.json
|
|
curl --fail --silent --show-error \
|
|
"$root/${paths[1]}" \
|
|
--output dist/agent-release/previous.sig
|
|
echo "arguments=--previous-catalog dist/agent-release/previous.json --previous-signature dist/agent-release/previous.sig" >> "$GITHUB_OUTPUT"
|
|
elif [[ "$pointer_status" == 404 ]]; then
|
|
rm -f dist/agent-release/previous-pointer.json
|
|
echo "arguments=" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "Agent catalog pointer request failed: $pointer_status" >&2
|
|
exit 1
|
|
fi
|
|
|
|
- name: Sign cumulative Agent catalog
|
|
shell: bash
|
|
env:
|
|
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
|
|
PREVIOUS_ARGUMENTS: ${{ steps.previous.outputs.arguments }}
|
|
GOODBUDDY_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_SIGNING_KEY_ID }}
|
|
GOODBUDDY_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_SIGNING_PRIVATE_KEY }}
|
|
run: |
|
|
set -euo pipefail
|
|
generated_at="$(git show -s --format=%cI "$GITHUB_SHA")"
|
|
read -r -a previous <<< "$PREVIOUS_ARGUMENTS"
|
|
node build/agent-catalog.cjs create \
|
|
--x64-package "dist/agent-release/goodbuddy-agent-$AGENT_VERSION-linux-x64.gbagent" \
|
|
--arm64-package "dist/agent-release/goodbuddy-agent-$AGENT_VERSION-linux-arm64.gbagent" \
|
|
--darwin-arm64-package "dist/agent-release/goodbuddy-agent-$AGENT_VERSION-darwin-arm64.gbagent" \
|
|
"${previous[@]}" \
|
|
--catalog dist/agent-release/agent-catalog.json \
|
|
--signature dist/agent-release/agent-catalog.sig \
|
|
--generated-at "$generated_at"
|
|
node build/agent-catalog.cjs verify \
|
|
--catalog dist/agent-release/agent-catalog.json \
|
|
--signature dist/agent-release/agent-catalog.sig
|
|
|
|
- name: Upload signed release set
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: goodbuddy-agent-release-${{ needs.authorize.outputs.version }}
|
|
path: |
|
|
dist/agent-release/goodbuddy-agent-*.gbagent
|
|
dist/agent-release/agent-catalog.json
|
|
dist/agent-release/agent-catalog.sig
|
|
if-no-files-found: error
|
|
compression-level: 0
|
|
retention-days: 7
|
|
|
|
publish:
|
|
name: Publish Agent to GitHub and OSS
|
|
needs:
|
|
- authorize
|
|
- catalog
|
|
if: needs.authorize.outputs.authorized == 'true'
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 25
|
|
environment:
|
|
name: aliyun-oss-release
|
|
permissions:
|
|
contents: write
|
|
actions: read
|
|
id-token: write
|
|
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
- uses: actions/setup-node@v7
|
|
with:
|
|
node-version: 24.19.0
|
|
|
|
- name: Install verification dependencies
|
|
run: npm ci
|
|
|
|
- name: Download signed release set
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: goodbuddy-agent-release-${{ needs.authorize.outputs.version }}
|
|
path: dist/agent-release
|
|
|
|
- name: Verify signed release set
|
|
run: |
|
|
node build/agent-catalog.cjs verify \
|
|
--catalog dist/agent-release/agent-catalog.json \
|
|
--signature dist/agent-release/agent-catalog.sig
|
|
|
|
- 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 "$OSS_BUCKET" = "goodbuddy"
|
|
test "$OSS_ENDPOINT" = "https://oss-cn-beijing.aliyuncs.com"
|
|
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-agent-${{ 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: Upload immutable Agent release to OSS
|
|
id: oss
|
|
shell: bash
|
|
env:
|
|
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
|
|
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
|
|
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
|
|
OSS_UPLOAD_ENDPOINT: https://oss-accelerate.aliyuncs.com
|
|
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"
|
|
endpoint_host="${OSS_ENDPOINT#https://}"
|
|
region="${endpoint_host#oss-}"
|
|
region="${region%.aliyuncs.com}"
|
|
test "$region" = "cn-beijing"
|
|
for file in dist/agent-release/*; do
|
|
name="$(basename "$file")"
|
|
ossutil cp "$file" \
|
|
"oss://${OSS_BUCKET}/agent-releases/v${AGENT_VERSION}/${name}" \
|
|
--endpoint "$OSS_UPLOAD_ENDPOINT" \
|
|
--region "$region" \
|
|
--update
|
|
done
|
|
echo "region=$region" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Verify public immutable OSS packages
|
|
shell: bash
|
|
env:
|
|
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
root="https://goodbuddy.oss-cn-beijing.aliyuncs.com/agent-releases/v$AGENT_VERSION"
|
|
for target in linux-x64 linux-arm64 darwin-arm64; do
|
|
name="goodbuddy-agent-$AGENT_VERSION-$target.gbagent"
|
|
curl --fail --silent --show-error --location \
|
|
"$root/$name" --output "$RUNNER_TEMP/$name"
|
|
cmp "dist/agent-release/$name" "$RUNNER_TEMP/$name"
|
|
done
|
|
for name in agent-catalog.json agent-catalog.sig; do
|
|
curl --fail --silent --show-error --location \
|
|
"$root/$name" --output "$RUNNER_TEMP/immutable-$name"
|
|
cmp "dist/agent-release/$name" \
|
|
"$RUNNER_TEMP/immutable-$name"
|
|
done
|
|
|
|
- name: Publish non-Latest GitHub Agent release
|
|
shell: bash
|
|
env:
|
|
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
set -euo pipefail
|
|
tag="agent-v$AGENT_VERSION"
|
|
title="GoodBuddy Agent $AGENT_VERSION"
|
|
notes="resources/agent-release-notes/$AGENT_VERSION.md"
|
|
test -s "$notes"
|
|
if gh release view "$tag" >/dev/null 2>&1; then
|
|
is_draft="$(gh release view "$tag" --json isDraft --jq .isDraft)"
|
|
if [[ "$is_draft" == true ]]; then
|
|
gh release edit "$tag" \
|
|
--title "$title" \
|
|
--notes-file "$notes" \
|
|
--latest=false
|
|
gh release upload "$tag" dist/agent-release/* --clobber
|
|
gh release edit "$tag" --draft=false --latest=false
|
|
else
|
|
verify="$RUNNER_TEMP/github-agent-release"
|
|
mkdir "$verify"
|
|
gh release download "$tag" --dir "$verify"
|
|
for file in dist/agent-release/*; do
|
|
cmp "$file" "$verify/$(basename "$file")"
|
|
done
|
|
fi
|
|
else
|
|
gh release create "$tag" \
|
|
--draft \
|
|
--verify-tag \
|
|
--title "$title" \
|
|
--notes-file "$notes" \
|
|
--latest=false
|
|
gh release upload "$tag" dist/agent-release/* --clobber
|
|
gh release edit "$tag" --draft=false --latest=false
|
|
fi
|
|
test "$(gh api repos/mesalogo/goodbuddy/releases/latest --jq .tag_name)" != "$tag"
|
|
|
|
- name: Point OSS Agent catalog to published release
|
|
shell: bash
|
|
env:
|
|
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
|
|
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
|
|
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
|
|
OSS_REGION: ${{ steps.oss.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"
|
|
node - "$AGENT_VERSION" > dist/agent-release/latest.json <<'NODE'
|
|
const version = process.argv[2]
|
|
process.stdout.write(`${JSON.stringify({
|
|
formatVersion: 1,
|
|
version,
|
|
catalog: `v${version}/agent-catalog.json`,
|
|
signature: `v${version}/agent-catalog.sig`
|
|
}, null, 2)}\n`)
|
|
NODE
|
|
ossutil cp dist/agent-release/latest.json \
|
|
"oss://${OSS_BUCKET}/agent-releases/latest.json" \
|
|
--endpoint "$OSS_ENDPOINT" \
|
|
--region "$OSS_REGION" \
|
|
--force
|
|
|
|
- name: Verify public Agent catalogs
|
|
shell: bash
|
|
env:
|
|
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
mirror="https://goodbuddy.oss-cn-beijing.aliyuncs.com/agent-releases"
|
|
github="https://github.com/mesalogo/goodbuddy/releases/download/agent-v$AGENT_VERSION"
|
|
curl --fail --silent --show-error --location \
|
|
"$mirror/latest.json" --output "$RUNNER_TEMP/mirror-pointer.json"
|
|
curl --fail --silent --show-error --location \
|
|
"$mirror/v$AGENT_VERSION/agent-catalog.json" \
|
|
--output "$RUNNER_TEMP/mirror.json"
|
|
curl --fail --silent --show-error --location \
|
|
"$mirror/v$AGENT_VERSION/agent-catalog.sig" \
|
|
--output "$RUNNER_TEMP/mirror.sig"
|
|
curl --fail --silent --show-error --location \
|
|
"$github/agent-catalog.json" --output "$RUNNER_TEMP/github.json"
|
|
curl --fail --silent --show-error --location \
|
|
"$github/agent-catalog.sig" --output "$RUNNER_TEMP/github.sig"
|
|
cmp dist/agent-release/latest.json "$RUNNER_TEMP/mirror-pointer.json"
|
|
cmp dist/agent-release/agent-catalog.json "$RUNNER_TEMP/mirror.json"
|
|
cmp dist/agent-release/agent-catalog.sig "$RUNNER_TEMP/mirror.sig"
|
|
cmp dist/agent-release/agent-catalog.json "$RUNNER_TEMP/github.json"
|
|
cmp dist/agent-release/agent-catalog.sig "$RUNNER_TEMP/github.sig"
|