From 962e007fc7a9a70ca35c2469d53894d3ff0c147a Mon Sep 17 00:00:00 2001 From: F-G Fernandez <26927750+frgfm@users.noreply.github.com> Date: Mon, 29 Jan 2024 04:39:37 +0100 Subject: [PATCH] ci(github): add jobs to verify PR labels and title (#74) * docs(contributing): update commit format * ci(github): update PR labeler * ci(github): update release note generator * ci(github): add a job to verify PR labels * ci(github): add job to check PR title --- .github/labeler.yml | 22 +++----- .github/release.yml | 27 ++++----- .github/verify_labels.py | 97 +++++++++++++++++++++++++++++++++ .github/workflows/pr-labels.yml | 27 +++++++++ .github/workflows/pr-title.yml | 21 +++++++ CONTRIBUTING.md | 2 +- pyproject.toml | 2 +- 7 files changed, 166 insertions(+), 32 deletions(-) create mode 100644 .github/verify_labels.py create mode 100644 .github/workflows/pr-labels.yml create mode 100644 .github/workflows/pr-title.yml diff --git a/.github/labeler.yml b/.github/labeler.yml index 1b3b528..7f5c07f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -10,6 +10,12 @@ - changed-files: - any-glob-to-any-file: apm/* +'module: core': +- changed-files: + - any-glob-to-any-file: + - src/app/main.py + - src/app/core/* + 'module: database': - changed-files: - any-glob-to-any-file: src/app/db.py @@ -18,12 +24,6 @@ - changed-files: - any-glob-to-any-file: src/app/services/* -'module: core': -- changed-files: - - any-glob-to-any-file: - - src/app/main.py - - src/app/core/* - 'module: models': - changed-files: - any-glob-to-any-file: src/app/models.py @@ -84,19 +84,13 @@ - Dockerfile - traefik/* -'topic: documentation': +'topic: docs': - changed-files: - any-glob-to-any-file: - README.md - CONTRIBUTING.md - CODE_OF_CONDUCT.md -'topic: security': -- changed-files: - - any-glob-to-any-file: - - src/app/api/dependencies.py - - src/app/security.py - -'type: code quality': +'topic: style': - changed-files: - any-glob-to-any-file: .pre-commit-config.yaml diff --git a/.github/release.yml b/.github/release.yml index 8f7f47c..e0bd916 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -4,21 +4,16 @@ changelog: - ignore-for-release categories: - title: Breaking Changes 🛠 - labels: - - "type: breaking change" - # NEW FEATURES - - title: New Features 🚀 - labels: - - "type: new feature" - # BUG FIXES + labels: ["type: breaking change"] + - title: New Features ✨ + labels: ["type: feat"] - title: Bug Fixes 🐛 - labels: - - "type: bug" - # IMPROVEMENTS + labels: ["type: fix"] + - title: Dependencies + labels: ["dependencies"] + - title: Documentation 📖 + labels: ["topic: docs"] - title: Improvements - labels: - - "type: enhancement" - # MISC - - title: Miscellaneous - labels: - - "type: misc" + labels: ["type: improvement"] + - title: Other changes + labels: ["*"] diff --git a/.github/verify_labels.py b/.github/verify_labels.py new file mode 100644 index 0000000..b36bb61 --- /dev/null +++ b/.github/verify_labels.py @@ -0,0 +1,97 @@ +# Copyright (C) 2024, Quack AI. + +# This program is licensed under the Apache License 2.0. +# See LICENSE or go to for full license details. + +""" +Borrowed & adapted from https://github.com/pytorch/vision/blob/main/.github/process_commit.py +This script finds the merger responsible for labeling a PR by a commit SHA. It is used by the workflow in +'.github/workflows/pr-labels.yml'. If there exists no PR associated with the commit or the PR is properly labeled, +this script is a no-op. +Note: we ping the merger only, not the reviewers, as the reviewers can sometimes be external to torchvision +with no labeling responsibility, so we don't want to bother them. +""" + +from typing import Any, Set, Tuple + +import requests + +# For a PR to be properly labeled it should have one primary label and one secondary label + +# Should specify the type of change +PRIMARY_LABELS = { + "type: feat", + "type: fix", + "type: improvement", + "type: misc", +} + +# Should specify what has been modified +SECONDARY_LABELS = { + "topic: docs", + "topic: build", + "topic: ci", + "topic: style", + "topic: migration", + "topic: docker", + "ext: tests", + "ext: scripts", + "ext: apm", + "module: core", + "module: database", + "module: services", + "module: models", + "module: schemas", + "module: crud", + "endpoint: login", + "endpoint: users", + "endpoint: repos", + "endpoint: guidelines", + "endpoint: compute", + "endpoint: code", +} + +GH_ORG = "quack-ai" +GH_REPO = "contribution-api" + + +def query_repo(cmd: str, *, accept) -> Any: + response = requests.get( + f"https://api.github.com/repos/{GH_ORG}/{GH_REPO}/{cmd}", + headers={"Accept": accept}, + timeout=5, + ) + return response.json() + + +def get_pr_merger_and_labels(pr_number: int) -> Tuple[str, Set[str]]: + # See https://docs.github.com/en/rest/reference/pulls#get-a-pull-request + data = query_repo(f"pulls/{pr_number}", accept="application/vnd.github.v3+json") + merger = data.get("merged_by", {}).get("login") + labels = {label["name"] for label in data["labels"]} + return merger, labels + + +def main(args): + merger, labels = get_pr_merger_and_labels(args.pr) + is_properly_labeled = bool(PRIMARY_LABELS.intersection(labels) and SECONDARY_LABELS.intersection(labels)) + if isinstance(merger, str) and not is_properly_labeled: + print(f"@{merger}") + + +def parse_args(): + import argparse + + parser = argparse.ArgumentParser( + description="PR label checker", formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + + parser.add_argument("pr", type=int, help="PR number") + args = parser.parse_args() + + return args + + +if __name__ == "__main__": + args = parse_args() + main(args) diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml new file mode 100644 index 0000000..49f5f27 --- /dev/null +++ b/.github/workflows/pr-labels.yml @@ -0,0 +1,27 @@ +name: pr-labels + +on: + pull_request: + branches: main + types: closed + +jobs: + is-properly-labeled: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + - name: Install requests + run: pip install requests + - name: Process commit and find merger responsible for labeling + id: commit + run: echo "::set-output name=merger::$(python .github/verify_labels.py ${{ github.event.pull_request.number }})" + - name: Comment PR + uses: actions/github-script@v7 + if: ${{ steps.commit.outputs.merger != '' }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { issue: { number: issue_number }, repo: { owner, repo } } = context; + github.issues.createComment({ issue_number, owner, repo, body: 'Hey ${{ steps.commit.outputs.merger }} 👋\nYou merged this PR, but it is not correctly labeled. The list of valid labels is available at https://github.com/quack-ai/companion/blob/main/.github/verify_labels.py' }); diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..be9de25 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,21 @@ +name: pr-title + +on: + pull_request_target: + types: [opened, reopened, edited, synchronize] + pull_request: + types: [opened, reopened, edited, synchronize] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + with: + subjectPattern: ^(?![A-Z]).+$ + subjectPatternError: | + The subject "{subject}" found in the pull request title "{title}" + didn't match the configured pattern. Please ensure that the subject + doesn't start with an uppercase character. + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 73525d4..e68db12 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,7 +76,7 @@ pre-commit install #### Commits - **Code**: ensure to provide docstrings to your Python code. In doing so, please follow [Google-style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) so it can ease the process of documentation later. -- **Commit message**: please follow [Udacity guide](http://udacity.github.io/git-styleguide/) +- **Commit message**: please follow [Angular commit format](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format) #### Unit tests diff --git a/pyproject.toml b/pyproject.toml index 9c77aa5..5c99f06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,7 +115,7 @@ indent-style = "space" [tool.ruff.per-file-ignores] "**/__init__.py" = ["I001", "F401", "CPY001"] "scripts/**.py" = ["D", "T201", "S101", "ANN"] -".github/**.py" = ["D", "T201"] +".github/**.py" = ["D", "T201", "ANN"] "client/docs/**.py" = ["E402"] "src/tests/**.py" = ["D103", "CPY001", "S101", "T201", "ANN001", "ANN201", "ARG001"] "src/alembic/versions/**.py" = ["CPY001"]