Orchestrate a full release-day workflow for iOS, Android, or Garmin apps: verify the main branch CI is green, generate a changelog from git history, draft platform-ready store listing copy, trigger semantic-release (or tag manually), wait for the build, and queue the App Store submission. Use when the user says "cut a release", "ship version X", "it's release day", or "prepare the release".
Installs into .claude/skills of the current project.
Are you the author of Release Day?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/oleg-koval-release-day)
---
name: release-day
description: >
Orchestrate a full release-day workflow for iOS, Android, or Garmin apps: verify the
main branch CI is green, generate a changelog from git history, draft platform-ready
store listing copy, trigger semantic-release (or tag manually), wait for the build,
and queue the App Store submission. Use when the user says "cut a release", "ship
version X", "it's release day", or "prepare the release".
license: MIT
allowed-tools: Bash, Read, Write, Edit, WebSearch
compatibility: Codex, Claude Code, Cursor, GitHub Copilot, Windsurf, Kiro, and other Agent Skills compatible tools. Requires git, gh, and the target platform's release toolchain (fastlane, Xcode CLI, Garmin SDK, or npm with semantic-release).
metadata:
targets: [_source-only]
author: Oleg Koval
tags:
- release
- ios
- android
- garmin
- app-store
- changelog
- store-listing
- semantic-release
- deployment
- automation
source: weekly-pattern-learner
source_reason: "changelog-generator, store-listing-copy, and apple-store-submit each reference each other in their Chaining tables but no skill chains them into a single end-to-end release-day workflow"
source_date: "2026-08-10"
---
<!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
> π€ *Auto-generated by **weekly-pattern-learner** Β· changelog-generator, store-listing-copy, and apple-store-submit each reference each other in their Chaining tables but no skill chains them into a single end-to-end release-day workflow*
# Release Day
Drive a release from "code is ready" to "build submitted" in one orchestrated pass.
Each phase validates its output before proceeding: the skill stops and reports
rather than pushing a broken release.
## Inputs
- **Platform** (required): `ios`, `android`, `garmin`, or `all`
- **Version** (optional): explicit version to release; if omitted, semantic-release
determines the next version automatically
- **Branch** (optional, default: `main`): branch to release from
- **--dry-run** (optional): run all checks and generate all copy without pushing
the tag or submitting to the store
## Phase 1: Verify the branch is ready
```bash
BRANCH="${Branch:-main}"
git fetch origin "$BRANCH"
BRANCH_SHA=$(git rev-parse "origin/$BRANCH")
# Validate optional Version input
if [ -n "$Version" ]; then
if ! echo "$Version" | grep -qE '^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then
echo "Error: Version '$Version' is not a valid SemVer (e.g. 1.2.3 or v1.2.3)" >&2
exit 1
fi
fi
git status --short
# Last release tag
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "none")
# Commits since last release
if [ "$LAST_TAG" = "none" ]; then
git log "origin/$BRANCH" --oneline --no-merges | head -20
else
git log "${LAST_TAG}..origin/$BRANCH" --oneline --no-merges | head -20
fi
# CI state on branch
gh run list --branch "$BRANCH" --limit 3 --json status,conclusion,workflowName \
--jq '.[] | "\(.workflowName): \(.conclusion // .status)"'
```
**Stop if:**
- `git status --short` is non-empty: there are uncommitted changes
- The latest CI run on main is failing or still in progress
- There are zero commits since the last tag (nothing to release)
Report the problem clearly and stop. Do not proceed to a release if the branch
is not clean and green.
## Phase 2: Generate changelog
Run `olko:changelog-generator`.
Use the range `${LAST_TAG}..origin/${BRANCH}` (or `origin/${BRANCH}` if no prior tag exists). Produce a
structured changelog with sections: New Features, Improvements, Bug Fixes, Breaking
Changes. Strip internal commits (refactor, test, ci, chore without behavior impact).
Write the changelog to:
```bash
RELEASE_DIR="$(mktemp -d)/release-$(date +%Y-%m-%d)"
mkdir -p "$RELEASE_DIR"
# changelog-generator writes to CHANGELOG.md: validate before proceeding
if [ ! -s CHANGELOG.md ]; then
echo "Error: CHANGELOG.md is missing or empty, cannot continue." >&2
exit 1
fi
cp CHANGELOG.md "$RELEASE_DIR/changelog.md"
```
Review the generated changelog for accuracy before continuing. If commits are
ambiguous (e.g. `fix: thing`), use `git show <sha>` to read the diff and
re-classify the entry.
## Phase 3: Draft store listing copy
Run `olko:store-listing-copy` with `--platform <platform>`.
This reads the changelog from Phase 2 and drafts:
- iOS: Title, Subtitle, What's New (4000 chars), Keywords (100 chars)
- Android: Title (30), Short Description (80), Release Notes (500)
- Garmin: Description, What's New (1500 chars)
The skill validates field lengths and known store-validator rules (e.g., no `<`/`>` in
Garmin copy) before delivering. Output goes to:
```
store-copy-v<version>-<YYYY-MM-DD>.md
```
**Do not proceed until store copy passes validation.** Fix any overflow or forbidden
character before tagging.
## Phase 4: Trigger the release
### If semantic-release is configured
```bash
# Dry-run first to confirm version and release notes
set -o pipefail
if ! npm run release:dry-run 2>&1 | tail -40; then
echo "Dry-run failed, aborting release." >&2
exit 1
fi
# On confirmation (or with --dry-run: stop here)
git push origin "$BRANCH" # trigger CI which runs semantic-release
```
Wait for the GitHub Actions release workflow to complete:
```bash
# Capture run ID for the push commit so we poll the right run
sleep 10
RUN_ID=$(gh run list --branch "$BRANCH" --workflow ci-release.yml --limit 1 \
--json databaseId,headSha \
--jq "[.[] | select(.headSha == \"$BRANCH_SHA\")] | .[0].databaseId")
# Poll until the targeted run is done (timeout: 15 min)
STATUS="pending"
for i in $(seq 1 30); do
STATUS=$(gh run view "$RUN_ID" --json conclusion \
--jq '.conclusion // "pending"' 2>/dev/null || echo "pending")
[ "$STATUS" = "success" ] && break
[ "$STATUS" = "failure" ] || [ "$STATUS" = "cancelled" ] || [ "$STATUS" = "timed_out" ] && \
echo "Release workflow ended with status: $STATUS" && exit 1
sleep 30
done
if [ "$STATUS" != "success" ]; then
echo "Release workflow timed out after 15 minutes." >&2
exit 1
fi
```
### If releasing manually (no semantic-release)
```bash
# Determine next version (follow SemVer: breaking β major, new feature β minor, fix β patch)
NEXT_VERSION="vX.Y.Z" # fill in based on changelog analysis
# Tag and push
git tag -a "$NEXT_VERSION" -m "Release $NEXT_VERSION"
git push origin "$NEXT_VERSION"
# Create GitHub release
gh release create "$NEXT_VERSION" \
--title "$NEXT_VERSION" \
--notes-file "$RELEASE_DIR/changelog.md"
```
## Phase 5: Confirm build artifact
Download and validate the platform-specific build artifact. Poll every 2 minutes
(timeout: 20 min). Do not proceed to Phase 6 until the artifact is confirmed present.
```bash
ARTIFACT_FOUND=false
for i in $(seq 1 10); do
gh run download "$RUN_ID" --dir "$RELEASE_DIR" 2>/dev/null || true
case "$Platform" in
ios)
IPA=$(find "$RELEASE_DIR" -name "*.ipa" | head -1)
[ -n "$IPA" ] && ARTIFACT_FOUND=true && echo "iOS artifact: $IPA" && break
;;
android)
APK=$(find "$RELEASE_DIR" \( -name "*.apk" -o -name "*.aab" \) | head -1)
[ -n "$APK" ] && ARTIFACT_FOUND=true && echo "Android artifact: $APK" && break
;;
garmin)
PRG=$(find "$RELEASE_DIR" dist -name "*.prg" 2>/dev/null | head -1)
[ -n "$PRG" ] && ARTIFACT_FOUND=true && echo "Garmin artifact: $PRG" && break
;;
all)
IPA=$(find "$RELEASE_DIR" -name "*.ipa" | head -1)
APK=$(find "$RELEASE_DIR" \( -name "*.apk" -o -name "*.aab" \) | head -1)
PRG=$(find "$RELEASE_DIR" dist -name "*.prg" 2>/dev/null | head -1)
[ -n "$IPA" ] && [ -n "$APK" ] && [ -n "$PRG" ] && ARTIFACT_FOUND=true && break
;;
esac
echo "Attempt $i/10: artifact not yet available, waiting 2 minutes..."
sleep 120
done
if [ "$ARTIFACT_FOUND" != "true" ]; then
echo "Error: required build artifact not found after 20 minutes, aborting." >&2
exit 1
fi
```
## Phase 6: Submit to the store
**Skip this phase with `--dry-run`**: stop after Phase 3 and print the store copy
for manual paste.
Run the platform-specific submission workflow with the artifact from Phase 5:
**iOS:** Run `olko:apple-store-submit`: handles `altool` / `xcrun notarytool`
submission, watches for Apple's processing email, and catches common rejection patterns.
**Android:** Upload the `.aab` (or `.apk`) to Google Play using `fastlane supply`:
```bash
fastlane supply \
--aab "$APK" \
--track internal \
--package_name "$(cat android/app/build.gradle | grep applicationId | awk '{print $2}' | tr -d '"')"
```
Promote from internal β production after QA sign-off.
**Garmin:** Run `olko:garmin-watchface` store publishing workflow: uploads the `.prg`
and the listing copy from Phase 3 via the Connect IQ developer portal.
**all:** Run iOS β Android β Garmin in sequence; report each result independently.
## Final report
```
Release day complete: YYYY-MM-DD
Version: vX.Y.Z (was vA.B.C)
Platform: ios / android / garmin / all
Changelog: N features, M fixes, K improvements
Store copy: validated β (all fields within limits)
Release: tagged + GitHub Release created β
Build: CI green, artifact downloaded β
Submission: queued for App Store / Google Play / Connect IQ review
Next:
- Check Apple / Google / Garmin review status in 24β72h
- Monitor crash rates via Crashlytics / Firebase after rollout
- Run /review-past-performance at end of day to capture release learnings
```
## Safety guardrails
- Never push a tag or trigger a release if Phase 1 fails
- Never submit a store build without a validated Phase 3 output
- Always `--dry-run` first on a new repo or a new platform
- Keep the store copy file in $RELEASE_DIR until the submission is confirmed live
## Chaining
| Before this skill | After this skill |
|---|---|
| `olko:pr-to-green`: merge the release PR | Monitor store review status |
| `olko:semantic-release-beta`: promote beta to stable | `olko:changelog-generator` for next iteration |
| Manual: "all features are in, ship it" | Post-release monitoring |