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:
@@ -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`.
|
||||
|
||||
Executable
+109
@@ -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 ""
|
||||
Executable
+113
@@ -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 ""
|
||||
Reference in New Issue
Block a user