chore(release): prepare v0.8.0 prerelease
This commit is contained in:
parent
6c98da3ea2
commit
613878e18e
170
.github/workflows/rust-release.yml
vendored
170
.github/workflows/rust-release.yml
vendored
|
|
@ -1,7 +1,8 @@
|
|||
name: Rust release artifacts
|
||||
|
||||
# Triggered on tag push (v*) — builds release binaries across desktop targets,
|
||||
# packages real installers per platform, and assembles a GitHub Release draft.
|
||||
# packages real installers per platform, builds the web Docker image and SDK
|
||||
# tarballs, and assembles a GitHub Release draft.
|
||||
#
|
||||
# Artifact matrix (per platform; <v> = tag version, arch token = x64/arm64 to
|
||||
# match electron-builder's `${productName}-${version}-${arch}-…` convention
|
||||
|
|
@ -58,16 +59,23 @@ name: Rust release artifacts
|
|||
# The `build` job builds the real `op-host-desktop` crate (winit + skia-safe
|
||||
# runner that drives the Rust editor); its shipped executable keeps the
|
||||
# stable `openpencil-desktop` name. The `op` CLI (crates/op-cli, binary name
|
||||
# `op`) is built in the same cargo invocation. The Rust web bundle is built by
|
||||
# `.github/workflows/wasm-bundle-build.yml` as a separate deployable CanvasKit
|
||||
# artifact; this workflow currently keeps the native desktop release matrix
|
||||
# independent from that web artifact.
|
||||
# `op`) is built in the same cargo invocation. The web Docker job builds the
|
||||
# Rust web host from `Dockerfile.web-rust`, and the SDK job packages the
|
||||
# wasm-backed `packages/op-web-sdk*` workspace tarballs.
|
||||
#
|
||||
# The standalone `.github/workflows/wasm-bundle-build.yml` still gates the
|
||||
# CanvasKit web bundle on push/PR. This release workflow only assembles the
|
||||
# tagged, publishable artifacts.
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.label }}
|
||||
|
|
@ -340,21 +348,108 @@ jobs:
|
|||
OpenPencil-*-linux.deb
|
||||
if-no-files-found: ignore
|
||||
|
||||
# Web bundle release note:
|
||||
# The deployable Rust web bundle is produced by
|
||||
# `.github/workflows/wasm-bundle-build.yml`, which builds the current
|
||||
# CanvasKit feature set, runs `tools/check-wasm-bundle.sh`, size-gates the
|
||||
# wasm output, and uploads the `op-web-bundle` artifact. This desktop release
|
||||
# workflow still ships only native desktop + CLI artifacts; attach or merge the
|
||||
# `op-web-bundle` artifact here only when tagged releases are meant to publish
|
||||
# the standalone web bundle from the same draft.
|
||||
web-docker:
|
||||
name: Build and push web Docker image
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
outputs:
|
||||
image: ${{ steps.meta.outputs.image }}
|
||||
tag: ${{ steps.meta.outputs.tag }}
|
||||
digest: ${{ steps.build.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
- name: Compute image metadata
|
||||
id: meta
|
||||
shell: bash
|
||||
run: |
|
||||
version="${GITHUB_REF_NAME#v}"
|
||||
image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/openpencil-web"
|
||||
tag="${image}:v${version}"
|
||||
echo "image=${image}" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
|
||||
- name: Build and push
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.web-rust
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tag }}
|
||||
|
||||
sdk-packages:
|
||||
name: Build web SDK npm tarballs
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: '1.94'
|
||||
targets: wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: web-sdk-release
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- name: Install wasm-bindgen-cli (pinned to Cargo.lock)
|
||||
shell: bash
|
||||
run: |
|
||||
version="$(awk '/^name = "wasm-bindgen"$/{found=1; next} found && /^version = /{gsub(/[" ]/,"",$3); print $3; exit}' Cargo.lock)"
|
||||
if [ -z "$version" ]; then
|
||||
echo "::error::could not resolve wasm-bindgen version from Cargo.lock"
|
||||
exit 1
|
||||
fi
|
||||
cargo install wasm-bindgen-cli --version "$version" --locked
|
||||
- name: Install binaryen
|
||||
run: |
|
||||
BINARYEN_VERSION=version_123
|
||||
curl -fsSL \
|
||||
"https://github.com/WebAssembly/binaryen/releases/download/${BINARYEN_VERSION}/binaryen-${BINARYEN_VERSION}-x86_64-linux.tar.gz" \
|
||||
| tar -xz
|
||||
echo "${GITHUB_WORKSPACE}/binaryen-${BINARYEN_VERSION}/bin" >> "$GITHUB_PATH"
|
||||
- name: Install package workspace dependencies
|
||||
working-directory: packages
|
||||
run: bun install --frozen-lockfile
|
||||
- name: Build wasm-backed SDK packages
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd packages/op-web-sdk
|
||||
bun run sync-wasm
|
||||
bun run build
|
||||
cd ../op-web-sdk-react
|
||||
bun run build
|
||||
cd ../op-web-sdk-vue
|
||||
bun run build
|
||||
- name: Pack npm tarballs
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p sdk-packages
|
||||
for pkg in op-web-sdk op-web-sdk-react op-web-sdk-vue; do
|
||||
(cd "packages/${pkg}" && bun pm pack --destination "${GITHUB_WORKSPACE}/sdk-packages")
|
||||
done
|
||||
ls -la sdk-packages
|
||||
- name: Upload SDK tarballs
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openpencil-web-sdk-packages
|
||||
path: sdk-packages/*.tgz
|
||||
if-no-files-found: error
|
||||
|
||||
release-draft:
|
||||
name: Create / update GitHub Release draft
|
||||
# Native release artifacts only. The deployable Rust web bundle is built by
|
||||
# wasm-bundle-build.yml and can be attached separately when release policy
|
||||
# calls for publishing the web bundle from this draft.
|
||||
needs: [build]
|
||||
needs: [build, web-docker, sdk-packages]
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
|
|
@ -363,16 +458,53 @@ jobs:
|
|||
with:
|
||||
path: dist
|
||||
- name: Flatten artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p release-files
|
||||
find dist -type f \
|
||||
\( -name "*.tar.gz" -o -name "*.zip" -o -name "*.dmg" \
|
||||
-o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" \) \
|
||||
-o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" \
|
||||
-o -name "*.tgz" \) \
|
||||
-exec cp {} release-files/ \;
|
||||
ls -la release-files
|
||||
shopt -s nullglob
|
||||
native=(release-files/OpenPencil-* release-files/openpencil-desktop-*)
|
||||
cli=(release-files/op-cli-*)
|
||||
sdk=(release-files/*.tgz)
|
||||
if [ "${#native[@]}" -eq 0 ]; then
|
||||
echo "::error::missing native desktop artifacts"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${#cli[@]}" -eq 0 ]; then
|
||||
echo "::error::missing op-cli artifacts"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${#sdk[@]}" -ne 3 ]; then
|
||||
echo "::error::expected 3 SDK tarballs, found ${#sdk[@]}"
|
||||
exit 1
|
||||
fi
|
||||
- name: Prepare release body
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="${GITHUB_REF_NAME#v}"
|
||||
note="RELEASE_NOTES/v${version}.md"
|
||||
if [ ! -f "$note" ]; then
|
||||
echo "::error::missing release note: $note"
|
||||
exit 1
|
||||
fi
|
||||
cp "$note" release-body.md
|
||||
{
|
||||
printf '\n## Web Docker Image\n\n'
|
||||
printf -- '- Image: `%s`\n' '${{ needs.web-docker.outputs.tag }}'
|
||||
printf -- '- Digest: `%s`\n' '${{ needs.web-docker.outputs.digest }}'
|
||||
} >> release-body.md
|
||||
- name: Create / update GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: true
|
||||
prerelease: true
|
||||
make_latest: false
|
||||
body_path: release-body.md
|
||||
files: release-files/*
|
||||
generate_release_notes: true
|
||||
|
|
|
|||
34
RELEASE_NOTES/v0.8.0.md
Normal file
34
RELEASE_NOTES/v0.8.0.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# OpenPencil v0.8.0
|
||||
|
||||
`v0.8.0` is a pre-release for the Rust OpenPencil stack.
|
||||
|
||||
## Highlights
|
||||
|
||||
- Rust desktop app is now the primary OpenPencil experience, with installer artifacts for macOS, Windows, and Linux.
|
||||
- `op` CLI ships as standalone release artifacts for the same desktop target matrix.
|
||||
- Rust web host is published as a GHCR Docker image for tagged releases.
|
||||
- Web SDK tarballs are attached for `@zseven-w/op-web-sdk`, `@zseven-w/op-web-sdk-react`, and `@zseven-w/op-web-sdk-vue`.
|
||||
- Desktop update checks can discover pre-release GitHub releases, so `v0.8.0` can be offered before it is promoted to a stable release.
|
||||
|
||||
## Release Artifacts
|
||||
|
||||
- macOS: `OpenPencil-0.8.0-<arch>-mac.dmg`, plus raw desktop and `op` CLI archives.
|
||||
- Windows: `OpenPencil-0.8.0-<arch>-win-setup.exe`, plus portable desktop and `op` CLI archives.
|
||||
- Linux: `OpenPencil-0.8.0-<arch>-linux.AppImage`, `OpenPencil-0.8.0-<arch>-linux.deb`, plus raw desktop and `op` CLI archives.
|
||||
- Web SDK: npm tarballs for the base SDK, React adapter, and Vue adapter.
|
||||
|
||||
## Docker
|
||||
|
||||
The release workflow publishes the web image to GHCR as:
|
||||
|
||||
```sh
|
||||
docker pull ghcr.io/zseven-w/openpencil-web:v0.8.0
|
||||
```
|
||||
|
||||
Because this is a pre-release, the workflow does not publish or update a `latest` tag.
|
||||
|
||||
## Notes
|
||||
|
||||
- The retired TypeScript apps and `pen-*` packages are not part of this release line.
|
||||
- The web SDK remains a read-only `.op` viewer SDK backed by the Rust wasm bundle.
|
||||
- Desktop auto-update currently checks for available releases and opens the platform installer path; it does not yet perform a signed in-place self-update.
|
||||
|
|
@ -33,9 +33,11 @@ pub fn releases_url() -> String {
|
|||
format!("https://github.com/{GITHUB_OWNER}/{GITHUB_REPO}/releases")
|
||||
}
|
||||
|
||||
/// `releases/latest` API endpoint.
|
||||
/// Releases list API endpoint. GitHub's `releases/latest` endpoint excludes
|
||||
/// prereleases, so the desktop updater uses the list feed and picks the first
|
||||
/// non-draft release itself.
|
||||
fn latest_release_api() -> String {
|
||||
format!("https://api.github.com/repos/{GITHUB_OWNER}/{GITHUB_REPO}/releases/latest")
|
||||
format!("https://api.github.com/repos/{GITHUB_OWNER}/{GITHUB_REPO}/releases?per_page=20")
|
||||
}
|
||||
|
||||
/// Background release-API probe. One request per `spawn`; the
|
||||
|
|
@ -99,8 +101,9 @@ impl UpdateProbe {
|
|||
}
|
||||
}
|
||||
|
||||
/// Query `releases/latest` and classify the running build against
|
||||
/// it. Blocking — only ever called on the probe worker thread.
|
||||
/// Query the releases list and classify the running build against the newest
|
||||
/// non-draft release, including prereleases. Blocking — only ever called on the
|
||||
/// probe worker thread.
|
||||
fn check_latest_release() -> UpdateStatus {
|
||||
let Some(tag) = fetch_latest_tag() else {
|
||||
return UpdateStatus::Error;
|
||||
|
|
@ -142,10 +145,26 @@ fn fetch_latest_tag() -> Option<String> {
|
|||
return None;
|
||||
}
|
||||
let json: serde_json::Value = resp.json().await.ok()?;
|
||||
json.get("tag_name")?.as_str().map(str::to_string)
|
||||
select_latest_release_tag(&json)
|
||||
})
|
||||
}
|
||||
|
||||
/// Select the newest published release tag from GitHub's releases list.
|
||||
/// GitHub returns newest releases first. Drafts are not visible to users and
|
||||
/// must not drive update prompts; prereleases are visible and are intentionally
|
||||
/// allowed for v0.8.0.
|
||||
fn select_latest_release_tag(json: &serde_json::Value) -> Option<String> {
|
||||
json.as_array()?
|
||||
.iter()
|
||||
.filter(|release| {
|
||||
!release
|
||||
.get("draft")
|
||||
.and_then(serde_json::Value::as_bool)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.find_map(|release| release.get("tag_name")?.as_str().map(str::to_string))
|
||||
}
|
||||
|
||||
/// Download the platform installer for `version` on a worker thread
|
||||
/// and open it when done (macOS mounts the DMG, Windows launches the
|
||||
/// NSIS setup, Linux reveals the AppImage after `chmod +x`). Any
|
||||
|
|
@ -359,6 +378,37 @@ mod tests {
|
|||
assert!(is_newer("0.9.0-rc.1", "0.8.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn releases_api_uses_list_endpoint_so_prereleases_are_visible() {
|
||||
assert_eq!(
|
||||
latest_release_api(),
|
||||
"https://api.github.com/repos/ZSeven-W/openpencil/releases?per_page=20"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn release_list_selection_keeps_prerelease_and_skips_drafts() {
|
||||
let feed = serde_json::json!([
|
||||
{
|
||||
"tag_name": "v0.9.0",
|
||||
"draft": true,
|
||||
"prerelease": false
|
||||
},
|
||||
{
|
||||
"tag_name": "v0.8.0",
|
||||
"draft": false,
|
||||
"prerelease": true
|
||||
},
|
||||
{
|
||||
"tag_name": "v0.7.5",
|
||||
"draft": false,
|
||||
"prerelease": false
|
||||
}
|
||||
]);
|
||||
|
||||
assert_eq!(select_latest_release_tag(&feed).as_deref(), Some("v0.8.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn releases_url_points_at_the_publish_repo() {
|
||||
assert_eq!(
|
||||
|
|
|
|||
Loading…
Reference in a new issue