Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 73 additions & 3 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
name: Release Please

# `next` accumulates validated SDK changes in one versioned PR to `main`.
# Merging that PR creates the GitHub release; the package publishing workflow
# runs from the release event.
on:
push:
branches:
- next
- main

permissions:
contents: read

concurrency:
group: release-please
cancel-in-progress: false

jobs:
release-please:
if: github.repository == 'kernel/hypeman-go'
Expand All @@ -26,7 +35,68 @@ jobs:
permission-pull-requests: write
permission-workflows: write

- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
id: release
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '18.20.2'

- name: Set up pnpm
uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
with:
token: ${{ steps.release-token.outputs.token }}
version: '9.11.0'
run_install: false

- name: Build pinned release tooling
id: tooling
env:
RELEASE_PLEASE_DIR: ${{ runner.temp }}/release-please
RELEASE_PLEASE_SHA: a116e1e520e0f87824acf46a2e79c91d41e819d7
run: |
set -euo pipefail
rm -rf "$RELEASE_PLEASE_DIR"
git init "$RELEASE_PLEASE_DIR"
git -C "$RELEASE_PLEASE_DIR" remote add origin https://github.com/stainless-api/release-please.git
git -C "$RELEASE_PLEASE_DIR" fetch --depth=1 origin "$RELEASE_PLEASE_SHA"
git -C "$RELEASE_PLEASE_DIR" checkout --detach FETCH_HEAD
pnpm --dir "$RELEASE_PLEASE_DIR" install --frozen-lockfile
pnpm --dir "$RELEASE_PLEASE_DIR" build
echo "cli=$RELEASE_PLEASE_DIR/build/src/bin/release-please.js" >> "$GITHUB_OUTPUT"

- name: Open or update the release PR
if: github.ref_name == 'next'
env:
GH_TOKEN: ${{ steps.release-token.outputs.token }}
RELEASE_PLEASE: ${{ steps.tooling.outputs.cli }}
run: |
set -euo pipefail
node "$RELEASE_PLEASE" release-pr \
--repo-url "$GITHUB_REPOSITORY" \
--token "$GH_TOKEN" \
--target-branch main \
--changes-branch next

- name: Remove the legacy promotion PR
if: github.ref_name == 'next'
env:
GH_TOKEN: ${{ steps.release-token.outputs.token }}
run: |
set -euo pipefail
legacy=$(gh pr list --repo "$GITHUB_REPOSITORY" --head stainless/release \
--state open --json number --jq '.[].number')
for pr in $legacy; do
gh pr close "$pr" --repo "$GITHUB_REPOSITORY" \
--comment "Superseded by the versioned release PR from next to main."
done
gh api -X DELETE "repos/$GITHUB_REPOSITORY/git/refs/heads/stainless/release" >/dev/null 2>&1 || true

- name: Create the GitHub release
if: github.ref_name == 'main'
env:
GH_TOKEN: ${{ steps.release-token.outputs.token }}
RELEASE_PLEASE: ${{ steps.tooling.outputs.cli }}
run: |
set -euo pipefail
node "$RELEASE_PLEASE" github-release \
--repo-url "$GITHUB_REPOSITORY" \
--token "$GH_TOKEN" \
--target-branch main
114 changes: 93 additions & 21 deletions .github/workflows/stlc-promote.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
name: Promote SDKs
name: Promote SDK changes

# Manually fast-forwards production main to the reviewed staging main. The
# ancestor check refuses divergent histories; this workflow never force-pushes.
# Staging is the generator's integration history. Production `next` is the
# developer-facing queue for the next release. This workflow combines the
# latest released state with validated staging changes, then advances `next`.
# Release automation maintains the single versioned PR from `next` to `main`.
on:
push:
branches: [main]
workflow_dispatch: {}

