#!/usr/bin/env bash # 前端构建和 CSS 检查,带依赖缓存:node_modules 按「锁文件 + package.json」的哈希缓存起来,同样的依赖不再 npm ci(约 20 秒); # 展示端和管理端并行构建。缓存里放的是硬链接副本(cp -al),恢复几乎是瞬间;依赖变了才重新安装并更新缓存,只留最近 3 份。 set -euo pipefail root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cache="${MOSHIRO_CI_CACHE:-/home/justme/woodpecker/cache/web-node-modules}" export NPM_CONFIG_CACHE="${NPM_CONFIG_CACHE:-/home/justme/.npm}" web="$root/web" dirs=(node_modules display/node_modules admin/node_modules) mkdir -p "$cache" exec 9> "$cache/.lock" # 开发构建会把版本号写进 package.json;缓存键里去掉 version 行,否则每个提交都是新的键,缓存永远命中不了 key="$(cat "$web/package-lock.json" "$web/package.json" "$web/display/package.json" "$web/admin/package.json" | grep -v '^ "version":' | sha256sum | cut -c1-16)" restore() { [[ -d "$cache/$key/node_modules" ]] || return 1 local d for d in "${dirs[@]}"; do [[ -d "$cache/$key/$d" ]] || continue mkdir -p "$(dirname "$web/$d")" cp -al "$cache/$key/$d" "$web/$d" 2>/dev/null || cp -a "$cache/$key/$d" "$web/$d" done } save() { local tmp="$cache/$key.tmp.$$" d rm -rf "$tmp" for d in "${dirs[@]}"; do [[ -d "$web/$d" ]] || continue mkdir -p "$(dirname "$tmp/$d")" cp -al "$web/$d" "$tmp/$d" 2>/dev/null || cp -a "$web/$d" "$tmp/$d" done rm -rf "$cache/$key" mv "$tmp" "$cache/$key" # 只留最近用过的 3 份 (cd "$cache" && ls -1t | grep -v '\.tmp\.' | tail -n +4 | xargs -r rm -rf) } # 并行的 workflow(web-build 和开发镜像里的前端构建)会同时读写这份缓存:恢复和保存期间持锁 flock -w 600 9 || { echo "could not get the node_modules cache lock" >&2; exit 1; } if restore; then echo "restored node_modules from the cache ($key)" else echo "no cache for $key; running npm ci" rm -rf "$web/node_modules" "$web/display/node_modules" "$web/admin/node_modules" npm ci --prefix "$web" --prefer-offline save || echo "could not save the node_modules cache" >&2 fi flock -u 9 # 展示端和管理端并行构建,再检查 CSS pids=() (cd "$web" && timeout 600 npm run build -w display) & pids+=($!) (cd "$web" && timeout 600 npm run build -w admin) & pids+=($!) failed=0 for pid in "${pids[@]}"; do wait "$pid" || failed=1; done [[ "$failed" == 0 ]] || { echo "web build failed" >&2; exit 1; } [[ -n "${WEB_SKIP_LINT:-}" ]] || npm run lint:css --prefix "$web" # 开发构建(main 上带 [CI] 的提交):前端是这条流水线的唯一一份,放进共享目录,开发镜像的 Backend 直接用(见 ci/shared-web.sh) [[ -z "${WEB_PUBLISH_SHARED:-}" ]] || bash "$root/ci/shared-web.sh" publish