Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ What actually happened. Include the full traceback if applicable.
## Environment
- OS: [e.g. macOS 15, Ubuntu 24.04]
- Python version: [e.g. 3.11.8]
- Commit: [run edb00ee chore: add Makefile, pyproject.toml, CONTRIBUTING, CHANGELOG, docs]
- Install method: [audit.pyz, uv tool, pipx, local clone]
- Version or commit: [run `audit --version` if available, or `git rev-parse --short HEAD` from a clone]
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## [Unreleased]

### Changed
- Made PyPI publishing explicitly opt-in while keeping GitHub Releases as the supported public distribution path.
- Added PyPI-ready package metadata and public distribution status documentation.

## [0.19.0] - 2026-05-11
### Added
- `audit serve` local web UI (FastAPI + HTMX): portfolio dashboard, per-repo drill-down, run history, approval queue, and SSE-streamed `audit run` trigger. Requires `pip install -e '.[serve]'` (Arc F S4.1).
Expand Down
12 changes: 8 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: install install-dev doctor audit control-center demo benchmark workbook-gate workbook-signoff test lint format type-check run clean release-gate build shiv dist-check release
.PHONY: install install-dev doctor audit control-center demo benchmark workbook-gate workbook-signoff test lint format type-check run clean release-gate build shiv dist-check release publish-pypi

PYTHON := python3
USERNAME ?= saagpatel
Expand Down Expand Up @@ -89,6 +89,10 @@ shiv:
shiv -c audit -o dist/audit.pyz . --python "/usr/bin/env python3"
@echo "=== dist/audit.pyz ready. Test: ./dist/audit.pyz --help ==="

release: build dist-check
@echo "=== Uploading to PyPI via scripts/release.sh ==="
bash scripts/release.sh
release: build dist-check shiv
@echo "=== Release artifacts are ready in dist/ ==="
@echo "Tag a v* release to publish GitHub Release assets. Use make publish-pypi only after PyPI trusted publishing or credentials are configured."

publish-pypi:
@echo "=== Publishing wheel + sdist to PyPI via scripts/release.sh ==="
bash scripts/release.sh --publish-pypi
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ Treat campaign/writeback, GitHub Projects, Notion sync, catalog overrides, score
- Workbook tour: [docs/workbook-tour.md](docs/workbook-tour.md)
- Extending analyzers: [docs/extending-analyzers.md](docs/extending-analyzers.md)
- Release gates: [docs/release-gates.md](docs/release-gates.md)
- Distribution status: [docs/distribution.md](docs/distribution.md)
- Project history: [docs/project-history.md](docs/project-history.md)

## Features
Expand Down Expand Up @@ -137,7 +138,7 @@ Treat campaign/writeback, GitHub Projects, Notion sync, catalog overrides, score

### Installation

The package is published as GitHub release artifacts today. PyPI/package-index publishing is not active yet, so registry commands like `pip install github-repo-auditor` are not the recommended public path.
The package is published as GitHub release artifacts today. PyPI/package-index publishing is not active yet, so registry commands like `pip install github-repo-auditor` are not the recommended public path. See [docs/distribution.md](docs/distribution.md) for the current distribution policy.

Fastest no-clone path:

Expand Down
48 changes: 48 additions & 0 deletions docs/distribution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Distribution

GitHub Repo Auditor is public and currently distributed through GitHub Releases.

## Current Public Path

Use the latest release binary when you want the fastest no-clone install:

```bash
curl -LO https://github.com/saagpatel/GithubRepoAuditor/releases/latest/download/audit.pyz
chmod +x audit.pyz
./audit.pyz --help
```

Use the public GitHub source when you want an isolated tool install:

```bash
uv tool install 'git+https://github.com/saagpatel/GithubRepoAuditor.git'
pipx install 'git+https://github.com/saagpatel/GithubRepoAuditor.git'
```

## PyPI Status

PyPI publishing is not active yet. The package name `github-repo-auditor` was
available when checked during the public-readiness pass on 2026-05-18, but that
can change and should be rechecked immediately before first publication.

The repository is prepared for a future PyPI release:

