Merge branch 'next' of github.com:OliveTin/OliveTin into next
This commit is contained in:
commit
25fd92396c
|
|
@ -37,6 +37,11 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
new_release_published: ${{ steps.release.outputs.new_release_published }}
|
||||
new_release_git_tag: ${{ steps.release.outputs.new_release_git_tag }}
|
||||
windows_zip_artifact_id: ${{ steps.upload-windows-zip.outputs.artifact-id }}
|
||||
windows_msi_artifact_id: ${{ steps.upload-windows-msi.outputs.artifact-id }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
|
@ -129,6 +134,7 @@ jobs:
|
|||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: release
|
||||
id: release
|
||||
if: github.ref_type != 'tag' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
||||
uses: cycjimmy/semantic-release-action@v5
|
||||
with:
|
||||
|
|
@ -139,6 +145,29 @@ jobs:
|
|||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.CONTAINER_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.CONTAINER_TOKEN }}
|
||||
MACOS_SIGN_P12: ${{ secrets.MACOS_SIGN_P12 }}
|
||||
MACOS_SIGN_PASSWORD: ${{ secrets.MACOS_SIGN_PASSWORD }}
|
||||
MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }}
|
||||
MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }}
|
||||
MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }}
|
||||
|
||||
- name: Upload unsigned Windows zip for SignPath
|
||||
id: upload-windows-zip
|
||||
if: steps.release.outputs.new_release_published == 'true'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: unsigned-windows-zip
|
||||
path: dist/OliveTin-windows-amd64.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload unsigned Windows MSI for SignPath
|
||||
id: upload-windows-msi
|
||||
if: steps.release.outputs.new_release_published == 'true'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: unsigned-windows-msi
|
||||
path: dist/OliveTin-windows-amd64.msi
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Archive binaries
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
|
||||
|
|
@ -146,3 +175,77 @@ jobs:
|
|||
with:
|
||||
name: "OliveTin-snapshot-${{ env.DATE }}-${{ github.sha }}"
|
||||
path: dist/OliveTin*.*
|
||||
|
||||
sign-windows:
|
||||
name: Sign Windows artifacts (SignPath)
|
||||
needs: build
|
||||
if: needs.build.outputs.new_release_published == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Require SignPath configuration
|
||||
env:
|
||||
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
SIGNPATH_ORGANIZATION_ID: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
|
||||
SIGNPATH_PROJECT_SLUG: ${{ vars.SIGNPATH_PROJECT_SLUG }}
|
||||
SIGNPATH_SIGNING_POLICY_SLUG: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
|
||||
run: |
|
||||
missing=0
|
||||
for name in SIGNPATH_API_TOKEN SIGNPATH_ORGANIZATION_ID SIGNPATH_PROJECT_SLUG SIGNPATH_SIGNING_POLICY_SLUG; do
|
||||
if [[ -z "${!name}" ]]; then
|
||||
echo "Missing required SignPath setting: ${name}" >&2
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
if [[ "${missing}" -ne 0 ]]; then
|
||||
echo "Windows signing is required before a draft release can be published. Configure SignPath secrets/vars (see docs/modules/dev/pages/signing.adoc)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Sign Windows zip
|
||||
uses: signpath/github-action-submit-signing-request@v2
|
||||
with:
|
||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
|
||||
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
|
||||
signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
|
||||
artifact-configuration-slug: windows-zip
|
||||
github-artifact-id: ${{ needs.build.outputs.windows_zip_artifact_id }}
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: signed-windows-zip
|
||||
|
||||
- name: Sign Windows MSI
|
||||
uses: signpath/github-action-submit-signing-request@v2
|
||||
with:
|
||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
|
||||
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
|
||||
signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
|
||||
artifact-configuration-slug: windows-msi
|
||||
github-artifact-id: ${{ needs.build.outputs.windows_msi_artifact_id }}
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: signed-windows-msi
|
||||
|
||||
- name: Publish signed Windows assets and undraft release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.CONTAINER_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.CONTAINER_TOKEN }}
|
||||
run: |
|
||||
zip_path="$(find signed-windows-zip -type f -name 'OliveTin-windows-amd64.zip' | head -n 1)"
|
||||
msi_path="$(find signed-windows-msi -type f -name 'OliveTin-windows-amd64.msi' | head -n 1)"
|
||||
if [[ -z "${zip_path}" || -z "${msi_path}" ]]; then
|
||||
echo "Signed Windows artifacts not found after SignPath:" >&2
|
||||
find signed-windows-zip signed-windows-msi -type f >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
./var/windows/signpath-publish-signed.sh \
|
||||
"${{ needs.build.outputs.new_release_git_tag }}" \
|
||||
"${zip_path}" \
|
||||
"${msi_path}"
|
||||
|
|
|
|||
|
|
@ -182,7 +182,25 @@ nfpms:
|
|||
- src: var/manpage/OliveTin.1.gz
|
||||
dst: /usr/share/man/man1/OliveTin.1.gz
|
||||
|
||||
# Sign and notarize darwin binaries via quill (no macOS runner required).
|
||||
# Skipped when MACOS_SIGN_P12 is unset so local/snapshot builds stay unsigned.
|
||||
notarize:
|
||||
macos:
|
||||
- enabled: '{{ isEnvSet "MACOS_SIGN_P12" }}'
|
||||
# Defaults to project_name when omitted; must match builds[].id (also project_name).
|
||||
sign:
|
||||
certificate: "{{.Env.MACOS_SIGN_P12}}"
|
||||
password: "{{.Env.MACOS_SIGN_PASSWORD}}"
|
||||
notarize:
|
||||
issuer_id: "{{.Env.MACOS_NOTARY_ISSUER_ID}}"
|
||||
key_id: "{{.Env.MACOS_NOTARY_KEY_ID}}"
|
||||
key: "{{.Env.MACOS_NOTARY_KEY}}"
|
||||
wait: true
|
||||
timeout: 30m
|
||||
|
||||
release:
|
||||
# Stay draft until the sign-windows job replaces unsigned Windows assets and undrafts.
|
||||
draft: true
|
||||
extra_files:
|
||||
- glob: ./dist/OliveTin-windows-amd64.msi
|
||||
footer: |
|
||||
|
|
|
|||
|
|
@ -1,31 +1,67 @@
|
|||
# macOS release signing
|
||||
# Release signing
|
||||
|
||||
Release builds can sign and notarize the `darwin` binaries using [quill](https://github.com/anchore/quill) via GoReleaser. This runs on the existing Linux CI runner; no macOS runner is required.
|
||||
OliveTin signs release binaries on two platforms:
|
||||
|
||||
Signing is **optional**. If the GitHub secrets below are not all set, GoReleaser skips macOS signing and publishes unsigned binaries (the previous behaviour).
|
||||
* **macOS** — Developer ID + notarization via [quill](https://github.com/anchore/quill) inside GoReleaser (optional only when `MACOS_SIGN_P12` is unset).
|
||||
* **Windows** — Authenticode via [SignPath Foundation](https://signpath.org/) in a separate GitHub Actions job (required before a draft release is published).
|
||||
|
||||
## Prerequisites
|
||||
## macOS release signing
|
||||
|
||||
Release builds can sign and notarize the `darwin` binaries using [quill](https://github.com/anchore/quill) via GoReleaser. This runs on the existing Linux CI runner; no macOS runner or Xcode is required.
|
||||
|
||||
Signing is **optional** only when `MACOS_SIGN_P12` is unset — GoReleaser then skips macOS signing and publishes unsigned binaries. When `MACOS_SIGN_P12` is set, the companion macOS secrets below are required or the release fails.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- An active [Apple Developer Program](https://developer.apple.com/programs/) membership.
|
||||
- A **Developer ID Application** certificate (not "Apple Development" or "Mac App Distribution").
|
||||
- An [App Store Connect API key](https://appstoreconnect.apple.com/access/integrations/api) with at least **Developer** access.
|
||||
|
||||
## One-time setup
|
||||
### One-time setup
|
||||
|
||||
### 1. Create the signing certificate
|
||||
#### 1. Create the signing certificate (OpenSSL, no Mac/Xcode)
|
||||
|
||||
Work in a private directory. Keep the private key offline and never commit it.
|
||||
|
||||
```sh
|
||||
mkdir -p ~/apple-signing && cd ~/apple-signing
|
||||
chmod 700 .
|
||||
|
||||
openssl genrsa -out developer_id_app.key 2048
|
||||
openssl req -new -key developer_id_app.key -out developer_id_app.csr \
|
||||
-subj "/emailAddress=you@example.com/CN=Your Name/C=GB"
|
||||
```
|
||||
|
||||
1. Open [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/certificates/list).
|
||||
2. Create a certificate of type **Developer ID Application**.
|
||||
3. Download the `.cer` file and double-click it to add it to **Keychain Access** on a Mac.
|
||||
4. In Keychain Access, export the certificate as a **Personal Information Exchange (`.p12`)** file. You will set an export password — remember it; this becomes `MACOS_SIGN_PASSWORD`.
|
||||
2. Create a certificate of type **Developer ID Application**. Prefer **G2 Sub-CA** if the portal asks.
|
||||
3. Upload `developer_id_app.csr` and download the resulting `.cer` (often named `developerID_application.cer`).
|
||||
|
||||
### 2. Create the notarization API key
|
||||
Build a `.p12` that includes Apple's Developer ID G2 intermediate:
|
||||
|
||||
```sh
|
||||
curl -fsSLO https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer
|
||||
|
||||
openssl x509 -inform DER -in developerID_application.cer -out developerID_application.pem
|
||||
openssl x509 -inform DER -in DeveloperIDG2CA.cer -out DeveloperIDG2CA.pem
|
||||
|
||||
# Export password becomes MACOS_SIGN_PASSWORD.
|
||||
# On OpenSSL 3 (e.g. Fedora), -legacy improves compatibility with some tooling:
|
||||
openssl pkcs12 -export -legacy \
|
||||
-inkey developer_id_app.key \
|
||||
-in developerID_application.pem \
|
||||
-certfile DeveloperIDG2CA.pem \
|
||||
-out Certificates.p12
|
||||
```
|
||||
|
||||
If you already have a Mac with the certificate in Keychain Access, you can export a `.p12` from there instead; the OpenSSL path above is enough when you do not.
|
||||
|
||||
#### 2. Create the notarization API key
|
||||
|
||||
1. Open [App Store Connect → Users and Access → Integrations → App Store Connect API](https://appstoreconnect.apple.com/access/integrations/api).
|
||||
2. Create a key with **Developer** role (or Admin).
|
||||
3. Download the `.p8` file once (it cannot be downloaded again). Note the **Key ID** shown in the portal and the **Issuer ID** at the top of the API keys page.
|
||||
|
||||
### 3. Base64-encode the key files
|
||||
#### 3. Base64-encode the key files
|
||||
|
||||
Run on a machine that has the files (Linux or macOS):
|
||||
|
||||
|
|
@ -36,7 +72,7 @@ base64 -w0 < ./AuthKey_XXXXXX.p8 # MACOS_NOTARY_KEY
|
|||
|
||||
On macOS without GNU coreutils, use `base64 -i file | tr -d '\n'`.
|
||||
|
||||
### 4. Add GitHub repository secrets
|
||||
#### 4. Add GitHub repository secrets
|
||||
|
||||
In **Settings → Secrets and variables → Actions**, create:
|
||||
|
||||
|
|
@ -48,9 +84,9 @@ In **Settings → Secrets and variables → Actions**, create:
|
|||
| `MACOS_NOTARY_KEY_ID` | Key ID from App Store Connect (e.g. `ABC123DEF4`) |
|
||||
| `MACOS_NOTARY_ISSUER_ID` | Issuer UUID from App Store Connect |
|
||||
|
||||
All five must be present for signing to run. Any missing secret disables signing for that release.
|
||||
All five must be present for signing to run. GoReleaser enables the step when `MACOS_SIGN_P12` is set; missing companion secrets will fail that release.
|
||||
|
||||
## Renewal
|
||||
### Renewal
|
||||
|
||||
| Item | Typical lifetime | What to do |
|
||||
|------|------------------|------------|
|
||||
|
|
@ -60,7 +96,7 @@ All five must be present for signing to run. Any missing secret disables signing
|
|||
|
||||
After updating secrets, the next release on `main` (via semantic-release) will use the new credentials automatically.
|
||||
|
||||
## Verifying a signed release
|
||||
### Verifying a signed release
|
||||
|
||||
On a Mac, download a `OliveTin-darwin-*.tar.gz` release artifact and run:
|
||||
|
||||
|
|
@ -71,8 +107,90 @@ spctl -a -vv -t execute OliveTin-darwin-arm64/OliveTin
|
|||
|
||||
A signed and notarized binary should report `accepted` with `source=Notarized Developer ID`.
|
||||
|
||||
## Configuration reference
|
||||
### Configuration reference
|
||||
|
||||
- GoReleaser: `notarize.macos` in [`.goreleaser.yml`](.goreleaser.yml)
|
||||
- CI secrets: [`.github/workflows/build-and-release.yml`](.github/workflows/build-and-release.yml) (`release` step)
|
||||
- [GoReleaser notarization docs](https://goreleaser.com/customization/notarize/)
|
||||
- GoReleaser: `notarize.macos` in link:https://github.com/OliveTin/OliveTin/blob/main/.goreleaser.yml[`.goreleaser.yml`]
|
||||
- CI secrets: link:https://github.com/OliveTin/OliveTin/blob/main/.github/workflows/build-and-release.yml[`.github/workflows/build-and-release.yml`] (`release` step)
|
||||
- link:https://goreleaser.com/customization/notarize/[GoReleaser notarization docs]
|
||||
|
||||
## Windows release signing (SignPath)
|
||||
|
||||
Windows Authenticode signing uses [SignPath Foundation](https://signpath.org/) (free for qualifying open-source projects). It does **not** use GoReleaser Pro.
|
||||
|
||||
GoReleaser creates a **draft** GitHub release with unsigned Windows assets. A separate `sign-windows` job submits those assets to SignPath, replaces them on the draft (including updated `checksums.txt`), then publishes the release.
|
||||
|
||||
Signed artifacts:
|
||||
|
||||
* `OliveTin.exe` inside `OliveTin-windows-amd64.zip`
|
||||
* nested `OliveTin.exe` and the `OliveTin-windows-amd64.msi` installer (deep signing)
|
||||
|
||||
Signing is **required** to publish. If SignPath secrets/vars are missing, `sign-windows` fails and the draft stays unpublished.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Approval for the [SignPath Foundation open-source program](https://signpath.io/product/open-source).
|
||||
- The SignPath GitHub App installed on the OliveTin organization/repository.
|
||||
- A SignPath project linked to this repository, with a release signing policy.
|
||||
|
||||
### One-time setup
|
||||
|
||||
#### 1. Apply for SignPath Foundation
|
||||
|
||||
1. Open https://signpath.io/product/open-source and apply with the OliveTin GitHub repository URL.
|
||||
2. After approval, create (or confirm) the organization and project in the SignPath portal.
|
||||
|
||||
#### 2. Install the SignPath GitHub App
|
||||
|
||||
1. Install the SignPath GitHub App and grant access to the OliveTin repository.
|
||||
2. Link the Trusted Build System **GitHub.com** to the SignPath project (required so SignPath can verify the workflow artifact origin).
|
||||
|
||||
#### 3. Create artifact configurations
|
||||
|
||||
In the SignPath project, create two artifact configurations with these slugs (must match CI). Paste the XML from the reference copies in this repo (SignPath does **not** load them automatically):
|
||||
|
||||
* slug `windows-zip` ← link:https://github.com/OliveTin/OliveTin/blob/main/docs/modules/dev/signpath/windows-zip.xml[`signpath/windows-zip.xml`]
|
||||
* slug `windows-msi` ← link:https://github.com/OliveTin/OliveTin/blob/main/docs/modules/dev/signpath/windows-msi.xml[`signpath/windows-msi.xml`]
|
||||
|
||||
Use **Custom** XML in the SignPath UI and paste the file contents. Do **not** use **Upload an artifact sample** on these `.xml` files — SignPath will treat them as XML documents to sign (`xml-file`), which is unavailable on the Foundation/Open Source plan. See link:https://github.com/OliveTin/OliveTin/blob/main/docs/modules/dev/signpath/README.md[`signpath/README.md`].
|
||||
|
||||
#### 4. Add GitHub secrets and variables
|
||||
|
||||
In **Settings → Secrets and variables → Actions**:
|
||||
|
||||
| Kind | Name | Value |
|
||||
|------|------|-------|
|
||||
| Secret | `SIGNPATH_API_TOKEN` | CI submitter API token from SignPath |
|
||||
| Variable | `SIGNPATH_ORGANIZATION_ID` | SignPath organization ID |
|
||||
| Variable | `SIGNPATH_PROJECT_SLUG` | SignPath project slug (e.g. `olivetin`) |
|
||||
| Variable | `SIGNPATH_SIGNING_POLICY_SLUG` | Signing policy slug (e.g. `release-signing`) |
|
||||
|
||||
#### 5. Pipeline behaviour
|
||||
|
||||
On a new semantic-release from `main`:
|
||||
|
||||
1. GoReleaser publishes container images and creates a **draft** GitHub release (including unsigned Windows zip/MSI).
|
||||
2. The build job uploads those Windows files as GitHub Actions artifacts.
|
||||
3. The `sign-windows` job submits each artifact to SignPath, waits for completion, then runs `var/windows/signpath-publish-signed.sh` to clobber-upload signed files, refresh `checksums.txt`, and undraft the release.
|
||||
|
||||
All jobs in this chain use GitHub-hosted runners (required by SignPath for OSS projects).
|
||||
|
||||
### Verifying a signed release
|
||||
|
||||
On Windows, download `OliveTin-windows-amd64.msi` or extract `OliveTin.exe` from the zip, then either:
|
||||
|
||||
* Right-click → **Properties** → **Digital Signatures**, or
|
||||
* Run:
|
||||
|
||||
```bat
|
||||
signtool verify /pa OliveTin.exe
|
||||
signtool verify /pa OliveTin-windows-amd64.msi
|
||||
```
|
||||
|
||||
### Configuration reference
|
||||
|
||||
- Draft release: `release.draft: true` in link:https://github.com/OliveTin/OliveTin/blob/main/.goreleaser.yml[`.goreleaser.yml`]
|
||||
- CI job: `sign-windows` in link:https://github.com/OliveTin/OliveTin/blob/main/.github/workflows/build-and-release.yml[`.github/workflows/build-and-release.yml`]
|
||||
- Publish helper: link:https://github.com/OliveTin/OliveTin/blob/main/var/windows/signpath-publish-signed.sh[`var/windows/signpath-publish-signed.sh`]
|
||||
- SignPath artifact configs (reference only): link:https://github.com/OliveTin/OliveTin/blob/main/docs/modules/dev/signpath/windows-zip.xml[`signpath/windows-zip.xml`], link:https://github.com/OliveTin/OliveTin/blob/main/docs/modules/dev/signpath/windows-msi.xml[`signpath/windows-msi.xml`]
|
||||
- link:https://docs.signpath.io/trusted-build-systems/github[SignPath GitHub Actions docs]
|
||||
- link:https://docs.signpath.io/artifact-configuration/examples[SignPath artifact configuration examples]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# SignPath artifact configurations (reference only)
|
||||
|
||||
These XML files are **paste templates** for the SignPath web UI. SignPath does not load them from this repository.
|
||||
|
||||
## Do not upload these `.xml` files as artifact samples
|
||||
|
||||
If you use **Upload an artifact sample** on `windows-zip.xml` / `windows-msi.xml`, SignPath treats them as XML documents to sign and generates an `<xml-file>` configuration. That feature is not available on SignPath Foundation / Open Source, and you get an error like:
|
||||
|
||||
> feature which is not currently available (XML element name: 'xml-file')
|
||||
|
||||
## Correct setup
|
||||
|
||||
1. In the SignPath project, **Add** an artifact configuration.
|
||||
2. Choose **Custom** (edit XML), not “upload sample” of these files.
|
||||
3. Paste the full contents of `windows-zip.xml` or `windows-msi.xml`.
|
||||
4. Set the slug to `windows-zip` or `windows-msi` (must match CI).
|
||||
|
||||
Optional: use **Upload an artifact sample** with a real `OliveTin-windows-amd64.zip` or `.msi` from a build, then trim the generated config to match these references.
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<artifact-configuration xmlns="http://signpath.io/artifact-configuration/v1">
|
||||
<msi-file path="OliveTin-windows-amd64.msi">
|
||||
<pe-file path="OliveTin.exe">
|
||||
<authenticode-sign/>
|
||||
</pe-file>
|
||||
<authenticode-sign/>
|
||||
</msi-file>
|
||||
</artifact-configuration>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<artifact-configuration xmlns="http://signpath.io/artifact-configuration/v1">
|
||||
<zip-file path="OliveTin-windows-amd64.zip">
|
||||
<pe-file path="OliveTin-windows-amd64/OliveTin.exe">
|
||||
<authenticode-sign/>
|
||||
</pe-file>
|
||||
</zip-file>
|
||||
</artifact-configuration>
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
DIST_DIR="${DIST_DIR:-${REPO_ROOT}/dist}"
|
||||
ARCH="${ARCH:-amd64}"
|
||||
ZIP_NAME="OliveTin-windows-${ARCH}.zip"
|
||||
MSI_NAME="OliveTin-windows-${ARCH}.msi"
|
||||
CHECKSUMS_NAME="checksums.txt"
|
||||
|
||||
usage() {
|
||||
echo "Usage: $(basename "$0") <release-tag> <signed-zip-path> <signed-msi-path>" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
TAG="${1:-}"
|
||||
SIGNED_ZIP="${2:-}"
|
||||
SIGNED_MSI="${3:-}"
|
||||
|
||||
if [[ -z "${TAG}" || -z "${SIGNED_ZIP}" || -z "${SIGNED_MSI}" ]]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ ! -f "${SIGNED_ZIP}" ]]; then
|
||||
echo "Signed zip not found: ${SIGNED_ZIP}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "${SIGNED_MSI}" ]]; then
|
||||
echo "Signed MSI not found: ${SIGNED_MSI}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v gh >/dev/null; then
|
||||
echo "gh is required to update the GitHub release" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${DIST_DIR}"
|
||||
cp -f "${SIGNED_ZIP}" "${DIST_DIR}/${ZIP_NAME}"
|
||||
cp -f "${SIGNED_MSI}" "${DIST_DIR}/${MSI_NAME}"
|
||||
|
||||
checksums_path="${DIST_DIR}/${CHECKSUMS_NAME}"
|
||||
checksums_backup="${DIST_DIR}/${CHECKSUMS_NAME}.orig"
|
||||
if ! gh release download "${TAG}" --pattern "${CHECKSUMS_NAME}" --dir "${DIST_DIR}" --clobber; then
|
||||
echo "Failed to download ${CHECKSUMS_NAME} from release ${TAG}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "${checksums_path}" ]]; then
|
||||
echo "${CHECKSUMS_NAME} not found after download from release ${TAG}" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp -f "${checksums_path}" "${checksums_backup}"
|
||||
|
||||
update_checksum() {
|
||||
local file_name="${1}"
|
||||
local new_checksum
|
||||
new_checksum="$(cd "${DIST_DIR}" && sha256sum "${file_name}")"
|
||||
|
||||
if [[ -f "${checksums_path}" ]] && grep -qF " ${file_name}" "${checksums_path}"; then
|
||||
local tmp
|
||||
tmp="$(mktemp)"
|
||||
grep -vF " ${file_name}" "${checksums_path}" > "${tmp}" || true
|
||||
printf '%s\n' "${new_checksum}" >> "${tmp}"
|
||||
mv "${tmp}" "${checksums_path}"
|
||||
else
|
||||
printf '%s\n' "${new_checksum}" >> "${checksums_path}"
|
||||
fi
|
||||
}
|
||||
|
||||
update_checksum "${ZIP_NAME}"
|
||||
update_checksum "${MSI_NAME}"
|
||||
|
||||
# Replace binaries first so a failed checksums upload leaves the draft recoverable.
|
||||
gh release upload "${TAG}" \
|
||||
"${DIST_DIR}/${ZIP_NAME}" \
|
||||
"${DIST_DIR}/${MSI_NAME}" \
|
||||
--clobber
|
||||
|
||||
if ! gh release upload "${TAG}" "${checksums_path}" --clobber; then
|
||||
echo "Failed to upload updated ${CHECKSUMS_NAME}; restoring previous asset" >&2
|
||||
restore_dir="$(mktemp -d)"
|
||||
cp -f "${checksums_backup}" "${restore_dir}/${CHECKSUMS_NAME}"
|
||||
gh release upload "${TAG}" "${restore_dir}/${CHECKSUMS_NAME}" --clobber
|
||||
rm -rf "${restore_dir}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gh release edit "${TAG}" --draft=false
|
||||
|
||||
echo "Published signed ${ZIP_NAME} and ${MSI_NAME} on release ${TAG}"
|
||||
Loading…
Reference in New Issue