Getting set for release

This commit is contained in:
Dave
2026-03-13 12:52:56 +00:00
parent 27d9d3a3a9
commit 3035dc2a7d
10 changed files with 134 additions and 26 deletions

View File

@@ -1,11 +1,9 @@
{
"enabledMcpjsonServers": [
"story-kit"
],
"enabledMcpjsonServers": ["story-kit"],
"permissions": {
"allow": [
"Bash(./server/target/debug/story-kit-server:*)",
"Bash(./target/debug/story-kit-server:*)",
"Bash(./server/target/debug/story-kit:*)",
"Bash(./target/debug/story-kit:*)",
"Bash(STORYKIT_PORT=*)",
"Bash(cargo build:*)",
"Bash(cargo check:*)",
@@ -61,4 +59,4 @@
"Write"
]
}
}
}

3
.gitignore vendored
View File

@@ -1,6 +1,9 @@
# Claude Code
.claude/settings.local.json
# Local environment (secrets)
.env
# App specific
store.json
.story_kit_port

View File

@@ -102,7 +102,7 @@ Read CLAUDE.md first, then .story_kit/README.md to understand the dev process.
- URL to visit in the browser
- Things to check in the UI
- curl commands to exercise relevant API endpoints
- Kill the test server when done: `pkill -f story-kit-server || true`
- Kill the test server when done: `pkill -f story-kit || true`
### 4. Produce Structured Report
Print your QA report to stdout before your process exits. The server will automatically run acceptance gates. Use this format:
@@ -179,7 +179,7 @@ Read CLAUDE.md first, then .story_kit/README.md to understand the dev process.
- URL to visit in the browser
- Things to check in the UI
- curl commands to exercise relevant API endpoints
- Kill the test server when done: `pkill -f story-kit-server || true`
- Kill the test server when done: `pkill -f story-kit || true`
### 4. Produce Structured Report
Print your QA report to stdout before your process exits. The server will automatically run acceptance gates. Use this format:

View File

@@ -13,7 +13,7 @@ Additionally, there is no way for the user to trigger scaffolding from the CLI
## How to Reproduce
1. Create an empty directory or navigate to a project without `.story_kit/`
2. Run `story-kit-server`
2. Run `story-kit`
3. Open the project via the UI file chooser
4. Try to chat using the claude-code provider

View File

@@ -6,13 +6,13 @@ name: "Accept optional positional path argument on startup"
## User Story
As a user, I want to run `story-kit-server .` or `story-kit-server /path/to/project` to start the server with that directory as the project, so that I don't have to use the UI file chooser.
As a user, I want to run `story-kit .` or `story-kit /path/to/project` to start the server with that directory as the project, so that I don't have to use the UI file chooser.
## Acceptance Criteria
- [ ] Running `story-kit-server` with no args behaves as today (auto-detect from cwd)
- [ ] Running `story-kit-server .` opens the current directory as the project
- [ ] Running `story-kit-server /some/path` opens that path as the project
- [ ] Running `story-kit` with no args behaves as today (auto-detect from cwd)
- [ ] Running `story-kit .` opens the current directory as the project
- [ ] Running `story-kit /some/path` opens that path as the project
- [ ] If the path has no .story_kit/, it is scaffolded automatically
- [ ] Invalid paths produce a clear error message

View File

@@ -6,7 +6,7 @@ name: "Skip selection screen when CLI path argument provided"
## User Story
As a user, I want the frontend to go straight to the project workspace when I start the server with an explicit path argument (e.g. `story-kit-server .`), so that I don't have to click through the selection screen for a project the server already opened.
As a user, I want the frontend to go straight to the project workspace when I start the server with an explicit path argument (e.g. `story-kit .`), so that I don't have to click through the selection screen for a project the server already opened.
## Acceptance Criteria

2
Cargo.lock generated
View File

