Lets the workflow set, e.g., public-read on the uploaded object so the HTTPS URL is actually downloadable without further configuration. Empty default means no ACL is sent — required for modern AWS buckets with Object Ownership = "Bucket owner enforced" that reject any ACL. Validates the value against the AWS canned-ACL list at config time so typos fail before the upload runs. Wires the input through action.yml, config, and the orchestrator; adds a unit test that the ACL is forwarded to PutObjectInput when set and omitted when empty. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 KiB
Wails Release Action
Build, sign, notarize, and (optionally) upload a Wails 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.
goonPATH(Wails requires it).- Network access to Apple's notary service and (if uploading) your S3 endpoint.
Quick start
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 becomesX.Y.Z. - Anything else → 7-character short SHA from
HEAD. - Override via the
versioninput.
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
.p8file, the Key ID, and the Issuer ID. - Apple ID + app-specific password + team ID. Generate the app-specific password at 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:
- 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
- Resolve config from
INPUT_*env, validate, and mask secrets in logs. - Resolve version (tag → strip
v, else short SHA) and app name (fromwails.json). - Ensure the Wails CLI matches the version pinned in your project's
go.mod(or thewails-versionoverride). - Run
wails build -platform darwin/universal -clean -trimpathplus yourextra-build-flags. - Create a temporary keychain, import the
.p12, and codesign the.appwith the hardened runtime and a secure timestamp. Verify the signature. dittothe.appinto a zip for notary submission.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.xcrun stapler staplethe bundle and re-zip so the on-disk artifact is offline-verifiable.- Optionally upload via the AWS SDK (custom endpoint supported).
- Always run cleanup: delete the temp keychain and remove decoded
.p12/.p8files.
Local development
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.