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

ci: Add project fields validator #334

Merged
merged 51 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
9a1d081
ci: add project-fields-validator
minikin Oct 18, 2024
1f8dbcb
chore: update python and earthly
minikin Oct 21, 2024
1edb413
Update Earthfile
minikin Oct 22, 2024
1d738f3
feat: update ProjectFieldsValidator
minikin Oct 22, 2024
f0e8d76
chore: refactor python code
minikin Oct 22, 2024
debf21b
Update README.md
minikin Oct 22, 2024
dd17f55
Update main.py
minikin Oct 22, 2024
045e5db
chore: ci lint fixes
minikin Oct 22, 2024
a818e6f
Update Earthfile
minikin Oct 22, 2024
a09ca82
Update Earthfile
minikin Oct 23, 2024
46a289d
Update Earthfile
minikin Oct 24, 2024
7e18b29
Update Earthfile
minikin Oct 24, 2024
061098f
Update Earthfile
minikin Oct 24, 2024
f1aadfb
Update Earthfile
minikin Oct 25, 2024
cae42c4
feat: add GitHub action
minikin Oct 25, 2024
a8c6448
Merge branch 'master' into feat/validate-project-fields-in-prs-and-is…
minikin Oct 25, 2024
304b6f2
Update validate-project-fields.yml
minikin Oct 25, 2024
6586bc7
Update validate-project-fields.yml
minikin Oct 25, 2024
c8bc9ed
wip: clean up
minikin Oct 25, 2024
8042e83
chore: add GITHUB_TOKEN
minikin Oct 25, 2024
a5a5f29
Update Earthfile
minikin Oct 25, 2024
33a8b4e
Update validate-project-fields.yml
minikin Oct 25, 2024
6e11382
Update validate-project-fields.yml
minikin Oct 25, 2024
2cc8180
Update validate-project-fields.yml
minikin Oct 25, 2024
fb45eb6
wip
minikin Oct 25, 2024
55ad662
wip
minikin Oct 25, 2024
e4577bd
wip
minikin Oct 25, 2024
dcf7afa
Update validate-project-fields.yml
minikin Oct 25, 2024
7b13c3f
wip
minikin Oct 25, 2024
279e0ae
wip: testing
jmgilman Oct 25, 2024
bcd275e
wip: testing
jmgilman Oct 25, 2024
e427593
wip: testing
jmgilman Oct 25, 2024
d27bbcb
wip: testing
jmgilman Oct 25, 2024
8202e22
chore: merge branch 'master' into feat/validate-project-fields-in-prs…
jmgilman Oct 25, 2024
039d0b9
wip: cleanup
jmgilman Oct 25, 2024
976f449
Update main.py
minikin Oct 27, 2024
a7cd613
Update main.py
minikin Oct 27, 2024
6736c79
Update main.py
minikin Oct 27, 2024
0efd054
Update validate-project-fields.yml
minikin Oct 27, 2024
4a40ecf
Merge branch 'master' into feat/validate-project-fields-in-prs-and-is…
minikin Oct 28, 2024
2c28d0c
Merge branch 'master' into feat/validate-project-fields-in-prs-and-is…
minikin Oct 29, 2024
919ce82
Merge branch 'master' into feat/validate-project-fields-in-prs-and-is…
minikin Nov 4, 2024
dc59408
Merge branch 'master' into feat/validate-project-fields-in-prs-and-is…
minikin Jan 8, 2025
6bca742
Merge branch 'master' into feat/validate-project-fields-in-prs-and-is…
minikin Jan 30, 2025
a5507a1
Merge branch 'master' into feat/validate-project-fields-in-prs-and-is…
stevenj Jan 31, 2025
a2b3bbe
Feat(ci): validate project fields in prs and issues (#374)
stevenj Feb 5, 2025
780e360
Merge branch 'master' into feat/validate-project-fields-in-prs-and-is…
minikin Feb 5, 2025
f416673
Merge branch 'master' into feat/validate-project-fields-in-prs-and-is…
minikin Feb 6, 2025
8878f1d
Update validate-project-fields.yml
minikin Feb 6, 2025
b3705ef
Update validate-project-fields.yml
minikin Feb 6, 2025
19ae9f8
Update validate-project-fields.yml
minikin Feb 6, 2025
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
2 changes: 2 additions & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ depgraph
devenv
dind
dockerhub
doseq
doublecircle
Earthfile
Earthfiles
Expand Down Expand Up @@ -50,6 +51,7 @@ idents
JDBC
jorm
jormungandr
jsonlib
junitreport
Kroki
kubeconfig
Expand Down
49 changes: 49 additions & 0 deletions .github/workflows/validate-project-fields.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Validate Project Fields

on:
pull_request:
types:
- opened
- edited
- synchronize
- reopened
- unassigned

permissions:
contents: write
pull-requests: write
id-token: write
repository-projects: write

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
validate-project-fields:
runs-on: ubuntu-latest
env:
# Needs a PAT Classic with (read:project)
GITHUB_PROJECTS_PAT: ${{ secrets.PROJECTS_PAT }}
GITHUB_REPOSITORY: "${{ github.repository }}"
GITHUB_EVENT_NUMBER: "${{ github.event.number || '0' }}"
PROJECT_NUMBER: 102
steps:
- name: Fetch Validation Script
uses: actions/checkout@v4
with:
repository: input-output-hk/catalyst-ci
ref: master
sparse-checkout: |
utilities/project-fields-validator/main.py
sparse-checkout-cone-mode: false

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Run Project Fields Validation
if: always()
continue-on-error: false
run: utilities/project-fields-validator/main.py
12 changes: 11 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,15 @@ check-spelling:
earthly +clean-spelling-list
earthly +check-spelling


# Fix and Check Markdown files
format-python-code:
ruff check --select I --fix .
ruff format .

# Fix and Check Markdown files
lint-python:
ruff check .

# Pre Push Checks - intended to be run by a git pre-push hook.
pre-push: check-markdown check-spelling
pre-push: check-markdown check-spelling format-python-code lint-python
9 changes: 5 additions & 4 deletions earthly/docs/common/macros/include.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import textwrap
import re
import textwrap


def inc_file(env, filename, start_line=0, end_line=None, indent=None):
"""
Expand All @@ -10,7 +11,7 @@ def inc_file(env, filename, start_line=0, end_line=None, indent=None):
project.
indent = number of spaces to indent every line but the first.
"""

try:
full_filename = os.path.join(env.project_dir, filename)

Expand All @@ -24,8 +25,8 @@ def inc_file(env, filename, start_line=0, end_line=None, indent=None):
else:
indent = " " * indent
text = textwrap.indent(text, indent)
text = text[len(indent):] # First line should not be indented at all.
text = re.sub(r'\n$', '', text, count=1)
text = text[len(indent) :] # First line should not be indented at all.
text = re.sub(r"\n$", "", text, count=1)
# print(text)
return text
except Exception as exc:
Expand Down
8 changes: 4 additions & 4 deletions earthly/docs/dev/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

# cspell: words gmtime

import argparse
import subprocess
import sys
import time
import urllib.request
import webbrowser
from dataclasses import dataclass, field
import argparse
import sys
import urllib.request


class ProcessRunError(Exception):
Expand Down Expand Up @@ -213,7 +213,7 @@ def main():

# Open the webpage in a browser (once)
if not browsed:
browsed=True
browsed = True
if not args.no_browser:
webbrowser.open(f"http://localhost:{docs_container.exposed_port}")

Expand Down
7 changes: 3 additions & 4 deletions earthly/postgresql/scripts/std_checks.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#!/usr/bin/env python3

import argparse

import python.exec_manager as exec_manager
import python.vendor_files_check as vendor_files_check
import argparse
import rich
from rich import print
import os

# This script is run inside the `check` stage for postgres database setup
# to perform all high level non-compilation checks.
Expand All @@ -32,7 +31,7 @@ def main():
# Force color output in CI
rich.reconfigure(color_system="256")

parser = argparse.ArgumentParser(description="Postgres checks processing.")
argparse.ArgumentParser(description="Postgres checks processing.")

results = exec_manager.Results("Postgres checks")

Expand Down
24 changes: 13 additions & 11 deletions earthly/postgresql/scripts/std_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

# cspell: words dbmigrations dbhost dbuser dbuserpw Tsvg pgsql11

from typing import Optional
import python.exec_manager as exec_manager
import python.db_ops as db_ops
import argparse
import rich
from rich import print
import os
import re
from textwrap import indent

import python.db_ops as db_ops
import python.exec_manager as exec_manager
import rich
from rich import print


def process_sql_files(directory):
file_pattern = r"V(\d+)__(\w+)\.sql"
migrations = {}
Expand All @@ -32,11 +33,12 @@ def process_sql_files(directory):
migrations[version] = {
"version": version,
"migration_name": migration_name,
"sql_data": sql_data
"sql_data": sql_data,
}

return migrations, largest_version


class Migrations:
def __init__(self, args: argparse.Namespace):
"""
Expand Down Expand Up @@ -73,6 +75,7 @@ def create_markdown_file(self, file_path):

print("Markdown file created successfully at: {}".format(file_path))


def main():
# Force color output in CI
rich.reconfigure(color_system="256")
Expand Down Expand Up @@ -124,9 +127,7 @@ def main():
f"-o docs/database_schema/ "
)
res = exec_manager.cli_run(
schemaspy_cmd,
name="Generate SchemaSpy Documentation",
verbose=True
schemaspy_cmd, name="Generate SchemaSpy Documentation", verbose=True
)
results.add(res)

Expand All @@ -135,7 +136,7 @@ def main():
exec_manager.cli_run(
'echo "hide: true" > docs/database_schema/.pages',
name="Create .pages file",
verbose=True
verbose=True,
)

migrations.create_markdown_file("docs/migrations.md")
Expand All @@ -145,5 +146,6 @@ def main():
if not results.ok():
exit(1)


if __name__ == "__main__":
main()
main()
9 changes: 8 additions & 1 deletion earthly/python/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ python-base:
# Adjust Poetry's configuration to prevent connection pool warnings.
RUN poetry config installer.max-workers 10

# Extension we use needs rust.
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
RUN echo 'source $HOME/.cargo/env' >> $HOME/.bashrc
ENV PATH="/root/.cargo/bin:${PATH}"

# Install ruff for linting.
RUN pip3 install ruff
RUN pip3 install rich
RUN pip3 install third-party-imports

# Universal build scripts we will always need and are not target dependent.
COPY --dir scripts /scripts
Expand All @@ -58,9 +64,10 @@ BUILDER:

CHECK:
FUNCTION
ARG options

# Execute the check script
RUN /scripts/std_checks.py
RUN /scripts/std_checks.py $options

LINT_PYTHON:
# Linting all Python code is done with ruff
Expand Down
94 changes: 82 additions & 12 deletions earthly/python/scripts/std_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,114 @@
import subprocess
import sys

def check_pyproject_toml():

def check_pyproject_toml(stand_alone):
# Check if 'pyproject.toml' exists in the project root.
if not os.path.isfile('pyproject.toml'):
if not os.path.isfile("pyproject.toml"):
if stand_alone:
print("pyproject.toml check passed.")
return True

print("Error: pyproject.toml not found.")
return False
else:
if stand_alone:
print("Error: pyproject.toml found in standalone python module.")
return False

print("pyproject.toml check passed.")
return True

def check_poetry_lock():


def check_poetry_lock(stand_alone):
# Check if 'poetry.lock' exists in the project root.
if not os.path.isfile('poetry.lock'):
if not os.path.isfile("poetry.lock"):
if stand_alone:
print("poetry.lock check passed.")
return True

print("Error: poetry.lock not found.")
return False
else:
if stand_alone:
print("Error: poetry.lock found in stand alone module.")
return False

print("poetry.lock check passed.")
return True


def check_lint_with_ruff():
# Check Python code linting issues using 'ruff'.
result = subprocess.run(["ruff", "check", "."], capture_output=True)
if result.returncode != 0:
print("Code linting issues found.")
print(result.stdout.decode())
return False
else:
print("Code linting check passed.")
return True


def check_code_format_with_ruff():
# Check Python code formatting and linting issues using 'ruff'.
result = subprocess.run(['ruff', 'check', '.'], capture_output=True)
result = subprocess.run(["ruff", "format", "--check", "."], capture_output=True)
if result.returncode != 0:
print("Code formatting and linting issues found.")
print("Code formatting issues found.")
print(result.stdout.decode())
return False
else:
print("Code formatting and linting check passed.")
print("Code formatting check passed.")
return True

def main():

def zero_third_party_packages_found(output):
lines = output.split("\n") # Split the multiline string into individual lines

if len(lines) < 2:
return False # The second line doesn't exist
else:
return lines[1].startswith("Found '0' third-party package imports")


def check_no_third_party_imports():
# Check No third party imports have been used
result = subprocess.run(["third-party-imports", "."], capture_output=True)
output = result.stdout.decode()

if result.returncode != 0 or not zero_third_party_packages_found(output):
print("Checking third party imports failed.")
print(output)
return False
else:
print("Checking third party imports passed.")
return True


def main(stand_alone):
if stand_alone:
print(
"Checking Standalone Python files (No third party imports or poetry project)"
)
checks_passed = True
# Perform checks
checks_passed &= check_pyproject_toml()
checks_passed &= check_poetry_lock()

# These are true on python programs that require third party libraries, false otherwise
checks_passed &= check_pyproject_toml(stand_alone)
checks_passed &= check_poetry_lock(stand_alone)

# Always done
checks_passed &= check_lint_with_ruff()
checks_passed &= check_code_format_with_ruff()

# Only done if the code should be able to run without third part libraries
if stand_alone:
checks_passed &= check_no_third_party_imports()

if not checks_passed:
sys.exit(1)


if __name__ == "__main__":
main()
print(f"Current Working Directory: {os.getcwd()}")
main("--stand-alone" in sys.argv[1:])
Loading
Loading