Skip to content

Latest commit

聽

History

History
154 lines (128 loc) 路 8.57 KB

releasing.md

File metadata and controls

154 lines (128 loc) 路 8.57 KB

Releasing Kit

Kit's source and binary releases are public at speakeasy-api/kit. GitHub Actions builds and publishes release artifacts on demand.

Publish a release

Ordinary pull requests do not update release versions. In GitHub, open Actions > release, select Run workflow on main, and run it. Do not create or push the release tag first.

The workflow verifies that the version in Cargo.toml matches the latest release tag, then examines commits since that tag. Any breaking commit advances the minor version; other commits advance the patch version. It prepares a release commit that updates Cargo.toml and Cargo.lock, then runs formatting, Clippy, tests, artifact builds, and release-note generation against that exact commit.

After every required build succeeds, the workflow atomically advances main to the release commit and creates the release tag. If main advances while the workflow is running, the release stops without updating either ref and must be run again. A rerun of the failed jobs resumes publication when the release commit and tag were pushed but a later publishing step failed.

The atomic push authenticates with the repository's write-enabled kit-release deploy key. Add Deploy keys to the main ruleset bypass list and store that key's private half in the repository Actions secret RELEASE_DEPLOY_KEY. The default GITHUB_TOKEN cannot bypass the pull-request and status-check rules.

The newly built Linux Kit CLI reviews every change since the previous release and writes the GitHub release notes before the workflow attaches the archives and checksums. Configure the repository Actions secret OPENROUTER_API_KEY for this step. Kit uses the release-notes skill with the openai/gpt-5.6-terra OpenRouter model.

Slack announcement smoke test

After the GitHub release and container images are published, announce-release runs the newly released Linux CLI with the announce-kit-release skill. It uses the same RELEASE_NOTES_MODEL setting and OPENROUTER_API_KEY secret as the release-note generator. Kit posts directly to the configured Slack channel ID through MCP tools, checks the posted message returned by Slack, and retrieves its permalink. It does not search for the channel, so renaming it does not break the announcement destination. This exercises skill activation, MCP discovery and tool calling, and static HTTP header authentication. The bot does not need channel-history access: the skill's restricted-bot path verifies the actual message, channel, timestamp, and formatting in the send response instead of fetching channel history. Verification is performed by the agent, not an independent Slack API assertion. The job fails if configuration is missing, Kit fails, or its final response is not a single Slack message permalink. The permalink is written to the job summary.

Configure these repository Actions values before releasing:

Kind Name Value
Variable SLACK_CHANNEL_ID Stable Slack channel ID, not its name. Resolve it once during setup; releases use the ID directly.
Variable SLACK_MCP_URL Trusted HTTPS Gram MCP endpoint exposing the built-in Slack tools and accepting consumer API keys.
Variable GRAM_ENVIRONMENT Slug of the environment in the MCP's project containing the Slack bot token.
Secret GRAM_API_KEY Gram API key with consumer scope authorized for the MCP's project.

Store the raw Slack bot token as the secret SLACK_BOT_TOKEN in the selected Gram environment, not in the workflow's MCP authentication header. If the MCP has variable-provisioning controls, allow this variable from the request-selected configuration (User), rather than filtering it to an attached System environment. Kit sends the Gram key as Authorization: Bearer <key> and selects the environment with Gram-Environment; Gram uses the Slack token for the upstream API calls. An issuer-gated endpoint that accepts only OAuth user sessions is not sufficient.

The MCP must expose posting with the actual posted message in its response and permalink retrieval. Invite the bot to the target channel and grant chat:write. The bot must be a channel member with this scope; MCP authentication and channel lookup can succeed even when posting would fail with not_in_channel. The announcement does not use channel lookup, message search, or channel-history tools. The final permalink must refer to the configured channel ID. Use only a trusted MCP endpoint: it receives the Gram API key.

