Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/scripts/changelog-section.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
Prints the section of CHANGELOG.md for one version, without its heading.
Fails if there is no section, or if the section is empty.

node .github/scripts/changelog-section.mjs 0.1.0
*/
import { readFileSync } from 'node:fs';

const version = (process.argv[2] ?? '').replace(/^v/, '');
const lines = readFileSync(new URL('../../CHANGELOG.md', import.meta.url), 'utf8').split('\n');
const start = lines.findIndex((l) => l.trim() === `## ${version}`);
if (!version || start < 0) {
console.error(`CHANGELOG.md has no "## ${version}" section`);
process.exit(1);
}
const end = lines.findIndex((l, i) => i > start && l.startsWith('## '));
const body = lines.slice(start + 1, end < 0 ? undefined : end).join('\n').trim();
if (!body) {
console.error(`the "## ${version}" section of CHANGELOG.md is empty`);
process.exit(1);
}
console.log(body);
25 changes: 25 additions & 0 deletions .github/scripts/collect-bundles.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# collect-bundles.sh <bundle dir> <out dir> <version> <platform>
#
# Copies the installers that `tauri build` wrote into <out dir>, under names
# without spaces: OpenDocument-Reader_<version>_<platform>.<ext>.
set -euo pipefail
bundle=$1 out=$2 version=$3 platform=$4
mkdir -p "$out"
found=0
while IFS= read -r -d '' file; do
name=$(basename "$file")
case "$name" in
*-setup.exe) ext=setup.exe ;;
*.AppImage) ext=AppImage ;;
*) ext=${name##*.} ;;
esac
cp "$file" "$out/OpenDocument-Reader_${version}_${platform}.${ext}"
found=$((found + 1))
done < <(find "$bundle" -maxdepth 2 -type f \
\( -name '*.dmg' -o -name '*.msi' -o -name '*-setup.exe' -o -name '*.AppImage' -o -name '*.deb' -o -name '*.rpm' \) -print0)
if [ "$found" -eq 0 ]; then
echo "::error::no installers under $bundle"
exit 1
fi
ls -la "$out"
189 changes: 189 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
name: release
# Builds the installers for every system, runs the smoke test on each build, and
# writes a draft GitHub release with the installers and the changelog section.
# Publishing the draft writes the v<version> tag; this workflow writes no tag.
#
# The version must already be in package.json and have a section in
# CHANGELOG.md, so the repository records what went out.
#
# Signing: with the APPLE_* secrets, the macOS build is signed with Developer
# ID and notarized. Without them, it is signed ad hoc. Windows builds are not
# signed yet.
on:
workflow_dispatch:
inputs:
version:
description: version to release, e.g. 0.1.0
type: string
required: true
dry_run:
description: build only, write no release
type: boolean
default: false

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

permissions:
contents: read

jobs:
check:
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v5

- name: Check the version
id: version
# Through the environment, because the input is outside text.
env:
given: ${{ inputs.version }}
run: |
version=${given#v}
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$ ]]; then
echo "::error::'$given' is not a version such as 0.1.0"
exit 1
fi
in_package=$(node -p "require('./package.json').version")
if [ "$in_package" != "$version" ]; then
echo "::error::package.json says $in_package, not $version"
exit 1
fi
if git ls-remote --exit-code --tags origin "v$version" > /dev/null 2>&1; then
echo "::error::the tag v$version exists already"
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"

- name: Check the changelog
run: node .github/scripts/changelog-section.mjs "${{ steps.version.outputs.version }}"

build:
needs: check
strategy:
fail-fast: true
matrix:
include:
- os: macos-latest
platform: macos-universal
args: --target universal-apple-darwin
targets: aarch64-apple-darwin,x86_64-apple-darwin
bundle: src-tauri/target/universal-apple-darwin/release/bundle
app: src-tauri/target/universal-apple-darwin/release/opendocument-desktop
- os: windows-latest
platform: windows-x64
bundle: src-tauri/target/release/bundle
app: src-tauri/target/release/opendocument-desktop.exe
# 22.04, so the packages run on systems with an older glibc too.
- os: ubuntu-22.04
platform: linux-x64
bundle: src-tauri/target/release/bundle
app: src-tauri/target/release/opendocument-desktop
- os: ubuntu-22.04-arm
platform: linux-arm64
bundle: src-tauri/target/release/bundle
app: src-tauri/target/release/opendocument-desktop
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5

- name: Install the Linux dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libxdo-dev xvfb

