Skip to content

Latest commit

 

History

History
280 lines (225 loc) · 12 KB

File metadata and controls

280 lines (225 loc) · 12 KB

Ad hoc release runbook

Scope and distribution warning

Phase 11E produces a ZIP containing an ad hoc-signed, Hardened Runtime-enabled macOS app. It also produces a SHA-256 checksum and a Manifest v1 JSON document. The GitHub Actions workflow retains those three files as one Actions artifact for 30 days. It does not create a GitHub Release automatically. After the canonical v1.0 tag artifact was independently verified, it was published as a separate, explicit GitHub Release operation.

This artifact is not signed with Developer ID and is not notarized. It is not expected to pass Gatekeeper as an ordinarily downloaded application. It is not an App Store, DMG, dSYM, or notarization deliverable.

Prerequisites

  • A clean, non-shallow clone with all Git refs available.
  • Xcode 26.6, build 17F113.
  • betterleaks available on PATH.
  • macOS command-line tools including codesign, ditto, lipo, plutil, and shasum.
  • No existing file at any of the three expected output paths. The script never silently replaces an artifact.

The default developer directory is /Applications/Xcode.app/Contents/Developer. The GitHub Actions runner instead uses /Applications/Xcode_26.6.app/Contents/Developer.

Build locally

From the repository root:

scripts/build-release.sh

To select another output directory or the versioned Xcode installation:

DEVELOPER_DIR=/Applications/Xcode_26.6.app/Contents/Developer \
  scripts/build-release.sh --output-dir build/release

For the current MARKETING_VERSION=1.0 and CURRENT_PROJECT_VERSION=1, the output is:

build/release/
  PicChronoImporter-1.0-build1.zip
  PicChronoImporter-1.0-build1.zip.sha256
  PicChronoImporter-1.0-build1-manifest.json

Before building, the script verifies the Git state, exact Xcode version, ad hoc manual signing, Hardened Runtime, disabled base-entitlement injection, bundle identifier, and output-path availability. It runs betterleaks with complete redaction against all Git refs and every tracked or untracked-but-not-ignored file. A finding stops the release before compilation.

Release-only CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO follows Apple’s guidance for removing development entitlements from a custom distribution build; see Resolving common notarization issues.

The script then builds both arm64 and x86_64 for generic/platform=macOS, verifies the built bundle, packages it with ditto, checks the SHA-256 file, extracts the ZIP, and verifies the extracted bundle again. Both bundle checks require strict deep code-signing validity, an adhoc,runtime signature, no Team ID, no get-task-allow entitlement, the expected bundle identifier, and both architectures.

Tag contract

A tag build must use exactly v<MARKETING_VERSION>. For the current version, the only valid release tag is v1.0; the build number remains 1 and is recorded in the filenames and manifest rather than in the tag. A tag such as v1.0.0, v1.0-build1, or v2.0 fails before the build.

The same check applies locally when HEAD has an exact tag. Creating or pushing a tag is an explicit release operation and is not performed by the script.

GitHub Actions

The Release artifact workflow supports:

  • a manual run from the Actions page (workflow_dispatch); and
  • a push of a tag matching v*.

It runs on macos-26 with Xcode 26.6 at the versioned developer directory. The workflow first runs the Core package tests, PlatformMac package tests, and app unit tests. UI tests and the private RepresentativeRealMedia suite are not run on GitHub Actions. It then invokes the same scripts/build-release.sh used locally and uploads exactly the ZIP, checksum, and manifest as one artifact.

Workflow permissions are limited to contents: read. Checkout does not retain credentials, and the workflow does not publish a GitHub Release or push any Git state.

Verify a downloaded artifact

Download and expand the single Actions artifact, then run these commands from the directory containing its three files:

shasum -a 256 -c PicChronoImporter-1.0-build1.zip.sha256

mkdir PicChronoImporter-verify
ditto -x -k PicChronoImporter-1.0-build1.zip PicChronoImporter-verify

