diff --git a/.github/REUSABLE_WORKFLOWS.md b/.github/REUSABLE_WORKFLOWS.md index b721e3de4..aa478ecc3 100644 --- a/.github/REUSABLE_WORKFLOWS.md +++ b/.github/REUSABLE_WORKFLOWS.md @@ -82,13 +82,25 @@ You can achieve this by passing the appropriate value to the `skip-relevance-che skip-relevance-check: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'issue_comment' }} ``` -Additionally, to ensure the reusable workflow can access the correct code in an extra-repository context, always pass the `ref` and `repo`: +Additionally, to ensure the reusable workflow can access the correct code in an extra-repository context, always pass the `ref` and `repo`. The correct value depends on whether the workflow is a **check** (read-only) or **fix** (push-capable) workflow: -```yaml - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - repo: ${{ github.repository }} -``` +- **Check workflows** (e.g., `cmake-format-check.yaml`): a commit SHA is safe and precise. + + ```yaml + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + repo: ${{ github.repository }} + ``` + +- **Fix workflows** (e.g., `cmake-format-fix.yaml`): a branch name is **required** because the + workflow pushes commits with `git push origin HEAD:`. A commit SHA will cause the push to + fail. + + ```yaml + with: + ref: ${{ github.event.pull_request.head.ref || github.ref_name }} + repo: ${{ github.repository }} + ``` --- @@ -146,40 +158,25 @@ jobs: ### 3. `cmake-format-fix.yaml` -Automatically formats CMake files using `gersemi` and commits the changes. Typically triggered by an `issue_comment`. +Automatically formats CMake files using `gersemi` and commits the changes. + +**Trigger Methods:** + +- `@phlexbot cmake-fix` comment on a PR (individual workflow) +- `@phlexbot format` comment on a PR (via `format-all.yaml`) +- `workflow_call` from another workflow +- `workflow_dispatch` manual trigger #### Usage Example ```yaml -name: 'Bot Commands' -on: - issue_comment: - types: [created] - jobs: - pre-check: - # Extract PR details for the comment trigger - 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 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 + fix-cmake: uses: Framework-R-D/phlex/.github/workflows/cmake-format-fix.yaml@ with: - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ github.event.pull_request.head.ref }} + repo: ${{ github.repository }} + skip-comment: "true" # Set to true when calling from a parent workflow secrets: WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} ``` @@ -187,8 +184,17 @@ jobs: #### All Inputs - `checkout-path` (string, optional): Path to check out code to. -- `ref` (string, **required**): The branch, ref, or SHA to check out. +- `ref` (string, **required**): The branch name to check out and push fixes to (must be a branch, not a commit SHA). - `repo` (string, **required**): The repository to check out from. +- `skip-comment` (string, optional, default: `"false"`): Skip posting individual PR comments (use when called from a parent workflow that will post a combined comment). + +#### Outputs + +- `changes` (string): Whether changes were detected (`true`/`false`). +- `pushed` (string): Whether changes were pushed (`true`/`false`). +- `commit_sha` (string): The full SHA of the pushed commit. +- `commit_sha_short` (string): The short SHA of the pushed commit. +- `patch_name` (string): The name of the patch file if created. ### 4. `python-check.yaml` @@ -205,7 +211,7 @@ 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 Python files have changed. +- `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. @@ -213,36 +219,25 @@ jobs: ### 5. `python-fix.yaml` -Automatically formats and fixes Python code using `ruff` and commits the changes. Typically triggered by an `issue_comment`. +Automatically formats and fixes Python code using `ruff` and commits the changes. + +**Trigger Methods:** + +- `@phlexbot python-fix` comment on a PR (individual workflow) +- `@phlexbot format` comment on a PR (via `format-all.yaml`) +- `workflow_call` from another workflow +- `workflow_dispatch` manual trigger #### Usage Example ```yaml -name: 'Bot Commands' -on: - issue_comment: - types: [created] - jobs: - 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 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: - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ github.event.pull_request.head.ref }} + repo: ${{ github.repository }} + skip-comment: "true" secrets: WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} ``` @@ -250,8 +245,17 @@ jobs: #### All Inputs - `checkout-path` (string, optional): Path to check out code to. -- `ref` (string, **required**): The branch, ref, or SHA to check out. +- `ref` (string, **required**): The branch name to check out and push fixes to (must be a branch, not a commit SHA). - `repo` (string, **required**): The repository to check out from. +- `skip-comment` (string, optional, default: `"false"`): Skip posting individual PR comments. + +#### Outputs + +- `changes` (string): Whether changes were detected. +- `pushed` (string): Whether changes were pushed. +- `commit_sha` (string): The full SHA of the pushed commit. +- `commit_sha_short` (string): The short SHA of the pushed commit. +- `patch_name` (string): The name of the patch file if created. ### 6. `markdown-check.yaml` @@ -276,40 +280,25 @@ jobs: ### 7. `markdown-fix.yaml` -Automatically formats Markdown files using `markdownlint` and commits the changes. Typically triggered by an `issue_comment`. +Automatically formats Markdown files using `markdownlint` and commits the changes. + +**Trigger Methods:** + +- `@phlexbot markdown-fix` comment on a PR (individual workflow) +- `@phlexbot format` comment on a PR (via `format-all.yaml`) +- `workflow_call` from another workflow +- `workflow_dispatch` manual trigger #### Usage Example ```yaml -name: 'Bot Commands' -on: - issue_comment: - types: [created] - jobs: - pre-check: - 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)) || - 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: - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ github.event.pull_request.head.ref }} + repo: ${{ github.repository }} + skip-comment: "true" secrets: WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} ``` @@ -317,8 +306,17 @@ jobs: #### All Inputs - `checkout-path` (string, optional): Path to check out code to. -- `ref` (string, **required**): The branch, ref, or SHA to check out. +- `ref` (string, **required**): The branch name to check out and push fixes to (must be a branch, not a commit SHA). - `repo` (string, **required**): The repository to check out from. +- `skip-comment` (string, optional, default: `"false"`): Skip posting individual PR comments. + +#### Outputs + +- `changes` (string): Whether changes were detected. +- `pushed` (string): Whether changes were pushed. +- `commit_sha` (string): The full SHA of the pushed commit. +- `commit_sha_short` (string): The short SHA of the pushed commit. +- `patch_name` (string): The name of the patch file if created. ### 8. `actionlint-check.yaml` @@ -367,39 +365,25 @@ jobs: ### 10. `jsonnet-format-fix.yaml` -Automatically formats Jsonnet files using `jsonnetfmt` and commits the changes. Typically triggered by an `issue_comment`. +Automatically formats Jsonnet files using `jsonnetfmt` and commits the changes. + +**Trigger Methods:** + +- `@phlexbot jsonnet-fix` comment on a PR (individual workflow) +- `@phlexbot format` comment on a PR (via `format-all.yaml`) +- `workflow_call` from another workflow +- `workflow_dispatch` manual trigger #### Usage Example ```yaml -name: 'Bot Commands' -on: - issue_comment: - types: [created] - jobs: - 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: - ref: ${{ needs.pre-check.outputs.ref }} - repo: ${{ needs.pre-check.outputs.repo }} + ref: ${{ github.event.pull_request.head.ref }} + repo: ${{ github.repository }} + skip-comment: "true" secrets: WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} ``` @@ -407,8 +391,17 @@ jobs: #### All Inputs - `checkout-path` (string, optional): Path to check out code to. -- `ref` (string, **required**): The branch, ref, or SHA to checkout. +- `ref` (string, **required**): The branch name to checkout and push fixes to (must be a branch, not a commit SHA). - `repo` (string, **required**): The repository to checkout from. +- `skip-comment` (string, optional, default: `"false"`): Skip posting individual PR comments. + +#### Outputs + +- `changes` (string): Whether changes were detected. +- `pushed` (string): Whether changes were pushed. +- `commit_sha` (string): The full SHA of the pushed commit. +- `commit_sha_short` (string): The short SHA of the pushed commit. +- `patch_name` (string): The name of the patch file if created. ### 11. `codeql-analysis.yaml` @@ -449,6 +442,143 @@ jobs: - **Pushes to main/develop**: All languages are analyzed regardless of changes. - **Language Override**: Providing the `language-matrix` input in `workflow_call` bypasses automatic detection. +### 12. `clang-format-fix.yaml` + +Automatically formats C++ files using `clang-format` and commits the changes. + +**Trigger Methods:** + +- `@phlexbot clang-fix` comment on a PR (individual workflow) +- `@phlexbot format` comment on a PR (via `format-all.yaml`) +- `workflow_call` from another workflow +- `workflow_dispatch` manual trigger + +#### Usage Example + +```yaml +jobs: + fix-clang-format: + uses: Framework-R-D/phlex/.github/workflows/clang-format-fix.yaml@ + with: + ref: ${{ github.event.pull_request.head.ref }} + repo: ${{ github.repository }} + skip-comment: "true" + secrets: + WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} +``` + +#### All Inputs + +- `checkout-path` (string, optional): Path to check out code to. +- `ref` (string, **required**): The branch name to check out and push fixes to (must be a branch, not a commit SHA). +- `repo` (string, **required**): The repository to check out from. +- `skip-comment` (string, optional, default: `"false"`): Skip posting individual PR comments. + +#### Outputs + +- `changes` (string): Whether changes were detected. +- `pushed` (string): Whether changes were pushed. +- `commit_sha` (string): The full SHA of the pushed commit. +- `commit_sha_short` (string): The short SHA of the pushed commit. +- `patch_name` (string): The name of the patch file if created. + +### 13. `header-guards-fix.yaml` + +Automatically fixes header guard formatting and commits the changes. + +**Trigger Methods:** + +- `@phlexbot header-guards-fix` comment on a PR (individual workflow) +- `@phlexbot format` comment on a PR (via `format-all.yaml`) +- `workflow_call` from another workflow +- `workflow_dispatch` manual trigger + +#### Usage Example + +```yaml +jobs: + fix-header-guards: + uses: Framework-R-D/phlex/.github/workflows/header-guards-fix.yaml@ + with: + ref: ${{ github.event.pull_request.head.ref }} + repo: ${{ github.repository }} + skip-comment: "true" + secrets: + WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} +``` + +#### All Inputs + +- `checkout-path` (string, optional): Path to check out code to. +- `ref` (string, **required**): The branch name to check out and push fixes to (must be a branch, not a commit SHA). +- `repo` (string, **required**): The repository to check out from. +- `skip-comment` (string, optional, default: `"false"`): Skip posting individual PR comments. + +#### Outputs + +- `changes` (string): Whether changes were detected. +- `pushed` (string): Whether changes were pushed. +- `commit_sha` (string): The full SHA of the pushed commit. +- `commit_sha_short` (string): The short SHA of the pushed commit. +- `patch_name` (string): The name of the patch file if created. + +### 14. `yaml-fix.yaml` + +Automatically formats YAML files using `prettier` and commits the changes. + +**Trigger Methods:** + +- `@phlexbot yaml-fix` comment on a PR (individual workflow) +- `@phlexbot format` comment on a PR (via `format-all.yaml`) +- `workflow_call` from another workflow +- `workflow_dispatch` manual trigger + +#### Usage Example + +```yaml +jobs: + fix-yaml: + uses: Framework-R-D/phlex/.github/workflows/yaml-fix.yaml@ + with: + ref: ${{ github.event.pull_request.head.ref }} + repo: ${{ github.repository }} + skip-comment: "true" + secrets: + WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} +``` + +#### All Inputs + +- `checkout-path` (string, optional): Path to check out code to. +- `ref` (string, **required**): The branch name to check out and push fixes to (must be a branch, not a commit SHA). +- `repo` (string, **required**): The repository to check out from. +- `skip-comment` (string, optional, default: `"false"`): Skip posting individual PR comments. + +#### Outputs + +- `changes` (string): Whether changes were detected. +- `pushed` (string): Whether changes were pushed. +- `commit_sha` (string): The full SHA of the pushed commit. +- `commit_sha_short` (string): The short SHA of the pushed commit. +- `patch_name` (string): The name of the patch file if created. + +### 15. `format-all.yaml` + +Centralized workflow that calls all format fix workflows in parallel and posts a single combined PR comment with results. + +**Trigger Methods:** + +- `@phlexbot format` comment on a PR + +**Behavior:** + +- Invokes all fix workflows (clang-format, cmake-format, header-guards, jsonnet-format, markdown, python, yaml) via `workflow_call` +- Each sub-workflow runs with `skip-comment: "true"` to suppress individual comments +- Collects all results and posts a single summary comment +- Removes the 👀 reaction and adds a 🚀 reaction when complete + +**Note:** This workflow is not designed to be called via `workflow_call` from other repositories. It is specifically for use within the Phlex repository. + ### Other Workflows -The repository also provides `clang-format-check.yaml`, `clang-format-fix.yaml`, `clang-tidy-check.yaml`, and `clang-tidy-fix.yaml`. However, these workflows are currently **not** available for reuse via `workflow_call` as they are specifically intended for use on this repository and its forks. +The repository also provides `clang-tidy-check.yaml` and `clang-tidy-fix.yaml`. These workflows are currently **not** available for reuse via `workflow_call` as they are specifically intended for use on this repository and its forks. diff --git a/.github/actions/handle-fix-commit/action.yaml b/.github/actions/handle-fix-commit/action.yaml index 13f9154b2..8b1ba2386 100644 --- a/.github/actions/handle-fix-commit/action.yaml +++ b/.github/actions/handle-fix-commit/action.yaml @@ -39,6 +39,10 @@ inputs: description: "The number of times to retry pushing the commit." required: false default: "6" + skip-comment: + description: "Skip posting PR comments (for workflow_call usage)" + required: false + default: "false" runs: using: "composite" @@ -55,7 +59,7 @@ runs: fi - name: No changes to apply - if: steps.check_changes.outputs.changes == 'false' + if: steps.check_changes.outputs.changes == 'false' && inputs.skip-comment != 'true' uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: message: "No automatic ${{ inputs.tool }} fixes were necessary." @@ -66,10 +70,19 @@ runs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | + // context.issue.number is only available when the root trigger is an + // issue_comment event. For workflow_dispatch triggers (and any other + // non-PR-comment trigger), default to false so the commit path still + // works for same-repo pushes via the first clause of the commit condition. + const prNumber = context.issue.number; + if (!prNumber) { + core.setOutput('maintainer_can_modify', 'false'); + return; + } const { data: pr } = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, - pull_number: context.issue.number + pull_number: prNumber }); core.setOutput('maintainer_can_modify', pr.maintainer_can_modify); - name: Commit fixes @@ -125,7 +138,9 @@ runs: exit 1 - name: Notify of commit - if: steps.commit_and_push.conclusion == 'success' && steps.commit_and_push.outputs.pushed == 'true' + if: + steps.commit_and_push.conclusion == 'success' && steps.commit_and_push.outputs.pushed == 'true' && + inputs.skip-comment != 'true' uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: # yamllint disable rule:line-length @@ -153,7 +168,7 @@ runs: path: ${{ inputs.working-directory }}/${{ steps.create_patch.outputs.patch_name }} - name: Comment with patch instructions - if: steps.create_patch.outputs.patch_name + if: steps.create_patch.outputs.patch_name && inputs.skip-comment != 'true' uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: # yamllint disable rule:line-length diff --git a/.github/workflows/clang-format-fix.yaml b/.github/workflows/clang-format-fix.yaml index e6e04c06b..92cf938f6 100644 --- a/.github/workflows/clang-format-fix.yaml +++ b/.github/workflows/clang-format-fix.yaml @@ -8,35 +8,74 @@ run-name: "${{ github.actor }} fixing C++ code format" workflow_dispatch: inputs: ref: - description: "The branch, ref, or SHA to checkout. 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: + inputs: + checkout-path: + description: "Path to check out code to" + required: false + type: string + ref: + description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA)" + required: true + type: string + repo: + description: "The repository to checkout from" + required: true + type: string + skip-comment: + description: "Skip posting PR comments" + required: false + type: string + default: "false" + outputs: + changes: + description: "Whether any fixes were applied" + value: ${{ jobs.apply_formatting.outputs.changes }} + pushed: + description: "Whether the fixes were pushed to the remote branch" + value: ${{ jobs.apply_formatting.outputs.pushed }} + commit_sha: + description: "The full commit SHA of the applied fixes" + value: ${{ jobs.apply_formatting.outputs.commit_sha }} + commit_sha_short: + description: "The short commit SHA of the applied fixes" + value: ${{ jobs.apply_formatting.outputs.commit_sha_short }} + patch_name: + description: "Name of the patch file if fixes could not be pushed" + value: ${{ jobs.apply_formatting.outputs.patch_name }} permissions: pull-requests: write contents: write +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 name: Parse command if: > - github.event_name == 'workflow_dispatch' || ( + github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || ( 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)) || - startsWith(github.event.comment.body, format('@{0}bot clang-fix', 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. 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 }} + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref_name)) || 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 }} steps: - name: Get PR Info @@ -49,26 +88,72 @@ jobs: name: Apply formatting needs: pre-check if: ${{ needs.pre-check.result == 'success' }} + outputs: + changes: ${{ steps.handle_commit.outputs.changes }} + pushed: ${{ steps.handle_commit.outputs.pushed }} + commit_sha: ${{ steps.handle_commit.outputs.commit_sha }} + commit_sha_short: ${{ steps.handle_commit.outputs.commit_sha_short }} + patch_name: ${{ steps.handle_commit.outputs.patch_name }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: phlex-src + path: ${{ env.local_checkout_path }} ref: ${{ needs.pre-check.outputs.ref }} repository: ${{ needs.pre-check.outputs.repo }} token: ${{ secrets.WORKFLOW_PAT }} - uses: DoozyX/clang-format-lint-action@bcb4eb2cb0d707ee4f3e5cc3b456eb075f12cf73 # v0.20 with: - source: "./phlex-src" + source: "./${{ env.local_checkout_path }}" clangFormatVersion: 20 inplace: "True" extensions: cpp,hpp,cpp.in,hpp.in - name: Handle fix commit + id: handle_commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: clang-format - working-directory: phlex-src + working-directory: ${{ env.local_checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} pr-info-ref: ${{ needs.pre-check.outputs.ref }} pr-info-repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: ${{ github.event_name == 'workflow_call' && inputs.skip-comment || 'false' }} + + - name: Remove eyes reaction + if: github.event_name == 'issue_comment' + 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' + 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' + }); diff --git a/.github/workflows/clang-tidy-fix.yaml b/.github/workflows/clang-tidy-fix.yaml index 065429e8e..061bd9568 100644 --- a/.github/workflows/clang-tidy-fix.yaml +++ b/.github/workflows/clang-tidy-fix.yaml @@ -8,7 +8,7 @@ name: Clang-Tidy Fix workflow_dispatch: inputs: ref: - description: "The branch, ref, or SHA to checkout. 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: @@ -35,7 +35,7 @@ jobs: # This covers repo owners, invited collaborators, and all org members. outputs: ref: - ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.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 }} tidy_checks: diff --git a/.github/workflows/cmake-format-fix.yaml b/.github/workflows/cmake-format-fix.yaml index cc0871a41..c2c7d3acc 100644 --- a/.github/workflows/cmake-format-fix.yaml +++ b/.github/workflows/cmake-format-fix.yaml @@ -8,7 +8,7 @@ run-name: "${{ github.actor }} fixing CMake format" workflow_dispatch: inputs: ref: - description: "The branch, ref, or SHA to checkout. 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: @@ -18,13 +18,34 @@ run-name: "${{ github.actor }} fixing CMake format" required: false type: string ref: - description: "The branch, ref, or SHA to checkout" + description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA)" required: true type: string repo: description: "The repository to checkout from" required: true type: string + skip-comment: + description: "Skip posting PR comments" + required: false + type: string + default: "false" + outputs: + changes: + description: "Whether any fixes were applied" + value: ${{ jobs.apply_cmake_formatting.outputs.changes }} + pushed: + description: "Whether the fixes were pushed to the remote branch" + value: ${{ jobs.apply_cmake_formatting.outputs.pushed }} + commit_sha: + description: "The full commit SHA of the applied fixes" + value: ${{ jobs.apply_cmake_formatting.outputs.commit_sha }} + commit_sha_short: + description: "The short commit SHA of the applied fixes" + value: ${{ jobs.apply_cmake_formatting.outputs.commit_sha_short }} + patch_name: + description: "Name of the patch file if fixes could not be pushed" + value: ${{ jobs.apply_cmake_formatting.outputs.patch_name }} permissions: pull-requests: write @@ -44,17 +65,14 @@ 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, format('@{0}bot format', github.event.repository.name)) || - startsWith(github.event.comment.body, format('@{0}bot cmake-fix', 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. 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 }} + (github.event.inputs.ref || github.ref_name)) || 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 }} @@ -70,6 +88,12 @@ jobs: name: Apply CMake formatting needs: pre-check if: needs.pre-check.result == 'success' + outputs: + changes: ${{ steps.handle_commit.outputs.changes }} + pushed: ${{ steps.handle_commit.outputs.pushed }} + commit_sha: ${{ steps.handle_commit.outputs.commit_sha }} + commit_sha_short: ${{ steps.handle_commit.outputs.commit_sha_short }} + patch_name: ${{ steps.handle_commit.outputs.patch_name }} steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -93,6 +117,7 @@ jobs: gersemi -i ${{ env.local_checkout_path }} - name: Handle fix commit + id: handle_commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: cmake-format @@ -100,3 +125,42 @@ jobs: token: ${{ secrets.WORKFLOW_PAT }} pr-info-ref: ${{ needs.pre-check.outputs.ref }} pr-info-repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: ${{ github.event_name == 'workflow_call' && inputs.skip-comment || 'false' }} + + - name: Remove eyes reaction + if: github.event_name == 'issue_comment' + 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' + 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' + }); diff --git a/.github/workflows/dependabot-auto-merge.yaml b/.github/workflows/dependabot-auto-merge.yaml index b55bcdfe3..214ea3f89 100644 --- a/.github/workflows/dependabot-auto-merge.yaml +++ b/.github/workflows/dependabot-auto-merge.yaml @@ -20,14 +20,10 @@ jobs: dependabot: runs-on: ubuntu-latest if: >- - (github.event_name == 'pull_request_target' && - github.event.pull_request.user.login == 'dependabot[bot]' && - github.event.pull_request.base.ref == 'main') || - (github.event_name == 'pull_request_review' && - github.event.pull_request.user.login == 'dependabot[bot]' && - github.event.pull_request.base.ref == 'main') || - (github.event_name == 'check_suite' && - github.event.check_suite.pull_requests[0] != null && + (github.event_name == 'pull_request_target' && github.event.pull_request.user.login == 'dependabot[bot]' && + github.event.pull_request.base.ref == 'main') || (github.event_name == 'pull_request_review' && + github.event.pull_request.user.login == 'dependabot[bot]' && github.event.pull_request.base.ref == 'main') || + (github.event_name == 'check_suite' && github.event.check_suite.pull_requests[0] != null && startsWith(github.event.check_suite.head_branch, 'dependabot/')) steps: - name: Get PR details @@ -76,6 +72,5 @@ jobs: fi fi # yamllint enable - env: GH_TOKEN: ${{ secrets.WORKFLOW_PAT }} diff --git a/.github/workflows/format-all.yaml b/.github/workflows/format-all.yaml new file mode 100644 index 000000000..364d7c2dc --- /dev/null +++ b/.github/workflows/format-all.yaml @@ -0,0 +1,233 @@ +name: Format All +run-name: "${{ github.actor }} fixing all format issues" + +on: + issue_comment: + types: + - created + +permissions: + pull-requests: write + contents: write + +jobs: + pre-check: + 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 }} + steps: + - name: Get PR Info + id: get_pr + uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + + clang-format: + needs: pre-check + uses: Framework-R-D/phlex/.github/workflows/clang-format-fix.yaml@main + with: + checkout-path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: "true" + secrets: inherit + + cmake-format: + needs: pre-check + uses: Framework-R-D/phlex/.github/workflows/cmake-format-fix.yaml@main + with: + checkout-path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: "true" + secrets: inherit + + header-guards: + needs: pre-check + uses: Framework-R-D/phlex/.github/workflows/header-guards-fix.yaml@main + with: + checkout-path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: "true" + secrets: inherit + + jsonnet-format: + needs: pre-check + uses: Framework-R-D/phlex/.github/workflows/jsonnet-format-fix.yaml@main + with: + checkout-path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: "true" + secrets: inherit + + markdown: + needs: pre-check + uses: Framework-R-D/phlex/.github/workflows/markdown-fix.yaml@main + with: + checkout-path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: "true" + secrets: inherit + + python: + needs: pre-check + uses: Framework-R-D/phlex/.github/workflows/python-fix.yaml@main + with: + checkout-path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: "true" + secrets: inherit + + yaml: + needs: pre-check + uses: Framework-R-D/phlex/.github/workflows/yaml-fix.yaml@main + with: + checkout-path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repo: ${{ needs.pre-check.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' }} + 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" ]; 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 + { + echo "message<> "$GITHUB_OUTPUT" + fi + + if [ "$has_failures" == "true" ]; then + exit 1 + fi + + - name: Post combined comment + if: always() + uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 + with: + message: ${{ steps.collect.outputs.message }} diff --git a/.github/workflows/header-guards-check.yaml b/.github/workflows/header-guards-check.yaml index 3e163a01c..8c5ffe609 100644 --- a/.github/workflows/header-guards-check.yaml +++ b/.github/workflows/header-guards-check.yaml @@ -134,9 +134,8 @@ jobs: - name: Check header guards id: check working-directory: ${{ env.local_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" + 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 - name: Report results diff --git a/.github/workflows/header-guards-fix.yaml b/.github/workflows/header-guards-fix.yaml index 82b776b94..9cbdcb038 100644 --- a/.github/workflows/header-guards-fix.yaml +++ b/.github/workflows/header-guards-fix.yaml @@ -12,17 +12,38 @@ run-name: "${{ github.actor }} fixing header guards" required: false type: string ref: - description: "The branch, ref, or SHA to checkout" + description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA)" required: true type: string repo: description: "The repository to checkout from" required: true type: string + skip-comment: + description: "Skip posting PR comments" + required: false + type: string + default: "false" + outputs: + changes: + description: "Whether any fixes were applied" + value: ${{ jobs.apply_fixes.outputs.changes }} + pushed: + description: "Whether the fixes were pushed to the remote branch" + value: ${{ jobs.apply_fixes.outputs.pushed }} + commit_sha: + description: "The full commit SHA of the applied fixes" + value: ${{ jobs.apply_fixes.outputs.commit_sha }} + commit_sha_short: + description: "The short commit SHA of the applied fixes" + value: ${{ jobs.apply_fixes.outputs.commit_sha_short }} + patch_name: + description: "Name of the patch file if fixes could not be pushed" + value: ${{ jobs.apply_fixes.outputs.patch_name }} workflow_dispatch: inputs: ref: - description: "The branch, ref, or SHA to checkout. 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 @@ -44,17 +65,14 @@ 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, format('@{0}bot format', github.event.repository.name)) || - startsWith(github.event.comment.body, format('@{0}bot header-guards-fix', github.event.repository.name)) - ) + 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: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && - (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} + (github.event.inputs.ref || github.ref_name)) || 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 }} @@ -70,6 +88,12 @@ jobs: name: Apply fixes needs: pre-check if: needs.pre-check.result == 'success' + outputs: + changes: ${{ steps.handle_commit.outputs.changes }} + pushed: ${{ steps.handle_commit.outputs.pushed }} + commit_sha: ${{ steps.handle_commit.outputs.commit_sha }} + commit_sha_short: ${{ steps.handle_commit.outputs.commit_sha_short }} + patch_name: ${{ steps.handle_commit.outputs.patch_name }} steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -91,6 +115,7 @@ jobs: python3 scripts/fix_header_guards.py --root . phlex plugins form || true - name: Handle fix commit + id: handle_commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: header-guards @@ -98,3 +123,42 @@ jobs: token: ${{ secrets.WORKFLOW_PAT }} pr-info-ref: ${{ needs.pre-check.outputs.ref }} pr-info-repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: ${{ github.event_name == 'workflow_call' && inputs.skip-comment || 'false' }} + + - name: Remove eyes reaction + if: github.event_name == 'issue_comment' + 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' + 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' + }); diff --git a/.github/workflows/jsonnet-format-fix.yaml b/.github/workflows/jsonnet-format-fix.yaml index bd3b8983d..a75725aa7 100644 --- a/.github/workflows/jsonnet-format-fix.yaml +++ b/.github/workflows/jsonnet-format-fix.yaml @@ -8,7 +8,7 @@ run-name: "${{ github.actor }} fixing Jsonnet format" workflow_dispatch: inputs: ref: - description: "The branch, ref, or SHA to checkout. 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: @@ -18,13 +18,34 @@ run-name: "${{ github.actor }} fixing Jsonnet format" required: false type: string ref: - description: "The branch, ref, or SHA to checkout" + description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA)" required: true type: string repo: description: "The repository to checkout from" required: true type: string + skip-comment: + description: "Skip posting PR comments" + required: false + type: string + default: "false" + outputs: + changes: + description: "Whether any fixes were applied" + value: ${{ jobs.apply_jsonnet_formatting.outputs.changes }} + pushed: + description: "Whether the fixes were pushed to the remote branch" + value: ${{ jobs.apply_jsonnet_formatting.outputs.pushed }} + commit_sha: + description: "The full commit SHA of the applied fixes" + value: ${{ jobs.apply_jsonnet_formatting.outputs.commit_sha }} + commit_sha_short: + description: "The short commit SHA of the applied fixes" + value: ${{ jobs.apply_jsonnet_formatting.outputs.commit_sha_short }} + patch_name: + description: "Name of the patch file if fixes could not be pushed" + value: ${{ jobs.apply_jsonnet_formatting.outputs.patch_name }} permissions: pull-requests: write @@ -44,17 +65,14 @@ 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, format('@{0}bot format', github.event.repository.name)) || - startsWith(github.event.comment.body, format('@{0}bot jsonnet-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. 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 }} + (github.event.inputs.ref || github.ref_name)) || 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 }} @@ -70,6 +88,12 @@ jobs: name: Apply Jsonnet formatting needs: pre-check if: needs.pre-check.result == 'success' + outputs: + changes: ${{ steps.handle_commit.outputs.changes }} + pushed: ${{ steps.handle_commit.outputs.pushed }} + commit_sha: ${{ steps.handle_commit.outputs.commit_sha }} + commit_sha_short: ${{ steps.handle_commit.outputs.commit_sha_short }} + patch_name: ${{ steps.handle_commit.outputs.patch_name }} steps: - name: Checkout code @@ -92,6 +116,7 @@ jobs: continue-on-error: true - name: Handle fix commit + id: handle_commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: jsonnetfmt @@ -99,3 +124,42 @@ jobs: token: ${{ secrets.WORKFLOW_PAT }} pr-info-ref: ${{ needs.pre-check.outputs.ref }} pr-info-repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: ${{ github.event_name == 'workflow_call' && inputs.skip-comment || 'false' }} + + - name: Remove eyes reaction + if: github.event_name == 'issue_comment' + 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' + 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' + }); diff --git a/.github/workflows/markdown-fix.yaml b/.github/workflows/markdown-fix.yaml index abc622fb8..2793cfc37 100644 --- a/.github/workflows/markdown-fix.yaml +++ b/.github/workflows/markdown-fix.yaml @@ -12,17 +12,38 @@ run-name: "${{ github.actor }} fixing Markdown format" required: false type: string ref: - description: "The branch, ref, or SHA to checkout" + description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA)" required: true type: string repo: description: "The repository to checkout from" required: true type: string + skip-comment: + description: "Skip posting PR comments" + required: false + type: string + default: "false" + outputs: + changes: + description: "Whether any fixes were applied" + value: ${{ jobs.apply_fixes.outputs.changes }} + pushed: + description: "Whether the fixes were pushed to the remote branch" + value: ${{ jobs.apply_fixes.outputs.pushed }} + commit_sha: + description: "The full commit SHA of the applied fixes" + value: ${{ jobs.apply_fixes.outputs.commit_sha }} + commit_sha_short: + description: "The short commit SHA of the applied fixes" + value: ${{ jobs.apply_fixes.outputs.commit_sha_short }} + patch_name: + description: "Name of the patch file if fixes could not be pushed" + value: ${{ jobs.apply_fixes.outputs.patch_name }} workflow_dispatch: inputs: ref: - description: "The branch, ref, or SHA to checkout. 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 @@ -44,17 +65,14 @@ 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, format('@{0}bot format', github.event.repository.name)) || - startsWith(github.event.comment.body, format('@{0}bot markdown-fix', 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. 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 }} + (github.event.inputs.ref || github.ref_name)) || 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 }} @@ -70,6 +88,12 @@ jobs: name: Apply fixes needs: pre-check if: needs.pre-check.result == 'success' + outputs: + changes: ${{ steps.handle_commit.outputs.changes }} + pushed: ${{ steps.handle_commit.outputs.pushed }} + commit_sha: ${{ steps.handle_commit.outputs.commit_sha }} + commit_sha_short: ${{ steps.handle_commit.outputs.commit_sha_short }} + patch_name: ${{ steps.handle_commit.outputs.patch_name }} steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -80,7 +104,7 @@ jobs: token: ${{ secrets.WORKFLOW_PAT }} - name: Set up Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: "20" @@ -94,6 +118,7 @@ jobs: markdownlint-cli2 --fix "**/*.md" "!**/CHANGELOG.md" || true - name: Handle fix commit + id: handle_commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: markdownlint @@ -101,3 +126,42 @@ jobs: token: ${{ secrets.WORKFLOW_PAT }} pr-info-ref: ${{ needs.pre-check.outputs.ref }} pr-info-repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: ${{ github.event_name == 'workflow_call' && inputs.skip-comment || 'false' }} + + - name: Remove eyes reaction + if: github.event_name == 'issue_comment' + 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' + 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' + }); diff --git a/.github/workflows/python-fix.yaml b/.github/workflows/python-fix.yaml index 5927d926c..41d29dc84 100644 --- a/.github/workflows/python-fix.yaml +++ b/.github/workflows/python-fix.yaml @@ -8,7 +8,7 @@ run-name: "${{ github.actor }} fixing Python code" workflow_dispatch: inputs: ref: - description: "The branch, ref, or SHA to checkout. 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: @@ -18,13 +18,34 @@ run-name: "${{ github.actor }} fixing Python code" required: false type: string ref: - description: "The branch, ref, or SHA to checkout" + description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA)" required: true type: string repo: description: "The repository to checkout from" required: true type: string + skip-comment: + description: "Skip posting PR comments" + required: false + type: string + default: "false" + outputs: + changes: + description: "Whether any fixes were applied" + value: ${{ jobs.apply_fixes.outputs.changes }} + pushed: + description: "Whether the fixes were pushed to the remote branch" + value: ${{ jobs.apply_fixes.outputs.pushed }} + commit_sha: + description: "The full commit SHA of the applied fixes" + value: ${{ jobs.apply_fixes.outputs.commit_sha }} + commit_sha_short: + description: "The short commit SHA of the applied fixes" + value: ${{ jobs.apply_fixes.outputs.commit_sha_short }} + patch_name: + description: "Name of the patch file if fixes could not be pushed" + value: ${{ jobs.apply_fixes.outputs.patch_name }} permissions: pull-requests: write @@ -51,7 +72,7 @@ jobs: 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 }} + (github.event.inputs.ref || github.ref_name)) || 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 }} @@ -66,6 +87,12 @@ jobs: name: Apply fixes needs: pre-check if: needs.pre-check.result == 'success' + outputs: + changes: ${{ steps.handle_commit.outputs.changes }} + pushed: ${{ steps.handle_commit.outputs.pushed }} + commit_sha: ${{ steps.handle_commit.outputs.commit_sha }} + commit_sha_short: ${{ steps.handle_commit.outputs.commit_sha_short }} + patch_name: ${{ steps.handle_commit.outputs.patch_name }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -94,6 +121,7 @@ jobs: ruff check --fix . || true - name: Handle fix commit + id: handle_commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: ruff @@ -101,3 +129,42 @@ jobs: token: ${{ secrets.WORKFLOW_PAT }} pr-info-ref: ${{ needs.pre-check.outputs.ref }} pr-info-repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: ${{ github.event_name == 'workflow_call' && inputs.skip-comment || 'false' }} + + - name: Remove eyes reaction + if: github.event_name == 'issue_comment' + 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' + 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' + }); diff --git a/.github/workflows/yaml-fix.yaml b/.github/workflows/yaml-fix.yaml index 55418af12..6c261ddc0 100644 --- a/.github/workflows/yaml-fix.yaml +++ b/.github/workflows/yaml-fix.yaml @@ -8,35 +8,74 @@ name: YAML Fix workflow_dispatch: inputs: ref: - description: "The branch, ref, or SHA to checkout. 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: + inputs: + checkout-path: + description: "Path to check out code to" + required: false + type: string + ref: + description: "The branch name to checkout and push fixes to (must be a branch, not a commit SHA)" + required: true + type: string + repo: + description: "The repository to checkout from" + required: true + type: string + skip-comment: + description: "Skip posting PR comments" + required: false + type: string + default: "false" + outputs: + changes: + description: "Whether any fixes were applied" + value: ${{ jobs.apply_yaml_fixes.outputs.changes }} + pushed: + description: "Whether the fixes were pushed to the remote branch" + value: ${{ jobs.apply_yaml_fixes.outputs.pushed }} + commit_sha: + description: "The full commit SHA of the applied fixes" + value: ${{ jobs.apply_yaml_fixes.outputs.commit_sha }} + commit_sha_short: + description: "The short commit SHA of the applied fixes" + value: ${{ jobs.apply_yaml_fixes.outputs.commit_sha_short }} + patch_name: + description: "Name of the patch file if fixes could not be pushed" + value: ${{ jobs.apply_yaml_fixes.outputs.patch_name }} permissions: pull-requests: write contents: write +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 name: Parse command if: > - github.event_name == 'workflow_dispatch' || ( + github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || ( 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)) || - startsWith(github.event.comment.body, format('@{0}bot yaml-fix', github.event.repository.name)) - ) + 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: - ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || - steps.get_pr.outputs.ref }} - repo: ${{ steps.get_pr.outputs.repo || github.repository }} + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref_name)) || 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 }} steps: - name: Get PR Info @@ -49,18 +88,24 @@ jobs: name: Apply YAML fixes needs: pre-check if: ${{ needs.pre-check.result == 'success' }} + outputs: + changes: ${{ steps.handle_commit.outputs.changes }} + pushed: ${{ steps.handle_commit.outputs.pushed }} + commit_sha: ${{ steps.handle_commit.outputs.commit_sha }} + commit_sha_short: ${{ steps.handle_commit.outputs.commit_sha_short }} + patch_name: ${{ steps.handle_commit.outputs.patch_name }} steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - path: phlex-src + path: ${{ env.local_checkout_path }} ref: ${{ needs.pre-check.outputs.ref }} repository: ${{ needs.pre-check.outputs.repo }} token: ${{ secrets.WORKFLOW_PAT }} - name: Setup Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: "20.x" @@ -69,14 +114,54 @@ jobs: - name: Apply YAML formatting run: | - cd phlex-src + cd ${{ env.local_checkout_path }} prettier --write '**/*.{yaml,yml}' - name: Handle fix commit + id: handle_commit uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main with: tool: YAML formatter - working-directory: phlex-src + working-directory: ${{ env.local_checkout_path }} token: ${{ secrets.WORKFLOW_PAT }} pr-info-ref: ${{ needs.pre-check.outputs.ref }} pr-info-repo: ${{ needs.pre-check.outputs.repo }} + skip-comment: ${{ github.event_name == 'workflow_call' && inputs.skip-comment || 'false' }} + + - name: Remove eyes reaction + if: github.event_name == 'issue_comment' + 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' + 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' + });