본문으로 건너뛰기
cccskills
무료GitHub에 공개

vibe-release

Build, sign, notarize, and ship Vibe on both platforms — the macOS Developer ID (make release) and App Store (make appstore-build) paths, the iOS App Store path (make appstore-build-ios), the GitHub release publish (make github-release), the Homebrew tap bump (make brew-set-version), the localized product-page metadata upload (make appstore-upload-metadata), the shared App Store Connect API key and its Admin-role requirement, and the signing traps each script preflights. Use when cutting a release, distributing a build, shipping or TestFlighting the iOS app, updating App Store copy or screenshots, or debugging a signing/notarization/upload failure.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

연동 도구GitHub

실행 점수 미검증

설치 방법 보기

포함된 파일(1)

  • SKILL.md18.4 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Releasing Vibe

There are three release paths, and they are not interchangeable. Each uses a different certificate, container and verification:

make releasemake appstore-buildmake appstore-build-ios
platformmacOSmacOSiOS
scriptscripts/release/release.shscripts/appstore/release-appstore.shthe same, --platform ios
schemeVibeVibeVibeiOS
configurationReleaseAppStore (no updater)Release
certificateDeveloper ID ApplicationApple Distribution (+ Mac Installer)Apple Distribution
architectureuniversal plus arm64-onlyuniversal (arm64 + x86_64)arm64
outputtwo stapled .dmgs plus zipped appsuniversal .pkg to App Store Connect.ipa to App Store Connect
verificationnotarize + staple + spctl, app and image bothApp Store Connect validationApp Store Connect validation

Each App Store pair works the same way: appstore-build[-ios] stops after validation, and appstore-upload-signed-build[-ios] submits. One-time setup, reviewer notes and the App Store runbook are in docs/app-store-releasing.md.

TRAP: .release-env is gitignored, so it exists only in the checkout that created it. A worktree needs its own file or credentials in the environment. Release from the main checkout, or pass ASC_KEY_ID and ASC_ISSUER_ID in the environment.

Both archive scripts preflight asc_require_translations before the archive. A key missing any catalog language fails the release, because nothing else catches it: make check-strings compares the catalog to the source, and the build compiles a partial key silently, so that locale would show English. Fix it by translating, never by skipping (the vibe-strings skill). This is the in-app catalog, separate from the product-page copy below.

The Developer ID path

make release builds two Release archives: universal under build/release/ and arm64-only under build/release/arm64/. Each is exported with Developer ID, notarized and stapled, then packaged in its own plain drag-to-Applications DMG, which is itself signed, notarized and stapled. Architecture is a build input. Never thin the signed universal app, since changing its executable invalidates the signature and notarization. Each final zip is signed for Sparkle into <zip>.sig.

The DMG is the default download. A zip expands wherever the browser drops it, and a quarantined app launched from ~/Downloads runs translocated, from a random read-only mount that vanishes on quit. Settings > General > Default music player registers the path the app runs at, so a translocated copy would register a path that ceases to exist. Dragging out of a disk image clears translocation. The window is deliberately plain, since a background image needs Finder driven over AppleScript, the flakiest step in any DMG script. Four notarization submissions are needed, and every staple is checked at publish.

make github-release (scripts/release/github-release.sh; ARGS="--draft" for a review pass, ARGS="--prerelease" for a beta) re-verifies every exported app, DMG, mounted app and zipped app, including architecture and version, then tags HEAD as v<MARKETING_VERSION>. It attaches Vibe-macOS-universal-<version>.dmg and .zip and Vibe-macOS-arm64-<version>.dmg and .zip. Release notes come from Assets/app-store/copy/en/macos/whats-new.txt. It needs gh authenticated and a pushed HEAD, and refuses an existing release for the same version.

The release commit

