From 30e5f51c60a7940711cb0246f5ba04a0ed659e9e Mon Sep 17 00:00:00 2001 From: josephj Date: Tue, 18 Feb 2025 15:49:04 +0100 Subject: [PATCH] WIP --- .../workflows/check_dependency_changes.yml | 104 ++++++++++++++++++ .github/workflows/check_pr.yml | 47 ++++---- scripts/check_dependency_changes.py | 60 ++++++++++ 3 files changed, 189 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/check_dependency_changes.yml create mode 100755 scripts/check_dependency_changes.py diff --git a/.github/workflows/check_dependency_changes.yml b/.github/workflows/check_dependency_changes.yml new file mode 100644 index 0000000000..3097ba38bd --- /dev/null +++ b/.github/workflows/check_dependency_changes.yml @@ -0,0 +1,104 @@ +name: Check dependency changes + +on: + workflow_call: + +jobs: + check_dependency_changes: + runs-on: ubuntu-latest + name: Check dependency changes + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup gradle + uses: gradle/actions/setup-gradle@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Compare dependencies between base and head + id: compare_deps + env: + BASE_REF: ${{ github.base_ref }} + HEAD_REF: ${{ github.head_ref }} + run: | + chmod +x scripts/check_dependency_changes.py + python scripts/check_dependency_changes.py dependency_changes.md + if [ -s dependency_changes.md ]; then + echo "Found dependency changes in this PR" + echo "has_changes=true" >> $GITHUB_OUTPUT + else + echo "No dependency changes found in this PR" + echo "has_changes=false" >> $GITHUB_OUTPUT + fi + + - name: Add comment on PR + uses: thollander/actions-comment-pull-request@v3 + if: steps.compare_deps.outputs.has_changes == 'true' + with: + file-path: "${{ github.workspace }}/dependency_changes.md" + comment-tag: dependency_changes + mode: recreate + + - name: Delete comment from PR + uses: thollander/actions-comment-pull-request@v3 + if: steps.compare_deps.outputs.has_changes == 'false' + with: + comment-tag: dependency_changes + mode: delete + + - name: Delete previous review comment + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_NUMBER=${{ github.event.pull_request.number }} + + # Fetch review comments in the PR + COMMENT_ID=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER/comments --jq '.[] | select(.body | contains("")) | .id') + + if [[ -z "$COMMENT_ID" ]]; then + echo "No existing review comment was found" + exit 0 + fi + + echo "Deleting previous review comment" + + # Delete the comment + gh api repos/${{ github.repository }}/pulls/comments/$COMMENT_ID -X DELETE + + - name: Add PR review comment + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: steps.compare_deps.outputs.has_changes == 'true' + run: | + PR_NUMBER=${{ github.event.pull_request.number }} + REPO=${{ github.repository }} + + # Fetch the list of modified files in the PR + FILES=$(gh api repos/$REPO/pulls/$PR_NUMBER/files --jq '.[].filename') + + # Check if 'libs.versions.toml' is in the modified files + TARGET_FILE=$(echo "$FILES" | grep -m1 'libs.versions.toml' || echo "") + + if [[ -z "$TARGET_FILE" ]]; then + echo "'libs.versions.toml' is not found in the PR, using first modified file instead." + TARGET_FILE=$(echo "$FILES" | head -n1) + fi + + if [[ -z "$TARGET_FILE" ]]; then + echo "No files found in the PR. Exiting." + exit 1 + fi + + echo "A review comment will be posted on file: $TARGET_FILE" + + gh api repos/${{ github.repository }}/pulls/$PR_NUMBER/comments \ + -X POST \ + -F body="$(printf "Some dependencies have been modified in this PR, review the modifications in the comments before merging.\n\n")" \ + -F commit_id=${{ github.event.pull_request.head.sha }} \ + -F path="$TARGET_FILE" \ + -F subject_type="file" diff --git a/.github/workflows/check_pr.yml b/.github/workflows/check_pr.yml index e08e0fa7f2..e738767324 100644 --- a/.github/workflows/check_pr.yml +++ b/.github/workflows/check_pr.yml @@ -9,25 +9,28 @@ concurrency: cancel-in-progress: true jobs: - code_analysis: - name: Code analysis - uses: ./.github/workflows/code_analysis.yml - assemble: - name: Assemble - uses: ./.github/workflows/assemble.yml - needs: code_analysis - test: - name: Test - uses: ./.github/workflows/run_tests.yml - needs: assemble - sonar_cloud: - name: SonarCloud - uses: ./.github/workflows/sonar_cloud.yml - secrets: inherit - validate_public_api: - name: Validate public API - uses: ./.github/workflows/validate_public_api.yml - secrets: inherit - verify_dependencies: - name: Validate dependencies - uses: ./.github/workflows/validate_dependencies.yml +# code_analysis: +# name: Code analysis +# uses: ./.github/workflows/code_analysis.yml +# assemble: +# name: Assemble +# uses: ./.github/workflows/assemble.yml +# needs: code_analysis +# test: +# name: Test +# uses: ./.github/workflows/run_tests.yml +# needs: assemble +# sonar_cloud: +# name: SonarCloud +# uses: ./.github/workflows/sonar_cloud.yml +# secrets: inherit +# validate_public_api: +# name: Validate public API +# uses: ./.github/workflows/validate_public_api.yml +# secrets: inherit +# verify_dependencies: +# name: Validate dependencies +# uses: ./.github/workflows/validate_dependencies.yml + check_dependency_changes: + name: Check dependency changes + uses: ./.github/workflows/check_dependency_changes.yml diff --git a/scripts/check_dependency_changes.py b/scripts/check_dependency_changes.py new file mode 100755 index 0000000000..124d8f6966 --- /dev/null +++ b/scripts/check_dependency_changes.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +import os +import subprocess +import sys +import difflib + +# You can test this script locally with this command: +# BASE_REF="BASE_REFERENCE" HEAD_REF="HEAD_REFERENCE" python ./scripts/check_dependency_changes.py test_output.txt + +def compare_dependency_list(base_ref: str, head_ref: str): + base_dependency_list = get_dependency_list(base_ref) + head_dependency_list = get_dependency_list(head_ref) + + dependency_diff = diff_strings(base_dependency_list, head_dependency_list) + output = generate_github_comment(dependency_diff) + + with open(sys.argv[1], 'w+') as f: + f.write(output) + if os.getenv('GITHUB_STEP_SUMMARY'): os.environ[os.getenv('GITHUB_STEP_SUMMARY')] = output + +def get_dependency_list(git_ref: str) -> str: + subprocess.Popen(["git", "checkout", "-f", git_ref]) + return subprocess.check_output(["./gradlew", "dependencyList", "-q", "--no-configuration-cache"] + ).decode(sys.stdout.encoding + ).strip() + +def diff_strings(first: str, second: str) -> str: + diff = difflib.unified_diff(first.splitlines(keepends=True), second.splitlines(keepends=True)) + return ''.join(diff) + +def generate_github_comment(dependency_diff: str) -> str: + if not dependency_diff: + return "" + + return '''\ +The following dependencies have been modified in this PR: +```diff +{diff} +``` +To check the affected modules run the `dependencyList` gradle task with `includeModules=true`. + '''.format(diff=dependency_diff) + +def main(): + red = '\033[31m' + + base_ref = os.getenv('BASE_REF') + if base_ref is None: + print(red + 'BASE_REF is not provided. Please provide it in env list. Exiting...') + sys.exit(1) + + head_ref = os.getenv('HEAD_REF') + if head_ref is None: + print(red + 'HEAD_REF is not provided. Please provide it in env list. Exiting...') + sys.exit(1) + + compare_dependency_list(base_ref, head_ref) + +if __name__ == '__main__': + main()