@@ -4078,7 +4078,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "story-kit-server"
name = "story-kit"
version = "0.1.0"
dependencies = [
"async-stream",

View File

@@ -1,22 +1,23 @@
.PHONY: help build-macos build-linux
.PHONY: help build-macos build-linux release
help:
@echo "Story Kit cross-platform build targets"
@echo ""
@echo " make build-macos Build native macOS release binary"
@echo " make build-linux Build static Linux x86_64 release binary (requires cross + Docker)"
@echo " make release V=x.y.z Build both targets and publish a Gitea release"
@echo ""
@echo "Prerequisites:"
@echo " build-macos: Rust stable toolchain, pnpm"
@echo " build-linux: cargo install cross AND Docker Desktop running"
@echo ""
@echo "Output:"
@echo " macOS : target/release/story-kit-server"
@echo " Linux : target/x86_64-unknown-linux-musl/release/story-kit-server"
@echo " macOS : target/release/story-kit"
@echo " Linux : target/x86_64-unknown-linux-musl/release/story-kit"
## Build a native macOS release binary.
## The frontend is compiled by build.rs (pnpm build) and embedded via rust-embed.
## Verify dynamic deps afterwards: otool -L target/release/story-kit-server
## Verify dynamic deps afterwards: otool -L target/release/story-kit
build-macos:
cargo build --release
@@ -26,3 +27,12 @@ build-macos:
## The resulting binary has zero dynamic library dependencies (ldd reports "not a dynamic executable").
build-linux:
cross build --release --target x86_64-unknown-linux-musl
## Publish a release to Gitea with macOS and Linux binaries.
## Requires: GITEA_TOKEN env var, cross, Docker running.
## Usage: make release V=0.2.0
release:
ifndef V
$(error Usage: make release V=x.y.z)
endif
script/release $(V)

View File

@@ -24,7 +24,7 @@ cargo run
cargo build --release
# Run the server (serves embedded frontend/dist/)
./target/release/story-kit-server
./target/release/story-kit
```
## Cross-Platform Distribution
@@ -37,10 +37,10 @@ Story Kit ships as a **single self-contained binary** with the React frontend em
```bash
# Native build no extra tools required beyond Rust + pnpm
make build-macos
# Output: target/release/story-kit-server
# Output: target/release/story-kit
# Verify only system frameworks are linked (Security.framework, libSystem.B.dylib, etc.)
otool -L target/release/story-kit-server
otool -L target/release/story-kit
```
### Linux (static x86_64, zero dynamic deps)
@@ -60,13 +60,13 @@ cargo install cross
```bash
make build-linux
# Output: target/x86_64-unknown-linux-musl/release/story-kit-server
# Output: target/x86_64-unknown-linux-musl/release/story-kit
# Verify the binary is statically linked
file target/x86_64-unknown-linux-musl/release/story-kit-server
file target/x86_64-unknown-linux-musl/release/story-kit
# Expected: ELF 64-bit LSB executable, x86-64, statically linked
ldd target/x86_64-unknown-linux-musl/release/story-kit-server
ldd target/x86_64-unknown-linux-musl/release/story-kit
# Expected: not a dynamic executable
```
@@ -74,7 +74,7 @@ ldd target/x86_64-unknown-linux-musl/release/story-kit-server
```bash
# No Rust, Node, glibc, or any other library needed just copy and run
./story-kit-server
./story-kit
```
## Testing

97
script/release Executable file
View File

@@ -0,0 +1,97 @@
#!/usr/bin/env bash
set -euo pipefail
# ── Configuration ──────────────────────────────────────────────
GITEA_URL="https://code.crashlabs.io"
REPO="dave/story-kit"
BINARY_NAME="story-kit"
# ── Load .env if present ───────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
if [ -f "${SCRIPT_DIR}/.env" ]; then
set -a
source "${SCRIPT_DIR}/.env"
set +a
fi
# ── Preflight ──────────────────────────────────────────────────
if [ -z "${GITEA_TOKEN:-}" ]; then
echo "Error: GITEA_TOKEN is not set."
echo "Create a token at ${GITEA_URL}/user/settings/applications"
echo "Then add to .env: GITEA_TOKEN=your_token"
exit 1
fi
VERSION="${1:-}"
if [ -z "$VERSION" ]; then
echo "Usage: script/release <version>"
echo "Example: script/release 0.2.0"
exit 1
fi
TAG="v${VERSION}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Error: Tag ${TAG} already exists."
exit 1
fi
if ! command -v cross >/dev/null 2>&1; then
echo "Error: 'cross' is not installed. Run: cargo install cross"
exit 1
fi
if ! docker info >/dev/null 2>&1; then
echo "Error: Docker is not running. Start Docker Desktop first."
exit 1
fi
echo "==> Releasing ${TAG}"
# ── Build ──────────────────────────────────────────────────────
echo "==> Building macOS (native)..."
cargo build --release
echo "==> Building Linux (static musl via cross)..."
cross build --release --target x86_64-unknown-linux-musl
# ── Package ────────────────────────────────────────────────────
DIST="target/dist"
rm -rf "$DIST"
mkdir -p "$DIST"
cp "target/release/${BINARY_NAME}" "${DIST}/${BINARY_NAME}-macos-arm64"
cp "target/x86_64-unknown-linux-musl/release/${BINARY_NAME}" "${DIST}/${BINARY_NAME}-linux-amd64"
chmod +x "${DIST}"/*
echo "==> Binaries:"
ls -lh "${DIST}"/
# ── Tag & Push ─────────────────────────────────────────────────
echo "==> Tagging ${TAG}..."
git tag -a "$TAG" -m "Release ${TAG}"
git push origin "$TAG"
# ── Create Gitea Release ──────────────────────────────────────
echo "==> Creating release on Gitea..."
RELEASE_RESPONSE=$(curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases" \
-d "{\"tag_name\": \"${TAG}\", \"name\": \"${TAG}\", \"body\": \"Release ${TAG}\"}")
RELEASE_ID=$(echo "$RELEASE_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
# ── Upload Binaries ───────────────────────────────────────────
for file in "${DIST}"/*; do
filename=$(basename "$file")
echo "==> Uploading ${filename}..."
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-F "attachment=@${file};filename=${filename}" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases/${RELEASE_ID}/assets" > /dev/null
done
echo ""
echo "==> Done! Release ${TAG} published:"
echo " ${GITEA_URL}/${REPO}/releases/tag/${TAG}"