diff --git a/.github/workflows/branch.yml b/.github/workflows/branch.yml index 240dc2dae0..260954884c 100644 --- a/.github/workflows/branch.yml +++ b/.github/workflows/branch.yml @@ -2,8 +2,11 @@ name: nf-core branch protection # This workflow is triggered on PRs to main branch on the repository # It fails when someone tries to make a PR against the nf-core `main` branch instead of `dev` on: - pull_request_target: - branches: [main] + pull_request: + branches: + - main + +permissions: {} jobs: test: @@ -12,32 +15,47 @@ jobs: # PRs to the nf-core repo main branch are only ok if coming from the nf-core repo `dev` or any `patch` branches - name: Check PRs if: github.repository == 'nf-core/tools' + env: + HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} run: | - { [[ ${{github.event.pull_request.head.repo.full_name}} == nf-core/tools ]] && [[ $GITHUB_HEAD_REF == "dev" ]]; } || [[ $GITHUB_HEAD_REF == "patch" ]] + { [[ "$HEAD_REPO" == "nf-core/tools" ]] && [[ "$GITHUB_HEAD_REF" == "dev" ]]; } || [[ "$GITHUB_HEAD_REF" == "patch" ]] - # If the above check failed, post a comment on the PR explaining the failure - - name: Post PR comment + # If the above check failed, build a comment to be posted by the shared poster workflow + - name: Build PR comment if: failure() - uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3 - with: - message: | - ## This PR is against the `main` branch :x: + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + BASE_REF: ${{ github.event.pull_request.base.ref }} + HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} + PR_USER: ${{ github.event.pull_request.user.login }} + run: | + mkdir -p pr-comment + echo "$PR_NUMBER" > pr-comment/pr_number.txt + echo "branch" > pr-comment/header.txt + cat > pr-comment/comment.md <> "$GITHUB_OUTPUT" + echo "header=$header" >> "$GITHUB_OUTPUT" + echo "post=true" >> "$GITHUB_OUTPUT" + + - name: Post PR comment + if: steps.meta.outputs.post == 'true' + uses: marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + number: ${{ steps.meta.outputs.pr_number }} + header: ${{ steps.meta.outputs.header }} + path: pr-comment/comment.md diff --git a/.prettierignore b/.prettierignore index f2983fc968..61ff2bcca6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,7 +6,9 @@ nf_core/pipeline-template/nextflow_schema.json nf_core/pipeline-template/modules.json nf_core/pipeline-template/tower.yml nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml +nf_core/pipeline-template/.github/workflows/branch.yml nf_core/pipeline-template/.github/workflows/nf-test.yml +nf_core/pipeline-template/.github/workflows/pr-comment.yml tests/data/pipeline_create_template_skip.yml # don't run on things handled by ruff *.py diff --git a/nf_core/pipeline-template/.github/workflows/branch.yml b/nf_core/pipeline-template/.github/workflows/branch.yml index 27468bc072..cbbc75e096 100644 --- a/nf_core/pipeline-template/.github/workflows/branch.yml +++ b/nf_core/pipeline-template/.github/workflows/branch.yml @@ -2,11 +2,13 @@ name: nf-core branch protection # This workflow is triggered on PRs to `main`/`master` branch on the repository # It fails when someone tries to make a PR against the nf-core `main`/`master` branch instead of `dev` on: - pull_request_target: + pull_request: branches: - main - master +permissions: {} + jobs: test: runs-on: ubuntu-latest @@ -14,33 +16,47 @@ jobs: # PRs to the nf-core repo main/master branch are only ok if coming from the nf-core repo `dev` or any `patch` branches - name: Check PRs if: github.repository == '{{ name }}' + env: + HEAD_REPO: {% raw %}${{ github.event.pull_request.head.repo.full_name }}{% endraw %} run: | - { [[ {% raw %}${{github.event.pull_request.head.repo.full_name }}{% endraw %} == {{ name }} ]] && [[ $GITHUB_HEAD_REF == "dev" ]]; } || [[ $GITHUB_HEAD_REF == "patch" ]] + { [[ "$HEAD_REPO" == {{ name }} ]] && [[ $GITHUB_HEAD_REF == "dev" ]]; } || [[ $GITHUB_HEAD_REF == "patch" ]] - # If the above check failed, post a comment on the PR explaining the failure {%- raw %} - # NOTE - this doesn't currently work if the PR is coming from a fork, due to limitations in GitHub actions secrets - - name: Post PR comment + # If the above check failed, build a comment to be posted by the shared poster workflow + - name: Build PR comment if: failure() - uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3 - with: - message: | - ## This PR is against the `${{github.event.pull_request.base.ref}}` branch :x: + env:{% raw %} + PR_NUMBER: ${{ github.event.pull_request.number }} + BASE_REF: ${{ github.event.pull_request.base.ref }} + HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} + PR_USER: ${{ github.event.pull_request.user.login }}{% endraw %} + run: | + mkdir -p pr-comment + echo "$PR_NUMBER" > pr-comment/pr_number.txt + echo "branch" > pr-comment/header.txt + cat > pr-comment/comment.md < pr-comment/pr_number.txt + echo "lint" > pr-comment/header.txt + [ -f lint_results.md ] && cp lint_results.md pr-comment/comment.md || true + + - name: Upload PR comment artifact + if: ${{ always() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: pr-comment + path: pr-comment/{%- endraw %} diff --git a/nf_core/pipeline-template/.github/workflows/linting_comment.yml b/nf_core/pipeline-template/.github/workflows/linting_comment.yml deleted file mode 100644 index c2923041b3..0000000000 --- a/nf_core/pipeline-template/.github/workflows/linting_comment.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: nf-core linting comment -# This workflow is triggered after the linting action is complete -# It posts an automated comment to the PR, even if the PR is coming from a fork {%- raw %} - -on: - workflow_run: - workflows: ["nf-core linting"] - -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Download lint results - uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 - with: - workflow: linting.yml - workflow_conclusion: completed - - - name: Get PR number - id: pr_number - run: echo "pr_number=$(cat linting-logs/PR_number.txt)" >> $GITHUB_OUTPUT - - - name: Post PR comment - uses: marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - number: ${{ steps.pr_number.outputs.pr_number }} - path: linting-logs/lint_results.md {%- endraw %} diff --git a/nf_core/pipeline-template/.github/workflows/pr-comment.yml b/nf_core/pipeline-template/.github/workflows/pr-comment.yml new file mode 100644 index 0000000000..46b015f093 --- /dev/null +++ b/nf_core/pipeline-template/.github/workflows/pr-comment.yml @@ -0,0 +1,68 @@ +name: Post PR comment +# Shared, privileged comment poster. +# +# This is the single workflow that runs with a write token. It is triggered +# after any of the listed "producer" workflows complete on a pull request. +# Each producer runs untrusted PR code (if any) with a read-only token and +# uploads a `pr-comment` artifact describing the comment to post; this workflow +# only ever reads that plain-text artifact, so no PR code is executed here. +# +# Artifact contract (uploaded by producers under the name `pr-comment`): +# pr_number.txt - the pull request number +# header.txt - sticky-comment identifier (keeps comment types separate) +# comment.md - the Markdown body (omit the file to post nothing) + +on: + workflow_run: + workflows: + - "nf-core linting" + - "nf-core template version comment" + - "nf-core branch protection" + +permissions: + actions: read + contents: read + pull-requests: write + +jobs: + post-comment: + runs-on: ubuntu-latest + # Only act on runs that were triggered by a pull request. + if: github.event.workflow_run.event == 'pull_request' + steps: + - name: Download PR comment artifact + uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 + with:{% raw %} + run_id: ${{ github.event.workflow_run.id }} + name: pr-comment + if_no_artifact_found: ignore + + - name: Read comment metadata + id: meta + run: | + # No comment body means there is nothing to post. + [ -f pr-comment/comment.md ] || exit 0 + + pr_number=$(cat pr-comment/pr_number.txt) + header=$(cat pr-comment/header.txt) + + # Guard against anything unexpected ending up in the PR number. + case "$pr_number" in + ''|*[!0-9]*) + echo "Invalid PR number: '$pr_number'" + exit 1 + ;; + esac + + echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT" + echo "header=$header" >> "$GITHUB_OUTPUT" + echo "post=true" >> "$GITHUB_OUTPUT" + + - name: Post PR comment + if: steps.meta.outputs.post == 'true' + uses: marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + number: ${{ steps.meta.outputs.pr_number }} + header: ${{ steps.meta.outputs.header }} + path: pr-comment/comment.md{%- endraw %} diff --git a/nf_core/pipeline-template/.github/workflows/template-version-comment.yml b/nf_core/pipeline-template/.github/workflows/template-version-comment.yml index a3333571e1..7a15d1f48d 100644 --- a/nf_core/pipeline-template/.github/workflows/template-version-comment.yml +++ b/nf_core/pipeline-template/.github/workflows/template-version-comment.yml @@ -2,10 +2,13 @@ name: nf-core template version comment # This workflow is triggered on PRs to check if the pipeline template version matches the latest nf-core version. # It posts a comment to the PR, even if it comes from a fork.{%- raw %} -on: pull_request_target +on: + pull_request: + +permissions: {} jobs: - template_version: + check_template_version: runs-on: ubuntu-latest steps: - name: Check out pipeline code @@ -22,25 +25,36 @@ jobs: - name: Install nf-core run: | python -m pip install --upgrade pip - pip install nf-core==${{ steps.read_yml.outputs['nf_core_version'] }} + pip install nf-core + + - name: Build PR comment if template is outdated + # The fork-controlled version is passed via the environment and only ever + # used as quoted shell data (never interpolated into a command), so it + # cannot be used for script injection. + env: + PR_VERSION: ${{ steps.read_yml.outputs['nf_core_version'] }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + mkdir -p pr-comment + echo "$PR_NUMBER" > pr-comment/pr_number.txt + echo "template-version" > pr-comment/header.txt + + latest_version=$(nf-core --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1) - - name: Check nf-core outdated - id: nf_core_outdated - run: echo "OUTPUT=$(pip list --outdated | grep nf-core)" >> ${GITHUB_ENV} + if [ -n "$PR_VERSION" ] && [ -n "$latest_version" ] && [ "$PR_VERSION" != "$latest_version" ]; then + cat > pr-comment/comment.md < [!WARNING] + > Newer version of the nf-core template is available. + > + > Your pipeline is using an old version of the nf-core template: ${PR_VERSION}. + > Please update your pipeline to the latest version. + > + > For more documentation on how to update your pipeline, please see the [Synchronisation documentation](https://nf-co.re/docs/developing/template-syncs/overview). + EOF + fi{% endraw %} - - name: Post nf-core template version comment - uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3 - if: | - contains(env.OUTPUT, 'nf-core') + - name: Upload PR comment artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - repo-token: ${{ secrets.NF_CORE_BOT_AUTH_TOKEN }} - allow-repeats: false - message: | - > [!WARNING] - > Newer version of the nf-core template is available. - > - > Your pipeline is using an old version of the nf-core template: ${{ steps.read_yml.outputs['nf_core_version'] }}. - > Please update your pipeline to the latest version. - > - > For more documentation on how to update your pipeline, please see the [Synchronisation documentation](https://nf-co.re/docs/developing/template-syncs/overview). - #{%- endraw %} + name: pr-comment + path: pr-comment/ diff --git a/nf_core/pipelines/create/template_features.yml b/nf_core/pipelines/create/template_features.yml index 6c5036a9d3..756af168e3 100644 --- a/nf_core/pipelines/create/template_features.yml +++ b/nf_core/pipelines/create/template_features.yml @@ -29,7 +29,7 @@ repository_setup: - ".github/ISSUE_TEMPLATE/feature_request.yml" - ".github/PULL_REQUEST_TEMPLATE.md" - ".github/workflows/branch.yml" - - ".github/workflows/linting_comment.yml" + - ".github/workflows/pr-comment.yml" - ".github/workflows/linting.yml" - ".github/.dockstore.yml" readme: @@ -117,7 +117,7 @@ continuous_integration_testing: - ".github/workflows/nf-test.yml" - ".github/actions/get-shards/action.yml" - ".github/actions/nf-test/action.yml" - - ".github/workflows/linting_comment.yml" + - ".github/workflows/pr-comment.yml" - ".github/workflows/linting.yml" nfcore_pipelines: False custom_pipelines: True diff --git a/nf_core/pipelines/lint/files_exist.py b/nf_core/pipelines/lint/files_exist.py index 9fb55ccedb..8e3c6f567e 100644 --- a/nf_core/pipelines/lint/files_exist.py +++ b/nf_core/pipelines/lint/files_exist.py @@ -34,7 +34,7 @@ def files_exist(self) -> dict[str, list[str]]: .github/workflows/nf-test.yml .github/actions/get-shards/action.yml .github/actions/nf-test/action.yml - .github/workflows/linting_comment.yml + .github/workflows/pr-comment.yml .github/workflows/linting.yml [LICENSE, LICENSE.md, LICENCE, LICENCE.md] # NB: British / American spelling assets/email_template.html @@ -149,7 +149,7 @@ def files_exist(self) -> dict[str, list[str]]: [Path(".github", "workflows", "nf-test.yml")], [Path(".github", "actions", "get-shards", "action.yml")], [Path(".github", "actions", "nf-test", "action.yml")], - [Path(".github", "workflows", "linting_comment.yml")], + [Path(".github", "workflows", "pr-comment.yml")], [Path(".github", "workflows", "linting.yml")], [Path("assets", "email_template.html")], [Path("assets", "email_template.txt")], diff --git a/nf_core/pipelines/lint/files_unchanged.py b/nf_core/pipelines/lint/files_unchanged.py index 20cef65ae5..3d5f87c266 100644 --- a/nf_core/pipelines/lint/files_unchanged.py +++ b/nf_core/pipelines/lint/files_unchanged.py @@ -28,7 +28,7 @@ def files_unchanged(self) -> dict[str, list[str] | bool]: .github/ISSUE_TEMPLATE/feature_request.yml .github/PULL_REQUEST_TEMPLATE.md .github/workflows/branch.yml - .github/workflows/linting_comment.yml + .github/workflows/pr-comment.yml .github/workflows/linting.yml assets/email_template.html assets/email_template.txt @@ -92,7 +92,7 @@ def files_unchanged(self) -> dict[str, list[str] | bool]: [Path(".github", "ISSUE_TEMPLATE", "feature_request.yml")], [Path(".github", "PULL_REQUEST_TEMPLATE.md")], [Path(".github", "workflows", "branch.yml")], - [Path(".github", "workflows", "linting_comment.yml")], + [Path(".github", "workflows", "pr-comment.yml")], [Path(".github", "workflows", "linting.yml")], [Path("assets", "email_template.html")], [Path("assets", "email_template.txt")],