diff --git a/.github/REUSABLE_WORKFLOWS.md b/.github/REUSABLE_WORKFLOWS.md index 82317010b..6e1c05a05 100644 --- a/.github/REUSABLE_WORKFLOWS.md +++ b/.github/REUSABLE_WORKFLOWS.md @@ -29,13 +29,15 @@ To run a workflow manually: If you are developing on a fork of `Framework-R-D/phlex` itself, the CI/CD workflows will run automatically on your pull requests within the fork, just as they do on the main repository. You do not need to use the `uses:` syntax described below. -However, to enable the automatic fixing features (e.g., for `cmake-format-fix` or `python-fix`), you will need to perform two steps: +However, to enable the automatic fixing features (e.g., for `cmake-format-fix` or `python-fix` workflows), you will need to perform two steps: 1. **Enable Workflows:** By default, GitHub Actions are disabled on forks. You must manually enable them by going to the `Actions` tab of your forked repository and clicking the "I understand my workflows, go ahead and enable them" button. 1. **Create the `WORKFLOW_PAT` Secret:** The auto-fix workflows require a Personal Access Token (PAT) with write permissions to commit changes back to your PR branch. Follow the instructions below to create a PAT and add it as a secret named `WORKFLOW_PAT` **to your forked repository's settings**. Once you have done this, you can trigger the auto-fix workflows by commenting on a pull request in your fork (e.g., `@${{ github.event.repository.name }}bot format`). +**Note on Authorization:** Comment-triggered workflows use authorization checks to ensure only trusted users can trigger potentially code-modifying operations. The workflows check that the comment author has one of the following associations: `OWNER`, `COLLABORATOR`, or `MEMBER`. This covers repository owners, explicitly invited collaborators, and organization members with any level of repository access. For a detailed analysis of the authorization model and security considerations, see [AUTHORIZATION_ANALYSIS.md](AUTHORIZATION_ANALYSIS.md). + ### Creating a Personal Access Token (PAT) For workflows that automatically commit fixes to pull requests (e.g., formatters), you must create a Personal Access Token (PAT) and add it as a secret to your repository. @@ -137,6 +139,8 @@ jobs: - `checkout-path` (string, optional): Path to check out code to. - `skip-relevance-check` (boolean, optional, default: `false`): Bypass the check that only runs if CMake files have changed. +- `ref` (string, optional): The branch, ref, or SHA to check out. +- `repo` (string, optional): The repository to check out from. - `pr-base-sha` (string, optional): Base SHA of the PR for relevance check. - `pr-head-sha` (string, optional): Head SHA of the PR for relevance check. @@ -153,23 +157,33 @@ on: types: [created] jobs: - format-cmake: - # Run only on comments from collaborators/owners that start with the bot command + pre-check: + # Extract PR details for the comment trigger if: > github.event.issue.pull_request && - (github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'OWNER') && - startsWith(github.event.comment.body, format('@{0}bot format', github.event.repository.name)) + contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && + ( + startsWith(github.event.comment.body, format('@{0}bot format', github.event.repository.name)) || + startsWith(github.event.comment.body, format('@{0}bot cmake-fix', github.event.repository.name)) + ) + runs-on: ubuntu-latest + outputs: + ref: ${{ steps.pr_info.outputs.ref }} + repo: ${{ steps.pr_info.outputs.repo }} + steps: + - id: pr_info + uses: Framework-R-D/phlex/.github/actions/get-pr-info@ + + format-cmake: + needs: pre-check uses: Framework-R-D/phlex/.github/workflows/cmake-format-fix.yaml@ with: - # The ref and repo of the PR need to be retrieved and passed - ref: ${{ steps.get_pr_info.outputs.ref }} - repo: ${{ steps.get_pr_info.outputs.repo }} + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.outputs.repo }} secrets: WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} ``` -*Note: You would need a preliminary step (`get_pr_info`) to extract the PR's `ref` and `repo` from the `issue_comment` event.* - #### All Inputs - `checkout-path` (string, optional): Path to check out code to. @@ -192,6 +206,8 @@ jobs: - `checkout-path` (string, optional): Path to check out code to. - `skip-relevance-check` (boolean, optional, default: `false`): Bypass the check that only runs if Python files have changed. +- `ref` (string, optional): The branch, ref, or SHA to check out. +- `repo` (string, optional): The repository to check out from. - `pr-base-sha` (string, optional): Base SHA of the PR for relevance check. - `pr-head-sha` (string, optional): Head SHA of the PR for relevance check. @@ -208,17 +224,25 @@ on: types: [created] jobs: - fix-python: - # Run only on comments from collaborators/owners that start with the bot command + pre-check: if: > github.event.issue.pull_request && - (github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'OWNER') && + contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && startsWith(github.event.comment.body, format('@{0}bot python-fix', github.event.repository.name)) + runs-on: ubuntu-latest + outputs: + ref: ${{ steps.pr_info.outputs.ref }} + repo: ${{ steps.pr_info.outputs.repo }} + steps: + - id: pr_info + uses: Framework-R-D/phlex/.github/actions/get-pr-info@ + + fix-python: + needs: pre-check uses: Framework-R-D/phlex/.github/workflows/python-fix.yaml@ with: - # The ref and repo of the PR need to be retrieved and passed - ref: ${{ steps.get_pr_info.outputs.ref }} - repo: ${{ steps.get_pr_info.outputs.repo }} + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.outputs.repo }} secrets: WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} ``` @@ -244,11 +268,11 @@ jobs: #### All Inputs - `checkout-path` (string, optional): Path to check out code to. -- `skip-relevance-check` (boolean, optional, default: `false`): Bypass the check that only runs if Markdown files have changed. Note that this workflow automatically emulates the trigger type of the caller; it will run relevance checks if called from a `pull_request` or `push` event, and skip them (running on all files) otherwise. -- `pr-base-sha` (string, optional): Base SHA of the PR for relevance check. -- `pr-head-sha` (string, optional): Head SHA of the PR for relevance check. +- `skip-relevance-check` (boolean, optional, default: `false`): Bypass the check that only runs if Markdown files have changed. - `ref` (string, optional): The branch, ref, or SHA to check out. - `repo` (string, optional): The repository to check out from. +- `pr-base-sha` (string, optional): Base SHA of the PR for relevance check. +- `pr-head-sha` (string, optional): Head SHA of the PR for relevance check. ### 7. `markdown-fix.yaml` @@ -263,20 +287,29 @@ on: types: [created] jobs: - fix-markdown: - # Run only on comments from collaborators/owners that start with the bot command + pre-check: if: > + github.event_name == 'issue_comment' && github.event.issue.pull_request && - (github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'OWNER') && + contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && ( startsWith(github.event.comment.body, format('@{0}bot format', github.event.repository.name)) || startsWith(github.event.comment.body, format('@{0}bot markdown-fix', github.event.repository.name)) ) + runs-on: ubuntu-latest + outputs: + ref: ${{ steps.pr_info.outputs.ref }} + repo: ${{ steps.pr_info.outputs.repo }} + steps: + - id: pr_info + uses: Framework-R-D/phlex/.github/actions/get-pr-info@ + + fix-markdown: + needs: pre-check uses: Framework-R-D/phlex/.github/workflows/markdown-fix.yaml@ with: - # The ref and repo of the PR need to be retrieved and passed - ref: ${{ steps.get_pr_info.outputs.ref }} - repo: ${{ steps.get_pr_info.outputs.repo }} + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.outputs.repo }} secrets: WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} ``` @@ -303,67 +336,100 @@ jobs: - `checkout-path` (string, optional): Path to check out code to. - `skip-relevance-check` (boolean, optional, default: `false`): Bypass the check that only runs if workflow files have changed. +- `ref` (string, optional): The branch, ref, or SHA to check out. +- `repo` (string, optional): The repository to check out from. - `pr-base-sha` (string, optional): Base SHA of the PR for relevance check. - `pr-head-sha` (string, optional): Head SHA of the PR for relevance check. -### 9. `codeql-analysis.yaml` +### 9. `jsonnet-format-check.yaml` -Performs static analysis on the codebase using GitHub CodeQL to identify potential security vulnerabilities and coding errors. +Checks Jsonnet files for formatting issues using `jsonnetfmt`. #### Usage Example ```yaml jobs: - analyze: - uses: Framework-R-D/phlex/.github/workflows/codeql-analysis.yaml@ + check_jsonnet: + uses: Framework-R-D/phlex/.github/workflows/jsonnet-format-check.yaml@ + with: + # Optional: bypass detection and check all files (useful for manual triggers) + skip-relevance-check: ${{ github.event_name == 'workflow_dispatch' }} ``` #### All Inputs - `checkout-path` (string, optional): Path to check out code to. -- `build-path` (string, optional): Path for build artifacts. -- `language-matrix` (string, optional, default: `'["cpp", "python", "actions"]'`): JSON array of languages to analyze. -- `pr-number` (string, optional): PR number if run in PR context. -- `pr-head-repo` (string, optional): The full name of the PR head repository. -- `pr-base-repo` (string, optional): The full name of the PR base repository. +- `skip-relevance-check` (boolean, optional, default: `false`): Bypass the check that only runs if Jsonnet files have changed. +- `ref` (string, optional): The branch, ref, or SHA to checkout. +- `repo` (string, optional): The repository to checkout from. +- `pr-base-sha` (string, optional): Base SHA of the PR for relevance check. +- `pr-head-sha` (string, optional): Head SHA of the PR for relevance check. -### 5. `jsonnet-format-check.yaml` +### 10. `jsonnet-format-fix.yaml` -Checks Jsonnet files for formatting issues using `jsonnetfmt`. +Automatically formats Jsonnet files using `jsonnetfmt` and commits the changes. Typically triggered by an `issue_comment`. #### Usage Example ```yaml +name: 'Bot Commands' +on: + issue_comment: + types: [created] + jobs: - check_jsonnet: - uses: Framework-R-D/phlex/.github/workflows/jsonnet-format-check.yaml@ + pre-check: + if: > + 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)) || + startsWith(github.event.comment.body, format('@{0}bot jsonnet-fix', github.event.repository.name)) + ) + runs-on: ubuntu-latest + outputs: + ref: ${{ steps.pr_info.outputs.ref }} + repo: ${{ steps.pr_info.outputs.repo }} + steps: + - id: pr_info + uses: Framework-R-D/phlex/.github/actions/get-pr-info@ + + fix-jsonnet: + needs: pre-check + uses: Framework-R-D/phlex/.github/workflows/jsonnet-format-fix.yaml@ with: - # Optional: bypass detection and check all files (useful for manual triggers) - skip-relevance-check: ${{ github.event_name == 'workflow_dispatch' }} + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.outputs.repo }} + secrets: + WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} ``` #### All Inputs - `checkout-path` (string, optional): Path to check out code to. -- `skip-relevance-check` (boolean, optional, default: `false`): Bypass the check that only runs if Jsonnet files have changed. -- `ref` (string, optional): The branch or ref to check out. -- `repo` (string, optional): The repository to check out from. -- `pr-base-sha` (string, optional): Base SHA of the PR for relevance check. -- `pr-head-sha` (string, optional): Head SHA of the PR for relevance check. +- `ref` (string, **required**): The branch, ref, or SHA to checkout. +- `repo` (string, **required**): The repository to checkout from. -### 6. `jsonnet-format-fix.yaml` +### 11. `codeql-analysis.yaml` -Automatically formats Jsonnet files using `jsonnetfmt` and commits the changes. Typically triggered by an `issue_comment`. +Performs static analysis on the codebase using GitHub CodeQL to identify potential security vulnerabilities and coding errors. #### Usage Example -*Similar to `cmake-format-fix.yaml`, but triggered by a command like `@bot jsonnet-format-fix`.* +```yaml +jobs: + analyze: + uses: Framework-R-D/phlex/.github/workflows/codeql-analysis.yaml@ +``` #### All Inputs - `checkout-path` (string, optional): Path to check out code to. -- `ref` (string, **required**): The branch or ref to check out. -- `repo` (string, **required**): The repository to check out from. +- `build-path` (string, optional): Path for build artifacts. +- `language-matrix` (string, optional, default: `'["cpp", "python", "actions"]'`): JSON array of languages to analyze. +- `pr-number` (string, optional): PR number if run in PR context. +- `pr-head-repo` (string, optional): The full name of the PR head repository. +- `pr-base-repo` (string, optional): The full name of the PR base repository. ### Other Workflows diff --git a/.github/actions/detect-relevant-changes/action.yaml b/.github/actions/detect-relevant-changes/action.yaml index 30968874e..3c2bdd8bb 100644 --- a/.github/actions/detect-relevant-changes/action.yaml +++ b/.github/actions/detect-relevant-changes/action.yaml @@ -108,7 +108,7 @@ runs: fi done - git diff --name-only "$BASE_REF" "$HEAD_REF" > "$RUNNER_TEMP/changed-files.txt" + git diff --name-only "$BASE_REF...$HEAD_REF" > "$RUNNER_TEMP/changed-files.txt" if [ ! -s "$RUNNER_TEMP/changed-files.txt" ]; then : > "$RUNNER_TEMP/matched-files.txt" diff --git a/.github/actions/get-pr-info/action.yaml b/.github/actions/get-pr-info/action.yaml index 5c6e524c4..e03d52ac6 100644 --- a/.github/actions/get-pr-info/action.yaml +++ b/.github/actions/get-pr-info/action.yaml @@ -10,6 +10,9 @@ outputs: repo: description: 'The full name of the head repository' value: ${{ steps.get_pr_info.outputs.repo }} + base_sha: + description: 'The SHA of the base branch' + value: ${{ steps.get_pr_info.outputs.base_sha }} runs: using: "composite" steps: @@ -25,6 +28,7 @@ runs: core.setOutput('ref', pr.data.head.ref); core.setOutput('sha', pr.data.head.sha); core.setOutput('repo', pr.data.head.repo.full_name); + core.setOutput('base_sha', pr.data.base.sha); - name: React to comment if: github.event_name == 'issue_comment' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 diff --git a/.github/actions/handle-fix-commit/action.yaml b/.github/actions/handle-fix-commit/action.yaml index 62877f4cb..21bdff620 100644 --- a/.github/actions/handle-fix-commit/action.yaml +++ b/.github/actions/handle-fix-commit/action.yaml @@ -1,6 +1,23 @@ name: 'Handle Fix Commit' description: 'Commits changes if possible, otherwise creates a patch and comments on the PR.' +outputs: + changes: + description: 'Whether changes were detected' + value: ${{ steps.check_changes.outputs.changes }} + pushed: + description: 'Whether changes were pushed' + value: ${{ steps.commit_and_push.outputs.pushed }} + commit_sha: + description: 'The full SHA of the pushed commit' + value: ${{ steps.commit_and_push.outputs.commit_sha }} + commit_sha_short: + description: 'The short SHA of the pushed commit' + value: ${{ steps.commit_and_push.outputs.commit_sha_short }} + patch_name: + description: 'The name of the patch file if created' + value: ${{ steps.create_patch.outputs.patch_name }} + inputs: tool: description: 'The tool name reported in commit messages and PR comments.' @@ -65,8 +82,8 @@ runs: git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" export GITHUB_TOKEN=${{ inputs.token }} - echo "https://x-access-token:${GITHUB_TOKEN}@github.com" > ~/.git-credentials trap 'rm -f ~/.git-credentials' EXIT + echo "https://x-access-token:${GITHUB_TOKEN}@github.com" > ~/.git-credentials git config --local credential.helper 'store --file ~/.git-credentials' git add -u @@ -76,7 +93,9 @@ runs: if git push origin HEAD:${{ inputs.pr-info-ref }}; then echo "Push successful on attempt $i." COMMIT_SHA=$(git rev-parse HEAD) + COMMIT_SHA_SHORT=$(git rev-parse --short HEAD) echo "commit_sha=$COMMIT_SHA" >> "$GITHUB_OUTPUT" + echo "commit_sha_short=$COMMIT_SHA_SHORT" >> "$GITHUB_OUTPUT" echo "pushed=true" >> "$GITHUB_OUTPUT" exit 0 fi @@ -100,7 +119,7 @@ runs: uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: message: | - Automatic ${{ inputs.tool }} fixes pushed (commit ${{ steps.commit_and_push.outputs.commit_sha }}). + Automatic ${{ inputs.tool }} fixes pushed (commit ${{ steps.commit_and_push.outputs.commit_sha_short || steps.commit_and_push.outputs.commit_sha }}). ⚠️ **Note:** Some issues may require manual review and fixing. - name: Create patch diff --git a/.github/workflows/actionlint-check.yaml b/.github/workflows/actionlint-check.yaml index c3178ff8a..cbd5bb0ea 100644 --- a/.github/workflows/actionlint-check.yaml +++ b/.github/workflows/actionlint-check.yaml @@ -1,5 +1,5 @@ name: Actionlint Check -run-name: "${{ github.actor }} checking workflow format" +run-name: "${{ github.actor }} checking GitHub Actions workflows" permissions: contents: read @@ -33,6 +33,14 @@ on: description: "Head SHA of the PR for relevance check" required: false type: string + ref: + description: "The branch, ref, or SHA to checkout" + required: false + type: string + repo: + description: "The repository to checkout from" + required: false + type: string env: local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} @@ -42,6 +50,9 @@ jobs: 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 @@ -51,7 +62,10 @@ jobs: needs: pre-check if: > github.event_name != 'workflow_dispatch' && - (github.event_name != 'workflow_call' || inputs.skip-relevance-check != 'true') && + ( + 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: @@ -65,14 +79,16 @@ jobs: with: fetch-depth: 0 path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect workflow changes + - 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: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || github.event.pull_request.head.sha || github.sha }} + 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 @@ -92,10 +108,11 @@ jobs: actionlint-check: needs: [pre-check, detect-changes] if: > - github.event_name == 'workflow_dispatch' || - (github.event_name == 'workflow_call' && inputs.skip-relevance-check == 'true') || - needs.pre-check.outputs.is_act == 'true' || - (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes == 'true') + needs.detect-changes.result == 'skipped' || + ( + needs.detect-changes.result == 'success' && + needs.detect-changes.outputs.has_changes == 'true' + ) runs-on: ubuntu-latest permissions: contents: read @@ -104,8 +121,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.ref }} + ref: ${{ needs.pre-check.outputs.ref }} path: ${{ env.local_checkout_path }} + repository: ${{ needs.pre-check.outputs.repo }} - name: Announce actionlint check run: echo "➡️ Running actionlint check..." @@ -121,9 +139,9 @@ jobs: continue-on-error: true - name: Evaluate actionlint result - if: always() + if: always() && steps.lint.outcome != 'skipped' run: | - if [[ ${{ steps.lint.outcome }} == 'success' ]]; then + if [ "${{ steps.lint.outcome }}" = 'success' ]; then echo "✅ actionlint check passed." else echo "::error::actionlint check failed. Please review the output above for details." @@ -133,8 +151,12 @@ jobs: 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_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 diff --git a/.github/workflows/clang-format-check.yaml b/.github/workflows/clang-format-check.yaml index c12927c54..ba13a7c9a 100644 --- a/.github/workflows/clang-format-check.yaml +++ b/.github/workflows/clang-format-check.yaml @@ -1,5 +1,5 @@ name: Clang-Format Check -run-name: "${{ github.actor }} checking code format" +run-name: "${{ github.actor }} checking C++ code format" permissions: contents: read @@ -20,6 +20,9 @@ jobs: 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 }} steps: - name: Detect act environment id: detect_act @@ -42,14 +45,16 @@ jobs: with: fetch-depth: 0 path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect C++ formatting changes + - name: Detect relevant changes id: filter uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main with: repo-path: phlex-src - base-ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }} - head-ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ needs.pre-check.outputs.ref }} file-type: cpp - name: Report detection outcome @@ -78,8 +83,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.ref }} + ref: ${{ needs.pre-check.outputs.ref }} path: phlex-src + repository: ${{ needs.pre-check.outputs.repo }} - name: Announce clang-format check run: echo "➡️ Running clang-format check..." @@ -94,12 +100,13 @@ jobs: continue-on-error: true - name: Evaluate clang-format result - if: always() + if: always() && steps.lint.outcome != 'skipped' run: | - if [[ ${{ steps.lint.outcome }} == 'success' ]]; then + if [ "${{ steps.lint.outcome }}" = 'success' ]; then echo "✅ clang-format check passed." else echo "::error::clang-format check failed. Please review the output above for details." + echo "::error::Comment '@${{ github.event.repository.name }}bot format' on the PR to attempt auto-fix." exit 1 fi diff --git a/.github/workflows/clang-format-fix.yaml b/.github/workflows/clang-format-fix.yaml index c8c719f5b..ceeb22b77 100644 --- a/.github/workflows/clang-format-fix.yaml +++ b/.github/workflows/clang-format-fix.yaml @@ -1,5 +1,5 @@ name: Clang-Format Fix -run-name: "${{ github.actor }} fixing code format" +run-name: "${{ github.actor }} fixing C++ code format" on: issue_comment: @@ -25,11 +25,15 @@ jobs: ( 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, '@phlexbot format') || - startsWith(github.event.comment.body, '@phlexbot clang-format-fix') + startsWith(github.event.comment.body, format('@{0}bot format', github.event.repository.name)) || + 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. + # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: ref: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} repo: ${{ steps.get_pr.outputs.repo || github.repository }} @@ -61,7 +65,7 @@ jobs: extensions: cpp,hpp,cpp.in,hpp.in - name: Handle fix commit - uses: ./phlex-src/.github/actions/handle-fix-commit + uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: clang-format working-directory: phlex-src diff --git a/.github/workflows/clang-tidy-check.yaml b/.github/workflows/clang-tidy-check.yaml index 20c179ab9..2c88cfb54 100644 --- a/.github/workflows/clang-tidy-check.yaml +++ b/.github/workflows/clang-tidy-check.yaml @@ -1,5 +1,5 @@ name: Clang-Tidy Check -'run-name': "${{ github.actor }} running clang-tidy check" +'run-name': "${{ github.actor }} checking C++ code with clang-tidy" permissions: contents: read @@ -16,10 +16,24 @@ on: type: string jobs: + pre-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)) || github.sha }} + repo: ${{ github.repository }} + 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 == 'pull_request' + needs.pre-check.result == 'success' && + github.event_name != 'workflow_dispatch' && + needs.pre-check.outputs.is_act != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -34,14 +48,16 @@ jobs: with: fetch-depth: 0 path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect clang-tidy relevant changes + - name: Detect relevant changes id: filter uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main with: repo-path: phlex-src - base-ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }} - head-ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ needs.pre-check.outputs.ref }} file-type: | cpp cmake @@ -57,11 +73,13 @@ jobs: fi clang-tidy-check: - needs: [detect-changes] + needs: [pre-check, detect-changes] if: > - github.event_name == 'workflow_dispatch' || - (github.event_name == 'pull_request' && needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes == 'true') - + needs.detect-changes.result == 'skipped' || + ( + needs.detect-changes.result == 'success' && + needs.detect-changes.outputs.has_changes == 'true' + ) runs-on: ubuntu-24.04 container: @@ -71,8 +89,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.ref }} + ref: ${{ needs.pre-check.outputs.ref }} path: phlex-src + repository: ${{ needs.pre-check.outputs.repo }} - name: Setup build environment uses: Framework-R-D/phlex/.github/actions/setup-build-env@main @@ -95,7 +114,7 @@ jobs: echo "::error::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 '@phlexbot tidy-fix [...]' on the PR to attempt auto-fix" + echo "Comment '@${{ github.event.repository.name }}bot tidy-fix [...]' on the PR to attempt auto-fix" exit 1 else echo "✅ clang-tidy check passed" @@ -135,3 +154,17 @@ jobs: clang_tidy_fixes: phlex-build/clang-tidy-fixes.yaml github_token: ${{ secrets.GITHUB_TOKEN }} + 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." diff --git a/.github/workflows/clang-tidy-fix.yaml b/.github/workflows/clang-tidy-fix.yaml index 56af51cf2..ad3d97b32 100644 --- a/.github/workflows/clang-tidy-fix.yaml +++ b/.github/workflows/clang-tidy-fix.yaml @@ -1,5 +1,5 @@ name: Clang-Tidy Fix -'run-name': "${{ github.actor }} applying clang-tidy fixes" +'run-name': "${{ github.actor }} fixing C++ code with clang-tidy" on: issue_comment: @@ -29,8 +29,12 @@ jobs: ( github.event_name == 'issue_comment' && github.event.issue.pull_request && - startsWith(github.event.comment.body, '@phlexbot tidy-fix') + contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && + startsWith(github.event.comment.body, format('@{0}bot tidy-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. + # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: ref: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} repo: ${{ steps.get_pr.outputs.repo || github.repository }} @@ -43,7 +47,8 @@ jobs: env: COMMENT_BODY: ${{ github.event.comment.body }} run: | - checks_line=$(echo "$COMMENT_BODY" | sed -nE 's/^@phlexbot[[:space:]]+tidy-fix[[:space:]]+(.*)/\1/p' | tr -d '\r') + bot_name="${{ github.event.repository.name }}bot" + checks_line=$(echo "$COMMENT_BODY" | sed -nE "s/^@${bot_name}[[:space:]]+tidy-fix[[:space:]]+(.*)/\1/p" | tr -d '\r') if [ -n "$checks_line" ]; then tidy_checks="$(echo "$checks_line" | tr ',' ' ' | xargs -n1 | paste -sd, -)" echo "tidy_checks=$tidy_checks" >> "$GITHUB_OUTPUT" @@ -99,7 +104,7 @@ jobs: retention-days: 7 - name: Handle fix commit - uses: ./phlex-src/.github/actions/handle-fix-commit + uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: clang-tidy working-directory: phlex-src diff --git a/.github/workflows/cmake-build.yaml b/.github/workflows/cmake-build.yaml index dcba5940f..7a8a234d6 100644 --- a/.github/workflows/cmake-build.yaml +++ b/.github/workflows/cmake-build.yaml @@ -45,7 +45,7 @@ on: required: false type: string ref: - description: "The branch or ref to checkout" + description: "The branch, ref, or SHA to checkout" required: false type: string repo: @@ -80,12 +80,12 @@ jobs: ( github.event_name == 'issue_comment' && github.event.issue.pull_request && - (github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'OWNER') && - ( - startsWith(github.event.comment.body, '@phlexbot build') || - startsWith(github.event.comment.body, format('@{0}bot build', github.event.repository.name)) - ) + 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. + # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. runs-on: ubuntu-latest permissions: contents: read @@ -93,27 +93,15 @@ jobs: outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - sha: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || (github.event_name == 'workflow_call' && inputs.ref) || steps.pr.outputs.sha || github.sha }} + 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 }} steps: - - name: Get PR details for comment trigger + - name: Get PR Info if: github.event_name == 'issue_comment' id: pr - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const pr = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number - }); - return { - ref: pr.data.head.ref, - sha: pr.data.head.sha, - repo: pr.data.head.repo.full_name, - base_sha: pr.data.base.sha - }; + uses: Framework-R-D/phlex/.github/actions/get-pr-info@main - name: Detect act environment id: detect_act @@ -126,7 +114,12 @@ jobs: ( github.event_name == 'pull_request' || github.event_name == 'push' || - (github.event_name == 'workflow_call' && inputs.skip-relevance-check != 'true') + ( + 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 @@ -141,14 +134,16 @@ jobs: with: fetch-depth: 0 path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect C++ and CMake changes + - 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: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || github.event.pull_request.head.sha || github.sha }} + 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 cmake @@ -200,7 +195,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.sha }} + ref: ${{ needs.pre-check.outputs.ref }} repository: ${{ needs.pre-check.outputs.repo }} - name: Setup build environment @@ -213,7 +208,9 @@ jobs: - name: Extract repository name id: repo_name - run: echo "name=$(echo '${{ needs.pre-check.outputs.repo }}' | sed 's:.*/::')" >> "$GITHUB_OUTPUT" + env: + REPO_FULL_NAME: ${{ needs.pre-check.outputs.repo }} + run: echo "name=${REPO_FULL_NAME##*/}" >> "$GITHUB_OUTPUT" - name: Configure CMake id: configure @@ -271,7 +268,10 @@ jobs: if: > needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && - (github.event_name != 'workflow_call' || inputs.skip-relevance-check != 'true') && + ( + 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 diff --git a/.github/workflows/cmake-format-check.yaml b/.github/workflows/cmake-format-check.yaml index 93221e1c3..f9b3a56aa 100644 --- a/.github/workflows/cmake-format-check.yaml +++ b/.github/workflows/cmake-format-check.yaml @@ -1,5 +1,5 @@ name: CMake Format Check -run-name: "${{ github.actor }} running CMake format check" +run-name: "${{ github.actor }} checking CMake format" permissions: contents: read @@ -33,6 +33,14 @@ on: description: "Head SHA of the PR for relevance check" required: false type: string + ref: + description: "The branch, ref, or SHA to checkout" + required: false + type: string + repo: + description: "The repository to checkout from" + required: false + type: string env: local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} @@ -42,6 +50,9 @@ jobs: 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 @@ -51,7 +62,10 @@ jobs: needs: pre-check if: > github.event_name != 'workflow_dispatch' && - (github.event_name != 'workflow_call' || inputs.skip-relevance-check != 'true') && + ( + 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: @@ -65,14 +79,16 @@ jobs: with: fetch-depth: 0 path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect CMake formatting changes + - 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: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || github.event.pull_request.head.sha || github.sha }} + 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 @@ -99,8 +115,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.ref }} + ref: ${{ needs.pre-check.outputs.ref }} path: ${{ env.local_checkout_path }} + repository: ${{ needs.pre-check.outputs.repo }} - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -111,21 +128,32 @@ jobs: run: pip install gersemi - name: Check CMake formatting + id: lint run: | echo "➡️ Checking CMake file formatting..." - if ! gersemi --check ${{ env.local_checkout_path }}; then + gersemi --check ${{ env.local_checkout_path }} + continue-on-error: true + + - name: Evaluate CMake formatting result + if: always() && steps.lint.outcome != 'skipped' + run: | + if [ "${{ steps.lint.outcome }}" = 'success' ]; then + echo "✅ All CMake files are properly formatted." + else echo "::error::Found files with formatting issues." echo "::error::Run 'gersemi -i ' locally or comment '@${{ github.event.repository.name }}bot format' on the PR to auto-fix." exit 1 - else - echo "✅ All CMake files are properly formatted." fi 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_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 diff --git a/.github/workflows/cmake-format-fix.yaml b/.github/workflows/cmake-format-fix.yaml index 531fd5798..6aac9e336 100644 --- a/.github/workflows/cmake-format-fix.yaml +++ b/.github/workflows/cmake-format-fix.yaml @@ -1,5 +1,5 @@ name: CMake Format Fix -run-name: '${{ github.actor }} fixing CMake format' +run-name: "${{ github.actor }} fixing CMake format" on: issue_comment: @@ -18,7 +18,7 @@ on: required: false type: string ref: - description: "The branch or ref to checkout" + description: "The branch, ref, or SHA to checkout" required: true type: string repo: @@ -43,12 +43,15 @@ jobs: ( 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, '@phlexbot format') || - startsWith(github.event.comment.body, '@phlexbot cmake-format-fix') || - startsWith(github.event.comment.body, format('@{0}bot format', github.event.repository.name)) + startsWith(github.event.comment.body, format('@{0}bot format', github.event.repository.name)) || + 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. + # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && github.repository) || steps.get_pr.outputs.repo }} diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index c662dc054..e6896842e 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -98,6 +98,7 @@ jobs: - name: Verify compile_commands.json (C++ only) if: matrix.language == 'cpp' run: | + set -eu if [ ! -f "$CODEQL_EXTRACTOR_CPP_COMPILATION_DATABASE" ]; then echo "Expected compile_commands.json at $CODEQL_EXTRACTOR_CPP_COMPILATION_DATABASE" >&2 exit 1 @@ -158,6 +159,7 @@ jobs: - name: Check CodeQL SARIF for new or resolved alerts id: check_codeql run: | + set -eu ARGS=( --sarif "$GITHUB_WORKSPACE/sarif" --min-level "${CODEQL_MIN_LEVEL}" diff --git a/.github/workflows/codeql-comment.yaml b/.github/workflows/codeql-comment.yaml index 332c8c403..5d768cab6 100644 --- a/.github/workflows/codeql-comment.yaml +++ b/.github/workflows/codeql-comment.yaml @@ -22,7 +22,7 @@ jobs: if: github.event.workflow_run.event == 'pull_request' && (github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure') steps: - name: Download PR comment data - uses: actions/download-artifact@fa0a91b85d4f404e8442c7c958156baef1102941 # v4.1.8 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: codeql-pr-data run-id: ${{ github.event.workflow_run.id }} diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 27a0a0160..5701a2429 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -1,6 +1,6 @@ # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json name: Code Coverage -run-name: "${{ github.actor }} running code coverage for Phlex" +run-name: "${{ github.actor }} running code coverage" on: push: @@ -31,6 +31,9 @@ jobs: 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 }} steps: - name: Detect act environment id: detect_act @@ -51,17 +54,20 @@ jobs: with: fetch-depth: 0 path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect coverage relevant changes + - name: Detect relevant changes id: filter uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main with: repo-path: phlex-src - base-ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }} - head-ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ needs.pre-check.outputs.ref }} file-type: | cpp cmake + python - name: Report detection outcome run: | @@ -111,8 +117,9 @@ jobs: - name: Check out source code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.ref }} + ref: ${{ needs.pre-check.outputs.ref }} path: phlex-src + repository: ${{ needs.pre-check.outputs.repo }} - name: Setup build environment uses: Framework-R-D/phlex/.github/actions/setup-build-env@main diff --git a/.github/workflows/jsonnet-format-check.yaml b/.github/workflows/jsonnet-format-check.yaml index 5319ee038..3ec98dbb8 100644 --- a/.github/workflows/jsonnet-format-check.yaml +++ b/.github/workflows/jsonnet-format-check.yaml @@ -1,5 +1,5 @@ name: Jsonnet Format Check -run-name: "${{ github.actor }} checking jsonnet format" +run-name: "${{ github.actor }} checking Jsonnet format" permissions: contents: read @@ -52,6 +52,7 @@ jobs: 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 @@ -60,17 +61,13 @@ jobs: detect-changes: needs: pre-check if: > - needs.pre-check.outputs.is_act != 'true' && + needs.pre-check.result == 'success' && + github.event_name != 'workflow_dispatch' && ( - 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 - ) - ) + 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 @@ -86,13 +83,13 @@ jobs: ref: ${{ needs.pre-check.outputs.ref }} repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect Jsonnet formatting changes + - 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: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || github.event.pull_request.head.sha || github.sha }} + 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 @@ -134,7 +131,7 @@ jobs: continue-on-error: true - name: Evaluate Jsonnet formatting result - if: always() + if: always() && steps.lint.outcome != 'skipped' run: | if [ "${{ steps.lint.outcome }}" = 'success' ]; then echo "✅ Jsonnet formatting check passed." @@ -147,17 +144,13 @@ jobs: jsonnet-format-check-skipped: needs: [pre-check, detect-changes] if: > - needs.pre-check.outputs.is_act != 'true' && + needs.pre-check.result == 'success' && + github.event_name != 'workflow_dispatch' && ( - 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 - ) + 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 diff --git a/.github/workflows/jsonnet-format-fix.yaml b/.github/workflows/jsonnet-format-fix.yaml index e4fb3a056..f44945e46 100644 --- a/.github/workflows/jsonnet-format-fix.yaml +++ b/.github/workflows/jsonnet-format-fix.yaml @@ -1,5 +1,5 @@ name: Jsonnet Format Fix -run-name: "${{ github.actor }} fixing jsonnet format" +run-name: "${{ github.actor }} fixing Jsonnet format" on: issue_comment: @@ -18,7 +18,7 @@ on: required: false type: string ref: - description: "The branch or ref to checkout" + description: "The branch, ref, or SHA to checkout" required: true type: string repo: @@ -43,12 +43,15 @@ jobs: ( github.event_name == 'issue_comment' && github.event.issue.pull_request && - contains(fromJSON('["OWNER", "COLLABORATOR"]'), github.event.comment.author_association) && + contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && ( startsWith(github.event.comment.body, format('@{0}bot format', github.event.repository.name)) || - startsWith(github.event.comment.body, format('@{0}bot jsonnet-format-fix', github.event.repository.name)) + 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. + # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && github.repository) || steps.get_pr.outputs.repo }} @@ -64,9 +67,6 @@ jobs: name: Apply Jsonnet formatting needs: pre-check if: needs.pre-check.result == 'success' - container: - image: public.ecr.aws/bitnami/jsonnet:latest - options: --user root steps: - name: Checkout code @@ -79,15 +79,17 @@ jobs: - name: Apply Jsonnet formatting id: lint - working-directory: ${{ env.local_checkout_path }} + env: + CHECKOUT_PATH: ${{ env.local_checkout_path }} run: | - find . \( -name "*.jsonnet" -o -name "*.libsonnet" \) -print0 | xargs -0 -r jsonnetfmt -i + docker run --rm -v "${{ github.workspace }}:/work" -w /work --user root public.ecr.aws/bitnami/jsonnet:latest \ + sh -c "find \"$CHECKOUT_PATH\" \( -name '*.jsonnet' -o -name '*.libsonnet' \) -print0 | xargs -0 -r jsonnetfmt -i" continue-on-error: true - name: Handle fix commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: - tool: jsonnet-format + tool: jsonnetfmt working-directory: ${{ env.local_checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} pr-info-ref: ${{ needs.pre-check.outputs.ref }} diff --git a/.github/workflows/markdown-check.yaml b/.github/workflows/markdown-check.yaml index 97d13d8ec..4a4cbf24b 100644 --- a/.github/workflows/markdown-check.yaml +++ b/.github/workflows/markdown-check.yaml @@ -1,5 +1,5 @@ name: Markdown Check -run-name: "${{ github.actor }} checking markdown format" +run-name: "${{ github.actor }} checking Markdown format" permissions: contents: read @@ -50,6 +50,9 @@ jobs: 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 @@ -58,16 +61,13 @@ jobs: detect-changes: needs: pre-check if: > - needs.pre-check.outputs.is_act != 'true' && + needs.pre-check.result == 'success' && + github.event_name != 'workflow_dispatch' && ( - github.event_name == 'pull_request' || - github.event_name == 'push' || - ( - github.event_name == 'workflow_call' && - inputs.skip-relevance-check != 'true' && - (github.event.pull_request || github.event.before) - ) - ) + 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 @@ -80,15 +80,16 @@ jobs: with: fetch-depth: 0 path: ${{ env.local_checkout_path }} - repository: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect Markdown changes + - 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: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || github.event.pull_request.head.sha || github.sha }} + 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 @@ -117,9 +118,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ (github.event_name == 'workflow_call' && (inputs.ref || inputs.pr-head-sha)) || (github.event_name == 'workflow_dispatch' && github.event.inputs.ref) || github.ref }} + ref: ${{ needs.pre-check.outputs.ref }} path: ${{ env.local_checkout_path }} - repository: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} + repository: ${{ needs.pre-check.outputs.repo }} - name: Add problem matcher uses: xt0rted/markdownlint-problem-matcher@1a5fabfb577370cfdf5af944d418e4be3ea06f27 # v3.0.0 @@ -147,16 +148,13 @@ jobs: markdown-check-skipped: needs: [pre-check, detect-changes] if: > - needs.pre-check.outputs.is_act != 'true' && + needs.pre-check.result == 'success' && + github.event_name != 'workflow_dispatch' && ( - github.event_name == 'pull_request' || - github.event_name == 'push' || - ( - github.event_name == 'workflow_call' && - inputs.skip-relevance-check != 'true' && - (github.event.pull_request || github.event.before) - ) + 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: diff --git a/.github/workflows/markdown-fix.yaml b/.github/workflows/markdown-fix.yaml index 6b10eb333..70ee10695 100644 --- a/.github/workflows/markdown-fix.yaml +++ b/.github/workflows/markdown-fix.yaml @@ -1,5 +1,5 @@ name: Markdown Fix -run-name: "${{ github.actor }} fixing markdown format" +run-name: "${{ github.actor }} fixing Markdown format" on: issue_comment: @@ -12,7 +12,7 @@ on: required: false type: string ref: - description: "The branch or ref to checkout" + description: "The branch, ref, or SHA to checkout" required: true type: string repo: @@ -43,13 +43,15 @@ jobs: ( 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, '@phlexbot format') || - startsWith(github.event.comment.body, '@phlexbot markdown-fix') || startsWith(github.event.comment.body, format('@{0}bot format', github.event.repository.name)) || 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. + # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && github.repository) || steps.get_pr.outputs.repo }} @@ -81,6 +83,7 @@ jobs: ${{ env.local_checkout_path }}/**/*.md !${{ env.local_checkout_path }}/**/CHANGELOG.md fix: true + continue-on-error: true - name: Handle fix commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main diff --git a/.github/workflows/python-check.yaml b/.github/workflows/python-check.yaml index b5f8aa1e7..2073cc45c 100644 --- a/.github/workflows/python-check.yaml +++ b/.github/workflows/python-check.yaml @@ -33,6 +33,14 @@ on: description: "Head SHA of the PR for relevance check" required: false type: string + ref: + description: "The branch, ref, or SHA to checkout" + required: false + type: string + repo: + description: "The repository to checkout from" + required: false + type: string env: local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} @@ -42,6 +50,9 @@ jobs: 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 @@ -50,8 +61,12 @@ jobs: 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_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: @@ -65,14 +80,16 @@ jobs: with: fetch-depth: 0 path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect Python changes + - 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: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || github.event.pull_request.head.sha || github.sha }} + 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 @@ -101,8 +118,9 @@ jobs: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.ref }} + ref: ${{ needs.pre-check.outputs.ref }} path: ${{ env.local_checkout_path }} + repository: ${{ needs.pre-check.outputs.repo }} - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -113,42 +131,43 @@ jobs: run: | pip install ruff mypy - - name: Run ruff and mypy checks + - name: Run ruff check + id: ruff working-directory: ${{ env.local_checkout_path }} env: - FORCE_COLOR: 1 # `ruff`/`colored` crate + FORCE_COLOR: 1 run: | - failed=0 - echo "➡️ Checking Python code with ruff..." - echo "::group::Running ruff check" - if ! ruff check; then - failed=1 - echo "::endgroup::" - echo "::error:: Ruff check failed." - else - echo "::endgroup::" - echo "✅ Ruff check passed." - fi + ruff check + continue-on-error: true - echo "➡️ Checking Python code with mypy..." - echo "::group::Running mypy" - if ! mypy --color-output .; then - failed=1 - echo "::endgroup::" - echo "::error:: MyPy check failed." + - name: Run MyPy check + id: mypy + working-directory: ${{ env.local_checkout_path }} + run: | + echo "➡️ Checking Python code with MyPy..." + mypy --color-output . + continue-on-error: true + + - name: Evaluate Python checks result + if: always() && (steps.ruff.outcome != 'skipped' || steps.mypy.outcome != 'skipped') + run: | + if [ "${{ steps.ruff.outcome }}" = 'success' ] && [ "${{ steps.mypy.outcome }}" = 'success' ]; then + echo "✅ Python checks passed." else - echo "::endgroup::" - echo "✅ MyPy check passed." + echo "::error::Python checks failed. Comment '@${{ github.event.repository.name }}bot python-fix' on the PR to attempt auto-fix." + exit 1 fi - exit $failed - 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_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 diff --git a/.github/workflows/python-fix.yaml b/.github/workflows/python-fix.yaml index 624984d91..0543afe42 100644 --- a/.github/workflows/python-fix.yaml +++ b/.github/workflows/python-fix.yaml @@ -18,7 +18,7 @@ on: required: false type: string ref: - description: "The branch or ref to checkout" + description: "The branch, ref, or SHA to checkout" required: true type: string repo: @@ -43,11 +43,12 @@ jobs: ( github.event_name == 'issue_comment' && github.event.issue.pull_request && - ( - startsWith(github.event.comment.body, '@phlexbot python-fix') || - startsWith(github.event.comment.body, format('@{0}bot python-fix', github.event.repository.name)) - ) + 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. + # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && github.repository) || steps.get_pr.outputs.repo }} @@ -92,7 +93,7 @@ jobs: - name: Handle fix commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: - tool: 'Python linting' + tool: ruff working-directory: ${{ env.local_checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} pr-info-ref: ${{ needs.pre-check.outputs.ref }} diff --git a/.gitignore b/.gitignore index 6376d96d7..dc994c55d 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,5 @@ __pycache__/ .DS_Store # act (local workflow testing) .act-artifacts/ -.secrets \ No newline at end of file +.secretsactionlint +actionlint