Skip to content

Commit 8569440

Browse files
committed
ci: publish releases from public repository
1 parent e84c29f commit 8569440

9 files changed

Lines changed: 259 additions & 239 deletions

File tree

.agents/skills/announce-kit-release/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ description: Use when asked to summarize the current Kit release against the pre
1111
4. Include mise installation instructions:
1212

1313
```bash
14-
mise use -g github:danielkov/kit-releases
14+
mise use -g github:speakeasy-api/kit
1515
kit --version
1616
```
1717

1818
Include the version-pinned form with the current version number:
1919

2020
```bash
21-
mise use -g github:danielkov/kit-releases@<version-without-v-prefix>
21+
mise use -g github:speakeasy-api/kit@<version-without-v-prefix>
2222
```
2323

2424
5. Search Slack for the requested channel by name and resolve its current channel ID. Never hard-code a Slack channel ID.

.github/workflows/release.yml

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
name: release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: read
10+
11+
env:
12+
CARGO_TERM_COLOR: always
13+
14+
jobs:
15+
verify:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: dtolnay/rust-toolchain@master
20+
with:
21+
toolchain: 1.94.0
22+
components: rustfmt, clippy
23+
- name: Verify release tag matches Cargo version
24+
shell: bash
25+
run: |
26+
version=$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -1)
27+
escaped_version=${version//./\.}
28+
if [[ $GITHUB_REF_NAME != "v$version" \
29+
&& ! $GITHUB_REF_NAME =~ ^v${escaped_version}-pre(\.[0-9]+)?$ ]]; then
30+
echo "expected v${version}, v${version}-pre, or v${version}-pre.N" >&2
31+
exit 1
32+
fi
33+
- run: cargo fmt --check
34+
- run: cargo clippy --locked --all-targets --all-features -- -D warnings
35+
- run: cargo test --locked
36+
env:
37+
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY || 'ci-placeholder' }}
38+
39+
linux-x64:
40+
needs: verify
41+
runs-on: ubuntu-22.04
42+
steps:
43+
- uses: actions/checkout@v4
44+
- uses: dtolnay/rust-toolchain@master
45+
with:
46+
toolchain: 1.94.0
47+
targets: x86_64-unknown-linux-gnu
48+
- name: Build and package
49+
shell: bash
50+
run: |
51+
version=${GITHUB_REF_NAME#v}
52+
cargo build --locked --release --target x86_64-unknown-linux-gnu
53+
mkdir -p stage dist
54+
cp target/x86_64-unknown-linux-gnu/release/kit stage/kit
55+
stage/kit --version
56+
tar -C stage -czf "dist/kit-v${version}-x86_64-unknown-linux-gnu.tar.gz" kit
57+
- uses: actions/upload-artifact@v4
58+
with:
59+
name: kit-linux-x64
60+
path: dist/*
61+
if-no-files-found: error
62+
63+
macos-arm64:
64+
needs: verify
65+
runs-on: macos-15
66+
outputs:
67+
signed: ${{ steps.signing.outputs.enabled }}
68+
env:
69+
MACOS_CERTIFICATE_P12_BASE64: ${{ secrets.MACOS_CERTIFICATE_P12_BASE64 }}
70+
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
71+
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }}
72+
APPLE_API_KEY_P8_BASE64: ${{ secrets.APPLE_API_KEY_P8_BASE64 }}
73+
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
74+
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
75+
steps:
76+
- uses: actions/checkout@v4
77+
- uses: dtolnay/rust-toolchain@master
78+
with:
79+
toolchain: 1.94.0
80+
targets: aarch64-apple-darwin
81+
- name: Check optional signing credentials
82+
id: signing
83+
shell: bash
84+
run: |
85+
credentials=(
86+
MACOS_CERTIFICATE_P12_BASE64
87+
MACOS_CERTIFICATE_PASSWORD
88+
MACOS_SIGNING_IDENTITY
89+
APPLE_API_KEY_P8_BASE64
90+
APPLE_API_KEY_ID
91+
APPLE_API_ISSUER_ID
92+
)
93+
configured=0
94+
for name in "${credentials[@]}"; do
95+
if [[ -n ${!name:-} ]]; then
96+
configured=$((configured + 1))
97+
fi
98+
done
99+
if [[ $configured -eq 0 ]]; then
100+
echo "Apple credentials are not configured; building an unsigned macOS release."
101+
echo "enabled=false" >> "$GITHUB_OUTPUT"
102+
elif [[ $configured -eq ${#credentials[@]} ]]; then
103+
echo "enabled=true" >> "$GITHUB_OUTPUT"
104+
else
105+
echo "Apple signing credentials are only partially configured." >&2
106+
exit 1
107+
fi
108+
- name: Import Developer ID certificate
109+
if: steps.signing.outputs.enabled == 'true'
110+
shell: bash
111+
run: |
112+
keychain=$RUNNER_TEMP/kit-signing.keychain-db
113+
certificate=$RUNNER_TEMP/kit-signing.p12
114+
printf '%s' "$MACOS_CERTIFICATE_P12_BASE64" | base64 --decode > "$certificate"
115+
security create-keychain -p "$MACOS_CERTIFICATE_PASSWORD" "$keychain"
116+
security set-keychain-settings -lut 21600 "$keychain"
117+
security unlock-keychain -p "$MACOS_CERTIFICATE_PASSWORD" "$keychain"
118+
security import "$certificate" -P "$MACOS_CERTIFICATE_PASSWORD" \
119+
-f pkcs12 -k "$keychain" -T /usr/bin/codesign
120+
security set-key-partition-list -S apple-tool:,apple: -s \
121+
-k "$MACOS_CERTIFICATE_PASSWORD" "$keychain"
122+
security list-keychains -d user -s "$keychain" login.keychain-db
123+
- name: Build and package
124+
shell: bash
125+
env:
126+
SIGN_RELEASE: ${{ steps.signing.outputs.enabled }}
127+
run: |
128+
version=${GITHUB_REF_NAME#v}
129+
cargo build --locked --release --target aarch64-apple-darwin
130+
mkdir -p stage dist
131+
cp target/aarch64-apple-darwin/release/kit stage/kit
132+
if [[ $SIGN_RELEASE == true ]]; then
133+
codesign --force --options runtime --timestamp \
134+
--identifier com.danielkov.kit \
135+
--sign "$MACOS_SIGNING_IDENTITY" stage/kit
136+
codesign --verify --strict --verbose=2 stage/kit
137+
ditto -c -k --keepParent stage/kit "$RUNNER_TEMP/kit-notarization.zip"
138+
api_key=$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8
139+
trap 'rm -f "$api_key"' EXIT
140+
printf '%s' "$APPLE_API_KEY_P8_BASE64" | base64 --decode > "$api_key"
141+
chmod 600 "$api_key"
142+
notary_log=$RUNNER_TEMP/kit-notarization.log
143+
xcrun notarytool submit "$RUNNER_TEMP/kit-notarization.zip" \
144+
--key "$api_key" \
145+
--key-id "$APPLE_API_KEY_ID" \
146+
--issuer "$APPLE_API_ISSUER_ID" \
147+
--wait 2>&1 | tee "$notary_log"
148+
if ! grep -Eq '(^|[[:space:]])status: Accepted([[:space:]]|$)' "$notary_log"; then
149+
echo "Apple did not accept the notarization submission." >&2
150+
exit 1
151+
fi
152+
fi
153+
stage/kit --version
154+
tar -C stage -czf "dist/kit-v${version}-aarch64-apple-darwin.tar.gz" kit
155+
- uses: actions/upload-artifact@v4
156+
with:
157+
name: kit-macos-arm64
158+
path: dist/*
159+
if-no-files-found: error
160+
161+
publish:
162+
needs: [linux-x64, macos-arm64]
163+
runs-on: ubuntu-latest
164+
permissions:
165+
contents: write
166+
env:
167+
GH_TOKEN: ${{ github.token }}
168+
MACOS_SIGNED: ${{ needs.macos-arm64.outputs.signed }}
169+
steps:
170+
- uses: actions/download-artifact@v4
171+
with:
172+
pattern: kit-*
173+
path: dist
174+
merge-multiple: true
175+
- name: Publish release
176+
shell: bash
177+
run: |
178+
cd dist
179+
sha256sum kit-*.tar.gz > SHA256SUMS
180+
prerelease=false
181+
if [[ $GITHUB_REF_NAME == *-pre* ]]; then
182+
prerelease=true
183+
fi
184+
if [[ $MACOS_SIGNED == true ]]; then
185+
signing_note="The macOS binary is Developer ID signed and notarized."
186+
else
187+
signing_note="The macOS binary is unsigned and not notarized."
188+
fi
189+
title="Kit ${GITHUB_REF_NAME#v}"
190+
notes="Prebuilt Kit binaries. $signing_note"
191+
if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
192+
gh release upload "$GITHUB_REF_NAME" kit-*.tar.gz SHA256SUMS \
193+
--clobber --repo "$GITHUB_REPOSITORY"
194+
else
195+
create_args=(
196+
"$GITHUB_REF_NAME" kit-*.tar.gz SHA256SUMS
197+
--repo "$GITHUB_REPOSITORY"
198+
--title "$title"
199+
--notes "$notes"
200+
)
201+
if [[ $prerelease == true ]]; then
202+
create_args+=(--prerelease)
203+
fi
204+
gh release create "${create_args[@]}"
205+
fi
206+
edit_args=(
207+
"$GITHUB_REF_NAME"
208+
--repo "$GITHUB_REPOSITORY"
209+
--title "$title"
210+
--notes "$notes"
211+
--draft=false
212+
--prerelease="$prerelease"
213+
)
214+
if [[ $prerelease == false ]]; then
215+
edit_args+=(--latest)
216+
fi
217+
gh release edit "${edit_args[@]}"

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "kit"
3-
version = "0.1.82"
3+
version = "0.1.83"
44
edition = "2024"
55
rust-version = "1.94.0"
66
publish = false

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,17 @@ rollback system, control-plane authentication, or web UI.
2020

2121
## Install
2222

23-
Prebuilt binaries are published from the public, binary-only
24-
[`danielkov/kit-releases`](https://github.com/danielkov/kit-releases) repository.
23+
Source code and prebuilt binaries are published from the public
24+
[`speakeasy-api/kit`](https://github.com/speakeasy-api/kit) repository.
2525
Install the latest release with mise:
2626

2727
```sh
28-
mise use -g github:danielkov/kit-releases
28+
mise use -g github:speakeasy-api/kit
2929
kit --version
3030
```
3131

3232
Pin a release by appending its version, for example
33-
`github:danielkov/kit-releases@0.1.28`. The source repository remains private;
34-
the release repository contains only packaged executables and checksums.
33+
`github:speakeasy-api/kit@0.1.83`.
3534

3635
## Run
3736

docs/releasing.md

Lines changed: 32 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,50 @@
11
# Releasing Kit
22

3-
Kit's source is public at
4-
[`speakeasy-api/kit`](https://github.com/speakeasy-api/kit). Binary releases are
5-
published to
6-
[`danielkov/kit-releases`](https://github.com/danielkov/kit-releases). GitHub
7-
Actions runs checks only; release artifacts are built and published locally.
3+
Kit's source and binary releases are public at
4+
[`speakeasy-api/kit`](https://github.com/speakeasy-api/kit).
5+
GitHub Actions builds and publishes release artifacts from version tags.
86

9-
## One-time release-machine setup
7+
## Publish a release
108

11-
Signing and notarization run on the trusted local Mac, not in GitHub Actions.
12-
Install the Inlucent Limited Developer ID Application identity in its Keychain
13-
and retain the matching App Store Connect API key in 1Password. The machine also
14-
needs a running Docker-compatible container engine and `gh` authenticated as an
15-
account with write access to `danielkov/kit-releases`. Downloaded `.p8` keys
16-
cannot be downloaded again, so keep the original in the company's secret
17-
manager.
18-
19-
## Prepare a signed macOS release locally
20-
21-
Run signing and notarization on a trusted macOS machine so Apple queue delays do
22-
not consume metered GitHub-hosted macOS runner time. Set
23-
`KIT_NOTARY_API_KEY_DOCUMENT`, `KIT_NOTARY_API_KEY_VAULT`,
24-
`KIT_NOTARY_API_KEY_ID`, and `KIT_NOTARY_API_ISSUER_ID` in the ignored repository
25-
root `.env` file or export them in the release environment. The script loads
26-
`.env` when present, reads the App Store Connect API key from 1Password, uses the
27-
installed Developer ID identity, and preserves the exact submitted binary under
28-
`dist/notarize/`:
9+
Update `Cargo.toml` and `Cargo.lock` to the new version, commit the release, then
10+
push a matching tag:
2911

3012
```sh
31-
caffeinate -i scripts/notarize-release.sh v0.1.29
13+
git tag v0.1.83
14+
git push origin v0.1.83
3215
```
3316

34-
The working tree must be clean when the script starts and the requested version
35-
must match `Cargo.toml`. The script builds an immutable archive of the current
36-
commit, records its SHA in `source-commit.txt`, and does not read later source
37-
edits. `caffeinate` prevents idle sleep while `notarytool --wait` runs. The API
38-
key exists only in a permission-restricted temporary directory and is deleted
39-
when the script exits. If Apple accepts the submission, the script
40-
creates the final macOS archive and its SHA-256 checksum. Do not delete the
41-
output directory while a submission is active; it contains the exact signed
42-
binary Apple is evaluating.
17+
For a prerelease, use `-pre` or `-pre.N`, for example `v0.1.83-pre.1`.
18+
The release workflow verifies the tag against `Cargo.toml`, runs formatting,
19+
Clippy, and tests, builds Linux x86-64 and macOS arm64 archives, generates
20+
checksums, and publishes them to the tagged GitHub release. Prerelease tags are
21+
marked as prereleases on GitHub.
4322

44-
## Publish a signed release
23+
## Optional macOS signing and notarization
4524

46-
Update `Cargo.toml` and `Cargo.lock` to the new version, commit every intended
47-
release change, and run the local orchestrator from a clean working tree:
25+
The macOS build does not require Apple credentials. When none are configured,
26+
the workflow publishes an unsigned, unnotarized archive and identifies it as
27+
such in the release notes. This is the expected setup until signing credentials
28+
are added to the repository.
4829

49-
```sh
50-
scripts/release-local.sh
51-
```
30+
To enable signing and notarization, configure all of these repository secrets:
31+
32+
- `MACOS_CERTIFICATE_P12_BASE64`
33+
- `MACOS_CERTIFICATE_PASSWORD`
34+
- `MACOS_SIGNING_IDENTITY`
35+
- `APPLE_API_KEY_P8_BASE64`
36+
- `APPLE_API_KEY_ID`
37+
- `APPLE_API_ISSUER_ID`
5238

53-
The script runs format, Clippy, and tests; signs and notarizes macOS; builds
54-
Linux x86-64 with a cached native ARM64 cross-build container; smoke-tests the
55-
Linux binary in an x86-64 Debian container; generates checksums; and creates a
56-
hidden draft release. Only after all artifacts are ready does it push the source
57-
branch and tag, publish the draft atomically, and test installation through mise.
58-
GitHub Actions only runs formatting, Clippy, and tests; it does not package or
59-
publish release artifacts.
39+
The workflow fails on a partial configuration rather than silently publishing
40+
an unsigned build. Downloaded `.p8` keys cannot be downloaded again, so retain
41+
the original in the company's secret manager.
6042

61-
The first Linux build creates a local container image and takes longer. Later
62-
builds reuse that image, the Cargo registry volume, and the target directory.
63-
The lower-level `notarize-release.sh` and `build-linux-release.sh` scripts remain
64-
available for diagnosis, but normal releases should use `release-local.sh`.
43+
## Verify a release
6544

66-
Verify the published result from a clean environment:
45+
Install a specific published version from a clean environment:
6746

6847
```sh
69-
mise use github:danielkov/kit-releases@0.1.32
48+
mise use github:speakeasy-api/kit@0.1.83
7049
kit --version
7150
```

docs/user/getting-started-and-configuration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ Run `kit init` to write the recommended `~/.kit/config.toml` and an empty `~/.ki
99
Install the latest packaged release with mise, then verify that the executable is on `PATH`:
1010

1111
```sh
12-
mise use -g github:danielkov/kit-releases
12+
mise use -g github:speakeasy-api/kit
1313
kit --version
1414
kit --help
1515
```
1616

17-
A version can be pinned with a mise package such as `github:danielkov/kit-releases@0.1.28`. The examples below invoke the installed binary directly; they do not use `cargo run`.
17+
A version can be pinned with a mise package such as `github:speakeasy-api/kit@0.1.83`. The examples below invoke the installed binary directly; they do not use `cargo run`.
1818

1919
## Choose a provider and authenticate
2020

0 commit comments

Comments
 (0)