Display native OpenCode and Continue checklists alongside scheduled tasks, preserving request ownership, explicit clears and saved progress. Bundle both remote runtimes and support managed Continue installation, model routing, cancellation and restart continuation. Validation: real local and Linux remote model checks, full Electron remote Continue flows, typecheck and lint passed. Full suite: 4707 passed, 67 skipped, one OpenCode compaction failure passed targeted rerun. Release note: Display native execution checklists above conversations and support managed remote Continue projects with saved progress and cancellation recovery.
620 lines
24 KiB
YAML
620 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 }}
|
|
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_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"
|