nickna / nickna/SharpTS

Publish managed and Native AOT variants through Homebrew

Open
#1,357 0 comments 0 reactions 1 assignee Claimed by @nickna View on GitHub
Dominant language
C#
Stars
154
Forks
4
Avg merge
2h 46m
Merged PRs (30d)
189

Description

## Summary

Publish both SharpTS Apple Silicon distributions through an upstream Homebrew tap:

- Managed, self-contained SKU (default/full feature set)
- Native AOT SKU (faster startup with documented Native AOT limitations)

The release workflow already produces macOS ARM64 tarballs for both SKUs. This issue covers release hardening, Homebrew cask creation, validation, initial tap publication, and update automation for Apple Silicon Macs.

Intel macOS is not in scope. SharpTS no longer publishes `osx-x64` release artifacts.

## Recommended package model

Create a public `nickna/homebrew-tap` repository with two Apple Silicon-only casks:

| Distribution | Cask token | Installed command |
| --- | --- | --- |
| Managed/default | `sharpts` | `sharpts` |
| Native AOT | `sharpts-native` | `sharpts` |

Installation commands:

```bash
brew install --cask nickna/tap/sharpts
# or
brew install --cask nickna/tap/sharpts-native
```

Declare the casks as mutually conflicting because both install `$(brew --prefix)/bin/sharpts`. Keeping the same command preserves scripts and documentation when switching implementations. If side-by-side installation becomes a requirement, rename the Native AOT command to `sharpts-aot` instead.

Use casks rather than `homebrew/core` formulae initially because the existing release artifacts are upstream-built, architecture-specific executables. A core formula would be separate work requiring a reproducible build from source and Homebrew bottles.

## Existing release assets

Use the versioned GitHub Release assets already produced by `.github/workflows/publish.yml`:

- `sharpts--osx-arm64.tar.gz`
- `sharpts-native--osx-arm64.tar.gz`

Each archive contains a root-level executable named `sharpts`, plus `LICENSE` and `README.md`. Both SKUs are standalone and should not declare a .NET dependency for basic CLI installation.

The initial casks must target a new successful stable release containing both assets. Do not target v1.0.8 because that release has no binary assets.

## Release hardening required

### Execute the managed Apple Silicon artifact

The managed `osx-arm64` artifact is currently cross-published on Ubuntu with `smoke: false`. Before Homebrew publication:

- [ ] Run `osx-arm64` managed publishing on an Apple Silicon macOS runner.
- [ ] Enable the managed release smoke suite for `osx-arm64`.
- [ ] Ensure the archived executable—not merely the publish-directory executable—is exercised before release.

The Native AOT `osx-arm64` release already builds and runs on a matching Apple Silicon macOS runner; retain that coverage.

### Developer ID signing and notarization

The current managed release relies on an ad-hoc Mach-O signature. Replace that distribution behavior for both SKUs:

- [ ] Obtain/configure a Developer ID Application certificate for CI.
- [ ] Sign managed and Native AOT executables with hardened runtime and a secure timestamp.
- [ ] Verify signatures with `codesign --verify --deep --strict`.
- [ ] Submit signed payloads to Apple with `xcrun notarytool submit --wait`.
- [ ] Validate Gatekeeper acceptance with `spctl` on Apple Silicon.
- [ ] Decide whether to add macOS ZIP assets alongside or instead of `.tar.gz`; ZIP aligns directly with Apple's documented notarization submission workflow and is supported by Homebrew Cask.
- [ ] Ensure the final distributed archive contains the exact signed/notarized executable that passed validation.

Do not require users to disable or bypass Gatekeeper.

## Tap and cask work

- [ ] Create the public `nickna/homebrew-tap` repository using Homebrew's standard tap layout.
- [ ] Retain the CI workflows generated by `brew tap-new nickna/tap`, adapting them for cask testing where needed.
- [ ] Add `Casks/sharpts.rb`.
- [ ] Add `Casks/sharpts-native.rb`.
- [ ] Add a tap README documenting Apple Silicon support, installation, upgrades, uninstall, SKU differences, mutual exclusivity, and switching instructions.

Each cask should include:

