From 4919770105edf2a0e8195dc272ce35109af918f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janusz=20=22Ivellios=22=20Kamie=C5=84ski?= Date: Tue, 17 Dec 2024 16:01:33 +0100 Subject: [PATCH 1/9] =?UTF-8?q?Dodane=20automatyczne=20tworzenie=20lub=20a?= =?UTF-8?q?ktualizacja=20branchy/commit=C3=B3w/Pull=20Request=C3=B3w=20dla?= =?UTF-8?q?=20zg=C5=82osze=C5=84=20nowej=20organizacji;=20formatowanie=20p?= =?UTF-8?q?lik=C3=B3w=20blackiem;=20zmiana=20ko=C5=84c=C3=B3w=20linii=20CR?= =?UTF-8?q?LF=20na=20LF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/config.yml | 16 +-- .github/ISSUE_TEMPLATE/dodanieproduktu.yaml | 76 ++++++------ .github/scripts/cli.py | 78 ++++++++++++ .github/scripts/consts.py | 7 +- .github/scripts/git_managers.py | 129 ++++++++++++++++++++ .github/scripts/labels.py | 11 +- .github/scripts/organization.yaml.j2 | 18 +++ .github/scripts/parsers.py | 6 +- .github/scripts/process.py | 64 ---------- .github/scripts/pullers.py | 24 ++-- .github/scripts/renderers.py | 28 +++++ .github/scripts/requirements.txt | 2 + .github/scripts/validators.py | 59 +++++---- .github/workflows/nowa-organizacja.yaml | 102 ++++++++-------- 14 files changed, 423 insertions(+), 197 deletions(-) create mode 100644 .github/scripts/cli.py create mode 100644 .github/scripts/git_managers.py create mode 100644 .github/scripts/organization.yaml.j2 delete mode 100644 .github/scripts/process.py create mode 100644 .github/scripts/renderers.py diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 4bff899..e7b601c 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ -blank_issues_enabled: true -contact_links: - - name: Usuwanie produktu - url: https://github.com/wyslijco/wyslijco.github.io/wiki - about: Zobacz, jak możesz usunąć produkt ze strony swojej organizacji w Wyślij.co - - name: Usuwanie organizacji - url: https://github.com/wyslijco/wyslijco.github.io/wiki - about: Zobacz, jak usunąć swoją organizację z Wyślij.co +blank_issues_enabled: true +contact_links: + - name: Usuwanie produktu + url: https://github.com/wyslijco/wyslijco.github.io/wiki + about: Zobacz, jak możesz usunąć produkt ze strony swojej organizacji w Wyślij.co + - name: Usuwanie organizacji + url: https://github.com/wyslijco/wyslijco.github.io/wiki + about: Zobacz, jak usunąć swoją organizację z Wyślij.co diff --git a/.github/ISSUE_TEMPLATE/dodanieproduktu.yaml b/.github/ISSUE_TEMPLATE/dodanieproduktu.yaml index c4bd976..b1d7f71 100644 --- a/.github/ISSUE_TEMPLATE/dodanieproduktu.yaml +++ b/.github/ISSUE_TEMPLATE/dodanieproduktu.yaml @@ -1,38 +1,38 @@ -name: Dodanie produktu -description: Dodaj produkt do strony swojej organizacji. -title: "[Dodanie produktu]" -labels: ["organizacje", "nowy-produkt"] -assignees: - - ivellios - -body: - - type: markdown - attributes: - value: | - Ten formularz pomoże Ci w dodaniu produktu do strony Twojej organizacji w Wyślij.co - - type: dropdown - id: nazwa_organizacji - attributes: - label: Twoja organizacja - options: - - Testowa - validations: - required: true - - type: input - id: nazwa - attributes: - label: Nazwa produktu - validations: - required: true - - type: input - id: opis - attributes: - label: Krótki opis produktu - validations: - required: false - - type: input - id: link - attributes: - label: Link do produktu w sklepie internetowym - validations: - required: true +name: Dodanie produktu +description: Dodaj produkt do strony swojej organizacji. +title: "[Dodanie produktu]" +labels: ["organizacje", "nowy-produkt"] +assignees: + - ivellios + +body: + - type: markdown + attributes: + value: | + Ten formularz pomoże Ci w dodaniu produktu do strony Twojej organizacji w Wyślij.co + - type: dropdown + id: nazwa_organizacji + attributes: + label: Twoja organizacja + options: + - Testowa + validations: + required: true + - type: input + id: nazwa + attributes: + label: Nazwa produktu + validations: + required: true + - type: input + id: opis + attributes: + label: Krótki opis produktu + validations: + required: false + - type: input + id: link + attributes: + label: Link do produktu w sklepie internetowym + validations: + required: true diff --git a/.github/scripts/cli.py b/.github/scripts/cli.py new file mode 100644 index 0000000..5b4d12a --- /dev/null +++ b/.github/scripts/cli.py @@ -0,0 +1,78 @@ +import json +import logging +import os + +from github import Auth, Github, Issue + +from consts import OrgFormSchemaIds, NEW_ORG_ISSUE_DEFAULT_TITLE, NEW_ORG_FORM_SCHEMA_FILENAME +from git_managers import create_organization_yaml_pr +from labels import Label +from parsers import GithubIssueFormDataParser +from pullers import KRSDataPuller +from utils import has_label +from validators import OrgValidator +from renderers import render_organization_yaml + +logging.basicConfig( + level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__file__) + +GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") +GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY") + +auth = Auth.Token(GITHUB_TOKEN) +g = Github(auth=auth) +repo = g.get_repo(GITHUB_REPOSITORY) + + +def process_new_org_issue(issue: Issue, data: GithubIssueFormDataParser): + if has_label(issue, Label.AUTO_VERIFIED): + issue.remove_from_labels(Label.AUTO_VERIFIED) + + validator = OrgValidator(data, issue) + if not validator.validate(): + logger.error("Validation failed") + return + + if not (org := KRSDataPuller.get_org_by_krs(issue, data.get(OrgFormSchemaIds.krs))): + logger.error("KRS db validation failed") + return + + # Update issue title + if issue.title == NEW_ORG_ISSUE_DEFAULT_TITLE: + logger.info("Updating issue title") + issue.edit( + title=f"{NEW_ORG_ISSUE_DEFAULT_TITLE} {org.name or data.get(OrgFormSchemaIds.name)}" + ) + + logger.info("Adding auto-verified label") + if not has_label(issue, Label.WAITING): + issue.add_to_labels(Label.WAITING) + issue.create_comment( + f"@{issue.user.login}, dziękujemy za podanie informacji. " + f"Przyjęliśmy zgłoszenie dodania nowej organizacji. " + f"Wkrótce skontaktujemy się celem weryfikacji zgłoszenia." + ) + + # mark as verified + issue.add_to_labels(Label.AUTO_VERIFIED) + + # create organization yaml file and add to the Pull Request + yaml_string = render_organization_yaml(data) + create_organization_yaml_pr(issue, yaml_string, data) + + +def main(): + github_form_json = os.getenv("GITHUB_FORM_JSON") + github_issue_number = int(os.getenv("GITHUB_ISSUE_NUMBER")) + + issue = repo.get_issue(github_issue_number) + data = GithubIssueFormDataParser( + json.loads(github_form_json), NEW_ORG_FORM_SCHEMA_FILENAME + ) + process_new_org_issue(issue, data) + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/consts.py b/.github/scripts/consts.py index b95d075..b58569e 100644 --- a/.github/scripts/consts.py +++ b/.github/scripts/consts.py @@ -1,9 +1,9 @@ import enum -class OrgSchemaIds(enum.StrEnum): +class OrgFormSchemaIds(enum.StrEnum): name = "nazwa" - www = "www" + website = "www" krs = "krs" slug = "nazwa_strony" street = "ulica" @@ -11,8 +11,9 @@ class OrgSchemaIds(enum.StrEnum): city = "miasto" phone_number = "telefon" + NEW_ORG_ISSUE_DEFAULT_TITLE = "[Nowa Organizacja]" -NEW_ORG_SCHEMA_FILENAME = "nowa.yaml" +NEW_ORG_FORM_SCHEMA_FILENAME = "nowa.yaml" ORG_SCHEMA_SLUG_FIELD = "adres" diff --git a/.github/scripts/git_managers.py b/.github/scripts/git_managers.py new file mode 100644 index 0000000..c080c33 --- /dev/null +++ b/.github/scripts/git_managers.py @@ -0,0 +1,129 @@ +import logging + +from github import GithubException, InputGitTreeElement +from github.GitCommit import GitCommit +from github.GitRef import GitRef +from github.Issue import Issue +from github.PullRequest import PullRequest +from github.Repository import Repository + +from consts import OrgFormSchemaIds +from parsers import GithubIssueFormDataParser + + +logger = logging.getLogger(__file__) + + +class GitManager: + """Manager for creating a new branch and pull request with a file commit in the repo.""" + + def __init__(self, repo: Repository): + self.repo = repo + + def commit_file_contents_to_branch( + self, branch_ref: GitRef, file_path: str, contents: str, commit_message: str + ) -> GitCommit: + latest_commit = self.repo.get_commit(branch_ref.object.sha) + blob = self.repo.create_git_blob(contents, "utf-8") + tree_element = InputGitTreeElement( + path=file_path, mode="100644", type="blob", sha=blob.sha + ) + base_tree = latest_commit.commit.tree + new_tree = self.repo.create_git_tree([tree_element], base_tree) + new_commit = self.repo.create_git_commit( + message=commit_message, tree=new_tree, parents=[latest_commit.commit] + ) + return new_commit + + def get_or_create_branch(self, source_branch: str, new_branch_name: str) -> GitRef: + try: + branch_ref = self.repo.get_git_ref(f"heads/{new_branch_name}") + print(f"Branch '{new_branch_name}' already exists.") + except GithubException as e: + if e.status == 404: + # Branch does not exist, create it from the source branch + source = self.repo.get_branch(source_branch) + self.repo.create_git_ref( + ref=f"refs/heads/{new_branch_name}", sha=source.commit.sha + ) + branch_ref = self.repo.get_git_ref(f"heads/{new_branch_name}") + print(f"Branch '{new_branch_name}' created from '{source_branch}'.") + else: + raise e + return branch_ref + + def get_or_create_pr( + self, target_branch: str, new_branch_name: str, pr_title: str, pr_body: str + ) -> PullRequest: + pulls = self.repo.get_pulls( + state="open", + head=f"{self.repo.owner.login}:{new_branch_name}", + base=target_branch, + ) + if pulls.totalCount > 0: + logger.warning( + f"Pull request already exists for branch '{new_branch_name}': {pulls[0].html_url}" + ) + return pulls[0] + else: + logger.info(f"Creating pull request for branch '{new_branch_name}'") + return self.repo.create_pull( + title=pr_title, body=pr_body, head=new_branch_name, base=target_branch + ) + + def create_or_update_remote_branch_with_file_commit( + self, + source_branch: str, + new_branch: str, + file_path: str, + file_contents: str, + commit_message: str, + ) -> GitRef: + """Create or update a remote branch with a file commit.""" + branch = self.get_or_create_branch(source_branch, new_branch) + commit = self.commit_file_contents_to_branch( + branch, file_path, file_contents, commit_message + ) + branch.edit(commit.sha) + return branch + + def create_or_update_pr_with_file( + self, + source_branch: str, + new_branch: str, + pr_title: str, + pr_body: str, + file_path: str, + file_contents: str, + commit_message: str, + ) -> PullRequest: + self.create_or_update_remote_branch_with_file_commit( + source_branch, new_branch, file_path, file_contents, commit_message + ) + return self.get_or_create_pr(source_branch, new_branch, pr_title, pr_body) + + +def create_organization_yaml_pr( + issue: Issue, yaml_string: str, data: GithubIssueFormDataParser +): + repo = issue.repository + + source_branch = "main" + new_branch_name = f"nowa-organizacja-zgloszenie-{issue.number}" + + commit_message = f"Dodana nowa organizacja: {data.get(OrgFormSchemaIds.name)} | Zgłoszenie: #{issue.number}" + pr_title = f"Dodana nowa organizacja: {data.get(OrgFormSchemaIds.name)} | Zgłoszenie: #{issue.number}" + pr_body = f"Automatycznie dodana nowa organizacja na podstawie zgłoszenia z issue #{issue.number}.\n\n Closes #{issue.number}" + + file_path = "organizations/organization.yaml" + + manager = GitManager(repo) + manager.create_or_update_pr_with_file( + source_branch=source_branch, + new_branch=new_branch_name, + pr_title=pr_title, + pr_body=pr_body, + file_path=file_path, + file_contents=yaml_string, + commit_message=commit_message, + ) diff --git a/.github/scripts/labels.py b/.github/scripts/labels.py index cff9c7d..eb8e5bd 100644 --- a/.github/scripts/labels.py +++ b/.github/scripts/labels.py @@ -1,6 +1,6 @@ import enum -from consts import OrgSchemaIds +from consts import OrgFormSchemaIds class Label(enum.StrEnum): @@ -9,11 +9,12 @@ class Label(enum.StrEnum): INVALID_PHONE = "niepoprawny numer telefonu" INVALID_SLUG = "niepoprawna nazwa strony" AUTO_VERIFIED = "zweryfikowana automatycznie" + WAITING = "oczekuje na akceptację" INVALID_FIELD_TO_LABEL = { - OrgSchemaIds.krs: Label.INVALID_KRS, - OrgSchemaIds.postal_code: Label.INVALID_POSTAL_CODE, - OrgSchemaIds.phone_number: Label.INVALID_PHONE, - OrgSchemaIds.slug: Label.INVALID_SLUG, + OrgFormSchemaIds.krs: Label.INVALID_KRS, + OrgFormSchemaIds.postal_code: Label.INVALID_POSTAL_CODE, + OrgFormSchemaIds.phone_number: Label.INVALID_PHONE, + OrgFormSchemaIds.slug: Label.INVALID_SLUG, } diff --git a/.github/scripts/organization.yaml.j2 b/.github/scripts/organization.yaml.j2 new file mode 100644 index 0000000..6fd42f5 --- /dev/null +++ b/.github/scripts/organization.yaml.j2 @@ -0,0 +1,18 @@ +nazwa: {{ organization.name }} + +# wyslij.co/testowej - adres, pod którym będzie dostępna strona +adres: {{ organization.slug }} + +strona: {{ organization.website }} +krs: {{ organization.krs }} + +dostawa: + ulica: {{ organization.street }} + kod: {{ organization.postal_code }} + miasto: {{ organization.city }} + telefon: {{ organization.phone_number }} + +produkty: + - nazwa: Testowy produkt + link: http://example.com + diff --git a/.github/scripts/parsers.py b/.github/scripts/parsers.py index 1d021a7..1bd142d 100644 --- a/.github/scripts/parsers.py +++ b/.github/scripts/parsers.py @@ -3,7 +3,11 @@ import yaml -class FormDataParser: +class GithubIssueFormDataParser: + """ + Parses data comming from a Github issue form converted + to a dictionary and provides methods to access the data + """ def __init__(self, form_data: dict[str, Any], form_schema_filename: str): self.form_data = form_data diff --git a/.github/scripts/process.py b/.github/scripts/process.py deleted file mode 100644 index 8410448..0000000 --- a/.github/scripts/process.py +++ /dev/null @@ -1,64 +0,0 @@ -import json -import logging -import os - -from github import Auth, Github, Issue - -from consts import OrgSchemaIds, NEW_ORG_ISSUE_DEFAULT_TITLE, NEW_ORG_SCHEMA_FILENAME -from labels import Label -from parsers import FormDataParser -from pullers import OrgDataPuller -from utils import has_label -from validators import OrgValidator - -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -logger = logging.getLogger(__file__) - -GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") -GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY") - -auth = Auth.Token(GITHUB_TOKEN) -g = Github(auth=auth) -repo = g.get_repo(GITHUB_REPOSITORY) - - -def process_new_org_issue(issue: Issue, data: FormDataParser): - if has_label(issue, Label.AUTO_VERIFIED): - issue.remove_from_labels(Label.AUTO_VERIFIED) - - validator = OrgValidator(data, issue) - if not validator.validate(): - logger.error("Validation failed") - return - - if not (org := OrgDataPuller.get_org_by_krs(issue, data.get(OrgSchemaIds.krs))): - logger.error("KRS db validation failed") - return - - # Update issue title - if issue.title == NEW_ORG_ISSUE_DEFAULT_TITLE: - logger.info("Updating issue title") - issue.edit(title=f"{NEW_ORG_ISSUE_DEFAULT_TITLE} {org.name or data.get(OrgSchemaIds.name)}") - - if not has_label(issue, Label.AUTO_VERIFIED): - logger.info("Adding auto-verified label") - issue.create_comment(f"@{issue.user.login}, dziękujemy za podanie informacji. " - f"Przyjęliśmy zgłoszenie dodania nowej organizacji. " - f"Wkrótce skontaktujemy się celem weryfikacji zgłoszenia.") - issue.add_to_labels(Label.AUTO_VERIFIED) - - -def main(): - github_form_json = os.getenv("GITHUB_FORM_JSON") - github_issue_number = int(os.getenv("GITHUB_ISSUE_NUMBER")) - - issue = repo.get_issue(github_issue_number) - data = FormDataParser( - json.loads(github_form_json), - NEW_ORG_SCHEMA_FILENAME - ) - process_new_org_issue(issue, data) - - -if __name__ == "__main__": - main() diff --git a/.github/scripts/pullers.py b/.github/scripts/pullers.py index 1b31313..d9f7fea 100644 --- a/.github/scripts/pullers.py +++ b/.github/scripts/pullers.py @@ -8,14 +8,16 @@ from labels import Label -class OrgDataPuller: +class KRSDataPuller: def __init__(self, krs: str): self.krs = krs self.data = self.pull_data() def pull_data(self) -> dict | None: - response = requests.get(f"https://api-krs.ms.gov.pl/api/krs/OdpisAktualny/{self.krs}?rejestr=S&format=json") + response = requests.get( + f"https://api-krs.ms.gov.pl/api/krs/OdpisAktualny/{self.krs}?rejestr=S&format=json" + ) if response.status_code == 200: return response.json() else: @@ -23,17 +25,25 @@ def pull_data(self) -> dict | None: @property def name(self): - return self.data.get("odpis", {}).get("dane", {}).get("dzial1", {}).get("danePodmiotu", {}).get("nazwa") + return ( + self.data.get("odpis", {}) + .get("dane", {}) + .get("dzial1", {}) + .get("danePodmiotu", {}) + .get("nazwa") + ) @staticmethod - def get_org_by_krs(issue: Issue, krs: str) -> Optional["OrgDataPuller"]: + def get_org_by_krs(issue: Issue, krs: str) -> Optional["KRSDataPuller"]: # Downloading official org data try: - org = OrgDataPuller(krs) + org = KRSDataPuller(krs) except requests.HTTPError as e: - issue.create_comment(f"Nie udało się pobrać danych o organizacji z KRS. " - f"Proszę sprawdzić, czy podany numer jest poprawny") + issue.create_comment( + f"Nie udało się pobrać danych o organizacji z KRS. " + f"Proszę sprawdzić, czy podany numer jest poprawny" + ) issue.add_to_labels(Label.INVALID_KRS) return diff --git a/.github/scripts/renderers.py b/.github/scripts/renderers.py new file mode 100644 index 0000000..2022209 --- /dev/null +++ b/.github/scripts/renderers.py @@ -0,0 +1,28 @@ +import os + +from jinja2 import FileSystemLoader, Environment + +from consts import OrgFormSchemaIds +from parsers import GithubIssueFormDataParser + + +def render_organization_yaml(data: GithubIssueFormDataParser): + template_dir = os.path.dirname(os.path.abspath(__file__)) + env = Environment(loader=FileSystemLoader(template_dir)) + + template = env.get_template("organization.yaml.j2") + + fields = ( + "name", + "slug", + "website", + "krs", + "street", + "postal_code", + "city", + "phone_number", + ) + + org_data = {field: data.get(getattr(OrgFormSchemaIds, field)) for field in fields} + + return template.render(organization=org_data) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index b1e69e0..2fe44d1 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -1,3 +1,5 @@ PyGithub==2.5.0 PyYAML==6.0.2 requests==2.32.3 +GitPython==3.1.43 +black==24.10.0 diff --git a/.github/scripts/validators.py b/.github/scripts/validators.py index f03f1e0..9dd958a 100644 --- a/.github/scripts/validators.py +++ b/.github/scripts/validators.py @@ -4,46 +4,56 @@ import yaml from github import Issue -from consts import OrgSchemaIds, ORG_SCHEMA_SLUG_FIELD +from consts import OrgFormSchemaIds, ORG_SCHEMA_SLUG_FIELD from labels import INVALID_FIELD_TO_LABEL -from parsers import FormDataParser +from parsers import GithubIssueFormDataParser from utils import has_label class OrgValidator: - def __init__(self, data: FormDataParser, issue: Issue): + def __init__(self, data: GithubIssueFormDataParser, issue: Issue): self.data = data self.issue = issue def validate_krs(self) -> tuple[bool, str]: - return bool(re.fullmatch(r"\d{10}", self.data.get(OrgSchemaIds.krs))), "niepoprawny numer KRS" + return ( + bool(re.fullmatch(r"\d{10}", self.data.get(OrgFormSchemaIds.krs))), + "niepoprawny numer KRS", + ) def validate_postal_code(self) -> tuple[bool, str]: - return bool( - re.fullmatch(r"\d{2}-\d{3}", self.data.get(OrgSchemaIds.postal_code)) - ), "niepoprawny kod pocztowy" + return ( + bool(re.fullmatch(r"\d{2}-\d{3}", self.data.get(OrgFormSchemaIds.postal_code))), + "niepoprawny kod pocztowy", + ) def validate_phone_number(self) -> tuple[bool, str]: - return bool( - re.fullmatch( - r"(\+?48|0048)?\d{9}", - re.sub(r"[\s-]", "", self.data.get(OrgSchemaIds.phone_number)) - ) - ), "niepoprawny numer telefonu" + return ( + bool( + re.fullmatch( + r"(\+?48|0048)?\d{9}", + re.sub(r"[\s-]", "", self.data.get(OrgFormSchemaIds.phone_number)), + ) + ), + "niepoprawny numer telefonu", + ) def validate_slug(self) -> tuple[bool, str]: """ Checks if any organization with the same slug already exists. """ - slug_value = self.data.get(OrgSchemaIds.slug) + slug_value = self.data.get(OrgFormSchemaIds.slug) for root, _, files in os.walk("../../organizations"): for file_name in files: with open(os.path.join(root, file_name), "r") as f: org_data = yaml.safe_load(f) if org_data[ORG_SCHEMA_SLUG_FIELD] == slug_value: - return False, f"organizacja z adresem `/{slug_value}` już istnieje w `wyślij.co`. Proszę zmienić wartość na inną." + return ( + False, + f"organizacja z adresem `/{slug_value}` już istnieje w `wyślij.co`. Proszę zmienić wartość na inną.", + ) return True, "" @@ -56,10 +66,10 @@ def validate(self) -> bool: """ validation_map = { - OrgSchemaIds.krs: self.validate_krs, - OrgSchemaIds.postal_code: self.validate_postal_code, - OrgSchemaIds.phone_number: self.validate_phone_number, - OrgSchemaIds.slug: self.validate_slug, + OrgFormSchemaIds.krs: self.validate_krs, + OrgFormSchemaIds.postal_code: self.validate_postal_code, + OrgFormSchemaIds.phone_number: self.validate_phone_number, + OrgFormSchemaIds.slug: self.validate_slug, } errors = [] @@ -71,10 +81,17 @@ def validate(self) -> bool: self.issue.remove_from_labels(label) else: self.issue.add_to_labels(label) - errors.append((field, msg,)) + errors.append( + ( + field, + msg, + ) + ) if errors: - msg = "Wprowadzone dane są nieprawidłowe. Prosimy o wprowadzenie poprawek:\n" + msg = ( + "Wprowadzone dane są nieprawidłowe. Prosimy o wprowadzenie poprawek:\n" + ) for field, error in errors: msg += f"- **{self.data.get_label(field)}**: {error}\n" self.issue.create_comment(msg) diff --git a/.github/workflows/nowa-organizacja.yaml b/.github/workflows/nowa-organizacja.yaml index 6f7807d..e43820b 100644 --- a/.github/workflows/nowa-organizacja.yaml +++ b/.github/workflows/nowa-organizacja.yaml @@ -1,50 +1,52 @@ -name: Przetwarzenie zgłoszenia organizacji - -on: - issues: - types: [opened, edited] - - -env: - GITHUB_TOKEN: ${{ github.token }} - -permissions: - contents: read - issues: write - -jobs: - - validate-form: - if: contains(github.event.issue.labels.*.name, 'nowa-organizacja') - name: Ekstrakcja danych z formularza - runs-on: ubuntu-latest - outputs: - payload: ${{ steps.parse.outputs.payload }} - steps: - - name: Parsowanie treści zgłoszenia - id: parse - uses: onmax/issue-form-parser@v1.5 - with: - issue_number: ${{ github.event.issue.number }} - - processing: - if: contains(github.event.issue.labels.*.name, 'nowa-organizacja') - needs: [validate-form] - name: Przetwarzenie zgłoszenia - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'pip' # caching pip dependencies - - name: Install dependencies - working-directory: ./.github/scripts - run: pip install -r requirements.txt - - name: Run script - env: - GITHUB_ISSUE_NUMBER: ${{ github.event.issue.number }} - GITHUB_FORM_JSON: ${{ needs.validate-form.outputs.payload }} - GITHUB_REPOSITORY: ${{ github.repository }} - working-directory: ./.github/scripts - run: python process.py +name: Przetwarzenie zgłoszenia organizacji + +on: + issues: + types: [opened, edited] + + +env: + GITHUB_TOKEN: ${{ github.token }} + +permissions: + contents: read + issues: write + pull-requests: write + +jobs: + + validate-form: + if: contains(github.event.issue.labels.*.name, 'nowa-organizacja') + name: Ekstrakcja danych z formularza + runs-on: ubuntu-latest + outputs: + payload: ${{ steps.parse.outputs.payload }} + steps: + - name: Parsowanie treści zgłoszenia + id: parse + uses: onmax/issue-form-parser@v1.5 + with: + issue_number: ${{ github.event.issue.number }} + + processing: + if: contains(github.event.issue.labels.*.name, 'nowa-organizacja') + needs: [validate-form] + name: Przetwarzenie zgłoszenia + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + cache: 'pip' # caching pip dependencies + - name: Install dependencies + working-directory: ./.github/scripts + run: pip install -r requirements.txt + - name: Run script + env: + GITHUB_ISSUE_NUMBER: ${{ github.event.issue.number }} + GITHUB_FORM_JSON: ${{ needs.validate-form.outputs.payload }} + GITHUB_REPOSITORY: ${{ github.repository }} + working-directory: ./.github/scripts + run: python cli.py + From ca58cd0f2c72f954ab63afc77bda9c3bb48ce9b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janusz=20=22Ivellios=22=20Kamie=C5=84ski?= Date: Tue, 17 Dec 2024 16:39:12 +0100 Subject: [PATCH 2/9] =?UTF-8?q?dodanie=20uprawnie=C5=84=20dla=20akcji=20pr?= =?UTF-8?q?zypisania=20do=20issue/PR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/auto-assign.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/auto-assign.yaml b/.github/workflows/auto-assign.yaml index e949f3d..ad91d51 100644 --- a/.github/workflows/auto-assign.yaml +++ b/.github/workflows/auto-assign.yaml @@ -4,6 +4,11 @@ on: types: [opened] pull_request: types: [opened] + +permissions: + issues: write + pull-requests: write + jobs: run: runs-on: ubuntu-latest From 558fd9c5e315f06759c829e2d435beab20727eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janusz=20=22Ivellios=22=20Kamie=C5=84ski?= Date: Tue, 17 Dec 2024 18:17:41 +0100 Subject: [PATCH 3/9] =?UTF-8?q?dodanie=20obs=C5=82ugi=20przerwy=20technicz?= =?UTF-8?q?nej=20w=20serwisie=20rz=C4=85dowym=20z=20danymi=20KRS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/scripts/cli.py | 11 ++++++++++- .github/scripts/exceptions.py | 8 ++++++++ .github/scripts/git_managers.py | 6 +++++- .github/scripts/pullers.py | 19 ++++++++++++++++++- 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 .github/scripts/exceptions.py diff --git a/.github/scripts/cli.py b/.github/scripts/cli.py index 5b4d12a..afcf3d3 100644 --- a/.github/scripts/cli.py +++ b/.github/scripts/cli.py @@ -5,6 +5,7 @@ from github import Auth, Github, Issue from consts import OrgFormSchemaIds, NEW_ORG_ISSUE_DEFAULT_TITLE, NEW_ORG_FORM_SCHEMA_FILENAME +from exceptions import BranchModifiedError from git_managers import create_organization_yaml_pr from labels import Label from parsers import GithubIssueFormDataParser @@ -60,7 +61,15 @@ def process_new_org_issue(issue: Issue, data: GithubIssueFormDataParser): # create organization yaml file and add to the Pull Request yaml_string = render_organization_yaml(data) - create_organization_yaml_pr(issue, yaml_string, data) + + try: + create_organization_yaml_pr(issue, yaml_string, data) + except BranchModifiedError: + logger.error("Branch was modified by someone else") + issue.create_comment( + f"Aktualizacja pliku organizacji na podstawie opisu zgłoszenia niemożliwa. " + f"Plik organizacji został już zmodyfikowany przez innego użytkownika." + ) def main(): diff --git a/.github/scripts/exceptions.py b/.github/scripts/exceptions.py new file mode 100644 index 0000000..3b6cff8 --- /dev/null +++ b/.github/scripts/exceptions.py @@ -0,0 +1,8 @@ + + +class BranchModifiedError(ValueError): + pass + + +class KRSMaintenanceError(Exception): + pass diff --git a/.github/scripts/git_managers.py b/.github/scripts/git_managers.py index c080c33..4465282 100644 --- a/.github/scripts/git_managers.py +++ b/.github/scripts/git_managers.py @@ -1,4 +1,5 @@ import logging +from multiprocessing.managers import Value from github import GithubException, InputGitTreeElement from github.GitCommit import GitCommit @@ -8,6 +9,7 @@ from github.Repository import Repository from consts import OrgFormSchemaIds +from exceptions import BranchModifiedError from parsers import GithubIssueFormDataParser @@ -24,6 +26,8 @@ def commit_file_contents_to_branch( self, branch_ref: GitRef, file_path: str, contents: str, commit_message: str ) -> GitCommit: latest_commit = self.repo.get_commit(branch_ref.object.sha) + if not latest_commit.commit.message.startswith("[automat] "): + raise BranchModifiedError() blob = self.repo.create_git_blob(contents, "utf-8") tree_element = InputGitTreeElement( path=file_path, mode="100644", type="blob", sha=blob.sha @@ -112,7 +116,7 @@ def create_organization_yaml_pr( new_branch_name = f"nowa-organizacja-zgloszenie-{issue.number}" commit_message = f"Dodana nowa organizacja: {data.get(OrgFormSchemaIds.name)} | Zgłoszenie: #{issue.number}" - pr_title = f"Dodana nowa organizacja: {data.get(OrgFormSchemaIds.name)} | Zgłoszenie: #{issue.number}" + pr_title = f"[automat] Dodana nowa organizacja: {data.get(OrgFormSchemaIds.name)} | Zgłoszenie: #{issue.number}" pr_body = f"Automatycznie dodana nowa organizacja na podstawie zgłoszenia z issue #{issue.number}.\n\n Closes #{issue.number}" file_path = "organizations/organization.yaml" diff --git a/.github/scripts/pullers.py b/.github/scripts/pullers.py index d9f7fea..2022b9b 100644 --- a/.github/scripts/pullers.py +++ b/.github/scripts/pullers.py @@ -1,9 +1,12 @@ import re +from json import JSONDecoder from typing import Optional import requests from github import Issue +from requests import JSONDecodeError +from exceptions import KRSMaintenanceError from utils import has_label from labels import Label @@ -19,7 +22,15 @@ def pull_data(self) -> dict | None: f"https://api-krs.ms.gov.pl/api/krs/OdpisAktualny/{self.krs}?rejestr=S&format=json" ) if response.status_code == 200: - return response.json() + try: + return response.json() + except JSONDecodeError: + if "Przerwa techniczna" in response.text: + raise KRSMaintenanceError( + f"Przerwa techniczna serwisu weryfikującego KRS. " + f"Proszę [zweryfikować KRS ręcznie]" + f"(https://api-krs.ms.gov.pl/api/krs/OdpisAktualny/{self.krs}?rejestr=S&format=json)." + ) else: raise requests.HTTPError(f"Failed to fetch data for KRS {self.krs}") @@ -39,6 +50,12 @@ def get_org_by_krs(issue: Issue, krs: str) -> Optional["KRSDataPuller"]: # Downloading official org data try: org = KRSDataPuller(krs) + except KRSMaintenanceError as e: + issue.create_comment( + str(e) + ) + issue.add_to_labels(Label.INVALID_KRS) + return except requests.HTTPError as e: issue.create_comment( f"Nie udało się pobrać danych o organizacji z KRS. " From 746ba2b84f4ab4717f80735220191ce6ca92555c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janusz=20=22Ivellios=22=20Kamie=C5=84ski?= Date: Tue, 17 Dec 2024 18:21:56 +0100 Subject: [PATCH 4/9] =?UTF-8?q?usuni=C4=99cie=20zb=C4=99dnych=20import?= =?UTF-8?q?=C3=B3w;=20reformatowanie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/scripts/cli.py | 6 +++++- .github/scripts/exceptions.py | 2 -- .github/scripts/pullers.py | 8 ++------ .github/scripts/validators.py | 6 +++++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/scripts/cli.py b/.github/scripts/cli.py index afcf3d3..abbb21a 100644 --- a/.github/scripts/cli.py +++ b/.github/scripts/cli.py @@ -4,7 +4,11 @@ from github import Auth, Github, Issue -from consts import OrgFormSchemaIds, NEW_ORG_ISSUE_DEFAULT_TITLE, NEW_ORG_FORM_SCHEMA_FILENAME +from consts import ( + OrgFormSchemaIds, + NEW_ORG_ISSUE_DEFAULT_TITLE, + NEW_ORG_FORM_SCHEMA_FILENAME, +) from exceptions import BranchModifiedError from git_managers import create_organization_yaml_pr from labels import Label diff --git a/.github/scripts/exceptions.py b/.github/scripts/exceptions.py index 3b6cff8..13b814f 100644 --- a/.github/scripts/exceptions.py +++ b/.github/scripts/exceptions.py @@ -1,5 +1,3 @@ - - class BranchModifiedError(ValueError): pass diff --git a/.github/scripts/pullers.py b/.github/scripts/pullers.py index 2022b9b..6bf1fe0 100644 --- a/.github/scripts/pullers.py +++ b/.github/scripts/pullers.py @@ -1,5 +1,3 @@ -import re -from json import JSONDecoder from typing import Optional import requests @@ -7,8 +5,8 @@ from requests import JSONDecodeError from exceptions import KRSMaintenanceError -from utils import has_label from labels import Label +from utils import has_label class KRSDataPuller: @@ -51,9 +49,7 @@ def get_org_by_krs(issue: Issue, krs: str) -> Optional["KRSDataPuller"]: try: org = KRSDataPuller(krs) except KRSMaintenanceError as e: - issue.create_comment( - str(e) - ) + issue.create_comment(str(e)) issue.add_to_labels(Label.INVALID_KRS) return except requests.HTTPError as e: diff --git a/.github/scripts/validators.py b/.github/scripts/validators.py index 9dd958a..7f73206 100644 --- a/.github/scripts/validators.py +++ b/.github/scripts/validators.py @@ -24,7 +24,11 @@ def validate_krs(self) -> tuple[bool, str]: def validate_postal_code(self) -> tuple[bool, str]: return ( - bool(re.fullmatch(r"\d{2}-\d{3}", self.data.get(OrgFormSchemaIds.postal_code))), + bool( + re.fullmatch( + r"\d{2}-\d{3}", self.data.get(OrgFormSchemaIds.postal_code) + ) + ), "niepoprawny kod pocztowy", ) From 8267996010aaa7e7a2d40ae859eeebfa9e5eda1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janusz=20=22Ivellios=22=20Kamie=C5=84ski?= Date: Tue, 17 Dec 2024 20:12:53 +0100 Subject: [PATCH 5/9] =?UTF-8?q?poprawka:=20nowy=20branch=20nie=20powoduje?= =?UTF-8?q?=20ju=C5=BC=20wykrycia=20r=C4=99cznej=20modyfikacji;=20commity?= =?UTF-8?q?=20od=20bota=20otrzymuj=C4=85=20prefix=20[auto];=20weryfikacja?= =?UTF-8?q?=20KRS=20w=20bazie=20nie=20blokuje=20procesu=20utworzenia=20PR?= =?UTF-8?q?=20z=20plikiem=20yaml=20organizacji;=20dodana=20informacja=20o?= =?UTF-8?q?=20dalszych=20krokach=20do=20formularza=20zg=C5=82oszeniowego.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/nowa.yaml | 9 +++++++++ .github/scripts/cli.py | 32 +++++++++++++++++++------------- .github/scripts/git_managers.py | 19 ++++++++++++++----- .github/scripts/pullers.py | 1 - 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/nowa.yaml b/.github/ISSUE_TEMPLATE/nowa.yaml index c2f7a23..518fb4a 100644 --- a/.github/ISSUE_TEMPLATE/nowa.yaml +++ b/.github/ISSUE_TEMPLATE/nowa.yaml @@ -103,3 +103,12 @@ body: placeholder: 000 000 000 validations: required: true + - type: markdown + attributes: + value: | + # Następne kroki + Po wypełnieniu formularza, Twój wniosek zostanie zweryfikowany przez nasz zespół. + W celu weryfikacji poprawności danych skontaktujemy się z Twoją organizacją poprzez + oficjalne dane kontaktowe dostępne na stronie internetowej organizacji lub w rejestrze KRS. + W przypadku pozytywnej weryfikacji, otrzymasz od nas informację o dalszych krokach w komentarzu + do tego zgłoszenia. diff --git a/.github/scripts/cli.py b/.github/scripts/cli.py index abbb21a..ac5ec5b 100644 --- a/.github/scripts/cli.py +++ b/.github/scripts/cli.py @@ -32,36 +32,42 @@ def process_new_org_issue(issue: Issue, data: GithubIssueFormDataParser): + validation_warnings = [] + + org_name = data.get(OrgFormSchemaIds.name) + if has_label(issue, Label.AUTO_VERIFIED): issue.remove_from_labels(Label.AUTO_VERIFIED) validator = OrgValidator(data, issue) if not validator.validate(): - logger.error("Validation failed") + logger.error("Validation failed - not continuing") return if not (org := KRSDataPuller.get_org_by_krs(issue, data.get(OrgFormSchemaIds.krs))): logger.error("KRS db validation failed") - return + validation_warnings.append("Nie można zweryfikować KRS") + else: + org_name = org.name # Update issue title if issue.title == NEW_ORG_ISSUE_DEFAULT_TITLE: logger.info("Updating issue title") issue.edit( - title=f"{NEW_ORG_ISSUE_DEFAULT_TITLE} {org.name or data.get(OrgFormSchemaIds.name)}" + title=f"{NEW_ORG_ISSUE_DEFAULT_TITLE} {org_name}" ) logger.info("Adding auto-verified label") - if not has_label(issue, Label.WAITING): - issue.add_to_labels(Label.WAITING) - issue.create_comment( - f"@{issue.user.login}, dziękujemy za podanie informacji. " - f"Przyjęliśmy zgłoszenie dodania nowej organizacji. " - f"Wkrótce skontaktujemy się celem weryfikacji zgłoszenia." - ) - - # mark as verified - issue.add_to_labels(Label.AUTO_VERIFIED) + if not validation_warnings: + issue.add_to_labels(Label.AUTO_VERIFIED) + + if not has_label(issue, Label.WAITING): + issue.add_to_labels(Label.WAITING) + issue.create_comment( + f"@{issue.user.login}, dziękujemy za podanie informacji. " + f"Przyjęliśmy zgłoszenie dodania nowej organizacji. " + f"Wkrótce skontaktujemy się celem weryfikacji zgłoszenia." + ) # create organization yaml file and add to the Pull Request yaml_string = render_organization_yaml(data) diff --git a/.github/scripts/git_managers.py b/.github/scripts/git_managers.py index 4465282..37ebc77 100644 --- a/.github/scripts/git_managers.py +++ b/.github/scripts/git_managers.py @@ -26,8 +26,6 @@ def commit_file_contents_to_branch( self, branch_ref: GitRef, file_path: str, contents: str, commit_message: str ) -> GitCommit: latest_commit = self.repo.get_commit(branch_ref.object.sha) - if not latest_commit.commit.message.startswith("[automat] "): - raise BranchModifiedError() blob = self.repo.create_git_blob(contents, "utf-8") tree_element = InputGitTreeElement( path=file_path, mode="100644", type="blob", sha=blob.sha @@ -40,13 +38,13 @@ def commit_file_contents_to_branch( return new_commit def get_or_create_branch(self, source_branch: str, new_branch_name: str) -> GitRef: + source = self.repo.get_branch(source_branch) try: branch_ref = self.repo.get_git_ref(f"heads/{new_branch_name}") print(f"Branch '{new_branch_name}' already exists.") except GithubException as e: if e.status == 404: # Branch does not exist, create it from the source branch - source = self.repo.get_branch(source_branch) self.repo.create_git_ref( ref=f"refs/heads/{new_branch_name}", sha=source.commit.sha ) @@ -54,6 +52,12 @@ def get_or_create_branch(self, source_branch: str, new_branch_name: str) -> GitR print(f"Branch '{new_branch_name}' created from '{source_branch}'.") else: raise e + + latest_commit = self.repo.get_commit(branch_ref.object.sha) + if latest_commit.sha != source.commit.sha and not latest_commit.commit.message.startswith("[auto] "): + logger.error(f"Branch was modified: {latest_commit.commit.message}") + raise BranchModifiedError() + return branch_ref def get_or_create_pr( @@ -75,6 +79,11 @@ def get_or_create_pr( title=pr_title, body=pr_body, head=new_branch_name, base=target_branch ) + def was_branch_modified(self, source_branch, branch: GitRef) -> bool: + latest_commit = self.repo.get_commit(branch.object.sha) + source = self.repo.get_branch(source_branch) + + def create_or_update_remote_branch_with_file_commit( self, source_branch: str, @@ -115,8 +124,8 @@ def create_organization_yaml_pr( source_branch = "main" new_branch_name = f"nowa-organizacja-zgloszenie-{issue.number}" - commit_message = f"Dodana nowa organizacja: {data.get(OrgFormSchemaIds.name)} | Zgłoszenie: #{issue.number}" - pr_title = f"[automat] Dodana nowa organizacja: {data.get(OrgFormSchemaIds.name)} | Zgłoszenie: #{issue.number}" + commit_message = f"[auto] Dodana nowa organizacja: {data.get(OrgFormSchemaIds.name)} | Zgłoszenie: #{issue.number}" + pr_title = f"Dodana nowa organizacja: {data.get(OrgFormSchemaIds.name)} | Zgłoszenie: #{issue.number}" pr_body = f"Automatycznie dodana nowa organizacja na podstawie zgłoszenia z issue #{issue.number}.\n\n Closes #{issue.number}" file_path = "organizations/organization.yaml" diff --git a/.github/scripts/pullers.py b/.github/scripts/pullers.py index 6bf1fe0..ae2a066 100644 --- a/.github/scripts/pullers.py +++ b/.github/scripts/pullers.py @@ -57,7 +57,6 @@ def get_org_by_krs(issue: Issue, krs: str) -> Optional["KRSDataPuller"]: f"Nie udało się pobrać danych o organizacji z KRS. " f"Proszę sprawdzić, czy podany numer jest poprawny" ) - issue.add_to_labels(Label.INVALID_KRS) return if has_label(issue, Label.INVALID_KRS): From 2b5d03992f501fc8640701caaf9514d3f19292f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janusz=20=22Ivellios=22=20Kamie=C5=84ski?= Date: Tue, 17 Dec 2024 20:15:14 +0100 Subject: [PATCH 6/9] =?UTF-8?q?zaktualizowana=20tre=C5=9B=C4=87=20informac?= =?UTF-8?q?ji=20o=20kolejnym=20kroku=20(+poprawione=20formatowanie)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/nowa.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/nowa.yaml b/.github/ISSUE_TEMPLATE/nowa.yaml index 518fb4a..a69e99f 100644 --- a/.github/ISSUE_TEMPLATE/nowa.yaml +++ b/.github/ISSUE_TEMPLATE/nowa.yaml @@ -107,8 +107,4 @@ body: attributes: value: | # Następne kroki - Po wypełnieniu formularza, Twój wniosek zostanie zweryfikowany przez nasz zespół. - W celu weryfikacji poprawności danych skontaktujemy się z Twoją organizacją poprzez - oficjalne dane kontaktowe dostępne na stronie internetowej organizacji lub w rejestrze KRS. - W przypadku pozytywnej weryfikacji, otrzymasz od nas informację o dalszych krokach w komentarzu - do tego zgłoszenia. + Po wypełnieniu formularza, Twój wniosek zostanie zweryfikowany przez nasz zespół. W celu weryfikacji poprawności danych skontaktujemy się z Twoją organizacją poprzez oficjalne dane kontaktowe dostępne na stronie internetowej organizacji lub w rejestrze KRS. W przypadku pozytywnej weryfikacji, otrzymasz od nas informację o dalszych krokach w komentarzu do tego zgłoszenia (zakładka Issues). From 4c3843b9a6a4b2df62d8ddbb55b612e18a5123d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janusz=20=22Ivellios=22=20Kamie=C5=84ski?= Date: Wed, 18 Dec 2024 09:45:01 +0100 Subject: [PATCH 7/9] =?UTF-8?q?poprawkiz=20code=20review;=20zmiana=20class?= =?UTF-8?q?=20na=20dataclass;=20poprawione=20wyj=C4=85tki;=20zamiana=20bla?= =?UTF-8?q?cka=20na=20ruff=20[dodany=20te=C5=BC=20linting]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/scripts/cli.py | 8 +++----- .github/scripts/git_managers.py | 30 +++++++++++++++--------------- .github/scripts/pullers.py | 9 ++++----- .github/scripts/renderers.py | 13 +------------ .github/scripts/requirements.txt | 2 +- .github/scripts/utils.py | 2 +- .github/scripts/validators.py | 10 +++++----- 7 files changed, 30 insertions(+), 44 deletions(-) diff --git a/.github/scripts/cli.py b/.github/scripts/cli.py index ac5ec5b..a7292d4 100644 --- a/.github/scripts/cli.py +++ b/.github/scripts/cli.py @@ -53,9 +53,7 @@ def process_new_org_issue(issue: Issue, data: GithubIssueFormDataParser): # Update issue title if issue.title == NEW_ORG_ISSUE_DEFAULT_TITLE: logger.info("Updating issue title") - issue.edit( - title=f"{NEW_ORG_ISSUE_DEFAULT_TITLE} {org_name}" - ) + issue.edit(title=f"{NEW_ORG_ISSUE_DEFAULT_TITLE} {org_name}") logger.info("Adding auto-verified label") if not validation_warnings: @@ -77,8 +75,8 @@ def process_new_org_issue(issue: Issue, data: GithubIssueFormDataParser): except BranchModifiedError: logger.error("Branch was modified by someone else") issue.create_comment( - f"Aktualizacja pliku organizacji na podstawie opisu zgłoszenia niemożliwa. " - f"Plik organizacji został już zmodyfikowany przez innego użytkownika." + "Aktualizacja pliku organizacji na podstawie opisu zgłoszenia niemożliwa. " + "Plik organizacji został już zmodyfikowany przez innego użytkownika." ) diff --git a/.github/scripts/git_managers.py b/.github/scripts/git_managers.py index 37ebc77..0eb0d30 100644 --- a/.github/scripts/git_managers.py +++ b/.github/scripts/git_managers.py @@ -1,5 +1,5 @@ import logging -from multiprocessing.managers import Value +from dataclasses import dataclass from github import GithubException, InputGitTreeElement from github.GitCommit import GitCommit @@ -16,11 +16,11 @@ logger = logging.getLogger(__file__) +@dataclass class GitManager: """Manager for creating a new branch and pull request with a file commit in the repo.""" - def __init__(self, repo: Repository): - self.repo = repo + repo: Repository def commit_file_contents_to_branch( self, branch_ref: GitRef, file_path: str, contents: str, commit_message: str @@ -41,7 +41,7 @@ def get_or_create_branch(self, source_branch: str, new_branch_name: str) -> GitR source = self.repo.get_branch(source_branch) try: branch_ref = self.repo.get_git_ref(f"heads/{new_branch_name}") - print(f"Branch '{new_branch_name}' already exists.") + logger.info(f"Found existing branch '{new_branch_name}'.") except GithubException as e: if e.status == 404: # Branch does not exist, create it from the source branch @@ -49,12 +49,17 @@ def get_or_create_branch(self, source_branch: str, new_branch_name: str) -> GitR ref=f"refs/heads/{new_branch_name}", sha=source.commit.sha ) branch_ref = self.repo.get_git_ref(f"heads/{new_branch_name}") - print(f"Branch '{new_branch_name}' created from '{source_branch}'.") + logger.info( + f"Branch '{new_branch_name}' created from '{source_branch}'." + ) else: raise e latest_commit = self.repo.get_commit(branch_ref.object.sha) - if latest_commit.sha != source.commit.sha and not latest_commit.commit.message.startswith("[auto] "): + if ( + latest_commit.sha != source.commit.sha + and not latest_commit.commit.message.startswith("[auto] ") + ): logger.error(f"Branch was modified: {latest_commit.commit.message}") raise BranchModifiedError() @@ -73,16 +78,11 @@ def get_or_create_pr( f"Pull request already exists for branch '{new_branch_name}': {pulls[0].html_url}" ) return pulls[0] - else: - logger.info(f"Creating pull request for branch '{new_branch_name}'") - return self.repo.create_pull( - title=pr_title, body=pr_body, head=new_branch_name, base=target_branch - ) - - def was_branch_modified(self, source_branch, branch: GitRef) -> bool: - latest_commit = self.repo.get_commit(branch.object.sha) - source = self.repo.get_branch(source_branch) + logger.info(f"Creating pull request for branch '{new_branch_name}'") + return self.repo.create_pull( + title=pr_title, body=pr_body, head=new_branch_name, base=target_branch + ) def create_or_update_remote_branch_with_file_commit( self, diff --git a/.github/scripts/pullers.py b/.github/scripts/pullers.py index ae2a066..15d9427 100644 --- a/.github/scripts/pullers.py +++ b/.github/scripts/pullers.py @@ -10,7 +10,6 @@ class KRSDataPuller: - def __init__(self, krs: str): self.krs = krs self.data = self.pull_data() @@ -29,6 +28,7 @@ def pull_data(self) -> dict | None: f"Proszę [zweryfikować KRS ręcznie]" f"(https://api-krs.ms.gov.pl/api/krs/OdpisAktualny/{self.krs}?rejestr=S&format=json)." ) + raise else: raise requests.HTTPError(f"Failed to fetch data for KRS {self.krs}") @@ -44,7 +44,6 @@ def name(self): @staticmethod def get_org_by_krs(issue: Issue, krs: str) -> Optional["KRSDataPuller"]: - # Downloading official org data try: org = KRSDataPuller(krs) @@ -52,10 +51,10 @@ def get_org_by_krs(issue: Issue, krs: str) -> Optional["KRSDataPuller"]: issue.create_comment(str(e)) issue.add_to_labels(Label.INVALID_KRS) return - except requests.HTTPError as e: + except requests.HTTPError: issue.create_comment( - f"Nie udało się pobrać danych o organizacji z KRS. " - f"Proszę sprawdzić, czy podany numer jest poprawny" + "Nie udało się pobrać danych o organizacji z KRS. " + "Proszę sprawdzić, czy podany numer jest poprawny" ) return diff --git a/.github/scripts/renderers.py b/.github/scripts/renderers.py index 2022209..0b95eec 100644 --- a/.github/scripts/renderers.py +++ b/.github/scripts/renderers.py @@ -12,17 +12,6 @@ def render_organization_yaml(data: GithubIssueFormDataParser): template = env.get_template("organization.yaml.j2") - fields = ( - "name", - "slug", - "website", - "krs", - "street", - "postal_code", - "city", - "phone_number", - ) - - org_data = {field: data.get(getattr(OrgFormSchemaIds, field)) for field in fields} + org_data = {field: data.get(field) for field in OrgFormSchemaIds} return template.render(organization=org_data) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 2fe44d1..dc8b87f 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -2,4 +2,4 @@ PyGithub==2.5.0 PyYAML==6.0.2 requests==2.32.3 GitPython==3.1.43 -black==24.10.0 +ruff==0.8.3 diff --git a/.github/scripts/utils.py b/.github/scripts/utils.py index 3aa306b..b86abda 100644 --- a/.github/scripts/utils.py +++ b/.github/scripts/utils.py @@ -2,4 +2,4 @@ def has_label(issue, label: Label): - return any([l for l in issue.labels if l.name == label]) + return any([ilabel for ilabel in issue.labels if ilabel.name == label]) diff --git a/.github/scripts/validators.py b/.github/scripts/validators.py index 7f73206..c6b8124 100644 --- a/.github/scripts/validators.py +++ b/.github/scripts/validators.py @@ -1,5 +1,6 @@ import os import re +from dataclasses import dataclass import yaml from github import Issue @@ -10,11 +11,10 @@ from utils import has_label +@dataclass class OrgValidator: - - def __init__(self, data: GithubIssueFormDataParser, issue: Issue): - self.data = data - self.issue = issue + data: GithubIssueFormDataParser + issue: Issue def validate_krs(self) -> tuple[bool, str]: return ( @@ -29,7 +29,7 @@ def validate_postal_code(self) -> tuple[bool, str]: r"\d{2}-\d{3}", self.data.get(OrgFormSchemaIds.postal_code) ) ), - "niepoprawny kod pocztowy", + "niepoprawny kod pocztowy (oczekiwany format: 00-000)", ) def validate_phone_number(self) -> tuple[bool, str]: From d676ae5817cbfeddb43dde7f409d9bab1848f0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janusz=20=22Ivellios=22=20Kamie=C5=84ski?= Date: Wed, 18 Dec 2024 12:00:16 +0100 Subject: [PATCH 8/9] aktualizacja typowania metody --- .github/scripts/pullers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/scripts/pullers.py b/.github/scripts/pullers.py index 15d9427..da8296f 100644 --- a/.github/scripts/pullers.py +++ b/.github/scripts/pullers.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, Self import requests from github import Issue @@ -42,8 +42,8 @@ def name(self): .get("nazwa") ) - @staticmethod - def get_org_by_krs(issue: Issue, krs: str) -> Optional["KRSDataPuller"]: + @classmethod + def get_org_by_krs(cls, issue: Issue, krs: str) -> Self | None: # Downloading official org data try: org = KRSDataPuller(krs) From e1e3e1a7df537e64c3093b6af701f786242dcd86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janusz=20=22Ivellios=22=20Kamie=C5=84ski?= Date: Wed, 18 Dec 2024 20:39:46 +0100 Subject: [PATCH 9/9] =?UTF-8?q?Tworzenie=20instancji=20klasy=20u=C5=BCywaj?= =?UTF-8?q?=C4=85c=20przekazanej=20klasy=20(pozwala=20na=20dziedziczenie)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/scripts/pullers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/pullers.py b/.github/scripts/pullers.py index da8296f..452402c 100644 --- a/.github/scripts/pullers.py +++ b/.github/scripts/pullers.py @@ -46,7 +46,7 @@ def name(self): def get_org_by_krs(cls, issue: Issue, krs: str) -> Self | None: # Downloading official org data try: - org = KRSDataPuller(krs) + org = cls(krs) except KRSMaintenanceError as e: issue.create_comment(str(e)) issue.add_to_labels(Label.INVALID_KRS)