Metadata-Version: 2.4
Name: github-checks
Version: 0.0.7
Summary: Minimal Python API for GitHub Checks to submit feedback from builds running on 3rd party build platforms
Author-email: Julian Gubler <mail@juliangubler.de>
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: jwt
Requires-Dist: pydantic
Requires-Dist: requests
Requires-Dist: configargparse
Requires-Dist: pysarif
Provides-Extra: dev
Requires-Dist: bandit; extra == "dev"
Requires-Dist: black; extra == "dev"
Requires-Dist: check-jsonschema; extra == "dev"
Requires-Dist: isort; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: pathspec; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Requires-Dist: pycodestyle; extra == "dev"
Requires-Dist: pydocstyle; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: types-requests; extra == "dev"
Dynamic: license-file

# github-checks
Minimal Python API for GitHub Checks to submit feedback from builds running on any build platform.
Allows keeping individual CI code minimal in each repository, and is agnostic towards the build platform (GCP, Azure, Actions, etc.) being used.

## Prerequisites

1. You have a code repository that you want to validate, which is hosted on either GitHub Cloud or GitHub Enterprise
2. You have created a GitHub App in your global GitHub user profile (in case of personal use), or in your organization user's profile, which has read access to this repository, and read and write access to its Checks API
3. The public key of your GitHub App has been added to the repository's deploy key, so the App can clone the repository during the CI build.
3. To authenticate during the CI build, the local environment must have access to the private key PEM file (in pkcs8 format for git's SSH connection).

Once you're running the checks, you'll need to provide the following information:
* The base GitHub URL of the repository
* The ID of the GitHub App, as well as the installation ID (which is specific to the app's installation in your repository/organization)
* The private key for the App, with which it can both pull the repository (acting as a deploy key) as well as authenticate as the App
* The path to your local copy of the repository (to resolve relative filepaths in check output)
* And finally, the commit revision that your check is running against (so that the right commit hashes are being annotated with the results)

These can be passed either via environment variables or cmdline parameters or, if using as a python library, via function parameters.

## Usage

There's two usage options, as a library and directly via CLI. In most cases, you will ususally want the CLI, as it comes with predefined formatters for annotations by common check tools.
Usually, you will probably want to run this from some type of CI/CD, usually from a bash script, in which case you can get away with ~10 lines of code to fully run your desired PR checks.

If you want to check & post something to the PR without needing annotations, you can also use the `raw` value for the `--log-format` parameter, which will just post it as a plain text conclusion.

Using the package as a library in your Python code gives you full flexibility, in case you want to run checks with detailed annotations that are not yet supported natively.
If you want to mix the two approaches, you can also use the package as a library generally, and just import the pre-built formatters are provided in `github_checks.formatters` where they are available.

### CLI

Providing the environment variables / cmdline parameters for GitHub App authentication aside, using the CLI is quite simple, as shown by this example running ruff checks:
```sh
python3 -m pip install github-checks ruff

# initialize the checks app to auth with GitHub, and let's ignore some dirty code
python3 -m github_checks.cli init --overwrite-existing
echo "/src/legacy_code/" > .checksignore

# start a check run (this will show a spinning check in the GitHub PR page)
python3 -m github_checks.cli start-check-run --check-name ruff-checks
ruff check . --output-format=json > ruff_output.json

# Finish the check run by providing ruff's output
# Let's ignore our legacy code for the check verdict, but still post annotations
python3 -m github_checks.cli finish-check-run ruff_output.json --log-format ruff-json --checksignore-filepath .checksignore --checksignore-verdict-only

# clean up, removing the GH_* environment variables and the pickle file cache
python3 -m github_checks.cli cleanup
```

See our shell script, which uses our own CLI to validate this repository with a bunch of tools (some real, some artificial):

https://github.com/jgubler/github-checks/blob/main/tests/run_checks_on_ourselves.sh


## How to initiate the Checks
Depending on your build environment and if it has an integration with GitHub, you _could_ use a direct "pull request trigger" to run your builds, which then perform & upload the checks.
However, as you need a GitHub App for this to work anyways, and they can also be configured to trigger webhooks based on the events of the repository they're connected to, it's recommended to utilize this instead, to trigger a build run.
This will e.g. also allow you to use the "Re-Run Checks" functionality in the GitHub PR web interface, which will then re-trigger that webhook, re-running your build on-demand.


### As a library

```python

from github_checks.github_api import GitHubChecks
from github_checks.models import (
    AnnotationLevel,
    CheckAnnotation,
    CheckRunConclusion,
    CheckRunOutput,
)

gh_checks: GitHubChecks = GitHubChecks(
    repo_base_url=YOUR_REPO_BASE_URL,  # e.g. https://github.com/yourname/yourrepo
    app_id=YOUR_APP_ID,
    app_installation_id=YOUR_APP_INSTALLATION_ID,
    app_privkey_pem=Path("/path/to/privkey.pem"),
)

gh_checks.start_check_run(
    revision_sha=HASH_OF_COMMIT_TO_BE_CHECKED,
    check_name="SomeCheck",
)

check_run_output = CheckRunOutput(
    title="short",
    summary="longer",
    annotations=[
        CheckAnnotation(
            annotation_level=AnnotationLevel.WARNING,
            start_line=1,
            start_column=1,  # caution: only use columns when start_line==end_line!
            end_line=1,
            end_column=10,
            path="src/myfile.py",
            message="this is no bueno",
            raw_details="can't believe you've done this",
            title="[NO001] no-bueno",
        ),
        ...
    ]
)

gh_checks.finish_check_run(
    CheckRunConclusion.ACTION_REQUIRED,
    check_run_output,
)
```

# Future Work

Add support for:
* pytest
* bandit
* pyroma
* vulture
* black
* shellcheck

Other features:
* Allow checks to run in parallel (likely using sqlite instead of a pickle file)
