From 0c2e973a28cc6a0a357ce4107215bbad3c3c8e61 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 23 Sep 2020 22:40:38 +0100 Subject: [PATCH] feat: initial commit --- .github/dependabot.yml | 15 +++ .github/workflows/example.yml | 15 +++ .github/workflows/pre-commit-autoupdate.yml | 32 +++++ .github/workflows/semantic-release.yml | 25 ++++ .github/workflows/test.yml | 38 ++++++ .gitignore | 129 ++++++++++++++++++++ .pre-commit-config.yaml | 36 ++++++ Dockerfile | 16 +++ LICENSE | 21 ++++ README.md | 24 ++++ action.yml | 35 ++++++ commitlint.config.js | 6 + requirements.in | 6 + requirements.txt | 36 ++++++ setup.cfg | 18 +++ src/__init__.py | 0 src/app.py | 94 ++++++++++++++ 17 files changed, 546 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/example.yml create mode 100644 .github/workflows/pre-commit-autoupdate.yml create mode 100644 .github/workflows/semantic-release.yml create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 action.yml create mode 100644 commitlint.config.js create mode 100644 requirements.in create mode 100644 requirements.txt create mode 100644 setup.cfg create mode 100644 src/__init__.py create mode 100644 src/app.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..d4f5c62 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/example.yml b/.github/workflows/example.yml new file mode 100644 index 0000000..e13702b --- /dev/null +++ b/.github/workflows/example.yml @@ -0,0 +1,15 @@ +name: Example + +on: + schedule: + - cron: "0 0 * 10 *" + workflow_dispatch: + +jobs: + example: + runs-on: ubuntu-latest + + steps: + - uses: browniebroke/hacktoberfest-labeler-action@main + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml new file mode 100644 index 0000000..56c9ad7 --- /dev/null +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -0,0 +1,32 @@ +# Run pre-commit autoupdate every day at midnight +# and create a pull request if any changes + + +name: Pre-commit auto-update + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + auto-update: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.3.2 + - name: Set up Python + uses: actions/setup-python@v2.1.2 + with: + python-version: 3.8 + - name: Install pre-commit + run: pip install pre-commit + - name: Run pre-commit autoupdate + run: pre-commit autoupdate + - name: Create Pull Request + uses: peter-evans/create-pull-request@v2 + with: + token: ${{ secrets.CPR_GITHUB_TOKEN }} + branch: update/pre-commit-autoupdate + title: Update pre-commit hooks + commit-message: 'chore: update pre-commit hooks' + body: Update versions of tools in pre-commit configs to latest version. + labels: dependencies diff --git a/.github/workflows/semantic-release.yml b/.github/workflows/semantic-release.yml new file mode 100644 index 0000000..4f2db6c --- /dev/null +++ b/.github/workflows/semantic-release.yml @@ -0,0 +1,25 @@ +name: Semantic Release + +on: + workflow_dispatch: + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2.3.2 + with: + fetch-depth: 0 + + # Run semantic release: + # - Update CHANGELOG.md + # - Update version in code + # - Create git tag + # - Create Github release + # - Publish to PyPI + - name: Python Semantic Release + uses: browniebroke/python-semantic-release@feat/changelog-md-file + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + pypi_token: ${{ secrets.PYPI_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..9e600cc --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: Test + +on: + push: + branches: + - main + pull_request: + +jobs: + test: + strategy: + fail-fast: false + matrix: + script: + - flake8 --config=setup.cfg + - black --check . + - pyupgrade --py3-plus `find . -name "*.py"` + - isort -c -v . + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.3.2 + - name: Set up Python + uses: actions/setup-python@v2.1.2 + with: + python-version: 3.8 + - name: Install Dependencies + run: | + python -m pip install -U pip + pip install -r requirements.txt + - run: ${{ matrix.script }} + + commitlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.3.2 + with: + fetch-depth: 0 + - uses: wagoid/commitlint-github-action@v2.1.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6e4761 --- /dev/null +++ b/.gitignore @@ -0,0 +1,129 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..40cb490 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,36 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook + rev: v3.0.0 + hooks: + - id: commitlint + stages: [ commit-msg ] + additional_dependencies: ['@commitlint/config-conventional'] + - repo: https://github.com/asottile/pyupgrade + rev: v2.7.2 + hooks: + - id: pyupgrade + args: [--py3-plus] + - repo: https://github.com/PyCQA/isort + rev: 5.5.3 + hooks: + - id: isort + - repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + # disabled due to bump2commit compatibility + # - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-json + - id: debug-statements + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.3 + hooks: + - id: flake8 + args: [--config=setup.cfg] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b80ca75 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.8-slim + +# Create app directory +WORKDIR /app + +# Copy requiremnts file +COPY requirements.txt requirements.txt + +# Install dependencies +RUN pip install -r requirements.txt + +# Copies source code +COPY src /app + +# Run your code +ENTRYPOINT ["python", "/app/app.py"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f63873e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Bruno Alla + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fb54a44 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Hacktoberfest Labeler Action + +A Github Action to add labels based on other labels. + +## Usage + +A workflow you can run during the month of October to add the `hacktoberfest` label to all the issues flagged as `good first issue`: + +```yaml +name: Hacktoberfest + +on: + schedule: + - cron: "0 0 * 10 *" + +jobs: + example: + runs-on: ubuntu-latest + + steps: + - uses: browniebroke/hacktoberfest-labeler-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} +``` diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..58a5747 --- /dev/null +++ b/action.yml @@ -0,0 +1,35 @@ +name: 'Hacktoberfest Labeler Action' +description: 'A Github Action to label issues for Hacktoberfest' +branding: + icon: tag + color: orange + +### Action inputs (see also args below) +inputs: + github_token: + description: 'Github token to use' + required: true + edit_label_name: + description: 'The label name to edit' + required: false + default: 'hacktoberfest' + edit_label_color: + description: 'The label color' + required: false + default: 'ffa663' + edit_label_description: + description: 'The label description' + required: false + default: 'Good issues for Hacktoberfest' + filter_label: + description: 'The label to use for filtering issues' + required: false + default: 'good first issue' + revert: + description: 'If the true the label will be removed instead of added' + required: false + default: '0' + +runs: + using: 'docker' + image: 'Dockerfile' diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..21f057f --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,6 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + 'body-max-line-length': [0, "always", Infinity], + }, +}; diff --git a/requirements.in b/requirements.in new file mode 100644 index 0000000..a2b6d7e --- /dev/null +++ b/requirements.in @@ -0,0 +1,6 @@ +PyGithub +black +environs +flake8 +isort +pyupgrade diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..092ef82 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,36 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile +# +appdirs==1.4.4 # via black +black==20.8b1 # via -r requirements.in +certifi==2020.6.20 # via requests +chardet==3.0.4 # via requests +click==7.1.2 # via black +deprecated==1.2.10 # via pygithub +environs==8.0.0 # via -r requirements.in +flake8==3.8.3 # via -r requirements.in +idna==2.10 # via requests +importlib-metadata==2.0.0 # via flake8 +isort==5.5.3 # via -r requirements.in +marshmallow==3.8.0 # via environs +mccabe==0.6.1 # via flake8 +mypy-extensions==0.4.3 # via black +pathspec==0.8.0 # via black +pycodestyle==2.6.0 # via flake8 +pyflakes==2.2.0 # via flake8 +pygithub==1.53 # via -r requirements.in +pyjwt==1.7.1 # via pygithub +python-dotenv==0.14.0 # via environs +pyupgrade==2.7.2 # via -r requirements.in +regex==2020.7.14 # via black +requests==2.24.0 # via pygithub +tokenize-rt==4.0.0 # via pyupgrade +toml==0.10.1 # via black +typed-ast==1.4.1 # via black +typing-extensions==3.7.4.3 # via black +urllib3==1.25.10 # via requests +wrapt==1.12.1 # via deprecated +zipp==3.2.0 # via importlib-metadata diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..82d8140 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,18 @@ +[semantic_release] +branch = main +version_variable = src/__init__.py:__version__ +upload_to_pypi = false +upload_to_release = false + +[flake8] +max-line-length = 88 + +[aliases] +test = pytest + +[tool:pytest] +addopts = -v -Wdefault --cov=src + +[tool:isort] +profile = black +known_first_party = tests,src diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..7544926 --- /dev/null +++ b/src/app.py @@ -0,0 +1,94 @@ +from typing import List + +from environs import Env +from github import Github, UnknownObjectException +from github.Label import Label +from github.Repository import Repository + + +def main( + github_token: str, + github_repository: str, + filter_label_names: List[str], + edit_label: str, + edit_label_color: str, + edit_label_description: str, + revert: bool = False, +) -> None: + """ + Script entry point. + + :param github_token: Github token. + :param github_repository: The owner and repository name (octocat/Hello-World). + :param filter_label_names: The label used to filter issues. + :param edit_label: The label to be edited. + :param edit_label_color: The color of label to be edited. + :param edit_label_description: The description label to be edited. + :param revert: Remove label instead of adding it. + """ + + gh = Github(login_or_token=github_token) + repo = gh.get_repo(github_repository) + if revert: + remove_label(repo, edit_label) + else: + label_to_add = get_or_create_label( + repo, + edit_label, + edit_label_color, + edit_label_description, + ) + add_label(repo, filter_label_names, label_to_add) + + +def remove_label(repo: Repository, edit_label: str): + """Remove label from all issues with it.""" + issues_list = repo.get_issues(state="open", labels=[edit_label]) + for issue in issues_list: + issue.remove_from_labels(edit_label) + + +def get_or_create_label( + repo: Repository, + edit_label: str, + edit_label_color: str, + edit_label_description: str, +) -> Label: + """Get the Label object or create it with given features.""" + try: + label_to_add = repo.get_label(edit_label) + except UnknownObjectException: + label_to_add = repo.create_label( + name=edit_label, + color=edit_label_color, + description=edit_label_description, + ) + return label_to_add + + +def add_label(repo: Repository, filter_label_names: List[str], label_to_add: Label): + """Add given label to all issues labeled with filter label.""" + issues_list = repo.get_issues(state="open", labels=filter_label_names) + for issue in issues_list: + issue.add_to_labels(label_to_add) + + +if __name__ == "__main__": + env = Env() + env.read_env() + gh_repository = env("GITHUB_REPOSITORY") + gh_token = env("INPUT_GITHUB_TOKEN") + input_label_name = env("INPUT_EDIT_LABEL_NAME") + input_label_color = env("INPUT_EDIT_LABEL_COLOR") + input_label_description = env("INPUT_EDIT_LABEL_DESCRIPTION") + input_filter_labels = env.list("INPUT_FILTER_LABEL") + input_revert = env.bool("INPUT_REVERT") + main( + gh_token, + gh_repository, + input_filter_labels, + input_label_name, + input_label_color, + input_label_description, + input_revert, + )