fix: Windows artifact signing
This commit is contained in:
parent
e2bf82ff9c
commit
e322887c4a
|
|
@ -37,6 +37,11 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
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:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|
@ -129,6 +134,7 @@ jobs:
|
||||||
uses: docker/setup-buildx-action@v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: release
|
- name: release
|
||||||
|
id: release
|
||||||
if: github.ref_type != 'tag' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
if: github.ref_type != 'tag' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
||||||
uses: cycjimmy/semantic-release-action@v5
|
uses: cycjimmy/semantic-release-action@v5
|
||||||
with:
|
with:
|
||||||
|
|
@ -140,9 +146,99 @@ jobs:
|
||||||
GITHUB_TOKEN: ${{ secrets.CONTAINER_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.CONTAINER_TOKEN }}
|
||||||
GH_TOKEN: ${{ secrets.CONTAINER_TOKEN }}
|
GH_TOKEN: ${{ secrets.CONTAINER_TOKEN }}
|
||||||
|
|
||||||
|
- 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
|
- name: Archive binaries
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: "OliveTin-snapshot-${{ env.DATE }}-${{ github.sha }}"
|
name: "OliveTin-snapshot-${{ env.DATE }}-${{ github.sha }}"
|
||||||
path: dist/OliveTin*.*
|
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
|
||||||
|
|
||||||
|
- 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}"
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,8 @@ nfpms:
|
||||||
dst: /usr/share/man/man1/OliveTin.1.gz
|
dst: /usr/share/man/man1/OliveTin.1.gz
|
||||||
|
|
||||||
release:
|
release:
|
||||||
|
# Stay draft until the sign-windows job replaces unsigned Windows assets and undrafts.
|
||||||
|
draft: true
|
||||||
extra_files:
|
extra_files:
|
||||||
- glob: ./dist/OliveTin-windows-amd64.msi
|
- glob: ./dist/OliveTin-windows-amd64.msi
|
||||||
footer: |
|
footer: |
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,38 @@
|
||||||
# macOS release signing
|
# Release signing
|
||||||
|
|
||||||
|
OliveTin signs release binaries on two platforms:
|
||||||
|
|
||||||
|
* **macOS** — Developer ID + notarization via [quill](https://github.com/anchore/quill) inside GoReleaser (optional if secrets are missing).
|
||||||
|
* **Windows** — Authenticode via [SignPath Foundation](https://signpath.org/) in a separate GitHub Actions job (required before a draft release is published).
|
||||||
|
|
||||||
|
## 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 is required.
|
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.
|
||||||
|
|
||||||
Signing is **optional**. If the GitHub secrets below are not all set, GoReleaser skips macOS signing and publishes unsigned binaries (the previous behaviour).
|
Signing is **optional**. If the GitHub secrets below are not all set, GoReleaser skips macOS signing and publishes unsigned binaries (the previous behaviour).
|
||||||
|
|
||||||
## Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- An active [Apple Developer Program](https://developer.apple.com/programs/) membership.
|
- An active [Apple Developer Program](https://developer.apple.com/programs/) membership.
|
||||||
- A **Developer ID Application** certificate (not "Apple Development" or "Mac App Distribution").
|
- 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.
|
- 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
|
||||||
|
|
||||||
1. Open [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/certificates/list).
|
1. Open [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/certificates/list).
|
||||||
2. Create a certificate of type **Developer ID Application**.
|
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.
|
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`.
|
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 the notarization API key
|
#### 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).
|
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).
|
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. 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):
|
Run on a machine that has the files (Linux or macOS):
|
||||||
|
|
||||||
|
|
@ -36,7 +43,7 @@ base64 -w0 < ./AuthKey_XXXXXX.p8 # MACOS_NOTARY_KEY
|
||||||
|
|
||||||
On macOS without GNU coreutils, use `base64 -i file | tr -d '\n'`.
|
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:
|
In **Settings → Secrets and variables → Actions**, create:
|
||||||
|
|
||||||
|
|
@ -50,7 +57,7 @@ In **Settings → Secrets and variables → Actions**, create:
|
||||||
|
|
||||||
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. Any missing secret disables signing for that release.
|
||||||
|
|
||||||
## Renewal
|
### Renewal
|
||||||
|
|
||||||
| Item | Typical lifetime | What to do |
|
| Item | Typical lifetime | What to do |
|
||||||
|------|------------------|------------|
|
|------|------------------|------------|
|
||||||
|
|
@ -60,7 +67,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.
|
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:
|
On a Mac, download a `OliveTin-darwin-*.tar.gz` release artifact and run:
|
||||||
|
|
||||||
|
|
@ -71,8 +78,90 @@ spctl -a -vv -t execute OliveTin-darwin-arm64/OliveTin
|
||||||
|
|
||||||
A signed and notarized binary should report `accepted` with `source=Notarized Developer ID`.
|
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)
|
- GoReleaser: `notarize.macos` in [`.goreleaser.yml`](.goreleaser.yml)
|
||||||
- CI secrets: [`.github/workflows/build-and-release.yml`](.github/workflows/build-and-release.yml) (`release` step)
|
- 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 notarization docs](https://goreleaser.com/customization/notarize/)
|
||||||
|
|
||||||
|
## 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` ← [`signpath/windows-zip.xml`](../signpath/windows-zip.xml)
|
||||||
|
* slug `windows-msi` ← [`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 [`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 [`.goreleaser.yml`](.goreleaser.yml)
|
||||||
|
- CI job: `sign-windows` in [`.github/workflows/build-and-release.yml`](.github/workflows/build-and-release.yml)
|
||||||
|
- Publish helper: [`var/windows/signpath-publish-signed.sh`](var/windows/signpath-publish-signed.sh)
|
||||||
|
- SignPath artifact configs (reference only): [`signpath/windows-zip.xml`](../signpath/windows-zip.xml), [`signpath/windows-msi.xml`](../signpath/windows-msi.xml)
|
||||||
|
- [SignPath GitHub Actions docs](https://docs.signpath.io/trusted-build-systems/github)
|
||||||
|
- [SignPath artifact configuration examples](https://docs.signpath.io/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,81 @@
|
||||||
|
#!/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}"
|
||||||
|
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
|
||||||
|
|
||||||
|
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}"
|
||||||
|
|
||||||
|
gh release upload "${TAG}" \
|
||||||
|
"${DIST_DIR}/${ZIP_NAME}" \
|
||||||
|
"${DIST_DIR}/${MSI_NAME}" \
|
||||||
|
"${checksums_path}" \
|
||||||
|
--clobber
|
||||||
|
|
||||||
|
gh release edit "${TAG}" --draft=false
|
||||||
|
|
||||||
|
echo "Published signed ${ZIP_NAME} and ${MSI_NAME} on release ${TAG}"
|
||||||
Loading…
Reference in New Issue