🐛 fix(workflow): manage Node versions with fnm
This commit is contained in:
@@ -26,6 +26,8 @@ env:
|
|||||||
SOURCE_SHA: ${{ github.event.workflow_run.head_sha }}
|
SOURCE_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||||
SOURCE_EVENT: ${{ github.event.workflow_run.event }}
|
SOURCE_EVENT: ${{ github.event.workflow_run.event }}
|
||||||
COMMIT_LINT_REQUIRE_EMOJI: "1"
|
COMMIT_LINT_REQUIRE_EMOJI: "1"
|
||||||
|
FNM_BIN: "/data/bin/fnm"
|
||||||
|
FNM_DIR: "/data/fnm"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
checks:
|
checks:
|
||||||
@@ -131,6 +133,29 @@ jobs:
|
|||||||
echo "========================================"
|
echo "========================================"
|
||||||
|
|
||||||
cd "$REPO_DIR"
|
cd "$REPO_DIR"
|
||||||
|
node_version=$(tr -d '[:space:]' < .node-version)
|
||||||
|
if ! [[ "$node_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo ".node-version 必须是完整的 Node.js 版本号: $node_version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ ! -x "$FNM_BIN" ]; then
|
||||||
|
echo "Prepare 未安装 fnm: $FNM_BIN" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval "$("$FNM_BIN" env --shell bash)"
|
||||||
|
exec 9>"${FNM_DIR}.install.lock"
|
||||||
|
flock 9
|
||||||
|
"$FNM_BIN" use --install-if-missing "$node_version"
|
||||||
|
flock -u 9
|
||||||
|
|
||||||
|
if [ "$(node --version)" != "v$node_version" ]; then
|
||||||
|
echo "Node.js 切换后版本不符合要求: $(node --version)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
node --version
|
||||||
|
npm --version
|
||||||
|
|
||||||
echo "📦 安装锁定的 Node.js 校验工具"
|
echo "📦 安装锁定的 Node.js 校验工具"
|
||||||
npm ci --ignore-scripts --no-audit --no-fund --no-bin-links
|
npm ci --ignore-scripts --no-audit --no-fund --no-bin-links
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ concurrency:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
ACCESS_TOKEN: ${{ secrets.WORKFLOW }}
|
ACCESS_TOKEN: ${{ secrets.WORKFLOW }}
|
||||||
|
FNM_ARCHIVE_SHA256: "7807664f39d39fc518da1c35ba0181e4b3267603c4b1dedeb4b5fc6ae440a224"
|
||||||
|
FNM_BIN: "/data/bin/fnm"
|
||||||
|
FNM_DIR: "/data/fnm"
|
||||||
|
FNM_VERSION: "1.39.0"
|
||||||
WORKSPACE_ROOT: "/data/workspace"
|
WORKSPACE_ROOT: "/data/workspace"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -45,10 +49,9 @@ jobs:
|
|||||||
[curl]="curl"
|
[curl]="curl"
|
||||||
[flock]="util-linux"
|
[flock]="util-linux"
|
||||||
[realpath]="coreutils"
|
[realpath]="coreutils"
|
||||||
[node]="nodejs"
|
[unzip]="unzip"
|
||||||
[npm]="npm"
|
|
||||||
)
|
)
|
||||||
required_commands=(git python3 python3.11 curl flock realpath node npm)
|
required_commands=(git python3 python3.11 curl flock realpath unzip)
|
||||||
missing_commands=()
|
missing_commands=()
|
||||||
missing_packages=()
|
missing_packages=()
|
||||||
declare -A seen_packages=()
|
declare -A seen_packages=()
|
||||||
@@ -94,6 +97,34 @@ jobs:
|
|||||||
|
|
||||||
python3.11 -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 11) else "Python 3.11 or newer is required")'
|
python3.11 -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 11) else "Python 3.11 or newer is required")'
|
||||||
|
|
||||||
|
fnm_installed=false
|
||||||
|
expected_fnm_version="fnm $FNM_VERSION"
|
||||||
|
current_fnm_version=""
|
||||||
|
if [ -x "$FNM_BIN" ]; then
|
||||||
|
current_fnm_version=$($FNM_BIN --version 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$current_fnm_version" != "$expected_fnm_version" ]; then
|
||||||
|
echo "准备安装 $expected_fnm_version"
|
||||||
|
mkdir -p "$(dirname "$FNM_BIN")"
|
||||||
|
fnm_tmp_dir=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$fnm_tmp_dir"' EXIT
|
||||||
|
fnm_archive="$fnm_tmp_dir/fnm-linux.zip"
|
||||||
|
curl -fsSL --retry 5 --retry-all-errors \
|
||||||
|
"https://github.com/Schniz/fnm/releases/download/v${FNM_VERSION}/fnm-linux.zip" \
|
||||||
|
-o "$fnm_archive"
|
||||||
|
printf '%s %s\n' "$FNM_ARCHIVE_SHA256" "$fnm_archive" | sha256sum -c -
|
||||||
|
unzip -q "$fnm_archive" -d "$fnm_tmp_dir"
|
||||||
|
install -m 0755 "$fnm_tmp_dir/fnm" "${FNM_BIN}.new"
|
||||||
|
mv -f "${FNM_BIN}.new" "$FNM_BIN"
|
||||||
|
fnm_installed=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$($FNM_BIN --version)" != "$expected_fnm_version" ]; then
|
||||||
|
echo "fnm 安装后版本不符合要求: $($FNM_BIN --version 2>&1)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
{
|
{
|
||||||
echo "## Prepare 环境"
|
echo "## Prepare 环境"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -105,12 +136,18 @@ jobs:
|
|||||||
version_line=${version_line//|/\\|}
|
version_line=${version_line//|/\\|}
|
||||||
printf '| `%s` | `%s` |\n' "$command" "$version_line"
|
printf '| `%s` | `%s` |\n' "$command" "$version_line"
|
||||||
done
|
done
|
||||||
|
printf '| `%s` | `%s` |\n' "fnm" "$($FNM_BIN --version)"
|
||||||
echo ""
|
echo ""
|
||||||
if [ "$installed" = true ]; then
|
if [ "$installed" = true ]; then
|
||||||
echo "安装的包:\`${missing_packages[*]}\`"
|
echo "安装的包:\`${missing_packages[*]}\`"
|
||||||
else
|
else
|
||||||
echo "安装的包:无,所有命令已存在。"
|
echo "安装的包:无,所有命令已存在。"
|
||||||
fi
|
fi
|
||||||
|
if [ "$fnm_installed" = true ]; then
|
||||||
|
echo "安装的版本管理器:\`$expected_fnm_version\`"
|
||||||
|
else
|
||||||
|
echo "安装的版本管理器:无,\`$expected_fnm_version\` 已存在。"
|
||||||
|
fi
|
||||||
} >> "$GITHUB_STEP_SUMMARY"
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|
||||||
echo "installed=$installed" >> "$GITHUB_OUTPUT"
|
echo "installed=$installed" >> "$GITHUB_OUTPUT"
|
||||||
@@ -205,6 +242,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
resolved_sha=$(git --git-dir="$REPOSITORY_DIR" rev-parse "${HEAD_SHA}^{commit}")
|
resolved_sha=$(git --git-dir="$REPOSITORY_DIR" rev-parse "${HEAD_SHA}^{commit}")
|
||||||
|
echo "PREPARED_REPOSITORY_DIR=$REPOSITORY_DIR" >> "$GITHUB_ENV"
|
||||||
|
echo "PREPARED_SHA=$resolved_sha" >> "$GITHUB_ENV"
|
||||||
{
|
{
|
||||||
echo ""
|
echo ""
|
||||||
echo "## Prepare 仓库"
|
echo "## Prepare 仓库"
|
||||||
@@ -215,3 +254,42 @@ jobs:
|
|||||||
} >> "$GITHUB_STEP_SUMMARY"
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|
||||||
echo "Prepare 完成: $REPOSITORY_DIR @ $resolved_sha"
|
echo "Prepare 完成: $REPOSITORY_DIR @ $resolved_sha"
|
||||||
|
|
||||||
|
- name: 🟢 准备 Node.js
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
node_version=$(git --git-dir="$PREPARED_REPOSITORY_DIR" show "${PREPARED_SHA}:.node-version" | tr -d '[:space:]')
|
||||||
|
if ! [[ "$node_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo ".node-version 必须是完整的 Node.js 版本号: $node_version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
node_major=${node_version%%.*}
|
||||||
|
if [ "$node_major" -lt 20 ]; then
|
||||||
|
echo "Node.js 版本必须不低于 20: $node_version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$FNM_DIR"
|
||||||
|
exec 9>"${FNM_DIR}.install.lock"
|
||||||
|
flock 9
|
||||||
|
"$FNM_BIN" install "$node_version"
|
||||||
|
flock -u 9
|
||||||
|
|
||||||
|
actual_node_version=$($FNM_BIN exec --using="$node_version" node --version)
|
||||||
|
actual_npm_version=$($FNM_BIN exec --using="$node_version" npm --version)
|
||||||
|
if [ "$actual_node_version" != "v$node_version" ]; then
|
||||||
|
echo "Node.js 安装后版本不符合要求: $actual_node_version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
|
echo ""
|
||||||
|
echo "## Prepare Node.js"
|
||||||
|
echo ""
|
||||||
|
echo "- fnm:\`$($FNM_BIN --version)\`"
|
||||||
|
echo "- Node.js:\`$actual_node_version\`"
|
||||||
|
echo "- npm:\`$actual_npm_version\`"
|
||||||
|
echo "- 缓存目录:\`$FNM_DIR\`"
|
||||||
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
24.19.0
|
||||||
Generated
+3
@@ -5,6 +5,9 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "playbook",
|
"name": "playbook",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"markdownlint-cli": "0.48.0",
|
"markdownlint-cli": "0.48.0",
|
||||||
"prettier": "3.9.6"
|
"prettier": "3.9.6"
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "playbook",
|
"name": "playbook",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"markdownlint-cli": "0.48.0",
|
"markdownlint-cli": "0.48.0",
|
||||||
"prettier": "3.9.6"
|
"prettier": "3.9.6"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ LOOKUP_PATH = SKILL_ROOT / "scripts" / "lookup.py"
|
|||||||
SKILL_PATH = SKILL_ROOT / "SKILL.md"
|
SKILL_PATH = SKILL_ROOT / "SKILL.md"
|
||||||
CI_PATH = ROOT / ".gitea" / "workflows" / "checks.yml"
|
CI_PATH = ROOT / ".gitea" / "workflows" / "checks.yml"
|
||||||
PREPARE_PATH = ROOT / ".gitea" / "workflows" / "prepare.yml"
|
PREPARE_PATH = ROOT / ".gitea" / "workflows" / "prepare.yml"
|
||||||
|
NODE_VERSION_PATH = ROOT / ".node-version"
|
||||||
|
|
||||||
|
|
||||||
def load_lookup_module():
|
def load_lookup_module():
|
||||||
@@ -445,8 +446,18 @@ class TslSyntaxReferenceTests(unittest.TestCase):
|
|||||||
"node_modules/prettier/bin/prettier.cjs --check skills/tsl-syntax-reference",
|
"node_modules/prettier/bin/prettier.cjs --check skills/tsl-syntax-reference",
|
||||||
workflow,
|
workflow,
|
||||||
)
|
)
|
||||||
self.assertIn("[node]=\"nodejs\"", prepare)
|
self.assertIn("[unzip]=\"unzip\"", prepare)
|
||||||
self.assertIn("[npm]=\"npm\"", prepare)
|
self.assertIn('FNM_BIN: "/data/bin/fnm"', prepare)
|
||||||
|
self.assertIn('FNM_DIR: "/data/fnm"', prepare)
|
||||||
|
self.assertIn('"$FNM_BIN" install "$node_version"', prepare)
|
||||||
|
self.assertNotIn('[node]="nodejs"', prepare)
|
||||||
|
self.assertNotIn('[npm]="npm"', prepare)
|
||||||
|
self.assertIn('"$FNM_BIN" use --install-if-missing "$node_version"', workflow)
|
||||||
|
self.assertIn("node --version", workflow)
|
||||||
|
self.assertRegex(
|
||||||
|
NODE_VERSION_PATH.read_text(encoding="utf-8").strip(),
|
||||||
|
r"^\d+\.\d+\.\d+$",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user