diff --git a/.github/linters/.flake8 b/.github/linters/.flake8 new file mode 100644 index 0000000..73c76f2 --- /dev/null +++ b/.github/linters/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 150 diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json new file mode 100644 index 0000000..225b930 --- /dev/null +++ b/.github/linters/.jscpd.json @@ -0,0 +1,7 @@ +{ + "threshold": 25, + "ignore": [ + "test*" + ], + "absolute": true +} \ No newline at end of file diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml new file mode 100644 index 0000000..c796544 --- /dev/null +++ b/.github/linters/.markdown-lint.yml @@ -0,0 +1,26 @@ +--- +########################### +########################### +## Markdown Linter rules ## +########################### +########################### + +# Linter rules doc: +# - https://github.com/DavidAnson/markdownlint +# +# Note: +# To comment out a single error: +# +# any violations you want +# +# + +############### +# Rules by id # +############### +# line length +MD013: false +# singe h1 +MD025: false +# duplicate headers +MD024: false diff --git a/.github/linters/.mypy.ini b/.github/linters/.mypy.ini new file mode 100644 index 0000000..18c5ef9 --- /dev/null +++ b/.github/linters/.mypy.ini @@ -0,0 +1,4 @@ +# Global options: + +[mypy] +disable_error_code = attr-defined, import-not-found diff --git a/.github/linters/.shellcheckrc b/.github/linters/.shellcheckrc new file mode 100644 index 0000000..8d3c10c --- /dev/null +++ b/.github/linters/.shellcheckrc @@ -0,0 +1,2 @@ +# Don't suggest [ -n "$VAR" ] over [ ! -z "$VAR" ] +disable=SC2129 diff --git a/.github/linters/.textlintrc b/.github/linters/.textlintrc new file mode 100644 index 0000000..58e4ba9 --- /dev/null +++ b/.github/linters/.textlintrc @@ -0,0 +1,10 @@ +{ + "filters": { + "comments": true + }, + "rules": { + "terminology": { + "severity": "warning" + } + } +} diff --git a/.github/linters/.yaml-lint.yml b/.github/linters/.yaml-lint.yml new file mode 100644 index 0000000..955950d --- /dev/null +++ b/.github/linters/.yaml-lint.yml @@ -0,0 +1,59 @@ +--- +########################################### +# These are the rules used for # +# linting all the yaml files in the stack # +# NOTE: # +# You can disable line with: # +# # yamllint disable-line # +########################################### +rules: + braces: + level: warning + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: 1 + max-spaces-inside-empty: 5 + brackets: + level: warning + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: 1 + max-spaces-inside-empty: 5 + colons: + level: warning + max-spaces-before: 0 + max-spaces-after: 1 + commas: + level: warning + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: disable + comments-indentation: disable + document-end: disable + document-start: + level: warning + present: true + empty-lines: + level: warning + max: 2 + max-start: 0 + max-end: 0 + hyphens: + level: warning + max-spaces-after: 1 + indentation: + level: warning + spaces: consistent + indent-sequences: true + check-multi-line-strings: false + key-duplicates: enable + line-length: + level: warning + max: 1024 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true + new-line-at-end-of-file: disable + new-lines: + type: unix + trailing-spaces: disable diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 02bea8a..beb28c0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,3 +1,4 @@ +# Pull Request ## Proposed Changes @@ -6,9 +7,11 @@ ## Readiness Checklist ### Author/Contributor + - [ ] If documentation is needed for this change, has that been included in this pull request - [ ] run `make lint` and fix any issues that you have introduced - [ ] run `make test` and ensure you have test coverage for the lines you are introducing ### Reviewer + - [ ] Label as either `bug`, `documentation`, `enhancement`, `infrastructure`, or `breaking` diff --git a/.github/workflows/contributors_report.yaml b/.github/workflows/contributors_report.yaml index 05f9147..8106a9d 100644 --- a/.github/workflows/contributors_report.yaml +++ b/.github/workflows/contributors_report.yaml @@ -1,3 +1,4 @@ +--- name: Monthly contributor report on: workflow_dispatch: @@ -13,33 +14,32 @@ jobs: runs-on: ubuntu-latest steps: - - - name: Get dates for last month - shell: bash - run: | - # Calculate the first day of the previous month - start_date=$(date -d "last month" +%Y-%m-01) - - # Calculate the last day of the previous month - end_date=$(date -d "$start_date +1 month -1 day" +%Y-%m-%d) - - #Set an environment variable with the date range - echo "START_DATE=$start_date" >> "$GITHUB_ENV" - echo "END_DATE=$end_date" >> "$GITHUB_ENV" - - - name: Run contributor action - uses: github/contributors@v1 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - START_DATE: ${{ env.START_DATE }} - END_DATE: ${{ env.END_DATE }} - REPOSITORY: github/contributors - SPONSOR_INFO: "true" - - - name: Create issue - uses: peter-evans/create-issue-from-file@v4 - with: - title: Monthly contributor report - token: ${{ secrets.GITHUB_TOKEN }} - content-filepath: ./contributors.md - assignees: zkoppert + - name: Get dates for last month + shell: bash + run: | + # Calculate the first day of the previous month + start_date=$(date -d "last month" +%Y-%m-01) + + # Calculate the last day of the previous month + end_date=$(date -d "$start_date +1 month -1 day" +%Y-%m-%d) + + #Set an environment variable with the date range + echo "START_DATE=$start_date" >> "$GITHUB_ENV" + echo "END_DATE=$end_date" >> "$GITHUB_ENV" + + - name: Run contributor action + uses: github/contributors@v1 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + START_DATE: ${{ env.START_DATE }} + END_DATE: ${{ env.END_DATE }} + REPOSITORY: github/contributors + SPONSOR_INFO: "true" + + - name: Create issue + uses: peter-evans/create-issue-from-file@v4 + with: + title: Monthly contributor report + token: ${{ secrets.GITHUB_TOKEN }} + content-filepath: ./contributors.md + assignees: zkoppert diff --git a/.github/workflows/docker-ci.yml b/.github/workflows/docker-ci.yml index 6d19a9f..14323bc 100644 --- a/.github/workflows/docker-ci.yml +++ b/.github/workflows/docker-ci.yml @@ -1,10 +1,11 @@ +--- name: Docker Image CI on: push: - branches: [ main ] + branches: main pull_request: - branches: [ main ] + branches: main jobs: @@ -13,6 +14,6 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Build the Docker image - run: docker build . --file Dockerfile --platform linux/amd64 + - uses: actions/checkout@v4 + - name: Build the Docker image + run: docker build . --file Dockerfile --platform linux/amd64 diff --git a/.github/workflows/major-version-updater.yml b/.github/workflows/major-version-updater.yml index 540271a..f8c36a4 100644 --- a/.github/workflows/major-version-updater.yml +++ b/.github/workflows/major-version-updater.yml @@ -3,7 +3,7 @@ name: Major Version Updater # Whenever a new release is made, push a major version tag on: release: - types: [ published ] + types: published jobs: update-major-version-tag: @@ -14,13 +14,15 @@ jobs: - name: version id: version + shell: bash run: | - tag=${GITHUB_REF/refs\/tags\//} - version=${tag#v} - major=${version%%.*} - echo "tag=${tag}" >> "$GITHUB_OUTPUT" - echo "version=${version}" >> "$GITHUB_OUTPUT" - echo "major=${major}" >> "$GITHUB_OUTPUT" + # shellcheck disable=all + tag=${GITHUB_REF/refs\/tags\//}; + version=${tag#v} ; + major=${version%%.*} ; + echo "tag=${tag}" >> "$GITHUB_OUTPUT" ; + echo "version=${version}" >> "$GITHUB_OUTPUT" ; + echo "major=${major}" >> "$GITHUB_OUTPUT" ; - name: force update major tag run: | diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 1a426a4..0555c39 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -1,3 +1,4 @@ +--- # This workflow will install Python dependencies, run tests and lint with a variety of Python versions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions @@ -5,9 +6,9 @@ name: Python package on: push: - branches: [ main ] + branches: main pull_request: - branches: [ main ] + branches: main jobs: build: @@ -15,22 +16,22 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.9, 3.11, 3.12] + python-version: [3.11, 3.12] steps: - - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install flake8 pylint pytest pytest-cov - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Lint with flake8 and pylint - run: | - make lint - - name: Test with pytest - run: | - make test + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pylint pytest pytest-cov + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 and pylint + run: | + make lint + - name: Test with pytest + run: | + make test diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 5b27fd6..6accb46 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -1,3 +1,4 @@ +--- name: Release Drafter on: diff --git a/.github/workflows/super-linter.yaml b/.github/workflows/super-linter.yaml new file mode 100644 index 0000000..e49ed86 --- /dev/null +++ b/.github/workflows/super-linter.yaml @@ -0,0 +1,56 @@ +--- +################################# +################################# +## Super Linter GitHub Actions ## +################################# +################################# +name: Lint Code Base + +############################# +# Start the job on all push # +############################# +on: + pull_request: + branches: main + +############### +# Set the Job # +############### +jobs: + build: + # Name the Job + name: Lint Code Base + # Set the agent to run on + runs-on: ubuntu-latest + + ############################################ + # Grant status permission for MULTI_STATUS # + ############################################ + permissions: + contents: read + packages: read + statuses: write + + ################## + # Load all steps # + ################## + steps: + ########################## + # Checkout the code base # + ########################## + - name: Checkout Code + uses: actions/checkout@v4 + with: + # Full git history is needed to get a proper + # list of changed files within `super-linter` + fetch-depth: 0 + + ################################ + # Run Linter against code base # + ################################ + - name: Lint Code Base + uses: super-linter/super-linter@v5 + env: + DEFAULT_BRANCH: main + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_ACTIONS_COMMAND_ARGS: -shellcheck= diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6842b4..8cb8fdf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,8 +29,9 @@ We will then take care of the issue as soon as possible. ## I Want To Contribute -> ### Legal Notice -> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. +### Legal Notice + +When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. ## Reporting Bugs @@ -92,6 +93,7 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/github To release a new version, maintainers are to release new versions following semantic versioning and via GitHub Releases. Once the code is ready to release please do the following + 1. Create a [GitHub release](https://github.com/github/contributors/releases) based off the current draft and review release notes 2. Ensure that the versioning is correct given the content of the release 3. Check the box to release it to the GitHub Marketplace diff --git a/Dockerfile b/Dockerfile index fb819fb..fab3fa8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ COPY requirements.txt *.py /action/workspace/ RUN python3 -m pip install --no-cache-dir -r requirements.txt \ && apt-get -y update \ - && apt-get -y install --no-install-recommends git \ + && apt-get -y install --no-install-recommends git-all=1:2.39.2-1.1 \ && rm -rf /var/lib/apt/lists/* CMD ["/action/workspace/contributors.py"] diff --git a/README.md b/README.md index e68f471..6d3c785 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Find out more in the [GitHub API documentation](https://docs.github.com/en/rest/ 1. Create a repository to host this GitHub Action or select an existing repository. 1. Select a best fit workflow file from the [examples below](#example-workflows). 1. Copy that example into your repository (from step 1) and into the proper directory for GitHub Actions: `.github/workflows/` directory with the file extension `.yml` (ie. `.github/workflows/contributors.yml`) -1. Edit the values (`ORGANIZATION`, `REPOSITORY`, `START_DATE`, `END_DATE`) from the sample workflow with your information. If no start and end date are supplied, the action will consider the entire repo history and be unable to determine if contributors are new or returning. If running on a whole organization then no repository is needed. If running the action on just one repository or a list of repositories, then no organization is needed. +1. Edit the values (`ORGANIZATION`, `REPOSITORY`, `START_DATE`, `END_DATE`) from the sample workflow with your information. If no start and end date are supplied, the action will consider the entire repository history and be unable to determine if contributors are new or returning. If running on a whole organization then no repository is needed. If running the action on just one repository or a list of repositories, then no organization is needed. 1. Also edit the value for `GH_ENTERPRISE_URL` if you are using a GitHub Server and not using github.com. For github.com users, don't put anything in here. 1. If you are running this action on an organization or repository other than the one where the workflow file is going to be, then update the value of `GH_TOKEN`. Do this by creating a [GitHub API token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) with permissions to read the repository/organization and write issues. Then take the value of the API token you just created, and [create a repository secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets) where the name of the secret is `GH_TOKEN` and the value of the secret the API token. Then finally update the workflow file to use that repository secret by changing `GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}` to `GH_TOKEN: ${{ secrets.GH_TOKEN }}`. The name of the secret can really be anything. It just needs to match between when you create the secret name and when you refer to it in the workflow file. 1. If you want the resulting issue with the output to appear in a different repository other than the one the workflow file runs in, update the line `token: ${{ secrets.GITHUB_TOKEN }}` with your own GitHub API token stored as a repository secret. This process is the same as described in the step above. More info on creating secrets can be found [here](https://docs.github.com/en/actions/security-guides/encrypted-secrets). @@ -73,39 +73,38 @@ jobs: runs-on: ubuntu-latest steps: - - - name: Get dates for last month - shell: bash - run: | - # Calculate the first day of the previous month - start_date=$(date -d "last month" +%Y-%m-01) - - # Calculate the last day of the previous month - end_date=$(date -d "$start_date +1 month -1 day" +%Y-%m-%d) - - #Set an environment variable with the date range - echo "START_DATE=$start_date" >> "$GITHUB_ENV" - echo "END_DATE=$end_date" >> "$GITHUB_ENV" - - - name: Run contributor action - uses: github/contributors@v1 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - START_DATE: ${{ env.START_DATE }} - END_DATE: ${{ env.END_DATE }} - ORGANIZATION: - SPONSOR_INFO: "true" - - - name: Create issue - uses: peter-evans/create-issue-from-file@v4 - with: - title: Monthly contributor report - token: ${{ secrets.GITHUB_TOKEN }} - content-filepath: ./contributors.md - assignees: + - name: Get dates for last month + shell: bash + run: | + # Calculate the first day of the previous month + start_date=$(date -d "last month" +%Y-%m-01) + + # Calculate the last day of the previous month + end_date=$(date -d "$start_date +1 month -1 day" +%Y-%m-%d) + + #Set an environment variable with the date range + echo "START_DATE=$start_date" >> "$GITHUB_ENV" + echo "END_DATE=$end_date" >> "$GITHUB_ENV" + + - name: Run contributor action + uses: github/contributors@v1 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + START_DATE: ${{ env.START_DATE }} + END_DATE: ${{ env.END_DATE }} + ORGANIZATION: + SPONSOR_INFO: "true" + + - name: Create issue + uses: peter-evans/create-issue-from-file@v4 + with: + title: Monthly contributor report + token: ${{ secrets.GITHUB_TOKEN }} + content-filepath: ./contributors.md + assignees: ``` -## Example markdown output with `start_date` and `end_date` supplied +## Example Markdown output with `start_date` and `end_date` supplied # Contributors @@ -120,7 +119,7 @@ jobs: | --- | --- | --- | --- | | zkoppert | 143 | False | [super-linter/super-linter](https://github.com/super-linter/super-linter/commits?author=zkoppert&since=2021-01-01&until=2023-10-10) | -## Example markdown output with no dates supplied +## Example Markdown output with no dates supplied # Contributors @@ -138,7 +137,7 @@ jobs: 1. Make sure you have at least Python3.11 installed 1. Copy `.env-example` to `.env` -1. Fill out the `.env` file with a _token_ from a user that has access to the organization to scan (listed below). Tokens should have at least read:org access for organization scanning and read:repo for repository scanning. +1. Fill out the `.env` file with a _token_ from a user that has access to the organization to scan (listed below). Tokens should have at least read:org access for organization scanning and read:repository for repository scanning. 1. Fill out the `.env` file with the configuration parameters you want to use 1. `pip3 install -r requirements.txt` 1. Run `python3 ./contributors.py`, which will output everything in the terminal diff --git a/contributor_stats.py b/contributor_stats.py index 66220f2..1d7b1db 100644 --- a/contributor_stats.py +++ b/contributor_stats.py @@ -12,6 +12,8 @@ # ] +from typing import List + import requests @@ -99,7 +101,7 @@ def merge_contributors(contributors: list) -> list: Returns: merged_contributors (list): A list of ContributorStats objects with no duplicate usernames """ - merged_contributors = [] + merged_contributors: List[ContributorStats] = [] for contributor_list in contributors: for contributor in contributor_list: # if the contributor is already in the merged list, merge their relavent attributes diff --git a/contributors.py b/contributors.py index 02ddc56..0084e07 100644 --- a/contributors.py +++ b/contributors.py @@ -2,9 +2,10 @@ """This file contains the main() and other functions needed to get contributor information from the organization or repository""" from typing import List -import env + import auth import contributor_stats +import env import markdown diff --git a/env.py b/env.py index 3127930..faca75a 100644 --- a/env.py +++ b/env.py @@ -2,10 +2,14 @@ import os from os.path import dirname, join +from typing import Any + from dotenv import load_dotenv -def get_env_vars() -> tuple[str, str, str, str, str, str, str]: +def get_env_vars() -> ( + tuple[str | None, list[str], str, str, str | None, str | None, str | Any] +): """ Get the environment variables for use in the action. @@ -53,7 +57,8 @@ def get_env_vars() -> tuple[str, str, str, str, str, str, str]: sponsor_info = os.getenv("SPONSOR_INFO") # make sure that sponsor_string is a boolean - sponsor_info = sponsor_info.lower().strip() + if sponsor_info: + sponsor_info = sponsor_info.lower().strip() if sponsor_info not in ["true", "false", ""]: raise ValueError( "SPONSOR_INFO environment variable not a boolean. ie. True or False or blank" diff --git a/test_auth.py b/test_auth.py index 3047e06..9c8786c 100644 --- a/test_auth.py +++ b/test_auth.py @@ -1,6 +1,7 @@ """Test cases for the auth module.""" import unittest from unittest.mock import patch + import auth diff --git a/test_contributor_stats.py b/test_contributor_stats.py index 9175fa9..adcd0a5 100644 --- a/test_contributor_stats.py +++ b/test_contributor_stats.py @@ -1,6 +1,7 @@ """This module contains the tests for the ContributorStats class.""" import unittest + from contributor_stats import ContributorStats, is_new_contributor, merge_contributors diff --git a/test_contributors.py b/test_contributors.py index 59ec71f..ab75d0c 100644 --- a/test_contributors.py +++ b/test_contributors.py @@ -1,9 +1,10 @@ """This module contains the tests for the contributors.py module""" import unittest -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch + from contributor_stats import ContributorStats -from contributors import get_contributors, get_all_contributors +from contributors import get_all_contributors, get_contributors class TestContributors(unittest.TestCase): diff --git a/test_env.py b/test_env.py index b7fc8ef..2b2f450 100644 --- a/test_env.py +++ b/test_env.py @@ -2,6 +2,7 @@ import unittest from unittest.mock import patch + import env diff --git a/test_markdown.py b/test_markdown.py index 5cdff42..95bccd1 100644 --- a/test_markdown.py +++ b/test_markdown.py @@ -1,8 +1,9 @@ """This is the test module for the markdown module""" import unittest -from unittest.mock import patch, mock_open -from markdown import write_to_markdown +from unittest.mock import mock_open, patch + import contributor_stats +from markdown import write_to_markdown class TestMarkdown(unittest.TestCase):