codesign --verify --deep --strict --verbose=4 \
  PicChronoImporter-verify/PicChronoImporter.app
codesign -d --verbose=4 PicChronoImporter-verify/PicChronoImporter.app
codesign -d --entitlements - PicChronoImporter-verify/PicChronoImporter.app

lipo -archs \
  PicChronoImporter-verify/PicChronoImporter.app/Contents/MacOS/PicChronoImporter
plutil -p PicChronoImporter-verify/PicChronoImporter.app/Contents/Info.plist
plutil -p PicChronoImporter-1.0-build1-manifest.json

Confirm all of the following:

  • the checksum reports OK;
  • codesign --verify exits successfully;
  • signature details contain Signature=adhoc, both adhoc and runtime flags, and TeamIdentifier=not set;
  • the entitlements output does not contain com.apple.security.get-task-allow;
  • architectures are exactly arm64 x86_64 (order is not significant);
  • CFBundleIdentifier is com.kofeincska.picchronoimporter, version is 1.0, build is 1, and the minimum macOS version matches the manifest.

Gatekeeper acceptance is deliberately not a verification criterion. If macOS blocks the app, use Apple’s Open a Mac app from an unknown developer “Open Anyway” procedure only after verifying both the checksum and the source of the artifact. Do not disable Gatekeeper globally or permanently.

Manifest v1 contract

The manifest is UTF-8 JSON with schemaVersion: 1. It contains no absolute filesystem path and no private-media filename, metadata, or content hash.

Manifest v1 defines these fields:

  • product: name, bundleIdentifier, marketingVersion, buildNumber, minimumSystemVersion, and architectures.
  • source: immutable commit, ref, dirty (always false), and optional tag.
  • build: UTC creation time, host macOS version/build, Xcode version/build, and SDK name.
  • security: betterleaks version and scan results, signing kind, Hardened Runtime state, getTaskAllow, and Team ID state.
  • package: ZIP/checksum/manifest basenames, ZIP SHA-256, packaging tool, arguments, and the path-free packaging command.
  • verification: built-bundle code-signing, extracted-bundle code-signing, checksum, and architecture results.

Field additions that preserve these meanings may remain schema v1. A consumer must reject an unsupported schemaVersion; incompatible meaning or structure requires a new schema version.

Publish a verified GitHub Release

GitHub Release publication is a separate operation after the tag-triggered artifact passes independent verification. Before publishing:

  • verify that the remote tag already exists and resolves to the expected commit;
  • verify that the tag-triggered workflow succeeded;
  • download and independently verify the Actions artifact;
  • confirm that a Release for the tag does not already exist; and
  • prepare notes that prominently disclose ad hoc signing and missing notarization.

Attach exactly the application ZIP, its SHA-256 file, and Manifest v1. Use --verify-tag so publication cannot silently create a tag at another commit. After publication, confirm that the Release is public and inspect its draft and prerelease flags, re-download all assets, and compare them byte-for-byte with the verified Actions artifact.

Publishing an ad hoc-signed artifact does not make it Gatekeeper-ready and does not change the deferred Developer ID, notarization, or App Store policy.

Release acceptance record

Phase 11E acceptance requires an artifact downloaded from GitHub Actions to be independently verified and exercised on a Mac.

2026-07-22 — PicChronoImporter 1.0 (build 1)

  • Workflow run URL: https://github.com/3ckey/picchronoimporter/actions/runs/29856922122
  • Artifact name and retention expiry: PicChronoImporter-1.0-build1, 2026-08-20T18:27:25Z (2026-08-21 03:27 JST)
  • Source commit and ref: ebe320194b815dca37c903729914fd3b8f7f7319, refs/heads/main
  • Host Mac model / architecture: Mac13,1 / arm64
  • macOS version and build: 26.5.2 / 25F84
  • SHA-256 verification: passed, 114c350aae65e7eb976514c641b41efb6eef0fe0799b0f8aaa4730fd3f532513
  • Extracted codesign --verify --deep --strict: passed
  • Signature (adhoc,runtime, no Team ID, no get-task-allow): passed
  • Architectures and Info.plist: passed; x86_64 arm64, bundle identifier com.kofeincska.picchronoimporter, version 1.0, build 1, minimum macOS 26.0
  • App launch: passed; confirmed by the repository owner
  • Curated representative import: passed; confirmed by the repository owner
  • Private RepresentativeRealMedia run and date: passed on 2026-07-22, including RAF, JPEG, HIF/HEIC, and MOV import-integrity coverage
  • Reviewer and notes: 3ckey; Gatekeeper acceptance was not used as a release criterion, and the artifact remains ad hoc signed and non-notarized

