Skip to content

Release ​

vidtrace uses GitHub Actions and GoReleaser for tagged releases.

CI ​

.github/workflows/ci.yml runs on pushes, pull requests, and manual dispatch:

  • formatting check with gofmt
  • go mod tidy drift check
  • go test ./...
  • binary build
  • golangci-lint v2.12.2
  • goreleaser check

CI does not run Whisper or OCR extraction because those checks require large runtime dependencies and media fixtures. Use task all locally for the full media-path verification.

Release Workflow ​

.github/workflows/release.yml runs on v* tags and manual dispatch.

Required repository secret:

  • HOMEBREW_TAP_TOKEN: a token with write access to abdul-hamid-achik/homebrew-tap

The normal GITHUB_TOKEN publishes the GitHub release in this repository. The Homebrew tap token updates the separate tap repository.

Routine Release ​

Before tagging, update CHANGELOG.md and run the local checks:

bash
task all
goreleaser check

Tag the release from main:

bash
git tag vX.Y.Z
git push origin vX.Y.Z

GoReleaser creates:

  • darwin and linux archives for amd64 and arm64
  • Linux .deb and .rpm packages for amd64 and arm64
  • checksums.txt
  • GitHub release notes
  • Casks/vidtrace.rb in the tap

Distribution decisions ​

  • Homebrew: cask, not formula. vidtrace ships a prebuilt binary, so the cask installs it directly without a build step. A formula would only duplicate that for a CLI, so it is intentionally not added. The cask currently strips the macOS quarantine attribute as a workaround for the unsigned binary; once signing and notarization are in place (below), that workaround and the security caveat can be removed.
  • Linux: .deb + .rpm via nfpms, attached to each release. No system dependencies are declared (see docs/INSTALL.md); vidtrace doctor reports the runtime tools.

macOS signing and notarization (blocked) ​

The binary is currently unsigned, so macOS Gatekeeper quarantines it (the cask works around this with xattr). Proper Developer ID signing + notarization would remove the warning. Enrollment and credentials are done, but signing is blocked by an Apple-side certificate issue, so releases still ship unsigned.

What is in place:

  • Apple Developer Program enrollment.
  • A Developer ID Application certificate, exported as a password-protected .p12.
  • An App Store Connect API key (Issuer ID, Key ID, and the .p8 private key) for notarization.
  • GitHub repository secrets: MACOS_SIGN_P12 (base64 .p12), MACOS_SIGN_PASSWORD, APPLE_API_ISSUER_ID, APPLE_API_KEY_ID, APPLE_API_KEY.

The blocker ​

A GoReleaser notarize.macos block (which uses the bundled quill) was wired up and the release failed during signing with:

failed to verify certificate chain: x509: unhandled critical extension

Inspecting the certificate confirms the trigger: Developer ID certificates mark the Apple extension OID 1.2.840.113635.100.6.1.13 as critical, and Go's crypto/x509 (which quill is built on) treats an unrecognized critical extension as a hard failure. This is not a problem with the certificate — that OID being critical is by-design on every Developer ID cert (global, non-removable; Apple's own US-issued certs carry it, confirmed on Apple Developer Forums thread 803047). Reissuing the certificate will not help.

A nuance worth knowing before re-attempting quill: the quill version GoReleaser pins already strips 1.2.840.113635.100.6.1.13 (and .6.1.18) from the leaf before verifying (anchore/quill since v0.5.0). So our failure was most likely a missing Developer ID G2 intermediate in the exported .p12, or a critical extension on a non-leaf cert — quill runs with failWithoutFullChain=true and Go's strictness either way. Re-exporting the .p12 with the full chain (leaf + key + "Developer ID Certification Authority - G2") might rescue the quill path, but that is unverified.

The lowest-risk fix (researched 2026-06-20, not yet implemented) is to replace quill with rcodesign (Rust apple-codesign, indygreg/apple-platform-rs), which runs on the existing Linux runner:

  • Its x509-certificate crate has no critical-extension rejection path — it cannot reproduce the Go failure. (rcodesign itself even generates Developer ID certs with that OID marked critical and round-trips it.)
  • It bundles the Apple intermediates (incl. Developer ID G2) and auto-embeds them, neutralizing the missing-intermediate failure mode.
  • Caveat: no published third-party success report for this exact cert, so confidence rests on source-code analysis — prove it on a throwaway prerelease tag (or a local rcodesign sign on a darwin build) before trusting it.

Sketch (keep GoReleaser for build/archive/checksum/cask; remove notarize.macos; add post-build steps on ubuntu-latest):

bash
# build the App Store Connect key JSON from the existing .p8 / issuer / key-id secrets
rcodesign encode-app-store-connect-api-key "$APPLE_API_ISSUER_ID" "$APPLE_API_KEY_ID" AuthKey.p8 key.json
# sign each darwin binary with the .p12 secret
rcodesign sign --p12-file cert.p12 --p12-password "$MACOS_SIGN_PASSWORD" --for-notarization ./vidtrace
# notarize (bare Mach-O binaries cannot be stapled; Gatekeeper does an online ticket check on first run)
rcodesign notary-submit --api-key-file key.json --wait ./vidtrace

Either use indygreg/apple-code-sign-action@v1 (pin rcodesign_version: 0.29.0; inputs map directly onto the existing secrets) or the CLI above as a GoReleaser build hook. A macOS-runner native codesign + xcrun notarytool job is a viable fallback but is strictly more CI surgery (temp keychain, set-key-partition-list, explicit G2 intermediate install, sign-before-checksum ordering) for the same outcome.

Until this is wired up, releases ship unsigned and the cask keeps the xattr quarantine workaround and caveat. The user-facing impact is limited to direct GitHub-Release downloaders; Homebrew users are unaffected.

One-Time Setup ​

The public repository is https://github.com/abdul-hamid-achik/vidtrace. The release workflow needs this repository secret before publishing tags:

  • HOMEBREW_TAP_TOKEN: a token with write access to abdul-hamid-achik/homebrew-tap

Homebrew Install ​

After the tag workflow passes:

bash
brew tap abdul-hamid-achik/tap
brew install --cask abdul-hamid-achik/tap/vidtrace
vidtrace doctor

Local Snapshot ​

Use a snapshot to inspect release artifacts without publishing:

bash
goreleaser release --snapshot --clean --skip publish

The generated files stay under dist/, which is ignored by Git.

Notes ​

  • Do not store release tokens in the repo.
  • Do not commit generated dist/ output.
  • Update .goreleaser.yaml if the final GitHub repository owner differs from the module path.

Local-first. MIT licensed.