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
192 changes: 163 additions & 29 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@ on:
push:
tags:
- 'v*'
# A manual run packages package.json's version from any branch and keeps the files as run artifacts: it neither waits
# for Check nor publishes.
workflow_dispatch:

permissions: {}

env:
NFPM_VERSION: 2.47.0
NFPM_SHA256: 0660ca602b2d2d2ae4781a06c692b3eeb9d437ffea05b831d76e41f4a3188783
ALPINE_IMAGE: alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
# The OpenWrt packages are PKGARCH all, so any target's SDK builds them.
OPENWRT_SDK_IMAGE: openwrt/sdk:x86-64-25.12.5@sha256:e916b7cc555890bdc4725c926e139b888294f0c8f56561b88d8da3a6348616a2

jobs:
# A tag points at a commit that main's Check already ran the browser suite on, so the release waits for that run
# instead of repeating the suite serially.
verified:
if: github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 40
permissions:
Expand All @@ -36,6 +43,7 @@ jobs:

build:
needs: [verified]
if: ${{ !cancelled() && (needs.verified.result == 'success' || github.event_name == 'workflow_dispatch') }}
runs-on: ubuntu-latest
permissions:
contents: read
Expand All @@ -58,9 +66,11 @@ jobs:
- name: Check the tag against package.json
id: version
run: |
eval "$(tools/version.sh "${GITHUB_REF_NAME}")"
tag=${GITHUB_REF_NAME}
[ "$GITHUB_EVENT_NAME" = push ] || tag=v$(node -p 'require("./package.json").version')
eval "$(tools/version.sh "$tag")"
declared=$(node -p 'require("./package.json").version')
[ "$NPM" = "$declared" ] || { echo "tag ${GITHUB_REF_NAME} is version $NPM, package.json says $declared" >&2; exit 1; }
[ "$NPM" = "$declared" ] || { echo "tag $tag is version $NPM, package.json says $declared" >&2; exit 1; }
{
echo "VERSION=$VERSION"
echo "PRERELEASE=$PRERELEASE"
Expand All @@ -72,7 +82,50 @@ jobs:
- run: pnpm check
- run: pnpm build
- run: pnpm check:size
- run: tools/package.sh --git-version
- name: Install asset compressors
run: sudo apt-get update && sudo apt-get install -y brotli gzip
- name: Package the archives
run: |
if [ "$GITHUB_EVENT_NAME" = push ]; then tools/package.sh --git-version; else tools/package.sh; fi
# Temporary: honk has no release with the native API yet, so every doona release carries the honk debug builds of the
# tag pinned in tools/honk-pin.txt, the source archive of their commit and a note naming it. fetch-honk.mjs reads that
# tag's own release and fails unless its body, target and source tag name the pinned tag and commit. Remove this step,
# that file and the three honk lines in release's `files` once honk ships one.
- name: Bundle the honk-core debug builds
env:
GH_TOKEN: ${{ github.token }}
run: |
node tools/fetch-honk.mjs release
# The sums job copies every intermediate artifact into release-assets, so they keep one day; release-assets
# and the recipes keep the repository's default retention.
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: archives
path: |
release/
!release/SHA256SUMS
if-no-files-found: error
retention-days: 1

# nfpm builds the deb, rpm, ipk and Arch packages in seconds, so one job runs the four formats in turn.
nfpm:
needs: [build]
if: ${{ !cancelled() && needs.build.result == 'success' }}
runs-on: ubuntu-latest
permissions:
contents: read
env:
VERSION: ${{ needs.build.outputs.VERSION }}
PRERELEASE: ${{ needs.build.outputs.PRERELEASE }}
ARCH_PRERELEASE: ${{ needs.build.outputs.ARCH_PRERELEASE }}
NPM: ${{ needs.build.outputs.NPM }}
PACKAGE_RELEASE: ${{ needs.build.outputs.PACKAGE_RELEASE }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: archives
path: release/
- name: Install nfpm
run: |
curl -fsSL --retry 3 -o "$RUNNER_TEMP/nfpm.tar.gz" \
Expand All @@ -88,9 +141,11 @@ jobs:
export SOURCE_DATE_EPOCH
program=build/stage/doona/usr/share/doona
fonts=build/stage/doona-fonts/usr/share/doona
mkdir -p "$program" "$fonts"
precompressed=build/stage/doona-precompressed/usr/share/doona
mkdir -p "$program" "$fonts" "$precompressed" packages
tar -xzf "release/doona-${NPM}.tar.gz" -C "$program"
tar -xzf "release/doona-fonts-${NPM}.tar.gz" -C "$fonts"
tar -xzf "release/doona-precompressed-${NPM}.tar.gz" -C "$precompressed"
doc=build/stage/doona/usr/share/doc/doona
mkdir -p "$doc" build/stage/doona-fonts/usr/share/doc/doona
mv "$program"/LICENSE "$program"/LICENSES "$program"/NOTICE "$program"/THIRD-PARTY-NOTICES.txt \
Expand All @@ -104,55 +159,130 @@ jobs:
# Debian's archive has an unrelated doona, so only the deb carries the -web suffix (see doona.yaml).
for packager in deb rpm ipk archlinux; do
if [ "$packager" = deb ]; then export PACKAGE_SUFFIX=-web; else export PACKAGE_SUFFIX=; fi
for config in install/nfpm/doona.yaml install/nfpm/doona-fonts.yaml; do
# doona-precompressed depends on exactly this doona version, spelled the way the format spells it.
if [ "$packager" = archlinux ]; then
export DOONA_VERSION="${VERSION}${ARCH_PRERELEASE}-${PACKAGE_RELEASE}"
else
export DOONA_VERSION="${VERSION}${PRERELEASE:+~$PRERELEASE}-${PACKAGE_RELEASE}"
fi
for config in install/nfpm/doona.yaml install/nfpm/doona-fonts.yaml install/nfpm/doona-precompressed.yaml; do
if [ "$packager" = archlinux ]; then
# nfpm 2.47 needs an explicit default epoch to retain Arch's prerelease in .PKGINFO.
arch_config="$RUNNER_TEMP/$(basename "$config")"
sed "/^version_schema: semver$/a epoch: '0'" "$config" > "$arch_config"
PRERELEASE="$ARCH_PRERELEASE" nfpm package --config "$arch_config" --packager "$packager" --target release/
PRERELEASE="$ARCH_PRERELEASE" nfpm package --config "$arch_config" --packager "$packager" --target packages/
else
nfpm package --config "$config" --packager "$packager" --target release/
nfpm package --config "$config" --packager "$packager" --target packages/
fi
done
done
# Keep upstream SemVer in asset names; package metadata retains each format's own version.
- name: Sum every asset
- name: Name the packages after the upstream version
run: |
cd release
if [ -n "$PRERELEASE" ]; then
for name in doona doona-fonts; do
deb="doona-web${name#doona}"
cd packages
for name in doona doona-fonts doona-precompressed; do
deb="doona-web${name#doona}"
if [ -n "$PRERELEASE" ]; then
mv "${deb}_${VERSION}~${PRERELEASE}-${PACKAGE_RELEASE}_all.deb" "${deb}_${NPM}-${PACKAGE_RELEASE}_all.deb"
mv "${name}-${VERSION}~${PRERELEASE}-${PACKAGE_RELEASE}.noarch.rpm" "${name}-${NPM}-${PACKAGE_RELEASE}.noarch.rpm"
mv "${name}_${VERSION}~${PRERELEASE}-${PACKAGE_RELEASE}_all.ipk" "${name}_${NPM}-${PACKAGE_RELEASE}_all.ipk"
done
fi
set -- "doona-${NPM}.tar.gz" "doona-fonts-${NPM}.tar.gz"
for name in doona doona-fonts; do
set -- "$@" "doona-web${name#doona}_${NPM}-${PACKAGE_RELEASE}_all.deb" "${name}-${NPM}-${PACKAGE_RELEASE}.noarch.rpm" \
fi
ls "${deb}_${NPM}-${PACKAGE_RELEASE}_all.deb" "${name}-${NPM}-${PACKAGE_RELEASE}.noarch.rpm" \
"${name}_${NPM}-${PACKAGE_RELEASE}_all.ipk" "${name}-${VERSION}${ARCH_PRERELEASE}-${PACKAGE_RELEASE}-any.pkg.tar.zst"
done
sha256sum "$@" > SHA256SUMS
# Temporary: honk has no release with the native API yet, so every doona release carries the honk debug builds of the
# tag pinned in tools/honk-pin.txt, the source archive of their commit and a note naming it. fetch-honk.mjs reads that
# tag's own release and fails unless its body, target and source tag name the pinned tag and commit. Remove this step,
# that file and the three honk lines in release's `files` once honk ships one.
- name: Bundle the honk-core debug builds
env:
GH_TOKEN: ${{ github.token }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: nfpm-packages
path: packages/
if-no-files-found: error
retention-days: 1

alpine:
needs: [build]
if: ${{ !cancelled() && needs.build.result == 'success' }}
runs-on: ubuntu-latest
permissions:
contents: read
env:
NPM: ${{ needs.build.outputs.NPM }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: archives
path: release/
- name: Build the Alpine packages
run: docker run --rm -v "$PWD:/src" -w /src "$ALPINE_IMAGE" sh tools/package-alpine.sh "$NPM"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: alpine-packages
path: release/alpine/
if-no-files-found: error
retention-days: 1
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: alpine-recipe
path: release/alpine-recipe/
if-no-files-found: error

openwrt-apk:
needs: [build]
if: ${{ !cancelled() && needs.build.result == 'success' }}
runs-on: ubuntu-latest
permissions:
contents: read
env:
NPM: ${{ needs.build.outputs.NPM }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: archives
path: release/
# The SDK image builds as its own unprivileged user, which needs to write the output directory.
- name: Build the OpenWrt apk packages
run: |
mkdir -p release/openwrt release/openwrt-recipe
chmod 777 release/openwrt release/openwrt-recipe
docker run --rm -v "$PWD:/src" -w /src "$OPENWRT_SDK_IMAGE" sh tools/package-openwrt.sh "$NPM"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: openwrt-packages
path: release/openwrt/
if-no-files-found: error
retention-days: 1
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: openwrt-recipe
path: release/openwrt-recipe/
if-no-files-found: error

# Collects every package next to the archives and sums them, so the release job only uploads.
sums:
needs: [build, nfpm, alpine, openwrt-apk]
if: ${{ !cancelled() && needs.nfpm.result == 'success' && needs.alpine.result == 'success' && needs.openwrt-apk.result == 'success' }}
runs-on: ubuntu-latest
permissions: {}
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: '{archives,*-packages}'
path: release/
merge-multiple: true
- name: Sum every asset
run: |
node tools/fetch-honk.mjs release
cd release
sha256sum honk-core-debug-*.tar.gz honk-source-*.tar.gz HONK-SOURCE.txt >> SHA256SUMS
sha256sum -- * > "$RUNNER_TEMP/SHA256SUMS"
mv "$RUNNER_TEMP/SHA256SUMS" SHA256SUMS
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-assets
path: release/
if-no-files-found: error
retention-days: 1

release:
needs: [build]
needs: [build, nfpm, alpine, openwrt-apk, sums]
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -179,6 +309,10 @@ jobs:
release/*-${{ env.NPM }}-${{ env.PACKAGE_RELEASE }}.noarch.rpm
release/*_${{ env.NPM }}-${{ env.PACKAGE_RELEASE }}_all.ipk
release/*-${{ env.VERSION }}${{ env.ARCH_PRERELEASE }}-${{ env.PACKAGE_RELEASE }}-any.pkg.tar.zst
release/*.apk
release/doona-alpine.rsa.pub
release/doona-openwrt.adb
release/doona-openwrt.pem
release/SHA256SUMS
release/honk-core-debug-*.tar.gz
release/honk-source-*.tar.gz
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ doona targets the native API implemented by honk's `feat/native-api` branch; tha

doona needs honk's native API, which only the `debug` release of [Glassyiris/honk `feat/native-api`](https://github.com/Glassyiris/honk/tree/feat/native-api) provides so far. Release archives (`doona-<version>.tar.gz`, the optional `doona-fonts-<version>.tar.gz` with Noto Sans TC and SC, and `SHA256SUMS`) are attached to tags on the [releases page](https://github.com/Zakkaus/doona/releases). Extract `doona-<version>.tar.gz` into the directory that honk's `native_api` block names in `ui`, and honk serves doona at `/ui/`.

The optional `doona-precompressed-<version>.tar.gz` adds `.br` and `.gz` copies of the text assets; extracted into the same directory, it lets honk or a web server send compressed responses.

From v0.1.0-beta.8 on, until honk publishes a release with the native API, each doona release also attaches prebuilt `honk-core-debug-<target>[-stock].tar.gz` archives, so no one needs to compile honk. The archives contain a debug build of honk's native API branch, which implements the final native API contract. `HONK-SOURCE.txt` names the honk commit they were built from, `honk-source-<commit>.tar.gz` holds that commit's source, and `SHA256SUMS` covers every release asset except itself. [Install honk](https://zakkaus.github.io/doona-docs/en/install.html#install) explains which archive fits a gateway.

The [documentation](https://zakkaus.github.io/doona-docs/en/) covers the requirements, installing honk and doona, an example configuration, the first sign-in, checking each feature and troubleshooting.
Expand Down
2 changes: 2 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ doona 对接 honk `feat/native-api` 分支实现的原生 API;这套 API 尚

doona 依赖 honk 的原生 API,目前只有 [Glassyiris/honk `feat/native-api`](https://github.com/Glassyiris/honk/tree/feat/native-api) 分支的 `debug` 版本提供。发行文件(`doona-<version>.tar.gz`、可选的 `doona-fonts-<version>.tar.gz`(Noto Sans TC 与 SC)、`SHA256SUMS`)附在[发布页](https://github.com/Zakkaus/doona/releases)的标签上。将 `doona-<version>.tar.gz` 解压到 honk `native_api` 配置块中 `ui` 指定的目录,honk 即在 `/ui/` 提供 doona。

可选的 `doona-precompressed-<version>.tar.gz` 包含文本资源的 `.br` 与 `.gz` 压缩副本;解压到同一目录后,honk 或 Web 服务器即可发送压缩响应。

自 v0.1.0-beta.8 起,在 honk 发行含原生 API 的正式版本之前,每个 doona 发行版也附带预先构建的 `honk-core-debug-<target>[-stock].tar.gz`,用户无需自行编译 honk。归档文件包含 honk 原生 API 分支的 debug 构建,实现最终的原生 API 契约。`HONK-SOURCE.txt` 注明构建所用的 honk 提交,`honk-source-<commit>.tar.gz` 为该提交的源码,`SHA256SUMS` 涵盖除自身以外的全部发布附件。[安装 honk](https://zakkaus.github.io/doona-docs/zh-CN/install.html#install) 说明如何按网关选择归档文件。

[文档](https://zakkaus.github.io/doona-docs/zh-CN/)包含系统要求、honk 与 doona 的安装、示例配置、首次登录、逐项检查功能与故障排查。
Expand Down
2 changes: 2 additions & 0 deletions README.zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ doona 對接 honk `feat/native-api` 分支實作的原生 API;這套 API 尚

doona 需要 honk 的原生 API,目前只有 [Glassyiris/honk `feat/native-api`](https://github.com/Glassyiris/honk/tree/feat/native-api) 分支的 `debug` 版本提供。發行檔(`doona-<version>.tar.gz`、選用的 `doona-fonts-<version>.tar.gz`(Noto Sans TC 與 SC)、`SHA256SUMS`)附在[發布頁](https://github.com/Zakkaus/doona/releases)的標籤上。將 `doona-<version>.tar.gz` 解壓縮到 honk `native_api` 組態區塊中 `ui` 指定的目錄,honk 即在 `/ui/` 提供 doona。

選用的 `doona-precompressed-<version>.tar.gz` 包含文字資源的 `.br` 與 `.gz` 壓縮副本;解壓縮到同一目錄後,honk 或 Web 伺服器即可傳送壓縮回應。

自 v0.1.0-beta.8 起,在 honk 發行含原生 API 的正式版本之前,每個 doona 發行版也附上預先建置的 `honk-core-debug-<target>[-stock].tar.gz`,使用者不需自行編譯 honk。封存檔包含 honk 原生 API 分支的 debug 建置,實作最終的原生 API 契約。`HONK-SOURCE.txt` 註明建置所用的 honk 提交,`honk-source-<commit>.tar.gz` 為該提交的原始碼,`SHA256SUMS` 涵蓋除自身以外的所有發布附件。[安裝 honk](https://zakkaus.github.io/doona-docs/zh-TW/install.html#install) 說明如何依閘道器選擇封存檔。

[文件](https://zakkaus.github.io/doona-docs/zh-TW/)涵蓋系統需求、honk 與 doona 的安裝、範例組態、首次登入、逐項檢查功能與疑難排解。
Expand Down
4 changes: 4 additions & 0 deletions changes/feat-package-precompress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Added

- The optional `doona-precompressed` package and archive install `.br` and `.gz` copies of the text assets next to doona's files, so servers can send compressed responses; the main package stays the same size.
- Releases include Alpine and OpenWrt 25.12 apk packages for doona, doona-fonts and doona-precompressed, with the public signing key of each release.
Loading
Loading