- An Apple Silicon architecture requirement
- Version-specific immutable GitHub Release URL for the `osx-arm64` asset
- SHA-256 value for the corresponding ARM64 archive
- `name`, `desc`, and canonical `homepage`
- `binary "sharpts"`
- Reciprocal `conflicts_with cask:` declarations
- Managed description emphasizing the full feature set
- Native description clearly listing the managed-only limitations
- Stable-release `livecheck` behavior, or an explicit skip if upstream automation is authoritative

Example managed cask shape:

```ruby
cask "sharpts" do
version ""
sha256 ""

url "https://github.com/nickna/SharpTS/releases/download/v#{version}/sharpts-#{version}-osx-arm64.tar.gz"
name "SharpTS"
desc "TypeScript interpreter and compiler implemented in C#"
homepage "https://github.com/nickna/SharpTS"

depends_on arch: :arm64
conflicts_with cask: "sharpts-native"

binary "sharpts"
end
```

## Validation

Run Homebrew audit/style plus install/uninstall tests for both casks on Apple Silicon:

- [ ] `brew audit --new --cask nickna/tap/sharpts`
- [ ] `brew style --fix --cask nickna/tap/sharpts`
- [ ] `brew install --cask nickna/tap/sharpts`
- [ ] Verify `sharpts --version` matches the release.
- [ ] Run an interpreter smoke test.
- [ ] Run managed compile and `--verify` smoke tests.
- [ ] `brew uninstall --cask nickna/tap/sharpts` and verify the binary link is removed.
- [ ] Repeat audit, style, install, runtime, and uninstall coverage for `sharpts-native`.
- [ ] Verify supported Native AOT interpretation/compilation paths.
- [ ] Verify managed-only Native AOT operations fail with their documented diagnostics.
- [ ] Verify the casks reject unsupported Intel Macs with a clear architecture requirement.
- [ ] Verify installing one SKU while the other is present reports the declared conflict.
- [ ] Verify `codesign` and `spctl` acceptance on the installed executable.
- [ ] Test upgrades between two cask versions once an update is available.

## Release automation

After a stable GitHub Release is created and both Apple Silicon artifacts have passed their release gates:

- [ ] Download or address the two published macOS ARM64 archives.
- [ ] Calculate the SHA-256 value for each archive.
- [ ] Update `version` and hash in both casks deterministically.
- [ ] Open a PR against `nickna/homebrew-tap`.
- [ ] Run Apple Silicon cask CI on the PR.
- [ ] Auto-merge only after all tap checks pass.
- [ ] Use a GitHub App restricted to `nickna/homebrew-tap`, or a fine-grained PAT with only Contents and Pull Requests access to that repository.
- [ ] Do not rely on the SharpTS repository's `GITHUB_TOKEN` for cross-repository writes.
- [ ] Gate automation until the initial casks have been published.
- [ ] Submit only stable tags. Add separate preview casks later only if preview distribution is explicitly desired.
- [ ] Update the main SharpTS README with the final Homebrew commands after the tap is live.

## Acceptance criteria

- `brew install --cask nickna/tap/sharpts` installs the managed Apple Silicon binary and exposes `sharpts`.
- `brew install --cask nickna/tap/sharpts-native` installs the Native AOT Apple Silicon binary and exposes `sharpts`.
- Both installed binaries pass macOS Gatekeeper assessment without user workarounds.
- `sharpts --version` reports the cask/release version for both SKUs.
- Install, upgrade, and uninstall work without stale Homebrew links.
- The two casks cannot be installed simultaneously under the shared command name.
- Unsupported Intel Macs receive a clear architecture compatibility error.
- The Native AOT cask communicates its reduced feature surface before installation.
- Future stable GitHub releases automatically open a tested update PR against the tap only after both Apple Silicon release assets have passed their existing smoke gates.

## Follow-up (not part of initial delivery)

Evaluate submitting the managed/default SKU to `homebrew/core` as a source-built formula. This requires reproducible .NET/NuGet dependency handling, a from-source build on Homebrew's supported matrix, meaningful formula tests, and Homebrew bottles. The Native AOT alternative should remain in the upstream tap unless maintaining two core build variants is justified.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.