A release commit bumps project.yml's vibe-version lines (MARKETING_VERSION and CURRENT_PROJECT_VERSION) and rewrites whats-new.txt. Make it as release: <ver>, on an up-to-date main in the main checkout. The build number never restarts with a new marketing version. Confirm rather than go backwards when a request reads like "1.14 (114)". When the notes are still under review, push the version bump alone and build from it. Then commit the notes as release: <ver> notes, wait for CI, and publish from that commit.

The notes name every cache archive version bump since the last release, since the new build then reads each file again. Two are pending after v1.17. The metadata archive's v8 (#400) parses every file's tags once. The waveform archive's format 7 (#355) decodes each waveform again when it is next shown.

A stable release sets VIBE_BETA_DEBUG: 0 in the same commit; a beta keeps 1. It controls VIBE_VERBOSE_LOGGING and the mac's Include beta versions default (receiveBetaUpdates). Verbose logging compiles in the beta instrumentation (Vibe/Audio/AGENTS.md, Beta diagnostics), which writes the user's file paths into the unified log as public text. No script flips VIBE_BETA_DEBUG, so a stable release ships verbose logging unless this commit turns it off. The commit that opens the next version on main sets it back to 1. Before publishing a stable release, strings -a build/release/export/Vibe.app/Contents/MacOS/Vibe | grep -c 'Timeline: play' must print 0. The flag is compile-time, so it governs the App Store builds too.

TRAP: github-release.sh tags the main checkout's HEAD at publish time, not the commit make release built. Another session committing in that checkout between the two moves the tag. Check git log -1 there just before publishing.

The full sequence

All from the main checkout, which has .release-env:

  1. The release commit, pushed.
  2. make release.
  3. make github-release. It repoints the web page, adds the feed items, tags, publishes, deploys the site and points the Homebrew tap. On the deploy's warning, run make deploy-web. Confirm 🔊 Homebrew tap pointed at <version>. On its warning, run make brew-set-version V=<version>.
  4. Check the tap as a user gets it: brew update && brew info --cask cmicali/tap/vibe names the new version.
  5. Once the tag exists, chart it: make bench-releases VERSIONS="<version>" on the machine every earlier version was measured on, then commit docs/performance.md and docs/performance/ (the vibe-perf skill). It replaces any pre-release point for that version.

A beta is make release, then make github-release ARGS="--prerelease". The page and the tap stay on the last stable release, and its feed items go out under the beta channel.

What github-release.sh does in order

The web site's mechanics (the Download button's version, the /download/latest 302 link, the local-only Cloudflare deploy, derived images and content-hashed assets) are in Assets/Web/README.md. The release ordering is here:

  1. Before the release, it runs scripts/web/web-set-version.sh <version> and adds the feed items. It commits index.html, _redirects and the feeds by explicit pathspec and pushes. A failed commit or push is fatal, since nothing is published yet and a tag on an unpushed commit would dangle. The tag-points-at-HEAD preflight runs after that push.
  2. It calls gh release create --target HEAD, so the tag names a tree whose page already advertises the release.
  3. After the release exists, it runs deploy-web.sh, since the page and the feed items name the release's assets, which 404 until then. A deploy failure is a warning naming the retry, make deploy-web.
  4. Last, it runs brew-set-version.sh (below). Its failure is a warning, so a missed warning is a stale tap.

--draft skips the page, the feeds, the deploy and the tap. A draft creates no tag until published and writes no feed item. Once a draft is published, repoint by hand: scripts/web/web-set-version.sh <version> && make deploy-web, then scripts/release/brew-set-version.sh <version>. --prerelease skips the page and the tap, since a beta must never become vibeplayer.app/download/latest, but still commits its feed items.

The Homebrew tap

brew install cmicali/tap/vibe installs from cmicali/homebrew-tap. Every stable release must move it, or brew upgrade keeps handing out the previous version. scripts/release/brew-set-version.sh writes the tap's one file, Casks/vibe.rb, whole from its own template and commits it through the GitHub contents API. Make cask changes (the zap paths are the sandbox's containers) in that template. The two sha256s are the digests GitHub computed for the published DMGs, so it cannot run before gh release create. It can be re-run for any stable release, and a re-run with nothing to change commits nothing. ARGS="--dry-run" prints the cask. A beta or a draft is refused.

