From 3035dc2a7d92538856c7c457c60042e33ee58001 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 13 Mar 2026 12:52:56 +0000 Subject: [PATCH] Getting set for release --- .claude/settings.json | 10 +- .gitignore | 3 + .story_kit/project.toml | 4 +- ...does_not_write_mcp_json_to_project_root.md | 2 +- ...nal_positional_path_argument_on_startup.md | 8 +- ..._screen_when_cli_path_argument_provided.md | 2 +- Cargo.lock | 2 +- Makefile | 18 +++- README.md | 14 +-- script/release | 97 +++++++++++++++++++ 10 files changed, 134 insertions(+), 26 deletions(-) create mode 100755 script/release diff --git a/.claude/settings.json b/.claude/settings.json index bcc78bb..830307e 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -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" ] } -} \ No newline at end of file +} diff --git a/.gitignore b/.gitignore index 1d0be17..dff541b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Claude Code .claude/settings.local.json +# Local environment (secrets) +.env + # App specific store.json .story_kit_port diff --git a/.story_kit/project.toml b/.story_kit/project.toml index 3d2a3de..82746fd 100644 --- a/.story_kit/project.toml +++ b/.story_kit/project.toml @@ -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: diff --git a/.story_kit/work/6_archived/208_bug_project_scaffold_does_not_write_mcp_json_to_project_root.md b/.story_kit/work/6_archived/208_bug_project_scaffold_does_not_write_mcp_json_to_project_root.md index 46e354b..ed7f1f7 100644 --- a/.story_kit/work/6_archived/208_bug_project_scaffold_does_not_write_mcp_json_to_project_root.md +++ b/.story_kit/work/6_archived/208_bug_project_scaffold_does_not_write_mcp_json_to_project_root.md @@ -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 diff --git a/.story_kit/work/6_archived/209_story_accept_optional_positional_path_argument_on_startup.md b/.story_kit/work/6_archived/209_story_accept_optional_positional_path_argument_on_startup.md index 8fa0d53..57746b6 100644 --- a/.story_kit/work/6_archived/209_story_accept_optional_positional_path_argument_on_startup.md +++ b/.story_kit/work/6_archived/209_story_accept_optional_positional_path_argument_on_startup.md @@ -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 diff --git a/.story_kit/work/6_archived/211_story_skip_selection_screen_when_cli_path_argument_provided.md b/.story_kit/work/6_archived/211_story_skip_selection_screen_when_cli_path_argument_provided.md index 3e63686..ccdce74 100644 --- a/.story_kit/work/6_archived/211_story_skip_selection_screen_when_cli_path_argument_provided.md +++ b/.story_kit/work/6_archived/211_story_skip_selection_screen_when_cli_path_argument_provided.md @@ -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 diff --git a/Cargo.lock b/Cargo.lock index b759039..f56c6f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Makefile b/Makefile index 22001ab..26c410b 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/README.md b/README.md index 8921160..0f741c5 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/script/release b/script/release new file mode 100755 index 0000000..f6660a1 --- /dev/null +++ b/script/release @@ -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 " + 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}"