diff --git a/.github/actions/collect-format-results/action.yaml b/.github/actions/collect-format-results/action.yaml new file mode 100644 index 00000000..e630cf14 --- /dev/null +++ b/.github/actions/collect-format-results/action.yaml @@ -0,0 +1,53 @@ +name: "Collect Format Results" +description: "Aggregates results from multiple formatting jobs and creates a summary comment." +inputs: + results-json: + description: "JSON object containing job results" + required: true + +outputs: + message: + description: "The formatted summary message" + value: ${{ steps.collect.outputs.message }} + has_failures: + description: "Whether any job failed" + value: ${{ steps.collect.outputs.has_failures }} + +runs: + using: "composite" + steps: + - name: Aggregate results + id: collect + shell: bash + run: | + echo "results<> $GITHUB_ENV + echo '${{ inputs.results-json }}' >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + # In a real script we would parse the JSON, but for a composite action + # we can pass pre-formatted data. To keep it simple and robust, + # let's assume the caller provides a bash-friendly format or we use python. + + python3 -c " + import json, os + data = json.loads(os.environ['results']) + messages = [] + has_failures = False + for name, res in data.items(): + if res['result'] in ['failure', 'cancelled', 'skipped']: + messages.append(f'❌ {name} workflow {res[\"result\"]}') + has_failures = True + elif res.get('changes') == 'true': + if res.get('pushed') == 'true': + messages.append(f'✅ {name} fixes pushed (commit {res.get(\"sha\", \"\")})') + elif res.get('patch_name'): + messages.append(f'⚠️ {name} fixes available as patch {res.get(\"patch_name\")}') + + output_msg = 'No automatic format fixes were necessary.' + if messages: + output_msg = '### Format Fixes Applied\n' + '\n'.join(messages) + '\n\n⚠️ **Note:** Some issues may require manual review.' + + with open(os.environ['GITHUB_OUTPUT'], 'a') as f: + f.write(f'message<- + Removes the 'eyes' reaction and adds a completion reaction ('rocket' for success, 'confused' for 'failure', + 'cancelled', or 'skipped') to the triggering comment. Requires `issues: write` permission in the calling workflow to + update comment reactions via the GitHub Reactions API. +inputs: + status: + description: + "The status of the workflow. If 'success', a 'rocket' reaction is added. For any non-success status (including + 'failure', 'cancelled', or 'skipped'), a 'confused' reaction is added. Defaults to 'success'." + required: false + default: "success" + +runs: + using: "composite" + steps: + - name: Update PR Comment Reactions + if: github.event_name == 'issue_comment' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const status = '${{ inputs.status }}'; + const completionReaction = (status === 'success') ? 'rocket' : 'confused'; + + // 1. Remove "eyes" reaction + try { + const { data: reactions } = await github.rest.reactions.listForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id + }); + const eyesReaction = reactions.find( + (r) => r.content === 'eyes' && r.user && r.user.login === 'github-actions[bot]' + ); + if (eyesReaction) { + await github.rest.reactions.deleteForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + reaction_id: eyesReaction.id + }); + } + } catch (e) { + core.warning(`Failed to remove "eyes" reaction: ${e.message}`); + } + + // 2. Add completion reaction + try { + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: completionReaction + }); + } catch (e) { + core.warning(`Failed to add "${completionReaction}" reaction: ${e.message}`); + } diff --git a/.github/actions/post-clang-tidy-results/action.yaml b/.github/actions/post-clang-tidy-results/action.yaml new file mode 100644 index 00000000..8221e81c --- /dev/null +++ b/.github/actions/post-clang-tidy-results/action.yaml @@ -0,0 +1,78 @@ +name: "Post Clang-Tidy Results" +description: "Consolidates Clang-Tidy artifacts and posts PR comments." +inputs: + build-path: + description: "Path where build artifacts are located" + required: true + pr-number: + description: "PR number for posting comments" + required: false + post-summary: + description: "Whether to post a summary comment (true/false)" + required: false + default: "false" + +runs: + using: "composite" + steps: + - name: Download artifacts + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + pattern: clang-tidy-* + merge-multiple: true + continue-on-error: true + + - name: Check if artifacts exist + id: check + shell: bash + run: | + if [ -f "${{ inputs.build-path }}/clang-tidy-fixes.yaml" ]; then + echo "has_fixes=true" >> "$GITHUB_OUTPUT" + else + echo "has_fixes=false" >> "$GITHUB_OUTPUT" + fi + + - name: Post inline comments + if: steps.check.outputs.has_fixes == 'true' && inputs.pr-number != '' + uses: platisd/clang-tidy-pr-comments@28cfb84edafa771c044bde7e4a2a3fae57463818 # v1.8.0 + with: + clang_tidy_fixes: ${{ inputs.build-path }}/clang-tidy-fixes.yaml + github_token: ${{ github.token }} + pull_request_id: ${{ inputs.pr-number }} + + - name: Post summary comment + if: steps.check.outputs.has_fixes == 'true' && inputs.post-summary == 'true' && inputs.pr-number != '' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const fs = require('fs'); + const fixesFile = '${{ inputs.build-path }}/clang-tidy-fixes.yaml'; + + let totalIssues = 0; + const checkCounts = {}; + + if (fs.existsSync(fixesFile)) { + try { + const content = fs.readFileSync(fixesFile, 'utf8'); + const matches = content.matchAll(/^\s*DiagnosticName:\s+(\S.+?)$/gm); + for (const match of matches) { + const name = match[1].trim(); + checkCounts[name] = (checkCounts[name] || 0) + 1; + totalIssues++; + } + } catch (e) { console.log(e); } + } + + if (totalIssues > 0) { + const sorted = Object.entries(checkCounts).sort((a, b) => b[1] - a[1]); + let body = `## Clang-Tidy Check Results\n\nFound ${totalIssues} issue(s).\n\n### Issues by check:\n\n`; + sorted.forEach(([check, count]) => { body += `- **${check}**: ${count}\n`; }); + body += '\nSee inline comments for details. Comment `@phlexbot tidy-fix` to auto-fix.'; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ inputs.pr-number }}, + body: body + }); + } diff --git a/.github/actions/prepare-check-outputs/action.yaml b/.github/actions/prepare-check-outputs/action.yaml new file mode 100644 index 00000000..f1cf3242 --- /dev/null +++ b/.github/actions/prepare-check-outputs/action.yaml @@ -0,0 +1,97 @@ +name: "Prepare Check Outputs" +description: "Determines environment and target refs for check workflows." +inputs: + ref: + description: "Manual ref override" + required: false + repo: + description: "Manual repo override" + required: false + pr-base-sha: + description: "Manual base SHA override" + required: false + checkout-path: + description: "Manual checkout path override" + required: false + build-path: + description: "Manual build path override" + required: false +outputs: + is_act: + description: "Whether running in act" + value: ${{ steps.detect_act.outputs.is_act }} + ref: + description: "The resolved ref" + value: ${{ steps.resolve.outputs.ref }} + repo: + description: "The resolved repository" + value: ${{ steps.resolve.outputs.repo }} + base_sha: + description: "The resolved base SHA for diffing" + value: ${{ steps.resolve.outputs.base_sha }} + pr_number: + description: "The PR number if available" + value: ${{ steps.resolve.outputs.pr_number }} + checkout_path: + description: "The resolved checkout path" + value: ${{ steps.resolve.outputs.checkout_path }} + build_path: + description: "The resolved build path" + value: ${{ steps.resolve.outputs.build_path }} +runs: + using: "composite" + steps: + - name: Get PR Info + if: github.event_name == 'issue_comment' + id: get_pr + uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + + - name: Detect act environment + id: detect_act + uses: Framework-R-D/phlex/.github/actions/detect-act-env@main + + - name: Resolve outputs + id: resolve + shell: bash + run: | + REF="${{ inputs.ref }}" + REPO="${{ inputs.repo }}" + BASE_SHA="${{ inputs.pr-base-sha }}" + PR_NUMBER="" + + if [ "${{ github.event_name }}" == "pull_request" ]; then + REF="${REF:-${{ github.sha }}}" + BASE_SHA="${BASE_SHA:-${{ github.event.pull_request.base.sha }}}" + PR_NUMBER="${{ github.event.pull_request.number }}" + elif [ "${{ github.event_name }}" == "issue_comment" ]; then + REF="${REF:-${{ steps.get_pr.outputs.ref }}}" + REPO="${REPO:-${{ steps.get_pr.outputs.repo }}}" + BASE_SHA="${BASE_SHA:-${{ steps.get_pr.outputs.base_sha }}}" + PR_NUMBER="${{ github.event.issue.number }}" + elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + REF="${REF:-${{ github.event.inputs.ref || github.ref }}}" + BASE_SHA="${BASE_SHA:-${{ github.event.before }}}" + else + REF="${REF:-${{ github.sha }}}" + BASE_SHA="${BASE_SHA:-${{ github.event.before }}}" + fi + + REPO="${REPO:-${{ github.repository }}}" + REPO_NAME="${REPO##*/}" + + CHECKOUT_PATH="${{ inputs.checkout-path }}" + if [ -z "$CHECKOUT_PATH" ]; then + CHECKOUT_PATH="${REPO_NAME}-src" + fi + + BUILD_PATH="${{ inputs.build-path }}" + if [ -z "$BUILD_PATH" ]; then + BUILD_PATH="${REPO_NAME}-build" + fi + + echo "ref=$REF" >> "$GITHUB_OUTPUT" + echo "repo=$REPO" >> "$GITHUB_OUTPUT" + echo "base_sha=$BASE_SHA" >> "$GITHUB_OUTPUT" + echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT" + echo "checkout_path=$CHECKOUT_PATH" >> "$GITHUB_OUTPUT" + echo "build_path=$BUILD_PATH" >> "$GITHUB_OUTPUT" diff --git a/.github/actions/prepare-fix-outputs/action.yaml b/.github/actions/prepare-fix-outputs/action.yaml new file mode 100644 index 00000000..a48fbdd8 --- /dev/null +++ b/.github/actions/prepare-fix-outputs/action.yaml @@ -0,0 +1,66 @@ +name: "Prepare Fix Outputs" +description: "Determines the target ref and repository for fix workflows triggered by various events." +inputs: + ref: + description: "Manual ref override" + required: false + repo: + description: "Manual repo override" + required: false + checkout-path: + description: "Manual checkout path override" + required: false +outputs: + ref: + description: "The resolved ref" + value: ${{ steps.resolve.outputs.ref }} + repo: + description: "The resolved repository" + value: ${{ steps.resolve.outputs.repo }} + checkout_path: + description: "The resolved checkout path" + value: ${{ steps.resolve.outputs.checkout_path }} +runs: + using: "composite" + steps: + - name: Get PR Info + if: github.event_name == 'issue_comment' && (inputs.ref == '' || inputs.repo == '') + id: get_pr + uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + + - name: Resolve outputs + id: resolve + shell: bash + run: | + REF="${{ inputs.ref }}" + REPO="${{ inputs.repo }}" + + if [ -z "$REF" ]; then + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + REF="${{ github.event.inputs.ref || github.ref_name }}" + elif [ "${{ github.event_name }}" == "issue_comment" ]; then + REF="${{ steps.get_pr.outputs.ref }}" + else + REF="${{ github.ref_name }}" + fi + fi + + if [ -z "$REPO" ]; then + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + REPO="${{ github.repository }}" + elif [ "${{ github.event_name }}" == "issue_comment" ]; then + REPO="${{ steps.get_pr.outputs.repo }}" + else + REPO="${{ github.repository }}" + fi + fi + + REPO_NAME="${REPO##*/}" + CHECKOUT_PATH="${{ inputs.checkout-path }}" + if [ -z "$CHECKOUT_PATH" ]; then + CHECKOUT_PATH="${REPO_NAME}-src" + fi + + echo "ref=$REF" >> "$GITHUB_OUTPUT" + echo "repo=$REPO" >> "$GITHUB_OUTPUT" + echo "checkout_path=$CHECKOUT_PATH" >> "$GITHUB_OUTPUT" diff --git a/.github/actions/run-change-detection/action.yaml b/.github/actions/run-change-detection/action.yaml new file mode 100644 index 00000000..ad40d539 --- /dev/null +++ b/.github/actions/run-change-detection/action.yaml @@ -0,0 +1,68 @@ +name: "Run Change Detection" +description: "Encapsulates checkout, detection of relevant changes, and reporting." +inputs: + checkout-path: + description: "Path to check out code to" + required: true + ref: + description: "The branch, ref, or SHA to checkout" + required: true + repo: + description: "The repository to checkout from" + required: true + base-ref: + description: "The base ref for comparison" + required: true + file-type: + description: "Predefined file type (cpp, python, cmake, jsonnet, yaml, md)" + required: false + include-globs: + description: "Custom include globs" + required: false + exclude-globs: + description: "Custom exclude globs" + required: false + head-ref: + description: "Explicit head ref (defaults to inputs.ref)" + required: false + +outputs: + has_changes: + description: "Whether relevant changes were detected" + value: ${{ steps.filter.outputs.matched }} + matched_files: + description: "The list of matched files" + value: ${{ steps.filter.outputs.matched_files }} + +runs: + using: "composite" + steps: + - name: Check out source code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: ${{ inputs.checkout-path }} + ref: ${{ inputs.ref }} + repository: ${{ inputs.repo }} + + - name: Detect relevant changes + id: filter + uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + with: + repo-path: ${{ inputs.checkout-path }} + base-ref: ${{ inputs.base-ref }} + head-ref: ${{ inputs.head-ref || inputs.ref }} + file-type: ${{ inputs.file-type }} + include-globs: ${{ inputs.include-globs }} + exclude-globs: ${{ inputs.exclude-globs }} + + - name: Report detection outcome + shell: bash + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No relevant changes detected for ${{ inputs.file-type || 'custom globs' }}; job will be skipped." + else + echo "::group::Relevant files detected" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi diff --git a/.github/actions/workflow-setup/action.yaml b/.github/actions/workflow-setup/action.yaml new file mode 100644 index 00000000..a72de8d0 --- /dev/null +++ b/.github/actions/workflow-setup/action.yaml @@ -0,0 +1,136 @@ +name: "Workflow Setup" +description: "Handles ref resolution, environment detection, and change detection in a single step." +inputs: + mode: + description: "Workflow mode ('check' or 'fix')" + required: false + default: "check" + ref: + description: "Manual ref override" + required: false + repo: + description: "Manual repo override" + required: false + pr-base-sha: + description: "Manual base SHA override (check mode only)" + required: false + checkout-path: + description: "Manual checkout path override" + required: false + build-path: + description: "Manual build path override" + required: false + file-type: + description: "File type for change detection (e.g., cpp, python)" + required: false + include-globs: + description: "Include globs for change detection" + required: false + exclude-globs: + description: "Exclude globs for change detection" + required: false + head-ref: + description: "Explicit head ref for change detection (overrides the resolved ref)" + required: false + +outputs: + is_act: + description: "Whether running in act" + value: ${{ steps.prepare.outputs.is_act }} + ref: + description: "Resolved ref" + value: ${{ steps.prepare.outputs.ref }} + repo: + description: "Resolved repository" + value: ${{ steps.prepare.outputs.repo }} + base_sha: + description: "Resolved base SHA" + value: ${{ steps.prepare.outputs.base_sha }} + pr_number: + description: "PR number" + value: ${{ steps.prepare.outputs.pr_number }} + checkout_path: + description: "Resolved checkout path" + value: ${{ steps.prepare.outputs.checkout_path }} + build_path: + description: "Resolved build path" + value: ${{ steps.prepare.outputs.build_path }} + has_changes: + description: "Whether relevant changes were detected" + value: ${{ steps.changes.outputs.has_changes }} + +runs: + using: "composite" + steps: + - name: Prepare basic outputs (check mode) + if: inputs.mode == 'check' + id: prepare_check + uses: Framework-R-D/phlex/.github/actions/prepare-check-outputs@main + with: + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + pr-base-sha: ${{ inputs.pr-base-sha }} + checkout-path: ${{ inputs.checkout-path }} + build-path: ${{ inputs.build-path }} + + - name: Prepare basic outputs (fix mode) + if: inputs.mode == 'fix' + id: prepare_fix + uses: Framework-R-D/phlex/.github/actions/prepare-fix-outputs@main + with: + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + checkout-path: ${{ inputs.checkout-path }} + + - name: Standardize outputs + id: prepare + shell: bash + run: | + if [ "${{ inputs.mode }}" == "check" ]; then + echo "is_act=${{ steps.prepare_check.outputs.is_act }}" >> "$GITHUB_OUTPUT" + echo "ref=${{ steps.prepare_check.outputs.ref }}" >> "$GITHUB_OUTPUT" + echo "repo=${{ steps.prepare_check.outputs.repo }}" >> "$GITHUB_OUTPUT" + echo "base_sha=${{ steps.prepare_check.outputs.base_sha }}" >> "$GITHUB_OUTPUT" + echo "pr_number=${{ steps.prepare_check.outputs.pr_number }}" >> "$GITHUB_OUTPUT" + echo "checkout_path=${{ steps.prepare_check.outputs.checkout_path }}" >> "$GITHUB_OUTPUT" + echo "build_path=${{ steps.prepare_check.outputs.build_path }}" >> "$GITHUB_OUTPUT" + else + echo "is_act=false" >> "$GITHUB_OUTPUT" + echo "ref=${{ steps.prepare_fix.outputs.ref }}" >> "$GITHUB_OUTPUT" + echo "repo=${{ steps.prepare_fix.outputs.repo }}" >> "$GITHUB_OUTPUT" + echo "base_sha=" >> "$GITHUB_OUTPUT" + echo "pr_number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT" + echo "checkout_path=${{ steps.prepare_fix.outputs.checkout_path }}" >> "$GITHUB_OUTPUT" + CHECKOUT_PATH="${{ steps.prepare_fix.outputs.checkout_path }}" + REPO_NAME="${CHECKOUT_PATH%-src}" + BUILD_PATH="${{ inputs.build-path }}" + if [ -z "$BUILD_PATH" ]; then + BUILD_PATH="${REPO_NAME}-build" + fi + echo "build_path=$BUILD_PATH" >> "$GITHUB_OUTPUT" + fi + + - name: Run change detection + id: detect + if: (inputs.file-type != '' || inputs.include-globs != '') && steps.prepare.outputs.is_act != 'true' + uses: Framework-R-D/phlex/.github/actions/run-change-detection@main + with: + checkout-path: ${{ steps.prepare.outputs.checkout_path }} + ref: ${{ steps.prepare.outputs.ref }} + repo: ${{ steps.prepare.outputs.repo }} + base-ref: ${{ steps.prepare.outputs.base_sha }} + file-type: ${{ inputs.file-type }} + include-globs: ${{ inputs.include-globs }} + exclude-globs: ${{ inputs.exclude-globs }} + head-ref: ${{ inputs.head-ref }} + + - name: Set has_changes output + id: changes + shell: bash + run: | + if [ "${{ steps.prepare.outputs.is_act }}" = "true" ] && \ + ([ "${{ inputs.file-type }}" != "" ] || [ "${{ inputs.include-globs }}" != "" ]); then + echo "has_changes=true" >> "$GITHUB_OUTPUT" + else + echo "has_changes=${{ steps.detect.outputs.has_changes }}" >> "$GITHUB_OUTPUT" + fi diff --git a/.github/workflows/actionlint-check.yaml b/.github/workflows/actionlint-check.yaml index 4bb2c38c..4236c799 100644 --- a/.github/workflows/actionlint-check.yaml +++ b/.github/workflows/actionlint-check.yaml @@ -41,82 +41,40 @@ permissions: required: false type: string -env: - local_checkout_path: - ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', - github.event.repository.name) }} - jobs: - pre-check: - runs-on: ubuntu-latest - outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: - ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref)) || github.sha }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: - ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || - github.event.before }} - steps: - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - - detect-changes: - needs: pre-check - if: > - github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' + setup: runs-on: ubuntu-latest - permissions: - contents: read - packages: read outputs: - has_changes: ${{ steps.filter.outputs.matched }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} include-globs: | .github/workflows/**/*.yml .github/workflows/**/*.yaml .github/actions/**/*.yml .github/actions/**/*.yaml - - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No actionlint related changes detected; job will be skipped." - else - echo "::group::Actionlint relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + head-ref: ${{ inputs.pr-head-sha }} + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + pr-base-sha: ${{ inputs.pr-base-sha }} + checkout-path: ${{ inputs.checkout-path }} actionlint-check: - needs: [pre-check, detect-changes] + needs: setup if: > always() && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) + github.event_name == 'workflow_dispatch' || + inputs.skip-relevance-check || + needs.setup.outputs.has_changes == 'true' ) runs-on: ubuntu-latest permissions: @@ -126,9 +84,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} - path: ${{ env.local_checkout_path }} - repository: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + path: ${{ needs.setup.outputs.checkout_path }} + repository: ${{ needs.setup.outputs.repo }} - name: Announce actionlint check run: echo "➡️ Running actionlint check..." @@ -137,7 +95,7 @@ jobs: id: lint run: | docker run --rm \ - -v "${{ github.workspace }}/${{ env.local_checkout_path }}:/work" \ + -v "${{ github.workspace }}/${{ needs.setup.outputs.checkout_path }}:/work" \ -w /work \ rhysd/actionlint:latest \ -config-file .github/actionlint.yaml @@ -152,19 +110,3 @@ jobs: echo "::error::actionlint check failed. Please review the output above for details." exit 1 fi - - actionlint-check-skipped: - needs: [pre-check, detect-changes] - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes != 'true') - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: No relevant workflow changes detected - run: echo "::notice::No actionlint relevant changes detected; check skipped." diff --git a/.github/workflows/clang-format-check.yaml b/.github/workflows/clang-format-check.yaml index 97d809ee..f385f740 100644 --- a/.github/workflows/clang-format-check.yaml +++ b/.github/workflows/clang-format-check.yaml @@ -15,66 +15,27 @@ permissions: type: string jobs: - pre-check: + setup: runs-on: ubuntu-latest outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || github.sha }} - repo: ${{ github.repository }} - base_sha: ${{ github.event.pull_request.base.sha || github.event.before }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - - detect-changes: - needs: pre-check - if: > - github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act != 'true' - runs-on: ubuntu-latest - permissions: - contents: read - packages: read - outputs: - has_changes: ${{ steps.filter.outputs.matched }} - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - repo-path: phlex-src - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ needs.pre-check.outputs.ref }} file-type: cpp - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No clang-format related changes detected; job will be skipped." - else - echo "::group::Clang-format relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi - clang-format-check: - needs: [pre-check, detect-changes] + needs: setup if: > - always() && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) - ) + always() && (github.event_name == 'workflow_dispatch' || needs.setup.outputs.has_changes == 'true') runs-on: ubuntu-latest permissions: contents: read @@ -83,9 +44,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} - path: phlex-src - repository: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + path: ${{ needs.setup.outputs.checkout_path }} + repository: ${{ needs.setup.outputs.repo }} - name: Announce clang-format check run: echo "➡️ Running clang-format check..." @@ -94,7 +55,7 @@ jobs: id: lint uses: DoozyX/clang-format-lint-action@bcb4eb2cb0d707ee4f3e5cc3b456eb075f12cf73 # v0.20 with: - source: "./phlex-src" + source: "./${{ needs.setup.outputs.checkout_path }}" clangFormatVersion: 20 extensions: cpp,hpp,cpp.in,hpp.in continue-on-error: true @@ -109,16 +70,3 @@ jobs: echo "::error::Comment '@${{ github.event.repository.name }}bot format' on the PR to attempt auto-fix." exit 1 fi - - clang-format-check-skipped: - needs: [pre-check, detect-changes] - if: > - github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act != 'true' && - needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true' - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: No relevant C++ changes detected - run: echo "::notice::No clang-format relevant changes detected; check skipped." diff --git a/.github/workflows/clang-format-fix.yaml b/.github/workflows/clang-format-fix.yaml index e19d86be..14fab5e0 100644 --- a/.github/workflows/clang-format-fix.yaml +++ b/.github/workflows/clang-format-fix.yaml @@ -8,7 +8,9 @@ run-name: "${{ github.actor }} fixing C++ code format" workflow_dispatch: inputs: ref: - description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the repository's default branch." + description: + "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the + repository's default branch." required: false type: string workflow_call: @@ -50,43 +52,38 @@ run-name: "${{ github.actor }} fixing C++ code format" permissions: pull-requests: write contents: write - -env: - local_checkout_path: - ${{ inputs.checkout-path || format('{0}-src', github.event.repository.name) }} + issues: write jobs: - pre-check: + setup: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || inputs.ref != '' || ( + inputs.ref != '' || github.event_name == 'workflow_dispatch' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot clang-fix', github.event.repository.name)) ) - # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. - # This covers repo owners, invited collaborators, and all org members. outputs: - ref: - ${{ inputs.ref || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref_name)) || steps.get_pr.outputs.ref }} - repo: - ${{ inputs.repo || (github.event_name == 'workflow_dispatch' && - github.repository) || steps.get_pr.outputs.repo }} - + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} steps: - - name: Get PR Info - if: github.event_name == 'issue_comment' && inputs.ref == '' - id: get_pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main + with: + mode: fix + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + checkout-path: ${{ inputs.checkout-path }} apply_formatting: runs-on: ubuntu-latest name: Apply formatting - needs: pre-check - if: ${{ needs.pre-check.result == 'success' }} + needs: setup + if: ${{ needs.setup.result == 'success' }} outputs: changes: ${{ steps.handle_commit.outputs.changes }} pushed: ${{ steps.handle_commit.outputs.pushed }} @@ -96,14 +93,14 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + path: ${{ needs.setup.outputs.checkout_path }} + ref: ${{ needs.setup.outputs.ref }} + repository: ${{ needs.setup.outputs.repo }} token: ${{ secrets.WORKFLOW_PAT }} - uses: DoozyX/clang-format-lint-action@bcb4eb2cb0d707ee4f3e5cc3b456eb075f12cf73 # v0.20 with: - source: "./${{ env.local_checkout_path }}" + source: "./${{ needs.setup.outputs.checkout_path }}" clangFormatVersion: 20 inplace: "True" extensions: cpp,hpp,cpp.in,hpp.in @@ -113,46 +110,14 @@ jobs: uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: clang-format - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} - pr-info-ref: ${{ needs.pre-check.outputs.ref }} - pr-info-repo: ${{ needs.pre-check.outputs.repo }} + pr-info-ref: ${{ needs.setup.outputs.ref }} + pr-info-repo: ${{ needs.setup.outputs.repo }} skip-comment: ${{ inputs.skip-comment || 'false' }} - - name: Remove eyes reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - try { - const { data: reactions } = await github.rest.reactions.listForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id - }); - const eyesReaction = reactions.find( - (r) => r.content === 'eyes' && r.user && r.user.login === 'github-actions[bot]' - ); - if (eyesReaction) { - await github.rest.reactions.deleteForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - reaction_id: eyesReaction.id - }); - } - } catch (_) { - // Reaction cleanup is best-effort; do not fail the workflow. - } - - - name: Add completion reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + - name: Update PR comment reactions + if: always() && github.event_name == 'issue_comment' && inputs.skip-comment != 'true' + uses: Framework-R-D/phlex/.github/actions/complete-pr-comment@main with: - script: | - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'rocket' - }); + status: ${{ job.status }} diff --git a/.github/workflows/clang-tidy-check.yaml b/.github/workflows/clang-tidy-check.yaml index 0e9df1f4..921a5c01 100644 --- a/.github/workflows/clang-tidy-check.yaml +++ b/.github/workflows/clang-tidy-check.yaml @@ -17,7 +17,8 @@ permissions: type: string jobs: - pre-check: + setup: + runs-on: ubuntu-latest if: > github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || ( github.event_name == 'issue_comment' && @@ -25,78 +26,28 @@ jobs: contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, '@phlexbot tidy-check') ) - runs-on: ubuntu-latest outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: - ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || - steps.pr.outputs.ref || github.sha }} - repo: ${{ steps.pr.outputs.repo || github.repository }} - base_sha: ${{ steps.pr.outputs.base_sha || github.event.pull_request.base.sha || github.event.before }} - pr_number: ${{ github.event.pull_request.number || github.event.issue.number }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + build_path: ${{ steps.setup.outputs.build_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Get PR Info - if: github.event_name == 'issue_comment' - id: pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main - - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - - detect-changes: - needs: pre-check - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act - != 'true' - runs-on: ubuntu-latest - permissions: - contents: read - packages: read - - outputs: - has_changes: ${{ steps.filter.outputs.matched }} - - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - repo-path: phlex-src - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ needs.pre-check.outputs.ref }} file-type: | cpp cmake - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No clang-tidy relevant changes detected; job will be skipped." - else - echo "::group::Clang-tidy relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi - clang-tidy-check: - needs: [pre-check, detect-changes] + needs: setup if: > - always() && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) - ) + always() && (github.event_name == 'workflow_dispatch' || needs.setup.outputs.has_changes == 'true') runs-on: ubuntu-24.04 container: image: ghcr.io/framework-r-d/phlex-ci:latest @@ -105,17 +56,21 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} - path: phlex-src - repository: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + path: ${{ needs.setup.outputs.checkout_path }} + repository: ${{ needs.setup.outputs.repo }} - name: Setup build environment uses: Framework-R-D/phlex/.github/actions/setup-build-env@main + with: + build-path: ${{ needs.setup.outputs.build_path }} - name: Configure CMake (Debug) uses: Framework-R-D/phlex/.github/actions/configure-cmake@main with: build-type: Debug + source-path: ${{ needs.setup.outputs.checkout_path }} + build-path: ${{ needs.setup.outputs.build_path }} extra-options: "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_SCAN_FOR_MODULES=OFF -DCMAKE_CXX_CLANG_TIDY='clang-tidy;--export-fixes=clang-tidy-fixes.yaml'" @@ -125,160 +80,44 @@ jobs: shell: bash run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" echo "➡️ Running clang-tidy checks..." cmake_status=0 cmake --build . -j "$(nproc)" > clang-tidy.log 2>&1 || cmake_status=$? - # Distinguish tooling failures from issue detection by checking log content if [ "$cmake_status" -ne 0 ]; then - if ! grep -qE '^/.+\.(cpp|hpp|c|h):[0-9]+:[0-9]+: (warning|error):' clang-tidy.log; then - echo "::error::clang-tidy failed without producing diagnostic output (exit code $cmake_status)" - echo "::group::clang-tidy log output" - cat clang-tidy.log - echo "::endgroup::" - exit "$cmake_status" - fi - fi - - if grep -qE '^/.+\.(cpp|hpp|c|h):[0-9]+:[0-9]+: (warning|error):' clang-tidy.log; then - echo "has_issues=true" >> "$GITHUB_OUTPUT" + echo "::error::CMake build failed with exit code $cmake_status" + exit 1 + elif grep -qE '^/.+\.(cpp|hpp|c|h):[0-9]+:[0-9]+: (warning|error):' clang-tidy.log; then echo "::warning::Clang-tidy found issues in the code" - echo "Error count by check (full details in clang-tidy-log artifact):" - sed -nEe '\&^/& s&^.*\[([^][:space:]]+)\]$&\1&p' clang-tidy.log | sort | uniq -c | sort -n -k 1 -r - echo "Comment '@${{ github.event.repository.name }}bot tidy-fix [...]' on the PR to attempt auto-fix" + exit 1 else - echo "has_issues=false" >> "$GITHUB_OUTPUT" echo "✅ clang-tidy check passed" fi - - name: Upload clang-tidy report - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: clang-tidy-report - path: phlex-build/clang-tidy-fixes.yaml - retention-days: 7 - if-no-files-found: ignore - - - name: Upload clang-tidy log + - name: Upload artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: clang-tidy-log - path: phlex-build/clang-tidy.log + name: clang-tidy-check + path: | + ${{ needs.setup.outputs.build_path }}/clang-tidy-fixes.yaml + ${{ needs.setup.outputs.build_path }}/clang-tidy.log retention-days: 7 if-no-files-found: ignore - clang-tidy-pr-comments: - needs: [pre-check, clang-tidy-check] - if: - always() && needs.clang-tidy-check.result == 'success' && (github.event_name == 'pull_request' || - github.event_name == 'issue_comment') + report: + needs: [setup, clang-tidy-check] + if: always() && needs.setup.result == 'success' runs-on: ubuntu-latest permissions: pull-requests: write issues: write steps: - - name: Download clang-tidy report - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: clang-tidy-report - path: phlex-build - continue-on-error: true - - - name: Download clang-tidy log - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: clang-tidy-log - path: phlex-build - continue-on-error: true - - - name: Setup Node.js - if: github.event_name == 'issue_comment' - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - with: - node-version: "20.x" - - - name: Install js-yaml - if: github.event_name == 'issue_comment' - run: npm install js-yaml - - - name: Check if artifacts exist - id: check_artifacts - run: | - if [ -f phlex-build/clang-tidy-fixes.yaml ]; then - echo "has_fixes=true" >> "$GITHUB_OUTPUT" - else - echo "has_fixes=false" >> "$GITHUB_OUTPUT" - fi - - - name: Post clang-tidy comments - if: steps.check_artifacts.outputs.has_fixes == 'true' - uses: platisd/clang-tidy-pr-comments@28cfb84edafa771c044bde7e4a2a3fae57463818 # v1.8.0 - with: - clang_tidy_fixes: phlex-build/clang-tidy-fixes.yaml - github_token: ${{ secrets.GITHUB_TOKEN }} - pull_request_id: ${{ github.event.pull_request.number || needs.pre-check.outputs.pr_number }} - - - name: Post summary comment - if: steps.check_artifacts.outputs.has_fixes == 'true' && github.event_name == 'issue_comment' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + - name: Post Clang-Tidy results + uses: Framework-R-D/phlex/.github/actions/post-clang-tidy-results@main with: - script: | - const fs = require('fs'); - const path = require('path'); - const yaml = require('js-yaml'); - - const fixesFile = 'phlex-build/clang-tidy-fixes.yaml'; - const checkCounts = {}; - let totalIssues = 0; - - if (fs.existsSync(fixesFile)) { - try { - const content = yaml.load(fs.readFileSync(fixesFile, 'utf8')); - if (content?.Diagnostics) { - content.Diagnostics.forEach(diag => { - if (diag.DiagnosticName) { - checkCounts[diag.DiagnosticName] = (checkCounts[diag.DiagnosticName] || 0) + 1; - totalIssues++; - } - }); - } - } catch (e) { - console.log(`Error reading fixes file: ${e.message}`); - } - } - - const sorted = Object.entries(checkCounts).sort((a, b) => b[1] - a[1]); - let body = '## Clang-Tidy Check Results\n\n'; - body += `Found ${totalIssues} issue(s) in the code.\n\n`; - if (sorted.length > 0) { - body += '### Issues by check:\n\n'; - sorted.forEach(([check, count]) => { - body += `- **${check}**: ${count}\n`; - }); - body += '\n'; - } - body += 'See inline comments for details. '; - body += 'Comment `@phlexbot tidy-fix [...]` to attempt auto-fix.'; - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: ${{ needs.pre-check.outputs.pr_number }}, - body: body - }); - - clang-tidy-check-skipped: - needs: [pre-check, detect-changes] - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act - != 'true' && (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: No relevant clang-tidy changes detected - run: echo "::notice::No clang-tidy relevant changes detected; check skipped." + build-path: ${{ needs.setup.outputs.build_path }} + pr-number: ${{ needs.setup.outputs.pr_number }} + post-summary: "true" diff --git a/.github/workflows/clang-tidy-fix.yaml b/.github/workflows/clang-tidy-fix.yaml index 092ddd0d..e6edcaca 100644 --- a/.github/workflows/clang-tidy-fix.yaml +++ b/.github/workflows/clang-tidy-fix.yaml @@ -8,7 +8,9 @@ name: Clang-Tidy Fix workflow_dispatch: inputs: ref: - description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the repository's default branch." + description: + "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the + repository's default branch." required: false type: string tidy-checks: @@ -19,9 +21,10 @@ name: Clang-Tidy Fix permissions: pull-requests: write contents: write + issues: write jobs: - pre-check: + setup: runs-on: ubuntu-latest name: Parse command if: > @@ -34,14 +37,13 @@ jobs: # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. # This covers repo owners, invited collaborators, and all org members. outputs: - ref: - ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref_name)) || - steps.get_pr.outputs.ref }} - repo: ${{ steps.get_pr.outputs.repo || github.repository }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + build_path: ${{ steps.setup.outputs.build_path }} tidy_checks: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tidy-checks || steps.parse_comment.outputs.tidy_checks }} - steps: - id: parse_comment name: Parse comment for tidy checks @@ -56,16 +58,17 @@ jobs: tidy_checks="$(echo "$checks_line" | tr ',' ' ' | xargs -n1 | paste -sd, -)" echo "tidy_checks=$tidy_checks" >> "$GITHUB_OUTPUT" fi - - name: Get PR Info - if: github.event_name == 'issue_comment' - id: get_pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main + with: + mode: fix apply_tidy_fixes: runs-on: ubuntu-24.04 name: Apply clang-tidy fixes - needs: pre-check - if: ${{ needs.pre-check.result == 'success' }} + needs: setup + if: ${{ needs.setup.result == 'success' }} container: image: ghcr.io/framework-r-d/phlex-ci:latest @@ -74,43 +77,43 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + path: ${{ needs.setup.outputs.checkout_path }} + ref: ${{ needs.setup.outputs.ref }} + repository: ${{ needs.setup.outputs.repo }} token: ${{ secrets.WORKFLOW_PAT }} - name: Try to download existing fixes from check workflow id: download_fixes_check - if: needs.pre-check.outputs.tidy_checks == '' - uses: dawidd6/action-download-artifact@09b07ec687d10771279a426c79925ee415c12906 # v17 + if: needs.setup.outputs.tidy_checks == '' + uses: dawidd6/action-download-artifact@2536c51d3d126276eb39f74d6bc9c72ac6ef30d3 # v16 with: workflow: clang-tidy-check.yaml - name: clang-tidy-report + name: clang-tidy-check path: fixes - branch: ${{ needs.pre-check.outputs.ref }} + branch: ${{ needs.setup.outputs.ref }} continue-on-error: true - name: Try to download existing fixes from previous fix workflow id: download_fixes_fix - if: steps.download_fixes_check.outcome != 'success' && needs.pre-check.outputs.tidy_checks == '' - uses: dawidd6/action-download-artifact@09b07ec687d10771279a426c79925ee415c12906 # v17 + if: steps.download_fixes_check.outcome != 'success' && needs.setup.outputs.tidy_checks == '' + uses: dawidd6/action-download-artifact@2536c51d3d126276eb39f74d6bc9c72ac6ef30d3 # v16 with: workflow: clang-tidy-fix.yaml - name: clang-tidy-report + name: clang-tidy-fix path: fixes - branch: ${{ needs.pre-check.outputs.ref }} + branch: ${{ needs.setup.outputs.ref }} continue-on-error: true - name: Apply fixes from artifact if available id: apply_from_artifact if: - needs.pre-check.outputs.tidy_checks == '' && (steps.download_fixes_check.outcome == 'success' || + needs.setup.outputs.tidy_checks == '' && (steps.download_fixes_check.outcome == 'success' || steps.download_fixes_fix.outcome == 'success') run: | if [ -f fixes/clang-tidy-fixes.yaml ]; then echo "Applying fixes from existing artifact..." . /entrypoint.sh - cd phlex-src + cd "${{ needs.setup.outputs.checkout_path }}" clang-apply-replacements ../fixes || true echo "applied=true" >> "$GITHUB_OUTPUT" else @@ -120,12 +123,14 @@ jobs: - name: Setup build environment if: steps.apply_from_artifact.outputs.applied != 'true' uses: Framework-R-D/phlex/.github/actions/setup-build-env@main + with: + build-path: ${{ needs.setup.outputs.build_path }} - name: Prepare CMake configuration options if: steps.apply_from_artifact.outputs.applied != 'true' id: prep_tidy_opts env: - TIDY_CHECKS: ${{ needs.pre-check.outputs.tidy_checks }} + TIDY_CHECKS: ${{ needs.setup.outputs.tidy_checks }} run: | . /entrypoint.sh cd "$GITHUB_WORKSPACE" @@ -142,6 +147,8 @@ jobs: uses: Framework-R-D/phlex/.github/actions/configure-cmake@main with: build-type: Debug + source-path: ${{ needs.setup.outputs.checkout_path }} + build-path: ${{ needs.setup.outputs.build_path }} extra-options: "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_SCAN_FOR_MODULES=OFF -DCMAKE_CXX_CLANG_TIDY='${{ steps.prep_tidy_opts.outputs.clang_tidy_opts }}'" @@ -150,24 +157,25 @@ jobs: if: steps.apply_from_artifact.outputs.applied != 'true' run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" cmake --build . -j "$(nproc)" || true - name: Apply clang-tidy fixes if: steps.apply_from_artifact.outputs.applied != 'true' run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" if [ -f clang-tidy-fixes.yaml ]; then clang-apply-replacements . || true fi - - name: Upload clang-tidy report + - name: Upload artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: clang-tidy-report - path: phlex-build/clang-tidy-fixes.yaml + name: clang-tidy-fix + path: | + ${{ needs.setup.outputs.build_path }}/clang-tidy-fixes.yaml retention-days: 7 if-no-files-found: ignore @@ -175,36 +183,25 @@ jobs: uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: clang-tidy - working-directory: phlex-src + working-directory: ${{ needs.setup.outputs.checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} - pr-info-ref: ${{ needs.pre-check.outputs.ref }} - pr-info-repo: ${{ needs.pre-check.outputs.repo }} + pr-info-ref: ${{ needs.setup.outputs.ref }} + pr-info-repo: ${{ needs.setup.outputs.repo }} - clang-tidy-pr-comments: - needs: apply_tidy_fixes + report: + needs: [setup, apply_tidy_fixes] + if: always() && needs.setup.result == 'success' && needs.setup.outputs.pr_number != '' runs-on: ubuntu-latest permissions: pull-requests: write steps: - - name: Download clang-tidy report - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + - name: Post Clang-Tidy results + uses: Framework-R-D/phlex/.github/actions/post-clang-tidy-results@main with: - name: clang-tidy-report - path: phlex-build - continue-on-error: true - - - name: Check if artifacts exist - id: check_artifacts - run: | - if [ -f phlex-build/clang-tidy-fixes.yaml ]; then - echo "has_fixes=true" >> "$GITHUB_OUTPUT" - else - echo "has_fixes=false" >> "$GITHUB_OUTPUT" - fi + build-path: ${{ needs.setup.outputs.build_path }} + pr-number: ${{ needs.setup.outputs.pr_number }} - - name: Post clang-tidy comments - if: steps.check_artifacts.outputs.has_fixes == 'true' - uses: platisd/clang-tidy-pr-comments@28cfb84edafa771c044bde7e4a2a3fae57463818 # v1.8.0 + - name: Update PR comment reactions + uses: Framework-R-D/phlex/.github/actions/complete-pr-comment@main with: - clang_tidy_fixes: phlex-build/clang-tidy-fixes.yaml - github_token: ${{ secrets.GITHUB_TOKEN }} + status: ${{ needs.apply_tidy_fixes.result }} diff --git a/.github/workflows/cmake-build.yaml b/.github/workflows/cmake-build.yaml index 34d7e6bb..8530ffff 100644 --- a/.github/workflows/cmake-build.yaml +++ b/.github/workflows/cmake-build.yaml @@ -67,15 +67,9 @@ permissions: env: BUILD_TYPE: Release CICOLOR_FORCE: 1 - local_checkout_path: - ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', - github.event.repository.name) }} - local_build_path: - ${{ (github.event_name == 'workflow_call' && inputs.build-path) || format('{0}-build', github.event.repository.name) - }} jobs: - pre-check: + setup: if: > github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || github.event_name == 'push' || github.event_name == 'workflow_call' || ( @@ -84,85 +78,38 @@ jobs: contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot build', github.event.repository.name)) ) - # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. - # This covers repo owners, invited collaborators, and all org members. runs-on: ubuntu-latest permissions: contents: read packages: read outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: - ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref)) || steps.pr.outputs.ref || steps.pr.outputs.sha || github.sha }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || steps.pr.outputs.repo || github.repository }} - base_sha: - ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || steps.pr.outputs.base_sha || - github.event.pull_request.base.sha || github.event.before }} - + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + build_path: ${{ steps.setup.outputs.build_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Get PR Info - if: github.event_name == 'issue_comment' - id: pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main - - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - - detect-changes: - needs: pre-check - if: > - needs.pre-check.result == 'success' && ( - github.event_name == 'pull_request' || - github.event_name == 'push' || - ( - github.event_name == 'workflow_call' && - inputs.skip-relevance-check != 'true' && - github.event.inputs == null && - github.event.comment == null - ) - ) && needs.pre-check.outputs.is_act != 'true' - runs-on: ubuntu-latest - permissions: - contents: read - outputs: - has_changes: ${{ steps.filter.outputs.matched }} - - steps: - - name: Check out source code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + pr-base-sha: ${{ inputs.pr-base-sha }} + checkout-path: ${{ inputs.checkout-path }} + build-path: ${{ inputs.build-path }} + head-ref: ${{ inputs.pr-head-sha }} file-type: | cpp cmake - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No C++ or CMake changes detected; build will be skipped." - else - echo "::group::C++ and CMake relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi - generate-matrix: - needs: pre-check - if: needs.pre-check.result == 'success' + needs: setup + if: needs.setup.result == 'success' runs-on: ubuntu-latest outputs: matrix: ${{ steps.generate.outputs.matrix }} @@ -170,20 +117,16 @@ jobs: - id: generate uses: Framework-R-D/phlex/.github/actions/generate-build-matrix@main with: - user-input: - ${{ (github.event_name == 'workflow_call' && inputs.build-combinations) || - github.event.inputs.build-combinations }} + user-input: ${{ inputs.build-combinations || github.event.inputs.build-combinations }} comment-body: ${{ github.event.comment.body }} build: - needs: [pre-check, detect-changes, generate-matrix] + needs: [setup, generate-matrix] if: > - always() && needs.pre-check.result == 'success' && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) + always() && needs.setup.result == 'success' && ( + github.event_name == 'workflow_dispatch' || + inputs.skip-relevance-check || + needs.setup.outputs.has_changes == 'true' ) runs-on: ubuntu-24.04 strategy: @@ -197,14 +140,14 @@ jobs: - name: Check out code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + path: ${{ needs.setup.outputs.checkout_path }} + ref: ${{ needs.setup.outputs.ref }} + repository: ${{ needs.setup.outputs.repo }} - name: Setup build environment uses: Framework-R-D/phlex/.github/actions/setup-build-env@main with: - build-path: ${{ env.local_build_path }} + build-path: ${{ needs.setup.outputs.build_path }} - name: Announce CMake configuration run: echo "➡️ Configuring CMake..." @@ -212,15 +155,15 @@ jobs: - name: Extract repository name id: repo_name env: - REPO_FULL_NAME: ${{ needs.pre-check.outputs.repo }} + REPO_FULL_NAME: ${{ needs.setup.outputs.repo }} run: echo "name=${REPO_FULL_NAME##*/}" >> "$GITHUB_OUTPUT" - name: Configure CMake id: configure uses: Framework-R-D/phlex/.github/actions/configure-cmake@main with: - source-path: ${{ env.local_checkout_path }} - build-path: ${{ env.local_build_path }} + source-path: ${{ needs.setup.outputs.checkout_path }} + build-path: ${{ needs.setup.outputs.build_path }} build-type: ${{ env.BUILD_TYPE }} cpp-compiler: ${{ matrix.compiler == 'gcc' && 'g++' || 'clang++' }} extra-options: | @@ -231,13 +174,13 @@ jobs: id: build uses: Framework-R-D/phlex/.github/actions/build-cmake@main with: - build-path: ${{ env.local_build_path }} + build-path: ${{ needs.setup.outputs.build_path }} - name: Run tests if: matrix.sanitizer != 'valgrind' run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${{ env.local_build_path }}" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" echo "➡️ Running tests..." echo "::group::Running ctest" @@ -254,7 +197,7 @@ jobs: if: matrix.sanitizer == 'valgrind' run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${{ env.local_build_path }}" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" echo "➡️ Running Valgrind tests..." echo "::group::Running ctest -T memcheck" @@ -266,33 +209,17 @@ jobs: echo "⚠️ Valgrind tests failed, but the workflow will continue." fi - cmake-build-skipped: - needs: [pre-check, detect-changes] - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes != 'true') - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: No relevant C++ or CMake changes detected - run: echo "::notice::No relevant C++ or CMake changes detected; build skipped." - build-complete: - needs: [pre-check, build] + needs: [setup, build] if: > - always() && github.event_name == 'issue_comment' && needs.pre-check.result == 'success' + always() && github.event_name == 'issue_comment' && needs.setup.result == 'success' runs-on: ubuntu-latest permissions: pull-requests: write steps: - name: Comment on build completion - uses: Framework-R-D/phlex/.github/actions/pr-comment@main + uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: # yamllint disable rule:line-length message: | diff --git a/.github/workflows/cmake-format-check.yaml b/.github/workflows/cmake-format-check.yaml index 377cf295..8a7eb5ed 100644 --- a/.github/workflows/cmake-format-check.yaml +++ b/.github/workflows/cmake-format-check.yaml @@ -41,87 +41,45 @@ permissions: required: false type: string -env: - local_checkout_path: - ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', - github.event.repository.name) }} - jobs: - pre-check: - runs-on: ubuntu-latest - outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: - ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref)) || github.sha }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: - ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || - github.event.before }} - steps: - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - - detect-changes: - needs: pre-check - if: > - github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' + setup: runs-on: ubuntu-latest - permissions: - contents: read - packages: read outputs: - has_changes: ${{ steps.filter.outputs.matched }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} file-type: cmake - - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No CMake-related changes detected; formatting check will be skipped." - else - echo "::group::CMake-related files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + head-ref: ${{ inputs.pr-head-sha }} + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + pr-base-sha: ${{ inputs.pr-base-sha }} + checkout-path: ${{ inputs.checkout-path }} cmake-format-check: - needs: [pre-check, detect-changes] + needs: setup if: > always() && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) + github.event_name == 'workflow_dispatch' || + inputs.skip-relevance-check || + needs.setup.outputs.has_changes == 'true' ) runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} - path: ${{ env.local_checkout_path }} - repository: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + path: ${{ needs.setup.outputs.checkout_path }} + repository: ${{ needs.setup.outputs.repo }} - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -135,7 +93,7 @@ jobs: id: lint run: | echo "➡️ Checking CMake file formatting..." - gersemi --check ${{ env.local_checkout_path }} + gersemi --check ${{ needs.setup.outputs.checkout_path }} continue-on-error: true - name: Evaluate CMake formatting result @@ -150,16 +108,3 @@ jobs: exit 1 fi # yamllint enable - - cmake-format-check-skipped: - needs: [pre-check, detect-changes] - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes != 'true') - runs-on: ubuntu-latest - steps: - - name: No relevant CMake changes detected - run: echo "::notice::No CMake-related changes detected; cmake-format check skipped." diff --git a/.github/workflows/cmake-format-fix.yaml b/.github/workflows/cmake-format-fix.yaml index 2bb6f639..cd8894ce 100644 --- a/.github/workflows/cmake-format-fix.yaml +++ b/.github/workflows/cmake-format-fix.yaml @@ -8,7 +8,9 @@ run-name: "${{ github.actor }} fixing CMake format" workflow_dispatch: inputs: ref: - description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the repository's default branch." + description: + "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the + repository's default branch." required: false type: string workflow_call: @@ -50,43 +52,38 @@ run-name: "${{ github.actor }} fixing CMake format" permissions: pull-requests: write contents: write - -env: - local_checkout_path: - ${{ inputs.checkout-path || format('{0}-src', github.event.repository.name) }} + issues: write jobs: - pre-check: + setup: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || inputs.ref != '' || ( + inputs.ref != '' || github.event_name == 'workflow_dispatch' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot cmake-fix', github.event.repository.name)) ) - # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. - # This covers repo owners, invited collaborators, and all org members. outputs: - ref: - ${{ inputs.ref || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref_name)) || steps.get_pr.outputs.ref }} - repo: - ${{ inputs.repo || (github.event_name == 'workflow_dispatch' && - github.repository) || steps.get_pr.outputs.repo }} - + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} steps: - - name: Get PR Info - if: github.event_name == 'issue_comment' && inputs.ref == '' - id: get_pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main + with: + mode: fix + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + checkout-path: ${{ inputs.checkout-path }} apply_cmake_formatting: runs-on: ubuntu-latest name: Apply CMake formatting - needs: pre-check - if: needs.pre-check.result == 'success' + needs: setup + if: needs.setup.result == 'success' outputs: changes: ${{ steps.handle_commit.outputs.changes }} pushed: ${{ steps.handle_commit.outputs.pushed }} @@ -97,9 +94,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + path: ${{ needs.setup.outputs.checkout_path }} + ref: ${{ needs.setup.outputs.ref }} + repository: ${{ needs.setup.outputs.repo }} token: ${{ secrets.WORKFLOW_PAT }} - name: Set up Python @@ -113,53 +110,21 @@ jobs: - name: Apply CMake formatting run: | echo "Applying CMake formatting..." - gersemi -i ${{ env.local_checkout_path }} + gersemi -i ${{ needs.setup.outputs.checkout_path }} - name: Handle fix commit id: handle_commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: cmake-format - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} - pr-info-ref: ${{ needs.pre-check.outputs.ref }} - pr-info-repo: ${{ needs.pre-check.outputs.repo }} + pr-info-ref: ${{ needs.setup.outputs.ref }} + pr-info-repo: ${{ needs.setup.outputs.repo }} skip-comment: ${{ inputs.skip-comment || 'false' }} - - name: Remove eyes reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - try { - const { data: reactions } = await github.rest.reactions.listForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id - }); - const eyesReaction = reactions.find( - (r) => r.content === 'eyes' && r.user && r.user.login === 'github-actions[bot]' - ); - if (eyesReaction) { - await github.rest.reactions.deleteForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - reaction_id: eyesReaction.id - }); - } - } catch (_) { - // Reaction cleanup is best-effort; do not fail the workflow. - } - - - name: Add completion reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + - name: Update PR comment reactions + if: always() && github.event_name == 'issue_comment' && inputs.skip-comment != 'true' + uses: Framework-R-D/phlex/.github/actions/complete-pr-comment@main with: - script: | - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'rocket' - }); + status: ${{ job.status }} diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index a87081bd..6f612f0e 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -66,166 +66,89 @@ env: CPP_COMPILER: g++ jobs: - pre-check: + setup: runs-on: ubuntu-latest outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: - ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref)) || github.sha }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: - ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || - github.event.before }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + build_path: ${{ steps.setup.outputs.build_path }} skip_detection: ${{ steps.should_skip.outputs.skip }} - local_checkout_path: - ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', - github.event.repository.name) }} + has_changes_cpp: ${{ steps.detect_cpp.outputs.has_changes }} + has_changes_python: ${{ steps.detect_python.outputs.has_changes }} + has_changes_actions: ${{ steps.detect_actions.outputs.has_changes }} steps: - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main + with: + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + pr-base-sha: ${{ inputs.pr-base-sha }} + checkout-path: ${{ inputs.checkout-path }} + build-path: ${{ inputs.build-path }} - name: Determine if detection should be skipped id: should_skip run: | - # Skip detection for scheduled runs, workflow_dispatch, or workflow_call with language-matrix override if [ "${{ github.event_name }}" = "schedule" ] || \ [ "${{ github.event_name }}" = "workflow_dispatch" ] || \ [ "${{ github.event_name }}" = "push" ] || \ { [ "${{ github.event_name }}" = "workflow_call" ] && [ -n "${{ inputs.language-matrix }}" ]; } || \ - [ "${{ steps.detect_act.outputs.is_act }}" = "true" ]; then + [ "${{ steps.setup.outputs.is_act }}" = "true" ]; then echo "skip=true" >> "$GITHUB_OUTPUT" else echo "skip=false" >> "$GITHUB_OUTPUT" fi - detect-changes-cpp: - needs: pre-check - if: > - needs.pre-check.result == 'success' && needs.pre-check.outputs.skip_detection != 'true' - runs-on: ubuntu-latest - permissions: - contents: read - outputs: - has_changes: ${{ steps.filter.outputs.matched }} - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ needs.pre-check.outputs.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect C++ relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Detect C++ changes + id: detect_cpp + if: steps.should_skip.outputs.skip != 'true' + uses: Framework-R-D/phlex/.github/actions/run-change-detection@main with: - repo-path: ${{ needs.pre-check.outputs.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} + checkout-path: ${{ steps.setup.outputs.checkout_path }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base-ref: ${{ steps.setup.outputs.base_sha }} + head-ref: ${{ inputs.pr-head-sha || steps.setup.outputs.ref }} file-type: | cpp cmake - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No C++ relevant changes detected; C++ CodeQL scan will be skipped." - else - echo "::group::C++ CodeQL relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi - - detect-changes-python: - needs: pre-check - if: > - needs.pre-check.result == 'success' && needs.pre-check.outputs.skip_detection != 'true' - runs-on: ubuntu-latest - permissions: - contents: read - outputs: - has_changes: ${{ steps.filter.outputs.matched }} - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ needs.pre-check.outputs.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect Python relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Detect Python changes + id: detect_python + if: steps.should_skip.outputs.skip != 'true' + uses: Framework-R-D/phlex/.github/actions/run-change-detection@main with: - repo-path: ${{ needs.pre-check.outputs.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} + checkout-path: ${{ steps.setup.outputs.checkout_path }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base-ref: ${{ steps.setup.outputs.base_sha }} + head-ref: ${{ inputs.pr-head-sha || steps.setup.outputs.ref }} file-type: python - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No Python relevant changes detected; Python CodeQL scan will be skipped." - else - echo "::group::Python CodeQL relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi - - detect-changes-actions: - needs: pre-check - if: > - needs.pre-check.result == 'success' && needs.pre-check.outputs.skip_detection != 'true' - runs-on: ubuntu-latest - permissions: - contents: read - outputs: - has_changes: ${{ steps.filter.outputs.matched }} - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ needs.pre-check.outputs.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect Actions/workflow relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Detect Actions changes + id: detect_actions + if: steps.should_skip.outputs.skip != 'true' + uses: Framework-R-D/phlex/.github/actions/run-change-detection@main with: - repo-path: ${{ needs.pre-check.outputs.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} + checkout-path: ${{ steps.setup.outputs.checkout_path }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base-ref: ${{ steps.setup.outputs.base_sha }} + head-ref: ${{ inputs.pr-head-sha || steps.setup.outputs.ref }} include-globs: | .github/workflows/*.yaml .github/workflows/*.yml .github/actions/**/action.yaml .github/actions/**/action.yml - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No Actions/workflow relevant changes detected; Actions CodeQL scan will be skipped." - else - echo "::group::Actions CodeQL relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi - determine-languages: - needs: - [ - pre-check, - detect-changes-cpp, - detect-changes-python, - detect-changes-actions, - ] - if: always() && needs.pre-check.result == 'success' + needs: setup + if: always() && needs.setup.result == 'success' runs-on: ubuntu-latest outputs: languages: ${{ steps.build_matrix.outputs.languages }} @@ -236,7 +159,7 @@ jobs: LANGUAGE_MATRIX: ${{ inputs.language-matrix }} run: | # If detection was skipped, use all languages or the provided language-matrix - if [ "${{ needs.pre-check.outputs.skip_detection }}" = "true" ]; then + if [ "${{ needs.setup.outputs.skip_detection }}" = "true" ]; then if [ "${{ github.event_name }}" = "workflow_call" ] && [ -n "$LANGUAGE_MATRIX" ]; then # Validate that language-matrix is valid JSON if ! echo "$LANGUAGE_MATRIX" | python3 -c "import sys, json; json.load(sys.stdin)" 2>/dev/null; then @@ -252,16 +175,13 @@ jobs: # Build array based on detection results langs=() - if [ "${{ needs.detect-changes-cpp.result }}" = "success" ] && - [ "${{ needs.detect-changes-cpp.outputs.has_changes }}" = "true" ]; then + if [ "${{ needs.setup.outputs.has_changes_cpp }}" = "true" ]; then langs+=("cpp") fi - if [ "${{ needs.detect-changes-python.result }}" = "success" ] && - [ "${{ needs.detect-changes-python.outputs.has_changes }}" = "true" ]; then + if [ "${{ needs.setup.outputs.has_changes_python }}" = "true" ]; then langs+=("python") fi - if [ "${{ needs.detect-changes-actions.result }}" = "success" ] && - [ "${{ needs.detect-changes-actions.outputs.has_changes }}" = "true" ]; then + if [ "${{ needs.setup.outputs.has_changes_actions }}" = "true" ]; then langs+=("actions") fi @@ -281,7 +201,7 @@ jobs: fi codeql: - needs: [pre-check, determine-languages] + needs: [setup, determine-languages] if: > needs.determine-languages.result == 'success' && needs.determine-languages.outputs.languages != '[]' name: Analyze ${{ matrix.language }} with CodeQL @@ -289,13 +209,10 @@ jobs: container: image: ghcr.io/framework-r-d/phlex-ci:latest env: - local_checkout_path: ${{ needs.pre-check.outputs.local_checkout_path }} - local_build_path: - ${{ (github.event_name == 'workflow_call' && inputs.build-path) || format('{0}-build', - github.event.repository.name) }} + local_checkout_path: ${{ needs.setup.outputs.checkout_path }} + local_build_path: ${{ needs.setup.outputs.build_path }} CODEQL_EXTRACTOR_CPP_COMPILATION_DATABASE: - ${{ github.workspace }}/${{ (github.event_name == 'workflow_call' && inputs.build-path) || format('{0}-build', - github.event.repository.name) }}/compile_commands.json + ${{ github.workspace }}/${{ needs.setup.outputs.build_path }}/compile_commands.json strategy: fail-fast: false matrix: @@ -305,7 +222,7 @@ jobs: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} + ref: ${{ needs.setup.outputs.ref }} path: ${{ env.local_checkout_path }} fetch-depth: 0 diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 8c8169ed..60d7a6d3 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -28,7 +28,7 @@ permissions: pull-requests: read jobs: - pre-check: + setup: if: > github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || github.event_name == 'push' || ( @@ -37,77 +37,33 @@ jobs: contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot coverage', github.event.repository.name)) ) - # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. - # This covers repo owners, invited collaborators, and all org members. runs-on: ubuntu-latest outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: - ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || - steps.pr.outputs.ref || github.sha }} - repo: ${{ steps.pr.outputs.repo || github.repository }} - base_sha: ${{ steps.pr.outputs.base_sha || github.event.pull_request.base.sha || github.event.before }} - steps: - - name: Get PR Info - if: github.event_name == 'issue_comment' - id: pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main - - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - detect-changes: - needs: pre-check - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && github.event_name != - 'issue_comment' && needs.pre-check.outputs.is_act != 'true' - runs-on: ubuntu-latest - permissions: - contents: read - packages: read - outputs: - has_changes: ${{ steps.filter.outputs.matched }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + build_path: ${{ steps.setup.outputs.build_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Check out source code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - repo-path: phlex-src - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ needs.pre-check.outputs.ref }} + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + pr-base-sha: ${{ inputs.pr-base-sha }} file-type: | cpp cmake python - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No coverage relevant changes detected; workflow will be skipped." - else - echo "::group::Coverage relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi - coverage: - needs: [pre-check, detect-changes] + needs: setup if: > - always() && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) - ) + always() && (github.event_name == 'workflow_dispatch' || needs.setup.outputs.has_changes == 'true') runs-on: ubuntu-24.04 container: image: ghcr.io/framework-r-d/phlex-ci:latest @@ -141,12 +97,14 @@ jobs: - name: Check out source code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} - path: phlex-src - repository: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + path: ${{ needs.setup.outputs.checkout_path }} + repository: ${{ needs.setup.outputs.repo }} - name: Setup build environment uses: Framework-R-D/phlex/.github/actions/setup-build-env@main + with: + build-path: ${{ needs.setup.outputs.build_path }} - name: Announce CMake configuration run: echo "➡️ Configuring CMake for coverage..." @@ -158,6 +116,8 @@ jobs: with: cpp-compiler: g++ preset: coverage-gcc + source-path: ${{ needs.setup.outputs.checkout_path }} + build-path: ${{ needs.setup.outputs.build_path }} enable-form: ${{ steps.coverage_options.outputs.enable_form }} form-root-storage: ${{ steps.coverage_options.outputs.enable_form }} @@ -168,6 +128,8 @@ jobs: with: cpp-compiler: clang++ preset: coverage-clang + source-path: ${{ needs.setup.outputs.checkout_path }} + build-path: ${{ needs.setup.outputs.build_path }} enable-form: ${{ steps.coverage_options.outputs.enable_form }} form-root-storage: ${{ steps.coverage_options.outputs.enable_form }} @@ -184,14 +146,16 @@ jobs: - name: Build with coverage instrumentation id: build uses: Framework-R-D/phlex/.github/actions/build-cmake@main + with: + build-path: ${{ needs.setup.outputs.build_path }} - name: Run tests with coverage run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" echo "➡️ Running tests with coverage..." - PROFILE_ROOT="$GITHUB_WORKSPACE/phlex-build/test/profraw" + PROFILE_ROOT="$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}/test/profraw" echo "Cleaning LLVM profile directory: $PROFILE_ROOT" rm -rf "$PROFILE_ROOT" mkdir -p "$PROFILE_ROOT" @@ -213,7 +177,7 @@ jobs: shell: bash run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" echo "➡️ Generating coverage reports for GCC..." echo "::group::Running coverage-gcov target" @@ -232,7 +196,7 @@ jobs: shell: bash run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" echo "➡️ Generating coverage reports for Clang..." echo "::group::Running coverage-llvm target" @@ -251,7 +215,7 @@ jobs: shell: bash run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" echo "➡️ Generating Python coverage report..." echo "::group::Running coverage-python target" @@ -275,7 +239,7 @@ jobs: if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }} shell: bash run: | - cd "$GITHUB_WORKSPACE/phlex-build" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" if [ -f coverage.xml ]; then echo "has_coverage_xml=true" >> "$GITHUB_OUTPUT" else @@ -311,7 +275,7 @@ jobs: ARTIFACT_DIR="$GITHUB_WORKSPACE/coverage-artifacts" rm -rf "$ARTIFACT_DIR" mkdir -p "$ARTIFACT_DIR" - cd "$GITHUB_WORKSPACE/phlex-build" + cd "$GITHUB_WORKSPACE/${{ needs.setup.outputs.build_path }}" for file in \ coverage-llvm.txt \ coverage-llvm.info \ @@ -432,16 +396,3 @@ jobs: path: coverage-artifacts/coverage-html/ if-no-files-found: warn retention-days: 30 - - coverage-skipped: - needs: [pre-check, detect-changes] - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act - != 'true' && (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: No relevant coverage changes detected - run: echo "::notice::No relevant C++ changes detected; coverage workflow skipped." diff --git a/.github/workflows/format-all.yaml b/.github/workflows/format-all.yaml index 53d99978..c166d5e0 100644 --- a/.github/workflows/format-all.yaml +++ b/.github/workflows/format-all.yaml @@ -9,225 +9,120 @@ on: permissions: pull-requests: write contents: write + issues: write jobs: - pre-check: + setup: runs-on: ubuntu-latest - name: Parse command if: > github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot format', github.event.repository.name)) outputs: - ref: ${{ steps.get_pr.outputs.ref }} - repo: ${{ steps.get_pr.outputs.repo }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} steps: - - name: Get PR Info - id: get_pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main + with: + mode: fix clang-format: - needs: pre-check - uses: Framework-R-D/phlex/.github/workflows/clang-format-fix.yaml@main + needs: setup + uses: ./.github/workflows/clang-format-fix.yaml with: - checkout-path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + repo: ${{ needs.setup.outputs.repo }} skip-comment: "true" secrets: inherit cmake-format: - needs: pre-check - uses: Framework-R-D/phlex/.github/workflows/cmake-format-fix.yaml@main + needs: setup + uses: ./.github/workflows/cmake-format-fix.yaml with: - checkout-path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + repo: ${{ needs.setup.outputs.repo }} skip-comment: "true" secrets: inherit header-guards: - needs: pre-check - uses: Framework-R-D/phlex/.github/workflows/header-guards-fix.yaml@main + needs: setup + uses: ./.github/workflows/header-guards-fix.yaml with: - checkout-path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + repo: ${{ needs.setup.outputs.repo }} skip-comment: "true" secrets: inherit jsonnet-format: - needs: pre-check - uses: Framework-R-D/phlex/.github/workflows/jsonnet-format-fix.yaml@main + needs: setup + uses: ./.github/workflows/jsonnet-format-fix.yaml with: - checkout-path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + repo: ${{ needs.setup.outputs.repo }} skip-comment: "true" secrets: inherit markdown: - needs: pre-check - uses: Framework-R-D/phlex/.github/workflows/markdown-fix.yaml@main + needs: setup + uses: ./.github/workflows/markdown-fix.yaml with: - checkout-path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + repo: ${{ needs.setup.outputs.repo }} skip-comment: "true" secrets: inherit python: - needs: pre-check - uses: Framework-R-D/phlex/.github/workflows/python-fix.yaml@main + needs: setup + uses: ./.github/workflows/python-fix.yaml with: - checkout-path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + repo: ${{ needs.setup.outputs.repo }} skip-comment: "true" secrets: inherit yaml: - needs: pre-check - uses: Framework-R-D/phlex/.github/workflows/yaml-fix.yaml@main + needs: setup + uses: ./.github/workflows/yaml-fix.yaml with: - checkout-path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + repo: ${{ needs.setup.outputs.repo }} skip-comment: "true" secrets: inherit combine-results: - needs: [pre-check, clang-format, cmake-format, header-guards, jsonnet-format, markdown, python, yaml] - if: ${{ always() && needs.pre-check.result == 'success' }} + needs: [setup, clang-format, cmake-format, header-guards, jsonnet-format, markdown, python, yaml] + if: always() && needs.setup.result == 'success' runs-on: ubuntu-latest steps: - - name: Remove eyes reaction - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - try { - const { data: reactions } = await github.rest.reactions.listForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id - }); - - const eyesReaction = reactions.find( - (r) => r.content === 'eyes' && r.user && r.user.login === 'github-actions[bot]' - ); - - if (eyesReaction && eyesReaction.id) { - await github.rest.reactions.deleteForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - reaction_id: eyesReaction.id - }); - } - } catch (_) { - // Reaction cleanup is best-effort; do not fail the workflow. - } - - - name: Add completion reaction - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'rocket' - }); - - name: Collect results id: collect - shell: bash - run: | - messages=() - has_failures=false - - check_job() { - local name="$1" result="$2" changes="$3" pushed="$4" patch_name="$5" sha_short="$6" - if [ "$result" == "failure" ] || [ "$result" == "cancelled" ] || [ "$result" == "skipped" ]; then - messages+=("❌ $name workflow $result") - has_failures=true - elif [ "$changes" == "true" ]; then - if [ "$pushed" == "true" ]; then - messages+=("✅ $name fixes pushed (commit $sha_short)") - elif [ -n "$patch_name" ]; then - messages+=("⚠️ $name fixes available as a patch file (\`$patch_name\`) in the \"fix-patch\" artifact. Download the artifact from this workflow run, then run: \`git apply $patch_name\` from the repository root.") - fi - fi - } - - check_job "clang-format" \ - "${{ needs.clang-format.result }}" \ - "${{ needs.clang-format.outputs.changes }}" \ - "${{ needs.clang-format.outputs.pushed }}" \ - "${{ needs.clang-format.outputs.patch_name }}" \ - "${{ needs.clang-format.outputs.commit_sha_short }}" - - check_job "cmake-format" \ - "${{ needs.cmake-format.result }}" \ - "${{ needs.cmake-format.outputs.changes }}" \ - "${{ needs.cmake-format.outputs.pushed }}" \ - "${{ needs.cmake-format.outputs.patch_name }}" \ - "${{ needs.cmake-format.outputs.commit_sha_short }}" - - check_job "header-guards" \ - "${{ needs.header-guards.result }}" \ - "${{ needs.header-guards.outputs.changes }}" \ - "${{ needs.header-guards.outputs.pushed }}" \ - "${{ needs.header-guards.outputs.patch_name }}" \ - "${{ needs.header-guards.outputs.commit_sha_short }}" - - check_job "jsonnetfmt" \ - "${{ needs.jsonnet-format.result }}" \ - "${{ needs.jsonnet-format.outputs.changes }}" \ - "${{ needs.jsonnet-format.outputs.pushed }}" \ - "${{ needs.jsonnet-format.outputs.patch_name }}" \ - "${{ needs.jsonnet-format.outputs.commit_sha_short }}" - - check_job "markdownlint" \ - "${{ needs.markdown.result }}" \ - "${{ needs.markdown.outputs.changes }}" \ - "${{ needs.markdown.outputs.pushed }}" \ - "${{ needs.markdown.outputs.patch_name }}" \ - "${{ needs.markdown.outputs.commit_sha_short }}" - - check_job "ruff" \ - "${{ needs.python.result }}" \ - "${{ needs.python.outputs.changes }}" \ - "${{ needs.python.outputs.pushed }}" \ - "${{ needs.python.outputs.patch_name }}" \ - "${{ needs.python.outputs.commit_sha_short }}" - - check_job "YAML formatter" \ - "${{ needs.yaml.result }}" \ - "${{ needs.yaml.outputs.changes }}" \ - "${{ needs.yaml.outputs.pushed }}" \ - "${{ needs.yaml.outputs.patch_name }}" \ - "${{ needs.yaml.outputs.commit_sha_short }}" - - if [ ${#messages[@]} -eq 0 ]; then - echo "message=No automatic format fixes were necessary." >> "$GITHUB_OUTPUT" - else + uses: Framework-R-D/phlex/.github/actions/collect-format-results@main + with: + results-json: | { - echo "message<> "$GITHUB_OUTPUT" - fi - - if [ "$has_failures" == "true" ]; then - exit 1 - fi + "clang-format": { "result": "${{ needs.clang-format.result }}", "changes": "${{ needs.clang-format.outputs.changes }}", "pushed": "${{ needs.clang-format.outputs.pushed }}", "sha": "${{ needs.clang-format.outputs.commit_sha_short }}", "patch_name": "${{ needs.clang-format.outputs.patch_name }}" }, + "cmake-format": { "result": "${{ needs.cmake-format.result }}", "changes": "${{ needs.cmake-format.outputs.changes }}", "pushed": "${{ needs.cmake-format.outputs.pushed }}", "sha": "${{ needs.cmake-format.outputs.commit_sha_short }}", "patch_name": "${{ needs.cmake-format.outputs.patch_name }}" }, + "header-guards": { "result": "${{ needs.header-guards.result }}", "changes": "${{ needs.header-guards.outputs.changes }}", "pushed": "${{ needs.header-guards.outputs.pushed }}", "sha": "${{ needs.header-guards.outputs.commit_sha_short }}", "patch_name": "${{ needs.header-guards.outputs.patch_name }}" }, + "jsonnetfmt": { "result": "${{ needs.jsonnet-format.result }}", "changes": "${{ needs.jsonnet-format.outputs.changes }}", "pushed": "${{ needs.jsonnet-format.outputs.pushed }}", "sha": "${{ needs.jsonnet-format.outputs.commit_sha_short }}", "patch_name": "${{ needs.jsonnet-format.outputs.patch_name }}" }, + "markdownlint": { "result": "${{ needs.markdown.result }}", "changes": "${{ needs.markdown.outputs.changes }}", "pushed": "${{ needs.markdown.outputs.pushed }}", "sha": "${{ needs.markdown.outputs.commit_sha_short }}", "patch_name": "${{ needs.markdown.outputs.patch_name }}" }, + "ruff": { "result": "${{ needs.python.result }}", "changes": "${{ needs.python.outputs.changes }}", "pushed": "${{ needs.python.outputs.pushed }}", "sha": "${{ needs.python.outputs.commit_sha_short }}", "patch_name": "${{ needs.python.outputs.patch_name }}" }, + "YAML formatter": { "result": "${{ needs.yaml.result }}", "changes": "${{ needs.yaml.outputs.changes }}", "pushed": "${{ needs.yaml.outputs.pushed }}", "sha": "${{ needs.yaml.outputs.commit_sha_short }}", "patch_name": "${{ needs.yaml.outputs.patch_name }}" } + } - name: Post combined comment - if: always() uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: message: ${{ steps.collect.outputs.message }} + + - name: Update PR comment reactions + uses: Framework-R-D/phlex/.github/actions/complete-pr-comment@main + with: + status: ${{ steps.collect.outputs.has_failures == 'true' && 'failure' || 'success' }} + + - name: Fail on formatter failures + if: steps.collect.outputs.has_failures == 'true' + run: | + echo "One or more formatters failed, were canceled, or were skipped." + exit 1 diff --git a/.github/workflows/header-guards-check.yaml b/.github/workflows/header-guards-check.yaml index 8c5ffe60..42bd4762 100644 --- a/.github/workflows/header-guards-check.yaml +++ b/.github/workflows/header-guards-check.yaml @@ -41,78 +41,36 @@ permissions: required: false type: string -env: - local_checkout_path: - ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', - github.event.repository.name) }} - jobs: - pre-check: + setup: runs-on: ubuntu-latest outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: - ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref)) || github.sha }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: - ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || - github.event.before }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - - detect-changes: - needs: pre-check - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' - runs-on: ubuntu-latest - permissions: - contents: read - packages: read - outputs: - has_changes: ${{ steps.filter.outputs.matched }} - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} file-type: cpp - - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No header file changes detected; check will be skipped." - else - echo "::group::Header files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + head-ref: ${{ inputs.pr-head-sha }} + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + pr-base-sha: ${{ inputs.pr-base-sha }} + checkout-path: ${{ inputs.checkout-path }} header-guards-check: - needs: [pre-check, detect-changes] + needs: setup if: > always() && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) + github.event_name == 'workflow_dispatch' || + inputs.skip-relevance-check || + needs.setup.outputs.has_changes == 'true' ) runs-on: ubuntu-latest permissions: @@ -122,9 +80,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} - path: ${{ env.local_checkout_path }} - repository: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + path: ${{ needs.setup.outputs.checkout_path }} + repository: ${{ needs.setup.outputs.repo }} - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -133,7 +91,7 @@ jobs: - name: Check header guards id: check - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} run: | python3 scripts/fix_header_guards.py --check --root . phlex plugins form > check_output.txt 2>&1 || echo "has_issues=true" >> "$GITHUB_OUTPUT" cat check_output.txt @@ -144,19 +102,3 @@ jobs: echo "::error::Header guard check failed." echo "::error::Comment '@${{ github.event.repository.name }}bot header-guards-fix' on the PR to auto-fix." exit 1 - - header-guards-check-skipped: - needs: [pre-check, detect-changes] - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes != 'true') - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: No relevant header changes detected - run: echo "::notice::No header file changes detected; header-guards-check skipped." diff --git a/.github/workflows/header-guards-fix.yaml b/.github/workflows/header-guards-fix.yaml index 2c0f7fb6..e0bbb7e3 100644 --- a/.github/workflows/header-guards-fix.yaml +++ b/.github/workflows/header-guards-fix.yaml @@ -43,50 +43,47 @@ run-name: "${{ github.actor }} fixing header guards" workflow_dispatch: inputs: ref: - description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the repository's default branch." + description: + "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the + repository's default branch." required: false type: string permissions: pull-requests: write contents: write - -env: - local_checkout_path: - ${{ inputs.checkout-path || format('{0}-src', github.event.repository.name) }} + issues: write jobs: - pre-check: + setup: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || inputs.ref != '' || ( + inputs.ref != '' || github.event_name == 'workflow_dispatch' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot header-guards-fix', github.event.repository.name)) ) - # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. - # This covers repo owners, invited collaborators, and all org members. outputs: - ref: - ${{ inputs.ref || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref_name)) || steps.get_pr.outputs.ref }} - repo: - ${{ inputs.repo || (github.event_name == 'workflow_dispatch' && - github.repository) || steps.get_pr.outputs.repo }} - + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} steps: - - name: Get PR Info - if: github.event_name == 'issue_comment' && inputs.ref == '' - id: get_pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main + with: + mode: fix + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + checkout-path: ${{ inputs.checkout-path }} apply_fixes: runs-on: ubuntu-latest name: Apply fixes - needs: pre-check - if: needs.pre-check.result == 'success' + needs: setup + if: needs.setup.result == 'success' outputs: changes: ${{ steps.handle_commit.outputs.changes }} pushed: ${{ steps.handle_commit.outputs.pushed }} @@ -97,9 +94,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + path: ${{ needs.setup.outputs.checkout_path }} + ref: ${{ needs.setup.outputs.ref }} + repository: ${{ needs.setup.outputs.repo }} token: ${{ secrets.WORKFLOW_PAT }} - name: Set up Python @@ -108,7 +105,7 @@ jobs: python-version: "3.12" - name: Fix header guards - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} run: | echo "Fixing header guards..." python3 scripts/fix_header_guards.py --root . phlex plugins form || true @@ -118,46 +115,14 @@ jobs: uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: header-guards - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} - pr-info-ref: ${{ needs.pre-check.outputs.ref }} - pr-info-repo: ${{ needs.pre-check.outputs.repo }} + pr-info-ref: ${{ needs.setup.outputs.ref }} + pr-info-repo: ${{ needs.setup.outputs.repo }} skip-comment: ${{ inputs.skip-comment || 'false' }} - - name: Remove eyes reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - try { - const { data: reactions } = await github.rest.reactions.listForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id - }); - const eyesReaction = reactions.find( - (r) => r.content === 'eyes' && r.user && r.user.login === 'github-actions[bot]' - ); - if (eyesReaction) { - await github.rest.reactions.deleteForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - reaction_id: eyesReaction.id - }); - } - } catch (_) { - // Reaction cleanup is best-effort; do not fail the workflow. - } - - - name: Add completion reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + - name: Update PR comment reactions + if: always() && github.event_name == 'issue_comment' && inputs.skip-comment != 'true' + uses: Framework-R-D/phlex/.github/actions/complete-pr-comment@main with: - script: | - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'rocket' - }); + status: ${{ job.status }} diff --git a/.github/workflows/jsonnet-format-check.yaml b/.github/workflows/jsonnet-format-check.yaml index 91f7c95d..476c5350 100644 --- a/.github/workflows/jsonnet-format-check.yaml +++ b/.github/workflows/jsonnet-format-check.yaml @@ -41,78 +41,36 @@ permissions: required: false type: string -env: - local_checkout_path: - ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', - github.event.repository.name) }} - jobs: - pre-check: - runs-on: ubuntu-latest - outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: - ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref)) || github.sha }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: - ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || - github.event.before }} - steps: - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - - detect-changes: - needs: pre-check - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' + setup: runs-on: ubuntu-latest - permissions: - contents: read - packages: read outputs: - has_changes: ${{ steps.filter.outputs.matched }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} file-type: jsonnet - - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No Jsonnet-related changes detected; formatting check will be skipped." - else - echo "::group::Jsonnet-related files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + head-ref: ${{ inputs.pr-head-sha }} + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + pr-base-sha: ${{ inputs.pr-base-sha }} + checkout-path: ${{ inputs.checkout-path }} jsonnet-format-check: - needs: [pre-check, detect-changes] + needs: setup if: > always() && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) + github.event_name == 'workflow_dispatch' || + inputs.skip-relevance-check || + needs.setup.outputs.has_changes == 'true' ) runs-on: ubuntu-latest container: @@ -122,13 +80,13 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - path: ${{ env.local_checkout_path }} + ref: ${{ needs.setup.outputs.ref }} + repository: ${{ needs.setup.outputs.repo }} + path: ${{ needs.setup.outputs.checkout_path }} - name: Check Jsonnet formatting id: lint - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} run: | find . \( -name "*.jsonnet" -o -name "*.libsonnet" \) -print0 | xargs -0 -r -I {} \ bash -c 'jsonnetfmt --test "{}" || (echo "FAILED: {}" && diff -u <(jsonnetfmt "{}") "{}" && exit 1)' @@ -146,16 +104,3 @@ jobs: exit 1 fi # yamllint enable - - jsonnet-format-check-skipped: - needs: [pre-check, detect-changes] - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes != 'true') - runs-on: ubuntu-latest - steps: - - name: No relevant Jsonnet changes detected - run: echo "::notice::No Jsonnet-related changes detected; jsonnet-format check skipped." diff --git a/.github/workflows/jsonnet-format-fix.yaml b/.github/workflows/jsonnet-format-fix.yaml index 5a02e4aa..aff39b7e 100644 --- a/.github/workflows/jsonnet-format-fix.yaml +++ b/.github/workflows/jsonnet-format-fix.yaml @@ -8,7 +8,9 @@ run-name: "${{ github.actor }} fixing Jsonnet format" workflow_dispatch: inputs: ref: - description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the repository's default branch." + description: + "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the + repository's default branch." required: false type: string workflow_call: @@ -50,43 +52,38 @@ run-name: "${{ github.actor }} fixing Jsonnet format" permissions: pull-requests: write contents: write - -env: - local_checkout_path: - ${{ inputs.checkout-path || format('{0}-src', github.event.repository.name) }} + issues: write jobs: - pre-check: + setup: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || inputs.ref != '' || ( + inputs.ref != '' || github.event_name == 'workflow_dispatch' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot jsonnet-fix', github.event.repository.name)) ) - # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. - # This covers repo owners, invited collaborators, and all org members. outputs: - ref: - ${{ inputs.ref || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref_name)) || steps.get_pr.outputs.ref }} - repo: - ${{ inputs.repo || (github.event_name == 'workflow_dispatch' && - github.repository) || steps.get_pr.outputs.repo }} - + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} steps: - - name: Get PR Info - if: github.event_name == 'issue_comment' && inputs.ref == '' - id: get_pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main + with: + mode: fix + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + checkout-path: ${{ inputs.checkout-path }} apply_jsonnet_formatting: runs-on: ubuntu-latest name: Apply Jsonnet formatting - needs: pre-check - if: needs.pre-check.result == 'success' + needs: setup + if: needs.setup.result == 'success' outputs: changes: ${{ steps.handle_commit.outputs.changes }} pushed: ${{ steps.handle_commit.outputs.pushed }} @@ -98,15 +95,15 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + path: ${{ needs.setup.outputs.checkout_path }} + ref: ${{ needs.setup.outputs.ref }} + repository: ${{ needs.setup.outputs.repo }} token: ${{ secrets.WORKFLOW_PAT }} - name: Apply Jsonnet formatting id: lint env: - CHECKOUT_PATH: ${{ env.local_checkout_path }} + CHECKOUT_PATH: ${{ needs.setup.outputs.checkout_path }} # yamllint disable rule:line-length run: | docker run --rm -v "${{ github.workspace }}:/work" -w /work --user root public.ecr.aws/bitnami/jsonnet:latest \ @@ -119,46 +116,14 @@ jobs: uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: jsonnetfmt - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} - pr-info-ref: ${{ needs.pre-check.outputs.ref }} - pr-info-repo: ${{ needs.pre-check.outputs.repo }} + pr-info-ref: ${{ needs.setup.outputs.ref }} + pr-info-repo: ${{ needs.setup.outputs.repo }} skip-comment: ${{ inputs.skip-comment || 'false' }} - - name: Remove eyes reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - try { - const { data: reactions } = await github.rest.reactions.listForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id - }); - const eyesReaction = reactions.find( - (r) => r.content === 'eyes' && r.user && r.user.login === 'github-actions[bot]' - ); - if (eyesReaction) { - await github.rest.reactions.deleteForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - reaction_id: eyesReaction.id - }); - } - } catch (_) { - // Reaction cleanup is best-effort; do not fail the workflow. - } - - - name: Add completion reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + - name: Update PR comment reactions + if: always() && github.event_name == 'issue_comment' && inputs.skip-comment != 'true' + uses: Framework-R-D/phlex/.github/actions/complete-pr-comment@main with: - script: | - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'rocket' - }); + status: ${{ job.status }} diff --git a/.github/workflows/markdown-check.yaml b/.github/workflows/markdown-check.yaml index e2b04050..1968e927 100644 --- a/.github/workflows/markdown-check.yaml +++ b/.github/workflows/markdown-check.yaml @@ -41,78 +41,36 @@ permissions: required: false type: string -env: - local_checkout_path: - ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', - github.event.repository.name) }} - jobs: - pre-check: - runs-on: ubuntu-latest - outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: - ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref)) || github.sha }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: - ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || - github.event.before }} - steps: - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - - detect-changes: - needs: pre-check - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' + setup: runs-on: ubuntu-latest - permissions: - contents: read - packages: read outputs: - has_changes: ${{ steps.filter.outputs.matched }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} file-type: md - - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No Markdown-related changes detected; formatting check will be skipped." - else - echo "::group::Markdown-related files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + head-ref: ${{ inputs.pr-head-sha }} + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + pr-base-sha: ${{ inputs.pr-base-sha }} + checkout-path: ${{ inputs.checkout-path }} markdown-check: - needs: [pre-check, detect-changes] + needs: setup if: > always() && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) + github.event_name == 'workflow_dispatch' || + inputs.skip-relevance-check || + needs.setup.outputs.has_changes == 'true' ) runs-on: ubuntu-latest permissions: @@ -122,9 +80,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} - path: ${{ env.local_checkout_path }} - repository: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + path: ${{ needs.setup.outputs.checkout_path }} + repository: ${{ needs.setup.outputs.repo }} - name: Add problem matcher uses: xt0rted/markdownlint-problem-matcher@1a5fabfb577370cfdf5af944d418e4be3ea06f27 # v3.0.0 @@ -134,8 +92,8 @@ jobs: uses: DavidAnson/markdownlint-cli2-action@07035fd053f7be764496c0f8d8f9f41f98305101 # v22.0.0 with: globs: | - ${{ env.local_checkout_path }}/**/*.md - !${{ env.local_checkout_path }}/**/CHANGELOG.md + ${{ needs.setup.outputs.checkout_path }}/**/*.md + !${{ needs.setup.outputs.checkout_path }}/**/CHANGELOG.md continue-on-error: true - name: Evaluate markdownlint result @@ -150,19 +108,3 @@ jobs: exit 1 fi # yamllint enable - - markdown-check-skipped: - needs: [pre-check, detect-changes] - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes != 'true') - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: No relevant Markdown changes detected - run: echo "::notice::No Markdown-related changes detected; markdown-check skipped." diff --git a/.github/workflows/markdown-fix.yaml b/.github/workflows/markdown-fix.yaml index bee7f76e..c75c0d78 100644 --- a/.github/workflows/markdown-fix.yaml +++ b/.github/workflows/markdown-fix.yaml @@ -43,50 +43,47 @@ run-name: "${{ github.actor }} fixing Markdown format" workflow_dispatch: inputs: ref: - description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the repository's default branch." + description: + "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the + repository's default branch." required: false type: string permissions: pull-requests: write contents: write - -env: - local_checkout_path: - ${{ inputs.checkout-path || format('{0}-src', github.event.repository.name) }} + issues: write jobs: - pre-check: + setup: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || inputs.ref != '' || ( + inputs.ref != '' || github.event_name == 'workflow_dispatch' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot markdown-fix', github.event.repository.name)) ) - # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. - # This covers repo owners, invited collaborators, and all org members. outputs: - ref: - ${{ inputs.ref || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref_name)) || steps.get_pr.outputs.ref }} - repo: - ${{ inputs.repo || (github.event_name == 'workflow_dispatch' && - github.repository) || steps.get_pr.outputs.repo }} - + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} steps: - - name: Get PR Info - if: github.event_name == 'issue_comment' && inputs.ref == '' - id: get_pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main + with: + mode: fix + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + checkout-path: ${{ inputs.checkout-path }} apply_fixes: runs-on: ubuntu-latest name: Apply fixes - needs: pre-check - if: needs.pre-check.result == 'success' + needs: setup + if: needs.setup.result == 'success' outputs: changes: ${{ steps.handle_commit.outputs.changes }} pushed: ${{ steps.handle_commit.outputs.pushed }} @@ -97,9 +94,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + path: ${{ needs.setup.outputs.checkout_path }} + ref: ${{ needs.setup.outputs.ref }} + repository: ${{ needs.setup.outputs.repo }} token: ${{ secrets.WORKFLOW_PAT }} - name: Set up Node.js @@ -111,7 +108,7 @@ jobs: run: npm install -g markdownlint-cli2 - name: Run markdownlint-cli2 fix - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} run: | echo "Fixing Markdown format with markdownlint-cli2..." markdownlint-cli2 --fix "**/*.md" "!**/CHANGELOG.md" || true @@ -121,46 +118,14 @@ jobs: uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: markdownlint - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} - pr-info-ref: ${{ needs.pre-check.outputs.ref }} - pr-info-repo: ${{ needs.pre-check.outputs.repo }} + pr-info-ref: ${{ needs.setup.outputs.ref }} + pr-info-repo: ${{ needs.setup.outputs.repo }} skip-comment: ${{ inputs.skip-comment || 'false' }} - - name: Remove eyes reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - try { - const { data: reactions } = await github.rest.reactions.listForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id - }); - const eyesReaction = reactions.find( - (r) => r.content === 'eyes' && r.user && r.user.login === 'github-actions[bot]' - ); - if (eyesReaction) { - await github.rest.reactions.deleteForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - reaction_id: eyesReaction.id - }); - } - } catch (_) { - // Reaction cleanup is best-effort; do not fail the workflow. - } - - - name: Add completion reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + - name: Update PR comment reactions + if: always() && github.event_name == 'issue_comment' && inputs.skip-comment != 'true' + uses: Framework-R-D/phlex/.github/actions/complete-pr-comment@main with: - script: | - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'rocket' - }); + status: ${{ job.status }} diff --git a/.github/workflows/python-check.yaml b/.github/workflows/python-check.yaml index 8724da18..d4eda96b 100644 --- a/.github/workflows/python-check.yaml +++ b/.github/workflows/python-check.yaml @@ -41,78 +41,36 @@ permissions: required: false type: string -env: - local_checkout_path: - ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', - github.event.repository.name) }} - jobs: - pre-check: - runs-on: ubuntu-latest - outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: - ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref)) || github.sha }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: - ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || - github.event.before }} - steps: - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - - detect-changes: - needs: pre-check - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' + setup: runs-on: ubuntu-latest - permissions: - contents: read - packages: read outputs: - has_changes: ${{ steps.filter.outputs.matched }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} file-type: python - - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No python check relevant changes detected; job will be skipped." - else - echo "::group::Python check relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + head-ref: ${{ inputs.pr-head-sha }} + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + pr-base-sha: ${{ inputs.pr-base-sha }} + checkout-path: ${{ inputs.checkout-path }} python-check: - needs: [pre-check, detect-changes] + needs: setup if: > always() && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) + github.event_name == 'workflow_dispatch' || + inputs.skip-relevance-check || + needs.setup.outputs.has_changes == 'true' ) runs-on: ubuntu-latest permissions: @@ -122,9 +80,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} - path: ${{ env.local_checkout_path }} - repository: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + path: ${{ needs.setup.outputs.checkout_path }} + repository: ${{ needs.setup.outputs.repo }} - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -137,7 +95,7 @@ jobs: - name: Run ruff check id: ruff - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} env: FORCE_COLOR: 1 run: | @@ -147,7 +105,7 @@ jobs: - name: Run MyPy check id: mypy - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} run: | echo "➡️ Checking Python code with MyPy..." mypy --color-output . @@ -164,19 +122,3 @@ jobs: exit 1 fi # yamllint enable - - python-check-skipped: - needs: [pre-check, detect-changes] - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( - github.event_name != 'workflow_call' || - (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes != 'true') - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: No relevant Python changes detected - run: echo "No Python relevant changes detected; check skipped." diff --git a/.github/workflows/python-fix.yaml b/.github/workflows/python-fix.yaml index 1f2bcc05..a4ed9b83 100644 --- a/.github/workflows/python-fix.yaml +++ b/.github/workflows/python-fix.yaml @@ -8,7 +8,9 @@ run-name: "${{ github.actor }} fixing Python code" workflow_dispatch: inputs: ref: - description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the repository's default branch." + description: + "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the + repository's default branch." required: false type: string workflow_call: @@ -50,42 +52,38 @@ run-name: "${{ github.actor }} fixing Python code" permissions: pull-requests: write contents: write - -env: - local_checkout_path: - ${{ inputs.checkout-path || format('{0}-src', github.event.repository.name) }} + issues: write jobs: - pre-check: + setup: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || inputs.ref != '' || ( + inputs.ref != '' || github.event_name == 'workflow_dispatch' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot python-fix', github.event.repository.name)) ) - # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. - # This covers repo owners, invited collaborators, and all org members. outputs: - ref: - ${{ inputs.ref || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref_name)) || steps.get_pr.outputs.ref }} - repo: - ${{ inputs.repo || (github.event_name == 'workflow_dispatch' && - github.repository) || steps.get_pr.outputs.repo }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} steps: - - name: Get PR Info - if: github.event_name == 'issue_comment' && inputs.ref == '' - id: get_pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main + with: + mode: fix + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + checkout-path: ${{ inputs.checkout-path }} apply_fixes: runs-on: ubuntu-latest name: Apply fixes - needs: pre-check - if: needs.pre-check.result == 'success' + needs: setup + if: needs.setup.result == 'success' outputs: changes: ${{ steps.handle_commit.outputs.changes }} pushed: ${{ steps.handle_commit.outputs.pushed }} @@ -95,9 +93,9 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + path: ${{ needs.setup.outputs.checkout_path }} + ref: ${{ needs.setup.outputs.ref }} + repository: ${{ needs.setup.outputs.repo }} token: ${{ secrets.WORKFLOW_PAT }} - name: Set up Python @@ -110,7 +108,7 @@ jobs: pip install ruff - name: Run ruff format and fix - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} env: FORCE_COLOR: 1 run: | @@ -124,46 +122,14 @@ jobs: uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: ruff - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} - pr-info-ref: ${{ needs.pre-check.outputs.ref }} - pr-info-repo: ${{ needs.pre-check.outputs.repo }} + pr-info-ref: ${{ needs.setup.outputs.ref }} + pr-info-repo: ${{ needs.setup.outputs.repo }} skip-comment: ${{ inputs.skip-comment || 'false' }} - - name: Remove eyes reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - try { - const { data: reactions } = await github.rest.reactions.listForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id - }); - const eyesReaction = reactions.find( - (r) => r.content === 'eyes' && r.user && r.user.login === 'github-actions[bot]' - ); - if (eyesReaction) { - await github.rest.reactions.deleteForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - reaction_id: eyesReaction.id - }); - } - } catch (_) { - // Reaction cleanup is best-effort; do not fail the workflow. - } - - - name: Add completion reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + - name: Update PR comment reactions + if: always() && github.event_name == 'issue_comment' && inputs.skip-comment != 'true' + uses: Framework-R-D/phlex/.github/actions/complete-pr-comment@main with: - script: | - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'rocket' - }); + status: ${{ job.status }} diff --git a/.github/workflows/yaml-check.yaml b/.github/workflows/yaml-check.yaml index 1fa6129d..05c41ed2 100644 --- a/.github/workflows/yaml-check.yaml +++ b/.github/workflows/yaml-check.yaml @@ -14,75 +14,35 @@ permissions: type: string jobs: - pre-check: + setup: runs-on: ubuntu-latest outputs: - is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || github.sha }} - repo: ${{ github.repository }} - base_sha: ${{ github.event.pull_request.base.sha || github.event.before }} + is_act: ${{ steps.setup.outputs.is_act }} + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + base_sha: ${{ steps.setup.outputs.base_sha }} + pr_number: ${{ steps.setup.outputs.pr_number }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} + has_changes: ${{ steps.setup.outputs.has_changes }} steps: - - name: Detect act environment - id: detect_act - uses: Framework-R-D/phlex/.github/actions/detect-act-env@main - - detect-changes: - needs: pre-check - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act - != 'true' - runs-on: ubuntu-latest - permissions: - contents: read - packages: read - outputs: - has_changes: ${{ steps.filter.outputs.matched }} - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main with: - repo-path: phlex-src - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ needs.pre-check.outputs.ref }} file-type: yaml - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No YAML-related changes detected; check will be skipped." - else - echo "::group::YAML-related files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi - yaml-check: - needs: [pre-check, detect-changes] + needs: setup if: > - always() && ( - needs.detect-changes.result == 'skipped' || - ( - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes == 'true' - ) - ) + always() && (github.event_name == 'workflow_dispatch' || needs.setup.outputs.has_changes == 'true') runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ needs.pre-check.outputs.ref }} - path: phlex-src - repository: ${{ needs.pre-check.outputs.repo }} + ref: ${{ needs.setup.outputs.ref }} + path: ${{ needs.setup.outputs.checkout_path }} + repository: ${{ needs.setup.outputs.repo }} - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -94,7 +54,7 @@ jobs: - name: Run yamllint id: lint - working-directory: phlex-src + working-directory: ${{ needs.setup.outputs.checkout_path }} run: yamllint . continue-on-error: true @@ -108,16 +68,3 @@ jobs: echo "::error::Comment '@${{ github.event.repository.name }}bot yaml-fix' on the PR to auto-fix." exit 1 fi - - yaml-check-skipped: - needs: [pre-check, detect-changes] - if: > - needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act - != 'true' && (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: No relevant YAML changes detected - run: echo "::notice::No YAML-related changes detected; yaml-check skipped." diff --git a/.github/workflows/yaml-fix.yaml b/.github/workflows/yaml-fix.yaml index 36c9f5c5..842c65d7 100644 --- a/.github/workflows/yaml-fix.yaml +++ b/.github/workflows/yaml-fix.yaml @@ -8,7 +8,9 @@ name: YAML Fix workflow_dispatch: inputs: ref: - description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the repository's default branch." + description: + "The branch name to checkout and push fixes to (must be a branch, not a commit SHA). Defaults to the + repository's default branch." required: false type: string workflow_call: @@ -50,43 +52,38 @@ name: YAML Fix permissions: pull-requests: write contents: write - -env: - local_checkout_path: - ${{ inputs.checkout-path || format('{0}-src', github.event.repository.name) }} + issues: write jobs: - pre-check: + setup: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || inputs.ref != '' || ( + inputs.ref != '' || github.event_name == 'workflow_dispatch' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot yaml-fix', github.event.repository.name)) ) - # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. - # This covers repo owners, invited collaborators, and all org members. outputs: - ref: - ${{ inputs.ref || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref_name)) || steps.get_pr.outputs.ref }} - repo: - ${{ inputs.repo || (github.event_name == 'workflow_dispatch' && - github.repository) || steps.get_pr.outputs.repo }} - + ref: ${{ steps.setup.outputs.ref }} + repo: ${{ steps.setup.outputs.repo }} + checkout_path: ${{ steps.setup.outputs.checkout_path }} steps: - - name: Get PR Info - if: github.event_name == 'issue_comment' && inputs.ref == '' - id: get_pr - uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + - name: Workflow setup + id: setup + uses: Framework-R-D/phlex/.github/actions/workflow-setup@main + with: + mode: fix + ref: ${{ inputs.ref }} + repo: ${{ inputs.repo }} + checkout-path: ${{ inputs.checkout-path }} apply_yaml_fixes: runs-on: ubuntu-latest name: Apply YAML fixes - needs: pre-check - if: ${{ needs.pre-check.result == 'success' }} + needs: setup + if: ${{ needs.setup.result == 'success' }} outputs: changes: ${{ steps.handle_commit.outputs.changes }} pushed: ${{ steps.handle_commit.outputs.pushed }} @@ -98,9 +95,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + path: ${{ needs.setup.outputs.checkout_path }} + ref: ${{ needs.setup.outputs.ref }} + repository: ${{ needs.setup.outputs.repo }} token: ${{ secrets.WORKFLOW_PAT }} - name: Setup Node.js @@ -113,7 +110,7 @@ jobs: - name: Apply YAML formatting run: | - cd ${{ env.local_checkout_path }} + cd ${{ needs.setup.outputs.checkout_path }} prettier --write '**/*.{yaml,yml}' - name: Handle fix commit @@ -121,46 +118,14 @@ jobs: uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: YAML formatter - working-directory: ${{ env.local_checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} - pr-info-ref: ${{ needs.pre-check.outputs.ref }} - pr-info-repo: ${{ needs.pre-check.outputs.repo }} + pr-info-ref: ${{ needs.setup.outputs.ref }} + pr-info-repo: ${{ needs.setup.outputs.repo }} skip-comment: ${{ inputs.skip-comment || 'false' }} - - name: Remove eyes reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - try { - const { data: reactions } = await github.rest.reactions.listForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id - }); - const eyesReaction = reactions.find( - (r) => r.content === 'eyes' && r.user && r.user.login === 'github-actions[bot]' - ); - if (eyesReaction) { - await github.rest.reactions.deleteForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - reaction_id: eyesReaction.id - }); - } - } catch (_) { - // Reaction cleanup is best-effort; do not fail the workflow. - } - - - name: Add completion reaction - if: github.event_name == 'issue_comment' && inputs.skip-comment != 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + - name: Update PR comment reactions + if: always() && github.event_name == 'issue_comment' && inputs.skip-comment != 'true' + uses: Framework-R-D/phlex/.github/actions/complete-pr-comment@main with: - script: | - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'rocket' - }); + status: ${{ job.status }}