permissions:
Expand All @@ -12,7 +16,9 @@ jobs:
promote:
if: github.repository == 'kernel/hypeman-go-staging'
runs-on: ${{ vars.STLC_RUNNER || 'ubuntu-latest' }}
environment: production
concurrency:
group: stlc-promote
cancel-in-progress: true
steps:
- name: Check out staging
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand All @@ -29,33 +35,99 @@ jobs:
owner: kernel
repositories: hypeman-go
permission-contents: write
permission-pull-requests: write
permission-workflows: write

- name: Fetch production main
- name: Fetch production branches
id: production
env:
GH_TOKEN: ${{ steps.production-token.outputs.token }}
PRODUCTION_REPO: kernel/hypeman-go
run: |
git remote add production "https://x-access-token:${GH_TOKEN}@github.com/${PRODUCTION_REPO}.git"
set -euo pipefail
git remote add production \
"https://x-access-token:${GH_TOKEN}@github.com/${PRODUCTION_REPO}.git"
git fetch production main

- name: Check whether production already has staging's content
id: diff
run: |
MERGED=$(git merge-tree --write-tree production/main origin/main) || MERGED=conflict
PRODUCTION_TREE=$(git rev-parse 'production/main^{tree}')
if [ "$MERGED" = "$PRODUCTION_TREE" ]; then
echo "Production already contains staging's content. Nothing to promote."
echo "synced=true" >> "$GITHUB_OUTPUT"
if git ls-remote --exit-code --heads production next >/dev/null 2>&1; then
git fetch production next
echo "has_next=true" >> "$GITHUB_OUTPUT"
else
echo "synced=false" >> "$GITHUB_OUTPUT"
echo "has_next=false" >> "$GITHUB_OUTPUT"
fi

- name: Promote staging to production
if: steps.diff.outputs.synced == 'false'
- name: Prepare the next release branch
env:
APP_SLUG: ${{ steps.production-token.outputs.app-slug }}
GH_TOKEN: ${{ steps.production-token.outputs.token }}
HAS_NEXT: ${{ steps.production.outputs.has_next }}
PRODUCTION_REPO: kernel/hypeman-go
run: |
if ! git merge-base --is-ancestor production/main origin/main; then
echo "::error title=Promote blocked::production/main is not an ancestor of staging main. Back-sync production first."
set -euo pipefail
bot_id=$(gh api "/users/${APP_SLUG}[bot]" --jq .id)
git config user.name "${APP_SLUG}[bot]"
git config user.email "${bot_id}+${APP_SLUG}[bot]@users.noreply.github.com"

open_conflict_pr() {
source_ref=$1
source_name=$2
advance_next=$3
conflict_branch=stlc/promotion-conflict

git merge --abort
existing=$(gh pr list --repo "$PRODUCTION_REPO" --base next \
--head "$conflict_branch" --state open --json url --jq '.[0].url // ""')
if [ -n "$existing" ]; then
echo "::error title=SDK promotion blocked::Resolve the existing recovery PR: $existing"
exit 1
fi

if [ "$advance_next" = "true" ]; then
git push production HEAD:refs/heads/next
fi
git push production "$source_ref:refs/heads/$conflict_branch" --force

body=$(mktemp)
printf '%s\n' \
'## SDK promotion conflict' \
'' \
"The automated promotion could not merge $source_name into the pending next release." \
'' \
'Resolve the conflicts on this branch, validate the SDK, mark this PR ready, and merge it with a merge commit.' \
'' \
'After merging, rerun the staging Promote SDK changes workflow to include any newer generated changes.' \
> "$body"
recovery_url=$(gh pr create --repo "$PRODUCTION_REPO" --draft \
--base next --head "$conflict_branch" \
--title 'chore: resolve SDK promotion conflict' --body-file "$body")
echo "::error title=SDK promotion conflict::Resolve the recovery PR: $recovery_url"
exit 1
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conflict helper assumes active merge

Medium Severity

open_conflict_pr always runs git merge --abort before opening a recovery PR. That only works when git merge left an in-progress conflicted merge. If merge fails for other reasons (for example unrelated histories), there is no MERGE_HEAD, abort fails under set -e, and the recovery PR is never created.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit f8f5daf. Configure here.


if [ "$HAS_NEXT" = "true" ]; then
git checkout -B stlc/promote-next production/next
else
git checkout -B stlc/promote-next production/main
fi

if ! git merge-base --is-ancestor production/main HEAD; then
if ! git merge --no-edit production/main; then
open_conflict_pr production/main 'production main' false
fi
fi
if ! git merge-base --is-ancestor origin/main HEAD; then
if ! git merge --no-edit origin/main; then
open_conflict_pr origin/main 'validated staging changes' true
fi
fi

if [ "$HAS_NEXT" = "true" ]; then
git merge-base --is-ancestor production/next HEAD
fi
git push production origin/main:refs/heads/main

- name: Update the pending release
env:
GH_TOKEN: ${{ steps.production-token.outputs.token }}
run: |
set -euo pipefail
git push production HEAD:refs/heads/next
echo "Updated production next; the versioned release PR will be opened or refreshed."
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.23.0"
".": "0.24.0"
}
2 changes: 1 addition & 1 deletion .stats.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
configured_endpoints: 58
configured_endpoints: 61
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## [0.24.0](https://github.com/kernel/hypeman-go/compare/v0.23.0...v0.24.0) (2026-08-12)


