[fix] repair desktop CI and upgrade outdated Xmake

This commit is contained in:
dijunkun
2026-10-06 10:51:52 +08:00
parent 76eddc9f02
commit ddefd14409
6 changed files with 329 additions and 22 deletions
+29
View File
@@ -0,0 +1,29 @@
name: Ensure minimum Xmake version
description: Keep a compatible Xmake installation, or install the minimum required version.
inputs:
minimum-version:
description: Minimum supported Xmake version (major.minor.patch).
required: true
runs:
using: composite
steps:
- name: Check installed Xmake version
id: check
shell: bash
env:
XMAKE_MIN_VERSION: ${{ inputs.minimum-version }}
run: bash "$GITHUB_ACTION_PATH/check-version.sh"
- name: Install or upgrade Xmake
if: steps.check.outputs.needs-upgrade == 'true'
uses: xmake-io/github-action-setup-xmake@v1
with:
xmake-version: ${{ inputs.minimum-version }}
- name: Verify Xmake meets the minimum version
shell: bash
env:
XMAKE_MIN_VERSION: ${{ inputs.minimum-version }}
run: bash "$GITHUB_ACTION_PATH/check-version.sh" --verify
@@ -0,0 +1,98 @@
#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C
verify=false
if [[ $# -eq 1 && "$1" == "--verify" ]]; then
verify=true
elif [[ $# -ne 0 ]]; then
echo "Usage: $0 [--verify]" >&2
exit 2
fi
minimum_version="${XMAKE_MIN_VERSION:-}"
if [[ ! "$minimum_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "XMAKE_MIN_VERSION must be a numeric major.minor.patch version, for example 3.1.1." >&2
exit 2
fi
version_at_least() {
local -a installed_parts required_parts
local index installed_component required_component
IFS=. read -r -a installed_parts <<< "$1"
IFS=. read -r -a required_parts <<< "$2"
for index in 0 1 2; do
installed_component="${installed_parts[$index]}"
required_component="${required_parts[$index]}"
# Strip leading zeroes and compare lengths first, avoiding both octal
# interpretation and integer overflow in Bash's arithmetic expansion.
while [[ ${#installed_component} -gt 1 && "$installed_component" == 0* ]]; do
installed_component="${installed_component#0}"
done
while [[ ${#required_component} -gt 1 && "$required_component" == 0* ]]; do
required_component="${required_component#0}"
done
if [[ ${#installed_component} -gt ${#required_component} ]]; then
return 0
elif [[ ${#installed_component} -lt ${#required_component} ]]; then
return 1
elif [[ "$installed_component" > "$required_component" ]]; then
return 0
elif [[ "$installed_component" < "$required_component" ]]; then
return 1
fi
done
return 0
}
detected_version=""
needs_upgrade=true
reason="xmake was not found on PATH"
if command -v xmake >/dev/null 2>&1; then
if version_output="$(xmake --root --version 2>&1)"; then
# Xmake can color individual words and version components. Strip ANSI
# CSI sequences before matching, using only Bash 3.2 builtins.
ansi_pattern=$'\033''\[[0-?]*[ -/]*[@-~]'
while [[ "$version_output" =~ $ansi_pattern ]]; do
version_output="${version_output//"${BASH_REMATCH[0]}"/}"
done
version_pattern='(^|[[:space:]])xmake[[:space:]]+v([0-9]+\.[0-9]+\.[0-9]+)([+][[:alnum:].-]+)?([,[:space:]]|$)'
while IFS= read -r line; do
if [[ "$line" =~ $version_pattern ]]; then
detected_version="${BASH_REMATCH[2]}"
break
fi
done <<< "$version_output"
if [[ -z "$detected_version" ]]; then
reason="xmake --root --version did not report a recognizable Xmake version"
elif version_at_least "$detected_version" "$minimum_version"; then
needs_upgrade=false
else
reason="installed Xmake $detected_version is below $minimum_version"
fi
else
reason="xmake --root --version failed with exit code $?"
fi
fi
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
{
printf 'needs-upgrade=%s\n' "$needs_upgrade"
printf 'detected-version=%s\n' "$detected_version"
} >> "$GITHUB_OUTPUT"
else
printf 'needs-upgrade=%s\n' "$needs_upgrade"
printf 'detected-version=%s\n' "$detected_version"
fi
if [[ "$needs_upgrade" == true ]]; then
if [[ "$verify" == true ]]; then
echo "Xmake verification failed: $reason. Ensure Xmake $minimum_version or newer is installed and on PATH." >&2
exit 1
fi
echo "Xmake upgrade required: $reason. Install Xmake $minimum_version or newer."
else
echo "Keeping installed Xmake $detected_version; it meets the minimum version $minimum_version."
fi
+157
View File
@@ -0,0 +1,157 @@
"""Exercise CI Xmake selection with isolated executable and Actions-output mocks."""
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
SCRIPT = Path(__file__).resolve().parents[1] / "actions/ensure-xmake/check-version.sh"
BASH = shutil.which("bash")
@unittest.skipUnless(BASH, "Bash is required to exercise the CI version check")
class XmakeVersionCheckTest(unittest.TestCase):
def check(self, version_output=None, *, minimum="3.1.1", status=0, arguments=()):
with tempfile.TemporaryDirectory(prefix="crossdesk-xmake-check-") as directory:
root = Path(directory)
output_file = root / "github-output"
output_file.write_text("existing-output=preserved\n", encoding="utf-8")
arguments_file = root / "arguments"
if version_output is not None:
executable = root / "xmake"
executable.write_text(
"#!/bin/sh\n"
'printf "%s\\n" "$@" > "$XMAKE_TEST_ARGUMENTS"\n'
'printf "%s" "$XMAKE_TEST_OUTPUT"\n'
'exit "$XMAKE_TEST_STATUS"\n',
encoding="utf-8",
)
executable.chmod(0o755)
environment = {
**os.environ,
# The checker only needs shell builtins. Isolating PATH also
# guarantees that missing-tool tests cannot find a real xmake.
"PATH": str(root),
"GITHUB_OUTPUT": str(output_file),
"XMAKE_TEST_ARGUMENTS": str(arguments_file),
"XMAKE_TEST_OUTPUT": version_output or "",
"XMAKE_TEST_STATUS": str(status),
}
if minimum is None:
environment.pop("XMAKE_MIN_VERSION", None)
else:
environment["XMAKE_MIN_VERSION"] = minimum
result = subprocess.run(
[BASH, str(SCRIPT), *arguments],
env=environment,
capture_output=True,
text=True,
check=False,
)
outputs = dict(
line.split("=", 1)
for line in output_file.read_text(encoding="utf-8").splitlines()
)
invocation = (
arguments_file.read_text(encoding="utf-8").splitlines()
if arguments_file.exists() else None
)
self.assertEqual(outputs["existing-output"], "preserved")
return result, outputs, invocation
def test_keeps_minimum_and_newer_versions(self):
for version in ("3.1.1", "3.1.2", "3.1.10", "3.2.0", "3.10.0", "4.0.0"):
with self.subTest(version=version):
result, outputs, invocation = self.check(f"xmake v{version}\n")
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(outputs["needs-upgrade"], "false")
self.assertEqual(outputs["detected-version"], version)
self.assertEqual(invocation, ["--root", "--version"])
self.assertIn("Keeping installed Xmake", result.stdout)
def test_older_versions_require_upgrade(self):
for version in ("3.1.0", "3.0.99", "2.99.99"):
with self.subTest(version=version):
result, outputs, _ = self.check(f"xmake v{version}\n")
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(outputs["needs-upgrade"], "true")
self.assertIn("Install Xmake 3.1.1 or newer", result.stdout)
def test_components_are_compared_numerically(self):
for installed, minimum, expected in (
("3.1.10", "3.1.9", "false"),
("3.1.9", "3.1.10", "true"),
("3.2.0", "3.1.99", "false"),
("3.1.99", "3.2.0", "true"),
("3.01.009", "3.1.9", "false"),
("3.1.999999999999999999999", "3.1.9", "false"),
):
with self.subTest(installed=installed, minimum=minimum):
result, outputs, _ = self.check(f"xmake v{installed}", minimum=minimum)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(outputs["needs-upgrade"], expected)
def test_parses_ansi_colors_build_suffix_and_windows_line_endings(self):
result, outputs, _ = self.check(
"Xmake banner\r\n"
"\x1b[1;38;5;10mxmake\x1b[0m v\x1b[1m3.1.10\x1b[0m+HEAD.abcdef, A build utility\r\n"
)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(outputs["needs-upgrade"], "false")
self.assertEqual(outputs["detected-version"], "3.1.10")
def test_missing_broken_and_unrecognizable_tools_require_upgrade(self):
cases = (
(None, 0),
("xmake v9.9.9\n", 17),
("", 0),
("xmake version unknown\n", 0),
("xmake v3.1\n", 0),
("xmake v3.1.1invalid\n", 0),
("xmake v3.1.1-rc.1\n", 0),
("otherxmake v9.9.9\n", 0),
)
for output, status in cases:
with self.subTest(output=output, status=status):
result, outputs, _ = self.check(output, status=status)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(outputs["needs-upgrade"], "true")
self.assertEqual(outputs["detected-version"], "")
self.assertIn("Install Xmake 3.1.1 or newer", result.stdout)
def test_verification_fails_when_install_did_not_provide_required_version(self):
for output, status in ((None, 0), ("xmake v3.1.0", 0), ("unknown", 0), ("xmake v3.1.1", 5)):
with self.subTest(output=output, status=status):
result, outputs, _ = self.check(output, status=status, arguments=("--verify",))
self.assertEqual(result.returncode, 1)
self.assertEqual(outputs["needs-upgrade"], "true")
self.assertIn("Xmake verification failed", result.stderr)
self.assertIn("3.1.1 or newer", result.stderr)
def test_verification_accepts_newer_version(self):
result, outputs, _ = self.check("xmake v4.0.0", arguments=("--verify",))
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(outputs["needs-upgrade"], "false")
def test_invalid_minimum_fails_before_running_xmake(self):
for minimum in (None, "", "3.1", "v3.1.1", "3.1.1+HEAD", "3.1.1\n", "3.a.1"):
with self.subTest(minimum=minimum):
result, outputs, invocation = self.check("xmake v3.1.1", minimum=minimum)
self.assertEqual(result.returncode, 2)
self.assertNotIn("needs-upgrade", outputs)
self.assertIsNone(invocation)
self.assertIn("XMAKE_MIN_VERSION", result.stderr)
def test_unknown_arguments_fail_before_running_xmake(self):
result, outputs, invocation = self.check("xmake v3.1.1", arguments=("--install",))
self.assertEqual(result.returncode, 2)
self.assertNotIn("needs-upgrade", outputs)
self.assertIsNone(invocation)
self.assertIn("Usage:", result.stderr)
if __name__ == "__main__":
unittest.main()
+35 -19
View File
@@ -27,6 +27,7 @@ permissions:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
XMAKE_MIN_VERSION: "3.1.1"
jobs:
build-linux:
@@ -37,12 +38,13 @@ jobs:
include:
- arch: amd64
runner: ubuntu-24.04
image: crossdesk/ubuntu20.04:buildenv-cuda12.6.3-gcc10-cmake3.31.6-pipewire0.3.48-xmake3.1.1-rust1.92.0
# Keep using the published dependency image; upgrade Xmake if needed.
image: crossdesk/ubuntu20.04:buildenv-cuda12.6.3-gcc10-cmake3.31.6-pipewire0.3.48-xmake3.0.9-rust1.92.0
package_script: ./apps/desktop/scripts/linux/pkg_amd64.sh
- arch: arm64
runner: ubuntu-24.04-arm
image: crossdesk/ubuntu20.04:buildenv-cuda12.6.3-gcc10-cmake3.31.6-pipewire0.3.48-xmake3.1.1-rust1.92.0
image: crossdesk/ubuntu20.04:buildenv-cuda12.6.3-gcc10-cmake3.31.6-pipewire0.3.48-xmake3.0.9-rust1.92.0
package_script: ./apps/desktop/scripts/linux/pkg_arm64.sh
container:
@@ -108,8 +110,16 @@ jobs:
with:
submodules: recursive
- name: Ensure minimum Xmake version
uses: ./.github/actions/ensure-xmake
with:
minimum-version: ${{ env.XMAKE_MIN_VERSION }}
- name: Verify Linux build image
run: verify-crossdesk-build-image
shell: bash
env:
EXPECTED_ARCH: ${{ matrix.arch }}
run: bash docker/linux-build/verify-build-image.sh
- name: Ensure Wayland build dependencies
shell: bash
@@ -264,18 +274,20 @@ jobs:
restore-keys: |
${{ runner.os }}-xmake-deps-${{ matrix.cache-key }}-
- name: Install xmake
run: brew install xmake
- name: Update xmake repositories
run: xmake repo -u
- name: Checkout code
uses: actions/checkout@v5
- name: Initialize submodules
run: git submodule update --init --recursive
- name: Ensure minimum Xmake version
uses: ./.github/actions/ensure-xmake
with:
minimum-version: ${{ env.XMAKE_MIN_VERSION }}
- name: Update xmake repositories
run: xmake repo -u
- name: Build CrossDesk
run: |
xmake f --target_minver=${MACOSX_DEPLOYMENT_TARGET} --CROSSDESK_VERSION=${VERSION_NUM} --USE_CUDA=false -vyD
@@ -384,13 +396,23 @@ jobs:
restore-keys: |
${{ runner.os }}-xmake-deps-intel-
- name: Install xmake
- name: Checkout code
uses: actions/checkout@v5
- name: Initialize submodules
run: git submodule update --init --recursive
- name: Ensure minimum Xmake version
uses: ./.github/actions/ensure-xmake
with:
minimum-version: ${{ env.XMAKE_MIN_VERSION }}
- name: Prepare CUDA dependencies
working-directory: ${{ runner.temp }}
run: |
Invoke-Expression (Invoke-Webrequest 'https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.ps1' -UseBasicParsing).Content
echo "C:\Users\runneradmin\xmake" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
xmake repo -u
xmake create cuda
Set-Location cuda
Push-Location cuda
xmake g --theme=plain
$cudaPath = ""
$packagesPath = "D:\xmake_global\.xmake\packages"
@@ -429,12 +451,6 @@ jobs:
Write-Host "Copying $source to $target"
Copy-Item $source $target -Force
- name: Checkout code
uses: actions/checkout@v5
- name: Initialize submodules
run: git submodule update --init --recursive
- name: Copy nsProcess plugin to NSIS folder
run: |
$nsisPluginDir = "C:\Program Files (x86)\NSIS\Plugins\x86-unicode"
@@ -79,7 +79,7 @@ bool CheckTask(ITaskFolder* folder, BSTR task_name,
if (FAILED(task->get_Definition(definition.GetAddressOf()))) return false;
ComPtr<IPrincipal> principal;
if (FAILED(definition->get_Principal(principal.GetAddressOf()))) return false;
TASK_RUNLEVEL level = TASK_RUNLEVEL_LUA;
TASK_RUNLEVEL_TYPE level = TASK_RUNLEVEL_LUA;
TASK_LOGON_TYPE logon = TASK_LOGON_NONE;
bool ok = Expect(SUCCEEDED(principal->get_RunLevel(&level)) &&
level == TASK_RUNLEVEL_HIGHEST,
+9 -2
View File
@@ -8,6 +8,13 @@ PipeWire 0.3/SPA 头文件 SDK)以及根据项目
Xmake 最低支持 3.1.1,与移动端一致,构建环境校验允许更新版本。发布镜像的工具链 tag
记录实际安装的版本;默认使用最低支持版本验证构建。
常规应用构建目前复用已发布的 `xmake3.0.9` 工具链镜像,并在容器启动后检查
Xmake 版本:缺失或低于 3.1.1 时安装或升级,达标时保留现有版本,随后再次校验。
Linux、macOS 和 Windows 桌面构建共用 `.github/actions/ensure-xmake`;Linux 还会
执行仓库中的 `verify-build-image.sh` 校验环境。这样既保留预编译
依赖缓存,也不依赖尚未发布的 `xmake3.1.1` 镜像。切换镜像 tag 前必须确认
amd64 和 arm64 变体均已发布。
## 发布方式
推送影响镜像的文件到 `ci/linux-build-image` 分支时,
@@ -47,5 +54,5 @@ Ubuntu 20.04 仓库只提供 PipeWire 0.2。镜像通过
## 更新依赖
修改 Dockerfile、xmake 清单、本地包配方或子模块中的包配方后,推送该分支即可。
升级工具链时应同时修改 Dockerfile 的版本参数、工作流中的 `STACK_TAG`,以及
常规构建工作流引用的镜像 tag。
升级工具链时应先修改 Dockerfile 的版本参数和镜像工作流中的 `STACK_TAG`,
等两个架构的镜像发布成功后,再修改常规构建工作流引用的镜像 tag。