The workflow writes a private temporary MCP JSON file with an explicit headers.Authorization and headers.Gram-Environment, passes it via --mcp-config, and removes it on exit. The captured CLI response uses the same temporary directory. On failure, the job logs the CLI exit code and up to 8 KiB of its final response (without the session ID) to explain which step failed. It does not upload the config or Kit session as an artifact.

An announcement failure marks the workflow failed but does not roll back the already published release. Before rerunning the announcement job, check Slack: if posting succeeded but verification failed, a rerun can produce a duplicate.

macOS signing and notarization

The release workflow signs the standalone Mach-O executable with the code-signing identifier com.speakeasy.kit. It also archives Kit.app, signs its bundled helper first with com.speakeasy.kit, and then signs the outer app with com.speakeasy.kit.desktop. Both signatures enable the Hardened Runtime and use an explicit empty entitlement set: the unsandboxed app and CLI need no Hardened Runtime exceptions. The workflow submits both products to Apple's notary service, staples and validates the app ticket, verifies nested signatures, and runs Gatekeeper assessment before packaging Kit-vVERSION-aarch64-apple-darwin.zip. This Developer ID distribution does not require an Apple App ID or provisioning profile.

Create the credentials as follows:

  1. In Keychain Access, use Certificate Assistant > Request a Certificate From a Certificate Authority to save a certificate signing request (CSR).

  2. In Apple Developer Certificates, Identifiers & Profiles, create a Developer ID Application certificate from that CSR. If that option is not available for your role, ask the team's Account Holder to create it. Import the downloaded certificate on the Mac that created the CSR.

  3. In Keychain Access, export the Developer ID certificate together with its private key as a password-protected PKCS#12 (.p12) file. Record the exact identity shown by security find-identity -v -p codesigning. It normally has the form Developer ID Application: <Organization> (<TEAM_ID>).

  4. In App Store Connect Users and Access > Integrations, create a team API key that can access the notary service. Record its key ID and issuer ID, and retain the downloaded .p8; Apple does not allow it to be downloaded again.

  5. Store the .p12, its password, and the .p8 in the company's secret manager. Configure these repository Actions secrets:

    • MACOS_CERTIFICATE_P12_BASE64: base64-encoded .p12
    • MACOS_CERTIFICATE_PASSWORD: .p12 export password
    • MACOS_SIGNING_IDENTITY: exact Keychain identity from step 3
    • APPLE_API_KEY_P8_BASE64: base64-encoded .p8
    • APPLE_API_KEY_ID: App Store Connect API key ID
    • APPLE_API_ISSUER_ID: App Store Connect issuer ID

For example, from a trusted Mac authenticated to GitHub CLI:

repo=speakeasy-api/kit
base64 < DeveloperIDApplication.p12 | gh secret set MACOS_CERTIFICATE_P12_BASE64 -R "$repo"
read -r -s 'p12_password?P12 password: '; echo
printf %s "$p12_password" | gh secret set MACOS_CERTIFICATE_PASSWORD -R "$repo"
unset p12_password
printf %s 'Developer ID Application: Example Corp (TEAMID)' | gh secret set MACOS_SIGNING_IDENTITY -R "$repo"
base64 < AuthKey_KEYID.p8 | gh secret set APPLE_API_KEY_P8_BASE64 -R "$repo"
printf %s 'KEYID' | gh secret set APPLE_API_KEY_ID -R "$repo"
printf %s 'issuer-uuid' | gh secret set APPLE_API_ISSUER_ID -R "$repo"

The workflow fails on a partial configuration rather than silently publishing an unsigned build. With none of these secrets configured, it still publishes the standalone archive and Kit.app ZIP unsigned and unnotarized, and identifies the release as such in the release notes. XcodeGen 2.45.4 is downloaded with a pinned SHA-256 before the app archive is generated; the checked-in project is independently regenerated and drift-checked in CI.

Verify a release

Install a specific published version from a clean environment:

mise use github:speakeasy-api/kit@0.1.83
kit --version