### Features

* Add QEMU microvm hypervisor backend ([f8a5d45](https://github.com/kernel/hypeman-go/commit/f8a5d4586ff9c812b8b5f0aa342bb0b7b47bd3dc))
* Add request header authorization to ingress rules ([fb25cdc](https://github.com/kernel/hypeman-go/commit/fb25cdc43a87da159d80da7560d87e300754907a))
* chore(stlc): seal custom-code tracking files ([f89533d](https://github.com/kernel/hypeman-go/commit/f89533d396385abb4ea110bb33266e006f44a46a))

## 0.23.0 (2026-08-06)

Full Changelog: [v0.22.0...v0.23.0](https://github.com/kernel/hypeman-go/compare/v0.22.0...v0.23.0)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Or to pin the version:
<!-- x-release-please-start-version -->

```sh
go get -u 'github.com/kernel/hypeman-go@v0.23.0'
go get -u 'github.com/kernel/hypeman-go@v0.24.0'
```

<!-- x-release-please-end -->
Expand Down
18 changes: 18 additions & 0 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,21 @@ Methods:
- <code title="delete /builds/{id}">client.Builds.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#BuildService.Cancel">Cancel</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) <a href="https://pkg.go.dev/builtin#error">error</a></code>
- <code title="get /builds/{id}/events">client.Builds.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#BuildService.Events">Events</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, query <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#BuildEventsParams">BuildEventsParams</a>) (\*<a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#BuildEvent">BuildEvent</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
- <code title="get /builds/{id}">client.Builds.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#BuildService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (\*<a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Build">Build</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>

# Pushes

Params Types:

- <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#CreatePushRequestParam">CreatePushRequestParam</a>
- <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#PushCredentialsParam">PushCredentialsParam</a>

Response Types:

- <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Push">Push</a>
- <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#PushStatus">PushStatus</a>

Methods:

- <code title="post /pushes">client.Pushes.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#PushService.New">New</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, body <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#PushNewParams">PushNewParams</a>) (\*<a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Push">Push</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
- <code title="get /pushes">client.Pushes.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#PushService.List">List</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) (\*[]<a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Push">Push</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
- <code title="get /pushes/{id}">client.Pushes.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#PushService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (\*<a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Push">Push</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
2 changes: 2 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Client struct {
Resources ResourceService
Builders BuilderService
Builds BuildService
Pushes PushService
}

// DefaultClientOptions read from the environment (HYPEMAN_API_KEY,
Expand Down Expand Up @@ -70,6 +71,7 @@ func NewClient(opts ...option.RequestOption) (r Client) {
r.Resources = NewResourceService(opts...)
r.Builders = NewBuilderService(opts...)
r.Builds = NewBuildService(opts...)
r.Pushes = NewPushService(opts...)

return
}
Expand Down
Loading
Loading