name: ๐Ÿงช Playbook ๆต‹่ฏ•ๅฅ—ไปถ on: push: branches: - main pull_request: branches: - main workflow_dispatch: # ๅ…่ฎธๆ‰‹ๅŠจ่งฆๅ‘ concurrency: group: test-${{ github.repository }}-${{ github.ref }} cancel-in-progress: true # ========================================== # ๐Ÿ”ง ้…็ฝฎๅŒบๅŸŸ - ๆต‹่ฏ•ๅ‚ๆ•ฐ # ========================================== env: # ===== ๆต‹่ฏ•็Žฏๅขƒ้…็ฝฎ ===== # ๆต‹่ฏ•ๅทฅไฝœ็›ฎๅฝ• WORKSPACE_DIR: "/home/workspace" TEST_WORKSPACE: "/home/workspace/playbook-test" jobs: # ========================================== # Job: ๅ…จ้‡ๆต‹่ฏ• # ========================================== test: name: ๐Ÿงช ๅ…จ้‡ๆต‹่ฏ• runs-on: ubuntu-22.04 steps: - name: ๐Ÿ“ฅ ๅ‡†ๅค‡ไป“ๅบ“ run: | echo "========================================" echo "๐Ÿ“ฅ ๅ‡†ๅค‡ไป“ๅบ“ๅˆฐ WORKSPACE_DIR" echo "========================================" REPO_NAME="${{ github.event.repository.name }}" REPO_DIR="${{ env.WORKSPACE_DIR }}/$REPO_NAME" TOKEN="${{ secrets.WORKFLOW }}" if [ -n "$TOKEN" ]; then REPO_URL="https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git" else REPO_URL="${GITHUB_SERVER_URL}/${{ github.repository }}.git" fi if [ -d "$REPO_DIR" ]; then if [ -d "$REPO_DIR/.git" ]; then cd "$REPO_DIR" git clean -fdx git reset --hard git fetch --all --tags --force --prune --prune-tags else rm -rf "$REPO_DIR" fi fi if [ ! -d "$REPO_DIR/.git" ]; then mkdir -p "${{ env.WORKSPACE_DIR }}" git clone "$REPO_URL" "$REPO_DIR" cd "$REPO_DIR" fi TARGET_SHA="${{ github.sha }}" TARGET_REF="${{ github.ref }}" if git cat-file -e "$TARGET_SHA^{commit}" 2>/dev/null; then git checkout -f "$TARGET_SHA" else if [ -n "$TARGET_REF" ]; then git fetch origin "$TARGET_REF" git checkout -f FETCH_HEAD else git checkout -f "${{ github.ref_name }}" fi fi git config --global --add safe.directory "$REPO_DIR" echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV - name: ๐Ÿ”ง ๅฎ‰่ฃ…ๆต‹่ฏ•ไพ่ต– run: | echo "========================================" echo "๐Ÿ”ง ๅฎ‰่ฃ…ๆต‹่ฏ•ไพ่ต–" echo "========================================" apt-get update apt-get install -y bats cmake clang-format python3-pip python3 -m pip install --upgrade pip python3 -m pip install toml tomli jsonschema yamllint echo "" echo "โœ“ bats ็‰ˆๆœฌ: $(bats --version)" echo "โœ“ Python ็‰ˆๆœฌ: $(python3 --version)" echo "========================================" - name: ๐Ÿงช ่ฟ่กŒๅ…จ้‡ๆต‹่ฏ•ๅนถ็”ŸๆˆๆŠฅๅ‘Š shell: bash run: | set +e set -o pipefail overall_fail=0 scripts_status="success" templates_status="success" integration_status="success" docs_status="success" echo "========================================" echo "๐Ÿš Shell ่„šๆœฌๆต‹่ฏ•" echo "========================================" cd "$REPO_DIR/tests/scripts" run_bats() { local name="$1" local file="$2" local output="${name}_test_results.tap" if [ ! -f "$file" ]; then echo "โš ๏ธ ๆœชๆ‰พๅˆฐๆต‹่ฏ•ๆ–‡ไปถ: $file" scripts_status="failure" overall_fail=1 return fi bats --formatter tap "$file" | tee "$output" if [ $? -ne 0 ]; then echo "โŒ $name ๆต‹่ฏ•ๅคฑ่ดฅ" scripts_status="failure" overall_fail=1 else echo "โœ… $name ๆต‹่ฏ•้€š่ฟ‡" fi } run_bats "sync_standards" "test_sync_standards.bats" run_bats "sync_templates" "test_sync_templates.bats" run_bats "vendor_playbook" "test_vendor_playbook.bats" run_bats "install_codex_skills" "test_install_codex_skills.bats" run_bats "windows_script_lints" "test_windows_script_lints.bats" echo "========================================" echo "๐Ÿ“„ ๆจกๆฟ้ชŒ่ฏๆต‹่ฏ•" echo "========================================" cd "$REPO_DIR/tests/templates" run_validator() { local name="$1" local script="$2" if [ ! -f "$script" ]; then echo "โš ๏ธ ๆœชๆ‰พๅˆฐ้ชŒ่ฏ่„šๆœฌ: $script" templates_status="failure" overall_fail=1 return fi chmod +x "$script" "./$script" if [ $? -ne 0 ]; then echo "โŒ $name ๆจกๆฟ้ชŒ่ฏๅคฑ่ดฅ" templates_status="failure" overall_fail=1 else echo "โœ… $name ๆจกๆฟ้ชŒ่ฏ้€š่ฟ‡" fi } run_validator "python" "validate_python_templates.sh" run_validator "cpp" "validate_cpp_templates.sh" run_validator "ci" "validate_ci_templates.sh" run_validator "project_templates" "validate_project_templates.sh" echo "========================================" echo "๐Ÿ”— ้›†ๆˆๆต‹่ฏ•" echo "========================================" mkdir -p "${TEST_WORKSPACE}" cd "${TEST_WORKSPACE}" # ๅˆ›ๅปบๆต‹่ฏ•้กน็›ฎ็›ฎๅฝ• mkdir -p test-project-tsl mkdir -p test-project-cpp mkdir -p test-project-multi echo "========================================" echo "๐Ÿงช ๆต‹่ฏ•ๅœบๆ™ฏ1: TSL ้กน็›ฎๆ ‡ๅ‡†ๅŒๆญฅ" echo "========================================" cd "${TEST_WORKSPACE}/test-project-tsl" # ๅˆๅง‹ๅŒ– git ไป“ๅบ“ git init git config user.name "Test User" git config user.email "test@example.com" # ๆจกๆ‹Ÿ subtree add๏ผˆๅŒ…ๅซ rulesets ็ญ‰็‚น็›ฎๅฝ•๏ผŒๆŽ’้™ค .git๏ผ‰ mkdir -p docs/standards/playbook tar -C "$REPO_DIR" --exclude .git -cf - . | tar -C docs/standards/playbook -xf - # ่ฟ่กŒๅŒๆญฅ่„šๆœฌ echo "โ–ถ ่ฟ่กŒ sync_standards.sh -langs tsl" sh docs/standards/playbook/scripts/sync_standards.sh -langs tsl # ้ชŒ่ฏ็ป“ๆžœ if [ -d ".agents/tsl" ] && [ -f ".agents/tsl/index.md" ]; then echo "โœ… TSL ่ง„ๅˆ™้›†ๅŒๆญฅๆˆๅŠŸ" else echo "โŒ TSL ่ง„ๅˆ™้›†ๅŒๆญฅๅคฑ่ดฅ" integration_status="failure" overall_fail=1 fi if grep -q "# BEGIN playbook .gitattributes" .gitattributes 2>/dev/null \ || grep -q "# Added from playbook .gitattributes" .gitattributes 2>/dev/null \ || grep -q "^\\* text=auto eol=lf" .gitattributes 2>/dev/null; then echo "โœ… .gitattributes ๆ›ดๆ–ฐๆˆๅŠŸ" else echo "โŒ .gitattributes ๆ›ดๆ–ฐๅคฑ่ดฅ" integration_status="failure" overall_fail=1 fi echo "========================================" echo "๐Ÿงช ๆต‹่ฏ•ๅœบๆ™ฏ2: C++ ้กน็›ฎๆ ‡ๅ‡†ๅŒๆญฅ" echo "========================================" cd "${TEST_WORKSPACE}/test-project-cpp" git init git config user.name "Test User" git config user.email "test@example.com" mkdir -p docs/standards/playbook tar -C "$REPO_DIR" --exclude .git -cf - . | tar -C docs/standards/playbook -xf - echo "โ–ถ ่ฟ่กŒ sync_standards.sh -langs cpp" sh docs/standards/playbook/scripts/sync_standards.sh -langs cpp if [ -d ".agents/cpp" ] && [ -f ".agents/cpp/index.md" ]; then echo "โœ… C++ ่ง„ๅˆ™้›†ๅŒๆญฅๆˆๅŠŸ" else echo "โŒ C++ ่ง„ๅˆ™้›†ๅŒๆญฅๅคฑ่ดฅ" integration_status="failure" overall_fail=1 fi echo "========================================" echo "๐Ÿงช ๆต‹่ฏ•ๅœบๆ™ฏ3: ๅคš่ฏญ่จ€้กน็›ฎๆ ‡ๅ‡†ๅŒๆญฅ" echo "========================================" cd "${TEST_WORKSPACE}/test-project-multi" git init git config user.name "Test User" git config user.email "test@example.com" mkdir -p docs/standards/playbook tar -C "$REPO_DIR" --exclude .git -cf - . | tar -C docs/standards/playbook -xf - echo "โ–ถ ่ฟ่กŒ sync_standards.sh -langs tsl,cpp" sh docs/standards/playbook/scripts/sync_standards.sh -langs tsl,cpp if [ -d ".agents/tsl" ] && [ -d ".agents/cpp" ] && [ -f ".agents/index.md" ]; then echo "โœ… ๅคš่ฏญ่จ€่ง„ๅˆ™้›†ๅŒๆญฅๆˆๅŠŸ" else echo "โŒ ๅคš่ฏญ่จ€่ง„ๅˆ™้›†ๅŒๆญฅๅคฑ่ดฅ" integration_status="failure" overall_fail=1 fi echo "========================================" echo "๐Ÿงช ๆต‹่ฏ•ๅœบๆ™ฏ4: vendor_playbook ่„šๆœฌ" echo "========================================" cd "${TEST_WORKSPACE}" mkdir -p test-project-vendor cd test-project-vendor git init git config user.name "Test User" git config user.email "test@example.com" echo "โ–ถ ่ฟ่กŒ vendor_playbook.sh" sh "$REPO_DIR/scripts/vendor_playbook.sh" -project-root . -langs tsl if [ -d "docs/standards/playbook" ] && [ -d "docs/standards/playbook/rulesets/tsl" ] && [ -d ".agents/tsl" ]; then echo "โœ… vendor_playbook ่„šๆœฌๆ‰ง่กŒๆˆๅŠŸ" else echo "โŒ vendor_playbook ่„šๆœฌๆ‰ง่กŒๅคฑ่ดฅ" integration_status="failure" overall_fail=1 fi echo "========================================" echo "๐Ÿงน ๆธ…็†ๆต‹่ฏ•็Žฏๅขƒ..." chmod -R u+w "${TEST_WORKSPACE}" 2>/dev/null || true rm -rf "${TEST_WORKSPACE}" echo "โœ“ ๆธ…็†ๅฎŒๆˆ" echo "========================================" echo "๐Ÿ“š ๆ–‡ๆกฃไธ€่‡ดๆ€งๆฃ€ๆŸฅ" echo "========================================" cd "$REPO_DIR/tests/integration" if [ -f "check_doc_links.sh" ]; then chmod +x check_doc_links.sh ./check_doc_links.sh if [ $? -eq 0 ]; then echo "โœ… ๆ–‡ๆกฃ้“พๆŽฅๆฃ€ๆŸฅ้€š่ฟ‡" else echo "โŒ ๅ‘็Žฐๆ— ๆ•ˆ้“พๆŽฅ" docs_status="failure" overall_fail=1 fi else echo "โš ๏ธ ๆœชๆ‰พๅˆฐ้“พๆŽฅๆฃ€ๆŸฅ่„šๆœฌ๏ผŒ่ทณ่ฟ‡" fi echo "========================================" echo "๐Ÿ” ๆฃ€ๆŸฅไปฃ็†่ง„ๅˆ™ไธ€่‡ดๆ€ง๏ผˆไธ‰ๅฑ‚ๆžถๆž„๏ผ‰" echo "========================================" cd "$REPO_DIR" # ๆฃ€ๆŸฅ rulesets/ ไธ‰ๅฑ‚ๆžถๆž„ๅฎŒๆ•ดๆ€ง python3 << 'EOF' import sys from pathlib import Path errors = [] warnings = [] print("ๆฃ€ๆŸฅ Layer 1: rulesets/ (ๆž็ฎ€้“ๅพ‹)") print("โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€") # ๆฃ€ๆŸฅๅ„่ฏญ่จ€็š„ rulesets/ ็›ฎๅฝ•๏ผˆๅช้œ€ index.md๏ผ‰ agents_base = Path("rulesets") # ๆฃ€ๆŸฅ rulesets/index.md agents_index = agents_base / "index.md" if not agents_index.exists(): errors.append(f"โŒ ็ผบๅฐ‘ๆ–‡ไปถ: {agents_index}") elif agents_index.stat().st_size == 0: errors.append(f"โŒ ๆ–‡ไปถไธบ็ฉบ: {agents_index}") else: print(f"โœ… {agents_index}") for lang_dir in ["tsl", "cpp", "python"]: agents_lang = agents_base / lang_dir if not agents_lang.exists(): errors.append(f"โŒ ็ผบๅฐ‘็›ฎๅฝ•: {agents_lang}") continue # ๅชๆฃ€ๆŸฅ index.md๏ผˆโ‰ค50 ่กŒ๏ผ‰ index_file = agents_lang / "index.md" if not index_file.exists(): errors.append(f"โŒ ็ผบๅฐ‘ๆ–‡ไปถ: {index_file}") elif index_file.stat().st_size == 0: errors.append(f"โŒ ๆ–‡ไปถไธบ็ฉบ: {index_file}") else: # ๆฃ€ๆŸฅ่ง„ๆจก๏ผˆโ‰ค50 ่กŒ๏ผ‰ line_count = len(index_file.read_text(encoding='utf-8').splitlines()) if line_count > 50: warnings.append(f"โš ๏ธ {index_file}: {line_count} ่กŒ (็›ฎๆ ‡: โ‰ค50)") else: print(f"โœ… {index_file} ({line_count} ่กŒ)") # ๆฃ€ๆŸฅๆ˜ฏๅฆๆœ‰ๆฎ‹็•™็š„ๆ—งๆ–‡ไปถ old_files = ["auth.md", "code_quality.md", "performance.md", "testing.md"] for old_file in old_files: old_path = agents_lang / old_file if old_path.exists(): warnings.append(f"โš ๏ธ ๆฎ‹็•™ๆ—งๆ–‡ไปถ: {old_path} (ๅบ”ๅˆ ้™ค)") print("") print("ๆฃ€ๆŸฅ Layer 2: codex/skills/ (ๆŒ‰้œ€ๅŠ ่ฝฝ)") print("โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€") # ๆฃ€ๆŸฅๅ…ณ้”ฎ skills skills_base = Path("codex/skills") required_skills = [ ("tsl-guide", ["SKILL.md", "references/primer.md", "references/advanced.md"]), ("commit-message", ["SKILL.md"]), ("style-cleanup", ["SKILL.md"]), ("bulk-refactor-workflow", ["SKILL.md"]), ] for skill_name, required_files in required_skills: skill_dir = skills_base / skill_name if not skill_dir.exists(): errors.append(f"โŒ ็ผบๅฐ‘ skill: {skill_dir}") continue for req_file in required_files: file_path = skill_dir / req_file if not file_path.exists(): errors.append(f"โŒ ็ผบๅฐ‘ๆ–‡ไปถ: {file_path}") elif file_path.stat().st_size == 0: errors.append(f"โŒ ๆ–‡ไปถไธบ็ฉบ: {file_path}") else: print(f"โœ… {file_path}") print("") print("ๆฃ€ๆŸฅ Layer 3: docs/ (ๆƒๅจๆ–‡ๆกฃ)") print("โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€") # ๆฃ€ๆŸฅๅ…ณ้”ฎๆ–‡ๆกฃ่ทฏๅพ„ docs_paths = [ "docs/tsl/syntax_book/index.md", "docs/tsl/code_style.md", "docs/tsl/naming.md", "docs/cpp/code_style.md", "docs/python/style_guide.md", ] for doc_path in docs_paths: path = Path(doc_path) if not path.exists(): errors.append(f"โŒ ็ผบๅฐ‘ๆ–‡ๆกฃ: {doc_path}") else: print(f"โœ… {doc_path}") print("") print("ๆฃ€ๆŸฅๆžถๆž„ๆ–‡ๆกฃ") print("โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€") # ๆฃ€ๆŸฅๆ–ฐๅขž็š„ๆžถๆž„ๆ–‡ๆกฃ arch_docs = ["AGENTS.md", "SKILLS.md", "README.md"] for doc in arch_docs: path = Path(doc) if not path.exists(): errors.append(f"โŒ ็ผบๅฐ‘ๆ–‡ๆกฃ: {doc}") else: print(f"โœ… {doc}") print("") print("โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€") if warnings: print("\nโš ๏ธ ่ญฆๅ‘Š:") for warning in warnings: print(f" {warning}") print("") if errors: print("\nโŒ ๅ‘็Žฐ้”™่ฏฏ:") for error in errors: print(f" {error}") sys.exit(1) else: print("โœ… ไธ‰ๅฑ‚ๆžถๆž„ๅฎŒๆ•ดๆ€งๆฃ€ๆŸฅ้€š่ฟ‡") EOF if [ $? -ne 0 ]; then docs_status="failure" overall_fail=1 fi echo "========================================" echo "๐Ÿ“Š ็”Ÿๆˆๆต‹่ฏ•็ปผๅˆๆŠฅๅ‘Š" echo "========================================" format_status() { case "$1" in success) echo "โœ… ้€š่ฟ‡" ;; failure) echo "โŒ ๅคฑ่ดฅ" ;; *) echo "โ” ๆœช็Ÿฅ" ;; esac } cat >> $GITHUB_STEP_SUMMARY << EOFSUMMARY # ๐Ÿงช Playbook ๆต‹่ฏ•ๆŠฅๅ‘Š ## ๐Ÿ“‹ ๆต‹่ฏ•ๆ‰ง่กŒๆ‘˜่ฆ | ๆต‹่ฏ•็ฑปๅž‹ | ็Šถๆ€ | |---------|------| | ๐Ÿš Shell ่„šๆœฌๆต‹่ฏ• | $(format_status "$scripts_status") | | ๐Ÿ“„ ๆจกๆฟ้ชŒ่ฏๆต‹่ฏ• | $(format_status "$templates_status") | | ๐Ÿ”— ้›†ๆˆๆต‹่ฏ• | $(format_status "$integration_status") | | ๐Ÿ“š ๆ–‡ๆกฃไธ€่‡ดๆ€งๆฃ€ๆŸฅ | $(format_status "$docs_status") | --- ## ๐Ÿ”— ็›ธๅ…ณ้“พๆŽฅ - ๐Ÿ“ [ๆต‹่ฏ•ๆ–‡ๆกฃ](tests/README.md) - ๐Ÿ› [้—ฎ้ข˜ๅ้ฆˆ](../../issues) - ๐Ÿ“– [ๅผ€ๅ‘ๆŒ‡ๅ—](docs/index.md) ---
*๐Ÿค– ็”ฑ [Gitea Actions](../../actions) ่‡ชๅŠจ็”Ÿๆˆ* EOFSUMMARY echo "*๐Ÿ“… ็”Ÿๆˆๆ—ถ้—ด: $(date -u '+%Y-%m-%d %H:%M:%S UTC')*" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "
" >> $GITHUB_STEP_SUMMARY echo "========================================" if [ "$overall_fail" -ne 0 ]; then echo "โŒ ๆต‹่ฏ•ๅคฑ่ดฅ" exit 1 fi echo "โœ… ๅ…จ้‡ๆต‹่ฏ•้€š่ฟ‡"