diff --git a/.github/workflows/check_cla_ruleset.yml b/.github/workflows/check_cla_ruleset.yml index a0585261..69a63c75 100644 --- a/.github/workflows/check_cla_ruleset.yml +++ b/.github/workflows/check_cla_ruleset.yml @@ -47,13 +47,13 @@ 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 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. @@ -62,12 +62,13 @@ 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 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, diff --git a/.github/workflows/internal_vs_external.yml b/.github/workflows/internal_vs_external.yml index f9a20c44..36fa3801 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/.github/workflows/repo_policies.yml b/.github/workflows/repo_policies.yml index b49a8250..8344ba6d 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: diff --git a/requirements.txt b/requirements.txt index 4da4fca5..a7868492 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 @@ -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 diff --git a/reusable_workflows/check_membership/check_membership.py b/reusable_workflows/check_membership/check_membership.py index 82346426..ff25b7f9 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", ] 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 0227c6c3..593759cb --- 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 236a347f..00000000 --- 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)