一键导入
release
Publish a new app release with versioning, changelog, native build files, and git tagging. Triggered by "publish a new release" or similar requests.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Publish a new app release with versioning, changelog, native build files, and git tagging. Triggered by "publish a new release" or similar requests.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Ditto's release and publishing pipeline — cutting a version tag, Zapstore APK publishing with NIP-46 bunker auth, nsite web deploys via nsyte, and Google Play AAB uploads via fastlane supply. Includes GitLab CI variable setup and credential rotation.
Operate the self-hosted GitLab Runner on the Mac that builds Ditto's iOS IPA. Covers SSH access, restarting the runner, viewing logs, updating Xcode, debugging fastlane locally, and rotating match certificates.
Decide whether to reuse an existing NIP or mint a new kind, design tag structures that relays can index, choose what goes in content vs. tags, and document new kinds or extensions in NIP.md. Load when authoring a new schema — not when wiring up rendering for a kind that already exists (use nostr-kind-rendering for that).
Add UI rendering for an event kind Ditto doesn't yet display — feed cards, detail pages, embedded previews, notifications, routes, feed-toggle registration, and the several kind-label maps (KIND_LABELS, KIND_HEADER_MAP, NOTIFICATION_KIND_NOUNS, CommentContext) that must stay in sync. Load when asked to "support / display / render" a NIP or kind number, when a kind renders blank or as "Kind 12345", or when quote embeds of a kind show "This event kind is not supported".
Browser-API gotchas inside Capacitor's WKWebView (iOS) and Android WebView — which common web APIs silently fail, the downloadTextFile/openUrl helpers that bridge web and native, platform detection, and the installed Capacitor plugins. Load when writing code that interacts with file downloads, external URLs, or platform-specific behavior.
Upload files (images, media, attachments) from the browser to a Blossom server via the useUploadFile hook, and attach them to Nostr events with NIP-94 imeta tags.
| name | release |
| description | Publish a new app release with versioning, changelog, native build files, and git tagging. Triggered by "publish a new release" or similar requests. |
This skill guides you through publishing a new release of the app. It handles version bumping, changelog generation, native build file updates, and git tagging/pushing.
package.json version fieldCHANGELOG.md in repo root, using Keep a Changelog formatv2.1.0) triggers the CI pipeline to build APKs, create a GitLab release, and publish to ZapstoreFollow these steps in order. Do NOT skip any step.
Before writing any release notes, you MUST read these pages to understand the product context, voice, and values:
These pages define what Ditto is, how it's positioned, and the tone of voice to use. Changelog entries should reflect this identity: fun, rebellious, user-focused, emphasizing freedom and self-expression. Avoid dry technical jargon -- write for people who use the app, not developers.
# Ensure working directory is clean
git status
# Ensure we're on main branch
git branch --show-current
# Run the full test suite
npm run test
main, warn the user and ask whether to proceed.# Get the current version from package.json
node -p "require('./package.json').version"
# Get commits since the last version tag
git log v$(node -p "require('./package.json').version")..HEAD --oneline
Analyze the commits from Step 3 and determine the appropriate bump level:
| Bump | When to use | Example |
|---|---|---|
| Patch | Bug fixes, minor tweaks, dependency updates, small UI polish, internal tooling, developer-facing pages, CI/build changes, settings/admin screens | 2.0.0 -> 2.0.1 |
| Minor | Significant new product features that change how users interact with the app -- the kind of thing you'd highlight in an app store update or announce on social media (e.g., new content type support, DM redesign, new social features, theme system overhaul) | 2.0.1 -> 2.1.0 |
| Major | ONLY when the user explicitly instructs a major bump | 2.1.0 -> 3.0.0 |
Default to patch when in doubt. The bar for a minor bump is high -- ask yourself: "Would a regular user notice and care about this change?" If the answer is no, it's a patch. Internal pages (changelog, settings, about screens), infrastructure improvements, CI fixes, and developer tooling are always patch-level regardless of whether they technically add a new page or screen.
When bumping minor, reset patch to 0 (e.g., 2.0.3 -> 2.1.0). When bumping major, reset minor and patch to 0 (e.g., 2.3.1 -> 3.0.0).
Prepend a new section to CHANGELOG.md directly below the # Changelog heading.
Format:
## [X.Y.Z] - YYYY-MM-DD
A short single-paragraph summary of this release written in plain prose -- max 500 characters. This appears on the App Store, Google Play, and the in-app "what's new" toast.
### Added
- Description of new features
### Changed
- Description of changes to existing features
### Fixed
- Description of bug fixes
### Removed
- Description of removed features
Every release section MUST start with a single plaintext paragraph (not a bullet, not a heading) that summarises the release for app-store-style audiences:
release-notes job warns when the summary is longer.A behind-the-scenes maintenance release with no user-facing changes. Don't leave it blank; the CI fallback Ditto vX.Y.Z is a last resort for legacy entries, not new ones.The same paragraph is used in three places automatically:
deliversupply metadata/android/<lang>/changelogs/<versionCode>.txtWhat's new in vX.Y.Z toast that fires when users load a new version (see src/components/VersionCheck.tsx)Extraction is handled by scripts/extract-release-notes.mjs; you don't have to write store-specific copy.
Before drafting any entries, run through this checklist. It is NOT optional -- skipping steps here is the most common way a release goes out with misleading notes.
Commit messages describe intent at the moment of commit; they over- and under-represent the cumulative effect at release time. Before drafting entries, run a real diff for each area of substantial change:
# Full diff between tags
git diff v<prev>..HEAD
# Or narrowed to an area you're unsure about
git diff v<prev>..HEAD -- src/components/ComposeBox.tsx
Only the diff reveals intra-release churn (commits that cancel each other out, bugs introduced and then fixed, refactors that land and get reverted). Reading commit messages alone is insufficient.
For each bug fix you're considering listing, find the commit that introduced the bug.
Fast path -- check for Regression-of: trailers (see AGENTS.md "Attributing Regressions"). If the fix commit declares its origin in a trailer, you don't need to hunt:
# List all commits in the release window with their Regression-of trailers (if any)
git log v<prev>..HEAD --no-merges \
--format='%h %s%n Regression-of: %(trailers:key=Regression-of,valueonly,separator=%x20)'
For each Regression-of: <sha> entry, check whether <sha> is also in the release window:
# Returns 0 if <sha> is BEFORE v<prev> (pre-existing bug -> legit "Fixed" entry)
# Returns non-zero if <sha> is AFTER v<prev> (intra-release -> omit from "Fixed")
git merge-base --is-ancestor <sha> v<prev>
Fallback -- manual tracing (when no trailer is present):
# Show the history of a file across all commits
git log --oneline v<prev>..HEAD -- path/to/file.tsx
# Or blame the specific lines the fix touched
git blame -L <start>,<end> -- path/to/file.tsx
If the introducing commit is also in this release window (i.e. after the previous tag), the bug is intra-release. The user on the previous version never experienced it. Do NOT list it as a "Fixed" entry. Fold it into the relevant "Added" or "Changed" entry, or omit it entirely.
The changelog describes the delta between the previous release and this one from the user's perspective -- not the development history. Before writing each entry, ask:
"Did a user on the previous published version experience this exact thing?"
This applies to more than just bugs:
Scenario: Commit A overhauls the compose box and, as a side effect, breaks the background of the expanded emoji picker. Commit B, later in the same release window, restores the background.
Correct changelog: One "Added" entry describing the compose box overhaul. The emoji picker background is part of the finished state the user receives.
Incorrect changelog: An "Added" entry for the overhaul AND a "Fixed" entry for the emoji picker background. The user on the previous version never saw the broken background; listing it invents a problem they didn't have and makes the release notes read like a developer changelog.
Update the version string in these files:
package.jsonUpdate the version field:
"version": "X.Y.Z"
android/app/build.gradleUpdate versionName (line 17). Do NOT change versionCode -- that is managed by CI:
versionName "X.Y.Z"
ios/App/App.xcodeproj/project.pbxprojUpdate MARKETING_VERSION in all occurrences (Debug + Release configs):
MARKETING_VERSION = X.Y.Z;
Important: All lines containing MARKETING_VERSION must be updated to the same value. Use a replaceAll operation.
Do NOT change CURRENT_PROJECT_VERSION -- it stays at 1 (may be managed separately for App Store submissions in the future).
The changelog is served at runtime by the app from the public/ directory. After updating CHANGELOG.md, copy it:
cp CHANGELOG.md public/CHANGELOG.md
Before committing the release, pull the latest changes from the remote to ensure the release commit sits on top of the latest code. This must happen before committing and tagging.
git pull origin main
CRITICAL: Always use git pull (merge), NEVER git pull --rebase. Rebasing rewrites commit hashes, which would orphan any tag pointing to the original commit. Since version tags are often protected on the remote and cannot be deleted or updated, a broken tag cannot be easily fixed.
If there are merge conflicts with the pulled changes, resolve them before proceeding.
git add package.json CHANGELOG.md public/CHANGELOG.md android/app/build.gradle ios/App/App.xcodeproj/project.pbxproj
git commit -m "release: vX.Y.Z"
git tag vX.Y.Z
The tag format is v followed by the semver version with no suffix. Examples: v2.0.0, v2.1.0, v2.1.1.
git push origin main vX.Y.Z
CRITICAL: Push only the specific tag being released. NEVER use --tags -- that pushes ALL local tags, including stale or deleted ones.
This triggers the GitLab CI pipeline which will:
CHANGELOG.mdAfter pushing, inform the user:
| File | What to update | Notes |
|---|---|---|
package.json | version field | Source of truth for the version |
CHANGELOG.md | Prepend new section | User-facing changelog |
public/CHANGELOG.md | Copy from CHANGELOG.md | Served at runtime by the app |
android/app/build.gradle | versionName on line 17 | versionCode is managed by CI |
ios/App/App.xcodeproj/project.pbxproj | MARKETING_VERSION (all occurrences) | CURRENT_PROJECT_VERSION stays at 1 |
The CI pipeline (.gitlab-ci.yml) is triggered by tags matching the pattern /^v\d+\.\d+\.\d+$/ (e.g., v2.1.0). It runs seven jobs:
versionName and versionCode into the buildtags: [macos]); stamps MARKETING_VERSION and CFBundleVersion into the Xcode project. The IPA is uploaded to GitLab's Generic Packages registry and exposed as a CI artifact for downstream jobsCHANGELOG.md into two artifacts (release-notes.md and release-notes-summary.txt) consumed by release, publish-app-store, and publish-google-playmetadata/android/en-US/changelogs/<versionCode>.txttags: [macos]) because fastlane deliver shells out to Apple's iTMSTransporter to upload the IPA, and that tool only ships inside Xcode — the previous Linux runner crashed at the upload step with No such file or directory @ dir_chdir0 because Helper.itms_path resolved to a missing Xcode path. The build appears in App Store Connect within ~30 minutes; Apple's human review then takes 24-48 hours typically. Once approved, you must release manually in App Store Connect (automatic_release: false) — this is the final human gate. For runner operations, match cert rotation, and debugging, load the mac-runner skill.If git log shows no commits since the last tag, there genuinely is nothing to release.
Fix the failing tests before proceeding. The release must not contain broken code.
If you tagged the wrong version and haven't pushed yet:
git tag -d vX.Y.Z # delete the local tag
git reset --soft HEAD~1 # undo the commit but keep changes staged
Then redo steps 4-10 with the correct version.
This requires manual intervention. Inform the user and suggest they delete the tag and release from GitLab manually, then re-run the release process.