# Release

Release pipeline for CLI, mobile, web, and server.

- Repository: https://github.com/slopus/happy (24443 stars)
- Category: Testing
- License: MIT
- Last updated: 2026-11-01
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add slopus/happy --skill release -g -a claude-code -y`
Codex: `npx skills add slopus/happy --skill release -g -a codex -y`
Cursor: `npx skills add slopus/happy --skill release -g -a cursor -y`
Gemini CLI: `npx skills add slopus/happy --skill release -g -a gemini-cli -y`

## Description

Release pipeline for CLI, mobile, web, and server. Guides through version bumping, building, testing, publishing, and deploying. Replaces the old interactive release-it flow with a Claude Code-native experience. Use when user types /release or asks to release, publish, deploy, or ship any component.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# Release

You are the release operator for the Happy monorepo. When invoked, walk the user through releasing the component they choose.

## Step 0: Where you release from (never skipped)

**Every release runs from a local `main` that is exactly `origin/main`, checked
against a fetch made right then.** Not from a worktree. Not from a branch. Not
from a `main` that is ahead by an unpushed commit or behind by anyone else's.
Not with uncommitted changes. This applies to every target below: preview OTA,
production OTA, native builds, store submissions, CLI dispatch.

Why this is rule zero. On 2026-09-20 a preview OTA was published from a
worktree commit (`fe7de0a1`) that had branched off `main` hours earlier. The
commit was fine on its own, and it was later rebased and pushed, so git looked
right afterwards. But expo-updates serves whatever was published *last* on a
branch, not whatever is on `main`. For the next several hours every phone on
the preview channel ran a bundle missing four fixes that `main` already had:
bot faces, the truncated "Auto" chip, paste-from-clipboard, and the send button
being pushed out of the composer. Nothing errored. The user saw "the same old
issue" and could not tell why. A release from a stale checkout is not a
release, it is a rollback that nobody asked for.

Run this and read the output before any release command:

```bash
git checkout main
git fetch origin main
git rev-parse --abbrev-ref HEAD            # must print exactly: main
git rev-parse HEAD origin/main             # the two hashes must be identical
git status --porcelain                     # must print nothing
```

If `HEAD` is behind, `git merge --ff-only origin/main`. If it is ahead, the
commits are not on `main` yet, push first (see `AGENTS.md` "Sync To Main"),
then release. If the tree is dirty, commit or stash; a release must be
reproducible from the commit EAS records.

The rest: https://github.com/slopus/happy/blob/HEAD/.agents/skills/release/SKILL.md
