232 lines
14 KiB
Bash
232 lines
14 KiB
Bash
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
REPO_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
|
|
|
|
fail() {
|
|
echo "FAIL: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
test_preset_compose_uses_env_for_instance() {
|
|
local file
|
|
|
|
for file in \
|
|
"${REPO_ROOT}/docker-runner/presets/standard-ubuntu-22/docker-compose.yml" \
|
|
"${REPO_ROOT}/docker-runner/presets/buildx-ubuntu-22/docker-compose.yml" \
|
|
"${REPO_ROOT}/docker-runner/presets/buildx-archlinux/docker-compose.yml"; do
|
|
grep -q 'GITEA_INSTANCE=${GITEA_INSTANCE}' "${file}" || fail "${file} should read GITEA_INSTANCE from env"
|
|
grep -q 'GITEA_TOKEN=${GITEA_TOKEN}' "${file}" || fail "${file} should read GITEA_TOKEN from env"
|
|
done
|
|
}
|
|
|
|
test_workflows_do_not_hardcode_company_server() {
|
|
! rg -q 'https://git\.mytsl\.cn' "${REPO_ROOT}/.gitea/workflows/changelog_and_release.yml" || fail "changelog workflow should not hardcode company server"
|
|
! rg -q 'https://git\.mytsl\.cn' "${REPO_ROOT}/.gitea/workflows/update_stats_badge.yaml" || fail "stats workflow should not hardcode company server"
|
|
}
|
|
|
|
test_stats_workflow_uses_workflow_secret_consistently() {
|
|
local file
|
|
|
|
file="${REPO_ROOT}/.gitea/workflows/update_stats_badge.yaml"
|
|
|
|
grep -q 'ACCESS_TOKEN: ${{ secrets.WORKFLOW }}' "${file}" || fail "stats workflow should read ACCESS_TOKEN from WORKFLOW secret"
|
|
! rg -q 'STATS_TOKEN' "${file}" || fail "stats workflow should not mention legacy STATS_TOKEN secret"
|
|
! rg -q 'GITHUB_TOKEN' "${file}" || fail "stats workflow should not mention GITHUB_TOKEN in token guidance"
|
|
}
|
|
|
|
test_workflow_docs_and_links_use_actual_paths() {
|
|
local workflow_doc stats_workflow release_workflow
|
|
|
|
workflow_doc="${REPO_ROOT}/WORKFLOW.md"
|
|
stats_workflow="${REPO_ROOT}/.gitea/workflows/update_stats_badge.yaml"
|
|
release_workflow="${REPO_ROOT}/.gitea/workflows/changelog_and_release.yml"
|
|
|
|
grep -q 'changelog_and_release.yml' "${workflow_doc}" || fail "WORKFLOW.md should mention changelog_and_release.yml"
|
|
grep -q 'update_stats_badge.yaml' "${workflow_doc}" || fail "WORKFLOW.md should mention update_stats_badge.yaml"
|
|
! rg -q 'changelog-and-release\.yml' "${workflow_doc}" || fail "WORKFLOW.md should not mention stale changelog-and-release.yml filename"
|
|
! rg -q 'update-stats-badge\.yml' "${workflow_doc}" || fail "WORKFLOW.md should not mention stale update-stats-badge.yml filename"
|
|
! rg -q 'update_stats_badge\.yml' "${workflow_doc}" || fail "WORKFLOW.md should not mention stale update_stats_badge.yml filename"
|
|
|
|
grep -q '/src/branch/${{ github.ref_name }}/.gitea/workflows/update_stats_badge.yaml' "${stats_workflow}" || fail "stats workflow summary should link to .gitea workflow path"
|
|
grep -q '/src/branch/${{ env.MAIN_BRANCH }}/.gitea/workflows/changelog_and_release.yml' "${release_workflow}" || fail "release workflow summary should link to .gitea workflow path"
|
|
! rg -q '/\\.github/workflows/' "${stats_workflow}" || fail "stats workflow should not link to .github/workflows"
|
|
! rg -q '/\\.github/workflows/' "${release_workflow}" || fail "release workflow should not link to .github/workflows"
|
|
}
|
|
|
|
test_workflow_doc_describes_workspace_architecture() {
|
|
local file
|
|
|
|
file="${REPO_ROOT}/WORKFLOW.md"
|
|
|
|
grep -Eq '^## .*运行模型' "${file}" || fail "WORKFLOW.md should include a run model section"
|
|
grep -q 'bare' "${file}" || fail "WORKFLOW.md should describe the bare mirror model"
|
|
grep -q '工作副本' "${file}" || fail "WORKFLOW.md should describe isolated work copies"
|
|
grep -Eq '共享.*缓存|缓存.*共享' "${file}" || fail "WORKFLOW.md should describe the shared cache model"
|
|
grep -Eq '并发|隔离' "${file}" || fail "WORKFLOW.md should mention concurrency or isolation tradeoffs"
|
|
grep -Eq '结束后.*清理|清理.*工作副本' "${file}" || fail "WORKFLOW.md should mention cleanup after workflow completion"
|
|
! rg -q 'bootstrap_workspace\.sh' "${file}" || fail "WORKFLOW.md should describe architecture rather than helper implementation"
|
|
! rg -q '/data/git-mirrors|/home/workspace/jobs' "${file}" || fail "WORKFLOW.md should avoid implementation-specific workspace paths"
|
|
}
|
|
|
|
test_presets_do_not_mount_check_crlf_helper() {
|
|
! rg -q 'check_crlf\.sh:/data/check_crlf\.sh:ro' "${REPO_ROOT}/docker-runner/presets" || fail "preset compose files should not mount check_crlf helper into containers"
|
|
}
|
|
|
|
test_runner_data_is_gitignored() {
|
|
local path
|
|
|
|
for path in \
|
|
"docker-runner/presets/standard-ubuntu-22/runner-data/config.yaml" \
|
|
"docker-runner/presets/buildx-ubuntu-22/runner-data/config.yaml" \
|
|
"docker-runner/presets/buildx-archlinux/runner-data/config.yaml"; do
|
|
git -C "${REPO_ROOT}" check-ignore -q "${path}" || fail "${path} should be ignored as runtime runner data"
|
|
done
|
|
}
|
|
|
|
test_readme_has_project_intro_and_navigation() {
|
|
local file
|
|
|
|
file="${REPO_ROOT}/README.md"
|
|
|
|
grep -Eq '^## .*项目简介' "${file}" || fail "README.md should provide a project intro section"
|
|
grep -Eq '^## .*文档导航' "${file}" || fail "README.md should provide a document navigation section"
|
|
grep -q '\[DEPLOYMENT.md\](\./DEPLOYMENT.md)' "${file}" || fail "README.md should link to DEPLOYMENT.md"
|
|
grep -q '\[WORKFLOW.md\](\./WORKFLOW.md)' "${file}" || fail "README.md should link to WORKFLOW.md"
|
|
! rg -q '^## 🚀 快速提示$' "${file}" || fail "README.md should not carry concrete deployment tip sections"
|
|
! rg -q '^## ⚙️ 当前默认行为$' "${file}" || fail "README.md should not carry default behavior details"
|
|
! rg -q 'docker compose (build|up|exec)' "${file}" || fail "README.md should stay high-level and leave compose commands to deployment docs"
|
|
}
|
|
|
|
test_deployment_doc_stays_runner_focused() {
|
|
local file
|
|
|
|
file="${REPO_ROOT}/DEPLOYMENT.md"
|
|
|
|
! rg -q '^## ⚙️ 默认行为$' "${file}" || fail "DEPLOYMENT.md should not carry generic default behavior sections"
|
|
! rg -q '/data/git-mirrors/<owner>/<repo>\.git' "${file}" || fail "DEPLOYMENT.md should not document workflow mirror paths"
|
|
! rg -q '/home/workspace/jobs/' "${file}" || fail "DEPLOYMENT.md should not document workflow temp workspace paths"
|
|
! rg -q 'bootstrap_workspace\.sh' "${file}" || fail "DEPLOYMENT.md should stay focused on runner deployment, not workflow helpers"
|
|
}
|
|
|
|
test_presets_define_expected_hostname() {
|
|
grep -q '^ hostname: ubuntu$' "${REPO_ROOT}/docker-runner/presets/standard-ubuntu-22/docker-compose.yml" || fail "standard preset should set hostname to ubuntu"
|
|
grep -q '^ hostname: ubuntu$' "${REPO_ROOT}/docker-runner/presets/buildx-ubuntu-22/docker-compose.yml" || fail "buildx ubuntu preset should set hostname to ubuntu"
|
|
grep -q '^ hostname: arch$' "${REPO_ROOT}/docker-runner/presets/buildx-archlinux/docker-compose.yml" || fail "buildx arch preset should set hostname to arch"
|
|
}
|
|
|
|
test_preset_env_examples_exist() {
|
|
local file
|
|
|
|
for file in \
|
|
"${REPO_ROOT}/docker-runner/presets/standard-ubuntu-22/.env.example" \
|
|
"${REPO_ROOT}/docker-runner/presets/buildx-ubuntu-22/.env.example" \
|
|
"${REPO_ROOT}/docker-runner/presets/buildx-archlinux/.env.example"; do
|
|
test -f "${file}" || fail "missing env example: ${file}"
|
|
grep -q '^GITEA_INSTANCE=https://git.mytsl.cn$' "${file}" || fail "${file} should include company default instance"
|
|
grep -q '^GITEA_TOKEN=$' "${file}" || fail "${file} should include empty token placeholder"
|
|
grep -q '^DEFAULT_RUNNER_NAME=' "${file}" || fail "${file} should define DEFAULT_RUNNER_NAME override example"
|
|
grep -q '^DEFAULT_RUNNER_LABEL=' "${file}" || fail "${file} should define DEFAULT_RUNNER_LABEL override example"
|
|
done
|
|
}
|
|
|
|
test_preset_compose_supports_runner_identity_overrides() {
|
|
grep -q 'DEFAULT_RUNNER_NAME=${DEFAULT_RUNNER_NAME:-standard-ubuntu-22}' \
|
|
"${REPO_ROOT}/docker-runner/presets/standard-ubuntu-22/docker-compose.yml" || \
|
|
fail "standard preset should allow DEFAULT_RUNNER_NAME override via env"
|
|
grep -q 'DEFAULT_RUNNER_LABEL=${DEFAULT_RUNNER_LABEL:-ubuntu-22.04:host://ubuntu:22.04,company-server:host://ubuntu:22.04,standard-ubuntu-22:host://ubuntu:22.04}' \
|
|
"${REPO_ROOT}/docker-runner/presets/standard-ubuntu-22/docker-compose.yml" || \
|
|
fail "standard preset should allow DEFAULT_RUNNER_LABEL override via env"
|
|
|
|
grep -q 'DEFAULT_RUNNER_NAME=${DEFAULT_RUNNER_NAME:-buildx-ubuntu-22}' \
|
|
"${REPO_ROOT}/docker-runner/presets/buildx-ubuntu-22/docker-compose.yml" || \
|
|
fail "buildx ubuntu preset should allow DEFAULT_RUNNER_NAME override via env"
|
|
grep -q 'DEFAULT_RUNNER_LABEL=${DEFAULT_RUNNER_LABEL:-ubuntu-22.04:host://ubuntu:22.04,company-server:host://ubuntu:22.04,buildx-ubuntu-22:host://ubuntu:22.04}' \
|
|
"${REPO_ROOT}/docker-runner/presets/buildx-ubuntu-22/docker-compose.yml" || \
|
|
fail "buildx ubuntu preset should allow DEFAULT_RUNNER_LABEL override via env"
|
|
|
|
grep -q 'DEFAULT_RUNNER_NAME=${DEFAULT_RUNNER_NAME:-buildx-archlinux}' \
|
|
"${REPO_ROOT}/docker-runner/presets/buildx-archlinux/docker-compose.yml" || \
|
|
fail "buildx arch preset should allow DEFAULT_RUNNER_NAME override via env"
|
|
grep -q 'DEFAULT_RUNNER_LABEL=${DEFAULT_RUNNER_LABEL:-archlinux:host://archlinux:latest,company-server:host://archlinux:latest,buildx-archlinux:host://archlinux:latest}' \
|
|
"${REPO_ROOT}/docker-runner/presets/buildx-archlinux/docker-compose.yml" || \
|
|
fail "buildx arch preset should allow DEFAULT_RUNNER_LABEL override via env"
|
|
}
|
|
|
|
test_register_requires_python_yaml_path() {
|
|
local file
|
|
|
|
file="${REPO_ROOT}/docker-runner/common/register.sh"
|
|
|
|
! rg -q 'Python configuration failed, using basic sed' "${file}" || fail "register.sh should not fall back to basic sed when Python config fails"
|
|
! rg -q 'Python3 not found, applying basic configuration' "${file}" || fail "register.sh should not continue with basic configuration when Python3 is missing"
|
|
! rg -q "sed -i 's/capacity: 1/capacity: 4/g' config.yaml" "${file}" || fail "register.sh should not mutate config.yaml via sed fallback"
|
|
grep -q "python3 << PYEOF" "${file}" || fail "register.sh should keep Python-based config generation"
|
|
}
|
|
|
|
test_setup_requires_upgrade_helper() {
|
|
local file
|
|
|
|
file="${REPO_ROOT}/docker-runner/common/setup.sh"
|
|
|
|
grep -q '^# shellcheck source=/dev/null$' "${file}" || fail "setup.sh should source upgrade.sh directly"
|
|
grep -q '^source "\${UPGRADE_HELPER}"$' "${file}" || fail "setup.sh should require sourcing upgrade.sh"
|
|
! grep -Fq 'if [ -f "${UPGRADE_HELPER}" ]; then' "${file}" || fail "setup.sh should not treat upgrade helper as optional"
|
|
! rg -q 'declare -F resolve_latest_version_or_fallback' "${file}" || fail "setup.sh should not guard helper functions with declare -F"
|
|
! rg -q 'declare -F validate_version' "${file}" || fail "setup.sh should not guard validate_version with declare -F"
|
|
! rg -q 'declare -F validate_binary_arch_or_fail' "${file}" || fail "setup.sh should not guard binary validation with declare -F"
|
|
}
|
|
|
|
test_entrypoint_uses_shared_buildx_builder_creation() {
|
|
local file
|
|
|
|
file="${REPO_ROOT}/docker-runner/common/entrypoint.sh"
|
|
|
|
grep -q '^create_buildx_builder() {$' "${file}" || fail "entrypoint.sh should extract Buildx builder creation into a helper"
|
|
grep -q '^ensure_buildx_builder() {$' "${file}" || fail "entrypoint.sh should extract Buildx builder selection into a helper"
|
|
grep -q '^[[:space:]]*create_buildx_builder$' "${file}" || fail "entrypoint.sh should use shared builder creation during initial setup"
|
|
grep -q '^[[:space:]]*ensure_buildx_builder$' "${file}" || fail "entrypoint.sh should use shared builder selection during reuse"
|
|
}
|
|
|
|
test_stats_workflow_avoids_eval_find() {
|
|
local file
|
|
|
|
file="${REPO_ROOT}/.gitea/workflows/update_stats_badge.yaml"
|
|
|
|
! rg -q 'eval "find ' "${file}" || fail "stats workflow should avoid eval when building find commands"
|
|
grep -q 'EXCLUDE_FIND_ARGS=()' "${file}" || fail "stats workflow should build reusable find exclusion arrays"
|
|
grep -q 'LANG_FIND_ARGS=()' "${file}" || fail "stats workflow should build language-specific find arguments via arrays"
|
|
}
|
|
|
|
test_entrypoint_uses_platform_aware_multiarch_verification() {
|
|
local file
|
|
|
|
file="${REPO_ROOT}/docker-runner/common/entrypoint.sh"
|
|
|
|
grep -q 'docker run --rm --platform linux/arm64 alpine uname -m' "${file}" || fail "entrypoint.sh should verify arm64 support with an explicit platform"
|
|
grep -q 'docker run --rm --platform linux/amd64 alpine uname -m' "${file}" || fail "entrypoint.sh should verify amd64 support with an explicit platform"
|
|
! rg -q 'docker run --rm arm64v8/alpine uname -m' "${file}" || fail "entrypoint.sh should not use arm64v8/alpine without an explicit platform"
|
|
! rg -q 'docker run --rm amd64/alpine uname -m' "${file}" || fail "entrypoint.sh should not use amd64/alpine without an explicit platform"
|
|
}
|
|
|
|
test_preset_compose_uses_env_for_instance
|
|
test_workflows_do_not_hardcode_company_server
|
|
test_stats_workflow_uses_workflow_secret_consistently
|
|
test_workflow_docs_and_links_use_actual_paths
|
|
test_workflow_doc_describes_workspace_architecture
|
|
test_presets_do_not_mount_check_crlf_helper
|
|
test_runner_data_is_gitignored
|
|
test_readme_has_project_intro_and_navigation
|
|
test_deployment_doc_stays_runner_focused
|
|
test_presets_define_expected_hostname
|
|
test_preset_env_examples_exist
|
|
test_preset_compose_supports_runner_identity_overrides
|
|
test_register_requires_python_yaml_path
|
|
test_setup_requires_upgrade_helper
|
|
test_entrypoint_uses_shared_buildx_builder_creation
|
|
test_stats_workflow_avoids_eval_find
|
|
test_entrypoint_uses_platform_aware_multiarch_verification
|
|
|
|
echo "template_defaults_test.sh: PASS"
|