- package metadata lives in `pyproject.toml`
- `make build` creates the wheel and source distribution
- `make dist-check` runs `twine check`
- `scripts/release.sh` builds and checks artifacts by default
- `scripts/release.sh --publish-pypi` is the only script path that uploads to PyPI

## Activation Checklist

Before the first PyPI release:

1. Recheck that the `github-repo-auditor` PyPI name is still available.
2. Create the PyPI project through a first upload or configure Trusted Publishing.
3. Prefer PyPI Trusted Publishing from GitHub Actions over long-lived API tokens.
4. Run the standard and distribution gates from [release-gates.md](release-gates.md).
5. Publish the same version that is tagged on GitHub.
6. Smoke-test `pipx install github-repo-auditor` or `uv tool install github-repo-auditor`.

Until that checklist is complete, GitHub Releases remain the supported public
distribution channel.
9 changes: 7 additions & 2 deletions docs/release-gates.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,17 @@ All three must pass before tagging:
and non-PEP 440 tag suffixes can break the release build.
- Public hardening releases should use patch versions (`v0.1.x`). Feature releases
should move the minor version (`v0.2.0`, `v0.3.0`, and so on).
- TWINE upload to PyPI is manual-only and not part of the current public install
story; CI only checks and uploads to GitHub Releases.
- PyPI upload is explicit opt-in and not part of the current public install story.
`scripts/release.sh` builds and checks artifacts by default; it uploads only when
run as `scripts/release.sh --publish-pypi` with valid credentials. CI only checks
and uploads to GitHub Releases.
- The `[serve]` extra is not bundled in the shiv binary by default. Users who need the
web UI should install from the GitHub source with the `[serve]` extra or use a local
editable clone.

See [distribution.md](distribution.md) for the public distribution policy and the
remaining PyPI activation checklist.

## Web UI Gate (scope: audit serve)

Run when any change touches `src/serve/` or `tests/test_serve.py`.
Expand Down
20 changes: 20 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ description = "Automated GitHub portfolio auditor with 12 analysis dimensions"
readme = "README.md"
requires-python = ">=3.11"
license = "MIT"
authors = [{ name = "Saag Patel" }]
keywords = [
"github",
"portfolio",
"audit",
"repository-analysis",
"developer-tools",
]
classifiers = [
"Development Status :: 3 - Alpha",
"Environment :: Console",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Quality Assurance",
"Topic :: Utilities",
]
dependencies = [
"requests>=2.31.0",
"python-dateutil>=2.8.0",
Expand Down Expand Up @@ -51,6 +69,8 @@ build = [
Homepage = "https://github.com/saagpatel/GithubRepoAuditor"
Repository = "https://github.com/saagpatel/GithubRepoAuditor"
"Bug Tracker" = "https://github.com/saagpatel/GithubRepoAuditor/issues"
Changelog = "https://github.com/saagpatel/GithubRepoAuditor/blob/main/CHANGELOG.md"
Documentation = "https://github.com/saagpatel/GithubRepoAuditor#readme"

[project.scripts]
audit = "src.cli:main"
Expand Down
52 changes: 33 additions & 19 deletions scripts/release.sh
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
#!/usr/bin/env bash
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore executable bit on release helper

With this commit the file mode changes from 100755 to 100644, so the documented direct invocation path (scripts/release.sh --publish-pypi in the updated release docs and the script's shebang-based usage) now fails with Permission denied on a normal checkout. Either keep the helper executable or make all docs/targets consistently invoke it via bash.

Useful? React with 👍 / 👎.

# scripts/release.sh — PyPI publish workflow for github-repo-auditor
# scripts/release.sh — distribution artifact workflow for github-repo-auditor
#
# Prerequisites:
# pip install build twine shiv (or: pip install ".[build]")
#
# Required env vars (one of):
# Required env vars for --publish-pypi (one of):
# TWINE_API_TOKEN — PyPI API token (preferred)
# TWINE_USERNAME + TWINE_PASSWORD — legacy username/password
#
# Usage:
# bash scripts/release.sh # build + check + upload to PyPI
# bash scripts/release.sh --dry-run # build + check only, skip upload
# bash scripts/release.sh # build + check only
# bash scripts/release.sh --publish-pypi # build + check + upload to PyPI
# bash scripts/release.sh --dry-run # compatibility alias for build + check only
#
set -euo pipefail

DRY_RUN=false
PUBLISH_PYPI=false
SKIP_CLEAN=false
for arg in "$@"; do
case "$arg" in
--dry-run) DRY_RUN=true ;;
--publish-pypi) PUBLISH_PYPI=true ;;
--dry-run) PUBLISH_PYPI=false ;;
--skip-clean) SKIP_CLEAN=true ;;
*)
echo "Unknown argument: $arg"
echo "Usage: bash scripts/release.sh [--publish-pypi] [--dry-run] [--skip-clean]"
exit 2
;;
esac
done

