wails-release/README.md

132 lines
6 KiB
Markdown
Raw Normal View History

# Wails Release Action
Build, sign, notarize, and (optionally) upload a [Wails](https://wails.io/) macOS app from a Forgejo Actions workflow on a self-hosted macOS runner.
The action produces a notarized, stapled `<AppName>-<version>.app.zip` and exposes its local path and (if uploaded) its `s3://` URL as outputs.
## Requirements
- A self-hosted **macOS** runner.
- `go` on `PATH` (Wails requires it).
- Network access to Apple's notary service and (if uploading) your S3 endpoint.
## Quick start
```yaml
name: Release
on:
push:
tags: ['v*']
jobs:
release:
runs-on: macos
steps:
- uses: actions/checkout@v4
- uses: leonmika/wails-release@v1
with:
developer-id-cert-base64: ${{ secrets.MAC_DEVELOPER_ID_CERT_BASE64 }}
developer-id-cert-password: ${{ secrets.MAC_DEVELOPER_ID_CERT_PASSWORD }}
notarization-api-key-base64: ${{ secrets.AC_API_KEY_BASE64 }}
notarization-api-key-id: ${{ secrets.AC_API_KEY_ID }}
notarization-api-issuer-id: ${{ secrets.AC_API_ISSUER_ID }}
s3-bucket: my-releases
s3-key: myapp/{version}/{filename}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
```
## Inputs
| Name | Required | Default | Description |
|---|---|---|---|
| `working-directory` | no | `.` | Directory containing `wails.json` |
| `app-name` | no | `wails.json` `name` | Override for the app name in artifact filenames |
| `version` | no | derived | Override; otherwise: matching semver tag → strip `v`, else 7-char short SHA |
| `wails-version` | no | from `go.mod` | Override the Wails CLI version |
| `extra-build-flags` | no | `""` | Additional flags appended to `wails build` (shell-quoted) |
| `developer-id-cert-base64` | **yes** | — | Base64-encoded Developer ID `.p12` |
| `developer-id-cert-password` | **yes** | — | `.p12` password |
| `notarization-method` | no | `auto` | `api-key`, `apple-id`, or `auto` (auto picks whichever group is fully populated) |
| `notarization-api-key-base64` | conditional | — | Base64-encoded App Store Connect `.p8` |
| `notarization-api-key-id` | conditional | — | API key ID |
| `notarization-api-issuer-id` | conditional | — | Issuer ID |
| `notarization-apple-id` | conditional | — | Apple ID (email) |
| `notarization-apple-password` | conditional | — | App-specific password |
| `notarization-team-id` | conditional | — | Developer Team ID |
| `s3-bucket` | no | — | Bucket name. If unset, upload is skipped. |
| `s3-key` | conditional | — | Object key. Required if `s3-bucket` is set. Supports `{version}` and `{filename}` placeholders. |
| `s3-endpoint-url` | no | — | Custom endpoint for S3-compatible storage (MinIO, R2, etc.) |
| `s3-region` | no | `us-east-1` | AWS region |
| `s3-acl` | no | — | Canned ACL applied to the uploaded object (e.g. `public-read`). Empty = no ACL sent. Modern AWS buckets with Object Ownership = "Bucket owner enforced" reject any ACL — leave this unset for those. |
AWS credentials (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, optionally `AWS_SESSION_TOKEN`) are read from the standard environment, **not** from action inputs.
## Outputs
| Name | Description |
|---|---|
| `version` | Resolved version string |
| `app-name` | Resolved app name |
| `artifact-path` | Local absolute path to the `.app.zip` |
| `artifact-filename` | Just the filename (e.g. `MyApp-1.2.3.app.zip`) |
| `s3-url` | `s3://bucket/key/...` if uploaded, else empty |
## Versioning rule
- A git ref of the form `refs/tags/vX.Y.Z` (no pre-release suffix) → version becomes `X.Y.Z`.
- Anything else → 7-character short SHA from `HEAD`.
- Override via the `version` input.
## Notarization credentials
You can use **either**:
- **App Store Connect API key** (recommended). Generate one in App Store Connect → Users and Access → Keys. You need the `.p8` file, the Key ID, and the Issuer ID.
- **Apple ID + app-specific password + team ID**. Generate the app-specific password at [appleid.apple.com](https://appleid.apple.com) → Sign-In and Security → App-Specific Passwords.
If both groups are populated and `notarization-method` is `auto`, the action errors with an ambiguity message — set `notarization-method` explicitly to disambiguate.
## S3-compatible storage
Set `s3-endpoint-url` to point at your storage:
```yaml
- uses: leonmika/wails-release@v1
with:
# …
s3-bucket: releases
s3-key: myapp/{version}/{filename}
s3-endpoint-url: https://my-minio.example.com
s3-region: auto
env:
AWS_ACCESS_KEY_ID: ${{ secrets.MINIO_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.MINIO_SECRET }}
```
For Cloudflare R2, set the endpoint to `https://<account>.r2.cloudflarestorage.com` and `s3-region` to `auto`.
## How it works
1. Resolve config from `INPUT_*` env, validate, and mask secrets in logs.
2. Resolve version (tag → strip `v`, else short SHA) and app name (from `wails.json`).
3. Ensure the Wails CLI matches the version pinned in your project's `go.mod` (or the `wails-version` override).
4. Run `wails build -platform darwin/universal -clean -trimpath` plus your `extra-build-flags`.
5. Create a temporary keychain, import the `.p12`, and codesign the `.app` with the hardened runtime and a secure timestamp. Verify the signature.
6. `ditto` the `.app` into a zip for notary submission.
7. `xcrun notarytool submit --wait` (API key or Apple ID, whichever was supplied). On rejection, fetch the per-submission log and embed it in the error.
8. `xcrun stapler staple` the bundle and re-zip so the on-disk artifact is offline-verifiable.
9. Optionally upload via the AWS SDK (custom endpoint supported).
10. Always run cleanup: delete the temp keychain and remove decoded `.p12` / `.p8` files.
## Local development
```bash
go test ./...
```
Smoke-testing real signing requires real credentials and is documented inline in `cmd/wails-release/integration_test.go`. The integration test itself uses fake external binaries on `PATH` and runs hermetically.