From c637519265a7b0561aa7cca9110ad2d4af31df05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=BB=E3=82=A4=E3=83=AB=20=C2=B7=20MJ=20Sabit?= <98249191+seyLu@users.noreply.github.com> Date: Fri, 29 Mar 2024 19:35:06 +0800 Subject: [PATCH] [PACKAGE-1] Package to PIP (#84) * chore: port changes from test-1 * feat: project rename * chore: clean up deps * feat: build with hatchling, build and upload dist using twine * feat: move deps to src * feat: add entry point * feat: add deps to pyproject.toml and resolve path shenanigans * feat: add deps to pyproject.toml and resolve path shenanigans * chore: change color of project name in version callback * docs: update README * docs: add basic usage * docs: add advanced usage * feat: rename PERSONAL_ACCESS_TOKEN to just TOKEN * docs: add short -h command on setup and dump * docs: update alt text of icon * chore: revert version to 0.0.1 * feat: add dynamic versioning * docs: update basic usage * chore: move build-system up * feat: add bandit to ruff config * chore: reformat * chore: reformat * feat: add pre-commit prettier * feat: add ruff config flake8-builtins * feat: add ruff config flake8-builtins * feat: add more ruff rules and add HTTPError request exception handling * feat (pre-commit): deprecate black in favor of ruff format * chore: remove unecessary main function * ci (lint): deprecate black in favor of ruff --- .editorconfig | 33 +--- .env.example | 2 +- .github/workflows/auto-update-pre-commit.yaml | 6 +- .github/workflows/codeql.yaml | 5 +- .github/workflows/lint.yaml | 10 +- .gitignore | 2 +- .pre-commit-config.yaml | 18 ++- README.md | 144 +++++++++++++----- ghlabel.md | 42 ++++- static/icons/labels.png => labels.png | Bin labels/affects_labels.yaml | 5 - labels/close_labels.yaml | 17 --- labels/needs_labels.yaml | 40 ----- labels/priority_labels.yaml | 8 - labels/state_labels.yaml | 10 -- labels/type_labels.yaml | 18 --- pyproject.toml | 74 ++++++--- requirements.txt | 32 ++-- src/ghlabel/__about__.py | 1 + {scripts => src/ghlabel}/__init__.py | 0 cli.py => src/ghlabel/cli.py | 32 ++-- logging.ini => src/ghlabel/logging.ini | 0 src/ghlabel/utils/__init__.py | 0 {scripts => src/ghlabel/utils}/dump_label.py | 28 ++-- .../ghlabel/utils}/setup_github_label.py | 140 ++++++++++------- {labels => src/labels}/_remove_labels.yaml | 0 src/labels/affects_labels.yaml | 5 + src/labels/close_labels.yaml | 18 +++ {labels => src/labels}/default_labels.yaml | 2 +- src/labels/needs_labels.yaml | 46 ++++++ src/labels/priority_labels.yaml | 8 + src/labels/state_labels.yaml | 11 ++ src/labels/type_labels.yaml | 19 +++ 33 files changed, 454 insertions(+), 322 deletions(-) rename static/icons/labels.png => labels.png (100%) delete mode 100644 labels/affects_labels.yaml delete mode 100644 labels/close_labels.yaml delete mode 100644 labels/needs_labels.yaml delete mode 100644 labels/priority_labels.yaml delete mode 100644 labels/state_labels.yaml delete mode 100644 labels/type_labels.yaml create mode 100644 src/ghlabel/__about__.py rename {scripts => src/ghlabel}/__init__.py (100%) rename cli.py => src/ghlabel/cli.py (89%) rename logging.ini => src/ghlabel/logging.ini (100%) create mode 100644 src/ghlabel/utils/__init__.py rename {scripts => src/ghlabel/utils}/dump_label.py (93%) rename {scripts => src/ghlabel/utils}/setup_github_label.py (74%) rename {labels => src/labels}/_remove_labels.yaml (100%) create mode 100644 src/labels/affects_labels.yaml create mode 100644 src/labels/close_labels.yaml rename {labels => src/labels}/default_labels.yaml (75%) create mode 100644 src/labels/needs_labels.yaml create mode 100644 src/labels/priority_labels.yaml create mode 100644 src/labels/state_labels.yaml create mode 100644 src/labels/type_labels.yaml diff --git a/.editorconfig b/.editorconfig index e8dc39f..1ed144c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,38 +1,15 @@ -# EditorConfig is awesome: https://EditorConfig.org - -# top-most EditorConfig file root = true -# Unix-style newlines with a newline ending every file [*] end_of_line = lf -insert_final_newline = true - -# Matches multiple files with brace expansion notation -# Set default charset -[*.{js,py}] +trim_trailing_whitespace = true charset = utf-8 - -# 2 space indentation -[*.json, *.html] -indent_style = space -indent_size = 2 - -# 4 space indentation -[*.py] indent_style = space indent_size = 4 -# Tab indentation (no size specified) -[Makefile] -indent_style = tab - -# Indentation override for all JS under lib directory -[lib/**.js] -indent_style = space -indent_size = 2 +[!**/labels] +insert_final_newline = true +max_line_length = 88 -# Matches the exact files either package.json or .travis.yml -[{package.json,.travis.yml}] -indent_style = space +[*.{yaml,yml,json,toml}] indent_size = 2 diff --git a/.env.example b/.env.example index f3b8553..a5b4d66 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,3 @@ -GITHUB_PERSONAL_ACCESS_TOKEN= +GITHUB_TOKEN= GITHUB_REPO_OWNER= GITHUB_REPO_NAME= diff --git a/.github/workflows/auto-update-pre-commit.yaml b/.github/workflows/auto-update-pre-commit.yaml index 417dfc5..dd07882 100644 --- a/.github/workflows/auto-update-pre-commit.yaml +++ b/.github/workflows/auto-update-pre-commit.yaml @@ -3,13 +3,13 @@ run-name: Updating Pre-commit Configuration on ${{ github.repository }} on: schedule: - - cron: '0 0 * * 0' # Run every Sunday at midnight UTC + - cron: "0 0 * * 0" # Run every Sunday at midnight UTC workflow_dispatch: inputs: logLevel: - description: 'Log level' + description: "Log level" required: true - default: 'warning' + default: "warning" jobs: auto_update: diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 75fccea..214e36a 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -13,7 +13,7 @@ name: "CodeQL" on: push: - branches: [ "main" ] + branches: ["main"] jobs: analyze: @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'python' ] + language: ["python"] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] # Use only 'java' to analyze code written in Java, Kotlin or both # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both @@ -60,7 +60,6 @@ jobs: # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 9451525..d801680 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - python-version: ["3.11"] + python-version: ["3.11", "3.12"] steps: - name: Harden Runner @@ -25,7 +25,7 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3.5.3 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.5.3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v4.7.0 @@ -40,19 +40,17 @@ jobs: run: > pip install ruff - black mypy - name: Ruff Linter run: ruff check . --diff - - name: Black Formatter - run: black . --diff --check + - name: Ruff Formatter + run: ruff format . - name: Mypy Type Checker run: > mypy . --install-types --non-interactive - --check-untyped-defs --ignore-missing-imports diff --git a/.gitignore b/.gitignore index 4e9e45c..4162d20 100644 --- a/.gitignore +++ b/.gitignore @@ -166,4 +166,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ccb62b3..4fc3928 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,14 +5,22 @@ repos: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace + - id: check-merge-conflict + - id: debug-statements + - id: fix-byte-order-marker + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v4.0.0-alpha.8 + hooks: + - id: prettier + types_or: [yaml, toml, markdown] + additional_dependencies: + - prettier@3.0.0 + - prettier-plugin-toml@1.0.1 - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.3.4 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - - - repo: https://github.com/psf/black - rev: 24.3.0 - hooks: - - id: black + - id: ruff-format diff --git a/README.md b/README.md index d5b6af6..e6027a5 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,100 @@
- Setup Github Label CLI Icon - + ghlabel icon +

ghlabel

CLI tool to help setup Github Labels from a yaml/json config file.

- Report Bug + Codeql badge + Lint badge + Python 3.11+ badge + PEP8 badge + MIT License +

+

+ Report Bug · - Request Feature + Request Feature · - Ask Question + Ask Question


-### Supported Python version +### Installation and Setup + +It is recommended to not install the CLI tool globally. + +#### 1. Create a new directory where you want the configuration to live ```bash -python==3.11 +mkdir ghlabel + +# this is where the labels config will be located +cd ghlabel ``` -
+#### 2. Create and activate virtual environment -### Setup +```bash +python -m venv .venv -#### 1. Clone the repo +# Linux/Mac +. .venv/bin/activate -```bash -git clone git@github.com:seyLu/setup-github-label-cli.git +# Windows +.venv\Scripts\activate.bat ``` -#### 2. Install dependencies - -##### Create a new virtual environment +#### 3. Install package ```bash -python -m venv .venv +pip install ghlabel + +# check if installed +ghlabel -v ``` -##### Activate the virtual environment +#### 4. Create `.env` and supply github credentials ```bash # Linux/Mac -. .venv/bin/activate +touch .env # Windows -.venv\Scripts\activate.bat +type nul > .env ``` -##### Install dependencies from requirements.txt +```bash +GITHUB_PERSONAL_ACCESS_TOKEN= +GITHUB_REPO_OWNER= +GITHUB_REPO_NAME= +``` + +
+ +### Basic Usage + +#### 1. Generate labels config ```bash -pip install -r requirements.txt +ghlabel dump ``` -#### 3. Create `.env` and supply github credentials +#### 2. Setup labels in GitHub ```bash -cp .env.example .env +ghlabel setup ``` -##### `.env.example` preview +
+ +For advanced usage, see: ```bash -GITHUB_PERSONAL_ACCESS_TOKEN= -GITHUB_REPO_OWNER= -GITHUB_REPO_NAME= +ghlabel -h ``` +

## :red_circle: `ghlabel` @@ -84,10 +112,15 @@ $ ghlabel [OPTIONS] COMMAND [ARGS]... ### :large_orange_diamond: Options: #### `--version`, `-v` + Show version and exit. + #### `--debug`, `-D` + Enable debug mode and show logs. -#### `--help` + +#### `--help`, `-h` + Show this message and exit.
@@ -95,8 +128,11 @@ Show this message and exit. ### Commands: #### `dump` + Generate starter labels config files. + #### `setup` + Add/Remove Github labels from config files.
@@ -116,14 +152,23 @@ $ ghlabel dump [OPTIONS] ### :large_orange_diamond: Options: #### `--new`, `-n` / `--keep-old-labels`, `-N` [default: new] + Deletes all files in labels dir. + #### `--dir`, `-d TEXT` [default: labels] + Specify the dir where to find labels. + #### `--ext`, `-e [json|yaml]` [default: yaml] + Label file extension. + #### `--app`, `-a [app|game|web]` [default: app] + App to determine label template. -#### `--help` + +#### `--help`, `-h` + Show this message and exit.
@@ -143,7 +188,9 @@ $ ghlabel setup [TOKEN] [REPO_OWNER] [REPO_NAME] [OPTIONS] ### :large_blue_diamond: Arguments: #### `TOKEN` [optional] + #### `REPO_OWNER` [optional] + #### `REPO_NAME` [optional]
@@ -151,16 +198,27 @@ $ ghlabel setup [TOKEN] [REPO_OWNER] [REPO_NAME] [OPTIONS] ### :large_orange_diamond: Options: #### `--dir`, `-d TEXT` [default: labels] + Specify the dir where to find labels. + #### `--strict`, `-s` / `--no-strict`, `-S` [default: no-strict] + Strictly mirror Github labels from labels config. + #### `--add-labels`, `-a TEXT` + Add more labels. + #### `--remove-labels`, `-r TEXT` + Remove more labels. -#### `--remove-all`, `-R [disable|enable|silent]` [default: disable] + +#### `--remove-all`, `-R [disable|enable|silent]` [default: disable] + Remove all Github labels. -#### `--help` + +#### `--help`, `-h` + Show this message and exit.
@@ -186,7 +244,7 @@ ghlabel setup -r "Type: Feature Request, Type: Bug" ```bash # -a [valid json string] # will be parsed as list[dict[str, str]] -ghlabel setup -a "[{'name': 'wontfix', 'color': '##ffffff'}, {'name': 'bug', 'color': '#d73a4a', 'description': 'Something isn't working'}]" +ghlabel setup -a "[{'name': 'wontfix', 'color': '#ffffff'}, {'name': 'bug', 'color': '#d73a4a', 'description': 'Something isn't working'}]" ```
@@ -201,41 +259,50 @@ ghlabel setup -a "[{'name': 'wontfix', 'color': '##ffffff'}, {'name': 'bug', 'co color: description: ``` + ```yaml # json [ - { - "name": , - "color": , - "description": - } + { + "name": , + "color": , + "description": , + }, ] ``` #### labels/affects_labels.yaml + ![Affects Labels Screenshot](static/images/affects_labels.png) #### labels/close_labels.yaml + ![Close Labels Screenshot](static/images/close_labels.png) #### labels/default_labels.yaml + ![Default Labels Screenshot](static/images/default_labels.png) #### labels/needs_labels.yaml + ![Needs Labels Screenshot](static/images/needs_labels.png) #### labels/priority_labels.yaml + ![Priority Labels Screenshot](static/images/priority_labels.png) #### labels/state_labels.yaml + ![State Labels Screenshot](static/images/state_labels.png) #### labels/type_labels.yaml + ![Type Labels Screenshot](static/images/type_labels.png) ### Removing labels -#### labels/_remove_labels.yaml +#### labels/\_remove_labels.yaml + ```yaml - bug - dependencies @@ -253,4 +320,5 @@ ghlabel setup -a "[{'name': 'wontfix', 'color': '##ffffff'}, {'name': 'bug', 'co ### [Optional] Game Dev Additional Labels #### labels/affects_labels.yaml + ![Game Dev Affects Labels Screenshot](static/images/game_dev/affects_labels.png) diff --git a/ghlabel.md b/ghlabel.md index de368d0..6638d02 100644 --- a/ghlabel.md +++ b/ghlabel.md @@ -1,5 +1,3 @@ - - ## :red_circle: `ghlabel` Setup Github Labels from a yaml/json config file. @@ -15,10 +13,15 @@ $ ghlabel [OPTIONS] COMMAND [ARGS]... ### :large_orange_diamond: Options: #### `--version`, `-v` + Show version and exit. + #### `--debug`, `-D` + Enable debug mode and show logs. -#### `--help` + +#### `--help`, `-h` + Show this message and exit.
@@ -26,8 +29,11 @@ Show this message and exit. ### Commands: #### `dump` + Generate starter labels config files. + #### `setup` + Add/Remove Github labels from config files.
@@ -47,14 +53,23 @@ $ ghlabel dump [OPTIONS] ### :large_orange_diamond: Options: #### `--new`, `-n` / `--keep-old-labels`, `-N` [default: new] + Deletes all files in labels dir. + #### `--dir`, `-d TEXT` [default: labels] + Specify the dir where to find labels. + #### `--ext`, `-e [json|yaml]` [default: yaml] + Label file extension. + #### `--app`, `-a [app|game|web]` [default: app] + App to determine label template. -#### `--help` + +#### `--help`, `-h` + Show this message and exit.
@@ -74,7 +89,9 @@ $ ghlabel setup [TOKEN] [REPO_OWNER] [REPO_NAME] [OPTIONS] ### :large_blue_diamond: Arguments: #### `TOKEN` [optional] + #### `REPO_OWNER` [optional] + #### `REPO_NAME` [optional]
@@ -82,16 +99,27 @@ $ ghlabel setup [TOKEN] [REPO_OWNER] [REPO_NAME] [OPTIONS] ### :large_orange_diamond: Options: #### `--dir`, `-d TEXT` [default: labels] + Specify the dir where to find labels. + #### `--strict`, `-s` / `--no-strict`, `-S` [default: no-strict] + Strictly mirror Github labels from labels config. + #### `--add-labels`, `-a TEXT` + Add more labels. + #### `--remove-labels`, `-r TEXT` + Remove more labels. -#### `--remove-all`, `-R [disable|enable|silent]` [default: disable] + +#### `--remove-all`, `-R [disable|enable|silent]` [default: disable] + Remove all Github labels. -#### `--help` + +#### `--help`, `-h` + Show this message and exit.
@@ -117,5 +145,5 @@ ghlabel setup -r "Type: Feature Request, Type: Bug" ```bash # -a [valid json string] # will be parsed as list[dict[str, str]] -ghlabel setup -a "[{'name': 'wontfix', 'color': '##ffffff'}, {'name': 'bug', 'color': '#d73a4a', 'description': 'Something isn't working'}]" +ghlabel setup -a "[{'name': 'wontfix', 'color': '#ffffff'}, {'name': 'bug', 'color': '#d73a4a', 'description': 'Something isn't working'}]" ``` diff --git a/static/icons/labels.png b/labels.png similarity index 100% rename from static/icons/labels.png rename to labels.png diff --git a/labels/affects_labels.yaml b/labels/affects_labels.yaml deleted file mode 100644 index 3f6f730..0000000 --- a/labels/affects_labels.yaml +++ /dev/null @@ -1,5 +0,0 @@ -- name: 'Affects: Infra' - color: '#fbbc9d' - description: Related to configuration, automation, CI, etc. -- name: 'Affects: Project Management' - color: '#fbbc9d' diff --git a/labels/close_labels.yaml b/labels/close_labels.yaml deleted file mode 100644 index 4a58220..0000000 --- a/labels/close_labels.yaml +++ /dev/null @@ -1,17 +0,0 @@ -- name: 'Close: Answered' - color: '#cdd1d5' -- name: 'Close: Backlog' - color: '#cdd1d5' - description: Issues are stale/expired; sent to backlog for later re-evaluation. -- name: 'Close: Duplicate' - color: '#cdd1d5' - description: This issue or pull request already exists (see comments for pointer - to it). -- name: 'Close: Not Actionable' - color: '#cdd1d5' -- name: 'Close: Not Reproducible' - color: '#cdd1d5' - description: Closed because we cannot reproduce the issue. -- name: 'Close: Will Not Fix' - color: '#cdd1d5' - description: Closed because we have decided not to address this (e.g. out of scope). diff --git a/labels/needs_labels.yaml b/labels/needs_labels.yaml deleted file mode 100644 index 58271f9..0000000 --- a/labels/needs_labels.yaml +++ /dev/null @@ -1,40 +0,0 @@ -- name: 'Needs: Breakdown' - color: '#0052cc' - description: This big issue needs a checklist or subissues to describe a breakdown - of work. -- name: 'Needs: Designs' - color: '#0052cc' -- name: 'Needs: Detail' - color: '#0052cc' - description: Submitter needs to provide more detail for this issue to be assessed - (see comments). -- name: 'Needs: Feedback' - color: '#0052cc' - description: A proposed feature or bug resolution needs feedback prior to forging - ahead. -- name: 'Needs: Help' - color: '#0052cc' - description: Issues, typically substantial ones, that need a dedicated developer - to take them on. -- name: 'Needs: Investigation' - color: '#0052cc' - description: This issue/PR needs a root-cause analysis to determine a solution. -- name: 'Needs: Response' - color: '#0052cc' - description: Issues which require feedback from staff members. -- name: 'Needs: Review' - color: '#0052cc' - description: This issue/PR needs to be reviewed in order to be closed or merged - (see comments) -- name: 'Needs: Revisiting' - color: '#0052cc' - description: Archived (usually noisy dependencies). -- name: 'Needs: Submitter Input' - color: '#0052cc' - description: Waiting on input from the creator of the issue/pr. -- name: 'Needs: Testing' - color: '#0052cc' -- name: 'Needs: Triage' - color: '#0052cc' - description: This issue needs triage. The team needs to decide who should own it, - what to do, by when. diff --git a/labels/priority_labels.yaml b/labels/priority_labels.yaml deleted file mode 100644 index 5e1980e..0000000 --- a/labels/priority_labels.yaml +++ /dev/null @@ -1,8 +0,0 @@ -- name: 'Priority: Critical' - color: '#7c0a02' -- name: 'Priority: High' - color: '#b22222' -- name: 'Priority: Medium' - color: '#ff8597' -- name: 'Priority: Low' - color: '#ffccc9' diff --git a/labels/state_labels.yaml b/labels/state_labels.yaml deleted file mode 100644 index 2378492..0000000 --- a/labels/state_labels.yaml +++ /dev/null @@ -1,10 +0,0 @@ -- name: 'State: Blocked' - color: '#e07bf9' - description: Work has stopped, waiting for something (Info, Dependent fix, etc. - See comments). -- name: 'State: In Review' - color: '#e07bf9' - description: This issue is waiting for review to finish. -- name: 'State: Work In Progress' - color: '#e07bf9' - description: This issue is being actively worked on. diff --git a/labels/type_labels.yaml b/labels/type_labels.yaml deleted file mode 100644 index 145fc34..0000000 --- a/labels/type_labels.yaml +++ /dev/null @@ -1,18 +0,0 @@ -- name: 'Type: Bug' - color: '#ff9900' - description: Something isn't working. -- name: 'Type: Documentation' - color: '#ff9900' - description: Improvements or additions to documentation. -- name: 'Type: Feature Request' - color: '#ff9900' - description: Issue describes a feature or enhancement we'd like to implement. -- name: 'Type: Question' - color: '#ff9900' - description: This issue doesn't require code. A question needs an answer. -- name: 'Type: Refactor/Clean-up' - color: '#ff9900' - description: Issues related to reorganization/clean-up of data or code (e.g. for - maintainability). -- name: 'Type: Suggestion' - color: '#ff9900' diff --git a/pyproject.toml b/pyproject.toml index 4c9e43c..22a88ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,45 +1,69 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + [project] -name = "python-template" -version = "0.0.1" -# Support Python 3.11. -requires-python = "==3.11" +name = "ghlabel" +requires-python = ">=3.11" license = "MIT" authors = [ - {name = "MJ Sabit (seyLu)", email = "98249191+seyLu@users.noreply.github.com"}, + { name = "MJ Sabit (seyLu)", email = "98249191+seyLu@users.noreply.github.com" }, +] +description = "CLI tool to help setup Github Labels from a yaml/json config file." +readme = "README.md" +dynamic = ["version"] +keywords = [ + "github-tool", + "github-labels", + "cli-tool", +] +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] +dependencies = [ + "typer[all]==0.9.0", + "requests==2.31.0", + "python-dotenv==1.0.0", + "PyYAML==6.0.1", ] +[project.urls] +Documentation = "https://github.com/seyLu/ghlabel#readme" +"Homepage" = "https://github.com/seyLu/ghlabel" +"Bug Tracker" = "https://github.com/seyLu/ghlabel/issues" + +[project.scripts] +ghlabel = "ghlabel.cli:app" + + +[tool.hatch.version] +path = "src/ghlabel/__about__.py" + -[tool.ruff] +[tool.ruff.lint] select = [ + "A", # flake8-builtins + "B", # flake8-bugbear + "S", # flake8-bandit "E", # Pyflakes "F", # Pycodestyle - "B", # flake8-bugbear - "I001" # isort + "I", # Isort + "PL", # Pylint + "RUF", # Ruff-specific rules ] - -# Never enforce `E501` (line length violations). ignore = ["E501"] -# Avoid trying to fix flake8-bugbear (`B`) violations. -unfixable = ["B"] - -# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`. -[tool.ruff.per-file-ignores] -"__init__.py" = ["E402", "F401"] -"path/to/file.py" = ["E402"] - - -[tool.isort] -# Automatically apply black to isort. -profile = "black" - [tool.mypy] strict = "True" implicit_reexport = "True" +check_untyped_defs = true +ignore_missing_imports = true # Exclude type checking on files/dirs exclude = [ - # TOML basic string (double-quotes, backslash and other characters need escaping) - # TOML literal string (single-quotes, no escaping necessary) + # TOML basic string (double-quotes, backslash and other characters need escaping) + # TOML literal string (single-quotes, no escaping necessary) ] diff --git a/requirements.txt b/requirements.txt index df6b721..8b6abfe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,24 +1,10 @@ -certifi==2023.11.17 -cfgv==3.4.0 -charset-normalizer==3.3.2 -click==8.1.7 -colorama==0.4.6 -distlib==0.3.8 -filelock==3.13.1 -identify==2.5.35 -idna==3.6 -markdown-it-py==3.0.0 -mdurl==0.1.2 -nodeenv==1.8.0 -platformdirs==4.1.0 -pre-commit==3.6.2 -Pygments==2.17.2 -python-dotenv==1.0.1 -PyYAML==6.0.1 +# core +typer[all]==0.9.0 requests==2.31.0 -rich==13.7.0 -shellingham==1.5.4 -typer==0.9.0 -typing_extensions==4.10.0 -urllib3==2.1.0 -virtualenv==20.25.0 +python-dotenv==1.0.0 +PyYAML==6.0.1 + +# packaging +hatchling==1.18.0 +build==1.0.3 +twine==4.0.2 diff --git a/src/ghlabel/__about__.py b/src/ghlabel/__about__.py new file mode 100644 index 0000000..f102a9c --- /dev/null +++ b/src/ghlabel/__about__.py @@ -0,0 +1 @@ +__version__ = "0.0.1" diff --git a/scripts/__init__.py b/src/ghlabel/__init__.py similarity index 100% rename from scripts/__init__.py rename to src/ghlabel/__init__.py diff --git a/cli.py b/src/ghlabel/cli.py similarity index 89% rename from cli.py rename to src/ghlabel/cli.py index 3dc7f06..2df6883 100644 --- a/cli.py +++ b/src/ghlabel/cli.py @@ -6,7 +6,6 @@ __github__ = "github.com/seyLu" __licence__ = "MIT" -__version__ = "0.0.1" __maintainer__ = "seyLu" __status__ = "Prototype" @@ -18,10 +17,12 @@ from typing import Annotated, Optional import typer +from rich import print as rich_print from rich.progress import Progress, SpinnerColumn, TextColumn -from scripts.dump_label import DumpLabel -from scripts.setup_github_label import GithubConfig, GithubLabel +from .__about__ import __version__ +from .utils.dump_label import DumpLabel +from .utils.setup_github_label import GithubConfig, GithubLabel def parse_remove_labels(labels: str | None) -> list[str] | None: @@ -38,7 +39,9 @@ def parse_add_labels(labels: str | None) -> list[dict[str, str]] | None: def version_callback(show_version: bool) -> None: if show_version: - print(f"{os.path.basename(__file__)} {__version__}") + rich_print( + f"\n[green]{os.path.basename(os.path.dirname(__file__))}[/green] {__version__}\n" + ) raise typer.Exit() @@ -59,15 +62,20 @@ class RemoveAllChoices(str, Enum): silent = "silent" -app = typer.Typer(add_completion=False) +app = typer.Typer( + add_completion=False, + context_settings={ + "help_option_names": ["-h", "--help"], + }, +) @app.command("setup", help="Add/Remove Github labels from config files.") # type: ignore[misc] -def setup_labels( +def setup_labels( # noqa: PLR0913 token: Annotated[ Optional[str], typer.Argument( - envvar="PERSONAL_ACCESS_TOKEN", + envvar="TOKEN", show_default=False, ), ] = None, @@ -85,7 +93,7 @@ def setup_labels( show_default=False, ), ] = None, - dir: Annotated[ + labels_dir: Annotated[ str, typer.Option( "--dir", @@ -133,14 +141,14 @@ def setup_labels( ) as progress: progress.add_task(description="[green]Fetching...", total=None) if token: - GithubConfig.set_PERSONAL_ACCESS_TOKEN(token) + GithubConfig.set_TOKEN(token) if repo_owner: GithubConfig.set_REPO_OWNER(repo_owner) if repo_name: GithubConfig.set_REPO_NAME(repo_name) github_config = GithubConfig() - github_label = GithubLabel(github_config=github_config, dir=dir) + github_label = GithubLabel(github_config=github_config, labels_dir=labels_dir) with Progress( SpinnerColumn(style="[magenta]"), @@ -176,7 +184,7 @@ def app_dump( help="Deletes all files in labels dir.", ), ] = True, - dir: Annotated[ + labels_dir: Annotated[ str, typer.Option( "--dir", @@ -210,7 +218,7 @@ def app_dump( ) as progress: progress.add_task(description="Dumping...", total=None) time.sleep(0.5) - DumpLabel.dump(dir=dir, new=new, ext=ext.value, app=app.value) + DumpLabel.dump(labels_dir=labels_dir, new=new, ext=ext.value, app=app.value) time.sleep(0.5) diff --git a/logging.ini b/src/ghlabel/logging.ini similarity index 100% rename from logging.ini rename to src/ghlabel/logging.ini diff --git a/src/ghlabel/utils/__init__.py b/src/ghlabel/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/dump_label.py b/src/ghlabel/utils/dump_label.py similarity index 93% rename from scripts/dump_label.py rename to src/ghlabel/utils/dump_label.py index 9a62ab0..25df2fa 100644 --- a/scripts/dump_label.py +++ b/src/ghlabel/utils/dump_label.py @@ -9,7 +9,6 @@ __github__ = "github.com/seyLu" __licence__ = "MIT" -__version__ = "0.0.1" __maintainer__ = "seyLu" __status__ = "Prototype" @@ -24,7 +23,7 @@ import yaml Path("logs").mkdir(exist_ok=True) -fileConfig("logging.ini") +fileConfig(os.path.join(os.path.dirname(__file__), "../logging.ini")) @dataclass(frozen=True) @@ -220,7 +219,8 @@ class Labels: @dataclass(frozen=True) class GameDevLabels(Labels): - AFFECTS: tuple[dict[str, str], ...] = Labels.AFFECTS + ( + AFFECTS: tuple[dict[str, str], ...] = ( + *Labels.AFFECTS, { "name": "Affects: Game Assets", "color": "#fbbc9d", @@ -266,23 +266,23 @@ class GameDevLabels(Labels): class DumpLabel: @staticmethod - def _init_dir(dir: str = "labels") -> None: - logging.info(f"Initializing {dir} dir.") - Path(dir).mkdir(exist_ok=True) - for filename in os.listdir(dir): - file_path: str = os.path.join(dir, filename) + def _init_labels_dir(labels_dir: str = "labels") -> None: + logging.info(f"Initializing {labels_dir} dir.") + Path(labels_dir).mkdir(exist_ok=True) + for filename in os.listdir(labels_dir): + file_path: str = os.path.join(labels_dir, filename) if os.path.isfile(file_path): os.remove(file_path) @staticmethod def dump( - dir: str = "labels", + labels_dir: str = "labels", new: bool = True, ext: str = "yaml", app: str = "app", ) -> None: if new: - DumpLabel._init_dir(dir) + DumpLabel._init_labels_dir(labels_dir) label_cls: Labels = LABELS_CLS_MAP.get(app, "app") # type: ignore[assignment] @@ -290,7 +290,7 @@ def dump( labels: tuple[dict[str, str], ...] | tuple[str, ...] = getattr( label_cls, field ) - label_file: str = os.path.join(dir, f"{field.lower()}_labels.{ext}") + label_file: str = os.path.join(labels_dir, f"{field.lower()}_labels.{ext}") with open(label_file, "w+") as f: logging.info(f"Dumping to {f.name}.") @@ -310,9 +310,5 @@ def dump( logging.info("Finished dumping of labels.") -def main() -> None: - DumpLabel.dump(new=True) - - if __name__ == "__main__": - main() + DumpLabel.dump(new=True) diff --git a/scripts/setup_github_label.py b/src/ghlabel/utils/setup_github_label.py similarity index 74% rename from scripts/setup_github_label.py rename to src/ghlabel/utils/setup_github_label.py index 75d6bda..0163a37 100644 --- a/scripts/setup_github_label.py +++ b/src/ghlabel/utils/setup_github_label.py @@ -9,7 +9,6 @@ __github__ = "github.com/seyLu" __licence__ = "MIT" -__version__ = "0.0.1" __maintainer__ = "seyLu" __status__ = "Prototype" @@ -24,11 +23,12 @@ import requests import yaml from dotenv import load_dotenv +from requests.exceptions import HTTPError, Timeout from requests.models import Response load_dotenv() Path("logs").mkdir(exist_ok=True) -fileConfig("logging.ini") +fileConfig(os.path.join(os.path.dirname(__file__), "../logging.ini")) def validate_env(env: str) -> str: @@ -40,13 +40,13 @@ def validate_env(env: str) -> str: class GithubConfig: - _PERSONAL_ACCESS_TOKEN: str = validate_env("GITHUB_PERSONAL_ACCESS_TOKEN") + _TOKEN: str = validate_env("GITHUB_TOKEN") _REPO_OWNER: str = validate_env("GITHUB_REPO_OWNER") _REPO_NAME: str = validate_env("GITHUB_REPO_NAME") @property - def PERSONAL_ACCESS_TOKEN(self) -> str: - return GithubConfig._PERSONAL_ACCESS_TOKEN + def TOKEN(self) -> str: + return GithubConfig._TOKEN @property def REPO_OWNER(self) -> str: @@ -57,8 +57,8 @@ def REPO_NAME(self) -> str: return GithubConfig._REPO_NAME @staticmethod - def set_PERSONAL_ACCESS_TOKEN(token: str) -> None: - GithubConfig._PERSONAL_ACCESS_TOKEN = token + def set_TOKEN(token: str) -> None: + GithubConfig._TOKEN = token @staticmethod def set_REPO_OWNER(repo_owner: str) -> None: @@ -73,18 +73,18 @@ class GithubLabel: def __init__( self, github_config: GithubConfig | None = None, - dir: str = "labels", + labels_dir: str = "labels", ) -> None: if github_config is None: github_config = GithubConfig() self._url: str = f"https://api.github.com/repos/{github_config.REPO_OWNER}/{github_config.REPO_NAME}/labels" self._headers: dict[str, str] = { - "Authorization": f"Bearer {github_config.PERSONAL_ACCESS_TOKEN}", + "Authorization": f"Bearer {github_config.TOKEN}", "Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", } - self._dir: str = dir + self._labels_dir: str = labels_dir self._github_labels: list[dict[str, str]] = self._fetch_github_labels( github_config ) @@ -97,8 +97,8 @@ def __init__( ) @property - def dir(self) -> str: - return self._dir + def labels_dir(self) -> str: + return self._labels_dir @property def url(self) -> str: @@ -135,15 +135,22 @@ def _fetch_github_labels(self, github_config: GithubConfig) -> list[dict[str, st while True: params: dict[str, int] = {"page": page, "per_page": per_page} logging.info(f"Fetching page {page}.") - res: Response = requests.get( - self.url, - headers=self.headers, - params=params, - ) - - if res.status_code != 200: + try: + res: Response = requests.get( + self.url, + headers=self.headers, + params=params, + timeout=10, + ) + res.raise_for_status() + except Timeout: logging.error( - f"Status {res.status_code}. Failed to fetch list of github labels" + "The site can't be reached, `github.com` took to long to respond. Try checking the connection." + ) + sys.exit() + except HTTPError: + logging.error( + f"Failed to fetch list of github labels. Check if token has permission to access `{github_config.REPO_OWNER}/{github_config.REPO_NAME}`." ) sys.exit() @@ -165,13 +172,13 @@ def _fetch_github_labels(self, github_config: GithubConfig) -> list[dict[str, st def _load_labels_from_config(self) -> list[dict[str, str]]: use_labels: list[dict[str, str]] = [] labels: list[dict[str, str]] = [] - files_in_dir: list[str] = [] + files_in_labels_dir: list[str] = [] try: - files_in_dir = os.listdir(self.dir) + files_in_labels_dir = os.listdir(self.labels_dir) except FileNotFoundError: logging.error( - f"No {self.dir} dir found. To solve this issue, first run `ghlabel dump`." + f"No {self.labels_dir} dir found. To solve this issue, first run `ghlabel dump`." ) sys.exit() @@ -179,13 +186,13 @@ def _load_labels_from_config(self) -> list[dict[str, str]]: filter( lambda f: (f.endswith(".yaml") or f.endswith(".yml")) and not f.startswith("_remove"), - files_in_dir, + files_in_labels_dir, ) ) json_filenames: list[str] = list( filter( lambda f: f.endswith(".json") and not f.startswith("_remove"), - files_in_dir, + files_in_labels_dir, ) ) @@ -208,7 +215,7 @@ def _load_labels_from_config(self) -> list[dict[str, str]]: for label_filename in label_filenames: logging.info(f"Loading labels from {label_filename}.") - label_file: str = os.path.join(self.dir, label_filename) + label_file: str = os.path.join(self.labels_dir, label_filename) with open(label_file, "r") as f: if label_ext == "yaml": @@ -236,19 +243,19 @@ def _load_labels_from_config(self) -> list[dict[str, str]]: def _load_labels_to_remove_from_config(self) -> list[str]: labels_to_remove: list[str] = [] - files_in_dir: list[str] = os.listdir(self.dir) + files_in_labels_dir: list[str] = os.listdir(self.labels_dir) label_to_remove_yaml: list[str] = list( filter( lambda f: (f.endswith(".yaml") or f.endswith("yml")) and f.startswith("_remove"), - files_in_dir, + files_in_labels_dir, ) ) label_to_remove_json: list[str] = list( filter( lambda f: f.endswith(".json") and f.startswith("_remove"), - files_in_dir, + files_in_labels_dir, ) ) @@ -256,10 +263,14 @@ def _load_labels_to_remove_from_config(self) -> list[str]: label_to_remove_ext: str = "" if label_to_remove_yaml: - label_to_remove_file = os.path.join(self.dir, label_to_remove_yaml[0]) + label_to_remove_file = os.path.join( + self.labels_dir, label_to_remove_yaml[0] + ) label_to_remove_ext = "yaml" elif label_to_remove_json: - label_to_remove_file = os.path.join(self.dir, label_to_remove_json[0]) + label_to_remove_file = os.path.join( + self.labels_dir, label_to_remove_json[0] + ) label_to_remove_ext = "json" logging.info(f"Deleting labels from {label_to_remove_file}") @@ -275,11 +286,22 @@ def _update_label(self, label: dict[str, str]) -> None: url: str = f"{self.url}/{label['name']}" label["new_name"] = label.pop("name") - res: Response = requests.patch(url, headers=self.headers, json=label) - - if res.status_code != 200: + try: + res: Response = requests.patch( + url, + headers=self.headers, + json=label, + timeout=10, + ) + res.raise_for_status() + except Timeout: + logging.error( + "The site can't be reached, `github.com` took to long to respond. Try checking the connection." + ) + sys.exit() + except HTTPError: logging.error( - f"Status {res.status_code}. Failed to update label `{label['new_name']}`." + f"Failed to update label `{label['new_name']}`. Check the label format." ) else: logging.info(f"Label `{label['new_name']}` updated successfully.") @@ -318,15 +340,20 @@ def remove_labels( if remove_label in self.github_label_names: url: str = f"{self.url}/{remove_label}" - res: Response = requests.delete( - url, - headers=self.headers, - ) - - if res.status_code != 204: + try: + res: Response = requests.delete( + url, + headers=self.headers, + timeout=10, + ) + res.raise_for_status() + except Timeout: logging.error( - f"Status {res.status_code}. Failed to delete label `{remove_label}`." + "The site can't be reached, `github.com` took to long to respond. Try checking the connection." ) + sys.exit() + except HTTPError: + logging.error(f"Failed to delete label `{remove_label}`.") else: logging.info(f"Label `{remove_label}` deleted successfully.") @@ -361,15 +388,22 @@ def add_labels(self, labels: list[dict[str, str]] | None = None) -> None: self._update_label(label) else: - res: Response = requests.post( - self.url, - headers=self.headers, - json=label, - ) - - if res.status_code != 201: + try: + res: Response = requests.post( + self.url, + headers=self.headers, + json=label, + timeout=10, + ) + res.raise_for_status() + except Timeout: + logging.error( + "The site can't be reached, `github.com` took to long to respond. Try checking the connection." + ) + sys.exit() + except HTTPError: logging.error( - f"Status {res.status_code}. Failed to add label `{label['name']}`." + f"Failed to add label `{label['name']}`. Check the label format." ) else: logging.info(f"Label `{label['name']}` added successfully.") @@ -377,11 +411,7 @@ def add_labels(self, labels: list[dict[str, str]] | None = None) -> None: logging.info("Label creation process completed.") -def main() -> None: +if __name__ == "__main__": github_label = GithubLabel() github_label.remove_labels() github_label.add_labels() - - -if __name__ == "__main__": - main() diff --git a/labels/_remove_labels.yaml b/src/labels/_remove_labels.yaml similarity index 100% rename from labels/_remove_labels.yaml rename to src/labels/_remove_labels.yaml diff --git a/src/labels/affects_labels.yaml b/src/labels/affects_labels.yaml new file mode 100644 index 0000000..bada267 --- /dev/null +++ b/src/labels/affects_labels.yaml @@ -0,0 +1,5 @@ +- name: "Affects: Infra" + color: "#fbbc9d" + description: Related to configuration, automation, CI, etc. +- name: "Affects: Project Management" + color: "#fbbc9d" diff --git a/src/labels/close_labels.yaml b/src/labels/close_labels.yaml new file mode 100644 index 0000000..fd52269 --- /dev/null +++ b/src/labels/close_labels.yaml @@ -0,0 +1,18 @@ +- name: "Close: Answered" + color: "#cdd1d5" +- name: "Close: Backlog" + color: "#cdd1d5" + description: Issues are stale/expired; sent to backlog for later re-evaluation. +- name: "Close: Duplicate" + color: "#cdd1d5" + description: + This issue or pull request already exists (see comments for pointer + to it). +- name: "Close: Not Actionable" + color: "#cdd1d5" +- name: "Close: Not Reproducible" + color: "#cdd1d5" + description: Closed because we cannot reproduce the issue. +- name: "Close: Will Not Fix" + color: "#cdd1d5" + description: Closed because we have decided not to address this (e.g. out of scope). diff --git a/labels/default_labels.yaml b/src/labels/default_labels.yaml similarity index 75% rename from labels/default_labels.yaml rename to src/labels/default_labels.yaml index 7f4262d..759bc7b 100644 --- a/labels/default_labels.yaml +++ b/src/labels/default_labels.yaml @@ -1,3 +1,3 @@ - name: good first issue - color: '#7057ff' + color: "#7057ff" description: Good for newcomers. diff --git a/src/labels/needs_labels.yaml b/src/labels/needs_labels.yaml new file mode 100644 index 0000000..abe4ba7 --- /dev/null +++ b/src/labels/needs_labels.yaml @@ -0,0 +1,46 @@ +- name: "Needs: Breakdown" + color: "#0052cc" + description: + This big issue needs a checklist or subissues to describe a breakdown + of work. +- name: "Needs: Designs" + color: "#0052cc" +- name: "Needs: Detail" + color: "#0052cc" + description: + Submitter needs to provide more detail for this issue to be assessed + (see comments). +- name: "Needs: Feedback" + color: "#0052cc" + description: + A proposed feature or bug resolution needs feedback prior to forging + ahead. +- name: "Needs: Help" + color: "#0052cc" + description: + Issues, typically substantial ones, that need a dedicated developer + to take them on. +- name: "Needs: Investigation" + color: "#0052cc" + description: This issue/PR needs a root-cause analysis to determine a solution. +- name: "Needs: Response" + color: "#0052cc" + description: Issues which require feedback from staff members. +- name: "Needs: Review" + color: "#0052cc" + description: + This issue/PR needs to be reviewed in order to be closed or merged + (see comments) +- name: "Needs: Revisiting" + color: "#0052cc" + description: Archived (usually noisy dependencies). +- name: "Needs: Submitter Input" + color: "#0052cc" + description: Waiting on input from the creator of the issue/pr. +- name: "Needs: Testing" + color: "#0052cc" +- name: "Needs: Triage" + color: "#0052cc" + description: + This issue needs triage. The team needs to decide who should own it, + what to do, by when. diff --git a/src/labels/priority_labels.yaml b/src/labels/priority_labels.yaml new file mode 100644 index 0000000..25be172 --- /dev/null +++ b/src/labels/priority_labels.yaml @@ -0,0 +1,8 @@ +- name: "Priority: Critical" + color: "#7c0a02" +- name: "Priority: High" + color: "#b22222" +- name: "Priority: Medium" + color: "#ff8597" +- name: "Priority: Low" + color: "#ffccc9" diff --git a/src/labels/state_labels.yaml b/src/labels/state_labels.yaml new file mode 100644 index 0000000..bc2a833 --- /dev/null +++ b/src/labels/state_labels.yaml @@ -0,0 +1,11 @@ +- name: "State: Blocked" + color: "#e07bf9" + description: + Work has stopped, waiting for something (Info, Dependent fix, etc. + See comments). +- name: "State: In Review" + color: "#e07bf9" + description: This issue is waiting for review to finish. +- name: "State: Work In Progress" + color: "#e07bf9" + description: This issue is being actively worked on. diff --git a/src/labels/type_labels.yaml b/src/labels/type_labels.yaml new file mode 100644 index 0000000..bef2781 --- /dev/null +++ b/src/labels/type_labels.yaml @@ -0,0 +1,19 @@ +- name: "Type: Bug" + color: "#ff9900" + description: Something isn't working. +- name: "Type: Documentation" + color: "#ff9900" + description: Improvements or additions to documentation. +- name: "Type: Feature Request" + color: "#ff9900" + description: Issue describes a feature or enhancement we'd like to implement. +- name: "Type: Question" + color: "#ff9900" + description: This issue doesn't require code. A question needs an answer. +- name: "Type: Refactor/Clean-up" + color: "#ff9900" + description: + Issues related to reorganization/clean-up of data or code (e.g. for + maintainability). +- name: "Type: Suggestion" + color: "#ff9900"