This acceptance record completes Phase 11E.

2026-07-22 — Canonical v1.0 tag artifact verification

  • Annotated tag and target: v1.0, 009122b094b5505fca1b1aaa25da4f53ad29f599
  • Workflow run URL: https://github.com/3ckey/picchronoimporter/actions/runs/29894839863
  • Trigger and source ref: tag push, refs/tags/v1.0
  • Workflow result: passed in 2 minutes 35 seconds, including Core, PlatformMac, app unit, Release build, verification, and artifact upload steps
  • Artifact name and retention expiry: PicChronoImporter-1.0-build1, 2026-08-21T05:51:16Z (2026-08-21 14:51 JST)
  • Downloaded contents: exactly the versioned ZIP, checksum, and Manifest v1
  • SHA-256 verification: passed, eb58972258f505fcf57e9af7cac416be05a781aa99314c3cd0f16a68538e93af
  • Extracted codesign --verify --deep --strict: passed
  • Signature (adhoc,runtime, no Team ID, no get-task-allow): passed
  • Architectures and Info.plist: passed; x86_64 arm64, bundle identifier com.kofeincska.picchronoimporter, version 1.0, build 1, minimum macOS 26.0
  • Manifest v1: passed; source tag/ref/commit, Xcode 26.6 build 17F113, security results, package checksum, and path-free fields are consistent
  • App launch and curated representative import: not repeated because the tag target differs from the exercised acceptance commit only by docs/ updates

2026-07-22 — Public GitHub Release publication

  • Release URL: https://github.com/3ckey/picchronoimporter/releases/tag/v1.0
  • Release ID and publication time: 357824088, 2026-07-22T06:54:39Z (2026-07-22 15:54 JST)
  • State: public, Latest, not a draft, and not a prerelease
  • Release title and tag: PicChronoImporter v1.0, v1.0
  • Attached assets: exactly the application ZIP, SHA-256 file, and Manifest v1
  • Published ZIP SHA-256: eb58972258f505fcf57e9af7cac416be05a781aa99314c3cd0f16a68538e93af
  • Manifest SHA-256: 88faea4c556facf453270b54c9c1475c83f30d862d91086e0bb71c7af5842a90
  • Checksum-file SHA-256: 53c8ccb34120e90ef61b52c64767e332120e89a8f3c7fb591b1ea06ca9be4894
  • Post-publication verification: all three assets were downloaded from the Release, matched the verified Actions artifact byte-for-byte, and the ZIP checksum passed
  • Public accessibility: unauthenticated requests to the Release page and all three asset URLs returned HTTP 200
  • Release notes: English and Japanese summary, requirements, build provenance, checksum, and explicit ad hoc-signing/notarization/Gatekeeper warning included

Copy the template below for future accepted builds:

### YYYY-MM-DD — PicChronoImporter <version> (build <build>)

- Workflow run URL:
- Artifact name and retention expiry:
- Source commit and ref:
- Host Mac model / architecture:
- macOS version and build:
- SHA-256 verification: passed / failed
- Extracted `codesign --verify --deep --strict`: passed / failed
- Signature (`adhoc,runtime`, no Team ID, no `get-task-allow`): passed / failed
- Architectures and Info.plist: passed / failed
- App launch: passed / failed
- Curated representative import: passed / failed
- Private `RepresentativeRealMedia` run and date:
- Reviewer and notes: