Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert malformed-yaml action to Python #255

Merged
merged 74 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
e3e74a9
add test python action to branch
tamsinforbes Apr 15, 2024
386ae87
update gitignore
tamsinforbes Apr 15, 2024
b54e6b0
add main python script
tamsinforbes Apr 16, 2024
4a5a880
change docker image
tamsinforbes Apr 16, 2024
ed13743
pathlib is in standard lib, yaml is installed with PyYaml
tamsinforbes Apr 16, 2024
dea9080
import pathlib
tamsinforbes Apr 16, 2024
52284cb
revert docker file
tamsinforbes Apr 16, 2024
d0bca10
fix entrypoint
tamsinforbes Apr 16, 2024
3f2c7f0
fix docker
tamsinforbes Apr 16, 2024
c3c30d0
fix docker
tamsinforbes Apr 16, 2024
a8c09c0
fix docker
tamsinforbes Apr 16, 2024
ec07694
fix docker
tamsinforbes Apr 16, 2024
cd0dde5
try entrypoint
tamsinforbes Apr 16, 2024
de35150
try chown
tamsinforbes Apr 16, 2024
d1cd8fa
revert to what works
tamsinforbes Apr 16, 2024
146d077
set up pytest, test and pipfile
tamsinforbes Apr 16, 2024
e8eb97f
Revert to using dashes in action name, add workaround for importing t…
tamsinforbes Apr 16, 2024
d5d8595
add workflow to run python tests
tamsinforbes Apr 16, 2024
02496e4
rename test files to avoid order issues
tamsinforbes Apr 16, 2024
809ff1e
simplify test to check if all expected malformed files are detected
tamsinforbes Apr 16, 2024
9a6e0bc
rename test function
tamsinforbes Apr 16, 2024
7aebb72
tidy names
tamsinforbes Apr 16, 2024
b10ddd7
tidy stray files
tamsinforbes Apr 16, 2024
f8fa930
update python malformed yaml read to match ruby malformed yaml readme…
tamsinforbes Apr 16, 2024
992d01f
add known bad yaml files to ignore files in code-formatter
tamsinforbes Apr 16, 2024
ac71f0e
Commit changes made by code formatters
github-actions[bot] Apr 16, 2024
80c6be9
add github service
tamsinforbes Apr 18, 2024
10bc4ad
add workflow to run action
tamsinforbes Apr 18, 2024
c45623c
ch wd
tamsinforbes Apr 18, 2024
b484d95
install pipenv
tamsinforbes Apr 18, 2024
5c9a456
add python 3.12
tamsinforbes Apr 18, 2024
9e59bd6
run funtion, update python to 3.12
tamsinforbes Apr 18, 2024
960336d
fix dockerfile
tamsinforbes Apr 18, 2024
e42e565
try multistage docker file
tamsinforbes Apr 18, 2024
f9fdd51
fix dockerfile
tamsinforbes Apr 18, 2024
15006a3
add required env vars
tamsinforbes Apr 18, 2024
5639911
fix
tamsinforbes Apr 18, 2024
66939c1
add github required envvars
tamsinforbes Apr 18, 2024
3f4a4b3
add github required envvars
tamsinforbes Apr 18, 2024
7812760
use python3
tamsinforbes Apr 18, 2024
1114a45
ditch pipenv
tamsinforbes Apr 19, 2024
dee5835
check changed files
tamsinforbes Apr 19, 2024
e4503a0
update PR with files
tamsinforbes Apr 19, 2024
c067744
add message function
tamsinforbes Apr 22, 2024
f5ad799
logic handling of malformed files messge
tamsinforbes Apr 22, 2024
4248cee
add first test
tamsinforbes Apr 22, 2024
c64881a
update wd in test.yml
tamsinforbes Apr 22, 2024
c156381
remove code formatter workflow
tamsinforbes Apr 22, 2024
e54e93c
update location of files for dockerfile
tamsinforbes Apr 22, 2024
37cce09
revert location of files for dockerfile
tamsinforbes Apr 22, 2024
94203ac
readd format-code empty file to be able to test malformed yaml code l…
tamsinforbes Apr 22, 2024
ef5551a
use GITHUB_REPOSITORY var to get org/repo_name format
tamsinforbes Apr 22, 2024
d342835
add logging
tamsinforbes Apr 22, 2024
fc98205
add hard fail
tamsinforbes Apr 22, 2024
c1ded3a
use sys.exit
tamsinforbes Apr 22, 2024
f182b92
add test for good or bad yaml files
tamsinforbes Apr 22, 2024
9279744
add main tests
tamsinforbes Apr 23, 2024
86d58c9
add requisite emojis
tamsinforbes Apr 23, 2024
9dc59fd
remove secrets folder, not needed
tamsinforbes Apr 23, 2024
a8bd7d1
handle no new or mofdified fiels and exit check
tamsinforbes Apr 23, 2024
f9d1c85
add system exist when no new or modified yaml files
tamsinforbes Apr 23, 2024
f556411
fix test relational file path
tamsinforbes Apr 23, 2024
f1b4cc8
make sys exit 0 to pass for no changed yaml files
tamsinforbes Apr 24, 2024
7ff3c79
remove python-malformed-yaml workflow as it will fail due t
tamsinforbes Apr 24, 2024
7439b64
update README for future version
tamsinforbes Apr 24, 2024
02e71d5
rename test.yml to a somehting more descriptive
tamsinforbes Apr 24, 2024
b974457
reinstate code-formatter workflow
tamsinforbes Apr 24, 2024
37da712
Update python-malformed-yaml/main.py
tamsinforbes Apr 26, 2024
4366136
Update python-malformed-yaml/Dockerfile
tamsinforbes Apr 26, 2024
3b6cdd1
Update python-malformed-yaml/Dockerfile
tamsinforbes Apr 26, 2024
d6c7b83
restructure and rename re pr comments
tamsinforbes Apr 26, 2024
8edeb19
remove format-code.yml workflow from running in this repo
tamsinforbes Apr 26, 2024
860649e
fix typo
tamsinforbes Apr 26, 2024
7ec1677
change name from python=-malformed-yaml to malformed-yaml of overwrit…
tamsinforbes Apr 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions .github/workflows/format-code.yml

This file was deleted.

29 changes: 29 additions & 0 deletions .github/workflows/test-malformed-yaml.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Test Malformed YAML Action
on:
- pull_request

defaults:
run:
working-directory: ./malformed-yaml

jobs:
test:
name: Test Malformed YAML Action
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install Python dependencies
run: |
pip install --upgrade pip
pip install --no-cache-dir -r requirements.txt
pip install --no-cache-dir -r requirements-dev.txt
- name: Run tests with pytest
run: |
pytest -vv test/
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
.built-image
.built-image
.DS_Store
__pycache__/
*.pytest_cache
*venv
*.venv
*.vscode
16 changes: 8 additions & 8 deletions malformed-yaml/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
FROM ministryofjustice/cloud-platform-tools:2.1
FROM python:3.12-slim

# Octokit depends on faraday, and an update to
# faraday breaks the current version of octokit
RUN gem install faraday --version 0.9
RUN gem install octokit --version 4.21.0
RUN apt-get update && apt-get upgrade -y && apt-get clean && rm -rf /var/lib/apt/lists/*

COPY reject-malformed-yaml.rb /reject-malformed-yaml.rb
COPY github.rb /github.rb
WORKDIR /app

ENTRYPOINT ["/reject-malformed-yaml.rb"]
COPY github_pull_request.py main.py requirements.txt /app/

RUN pip install --no-cache-dir -r requirements.txt

ENTRYPOINT [ "python3", "/app/main.py" ]
15 changes: 9 additions & 6 deletions malformed-yaml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ errors before the corresponding PR is merged.
## Example

This is an example of malformed YAML, which will raise an error if
you try to parse it (at least, in ruby):
you try to parse it (at least, in Ruby and Python):

```
desc: Example of a malformed YAML file
Expand All @@ -30,13 +30,16 @@ jobs:
reject-malformed-yaml:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ministryofjustice/github-actions/malformed-yaml@main
- name: Checkout repo
- uses: actions/[email protected]
- name: Detect malformed YAML files
- uses: ministryofjustice/github-actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.number }}

```

`GITHUB_TOKEN` is provided automatically by github actions. You do
not need to do anything extra to make it available. Just use the
`GITHUB_TOKEN` and `PR_NUMBER` are provided automatically. You do
not need to do anything extra to make these available. Just use the
content above, exactly as shown.

5 changes: 5 additions & 0 deletions malformed-yaml/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: "Malformed YAML"
description: "Detect malformed YAML files in a PR."
runs:
using: "docker"
image: "Dockerfile"
136 changes: 0 additions & 136 deletions malformed-yaml/github.rb

This file was deleted.

20 changes: 20 additions & 0 deletions malformed-yaml/github_pull_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from github import Github


class GitHubPullRequest():
"""A class to interact with the GitHub API."""

def __init__(self, github_token, repository_name: str, pr_number: int):
self.github_token = github_token
self.pr_number = pr_number
self.repository_name = repository_name
self.client = Github(self.github_token)

def get_changed_files_from_pr(self) -> list:
return [file.filename for file in self.client.get_repo(
self.repository_name).get_pull(self.pr_number).get_files()]

def fail_pr(self, message: str):
self.client.get_repo(
self.repository_name).get_pull(self.pr_number).create_issue_comment(
message)
100 changes: 100 additions & 0 deletions malformed-yaml/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import os
import logging
import sys
import re
import yaml
from github_pull_request import GitHubPullRequest as github_pull_request


logging.basicConfig(
format="%(asctime)s %(levelname)-8s %(message)s",
level="INFO",
datefmt="%Y-%m-%d %H:%M:%S",
)

logger = logging.getLogger(__name__)


def get_github_env() -> tuple[str, str, str]:
"""
Function to collect the three required GitHub
environment variables
"""
token = os.getenv("GITHUB_TOKEN")
pr_number = os.getenv("PR_NUMBER")
repo = os.getenv("GITHUB_REPOSITORY")
if not token:
raise ValueError("No GITHUB_TOKEN.")
if not pr_number:
raise ValueError("No PR_NUMBER.")
return token, repo, pr_number


def get_changed_yaml_files_from_pr() -> list[str]:
"""
Collect a list of all the new or modified YAML files
in a PR, except those in a 'secret/' directory.
"""
token, repository_name, pr = get_github_env()
github_pr = github_pull_request(token, repository_name, int(pr))
# We assume there must always be some changed or new files in a PR
changed_files = github_pr.get_changed_files_from_pr()
pattern = re.compile("\\.yml$|\\.yaml$")
skip_pattern = re.compile("secret/")
return [
file for file in changed_files if pattern.search(file) and not skip_pattern.search(file)
]

def get_malformed_yaml_files_and_errors(yaml_files: list[str]) -> list[str]:
"""
Input:
yaml_files: List of YAML files to be tested for correct format
Output:
malformed_yaml_files: List of those YAML files that are malformed and
their error messages.
"""
malformed_yaml_files_and_errors= []
for y in yaml_files:
with open(y) as stream:
try:
yaml.safe_load(stream)
except yaml.YAMLError as exc:
malformed_yaml_files_and_errors.append(f"\n{str(y)}:\n{str(exc)}")
return malformed_yaml_files_and_errors

def malformed_yaml_files_message(files_and_errors: list):
"""
Compose message to display in the PR.
"""
msg = "😱 The following malformed YAML files and related errors were found:\n"
msg += "\n".join(files_and_errors) + "\n\nπŸ₯Ί Please correct them and resubmit this PR."
return msg

def main():
"""
Function to collect the new or modified YAML files from the PR that
are malformed, report these to the user, and request changes.
"""
token, repository_name, pr = get_github_env()
github_pr = github_pull_request(token, repository_name, int(pr))


changed_yaml_files = get_changed_yaml_files_from_pr()
if not changed_yaml_files:
logger.info("🫧 No new or modified YAML files.")
return False

malformed_yaml_files_and_errors = get_malformed_yaml_files_and_errors(changed_yaml_files)
if malformed_yaml_files_and_errors:
msg = malformed_yaml_files_message(malformed_yaml_files_and_errors)
github_pr.fail_pr(message=msg)
logger.error(msg)
return True

logger.info("🀩 PR YAML files all OK!")
return False


if __name__ == "__main__":
if main():
sys.exit(1)
Loading