Validate a template change with brew tap cmicali/tap <local clone> and brew audit --cask --strict --online cmicali/tap/vibe. brew install --cask --appdir=<scratch dir> installs without touching /Applications, and brew untap cmicali/tap puts the machine back. homebrew/cask refuses self-submitted apps below its notability bar, so the tap is the channel.

Updates (Sparkle)

The direct download updates itself through Sparkle 2, vendored at vibe-libs' third_party/Sparkle/ with its sign_update and generate_keys tools. The Mac App Store build has none of it. release-appstore.sh archives the AppStore configuration and fails if the archive carries any Sparkle file, link, SU* Info.plist key or the direct download's entitlements (asc_require_no_updater). CI builds AppStore and runs the same check.

The signing key is permanent. generate_keys makes an EdDSA key pair once and keeps the private half in the login keychain. The public half is VIBE_SPARKLE_PUBLIC_KEY in project.yml, and every shipped build trusts only it. Back the private half up offline beside the Developer ID certificate (generate_keys -x <file>; -f <file> imports it on another Mac). Losing it strands every installed build. release.sh preflights the key, then checks that each exported app trusts it.

Two feeds, by architecture. vibeplayer.app/appcast.xml offers the universal zip and appcast-arm64.xml the arm64-only one, so an arm64-only install stays one. The arm64-only archive passes VIBE_SPARKLE_FEED_URL. The feed URLs are baked into every build, so they never move.

The feeds live in Assets/Web/ and only github-release.sh writes them. It verifies each <zip>.sig, refuses a build number below a feed's newest item, and prepends one item per feed with whats-new.txt as Markdown. A --prerelease item carries <sparkle:channel>beta</sparkle:channel>, which only a build with Settings > General > Include beta versions on accepts. That setting defaults on in a beta build.

The export must sign Sparkle's helpers. release.sh checks that the app, the framework, Autoupdate, Updater.app and both XPC services carry the Developer ID, the hardened runtime and a timestamp. TRAP: a local build runs without the hardened runtime (project.yml). Under it, library validation refuses an ad-hoc-signed Sparkle in an ad-hoc-signed app, which dies at launch with "different Team IDs". asc_archive turns it on for every archive, and notarization rejects a build without it. A Debug build starts no updater unless launched with --update-feed <url>.

The App Store paths

Both apps ship under one bundle id, com.commonwealthrecordings.Vibe: Universal Purchase, one app record with shared name, subtitle, category, age rating and privacy answers, and a separate version train per platform. project.yml declares one version for both targets, but each platform needs its own version record open in App Store Connect carrying that string, or the build has nothing to attach to. A platform newly added to the record starts at 1.0, not at your number.

iOS has no direct-download path. A beta is TestFlight. Internal testing needs no beta review and lands minutes after processing. External testing needs Beta App Review for the first build of each version train, plus a beta description, a feedback email and Beta App Review Information.

Two postflights run on the exported .ipa, because neither thing exists until the archive is re-signed for distribution. The widget executable must be present in PlugIns/VibeWidget.appex/, and the embedded profile must grant group.com.commonwealthrecordings.Vibe. The widget draws only what it reads from that container, so a profile that dropped the entitlement would ship a permanently blank widget. On failure the decoded profile is left at build/appstore-ios/embedded.mobileprovision.plist.

Product-page metadata

One run writes one platform's page. App Store Connect localizations hang off a version, and versions are per platform, so macOS and iOS each have their own description, keywords, promotional text, what's new, captions and screenshots. --platform macos (the default) or --platform ios picks which. The iOS text is not the macOS text reworded: key analysis, the pitch fader and bit-perfect output are macOS-only, and a page describing features the app lacks is a review rejection.

