chore: add release scripts and document release process

- scripts/prepare-release.sh — create release branch + changeset version + validate
- scripts/publish.sh — build + publish + tag + merge back
- CLAUDE.md — full rewrite with release process docs
This commit is contained in:
2026-06-01 07:37:45 +00:00
parent 30bb1a238b
commit cec19f8dad
3 changed files with 279 additions and 10 deletions
+57 -10
View File
@@ -1,16 +1,16 @@
# CLAUDE.md — ocas
# CLAUDE.md — OCAS
Self-describing content-addressable storage with JSON Schema typed nodes.
Object Content Addressable Store — self-describing CAS with JSON Schema typed nodes.
## Project Structure
Monorepo with 4 packages under `packages/`:
Monorepo with 3 packages under `packages/`:
| Package | Description |
|---------|-------------|
| `ocas` | Core CAS engine — hashing, schema, store, verify, bootstrap |
| `ocas-fs` | Filesystem-backed CAS store |
| `cli` | CLI tool |
| Package | Directory | Description |
|---------|-----------|-------------|
| `@ocas/core` | `packages/core` | Core CAS engine — hashing, schema, store, verify, bootstrap |
| `@ocas/fs` | `packages/fs` | Filesystem-backed CAS store |
| `@ocas/cli` | `packages/cli` | CLI tool (`ocas` binary) |
## Tech Stack
@@ -19,7 +19,7 @@ Monorepo with 4 packages under `packages/`:
- **Build:** `tsc --build` (composite project references)
- **Test:** `bun test`
- **Lint/Format:** Biome (`biome check .` / `biome format --write .`)
- **Publish:** Changesets → npmjs (`@ocas/*`)
- **Publish:** Changesets + `bun publish` → npmjs (`@ocas/*`)
## Commands
@@ -41,7 +41,7 @@ bun run format # Biome format (auto-fix)
### Biome Rules
- `noConsole: "error"` globally (except `cli`)
- `noConsole: "error"` globally (except `packages/cli`)
- Recommended ruleset enabled
- Auto-organize imports via `assist.actions.source.organizeImports`
- Indent: 2 spaces
@@ -60,6 +60,11 @@ bun run format # Biome format (auto-fix)
- `CasNode` — content-addressed node with schema
- `Store` — abstract storage interface (get/put)
### Internal Dependencies
Workspace packages reference each other with `workspace:*` in `package.json`.
This is resolved to real version numbers only during publishing (see below).
## Git
- Commit format: `type: description` (conventional commits)
@@ -75,3 +80,45 @@ bun run format # Biome format (auto-fix)
1. `bun test` — all tests pass
2. `bun run check` — no lint errors
3. `bun run build` — builds cleanly
## Release Process
Releases use a **release branch** workflow. `main` always keeps `workspace:*` for
internal dependencies; version numbers are only fixed on the release branch.
### Prepare
```bash
./scripts/prepare-release.sh
```
This script:
1. Checks you're on `main` with a clean tree and pending changesets
2. Creates `release/<version>` branch
3. Runs `changeset version` to fix versions and generate CHANGELOGs
4. Runs full validation (install, build, lint, test)
5. Commits the version bump
After preparation, review changes and fix any issues on the release branch.
### Publish
```bash
./scripts/publish.sh
```
This script:
1. Validates you're on a `release/*` branch with no pending changesets
2. Runs final build + test
3. Publishes packages in order: `@ocas/core``@ocas/fs``@ocas/cli`
4. Tags, pushes, merges back to `main`, cleans up the release branch
### Adding a Changeset
Before releasing, add changesets for your changes:
```bash
bunx changeset # interactive — pick packages + bump type + summary
```
Changesets live in `.changeset/` as markdown files until consumed by `prepare-release.sh`.
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
set -euo pipefail
# OCAS Release Preparation
# Creates a release branch, runs changeset version, and validates.
#
# Usage: ./scripts/prepare-release.sh
#
# Prerequisites:
# - Clean working tree on main branch
# - Pending changesets in .changeset/
BOLD='\033[1m'
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
info() { echo -e "${BOLD}${GREEN}${NC} $1"; }
warn() { echo -e "${BOLD}${YELLOW}${NC} $1"; }
error() { echo -e "${BOLD}${RED}${NC} $1"; exit 1; }
# --- Pre-flight checks ---
echo -e "\n${BOLD}OCAS Release Preparation${NC}\n"
# Must be on main
BRANCH=$(git branch --show-current)
[[ "$BRANCH" == "main" ]] || error "Must be on main branch (currently on $BRANCH)"
# Clean working tree
[[ -z "$(git status --porcelain)" ]] || error "Working tree is not clean. Commit or stash changes first."
# Check for pending changesets
CHANGESETS=$(ls .changeset/*.md 2>/dev/null | grep -v README.md || true)
if [[ -z "$CHANGESETS" ]]; then
error "No pending changesets found. Run 'bunx changeset' to add one first."
fi
info "Found pending changesets:"
for cs in $CHANGESETS; do
echo " $(basename "$cs")"
done
# --- Determine version ---
# Dry-run to peek at the version bump
echo ""
info "Previewing version changes..."
bunx changeset status
# --- Create release branch ---
echo ""
read -rp "Proceed with release branch? [y/N] " confirm
[[ "$confirm" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }
# Get current version to name the branch
CURRENT_VERSION=$(python3 -c "import json; print(json.load(open('packages/core/package.json'))['version'])")
info "Current version: $CURRENT_VERSION"
git fetch origin
git checkout -b release/next
# --- Run changeset version ---
info "Running changeset version..."
bunx changeset version
# Show what changed
NEW_VERSION=$(python3 -c "import json; print(json.load(open('packages/core/package.json'))['version'])")
info "New version: $NEW_VERSION"
# Rename branch to include actual version
git branch -m "release/next" "release/$NEW_VERSION"
info "Release branch: release/$NEW_VERSION"
# --- Validate ---
echo ""
info "Running validation..."
echo " → bun install"
bun install --no-cache
echo " → bun run build"
bun run build
echo " → bun run check"
bun run check || warn "Lint warnings found (review above)"
echo " → bun test"
bun test || error "Tests failed!"
info "All checks passed"
# --- Commit ---
git add -A
git commit -m "chore(release): prepare v$NEW_VERSION"
echo ""
info "Release branch ready: release/$NEW_VERSION"
echo ""
echo " Next steps:"
echo " 1. Review changes: git diff main...HEAD"
echo " 2. Fix issues if needed, commit to this branch"
echo " 3. When ready: ./scripts/publish.sh"
echo ""
+113
View File
@@ -0,0 +1,113 @@
#!/usr/bin/env bash
set -euo pipefail
# OCAS Publish
# Builds, publishes to npm, tags, and pushes.
#
# Usage: ./scripts/publish.sh
#
# Prerequisites:
# - On a release/* branch (created by prepare-release.sh)
# - npm authenticated (`npm whoami` works)
# - All checks passing
BOLD='\033[1m'
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
info() { echo -e "${BOLD}${GREEN}${NC} $1"; }
warn() { echo -e "${BOLD}${YELLOW}${NC} $1"; }
error() { echo -e "${BOLD}${RED}${NC} $1"; exit 1; }
# --- Pre-flight checks ---
echo -e "\n${BOLD}OCAS Publish${NC}\n"
# Must be on release/* branch
BRANCH=$(git branch --show-current)
[[ "$BRANCH" == release/* ]] || error "Must be on a release/* branch (currently on $BRANCH)"
# Clean working tree
[[ -z "$(git status --porcelain)" ]] || error "Working tree is not clean. Commit changes first."
# Extract version
VERSION=$(python3 -c "import json; print(json.load(open('packages/core/package.json'))['version'])")
info "Publishing version: $VERSION"
# No pending changesets (should have been consumed by prepare-release.sh)
CHANGESETS=$(ls .changeset/*.md 2>/dev/null | grep -v README.md || true)
if [[ -n "$CHANGESETS" ]]; then
error "Pending changesets found. Run prepare-release.sh first."
fi
# npm auth check
npm whoami &>/dev/null || error "Not authenticated with npm. Run 'npm login' first."
NPM_USER=$(npm whoami)
info "npm user: $NPM_USER"
# --- Final validation ---
info "Running final validation..."
echo " → bun run build"
bun run build
echo " → bun test"
bun test || error "Tests failed! Fix before publishing."
# --- Confirm ---
echo ""
echo -e "${BOLD}Will publish:${NC}"
for pkg in core fs cli; do
PKG_NAME=$(python3 -c "import json; print(json.load(open('packages/$pkg/package.json'))['name'])")
echo " $PKG_NAME@$VERSION"
done
echo ""
read -rp "Publish to npm? [y/N] " confirm
[[ "$confirm" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }
# --- Publish (order matters: core → fs → cli) ---
for pkg in core fs cli; do
PKG_NAME=$(python3 -c "import json; print(json.load(open('packages/$pkg/package.json'))['name'])")
echo ""
info "Publishing $PKG_NAME@$VERSION..."
(cd "packages/$pkg" && bun publish --access public)
info "$PKG_NAME@$VERSION published ✓"
done
# --- Tag and push ---
echo ""
TAG="v$VERSION"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$BRANCH"
git push origin "$TAG"
info "Tag $TAG pushed"
# --- Merge back to main ---
echo ""
info "Merging release into main..."
git checkout main
git merge "$BRANCH" --no-ff -m "chore: merge release $TAG"
git push origin main
info "Merged to main"
# Clean up release branch
git branch -d "$BRANCH"
git push origin --delete "$BRANCH" 2>/dev/null || true
info "Release branch cleaned up"
echo ""
info "Release $TAG complete! 🎉"
echo ""
echo " Published:"
for pkg in core fs cli; do
PKG_NAME=$(python3 -c "import json; print(json.load(open('packages/$pkg/package.json'))['name'])")
echo " https://www.npmjs.com/package/$PKG_NAME"
done
echo ""