Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
611736e
ci(harmony): HAP 每次真编之后收尾,并把缓存配额还给基线
aeoform Oct 20, 2026
f3daaeb
ci(windows): 砍掉 16 分钟重复编译,Windows 不再是关键路径
aeoform Oct 20, 2026
594b98e
test: pin 契约接受 Windows 的 --lib --bins --no-run
aeoform Oct 20, 2026
dde8163
ci: 把 Windows 前端与 Linux 测试从关键路径上拆出去
aeoform Oct 20, 2026
f4adbfc
ci(android): 固定 emulator 构建并缓存,去掉「最后一个完成」
aeoform Oct 20, 2026
4776c58
test: 删掉一个伪装成契约测试的 cargo 调用(整套契约 65.9s → 13.2s)
aeoform Oct 20, 2026
6969389
fix(release): 鸿蒙签名目录被 job 级 env 遮蔽,正式发版签不出来
aeoform Oct 20, 2026
d847f0e
feat(harmony): 补全应用市场发布链(通道化凭据 + debug 本地签名 + 获取步骤)
aeoform Oct 20, 2026
7536a78
feat(harmony): 签名材料自检脚本,把「签不出来」的四个坑挡在构建之前
aeoform Oct 20, 2026
29bcfe0
feat(harmony): release 默认只发未签名包,签名仅用于应用市场
aeoform Oct 20, 2026
e72771e
fix(linux-egui): 打包 job 必须自己编 fcitx5 插件(我拆过头了)
aeoform Oct 20, 2026
b2be02f
fix(ci): Windows 的 Rust job 仍要先构建前端(generate_context! 要 dist)
aeoform Oct 20, 2026
9c8a4a9
fix(release): 通道化 secret 名称解析错位,release 通道永远读不到材料
aeoform Oct 20, 2026
b52c316
feat(harmony): 应用市场要的是 .app(App Pack),不是 .hap —— 已真机上传成功
aeoform Oct 21, 2026
f20848b
fix(harmony): channel=none 时 APP_PACK 未初始化,set -u 直接退出
aeoform Oct 21, 2026
82200b6
Merge remote-tracking branch 'origin/beta' into feat/harmony-ci-caches
aeoform Oct 21, 2026
e377f55
ci: 原生胶囊并进 ci.yml(并入 #1239,不再单开 PR)
aeoform Oct 21, 2026
c9a86a1
fix(ci): restore release gates and cache correctness
H-Chris233 Oct 22, 2026
60f2be4
fix(ci): drain emulator version output and support BSD sed
H-Chris233 Oct 22, 2026
68ef056
fix(ci): install emulator runtime and resolve fixture tool paths
H-Chris233 Oct 22, 2026
7be7939
Merge remote-tracking branch 'origin/beta' into codex/pr-1239-fixes
H-Chris233 Oct 22, 2026
2ae806c
fix(ci): register pinned emulator as an installed SDK package
H-Chris233 Oct 22, 2026
a912752
fix(ci): share AVD registry across SDK tools and emulator
H-Chris233 Oct 22, 2026
d6d9cbd
fix(ci): register pinned emulator before image dependencies
H-Chris233 Oct 22, 2026
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
197 changes: 143 additions & 54 deletions .github/workflows/check-linux-egui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,104 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
test-linux-egui:
# Run in parallel with packaging: the tests used
# to serialise ~8 minutes in front of the ~7 minute release build, making this
# workflow the critical path of the whole CI run.
runs-on: ubuntu-24.04
timeout-minutes: 60
env:
CARGO_PROFILE_DEV_DEBUG: 0
CARGO_PROFILE_TEST_DEBUG: 0
CARGO_TARGET_DIR: ${{ github.workspace }}/openless-all/app/target
steps:
- uses: actions/checkout@v6
with:
submodules: false
ref: ${{ inputs.checkout_ref || github.ref }}

- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
if: inputs.scope != 'none'
- uses: actions/setup-node@v4
with:
node-version: '22'
if: inputs.scope != 'none'

- name: Install native build and test dependencies
if: inputs.scope != 'none'
# Same list as the packaging job: the fcitx5 plugin build and the
# `pwsh` contract scripts both live here now.
run: |
sudo add-apt-repository -y universe || true
sudo apt-get update
sudo apt-get install -y \
appstream build-essential cmake cpio desktop-file-utils \
fcitx5-modules-dev file libasound2-dev libdbus-1-dev \
libfcitx5config-dev libfcitx5core-dev libfcitx5utils-dev \
liblzma-dev libpipewire-0.3-dev libpulse-dev libssl-dev \
libwayland-dev libx11-dev libxkbcommon-dev pkg-config ripgrep rpm

- uses: swatinem/rust-cache@v2
if: inputs.scope != 'none'
with:
# Test and release profiles are produced independently; each needs its own cache.
shared-key: test-linux-egui
workspaces: |
openless-all/app -> target
openless-all/app/src-tauri/backend-tests -> ../../target
cache-on-failure: true
save-if: ${{ github.ref == 'refs/heads/beta' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}

- name: Build and test fcitx5 plugin
if: inputs.scope != 'none'
working-directory: openless-all/app/scripts/linux-fcitx5-plugin
run: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
cmake --build build --parallel
ctest --test-dir build --output-on-failure
test -s build/libopenless.so
test -s build/openless.conf

- name: Test Core, Linux host, and platform boundaries
if: inputs.scope != 'none'
working-directory: openless-all/app
run: |
set -euo pipefail
cargo test --locked -p openless-core
# Named re-run of the hotkey edge gate that used to be a standalone
# script spawning its own `cargo test`: the binary is already built by
# the line above, so this is free, and a rename now fails loudly here
# instead of silently dropping the gate.
cargo test --locked -p openless-core --lib \
api::tests::shared_hotkey_edges_own_hold_auto_and_combo_abort_semantics -- --exact \
| tee "$RUNNER_TEMP/openless-hotkey-edge.log"
grep -Fq 'test api::tests::shared_hotkey_edges_own_hold_auto_and_combo_abort_semantics ... ok' \
"$RUNNER_TEMP/openless-hotkey-edge.log"
cargo test --locked -p openless-harmony-host
cargo clippy --locked -p openless-core --all-targets -- -D warnings
cargo test --locked -p openless-linux-egui --all-targets
cargo clippy --locked -p openless-linux-egui --all-targets -- -D warnings
node scripts/linux-egui-tauri-free-contract.test.mjs
node scripts/linux-egui-release-contract.test.mjs
node scripts/ci-changed-areas.test.mjs
node scripts/ci-cache-usage.test.mjs
node scripts/ci-cache-sharing.test.mjs
node scripts/harmony-release-contract.test.mjs
node scripts/tauri-linux-free-contract.test.mjs
pwsh -NoProfile -File ./scripts/check-core-deps.ps1
pwsh -NoProfile -File ./scripts/check-core-deps.ps1 openless-linux-egui
pwsh -NoProfile -File ./scripts/check-core-secret-surface.ps1
pwsh -NoProfile -File ./scripts/check-core-test-isolation.ps1
pwsh -NoProfile -File ./scripts/check-core-runtime-seam.ps1
pwsh -NoProfile -File ./scripts/check-linux-public-surface.ps1

- name: Test remote input TLS identity and Unix key permissions
if: inputs.scope != 'none'
working-directory: openless-all/app
run: cargo test --locked --manifest-path src-tauri/backend-tests/Cargo.toml --test remote_tls

build-linux-egui:
# PipeWire/libspa in this host needs 24.04. Packages target glibc >= 2.39.
runs-on: ubuntu-24.04
Expand Down Expand Up @@ -110,6 +208,8 @@ jobs:
- uses: swatinem/rust-cache@v2
if: inputs.scope != 'none'
with:
# Keep the release cache shared with tag builds, separate from test profiles.
shared-key: build-linux-egui
workspaces: |
openless-all/app -> target
openless-all/app/src-tauri/backend-tests -> ../../target
Expand All @@ -118,43 +218,15 @@ jobs:
cache-on-failure: true
save-if: ${{ github.ref == 'refs/heads/beta' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}

- name: Build and test fcitx5 plugin
- name: Build the fcitx5 plugin for packaging
if: inputs.scope != 'none'
# The package installs `libopenless.so` + `openless.conf`, so the
# packaging job needs the artifact. The assertions and `ctest` run in the
# parallel `test-linux-egui` job; this is the build only.
working-directory: openless-all/app/scripts/linux-fcitx5-plugin
run: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
cmake --build build --parallel
ctest --test-dir build --output-on-failure
test -s build/libopenless.so
test -s build/openless.conf

- name: Test Core, Linux host, and platform boundaries
if: inputs.scope != 'none'
working-directory: openless-all/app
run: |
cargo test --locked -p openless-core
cargo test --locked -p openless-harmony-host
cargo clippy --locked -p openless-core --all-targets -- -D warnings
cargo test --locked -p openless-linux-egui --all-targets
cargo clippy --locked -p openless-linux-egui --all-targets -- -D warnings
node scripts/linux-egui-tauri-free-contract.test.mjs
node scripts/linux-egui-release-contract.test.mjs
node scripts/ci-changed-areas.test.mjs
node scripts/ci-cache-usage.test.mjs
node scripts/ci-cache-sharing.test.mjs
node scripts/harmony-release-contract.test.mjs
node scripts/tauri-linux-free-contract.test.mjs
pwsh -NoProfile -File ./scripts/check-core-deps.ps1
pwsh -NoProfile -File ./scripts/check-core-deps.ps1 openless-linux-egui
pwsh -NoProfile -File ./scripts/check-core-secret-surface.ps1
pwsh -NoProfile -File ./scripts/check-core-test-isolation.ps1
pwsh -NoProfile -File ./scripts/check-core-runtime-seam.ps1
pwsh -NoProfile -File ./scripts/check-linux-public-surface.ps1

- name: Test remote input TLS identity and Unix key permissions
if: inputs.scope != 'none'
working-directory: openless-all/app
run: cargo test --locked --manifest-path src-tauri/backend-tests/Cargo.toml --test remote_tls

- name: Validate Linux desktop metadata
if: inputs.scope != 'none'
Expand Down Expand Up @@ -217,18 +289,45 @@ jobs:
path: openless-all/app/target/linux-egui-packages/*
if-no-files-found: error

- name: Upload direct download aliases
if: inputs.scope != 'none' && (github.event_name != 'push' || env.RELEASE_TAG != '')
uses: actions/upload-artifact@v7
with:
name: openless-linux-egui-direct-download
path: ${{ runner.temp }}/openless-linux-egui-direct-download/*
if-no-files-found: error

publish-linux-egui:
needs: [test-linux-egui, build-linux-egui]
if: inputs.scope != 'none' && inputs.publish && inputs.release_tag != ''
runs-on: ubuntu-24.04
timeout-minutes: 20
env:
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.release_tag }}
steps:
- name: Download verified Linux packages
uses: actions/download-artifact@v8
with:
name: openless-linux-egui-x86_64
path: ${{ runner.temp }}/openless-linux-egui-packages
- name: Download direct download aliases
uses: actions/download-artifact@v8
with:
name: openless-linux-egui-direct-download
path: ${{ runner.temp }}/openless-linux-egui-direct-download
- name: Attach Linux packages to the GitHub Release
# Runs only for the release entry point, which pins the tag; PR and push
# builds never publish.
if: inputs.scope != 'none' && inputs.publish && inputs.release_tag != ''
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.release_tag }}
working-directory: openless-all/app
run: |
set -euo pipefail
# The desktop workflow creates the Release for this tag; wait for it
# instead of racing it, and fail loudly if it never appears.
output="$RUNNER_TEMP/openless-linux-egui-packages"
alias_dir="$RUNNER_TEMP/openless-linux-egui-direct-download"
for file in "$output"/OpenLess-Linux-egui-*.deb "$output"/OpenLess-Linux-egui-*.rpm \
"$output/SHA256SUMS-linux" "$alias_dir/OpenLess_linux_x86_64.deb" \
"$alias_dir/OpenLess_linux_x86_64.rpm"; do
[ -s "$file" ] || { echo "::error::missing Linux release asset $file"; exit 1; }
done
# The desktop workflow creates the Release; wait without racing its creation.
for attempt in $(seq 1 40); do
if gh release view "$RELEASE_TAG" --json isDraft > /dev/null 2>&1; then
break
Expand All @@ -239,21 +338,11 @@ jobs:
fi
sleep 15
done
output=target/linux-egui-packages
alias_dir="$RUNNER_TEMP/openless-linux-egui-direct-download"
gh release upload "$RELEASE_TAG" \
"$output"/OpenLess-Linux-egui-*.deb \
"$output"/OpenLess-Linux-egui-*.rpm \
"$output"/SHA256SUMS-linux \
"$alias_dir"/OpenLess_linux_x86_64.deb \
"$alias_dir"/OpenLess_linux_x86_64.rpm \
"$output/SHA256SUMS-linux" \
"$alias_dir/OpenLess_linux_x86_64.deb" \
"$alias_dir/OpenLess_linux_x86_64.rpm" \
--clobber
echo "attached Linux assets to $RELEASE_TAG"

- name: Upload direct download aliases
if: inputs.scope != 'none' && (github.event_name != 'push' || env.RELEASE_TAG != '')
uses: actions/upload-artifact@v7
with:
name: openless-linux-egui-direct-download
path: ${{ runner.temp }}/openless-linux-egui-direct-download/*
if-no-files-found: error
Loading
Loading