The build upload carries no product-page content. Copy and screenshots live in Assets/app-store/ (format in its README) and upload separately with make appstore-upload-metadata. That runs scripts/appstore/appstore-upload-metadata.sh, which drives the Swift tool in scripts/appstore/asc-upload/ with the shared key.

The loop:

  1. Edit Assets/app-store/copy/<lang>/<platform>/ for every catalog language, not just en. Nothing auto-translates. Rewrite whats-new.txt for every release. The uploader omits it for a platform's first version. Later versions upload it verbatim. The shared support and marketing URLs upload to every version localization. The privacy URL uploads to app-info localizations.
  2. make appstore-validate-copy: character limits, leftover markdown, captions that overflow the screenshot layout. The upload runs it first anyway.
  3. make appstore-generate-store-screenshots-all, only if screenshots.json captions or the window captures changed.
  4. make appstore-upload-metadata ARGS="--dry-run", then without.

Flags via ARGS: --platform ios, --locales de,fr, --skip-screenshots, --skip-text, --create-version <v>.

  • It targets the one editable version on the chosen platform. After a release goes live there is none, and the tool errors with every version's state. --create-version <next> opens the next version's page, the same record a later build attaches to, so metadata first is the normal order.
  • Text is diffed, screenshots are not. Unchanged text fields are skipped. Each screenshot set is deleted and re-uploaded whole, ordered by file name, so "uploaded 4 screenshots" does not mean they changed.
  • macOS has one screenshot set per locale, iOS has two: APP_DESKTOP against APP_IPHONE_67 and APP_IPAD_PRO_3GEN_129. The iPad set is required because TARGETED_DEVICE_FAMILY is 1,2.
  • bg is skipped by design, since the App Store has no Bulgarian product page. Catalog nb maps to no. A new catalog language fails loudly until added to ascLocale in ASCUpload.swift.
  • The privacy policy URL is not a version field. It lives on appInfoLocalizations, per locale. copy/privacy-url.txt patches each locale whose URL differs. Creating an appInfoLocalization needs a name, so a locale with none is reported, not created.
  • Out of scope: the app name and subtitle. Edit them in App Store Connect by hand.
  • description.txt uploads verbatim, as plain text.

The shared API key

All scripts share one App Store Connect API key, ASC_KEY_ID and ASC_ISSUER_ID, from the gitignored .release-env at the repo root. scripts/appstore/asc-auth-lib.sh resolves it. The key covers cloud signing, notarization (notarytool --key/--key-id/--issuer, no app-specific password) and upload.

The key must carry the Admin role. Cloud-managed App Store certificates are Admin-gated, so an App Manager key authenticates and uploads but fails the export with a 403. A key's role cannot be edited after creation.

Signing traps

Each is guarded by a preflight or an error explainer.

  • Archives keep the project's automatic signing. Distribution identities belong in the export options. Pinning a distribution identity for the archive conflicts with automatic signing.
  • release.sh requires a local Developer ID Application certificate. It checks the keychain before building. Create the certificate through the team's signing setup before running it.
  • xcodebuild hides the reason. A cloud-signing denial surfaces only as "Cloud signing permission error", with Apple's real 403 buried in a temporary .xcdistributionlogs bundle. asc_explain_export_failure reprints it, with guidance per certificate type.

A Debug build for someone else's Mac

make release is Release-only. A Debug build for another Mac needs a separate signed and notarized package. Remove com.apple.security.get-task-allow from its entitlements. Sign the app and every nested Sparkle executable with Developer ID, hardened runtime and a timestamp. Use release.sh's nested-code checks as the inventory. Notarize and staple the package before sharing it.

TRAP: Source asc-auth-lib.sh from bash. Its repo lookup uses BASH_SOURCE.

What "upload succeeded" does not mean