Expand All @@ -30,9 +39,20 @@ echo "Project root: $PROJECT_ROOT"
echo ""

# 1. Clean previous artifacts
echo "[1/4] Cleaning dist/"
rm -rf dist/ build/ src/*.egg-info
echo " Done."
echo "[1/4] Cleaning build artifacts..."
if [ "$SKIP_CLEAN" = "true" ]; then
echo " Skipped."
else
python3 - <<'PY'
from pathlib import Path
import shutil

for path in [Path("dist"), Path("build"), *Path("src").glob("*.egg-info")]:
if path.exists():
shutil.rmtree(path)
PY
echo " Done."
fi

# 2. Build wheel + sdist
echo "[2/4] Building wheel + sdist..."
Expand All @@ -45,11 +65,11 @@ echo "[3/4] Running twine check..."
python3 -m twine check dist/*
echo " All checks passed."

# 4. Upload (unless --dry-run)
if [ "$DRY_RUN" = "true" ]; then
echo "[4/4] --dry-run set — skipping upload."
# 4. Upload only when explicitly requested.
if [ "$PUBLISH_PYPI" != "true" ]; then
echo "[4/4] PyPI publish not requested — skipping upload."
echo ""
echo "=== Dry run complete. Artifacts in dist/ ==="
echo "=== Distribution check complete. Artifacts in dist/ ==="
exit 0
fi

Expand All @@ -70,9 +90,3 @@ fi

echo ""
echo "=== Upload complete! ==="
echo ""
echo "Next steps:"
echo " 1. Tag this release: git tag v$(python3 -c "import importlib.metadata; print(importlib.metadata.version('github-repo-auditor'))" 2>/dev/null || grep '^version' pyproject.toml | head -1 | cut -d'"' -f2)"
echo " 2. Push the tag: git push origin --tags"
echo " 3. Create GitHub Release and attach dist/audit.pyz (if built)"
echo ""
40 changes: 40 additions & 0 deletions tests/test_distribution_policy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Distribution policy checks for the public package surface."""

from __future__ import annotations

import tomllib
from pathlib import Path

ROOT = Path(__file__).parent.parent


def test_pyproject_has_public_package_metadata() -> None:
with open(ROOT / "pyproject.toml", "rb") as fh:
project = tomllib.load(fh)["project"]

assert project["name"] == "github-repo-auditor"
assert project.get("authors"), "PyPI metadata should include an author entry."
assert project.get("classifiers"), "PyPI metadata should include classifiers."
assert project.get("keywords"), "PyPI metadata should include package keywords."
assert "Documentation" in project.get("urls", {})
assert "Changelog" in project.get("urls", {})


def test_release_script_requires_explicit_pypi_publish_flag() -> None:
release_script = (ROOT / "scripts" / "release.sh").read_text()

assert "PUBLISH_PYPI=false" in release_script
assert "--publish-pypi" in release_script
assert 'if [ "$PUBLISH_PYPI" != "true" ]' in release_script
assert "PyPI publish not requested" in release_script


def test_distribution_docs_name_supported_public_channel() -> None:
distribution_doc = (ROOT / "docs" / "distribution.md").read_text()
readme = (ROOT / "README.md").read_text()
release_gates = (ROOT / "docs" / "release-gates.md").read_text()

assert "GitHub Releases remain the supported public" in distribution_doc
assert "PyPI publishing is not active yet" in distribution_doc
assert "docs/distribution.md" in readme
assert "scripts/release.sh --publish-pypi" in release_gates