diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c24dfdf96..8e3218376 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,13 +5,19 @@ on: branches: [main] pull_request: +permissions: {} + jobs: test: runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + persist-credentials: false - - uses: oven-sh/setup-bun@v2 + - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2 with: bun-version: 1.2.12 @@ -23,10 +29,14 @@ jobs: prettier: runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + persist-credentials: false - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # v1 with: bun-version: latest @@ -38,10 +48,14 @@ jobs: typecheck: runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + persist-credentials: false - - uses: oven-sh/setup-bun@v2 + - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2 with: bun-version: 1.2.12 @@ -50,3 +64,17 @@ jobs: - name: Run TypeScript type check run: bun run typecheck + + zizmor: + name: Run zizmor + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - name: Checkout repository + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0 diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index b50b538b3..b2b7309c6 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -4,6 +4,8 @@ on: pull_request: types: [opened] +permissions: {} + jobs: review: runs-on: ubuntu-latest @@ -13,12 +15,13 @@ jobs: id-token: write steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 1 + persist-credentials: false - name: PR Review with Progress Tracking - uses: anthropics/claude-code-action@v1 + uses: anthropics/claude-code-action@7145c3e0510bcdbdd29f67cc4a8c1958f1acfa2f # v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 3ee052746..e58c088bd 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -10,6 +10,8 @@ on: pull_request_review: types: [submitted] +permissions: {} + jobs: claude: if: | @@ -25,13 +27,14 @@ jobs: id-token: write steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 1 + persist-credentials: false - name: Run Claude Code id: claude - uses: anthropics/claude-code-action@v1 + uses: anthropics/claude-code-action@7145c3e0510bcdbdd29f67cc4a8c1958f1acfa2f # v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} claude_args: | diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml index 599df15f5..7872f74e3 100644 --- a/.github/workflows/issue-triage.yml +++ b/.github/workflows/issue-triage.yml @@ -4,6 +4,8 @@ on: issues: types: [opened] +permissions: {} + jobs: triage-issue: runs-on: ubuntu-latest @@ -14,12 +16,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 + persist-credentials: false - name: Run Claude Code for Issue Triage - uses: anthropics/claude-code-action@main + uses: anthropics/claude-code-action@3ba9f7c8c2d3f122d3465c267a87b26a1f4783a6 # main with: prompt: "/label-issue REPO: ${{ github.repository }} ISSUE_NUMBER${{ github.event.issue.number }}" anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3d611fac2..dfddb25aa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,8 @@ on: type: boolean default: false +permissions: {} + jobs: create-release: runs-on: ubuntu-latest @@ -19,7 +21,7 @@ jobs: next_version: ${{ steps.next_version.outputs.next_version }} steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 @@ -36,10 +38,11 @@ jobs: - name: Calculate next version id: next_version + env: + LATEST_TAG: ${{ steps.get_latest_tag.outputs.latest_tag }} run: | - latest_tag="${{ steps.get_latest_tag.outputs.latest_tag }}" # Remove 'v' prefix and split by dots - version=${latest_tag#v} + version=${LATEST_TAG#v} IFS='.' read -ra VERSION_PARTS <<< "$version" # Increment patch version @@ -54,31 +57,34 @@ jobs: - name: Display dry run info if: ${{ inputs.dry_run }} + env: + NEXT_VERSION: ${{ steps.next_version.outputs.next_version }} + LATEST_TAG: ${{ steps.get_latest_tag.outputs.latest_tag }} run: | echo "🔍 DRY RUN MODE" - echo "Would create tag: ${{ steps.next_version.outputs.next_version }}" + echo "Would create tag: $NEXT_VERSION" echo "From commit: ${{ github.sha }}" - echo "Previous tag: ${{ steps.get_latest_tag.outputs.latest_tag }}" + echo "Previous tag: $LATEST_TAG" - name: Create and push tag if: ${{ !inputs.dry_run }} + env: + NEXT_VERSION: ${{ steps.next_version.outputs.next_version }} run: | - next_version="${{ steps.next_version.outputs.next_version }}" git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git tag -a "$next_version" -m "Release $next_version" - git push origin "$next_version" + git tag -a "$NEXT_VERSION" -m "Release $NEXT_VERSION" + git push origin "$NEXT_VERSION" - name: Create Release if: ${{ !inputs.dry_run }} env: GH_TOKEN: ${{ github.token }} + NEXT_VERSION: ${{ steps.next_version.outputs.next_version }} run: | - next_version="${{ steps.next_version.outputs.next_version }}" - - gh release create "$next_version" \ - --title "$next_version" \ + gh release create "$NEXT_VERSION" \ + --title "$NEXT_VERSION" \ --generate-notes \ --latest=false # keep v1 as latest @@ -91,36 +97,40 @@ jobs: contents: write steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 - name: Update major version tag + env: + NEXT_VERSION: ${{ needs.create-release.outputs.next_version }} run: | - next_version="${{ needs.create-release.outputs.next_version }}" # Extract major version (e.g., v0 from v0.0.20) - major_version=$(echo "$next_version" | cut -d. -f1) + major_version=$(echo "$NEXT_VERSION" | cut -d. -f1) # Update the major version tag to point to this release git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git tag -fa "$major_version" -m "Update $major_version tag to $next_version" + git tag -fa "$major_version" -m "Update $major_version tag to $NEXT_VERSION" git push origin "$major_version" --force - echo "Updated $major_version tag to point to $next_version" + echo "Updated $major_version tag to point to $NEXT_VERSION" release-base-action: needs: create-release if: ${{ !inputs.dry_run }} runs-on: ubuntu-latest environment: production + permissions: + contents: read steps: - name: Checkout base-action repo - uses: actions/checkout@v5 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: repository: anthropics/claude-code-base-action token: ${{ secrets.CLAUDE_CODE_BASE_ACTION_PAT }} fetch-depth: 0 + persist-credentials: false # - name: Create and push tag # run: | diff --git a/.github/workflows/sync-base-action.yml b/.github/workflows/sync-base-action.yml index 72bf8c0fc..a1fe3a881 100644 --- a/.github/workflows/sync-base-action.yml +++ b/.github/workflows/sync-base-action.yml @@ -8,26 +8,30 @@ on: - "base-action/**" workflow_dispatch: -permissions: - contents: write +permissions: {} jobs: sync-base-action: name: Sync base-action to claude-code-base-action repository runs-on: ubuntu-latest environment: production + permissions: + contents: read timeout-minutes: 10 steps: - name: Checkout source repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: fetch-depth: 1 + persist-credentials: false - name: Setup SSH and clone target repository + env: + DEPLOY_KEY: ${{ secrets.CLAUDE_CODE_BASE_ACTION_REPO_DEPLOY_KEY }} run: | # Configure SSH with deploy key mkdir -p ~/.ssh - echo "${{ secrets.CLAUDE_CODE_BASE_ACTION_REPO_DEPLOY_KEY }}" > ~/.ssh/deploy_key_base + echo "$DEPLOY_KEY" > ~/.ssh/deploy_key_base chmod 600 ~/.ssh/deploy_key_base # Configure SSH host diff --git a/.github/workflows/test-base-action.yml b/.github/workflows/test-base-action.yml index b4896631a..440ca6a3b 100644 --- a/.github/workflows/test-base-action.yml +++ b/.github/workflows/test-base-action.yml @@ -12,11 +12,17 @@ on: required: false default: "List the files in the current directory starting with 'package'" +permissions: {} + jobs: test-inline-prompt: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Test with inline prompt id: inline-test @@ -27,10 +33,10 @@ jobs: allowed_tools: "LS,Read" - name: Verify inline prompt output + env: + OUTPUT_FILE: ${{ steps.inline-test.outputs.execution_file }} + CONCLUSION: ${{ steps.inline-test.outputs.conclusion }} run: | - OUTPUT_FILE="${{ steps.inline-test.outputs.execution_file }}" - CONCLUSION="${{ steps.inline-test.outputs.conclusion }}" - echo "Conclusion: $CONCLUSION" echo "Output file: $OUTPUT_FILE" @@ -64,16 +70,20 @@ jobs: test-prompt-file: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Create test prompt file + env: + PROMPT: ${{ github.event.inputs.test_prompt || 'List the files in the current directory starting with "package"' }} run: | cat > test-prompt.txt << EOF ${PROMPT} EOF - env: - PROMPT: ${{ github.event.inputs.test_prompt || 'List the files in the current directory starting with "package"' }} - name: Test with prompt file and allowed tools id: prompt-file-test @@ -84,10 +94,10 @@ jobs: allowed_tools: "LS,Read" - name: Verify prompt file output + env: + OUTPUT_FILE: ${{ steps.prompt-file-test.outputs.execution_file }} + CONCLUSION: ${{ steps.prompt-file-test.outputs.conclusion }} run: | - OUTPUT_FILE="${{ steps.prompt-file-test.outputs.execution_file }}" - CONCLUSION="${{ steps.prompt-file-test.outputs.conclusion }}" - echo "Conclusion: $CONCLUSION" echo "Output file: $OUTPUT_FILE" @@ -121,8 +131,12 @@ jobs: test-agent-sdk: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Test with Agent SDK id: sdk-test @@ -135,10 +149,10 @@ jobs: allowed_tools: "LS,Read" - name: Verify SDK output + env: + OUTPUT_FILE: ${{ steps.sdk-test.outputs.execution_file }} + CONCLUSION: ${{ steps.sdk-test.outputs.conclusion }} run: | - OUTPUT_FILE="${{ steps.sdk-test.outputs.execution_file }}" - CONCLUSION="${{ steps.sdk-test.outputs.conclusion }}" - echo "Conclusion: $CONCLUSION" echo "Output file: $OUTPUT_FILE" diff --git a/.github/workflows/test-custom-executables.yml b/.github/workflows/test-custom-executables.yml index 2fd2fc00a..ad146e2e4 100644 --- a/.github/workflows/test-custom-executables.yml +++ b/.github/workflows/test-custom-executables.yml @@ -7,11 +7,17 @@ on: pull_request: workflow_dispatch: +permissions: {} + jobs: test-custom-executables: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Install Bun manually run: | @@ -55,10 +61,10 @@ jobs: allowed_tools: "LS,Read" - name: Verify custom executables worked + env: + OUTPUT_FILE: ${{ steps.custom-test.outputs.execution_file }} + CONCLUSION: ${{ steps.custom-test.outputs.conclusion }} run: | - OUTPUT_FILE="${{ steps.custom-test.outputs.execution_file }}" - CONCLUSION="${{ steps.custom-test.outputs.conclusion }}" - echo "Conclusion: $CONCLUSION" echo "Output file: $OUTPUT_FILE" diff --git a/.github/workflows/test-mcp-servers.yml b/.github/workflows/test-mcp-servers.yml index 46db1a7e0..8d5e7deb3 100644 --- a/.github/workflows/test-mcp-servers.yml +++ b/.github/workflows/test-mcp-servers.yml @@ -7,15 +7,21 @@ on: branches: [main] workflow_dispatch: +permissions: {} + jobs: test-mcp-integration: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Setup Bun - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 #v2 + uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2 - name: Install dependencies run: | @@ -51,7 +57,7 @@ jobs: # Check if mcp_servers field exists in the init event if jq -e '.[] | select(.type == "system" and .subtype == "init") | .mcp_servers' "$OUTPUT_FILE" > /dev/null; then echo "✓ Found mcp_servers in output" - + # Check if test-server is connected if jq -e '.[] | select(.type == "system" and .subtype == "init") | .mcp_servers[] | select(.name == "test-server" and .status == "connected")' "$OUTPUT_FILE" > /dev/null; then echo "✓ test-server is connected" @@ -60,7 +66,7 @@ jobs: jq '.[] | select(.type == "system" and .subtype == "init") | .mcp_servers' "$OUTPUT_FILE" exit 1 fi - + # Check if mcp tools are available if jq -e '.[] | select(.type == "system" and .subtype == "init") | .tools[] | select(. == "mcp__test-server__test_tool")' "$OUTPUT_FILE" > /dev/null; then echo "✓ MCP test tool found" @@ -79,12 +85,16 @@ jobs: test-mcp-config-flag: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Setup Bun - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 #v2 + uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2 - name: Install dependencies run: | @@ -133,7 +143,7 @@ jobs: # Check if mcp_servers field exists in the init event if jq -e '.[] | select(.type == "system" and .subtype == "init") | .mcp_servers' "$OUTPUT_FILE" > /dev/null; then echo "✓ Found mcp_servers in output" - + # Check if test-server is connected if jq -e '.[] | select(.type == "system" and .subtype == "init") | .mcp_servers[] | select(.name == "test-server" and .status == "connected")' "$OUTPUT_FILE" > /dev/null; then echo "✓ test-server is connected" @@ -142,7 +152,7 @@ jobs: jq '.[] | select(.type == "system" and .subtype == "init") | .mcp_servers' "$OUTPUT_FILE" exit 1 fi - + # Check if mcp tools are available if jq -e '.[] | select(.type == "system" and .subtype == "init") | .tools[] | select(. == "mcp__test-server__test_tool")' "$OUTPUT_FILE" > /dev/null; then echo "✓ MCP test tool found" diff --git a/.github/workflows/test-settings.yml b/.github/workflows/test-settings.yml index caa7f3506..be48888f5 100644 --- a/.github/workflows/test-settings.yml +++ b/.github/workflows/test-settings.yml @@ -7,11 +7,17 @@ on: pull_request: workflow_dispatch: +permissions: {} + jobs: test-settings-inline-allow: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Test with inline settings JSON (echo allowed) id: inline-settings-test @@ -28,10 +34,10 @@ jobs: } - name: Verify echo worked + env: + OUTPUT_FILE: ${{ steps.inline-settings-test.outputs.execution_file }} + CONCLUSION: ${{ steps.inline-settings-test.outputs.conclusion }} run: | - OUTPUT_FILE="${{ steps.inline-settings-test.outputs.execution_file }}" - CONCLUSION="${{ steps.inline-settings-test.outputs.conclusion }}" - echo "Conclusion: $CONCLUSION" if [ "$CONCLUSION" = "success" ]; then @@ -59,8 +65,12 @@ jobs: test-settings-inline-deny: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Test with inline settings JSON (echo denied) id: inline-settings-test @@ -77,9 +87,9 @@ jobs: } - name: Verify echo was denied + env: + OUTPUT_FILE: ${{ steps.inline-settings-test.outputs.execution_file }} run: | - OUTPUT_FILE="${{ steps.inline-settings-test.outputs.execution_file }}" - # Check that permission was denied in the tool_result if grep -q "Permission to use Bash with command echo.*has been denied" "$OUTPUT_FILE"; then echo "✅ Echo command was correctly denied by permissions" @@ -91,8 +101,12 @@ jobs: test-settings-file-allow: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Create settings file (echo allowed) run: | @@ -114,10 +128,10 @@ jobs: settings: "test-settings.json" - name: Verify echo worked + env: + OUTPUT_FILE: ${{ steps.file-settings-test.outputs.execution_file }} + CONCLUSION: ${{ steps.file-settings-test.outputs.conclusion }} run: | - OUTPUT_FILE="${{ steps.file-settings-test.outputs.execution_file }}" - CONCLUSION="${{ steps.file-settings-test.outputs.conclusion }}" - echo "Conclusion: $CONCLUSION" if [ "$CONCLUSION" = "success" ]; then @@ -145,8 +159,12 @@ jobs: test-settings-file-deny: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Create settings file (echo denied) run: | @@ -168,9 +186,9 @@ jobs: settings: "test-settings.json" - name: Verify echo was denied + env: + OUTPUT_FILE: ${{ steps.file-settings-test.outputs.execution_file }} run: | - OUTPUT_FILE="${{ steps.file-settings-test.outputs.execution_file }}" - # Check that permission was denied in the tool_result if grep -q "Permission to use Bash with command echo.*has been denied" "$OUTPUT_FILE"; then echo "✅ Echo command was correctly denied by permissions" diff --git a/.github/workflows/test-structured-output.yml b/.github/workflows/test-structured-output.yml index 9b33360c5..6125c021a 100644 --- a/.github/workflows/test-structured-output.yml +++ b/.github/workflows/test-structured-output.yml @@ -7,16 +7,19 @@ on: pull_request: workflow_dispatch: -permissions: - contents: read +permissions: {} jobs: test-basic-types: name: Test Basic Type Conversions runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Test with explicit values id: test @@ -36,10 +39,9 @@ jobs: --json-schema '{"type":"object","properties":{"text_field":{"type":"string"},"number_field":{"type":"number"},"boolean_true":{"type":"boolean"},"boolean_false":{"type":"boolean"}},"required":["text_field","number_field","boolean_true","boolean_false"]}' - name: Verify outputs + env: + OUTPUT: ${{ steps.test.outputs.structured_output }} run: | - # Parse the structured_output JSON - OUTPUT='${{ steps.test.outputs.structured_output }}' - # Test string pass-through TEXT_FIELD=$(echo "$OUTPUT" | jq -r '.text_field') if [ "$TEXT_FIELD" != "hello" ]; then @@ -73,9 +75,13 @@ jobs: test-complex-types: name: Test Arrays and Objects runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Test complex types id: test @@ -94,10 +100,9 @@ jobs: --json-schema '{"type":"object","properties":{"items":{"type":"array","items":{"type":"string"}},"config":{"type":"object"},"empty_array":{"type":"array"}},"required":["items","config","empty_array"]}' - name: Verify JSON stringification + env: + OUTPUT: ${{ steps.test.outputs.structured_output }} run: | - # Parse the structured_output JSON - OUTPUT='${{ steps.test.outputs.structured_output }}' - # Arrays should be JSON stringified if ! echo "$OUTPUT" | jq -e '.items | length == 3' > /dev/null; then echo "❌ Array not properly formatted" @@ -124,9 +129,13 @@ jobs: test-edge-cases: name: Test Edge Cases runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Test edge cases id: test @@ -146,10 +155,9 @@ jobs: --json-schema '{"type":"object","properties":{"zero":{"type":"number"},"empty_string":{"type":"string"},"negative":{"type":"number"},"decimal":{"type":"number"}},"required":["zero","empty_string","negative","decimal"]}' - name: Verify edge cases + env: + OUTPUT: ${{ steps.test.outputs.structured_output }} run: | - # Parse the structured_output JSON - OUTPUT='${{ steps.test.outputs.structured_output }}' - # Zero should be "0", not empty or falsy ZERO=$(echo "$OUTPUT" | jq -r '.zero') if [ "$ZERO" != "0" ]; then @@ -183,9 +191,13 @@ jobs: test-name-sanitization: name: Test Output Name Sanitization runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Test special characters in field names id: test @@ -200,10 +212,9 @@ jobs: --json-schema '{"type":"object","properties":{"test-result":{"type":"string"},"item_count":{"type":"number"}},"required":["test-result","item_count"]}' - name: Verify sanitized names work + env: + OUTPUT: ${{ steps.test.outputs.structured_output }} run: | - # Parse the structured_output JSON - OUTPUT='${{ steps.test.outputs.structured_output }}' - # Hyphens should be preserved in the JSON TEST_RESULT=$(echo "$OUTPUT" | jq -r '.["test-result"]') if [ "$TEST_RESULT" != "passed" ]; then @@ -223,9 +234,13 @@ jobs: test-execution-file-structure: name: Test Execution File Format runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Run with structured output id: test @@ -238,9 +253,9 @@ jobs: --json-schema '{"type":"object","properties":{"done":{"type":"boolean"}},"required":["done"]}' - name: Verify execution file contains structured_output + env: + FILE: ${{ steps.test.outputs.execution_file }} run: | - FILE="${{ steps.test.outputs.execution_file }}" - # Check file exists if [ ! -f "$FILE" ]; then echo "❌ Execution file missing" @@ -266,6 +281,8 @@ jobs: test-summary: name: Summary runs-on: ubuntu-latest + permissions: + contents: read needs: - test-basic-types - test-complex-types @@ -275,6 +292,12 @@ jobs: if: always() steps: - name: Generate Summary + env: + BASIC_TYPES_RESULT: ${{ needs.test-basic-types.result }} + COMPLEX_TYPES_RESULT: ${{ needs.test-complex-types.result }} + EDGE_CASES_RESULT: ${{ needs.test-edge-cases.result }} + NAME_SANITIZATION_RESULT: ${{ needs.test-name-sanitization.result }} + EXECUTION_FILE_RESULT: ${{ needs.test-execution-file-structure.result }} run: | echo "# Structured Output Tests (Optimized)" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY @@ -282,22 +305,43 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "| Test | Result |" >> $GITHUB_STEP_SUMMARY echo "|------|--------|" >> $GITHUB_STEP_SUMMARY - echo "| Basic Types | ${{ needs.test-basic-types.result == 'success' && '✅ PASS' || '❌ FAIL' }} |" >> $GITHUB_STEP_SUMMARY - echo "| Complex Types | ${{ needs.test-complex-types.result == 'success' && '✅ PASS' || '❌ FAIL' }} |" >> $GITHUB_STEP_SUMMARY - echo "| Edge Cases | ${{ needs.test-edge-cases.result == 'success' && '✅ PASS' || '❌ FAIL' }} |" >> $GITHUB_STEP_SUMMARY - echo "| Name Sanitization | ${{ needs.test-name-sanitization.result == 'success' && '✅ PASS' || '❌ FAIL' }} |" >> $GITHUB_STEP_SUMMARY - echo "| Execution File | ${{ needs.test-execution-file-structure.result == 'success' && '✅ PASS' || '❌ FAIL' }} |" >> $GITHUB_STEP_SUMMARY + + if [ "$BASIC_TYPES_RESULT" = "success" ]; then + echo "| Basic Types | ✅ PASS |" >> $GITHUB_STEP_SUMMARY + else + echo "| Basic Types | ❌ FAIL |" >> $GITHUB_STEP_SUMMARY + fi + + if [ "$COMPLEX_TYPES_RESULT" = "success" ]; then + echo "| Complex Types | ✅ PASS |" >> $GITHUB_STEP_SUMMARY + else + echo "| Complex Types | ❌ FAIL |" >> $GITHUB_STEP_SUMMARY + fi + + if [ "$EDGE_CASES_RESULT" = "success" ]; then + echo "| Edge Cases | ✅ PASS |" >> $GITHUB_STEP_SUMMARY + else + echo "| Edge Cases | ❌ FAIL |" >> $GITHUB_STEP_SUMMARY + fi + + if [ "$NAME_SANITIZATION_RESULT" = "success" ]; then + echo "| Name Sanitization | ✅ PASS |" >> $GITHUB_STEP_SUMMARY + else + echo "| Name Sanitization | ❌ FAIL |" >> $GITHUB_STEP_SUMMARY + fi + + if [ "$EXECUTION_FILE_RESULT" = "success" ]; then + echo "| Execution File | ✅ PASS |" >> $GITHUB_STEP_SUMMARY + else + echo "| Execution File | ❌ FAIL |" >> $GITHUB_STEP_SUMMARY + fi # Check if all passed - ALL_PASSED=${{ - needs.test-basic-types.result == 'success' && - needs.test-complex-types.result == 'success' && - needs.test-edge-cases.result == 'success' && - needs.test-name-sanitization.result == 'success' && - needs.test-execution-file-structure.result == 'success' - }} - - if [ "$ALL_PASSED" = "true" ]; then + if [ "$BASIC_TYPES_RESULT" = "success" ] && \ + [ "$COMPLEX_TYPES_RESULT" = "success" ] && \ + [ "$EDGE_CASES_RESULT" = "success" ] && \ + [ "$NAME_SANITIZATION_RESULT" = "success" ] && \ + [ "$EXECUTION_FILE_RESULT" = "success" ]; then echo "" >> $GITHUB_STEP_SUMMARY echo "## ✅ All Tests Passed" >> $GITHUB_STEP_SUMMARY else diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 000000000..91a15a8c7 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,6 @@ +rules: + artipacked: + ignore: + # These checkouts need persist-credentials for git push to create/update tags + - release.yml:23 + - release.yml:99 \ No newline at end of file