# Client Release Agent
A collection of [Warp](https://www.warp.dev) **agent skills** that automate common
release-engineering chores: cutting release candidates, cherry-picking fixes into
release branches, tracking cherry-pick status, posting a daily Sentry issue digest,
and replying to Slack threads.
The skills are written to be **tool-agnostic and reusable**. Environment-specific
values — your organization, repositories, Slack workspace, Sentry project, and release
conventions like channel names and branch naming — are supplied through environment
variables rather than hardcoded, so you can adopt these skills out of the box and
override anything that differs in your setup.
## What's included
| Skill | What it does |
|---|---|
| `setup` | One-time setup: clones the repo, checks prerequisites, fills in `.env`, and validates credentials. |
| `cherrypick-to-release` | Cherry-picks a commit into one or more release branches and opens PRs assigned to the current on-call. |
| `cut-new-release-candidate` | Triggers a "Cut New Release Candidate" GitHub Actions workflow on a release branch. |
| `post-release-status` | Posts/updates a Slack message tracking the status of cherry-pick PRs for a release. |
| `post-daily-new-issues` | Posts a daily digest of *new* Sentry issues for the current release to a Slack channel. |
| `respond-to-slack-thread` | Posts a reply to a Slack thread. |
Each skill lives in `.warp/skills/<name>/SKILL.md`. See the
[Warp skills documentation](https://docs.warp.dev) for how skills are discovered and run.
## How to deploy
### With Warp (recommended)
Paste this prompt into [Warp](https://www.warp.dev) — it works whether or not you've
cloned the repo yet:
> Clone https://github.com/warpdotdev/client-release-agent-oss.git (or use my existing
> clone), then read and follow `.warp/skills/setup/SKILL.md` to set up the client
> release agent.
If you already have the repo cloned and open in Warp, just ask: **"Run the setup
skill"**. The agent will verify prerequisites, walk you through `.env` configuration,
and validate your credentials.
### Manually
1. Install the prerequisites: [`gh`](https://cli.github.com/) (GitHub CLI),
[`jq`](https://jqlang.github.io/jq/), `curl`, and `git`.
2. Authenticate the GitHub CLI: `gh auth login`.
3. Clone this repo: `git clone https://github.com/warpdotdev/client-release-agent-oss.git`
4. Copy `.env.example` to `.env` and fill it in (see the
[Configuration reference](#configuration-reference)). You'll need a Slack bot token
for the Slack-related skills and a Sentry auth token for the Sentry digest skill.
5. Load the variables before running the skills: `set -a; source .env; set +a`
(or export them in your shell / CI / scheduled-agent environment).
**Never commit your `.env` file** — it is already covered by `.gitignore`.
## Configuration reference
Configuration is supplied entirely through environment variables, in two groups:
- **Environment-specific values** — identifiers and secrets for your org, repos, Slack
workspace, and Sentry project. Set the ones used by the skills you run; a few are
optional (noted below).
- **Release conventions** — channel names, branch naming, workflow name, and Slack
status emoji. Each ships a sensible default, so you set these only to override the
built-in convention when your setup differs. `RELEASE_BRANCH_PREFIX` uses a
`{channel}` placeholder that each skill replaces with the actual channel name (e.g.
`stable` → `stable_release/`).
### Environment-specific values
| Variable | Used by | Description |
|---|---|---|
| `SLACK_BOT_TOKEN` | all Slack skills | Slack **bot** token (`xoxb-…`). See scopes below. |
| `SENTRY_AUTH_TOKEN` | `post-daily-new-issues` | Sentry auth token with `event:read` and `project:read`. |
| `INTERNAL_REPO` | release skills | `owner/repo` that holds your release branches (e.g. `your-org/your-repo`). |
| `PUBLIC_REPO` | `cherrypick-to-release` | Optional. `owner/repo` of a public repo that syncs into `INTERNAL_REPO`. Omit if you don't use a public↔internal sync. |
| `REPO_DIR` | release skills | Absolute path to your local checkout of `INTERNAL_REPO`. |
| `GITHUB_ORG` | `cherrypick-to-release` | GitHub org used to resolve the on-call's GitHub username. |
| `SENTRY_ORG` | `post-daily-new-issues` | Sentry org slug. |
| `SENTRY_PROJECT` | `post-daily-new-issues` | Sentry project slug. |
| `SENTRY_PROJECT_ID` | `post-daily-new-issues` | Numeric Sentry project ID (used to build release URLs). |
| `RELEASE_VERSIONS_URL` | `post-daily-new-issues` | Public URL returning the current channel versions JSON. Optional if you pass a version explicitly. |
| `RELEASE_SLACK_CHANNEL` | `post-daily-new-issues`, `post-release-status` | Slack channel name (or ID) to post release updates in. |
| `ONCALL_SLACK_GROUP` | release skills | Slack usergroup handle for the on-call (e.g. `oncall-primary`). |
### Release conventions (optional; defaults shown)
Each variable below is optional — when unset, the skill falls back to the default.
Override one only if your setup differs.
| Variable | Used by | Description |
|---|---|---|
| `RELEASE_CHANNELS` | release skills | Space-separated, ordered release channels (default `preview stable`). Cherry-picks run in this order. |
| `RELEASE_BRANCH_PREFIX` | release skills | Per-channel branch path prefix; `{channel}` is replaced with the channel name (default `{channel}_release/`). |
| `DEFAULT_BRANCH` | `cherrypick-to-release` | Internal default branch searched for synced commits (default `main`). |
| `CHERRYPICK_BRANCH_PREFIX` | `cherrypick-to-release` | Prefix for created cherry-pick branches (default `cherrypick/`). |
| `RC_WORKFLOW_NAME` | `cut-new-release-candidate` | Name of the GitHub Actions workflow that cuts a release candidate (default `Cut New Release Candidate`). |
| `SYNC_TRAILER_KEY` | `cherrypick-to-release` | Commit trailer key identifying a synced commit's public origin (default `Repo-Sync-Origin`). |
| `DIGEST_CHANNEL` | `post-daily-new-issues` | Channel whose release the daily digest reports on (default `stable`). |
| `STATUS_EMOJI_IN_REVIEW` / `STATUS_EMOJI_MERGED` / `STATUS_EMOJI_VERIFIED` | `post-release-status` | Slack emoji for each PR status (defaults `:large_yellow_square:` / `:merged:` / `:verified:`). |
### Required Slack scopes
Grant your Slack bot token the union of scopes needed by the skills you use:
`channels:read`, `channels:history`, `chat:write`, `chat:update`, `usergroups:read`,
`users:read`, `users:read.email`.
## Usage
These are [Warp agent skills](https://docs.warp.dev), so you don't invoke them directly.
Set and source your environment variables (see [How to deploy](#how-to-deploy)), then
describe what you want to Warp's agent in plain language — the agent matches your request
to a skill (via its `description`) and runs the documented steps.
For example, to cherry-pick a merged PR into your release branches, ask:
> Cherry-pick PR #1234 into stable and preview
Other example prompts:
- **Cut a release candidate** — `Cut a new release candidate for stable_release/v1.4.0-stable.1`
- **Post the daily Sentry digest** — `Post the daily new-issues digest for on-call`
- **Update cherry-pick status** — `Update the release status for stable_release/v1.4.0-stable.1`
- **Reply in Slack** — `Reply to this Slack thread: <message link>`
Skills also chain: `cherrypick-to-release` can invoke `post-release-status` after opening
the PRs, and `cut-new-release-candidate` posts its update via `respond-to-slack-thread`.
## Security
Secrets are only ever read from environment variables — never hardcode tokens in a
skill or commit them. See [`SECURITY.md`](SECURITY.md) to report a vulnerability.
## License
[MIT](LICENSE)