diff --git a/.github/workflows/commit-message.yml b/.github/workflows/commit-message.yml deleted file mode 100644 index e61c8b6..0000000 --- a/.github/workflows/commit-message.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: "Commit Message Check" -on: - pull_request: - types: - - opened - - edited - - reopened - - synchronize -jobs: - check-commit-message: - name: Check Commit Message - runs-on: ubuntu-latest - steps: - - name: Check commit lines length - uses: gsactions/commit-message-checker@v2 - with: - pattern: "^.{4,72}" - flags: "gms" - error: "The maximum line length of 72 characters is exceeded." - excludeDescription: "true" - excludeTitle: "true" - checkAllCommitMessages: "true" - accessToken: ${{ secrets.GITHUB_TOKEN }} - - name: Check for Resolves/Fixes links in PR description - uses: gsactions/commit-message-checker@v2 - with: - pattern: '(Resolves|Fixes):? (?:https:\/\/github.com\/)?AlmaLinux\/build-system(\/issues\/|#)[0-9]+$' - error: 'You need at least one "Resolves|Fixes: " line.' diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml new file mode 100644 index 0000000..005cece --- /dev/null +++ b/.github/workflows/pr-checks.yml @@ -0,0 +1,36 @@ +name: Pull Request Checks +on: + pull_request: + types: + - opened + - edited + - reopened + +defaults: + run: + shell: bash + +jobs: + check-pr-message: + runs-on: ubuntu-latest + steps: + + - name: Check the PR title and description + run: | + errors= + + if grep -qE '^.{73,}$' <<< "${{ github.event.pull_request.title }}"; then + printf "ERROR: The PR title is longer than 72 characters:\n" + printf " > ${{ github.event.pull_request.title }}\n" + errors=true + fi + + issue_regex='(Resolves|Fixes):? +(https:\/\/github.com\/)?AlmaLinux\/build-system(\/issues\/|#)[0-9]+' + if ! grep -qE "$issue_regex" <<< "${{ github.event.pull_request.body }}"; then + printf "ERROR: You need at least one \"Resolves|Fixes: \" line.\n" + errors=true + fi + + if [[ $errors == true ]]; then + exit 2 + fi diff --git a/.github/workflows/preflight-summary.yml b/.github/workflows/preflight-summary.yml new file mode 100644 index 0000000..e807c46 --- /dev/null +++ b/.github/workflows/preflight-summary.yml @@ -0,0 +1,71 @@ +name: Preflight Summary +on: + workflow_run: + workflows: [Preflight] + types: [completed] + +defaults: + run: + shell: bash + +jobs: + + submit-summary: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + + - name: Download Preflight artifacts + # https://github.com/marketplace/actions/download-workflow-artifact + uses: dawidd6/action-download-artifact@v3 + with: + name: preflight-reports + run_id: ${{ github.event.workflow_run.id }} + + - name: Load Environment + run: cat environment.txt | tee -a $GITHUB_ENV + + - name: Generate Test Summary + # https://github.com/marketplace/actions/junit-test-dashboard + uses: test-summary/action@v2 + with: + paths: pytest-report.xml + output: test-summary.md + + - name: Generate Coverage Summary + # https://github.com/marketplace/actions/code-coverage-summary + # Generates code-coverage-results.md + uses: irongut/CodeCoverageSummary@v1.3.0 + with: + filename: pytest-coverage.xml + badge: false + hide_branch_rate: true + hide_complexity: true + indicators: false + format: markdown + output: file + + - name: Generate Preflight Summary + run: | + { + JOB_URL="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/${{github.event.workflow_run.id }}" + printf "[%s]($JOB_URL \"Go to Job Summary\")\n\n" "$(< test-summary.md)" + printf "### Code Coverage Summary\n" + cat code-coverage-results.md + printf "\nView full reports on the [Job Summary]($JOB_URL \"Go to Job Summary\") page\n\n" + + cat {pylint,black,isort,bandit}-report.md > linter-reports.md 2>/dev/null || true + if [[ -s linter-reports.md ]]; then + printf "### Linter reports\n" + cat linter-reports.md + fi + } > preflight-report.md + + - name: Comment PR + # https://github.com/marketplace/actions/comment-pull-request + uses: thollander/actions-comment-pull-request@v2 + with: + filePath: preflight-report.md + comment_tag: preflight_summary + pr_number: ${{ env.PR_NUMBER }} diff --git a/.github/workflows/preflight.yml b/.github/workflows/preflight.yml new file mode 100644 index 0000000..7a78565 --- /dev/null +++ b/.github/workflows/preflight.yml @@ -0,0 +1,146 @@ +name: Preflight +on: [pull_request] + +defaults: + run: + shell: bash + +jobs: + + check-commit-message: + runs-on: ubuntu-latest + steps: + + - name: Check out repository + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Check commit message + run: | + errors= + + readarray -t long_lines < \ + <(git log -1 --pretty=format:%B ${{ github.event.pull_request.head.sha }} | grep -E '^.{73,}$') + if [[ ${#long_lines[@]} -ne 0 ]]; then + printf "ERROR: The following lines are longer than 72 characters:\n" + printf " > %s\n" "${long_lines[@]}" + errors=true + fi + + if [[ $errors == true ]]; then + exit 2 + fi + + preflight: + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + REPORTS_DIR: .preflight-reports + steps: + + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + # https://github.com/marketplace/actions/docker-setup-buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + # https://github.com/marketplace/actions/build-and-push-docker-images + uses: docker/build-push-action@v5 + with: + context: . + load: true + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Prepare working directory + run: mkdir -p $REPORTS_DIR + + - name: Get changed .py files + # https://github.com/marketplace/actions/paths-changes-filter + uses: dorny/paths-filter@v3 + id: changed-files + with: + list-files: shell + filters: | + py: + - added|modified: '**/*.py' + cacher: + - added|modified: 'alma_tests_cacher/**/*.py' + - added|modified: '*.py' + + - name: Run pytest + run: | + docker compose run --rm cacher bash -c " + pytest -v --cov \ + --junit-xml=$REPORTS_DIR/pytest-report.xml \ + --cov-report=xml:$REPORTS_DIR/pytest-coverage.xml \ + --cov-report=term | tee $REPORTS_DIR/pytest-output.txt" + + - name: Run pylint + if: ${{ steps.changed-files.outputs.cacher == 'true' }} + run: | + docker compose run --rm cacher bash -c " + pylint --exit-zero ${{ steps.changed-files.outputs.cacher_files }} \ + | tee $REPORTS_DIR/pylint-report.txt" + + - name: Run black + if: ${{ steps.changed-files.outputs.py == 'true' }} + run: | + docker compose run --rm cacher bash -c " + black --check --diff --color ${{ steps.changed-files.outputs.py_files }} \ + | tee >(sed 's/\x1B\[[0-9;]*m//g' > $REPORTS_DIR/black-report.txt)" + + - name: Run isort + if: ${{ steps.changed-files.outputs.py == 'true' }} + run: | + docker compose run --rm cacher bash -c " + isort --check-only --diff --color ${{ steps.changed-files.outputs.py_files }} \ + | tee >(sed 's/\x1B\[[0-9;]*m//g' > $REPORTS_DIR/isort-report.txt)" + + - name: Run bandit + if: ${{ steps.changed-files.outputs.cacher == 'true' }} + run: | + docker compose run --rm cacher bash -c " + bandit -c pyproject.toml ${{ steps.changed-files.outputs.cacher_files }} \ + | tee >(sed 's/\x1B\[[0-9;]*m//g' > $REPORTS_DIR/bandit-report.txt)" + + - name: Generate .md reports + run: | + awk 'NR == 1 {next}; /^-+ coverage:/ {exit}; {print}' $REPORTS_DIR/pytest-output.txt \ + > $REPORTS_DIR/pytest-report.txt + awk '/^-+ coverage:/, /^TOTAL/' $REPORTS_DIR/pytest-output.txt \ + > $REPORTS_DIR/coverage-report.txt + + for tool in coverage pytest pylint black isort bandit; do + if [[ -s $REPORTS_DIR/${tool}-report.txt ]]; then + { + printf "
${tool^} report\n" + printf '\n```\n' + cat $REPORTS_DIR/${tool}-report.txt + printf '\n```\n' + printf '\n
\n\n' + } > $REPORTS_DIR/${tool}-report.md + fi + done + + - name: Save environment + run: | + { + echo "PR_NUMBER=${{ github.event.number }}" + } > $REPORTS_DIR/environment.txt + + - name: Upload Pytest reports + # https://github.com/actions/upload-artifact + uses: actions/upload-artifact@v4 + with: + name: preflight-reports + path: ${{ env.REPORTS_DIR }} + compression-level: 9 + + - name: Publish Job Summary + run: | + cat $REPORTS_DIR/{coverage,pytest,pylint,black,isort,bandit}-report.md \ + > $GITHUB_STEP_SUMMARY 2>/dev/null || true diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml deleted file mode 100644 index 368170a..0000000 --- a/.github/workflows/pytest.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: pytest -on: - pull_request: - branches: - - "**" - push: - branches: - - master -jobs: - test: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v4 - name: Check out repository - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v42 - with: - files: | - **/*.py - - name: Prepare python env - if: ${{ steps.changed-files.outputs.all_changed_files }} - run: pip3 install -r requirements.devel.txt - - name: Run unit tests (pytest) - if: ${{ steps.changed-files.outputs.all_changed_files }} - run: pytest -v --cov - --cov-report xml:/tmp/coverage.xml --junitxml=/tmp/pytest.xml - --cov-report term-missing:skip-covered | tee /tmp/pytest-coverage.txt - - name: Pytest coverage comment - if: ${{ steps.changed-files.outputs.all_changed_files }} - uses: MishaKav/pytest-coverage-comment@main - id: coverageComment - with: - pytest-coverage-path: /tmp/pytest-coverage.txt - pytest-xml-coverage-path: /tmp/coverage.xml - title: Coverage report for changed files - badge-title: Total coverage - hide-badge: false - hide-report: false - report-only-changed-files: true - hide-comment: false - remove-link-from-badge: false - junitxml-path: /tmp/pytest.xml - - name: Create the Badge - if: ${{ github.ref == 'refs/heads/master' && steps.coverageComment.outputs.coverage }} - uses: schneegans/dynamic-badges-action@v1.7.0 - with: - auth: ${{ secrets.GIST_SECRET }} - gistID: 809b43cccaf8256b03fc0103e245eefc - filename: alma-tests-cacher-badge__main.json - label: Coverage Report - message: ${{ steps.coverageComment.outputs.coverage }} - color: ${{ steps.coverageComment.outputs.color }} - namedLogo: python diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 0000000..8726add --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,87 @@ +name: Run Tests +on: + push: + branches: [main] + +defaults: + run: + shell: bash + +jobs: + + pytest: + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + REPORTS_DIR: .reports + steps: + + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + # https://github.com/marketplace/actions/docker-setup-buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + # https://github.com/marketplace/actions/build-and-push-docker-images + uses: docker/build-push-action@v5 + with: + context: . + load: true + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Prepare working directory + run: mkdir -p $REPORTS_DIR + + - name: Run pytest + id: pytest + run: | + docker compose run --rm cacher bash -c " + pytest -v --cov \ + --cov-report=json:$REPORTS_DIR/pytest-report.json \ + --cov-report=term | tee $REPORTS_DIR/pytest-output.txt" + + python -c " + import json + coverage = json.load(open('$REPORTS_DIR/pytest-report.json'))['totals']['percent_covered_display'] + print(f'percent_covered={coverage}', file=open('$GITHUB_OUTPUT', 'a'))" + + - name: Create Coverage Badge + # https://github.com/marketplace/actions/dynamic-badges + uses: schneegans/dynamic-badges-action@v1.7.0 + with: + auth: ${{ secrets.GIST_SECRET }} + gistID: 809b43cccaf8256b03fc0103e245eefc + filename: alma-tests-cacher-badge__main.json + label: Test Coverage + message: ${{ steps.pytest.outputs.percent_covered }}% + valColorRange: ${{ steps.pytest.outputs.percent_covered }} + minColorRange: 25 + maxColorRange: 60 + namedLogo: pytest + + - name: Generate .md reports + run: | + awk 'NR == 1 {next}; /^-+ coverage:/ {exit}; {print}' $REPORTS_DIR/pytest-output.txt \ + > $REPORTS_DIR/pytest-report.txt + awk '/^-+ coverage:/, /^TOTAL/' $REPORTS_DIR/pytest-output.txt \ + > $REPORTS_DIR/coverage-report.txt + + for tool in coverage pytest; do + if [[ -s $REPORTS_DIR/${tool}-report.txt ]]; then + { + printf "
${tool^} report\n" + printf '\n```\n' + cat $REPORTS_DIR/${tool}-report.txt + printf '\n```\n' + printf '\n
\n\n' + } > $REPORTS_DIR/${tool}-report.md + fi + done + + - name: Publish Job Summary + run: | + cat $REPORTS_DIR/{coverage,pytest}-report.md \ + > $GITHUB_STEP_SUMMARY 2>/dev/null || true diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml deleted file mode 100644 index 3a6537a..0000000 --- a/.github/workflows/syntax.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: syntax -on: - pull_request: - branches: - - "**" -jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - name: Check out repository - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v42 - with: - files: | - **/*.py - - name: Prepare python env - if: ${{ steps.changed-files.outputs.all_changed_files }} - run: pip3 install -r requirements.devel.txt - - name: Run pylint - id: pylint - if: ${{ steps.changed-files.outputs.all_changed_files }} - run: | - delimiter=$(openssl rand -hex 8) - echo "report<<$delimiter" >> $GITHUB_OUTPUT - pylint ${{ steps.changed-files.outputs.all_changed_files }} --exit-zero >> $GITHUB_OUTPUT - echo $delimiter >> $GITHUB_OUTPUT - - name: Post pylint output - uses: mshick/add-pr-comment@v2 - if: ${{ steps.changed-files.outputs.all_changed_files }} - with: - message: | -
- pylint output - - ``` - ${{ steps.pylint.outputs.report }} - ``` -
- message-id: pylint-report - - name: Run black - if: ${{ steps.changed-files.outputs.all_changed_files }} - run: black ${{ steps.changed-files.outputs.all_changed_files }} --check --diff --color - - name: Run isort - if: ${{ steps.changed-files.outputs.all_changed_files }} - run: isort ${{ steps.changed-files.outputs.all_changed_files }} --diff --color --check-only diff --git a/README.md b/README.md index 4aafbb0..34ea907 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # AlmaLinux tests cacher -![badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/maccelf/809b43cccaf8256b03fc0103e245eefc/raw/alma-tests-cacher-badge__main.json) + + Test Coverage + +

Tool for caching third-party tests for ALTS from git repositories. diff --git a/pyproject.toml b/pyproject.toml index 59483fa..8922a64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,14 +3,25 @@ profile = "black" split_on_trailing_comma = true [tool.black] -line-length = 79 +line-length = 120 skip-string-normalization = true # see https://black.readthedocs.io/en/stable/the_black_code_style/future_style.html#preview-style preview = true enable-unstable-feature = ["hug_parens_with_braces_and_square_brackets"] [tool.pylint] -max-line-length = 80 +max-line-length = 120 + +# Minimum line length for functions/classes that require docstrings +docstring-min-length = 50 + +# https://pylint.readthedocs.io/en/stable/user_guide/checkers/features.html +disable = [ + "C0114", # missing-module-docstring + "R0902", # too-many-instance-attributes + "R0913", # too-many-arguments + "W1514", # unspecified-encoding +] [tool.coverage.run] source = ['.'] diff --git a/requirements.devel.txt b/requirements.devel.txt index 2a613e0..758de15 100644 --- a/requirements.devel.txt +++ b/requirements.devel.txt @@ -1,9 +1,13 @@ -r requirements.txt +anyio==4.3.0 + pytest==8.1.1 pytest-cov==4.1.0 -anyio==4.3.0 +pyfakefs==5.3.5 +# Linters isort[colors]==5.13.2 black==24.3.0 pylint==3.1.0 +bandit[toml]==1.7.8