Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f9dda3d617 | |||
| 83a3d072fc | |||
| 7f8193fbc6 |
@@ -146,7 +146,7 @@ jobs:
|
||||
shell: sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: "set -eu\nif command -v git >/dev/null 2>&1; then\n if [ ! -d .git ]; then\n git init -q\n git config user.email \"ci@localhost\"\n git config user.name \"CI\"\n git add .\n git commit -q -m \"current\"\n REPO_URL=\"https://x-access-token:${GITHUB_TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git\"\n git remote add origin \"$REPO_URL\"\n fi\n git fetch origin main --depth=1 -q 2>/dev/null || echo \"WARN: cannot fetch main, will check all migrations\"\nelse\n echo \"WARN: git not available, will check all migrations\"\nfi\n"
|
||||
run: "set +e\nif command -v git >/dev/null 2>&1; then\n if [ ! -d .git ]; then\n git init -q\n git config user.email \"ci@localhost\"\n git config user.name \"CI\"\n git add .\n git commit -q -m \"current\"\n REPO_URL=\"https://x-access-token:${GITHUB_TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git\"\n git remote add origin \"$REPO_URL\"\n fi\n git fetch origin main --depth=1 -q 2>/dev/null || echo \"WARN: cannot fetch main, will check all migrations\"\nelse\n echo \"WARN: git not available, will check all migrations\"\nfi\nexit 0\n"
|
||||
- name: Check migration safety
|
||||
shell: sh
|
||||
run: "set -eu\nif git rev-parse origin/main >/dev/null 2>&1; then\n python3 scripts/check_migration_safety.py --allow-medium-risk --diff-against origin/main\nelse\n python3 scripts/check_migration_safety.py --allow-medium-risk\nfi\n"
|
||||
@@ -375,7 +375,7 @@ jobs:
|
||||
'
|
||||
- name: Install dependencies
|
||||
shell: sh
|
||||
run: "set -eu\nNPM_CACHE_VOLUME=\"xiaoxia-npm-cache\"\nif ! docker volume inspect \"$NPM_CACHE_VOLUME\" >/dev/null 2>&1; then\n docker volume create \"$NPM_CACHE_VOLUME\" >/dev/null\n echo \"Created npm cache volume: $NPM_CACHE_VOLUME\"\nfi\n\ndocker run --rm \\\n -v \"$PWD:/workspace\" \\\n -v \"$NPM_CACHE_VOLUME:/workspace/apps/web/node_modules\" -w /workspace/apps/web \\\n docker.m.daocloud.io/library/node:20 \\\n sh -lc 'PACKAGE_LOCK_HASH=$(md5sum package-lock.json 2>/dev/null | cut -d\" \" -f1)\nCACHE_HASH_FILE=\"node_modules/.package-lock-hash\"\nCACHE_VALID=false\nif [ -f \"$CACHE_HASH_FILE\" ] && [ \"$(cat \"$CACHE_HASH_FILE\")\" = \"$PACKAGE_LOCK_HASH\" ] && [ -x \"node_modules/.bin/eslint\" ]; then\n CACHE_VALID=true\n echo \"Cache hit: dependencies valid, skipping npm ci\"\nfi\nif [ \"$CACHE_VALID\" = \"false\" ]; then\n echo \"Cache miss or invalid: running npm ci...\"\n if ! npm ci --include=dev; then\n echo \"npm ci failed, cleaning node_modules and retrying...\"\n rm -rf node_modules\n mkdir -p node_modules\n npm ci --include=dev\n fi\n echo \"$PACKAGE_LOCK_HASH\" > \"$CACHE_HASH_FILE\"\n echo \"Dependencies installed, cache updated\"\nfi'\n"
|
||||
run: "set -eu\nNPM_CACHE_VOLUME=\"xiaoxia-npm-cache\"\nif ! docker volume inspect \"$NPM_CACHE_VOLUME\" >/dev/null 2>&1; then\n docker volume create \"$NPM_CACHE_VOLUME\" >/dev/null\n echo \"Created npm cache volume: $NPM_CACHE_VOLUME\"\nfi\n\ndocker run --rm \\\n -v \"$PWD:/workspace\" \\\n -v \"$NPM_CACHE_VOLUME:/workspace/apps/web/node_modules\" -w /workspace/apps/web \\\n docker.m.daocloud.io/library/node:20 \\\n sh -lc 'PACKAGE_LOCK_HASH=$(md5sum package-lock.json 2>/dev/null | cut -d\" \" -f1)\nCACHE_HASH_FILE=\"node_modules/.package-lock-hash\"\nCACHE_VALID=false\nif [ -f \"$CACHE_HASH_FILE\" ] && [ \"$(cat \"$CACHE_HASH_FILE\")\" = \"$PACKAGE_LOCK_HASH\" ] && [ -x \"node_modules/.bin/eslint\" ] && [ -x \"node_modules/.bin/tsc\" ] && [ -x \"node_modules/.bin/prettier\" ] && [ -x \"node_modules/.bin/vitest\" ]; then\n CACHE_VALID=true\n echo \"Cache hit: dependencies valid, skipping npm ci\"\nfi\nif [ \"$CACHE_VALID\" = \"false\" ]; then\n echo \"Cache miss or invalid: running npm ci...\"\n if ! npm ci --include=dev; then\n echo \"npm ci failed, cleaning node_modules and retrying...\"\n rm -rf node_modules\n mkdir -p node_modules\n npm ci --include=dev\n fi\n # Post-install integrity check: verify all critical tools exist\n if [ ! -x \"node_modules/.bin/eslint\" ] || [ ! -x \"node_modules/.bin/tsc\" ] || [ ! -x \"node_modules/.bin/prettier\" ] || [ ! -x \"node_modules/.bin/vitest\" ]; then\n echo \"Post-install check failed: critical binaries missing, cleaning and retrying...\"\n rm -rf node_modules\n mkdir -p node_modules\n npm ci --include=dev\n fi\n echo \"$PACKAGE_LOCK_HASH\" > \"$CACHE_HASH_FILE\"\n echo \"Dependencies installed, cache updated\"\nfi'\n"
|
||||
- name: Run ESLint
|
||||
shell: sh
|
||||
run: "set -eu\nNPM_CACHE_VOLUME=\"xiaoxia-npm-cache\"\nif ! docker volume inspect \"$NPM_CACHE_VOLUME\" >/dev/null 2>&1; then\n docker volume create \"$NPM_CACHE_VOLUME\" >/dev/null\n echo \"Created npm cache volume: $NPM_CACHE_VOLUME\"\nfi\n\ndocker run --rm \\\n -v \"$PWD:/workspace\" \\\n -v \"$NPM_CACHE_VOLUME:/workspace/apps/web/node_modules\" -w /workspace/apps/web \\\n docker.m.daocloud.io/library/node:20 \\\n sh -lc 'npx --no-install eslint src --ext .ts,.tsx --max-warnings 50'\n"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/bin/bash
|
||||
# 通用Docker镜像构建+推送脚本(local cache为主 + registry cache兜底)
|
||||
# M-2优化:解决registry缓存导入慢(247s)和推送不稳定问题
|
||||
# 通用Docker镜像构建+推送脚本(构建与缓存解耦)
|
||||
# 用法: docker_build_push.sh <Dockerfile> <image_tag> <cache_ref> [build_arg...]
|
||||
set -eu
|
||||
|
||||
@@ -22,59 +21,40 @@ else
|
||||
fi
|
||||
docker buildx inspect --bootstrap
|
||||
|
||||
# 从cache_ref中提取缓存名称(如 api-cache:develop -> api-cache-develop)
|
||||
CACHE_NAME=$(echo "$CACHE_REF" | tr '/' '_' | tr ':' '-')
|
||||
LOCAL_CACHE_DIR="/tmp/buildx-cache/${CACHE_NAME}"
|
||||
|
||||
mkdir -p "$LOCAL_CACHE_DIR"
|
||||
|
||||
# 缓存源:local优先,registry兜底
|
||||
CACHE_FROM_LOCAL="type=local,src=${LOCAL_CACHE_DIR}"
|
||||
CACHE_FROM_REGISTRY="type=registry,ref=${CACHE_REF},ignore-error=true"
|
||||
|
||||
# 本地缓存目标(必选,mode=max最大化命中率)
|
||||
CACHE_TO_LOCAL="type=local,dest=${LOCAL_CACHE_DIR},mode=max"
|
||||
|
||||
echo "=== Step 1: Build & push image (local cache read-write + registry read) ==="
|
||||
echo "Local cache: ${LOCAL_CACHE_DIR}"
|
||||
echo "Registry cache: ${CACHE_REF}"
|
||||
echo ""
|
||||
CACHE_FROM="type=registry,ref=${CACHE_REF},ignore-error=true"
|
||||
|
||||
echo "=== Step 1: Build & push image (read-only cache) ==="
|
||||
docker buildx build \
|
||||
$BUILD_ARGS \
|
||||
--cache-from "${CACHE_FROM_LOCAL}" \
|
||||
--cache-from "${CACHE_FROM_REGISTRY}" \
|
||||
--cache-to "${CACHE_TO_LOCAL}" \
|
||||
--cache-from "${CACHE_FROM}" \
|
||||
-f "${DOCKERFILE}" \
|
||||
-t "${IMAGE_TAG}" \
|
||||
--push \
|
||||
.
|
||||
|
||||
echo ""
|
||||
echo "Image pushed: ${IMAGE_TAG}"
|
||||
echo "Local cache updated"
|
||||
|
||||
echo ""
|
||||
echo "=== Step 2: Sync registry cache (best effort, retries 3x) ==="
|
||||
CACHE_TO_REGISTRY="type=registry,ref=${CACHE_REF},mode=max,compression=zstd"
|
||||
echo "=== Step 2: Push cache (best effort, retries 3x) ==="
|
||||
CACHE_TO="type=registry,ref=${CACHE_REF},mode=max,compression=zstd"
|
||||
|
||||
MAX_RETRIES=3
|
||||
SUCCESS=0
|
||||
for attempt in $(seq 1 $MAX_RETRIES); do
|
||||
echo "Registry cache sync attempt $attempt/$MAX_RETRIES"
|
||||
echo "Cache push attempt $attempt/$MAX_RETRIES"
|
||||
if docker buildx build \
|
||||
$BUILD_ARGS \
|
||||
--cache-from "${CACHE_FROM_LOCAL}" \
|
||||
--cache-to "${CACHE_TO_REGISTRY}" \
|
||||
--cache-from "${CACHE_FROM}" \
|
||||
--cache-to "${CACHE_TO}" \
|
||||
-f "${DOCKERFILE}" \
|
||||
-t "${IMAGE_TAG}" \
|
||||
--push \
|
||||
.; then
|
||||
echo "Registry cache synced (attempt $attempt)"
|
||||
echo "Cache pushed (attempt $attempt)"
|
||||
SUCCESS=1
|
||||
break
|
||||
else
|
||||
echo "Registry cache sync failed (attempt $attempt)"
|
||||
echo "Cache push failed (attempt $attempt)"
|
||||
if [ $attempt -lt $MAX_RETRIES ]; then
|
||||
WAIT=$((attempt * 5))
|
||||
echo "Retrying in ${WAIT}s..."
|
||||
@@ -84,7 +64,7 @@ for attempt in $(seq 1 $MAX_RETRIES); do
|
||||
done
|
||||
|
||||
if [ $SUCCESS -eq 0 ]; then
|
||||
echo "WARNING: Registry cache sync failed after $MAX_RETRIES attempts (non-fatal, local cache still works)"
|
||||
echo "WARNING: Cache push failed after $MAX_RETRIES attempts (non-fatal)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
@@ -44,7 +44,7 @@ class TestEditTemplate:
|
||||
def test_create_empty_name_raises(self):
|
||||
try:
|
||||
EditTemplate.create(" ")
|
||||
assert False, "应该抛出 ValueError"
|
||||
raise AssertionError("应该抛出 ValueError")
|
||||
except ValueError as e:
|
||||
assert "模板名称不能为空" in str(e)
|
||||
|
||||
@@ -74,14 +74,14 @@ class TestEditPlan:
|
||||
def test_create_empty_name_raises(self):
|
||||
try:
|
||||
EditPlan.create("tpl-1", " ")
|
||||
assert False, "应该抛出 ValueError"
|
||||
raise AssertionError("应该抛出 ValueError")
|
||||
except ValueError as e:
|
||||
assert "计划名称不能为空" in str(e)
|
||||
|
||||
def test_create_empty_template_id_raises(self):
|
||||
try:
|
||||
EditPlan.create(" ", "test")
|
||||
assert False, "应该抛出 ValueError"
|
||||
raise AssertionError("应该抛出 ValueError")
|
||||
except ValueError as e:
|
||||
assert "template_id 不能为空" in str(e)
|
||||
|
||||
@@ -112,7 +112,7 @@ class TestEditPlan:
|
||||
p = EditPlan.create("tpl-1", "test")
|
||||
try:
|
||||
p.start_rendering() # draft → rendering 不合法
|
||||
assert False, "应该抛出 ValueError"
|
||||
raise AssertionError("应该抛出 ValueError")
|
||||
except ValueError as e:
|
||||
logger.warning(f"Operation failed in tests/unit/test_phase8_edit_models.py: {e}", exc_info=True)
|
||||
|
||||
@@ -120,7 +120,7 @@ class TestEditPlan:
|
||||
p = EditPlan.create("tpl-1", "test")
|
||||
try:
|
||||
p.mark_completed() # draft → completed 不合法
|
||||
assert False, "应该抛出 ValueError"
|
||||
raise AssertionError("应该抛出 ValueError")
|
||||
except ValueError as e:
|
||||
logger.warning(f"Operation failed in tests/unit/test_phase8_edit_models.py: {e}", exc_info=True)
|
||||
|
||||
@@ -128,7 +128,7 @@ class TestEditPlan:
|
||||
p = EditPlan.create("tpl-1", "test")
|
||||
try:
|
||||
p.reset_to_draft() # draft → draft 不合法
|
||||
assert False, "应该抛出 ValueError"
|
||||
raise AssertionError("应该抛出 ValueError")
|
||||
except ValueError as e:
|
||||
logger.warning(f"Operation failed in tests/unit/test_phase8_edit_models.py: {e}", exc_info=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user