From 0c5b58e53326944d3c6220612a56159561c939a6 Mon Sep 17 00:00:00 2001 From: Carly Gundy <47304080+cgundy@users.noreply.github.com> Date: Thu, 15 May 2025 20:24:58 +0200 Subject: [PATCH 01/10] chore(IDX): add workflow_call back (#158) --- .github/workflows/repo_policies.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/repo_policies.yml b/.github/workflows/repo_policies.yml index b49a825..8344ba6 100644 --- a/.github/workflows/repo_policies.yml +++ b/.github/workflows/repo_policies.yml @@ -3,6 +3,7 @@ name: Bot Policies Ruleset on: pull_request_target: merge_group: + workflow_call: jobs: check-is-bot: From e54cc8c4c75ad50126e46ec5c1becba32fe124d1 Mon Sep 17 00:00:00 2001 From: Carly Gundy <47304080+cgundy@users.noreply.github.com> Date: Mon, 19 May 2025 20:55:36 +0200 Subject: [PATCH 02/10] chore(IDX): pin action to commit (#160) --- .github/workflows/check_cla_ruleset.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_cla_ruleset.yml b/.github/workflows/check_cla_ruleset.yml index a058526..b4dbed9 100644 --- a/.github/workflows/check_cla_ruleset.yml +++ b/.github/workflows/check_cla_ruleset.yml @@ -53,7 +53,7 @@ jobs: - name: Close Pull Request id: close_pr if: ${{ steps.accepts_external_contrib.outputs.accepts_contrib != 'true' }} - uses: superbrothers/close-pull-request@v3 + uses: superbrothers/close-pull-request@9c18513d320d7b2c7185fb93396d0c664d5d8448 #v3 with: comment: | Thank you for contributing! Unfortunately this repository does not accept external contributions yet. From 875ac0e69fa67cf96de81cdada7014d83420fd82 Mon Sep 17 00:00:00 2001 From: Carly Gundy <47304080+cgundy@users.noreply.github.com> Date: Tue, 20 May 2025 15:11:40 +0200 Subject: [PATCH 03/10] chore(IDX): add github token to CLA check (#159) * chore(IDX): add checkout steps back to the CLA * switch to base * switch to base * update owner and repo * update * add context * update ref * update token --- .github/workflows/check_cla_ruleset.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check_cla_ruleset.yml b/.github/workflows/check_cla_ruleset.yml index b4dbed9..b01625d 100644 --- a/.github/workflows/check_cla_ruleset.yml +++ b/.github/workflows/check_cla_ruleset.yml @@ -47,7 +47,7 @@ jobs: python reusable_workflows/check_membership/check_external_contrib.py shell: bash env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} REPO: ${{ github.event.repository.name }} - name: Close Pull Request @@ -68,6 +68,7 @@ jobs: uses: actions/github-script@v6 if: ${{ steps.accepts_external_contrib.outputs.accepts_contrib != 'false' }} with: + github-token: ${{ steps.app-token.outputs.token }} script: | github.rest.issues.addLabels({ issue_number: context.issue.number, From e81129d1398beb4080663de1a741ccdc1485f53d Mon Sep 17 00:00:00 2001 From: Carly Gundy <47304080+cgundy@users.noreply.github.com> Date: Mon, 2 Jun 2025 18:24:31 +0200 Subject: [PATCH 04/10] chore(IDX): add new bot to list of approved bots (#161) --- reusable_workflows/check_membership/check_membership.py | 1 + 1 file changed, 1 insertion(+) diff --git a/reusable_workflows/check_membership/check_membership.py b/reusable_workflows/check_membership/check_membership.py index 8234642..ff25b7f 100644 --- a/reusable_workflows/check_membership/check_membership.py +++ b/reusable_workflows/check_membership/check_membership.py @@ -10,6 +10,7 @@ "gix-bot", "mergify[bot]", "pr-automation-bot-public[bot]", + "pr-creation-bot-dfinity-ic[bot]", "pr-creation-bot-dfinity[bot]", "sa-github-api", ] From 53b2e65845232df4db9e6632ccda8221c47064a1 Mon Sep 17 00:00:00 2001 From: Marko Kosmerl Date: Wed, 4 Jun 2025 10:14:09 +0200 Subject: [PATCH 05/10] Update check_cla_ruleset.yml (#162) Cleaning up quotes --- .github/workflows/check_cla_ruleset.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_cla_ruleset.yml b/.github/workflows/check_cla_ruleset.yml index b01625d..69a63c7 100644 --- a/.github/workflows/check_cla_ruleset.yml +++ b/.github/workflows/check_cla_ruleset.yml @@ -62,7 +62,7 @@ jobs: We hope you understand and will come back once we accept external PRs. - — The DFINITY Foundation""" + — The DFINITY Foundation - name: Add Label uses: actions/github-script@v6 From dcdb060d030450aaa15b428786efcbf14e143ec5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 12:48:35 +0200 Subject: [PATCH 06/10] chore(deps): bump requests from 2.32.3 to 2.32.4 (#163) Bumps [requests](https://github.com/psf/requests) from 2.32.3 to 2.32.4. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.3...v2.32.4) --- updated-dependencies: - dependency-name: requests dependency-version: 2.32.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4da4fca..918b7f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -54,7 +54,7 @@ pytest==8.3.4 # via -r requirements.in python-dateutil==2.9.0.post0 # via github3-py -requests==2.32.3 +requests==2.32.4 # via github3-py six==1.17.0 # via python-dateutil From f46a8fa628d0e02b9cf1b455907f461aab09b735 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 10:37:45 +0200 Subject: [PATCH 07/10] chore(deps): bump urllib3 from 2.2.3 to 2.5.0 (#164) Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.3 to 2.5.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.2.3...2.5.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.5.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 918b7f2..a786849 100644 --- a/requirements.txt +++ b/requirements.txt @@ -64,5 +64,5 @@ typing-extensions==4.12.2 # mypy uritemplate==4.1.1 # via github3-py -urllib3==2.2.3 +urllib3==2.5.0 # via requests From 2262404c941a9ac9709feae1e23909f21cfdd668 Mon Sep 17 00:00:00 2001 From: Carly Gundy <47304080+cgundy@users.noreply.github.com> Date: Tue, 7 Oct 2025 09:42:06 +0200 Subject: [PATCH 08/10] chore(IDX): allow testing with droid-uexternal user (#165) Temporarily allow 'droid-uexternal' bot to contribute for testing. --- reusable_workflows/check_membership/check_membership.py | 1 + 1 file changed, 1 insertion(+) diff --git a/reusable_workflows/check_membership/check_membership.py b/reusable_workflows/check_membership/check_membership.py index ff25b7f..4cdffad 100644 --- a/reusable_workflows/check_membership/check_membership.py +++ b/reusable_workflows/check_membership/check_membership.py @@ -13,6 +13,7 @@ "pr-creation-bot-dfinity-ic[bot]", "pr-creation-bot-dfinity[bot]", "sa-github-api", + "droid-uexternal", # temporarily allow external user to contribute to repo not open to contributions for testing purposes ] From 29b1a74b2171d9bff3b9f1030a777a51d3240a68 Mon Sep 17 00:00:00 2001 From: Carly Gundy <47304080+cgundy@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:26:45 +0200 Subject: [PATCH 09/10] Revert "chore(IDX): allow testing with droid-uexternal user (#165)" (#167) This reverts commit 2262404c941a9ac9709feae1e23909f21cfdd668. --- reusable_workflows/check_membership/check_membership.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reusable_workflows/check_membership/check_membership.py b/reusable_workflows/check_membership/check_membership.py index 4cdffad..ff25b7f 100644 --- a/reusable_workflows/check_membership/check_membership.py +++ b/reusable_workflows/check_membership/check_membership.py @@ -13,7 +13,6 @@ "pr-creation-bot-dfinity-ic[bot]", "pr-creation-bot-dfinity[bot]", "sa-github-api", - "droid-uexternal", # temporarily allow external user to contribute to repo not open to contributions for testing purposes ] From 46f06b833d9b69e0fb50fbef077ed05d1d96b414 Mon Sep 17 00:00:00 2001 From: Bas van Dijk Date: Mon, 20 Oct 2025 14:02:53 +0200 Subject: [PATCH 10/10] chore: close external PRs that touch blacklisted files (#166) Instead of just commenting on external PRs that touch blacklisted files this commit causes them to be closed as well. We intend to use this in dfinity/ic to automatically close PRs by non-DFINITY contributors that touch files under `.github`. See: https://github.com/dfinity/ic/pull/7307. This also changes the definition of an "external" PR from any PR created by a non-DFINITY member to any PR created from a fork. The latter is easier to determine because we don't need to query the GitHub API. Also note that the set of PRs created from forks includes the set of PRs created by non-DFINITY members since non-DFINITY members can create PRs from source repos since they're not allowed to push there. Finally this commit simplifies the `reusable_workflows/repo_policies/check_external_changes.py` Python code by not fetching the `.github/repo_policies/EXTERNAL_CONTRIB_BLACKLIST` file from within the script but using the `actions/checkout` action instead. Tested here: https://github.com/dfinity/test-compliant-repository-public/pull/72#issuecomment-3414675955. --- .github/workflows/internal_vs_external.yml | 68 +++++++++--------- .../repo_policies/check_external_changes.py | 70 ++++++------------- .../tests/test_external_changes.py | 29 -------- 3 files changed, 58 insertions(+), 109 deletions(-) mode change 100644 => 100755 reusable_workflows/repo_policies/check_external_changes.py delete mode 100644 reusable_workflows/tests/test_external_changes.py diff --git a/.github/workflows/internal_vs_external.yml b/.github/workflows/internal_vs_external.yml index f9a20c4..36fa380 100644 --- a/.github/workflows/internal_vs_external.yml +++ b/.github/workflows/internal_vs_external.yml @@ -11,15 +11,10 @@ permissions: pull-requests: write jobs: - check-membership: - uses: dfinity/public-workflows/.github/workflows/check_membership.yml@main - secrets: inherit - revoke-approvals: name: Check Revoke Approvals runs-on: ubuntu-latest - needs: check-membership - if: ${{ needs.check-membership.outputs.is_member != 'true' && needs.check-membership.result == 'success' }} + if: github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository steps: - name: Dismiss Pull Request Reviews if: ${{ ! github.event.pull_request_target.draft }} @@ -54,52 +49,61 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # actor is github actions with above permissions GH_ORG: ${{ github.repository_owner }} REPO: ${{ github.event.repository.name }} - PULL_NUMBER: ${{ github.event.pull_request.number }} + PULL_NUMBER: ${{ github.event.pull_request.number }} check-external-file-changes: name: Check Unallowed File Changes runs-on: ubuntu-latest - needs: check-membership - if: ${{ needs.check-membership.outputs.is_member != 'true' && needs.check-membership.result == 'success' }} + if: github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository steps: - # First check out code from public-workflows - - name: Checkout - uses: actions/checkout@v4 + - name: Checkout EXTERNAL_CONTRIB_BLACKLIST from ${{ github.repository }} + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: - repository: dfinity/public-workflows - path: public-workflows + path: repo + # actions/checkout will checkout the target repo and default branch by default + # when triggered by pull_request_target. However for security reasons we want to + # be explicit here. + repository: ${{ github.repository }} + ref: ${{ github.event.repository.default_branch }} + sparse-checkout: .github/repo_policies/EXTERNAL_CONTRIB_BLACKLIST - - name: Python Setup - uses: ./public-workflows/.github/workflows/python-setup + - name: Checkout check_external_changes.py from dfinity/public-workflows + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: - working-directory: public-workflows + repository: dfinity/public-workflows + path: public-workflows + sparse-checkout: reusable_workflows/repo_policies/check_external_changes.py - name: Get changed files - id: changed-files uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5 with: - use_rest_api: 'true' + use_rest_api: true + json: true + write_output_files: true - name: Check External Changes - id: check-external-changes - run: | - export PYTHONPATH="$PWD/public-workflows/reusable_workflows/" - python public-workflows/reusable_workflows/repo_policies/check_external_changes.py - shell: bash + if: ${{ hashFiles('repo/.github/repo_policies/EXTERNAL_CONTRIB_BLACKLIST') != '' }} + id: check_external_changes + run: public-workflows/reusable_workflows/repo_policies/check_external_changes.py env: - CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # no actual token stored, read-only permissions - ORG: ${{ github.repository_owner }} - REPO: ${{ github.event.repository.name }} + # populated by the action + # https://github.com/tj-actions/changed-files/blob/d03a93c0dbfac6d6dd6a0d8a5e7daff992b07449/README.md?plain=1#L569-L572 + CHANGED_FILES_JSON_PATH: ".github/outputs/all_changed_and_modified_files.json" + EXTERNAL_CONTRIB_BLACKLIST_PATH: "repo/.github/repo_policies/EXTERNAL_CONTRIB_BLACKLIST" - - name: Add PR Comment + - name: Close PR uses: actions/github-script@v7 - if: ${{ failure() }} + if: ${{ !cancelled() && steps.check_external_changes.conclusion == 'failure' }} with: script: | - let message = "Changes made to unallowed files. " + github.rest.pulls.update({ + pull_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + state: 'closed' + }) + let message = "Closed Pull Request since changes were made to [unallowed files](${{ github.server_url }}/${{ github.repository }}/blob/${{ github.event.repository.default_branch }}/.github/repo_policies/EXTERNAL_CONTRIB_BLACKLIST).\n\n" message += 'Please see details here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n\n' - github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, diff --git a/reusable_workflows/repo_policies/check_external_changes.py b/reusable_workflows/repo_policies/check_external_changes.py old mode 100644 new mode 100755 index 0227c6c..593759c --- a/reusable_workflows/repo_policies/check_external_changes.py +++ b/reusable_workflows/repo_policies/check_external_changes.py @@ -1,65 +1,39 @@ +#!/usr/bin/env python3 import fnmatch +import json +import os import sys +from pathlib import Path -import github3 -from shared.utils import download_gh_file, load_env_vars +def main(): + EXTERNAL_CONTRIB_BLACKLIST_PATH = os.environ['EXTERNAL_CONTRIB_BLACKLIST_PATH'] -EXTERNAL_CONTRIB_BLACKLIST_PATH = ".github/repo_policies/EXTERNAL_CONTRIB_BLACKLIST" + blacklisted_patterns = [ + pattern for line in Path(EXTERNAL_CONTRIB_BLACKLIST_PATH).read_text().splitlines() + if (pattern := line.split("#")[0].strip()) != "" + ] + with open(os.environ['CHANGED_FILES_JSON_PATH'], 'r') as f: + changed_files = json.load(f) -def get_blacklisted_files(repo: github3.github.repo) -> list[str]: - """ - Loads the config from the repository that contains the list of blacklisted files. - """ - try: - config_file = download_gh_file(repo, EXTERNAL_CONTRIB_BLACKLIST_PATH) - except github3.exceptions.NotFoundError: - return [] - blacklisted_files = [ - line for line in config_file.splitlines() if line.strip() and not line.strip().startswith("#") - ] - return blacklisted_files + print(f"Changed files: {changed_files}") + print(f"Blacklisted patterns: {blacklisted_patterns}") + if blacklisted_patterns == []: + print("No blacklisted patterns found.") + sys.exit(0) -def check_files_against_blacklist(changed_files: list, blacklist_files: list) -> None: - """ - Check if any changed files match the blacklist rules using glob pattern matching. - """ - violations = [] - for file in changed_files: - for rule in blacklist_files: - if fnmatch.fnmatch(file, rule): # Use glob pattern matching - violations.append(file) + violations = [ + file for pattern in blacklisted_patterns for file in changed_files + if fnmatch.fnmatch(file, pattern) + ] if len(violations) > 0: print(f"No changes allowed to files: {violations}") sys.exit(1) - else: - print("All changed files pass conditions.") - - -def main(): - # Environment variables - REQUIRED_ENV_VARS = ["REPO", "CHANGED_FILES", "ORG", "GH_TOKEN"] - env_vars = load_env_vars(REQUIRED_ENV_VARS) - - gh = github3.login(token=env_vars["GH_TOKEN"]) - repo = gh.repository(owner=env_vars["ORG"], repository=env_vars["REPO"]) - - # Get changed files - changed_files = env_vars["CHANGED_FILES"].split() - print(f"Changed files: {changed_files}") - - blacklist_files = get_blacklisted_files(repo) - - if blacklist_files == []: - print("No blacklisted files found found.") - sys.exit(0) - - # Check changed files against blacklist - check_files_against_blacklist(changed_files, blacklist_files) + print("All changed files pass conditions.") if __name__ == "__main__": diff --git a/reusable_workflows/tests/test_external_changes.py b/reusable_workflows/tests/test_external_changes.py deleted file mode 100644 index 236a347..0000000 --- a/reusable_workflows/tests/test_external_changes.py +++ /dev/null @@ -1,29 +0,0 @@ -import pytest -from unittest.mock import patch -from repo_policies.check_external_changes import check_files_against_blacklist - - - -@patch("os.system") -def test_check_files_against_blacklist_match(os_system): - changed_files = ["file1.py", "docs/README.md"] - blacklist_files = ["file2.*", "docs/*.md"] - - with pytest.raises(SystemExit): - check_files_against_blacklist(changed_files, blacklist_files) - - -def test_check_files_against_blacklist_no_match(): - changed_files = ["file1.txt", "docs/README.txt"] - blacklist_files = ["*.py", "docs/*.md"] - - # Should not raise an exception - check_files_against_blacklist(changed_files, blacklist_files) - - -def test_check_files_against_blacklist_empty_blacklist(): - changed_files = ["file1.py", "docs/README.md"] - blacklist_files = [] - - # Should not raise an exception - check_files_against_blacklist(changed_files, blacklist_files)