Publish SDKs to OperatorStack Registry #34
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: Publish SDKs to OperatorStack Registry | |
| on: | |
| pull_request: | |
| paths: [".github/workflows/private-registry.yml"] | |
| push: | |
| tags: ["v*"] | |
| workflow_run: | |
| workflows: ["Release Yield"] | |
| types: [completed] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Existing release version without the leading v" | |
| required: true | |
| default: "0.1.8" | |
| type: string | |
| permissions: | |
| contents: read | |
| id-token: write | |
| concurrency: | |
| group: publish-yield-sdks | |
| cancel-in-progress: false | |
| jobs: | |
| validate: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: stable | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| - name: Verify the current release source | |
| run: | | |
| go test ./... | |
| node --test packaging/*.test.mjs | |
| - name: Check the one-package release contract | |
| run: | | |
| set -euo pipefail | |
| grep -F 'CGO_ENABLED=0' .github/workflows/private-registry.yml | |
| grep -F 'packaging/assemble.mjs' .github/workflows/private-registry.yml | |
| grep -F 'Install and test all language packages' .github/workflows/private-registry.yml | |
| publish: | |
| if: github.event_name != 'pull_request' && (github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success') | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| previous_version: ${{ steps.version.outputs.previous_version }} | |
| source_sha: ${{ steps.version.outputs.source_sha }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event_name == 'workflow_dispatch' && format('v{0}', inputs.version) || github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.ref }} | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: stable | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - uses: dtolnay/rust-toolchain@stable | |
| - id: version | |
| name: Resolve published version | |
| env: | |
| REQUESTED_VERSION: ${{ inputs.version }} | |
| run: | | |
| set -euo pipefail | |
| if [[ "$GITHUB_EVENT_NAME" == "workflow_run" ]]; then | |
| tag="$(git tag --points-at HEAD --list 'v[0-9]*' --sort=-v:refname | head -n 1)" | |
| test -n "$tag" | |
| version="${tag#v}" | |
| elif [[ "$GITHUB_REF" == refs/tags/v* ]]; then | |
| version="${GITHUB_REF_NAME#v}" | |
| else | |
| version="$REQUESTED_VERSION" | |
| fi | |
| [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] | |
| git rev-parse --verify "refs/tags/v${version}" | |
| test "$(git rev-list -n 1 "v${version}")" = "$(git rev-parse HEAD)" | |
| previous="$(git tag --list 'v[0-9]*' --sort=-v:refname \ | |
| | sed 's/^v//' \ | |
| | awk -F. '$1 > 0 || $2 > 1 || ($2 == 1 && $3 >= 23)' \ | |
| | awk -F. -v current="$version" ' | |
| BEGIN { split(current, part, ".") } | |
| $1 < part[1] || ($1 == part[1] && $2 < part[2]) || ($1 == part[1] && $2 == part[2] && $3 < part[3]) { print; exit } | |
| ')" | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "previous_version=$previous" >> "$GITHUB_OUTPUT" | |
| echo "source_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - id: auth | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: ${{ vars.WIF_PROVIDER }} | |
| service_account: ${{ vars.DEPLOYER_SA_EMAIL }} | |
| - uses: google-github-actions/setup-gcloud@v2 | |
| with: | |
| install_components: package-go-module | |
| - name: Verify Go SDK and runtime | |
| run: go test ./... | |
| - name: Build immutable runtimes and language packages | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p dist/bin | |
| while read -r goos goarch; do | |
| suffix="" | |
| if [[ "$goos" == "windows" ]]; then suffix=".exe"; fi | |
| CGO_ENABLED=0 GOOS="$goos" GOARCH="$goarch" go build \ | |
| -trimpath -ldflags "-s -w -X main.version=${VERSION}" \ | |
| -o "dist/bin/yskill-${goos}-${goarch}${suffix}" ./cmd/yskill | |
| done <<'TARGETS' | |
| darwin amd64 | |
| darwin arm64 | |
| linux amd64 | |
| linux arm64 | |
| windows amd64 | |
| windows arm64 | |
| TARGETS | |
| node packaging/assemble.mjs --version "$VERSION" --binaries dist/bin --output dist/packages | |
| - name: Keep runtime checksums with the release run | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: yskill-${{ steps.version.outputs.version }}-runtimes | |
| path: | | |
| dist/bin/ | |
| dist/packages/SHA256SUMS.json | |
| if-no-files-found: error | |
| - name: Publish Go module | |
| run: | | |
| set -euo pipefail | |
| version="${{ steps.version.outputs.version }}" | |
| if gcloud artifacts versions describe "v${version}" \ | |
| --project="${{ vars.AR_PROJECT }}" --location="${{ vars.AR_LOCATION }}" \ | |
| --repository="${{ vars.AR_GO_REPO }}" --package=github.com/operatorstack/yield \ | |
| >/dev/null 2>&1; then | |
| echo "Go module already published." | |
| else | |
| source_dir="$(mktemp -d)" | |
| git archive "v${version}" | tar -x -C "$source_dir" | |
| gcloud artifacts go upload \ | |
| --project="${{ vars.AR_PROJECT }}" \ | |
| --location="${{ vars.AR_LOCATION }}" \ | |
| --repository="${{ vars.AR_GO_REPO }}" \ | |
| --module-path=github.com/operatorstack/yield \ | |
| --version="v${version}" \ | |
| --source="$source_dir" | |
| fi | |
| - name: Configure npm publishing | |
| run: | | |
| gcloud artifacts print-settings npm \ | |
| --project="${{ vars.AR_PROJECT }}" --location="${{ vars.AR_LOCATION }}" \ | |
| --repository="${{ vars.AR_NPM_REPO }}" --scope=@operatorstack > "$RUNNER_TEMP/yield.npmrc" | |
| npx -y google-artifactregistry-auth "$RUNNER_TEMP/yield.npmrc" | |
| - name: Publish TypeScript package | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/yield.npmrc | |
| run: | | |
| set -euo pipefail | |
| for directory in dist/packages/npm/darwin-amd64 dist/packages/npm/darwin-arm64 dist/packages/npm/linux-amd64 dist/packages/npm/linux-arm64 dist/packages/npm/windows-amd64 dist/packages/npm/windows-arm64; do | |
| package="$(node -p "require('./${directory}/package.json').name")" | |
| if ! gcloud artifacts versions describe "$VERSION" \ | |
| --project="${{ vars.AR_PROJECT }}" --location="${{ vars.AR_LOCATION }}" \ | |
| --repository="${{ vars.AR_NPM_REPO }}" --package="$package" >/dev/null 2>&1; then | |
| npm publish "$directory" | |
| fi | |
| done | |
| if ! gcloud artifacts versions describe "$VERSION" \ | |
| --project="${{ vars.AR_PROJECT }}" --location="${{ vars.AR_LOCATION }}" \ | |
| --repository="${{ vars.AR_NPM_REPO }}" --package=@operatorstack/yield >/dev/null 2>&1; then | |
| npm publish dist/packages/npm/yield | |
| fi | |
| - name: Build and publish Python wheels | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| python -m pip install --quiet build twine keyrings.google-artifactregistry-auth | |
| mkdir -p dist/python-wheels | |
| for directory in dist/packages/python/*; do | |
| python -m build --wheel --outdir "$GITHUB_WORKSPACE/dist/python-wheels" "$directory" | |
| done | |
| existing="$(gcloud artifacts files list \ | |
| --project="${{ vars.AR_PROJECT }}" --location="${{ vars.AR_LOCATION }}" \ | |
| --repository="${{ vars.AR_PYTHON_REPO }}" --package=yieldskill --version="$VERSION" \ | |
| --format='value(name)' 2>/dev/null || true)" | |
| for wheel in dist/python-wheels/*; do | |
| filename="$(basename "$wheel")" | |
| if ! grep -Fq "$filename" <<<"$existing"; then | |
| twine upload \ | |
| --repository-url "https://${{ vars.AR_LOCATION }}-python.pkg.dev/${{ vars.AR_PROJECT }}/${{ vars.AR_PYTHON_REPO }}/" \ | |
| "$wheel" | |
| existing="${existing}"$'\n'"${filename}" | |
| fi | |
| done | |
| - name: Publish Rust runtime crates | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p dist/rust-runtime-publish | |
| for directory in dist/packages/rust/runtime/*; do | |
| name="$(sed -n 's/^name = "\(.*\)"/\1/p' "$directory/Cargo.toml")" | |
| cargo package --manifest-path "$directory/Cargo.toml" --allow-dirty --no-verify | |
| crate="$directory/target/package/${name}-${VERSION}.crate" | |
| checksum="$(sha256sum "$crate" | cut -d' ' -f1)" | |
| curl -fsSL "https://get.operatorstack.systems/cargo/index/$(printf '%s' "$name" | cut -c1-2)/$(printf '%s' "$name" | cut -c3-4)/${name}" \ | |
| -o "dist/rust-runtime-publish/${name}-index.json" || true | |
| node packaging/cargo-index.mjs --name "$name" --version "$VERSION" --checksum "$checksum" \ | |
| --output "dist/rust-runtime-publish/${name}-index.json" | |
| cp "$crate" dist/rust-runtime-publish/ | |
| done | |
| gcloud artifacts generic upload \ | |
| --project="${{ vars.AR_PROJECT }}" --location="${{ vars.AR_LOCATION }}" \ | |
| --repository="${{ vars.AR_GENERIC_REPO }}" --package=yield-rust --version="$VERSION" \ | |
| --source-directory=dist/rust-runtime-publish --skip-existing | |
| - name: Publish Rust public crate | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "$HOME/.cargo" dist/rust-public-publish | |
| printf '[registries.operatorstack]\nindex = "sparse+https://get.operatorstack.systems/cargo/index/"\n' > "$HOME/.cargo/config.toml" | |
| directory=dist/packages/rust/yieldskill | |
| rm -f "$directory/Cargo.lock" | |
| cargo generate-lockfile --manifest-path "$directory/Cargo.toml" | |
| cargo package --manifest-path "$directory/Cargo.toml" --registry operatorstack --allow-dirty | |
| crate="$directory/target/package/yieldskill-${VERSION}.crate" | |
| checksum="$(sha256sum "$crate" | cut -d' ' -f1)" | |
| curl -fsSL https://get.operatorstack.systems/cargo/index/yi/el/yieldskill \ | |
| -o dist/rust-public-publish/yieldskill-index.json || true | |
| node packaging/cargo-index.mjs --name yieldskill --version "$VERSION" --checksum "$checksum" \ | |
| --output dist/rust-public-publish/yieldskill-index.json | |
| cp "$crate" dist/rust-public-publish/ | |
| gcloud artifacts generic upload \ | |
| --project="${{ vars.AR_PROJECT }}" --location="${{ vars.AR_LOCATION }}" \ | |
| --repository="${{ vars.AR_GENERIC_REPO }}" --package=yield-rust --version="$VERSION" \ | |
| --source-directory=dist/rust-public-publish --skip-existing | |
| - name: Verify complete package history | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| versions="$(git tag --list 'v[0-9]*' --sort=v:refname \ | |
| | sed 's/^v//' \ | |
| | awk -F. '$1 > 0 || $2 > 1 || ($2 == 1 && $3 >= 23)' \ | |
| | awk -v current="$VERSION" '{ print; if ($0 == current) exit }' \ | |
| | paste -sd, -)" | |
| test -n "$versions" | |
| for attempt in {1..12}; do | |
| if node packaging/verify-registry-history.mjs --versions "$versions"; then | |
| exit 0 | |
| fi | |
| if [[ "$attempt" == 12 ]]; then | |
| echo "Package history was still incomplete after 12 checks." >&2 | |
| exit 1 | |
| fi | |
| sleep 10 | |
| done | |
| install-journeys: | |
| name: Install journeys (${{ matrix.target }}) | |
| needs: publish | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: linux-amd64 | |
| runner: ubuntu-24.04 | |
| - target: linux-arm64 | |
| runner: ubuntu-24.04-arm | |
| - target: darwin-amd64 | |
| runner: macos-15-intel | |
| - target: darwin-arm64 | |
| runner: macos-15 | |
| - target: windows-amd64 | |
| runner: windows-2025 | |
| - target: windows-arm64 | |
| runner: windows-11-arm | |
| runs-on: ${{ matrix.runner }} | |
| steps: | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: stable | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Install and test all language packages | |
| shell: bash | |
| env: | |
| VERSION: ${{ needs.publish.outputs.version }} | |
| PREVIOUS_VERSION: ${{ needs.publish.outputs.previous_version }} | |
| GOPROXY: https://get.operatorstack.systems/go,direct | |
| run: | | |
| set -euo pipefail | |
| workspace="$RUNNER_TEMP/yield-install" | |
| mkdir -p "$workspace/typescript" "$workspace/python" "$workspace/go" "$workspace/rust" | |
| python_cmd=python | |
| if ! command -v "$python_cmd" >/dev/null 2>&1; then python_cmd=python3; fi | |
| finish_starter() { | |
| FILE="$1" OLD="$2" NEW="$3" "$python_cmd" - <<'PY' | |
| import os | |
| from pathlib import Path | |
| path = Path(os.environ["FILE"]) | |
| source = path.read_text() | |
| old = os.environ["OLD"] | |
| if old not in source: | |
| raise SystemExit(f"starter line not found in {path}") | |
| path.write_text(source.replace(old, os.environ["NEW"], 1)) | |
| PY | |
| } | |
| expect_incomplete_starter() { | |
| if "$@" > "$RUNNER_TEMP/starter-test.log" 2>&1; then | |
| echo "starter workflow unexpectedly passed" >&2 | |
| exit 1 | |
| fi | |
| grep -F "replace the starter workflow and fixture before testing" "$RUNNER_TEMP/starter-test.log" | |
| } | |
| cd "$workspace/typescript" | |
| git init -q | |
| npm init -y >/dev/null | |
| npm install --save-exact "@operatorstack/yield@${VERSION}" --registry=https://get.operatorstack.systems/npm/ | |
| npm exec -- yskill --version | grep -F "yskill ${VERSION}" | |
| npm exec -- yskill init skill --language typescript --description "Run this workflow when checking the installed TypeScript package." | |
| expect_incomplete_starter npm exec -- yskill doctor skill --test | |
| finish_starter skill/main.ts \ | |
| ' ctx.blocked("replace the starter workflow and fixture before testing")' \ | |
| ' return { installed: true }' | |
| npm exec -- yskill doctor skill --test | |
| npm exec -- yskill register skill --agent cursor,codex,claude-code | |
| npm exec -- yskill doctor skill --agent cursor,codex,claude-code | |
| cd "$workspace/python" | |
| git init -q | |
| "$python_cmd" -m venv .venv | |
| if [[ "$RUNNER_OS" == "Windows" ]]; then python_bin=.venv/Scripts/python; else python_bin=.venv/bin/python; fi | |
| "$python_bin" -m pip install "yieldskill==${VERSION}" --index-url https://get.operatorstack.systems/pip/simple/ | |
| "$python_bin" -m yieldskill --version | grep -F "yskill ${VERSION}" | |
| "$python_bin" -m yieldskill init skill --language python --description "Run this workflow when checking the installed Python package." | |
| expect_incomplete_starter "$python_bin" -m yieldskill doctor skill --test | |
| finish_starter skill/main.py \ | |
| ' ctx.blocked("replace the starter workflow and fixture before testing")' \ | |
| $' python = ctx.run_command("venv-python", "python --version", 30)\n ctx.require(python.exit_code == 0, "the selected Python environment is on PATH", python)\n tool = ctx.run_command("venv-tool", "pip --version", 30)\n ctx.require(tool.exit_code == 0 and ".venv" in tool.stdout.lower(), "environment-local console scripts are on PATH", tool)\n return {"installed": True}' | |
| "$python_bin" -m yieldskill doctor skill --test | |
| "$python_bin" -m yieldskill register skill --agent cursor,codex,claude-code | |
| "$python_bin" -m yieldskill doctor skill --agent cursor,codex,claude-code | |
| cd "$workspace/go" | |
| mkdir -p .yield/bin | |
| GOBIN="$PWD/.yield/bin" go install "github.com/operatorstack/yield/cmd/yskill@v${VERSION}" | |
| if [[ "$RUNNER_OS" == "Windows" ]]; then go_cli=.yield/bin/yskill.exe; go_launcher='.\.yield\bin\yskill.exe'; else go_cli=.yield/bin/yskill; go_launcher=.yield/bin/yskill; fi | |
| "$go_cli" --version | grep -F "yskill ${VERSION}" | |
| "$go_cli" init skill --language go --description "Run this workflow when checking the installed Go package." | tee "$RUNNER_TEMP/go-init.log" | |
| grep -F "$go_launcher doctor 'skill' --test" "$RUNNER_TEMP/go-init.log" | |
| grep -F "$go_launcher run 'skill'" skill/SKILL.md | |
| expect_incomplete_starter "$go_cli" doctor skill --test | |
| finish_starter skill/main.go \ | |
| 'return yield.Outcome{}, ctx.Blocked("replace the starter workflow and fixture before testing")' \ | |
| 'return ctx.Complete(map[string]any{"installed": true})' | |
| "$go_cli" doctor skill --test | |
| "$go_cli" register skill --agent cursor,codex,claude-code | |
| "$go_cli" doctor skill --agent cursor,codex,claude-code | |
| cd "$workspace/rust" | |
| cargo install "yieldskill@${VERSION}" --index sparse+https://get.operatorstack.systems/cargo/index/ --locked --root .yield | |
| if [[ "$RUNNER_OS" == "Windows" ]]; then rust_cli=.yield/bin/yskill.exe; rust_launcher='.\.yield\bin\yskill.exe'; else rust_cli=.yield/bin/yskill; rust_launcher=.yield/bin/yskill; fi | |
| "$rust_cli" --version | grep -F "yskill ${VERSION}" | |
| "$rust_cli" init skill --language rust --description "Run this workflow when checking the installed Rust package." | tee "$RUNNER_TEMP/rust-init.log" | |
| grep -F "$rust_launcher doctor 'skill' --test" "$RUNNER_TEMP/rust-init.log" | |
| grep -F "$rust_launcher run 'skill'" skill/SKILL.md | |
| expect_incomplete_starter "$rust_cli" doctor skill --test | |
| finish_starter skill/src/main.rs \ | |
| ' Err(ctx.blocked("replace the starter workflow and fixture before testing"))' \ | |
| ' Ok(json!({"installed": true}))' | |
| "$rust_cli" doctor skill --test | |
| "$rust_cli" register skill --agent cursor,codex,claude-code | |
| "$rust_cli" doctor skill --agent cursor,codex,claude-code | |
| if [[ -n "$PREVIOUS_VERSION" ]]; then | |
| cd "$workspace" | |
| mkdir -p previous/typescript previous/python previous/go previous/rust | |
| cd previous/typescript | |
| npm init -y >/dev/null | |
| npm install --save-exact "@operatorstack/yield@${PREVIOUS_VERSION}" --registry=https://get.operatorstack.systems/npm/ | |
| npm exec -- yskill --version | grep -F "yskill ${PREVIOUS_VERSION}" | |
| cd "$workspace/previous/python" | |
| "$python_cmd" -m venv .venv | |
| if [[ "$RUNNER_OS" == "Windows" ]]; then previous_python=.venv/Scripts/python; else previous_python=.venv/bin/python; fi | |
| "$previous_python" -m pip install "yieldskill==${PREVIOUS_VERSION}" --index-url https://get.operatorstack.systems/pip/simple/ | |
| "$previous_python" -m yieldskill --version | grep -F "yskill ${PREVIOUS_VERSION}" | |
| cd "$workspace/previous/go" | |
| mkdir -p .yield/bin | |
| GOBIN="$PWD/.yield/bin" go install "github.com/operatorstack/yield/cmd/yskill@v${PREVIOUS_VERSION}" | |
| if [[ "$RUNNER_OS" == "Windows" ]]; then previous_go=.yield/bin/yskill.exe; else previous_go=.yield/bin/yskill; fi | |
| "$previous_go" --version | grep -F "yskill ${PREVIOUS_VERSION}" | |
| cd "$workspace/previous/rust" | |
| cargo install "yieldskill@${PREVIOUS_VERSION}" --index sparse+https://get.operatorstack.systems/cargo/index/ --locked --root .yield | |
| if [[ "$RUNNER_OS" == "Windows" ]]; then previous_rust=.yield/bin/yskill.exe; else previous_rust=.yield/bin/yskill; fi | |
| "$previous_rust" --version | grep -F "yskill ${PREVIOUS_VERSION}" | |
| fi | |
| finalize-release: | |
| name: Publish verified release receipt | |
| needs: [publish, install-journeys] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Attach package contract and publish the draft | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| VERSION: ${{ needs.publish.outputs.version }} | |
| PREVIOUS_VERSION: ${{ needs.publish.outputs.previous_version }} | |
| SOURCE_SHA: ${{ needs.publish.outputs.source_sha }} | |
| run: | | |
| set -euo pipefail | |
| if ! gh release view "v${VERSION}" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then | |
| gh release create "v${VERSION}" \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --title "v${VERSION}" \ | |
| --generate-notes \ | |
| --draft \ | |
| --verify-tag | |
| fi | |
| jq -n \ | |
| --arg version "$VERSION" \ | |
| --arg previous_version "$PREVIOUS_VERSION" \ | |
| --arg source_sha "$SOURCE_SHA" \ | |
| --arg repository "$GITHUB_REPOSITORY" \ | |
| '{ | |
| schema_version: 1, | |
| version: $version, | |
| previous_version: $previous_version, | |
| source_sha: $source_sha, | |
| repository: $repository, | |
| languages: ["typescript", "python", "go", "rust"], | |
| targets: ["linux-amd64", "linux-arm64", "darwin-amd64", "darwin-arm64", "windows-amd64", "windows-arm64"], | |
| package_history_from: "0.1.23", | |
| current_and_previous_install_journeys: true, | |
| package_history_complete: true | |
| }' > yield-package-contract.json | |
| gh release upload "v${VERSION}" yield-package-contract.json --repo "$GITHUB_REPOSITORY" --clobber | |
| gh release edit "v${VERSION}" --repo "$GITHUB_REPOSITORY" --draft=false --latest |