- uses: actions/setup-node@v5
with:
node-version-file: .nvmrc
cache: npm

- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.targets }}

- name: Use the Apple signing secrets
if: runner.os == 'macOS'
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY_CONTENT }}
run: |
# Only set what exists: Tauri reads an empty variable as a value.
if [ -n "$APPLE_CERTIFICATE" ] && [ -n "$APPLE_SIGNING_IDENTITY" ]; then
{
echo "APPLE_CERTIFICATE=$APPLE_CERTIFICATE"
echo "APPLE_CERTIFICATE_PASSWORD=$APPLE_CERTIFICATE_PASSWORD"
echo "APPLE_SIGNING_IDENTITY=$APPLE_SIGNING_IDENTITY"
} >> "$GITHUB_ENV"
echo "Signing with Developer ID."
else
echo "::warning::no Developer ID secrets, so the macOS build is signed ad hoc"
fi
if [ -n "$APPLE_API_KEY_CONTENT" ] && [ -n "$APPLE_API_KEY" ] && [ -n "$APPLE_API_ISSUER" ]; then
key="$RUNNER_TEMP/AuthKey_$APPLE_API_KEY.p8"
printf '%s' "$APPLE_API_KEY_CONTENT" > "$key"
{
echo "APPLE_API_ISSUER=$APPLE_API_ISSUER"
echo "APPLE_API_KEY=$APPLE_API_KEY"
echo "APPLE_API_KEY_PATH=$key"
} >> "$GITHUB_ENV"
echo "Notarizing."
fi

- run: npm ci
- run: npm run check
- run: npx tauri build ${{ matrix.args }}

- name: Smoke test
shell: bash
run: |
if [ "$RUNNER_OS" = Linux ]; then
xvfb-run -a node test/smoke.mjs "${{ matrix.app }}"
else
node test/smoke.mjs "${{ matrix.app }}"
fi

- name: Collect the installers
shell: bash
run: .github/scripts/collect-bundles.sh "${{ matrix.bundle }}" release "${{ needs.check.outputs.version }}" "${{ matrix.platform }}"

- uses: actions/upload-artifact@v5
with:
name: release-${{ matrix.platform }}
path: release/
if-no-files-found: error

release:
needs: [check, build]
if: ${{ !inputs.dry_run }}
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/checkout@v5

- uses: actions/download-artifact@v5
with:
pattern: release-*
path: release
merge-multiple: true

- name: Write the draft release
env:
GH_TOKEN: ${{ github.token }}
version: ${{ needs.check.outputs.version }}
run: |
node .github/scripts/changelog-section.mjs "$version" > notes.md
(cd release && sha256sum * > SHA256SUMS.txt)
gh release create "v$version" release/* \
--draft \
--target "$GITHUB_SHA" \
--title "$version" \
--notes-file notes.md
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Before a release, a pull request changes that heading to the version.

## Unreleased

## 0.1.0

- The first version of OpenDocument Reader for Windows, macOS and Linux. It opens
OpenDocument, Word, Excel, PowerPoint, PDF, Pages, Numbers, Keynote and more,
also with a password.
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ npm run tauri build # writes the installers to src-tauri/target/release/bundle
`node test/smoke.mjs src-tauri/target/release/opendocument-desktop` opens each
document in `test/documents/` in the app and saves a copy.

## Release

1. In a pull request, set the version in `package.json` (`npm version --no-git-tag-version 0.2.0`),
and change the `Unreleased` heading of `CHANGELOG.md` to that version.
2. Run the `release` workflow with that version. `dry_run` builds only.
3. The workflow writes a draft release with the installers for macOS (universal),
Windows (x64) and Linux (x64 and arm64). Publishing the draft writes the tag.

The macOS build is signed with Developer ID and notarized if the repository has
the secrets `APPLE_CERTIFICATE`, `APPLE_CERTIFICATE_PASSWORD`,
`APPLE_SIGNING_IDENTITY`, `APPLE_API_ISSUER`, `APPLE_API_KEY` and
`APPLE_API_KEY_CONTENT`. Without them, it is signed ad hoc, and macOS asks the
user to confirm the first start. The Windows build is not signed.

## Layout

| Path | What |
Expand Down
6 changes: 5 additions & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@
"mimeType": "application/vnd.apple.keynote",
"role": "Viewer"
}
]
],
"macOS": {
"signingIdentity": "-",
"minimumSystemVersion": "11.0"
}
}
}
Loading