UPLOAD SUCCEEDED is about bytes, not registration. A build takes minutes to appear in App Store Connect and the API. A run can end with buildUploadFiles timing out and "Skipping validation" among its warnings and still register. Never read an absent build as a failed delivery and re-upload: that burns the build number, and recovery means bumping CURRENT_PROJECT_VERSION and archiving again. Wait, or poll /v1/builds.

Two things mislead while checking:

  • curl eats filter[app]=…. [ and ] are its URL-glob syntax, so the query returns an empty body with exit 0. Pass -g (--globoff).
  • Never pipe JSON through zsh's echo. It rewrites escapes into control characters, and jq then reports malformed JSON. Use printf '%s', or a file.

A stuck App Review attachment (AWAITING_UPLOAD) is in docs/app-store-releasing.md.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Check and document Vibe's audio quality and conformance. Runs public conformance and torture corpora (IETF CELLAR FLAC files, ISO/IEC 14496-26 Layer III, FFmpeg FATE samples, McGill WAVE and AIFF samples, EBU BW64 tests, Chromium and Firefox media tests, TagLib test data) through the real decoders. Valid files must match their FLAC MD5 or reference decode within the ISO accuracy limits, decode to the length the handle states and seek exactly. Every file must finish with finite samples. Also regenerates docs/audio-quality.md's charts and its corpus results matrix. Use after changing a decoder, a parser, the file-open path or the resampler, before a release, when asked whether decoding is correct or conformant, or to update the audio quality page.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

Launch, drive, inspect, and visually verify the Vibe app — macOS, and the iOS simulator loop (launch-ios.sh, the debug-ios.sh command channel, the drive-ios.sh touch driver for taps and drags, silent flags, screenshots, host-side log streaming). Use whenever a change needs end-to-end verification, a screenshot of the running app, playback/UI state inspection, or appearance (light/dark) testing.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

vibe-perf

무료

Measure Vibe's performance and compare two versions of the code. VibeBenchComponents drives the production decode, open, seek, resample, pitch, waveform, tempo/key, meter, metadata and cache code in-process with no app, counting retired instructions as well as wall and CPU time; perf.py builds it at any git ref and runs two refs interleaved (make bench-components); profile.sh finds one benchmark's hotspots; --analyze proves an analyzer change exact. Also owns the performance page (docs/performance.md): component charts per release (make bench-components-releases), app benchmarks (make bench-app), both (make bench-releases), redraw (make bench-report). Use before and after any change that could move cost, to find where time goes, to choose between implementations, to back a performance claim with numbers, or when asked to update, rerun or regenerate the performance page for a release, pre-release or new machine.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

vibe-pr

무료

How to write a Vibe pull request title and description — simple, clear English that says what the change does, its benefit, and why, with pros and cons, charted results, and screenshots where they apply. Use when opening a pull request, or writing or rewriting a PR title or description.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

Stress, soak, fuzz, and torture the running Vibe app against a folder of real audio files — seeded random controller actions without pointer input, with consistency, leak, hang, and crash oracles, a single-playlist skip/seek torture suite for the delivery races that only open when transport outruns the metadata scan, a deterministic cloud-loading scenario suite over a fake file provider (download ordering, the foreground hold, the open deadline), plus the sanitizer (ASan/UBSan/TSan) and malloc-debug build matrix. Use for soak or endurance runs, memory-leak and resource-growth hunting, race hunting, fuzzing the file-loading path, hammering skips and seeks on a large playlist, testing cloud/placeholder loading order, or minimizing a failing run to a repro.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

The localization pipeline — VibeStrings.h registry conventions, the extract-strings.sh preprocessor pass and extractionState rules, catalog and InfoPlist.xcstrings mechanics, authoring translations and the untranslated-keys release gate, translation terminology and per-language conventions, the pseudolocale audit, and the localized App Store product page. Use when adding or rewording any user-facing string, translating catalog keys, editing VibeStrings.h or the .xcstrings catalogs, debugging make strings / make check-strings, testing a language, or updating App Store copy or screenshots.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

cmicali의 스킬 모두 보기

이 스킬의 문제 신고