github-overlord 0.8.0__tar.gz → 0.9.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- github_overlord-0.9.1/PKG-INFO +138 -0
- github_overlord-0.9.1/README.md +111 -0
- github_overlord-0.9.1/github_overlord/__init__.py +211 -0
- github_overlord-0.9.1/github_overlord/ai.py +69 -0
- {github_overlord-0.8.0 → github_overlord-0.9.1}/github_overlord/config.py +7 -3
- github_overlord-0.9.1/github_overlord/data/release_analysis_prompt.j2 +50 -0
- github_overlord-0.9.1/github_overlord/dependabot.py +388 -0
- github_overlord-0.9.1/github_overlord/notifications.py +352 -0
- github_overlord-0.9.1/github_overlord/patch.py +72 -0
- {github_overlord-0.8.0 → github_overlord-0.9.1}/github_overlord/release_checker.py +186 -61
- github_overlord-0.9.1/github_overlord/stale_commenter.py +232 -0
- {github_overlord-0.8.0 → github_overlord-0.9.1}/github_overlord/utils.py +44 -6
- github_overlord-0.9.1/github_overlord/version.py +34 -0
- github_overlord-0.9.1/pyproject.toml +82 -0
- github_overlord-0.9.1/pyproject.toml.orig +67 -0
- github_overlord-0.8.0/LICENSE.md +0 -21
- github_overlord-0.8.0/PKG-INFO +0 -116
- github_overlord-0.8.0/README.md +0 -93
- github_overlord-0.8.0/data/release_analysis_prompt.j2 +0 -46
- github_overlord-0.8.0/github_overlord/__init__.py +0 -406
- github_overlord-0.8.0/github_overlord/patch.py +0 -14
- github_overlord-0.8.0/github_overlord/stale_commenter.py +0 -120
- github_overlord-0.8.0/pyproject.toml +0 -40
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: github-overlord
|
|
3
|
+
Version: 0.9.1
|
|
4
|
+
Summary: Automatically merge Dependabot PRs in public repositories
|
|
5
|
+
Keywords: github,dependabot,automation
|
|
6
|
+
Author: Michael Bianco
|
|
7
|
+
Author-email: Michael Bianco <mike@mikebian.co>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Requires-Dist: pygithub>=2.4.0
|
|
15
|
+
Requires-Dist: click>=8.1.7
|
|
16
|
+
Requires-Dist: python-decouple-typed>=3.11.0
|
|
17
|
+
Requires-Dist: funcy-pipe>=0.11.1,<0.15.0
|
|
18
|
+
Requires-Dist: structlog>=24.4,<27.0
|
|
19
|
+
Requires-Dist: apscheduler>=3.10.4
|
|
20
|
+
Requires-Dist: openai>=1.52.2,<4.0.0
|
|
21
|
+
Requires-Dist: pydantic-ai-slim[google]>=0.0.18
|
|
22
|
+
Requires-Dist: jinja2>=3.1.0
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Project-URL: Homepage, https://github.com/iloveitaly/github-overlord
|
|
25
|
+
Project-URL: Repository, https://github.com/iloveitaly/github-overlord
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
[](https://github.com/iloveitaly/github-overlord/releases)
|
|
29
|
+
[](https://pepy.tech/project/github-overlord)
|
|
30
|
+
[](https://pypi.org/project/github-overlord)
|
|
31
|
+

|
|
32
|
+
[](https://opensource.org/licenses/MIT)
|
|
33
|
+
|
|
34
|
+
# GitHub Overlord
|
|
35
|
+
|
|
36
|
+
GitHub Overlord is a Python script that does a couple things to help manage open source projects on GitHub:
|
|
37
|
+
|
|
38
|
+
* Automatically merges Dependabot PRs in public repositories that have passed CI checks.
|
|
39
|
+
* Comment on PRs that are going to automatically be marked as stale
|
|
40
|
+
* Removes notifications from bot-authored PRs, Release Please PRs, self-authored PR creations without external activity, PRs merged by you, PRs closed by you, PRs approved by you that are now merged, notifications older than two months, and releases on repos you control
|
|
41
|
+
* Automatically creates releases for repositories based on LLM analysis of recent commits
|
|
42
|
+
|
|
43
|
+
This simple project has also given me the chance to iterate on my [nixpacks github actions project](https://github.com/iloveitaly/github-action-nixpacks).
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```shell
|
|
48
|
+
pip install github-overlord
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```shell
|
|
54
|
+
Usage: github-overlord [OPTIONS] COMMAND [ARGS]...
|
|
55
|
+
|
|
56
|
+
Options:
|
|
57
|
+
--help Show this message and exit.
|
|
58
|
+
|
|
59
|
+
Commands:
|
|
60
|
+
generate-releases Check repositories for release readiness using LLM analysis
|
|
61
|
+
dependabot Automatically merge dependabot PRs in public repos that...
|
|
62
|
+
keep-alive-prs Detect when a bot is about to close a PR for no good reason
|
|
63
|
+
notifications Look at notifications and mark them as read
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Automatic Release Creation
|
|
67
|
+
|
|
68
|
+
The `generate-releases` command uses LLM analysis (via [Pydantic AI](https://ai.pydantic.dev/)) to determine when repositories are ready for a new release. Both `generate-releases` and `keep-alive-prs` share the same unified AI model configuration (defaulting to Google Gemini `gemini-3.8-flash`).
|
|
69
|
+
|
|
70
|
+
This is particularly useful for template repositories, starter projects, etc that don't have automated release workflows and aren't versioned for whatever reason.
|
|
71
|
+
|
|
72
|
+
I have a bunch of these and I wanted an easy way to let the world know when I build something interesting in them.
|
|
73
|
+
|
|
74
|
+
**Usage:**
|
|
75
|
+
|
|
76
|
+
```shell
|
|
77
|
+
# Check all repos with a specific topic
|
|
78
|
+
github-overlord generate-releases --topic auto-release
|
|
79
|
+
|
|
80
|
+
# Check a single repository
|
|
81
|
+
github-overlord generate-releases --repo owner/repo-name
|
|
82
|
+
|
|
83
|
+
# Dry run (see what would happen without creating releases)
|
|
84
|
+
github-overlord generate-releases --topic auto-release --dry-run
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Requirements & Configuration:**
|
|
88
|
+
|
|
89
|
+
* `GITHUB_TOKEN` - GitHub token with repo write permissions
|
|
90
|
+
* `GITHUB_OVERLORD_AI_KEY` - API key for the AI provider (or provider-specific `GOOGLE_API_KEY`, `OPENAI_API_KEY`, etc.)
|
|
91
|
+
* `GITHUB_OVERLORD_MODEL` - (Optional) AI model override (e.g. `gemini-3.8-flash`, `openai:gpt-4o`). Defaults to `gemini-3.8-flash`.
|
|
92
|
+
* `--topic` flag or `RELEASE_CHECKER_TOPIC` - Topic to filter repositories (required unless using `--repo`)
|
|
93
|
+
* `--repo` flag or `RELEASE_CHECKER_REPO` - Single repository to process (useful for testing)
|
|
94
|
+
* `RELEASE_CHECKER_MIN_GAP` - Minimum time between releases for the same repo (defaults to `2w`, e.g. `14d`, `48h`)
|
|
95
|
+
|
|
96
|
+
**How it works:**
|
|
97
|
+
|
|
98
|
+
1. Finds repositories matching the specified topic
|
|
99
|
+
2. For each repo, gets commits since the last release (or since repo creation if no releases)
|
|
100
|
+
3. Analyzes up to the last 50 commits using the configured AI model (defaulting to `gemini-3.8-flash`) to determine if a release is warranted
|
|
101
|
+
4. If the LLM recommends a release, automatically creates one with:
|
|
102
|
+
|
|
103
|
+
* Auto-incremented semantic version (patch/minor/major based on changes)
|
|
104
|
+
* AI-generated release notes highlighting key changes
|
|
105
|
+
* Link to full changelog
|
|
106
|
+
|
|
107
|
+
**Scheduling:**
|
|
108
|
+
To run this weekly, set the `SCHEDULE` environment variable:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Run every Monday at 9 AM
|
|
112
|
+
export SCHEDULE="0 9 * * 1"
|
|
113
|
+
export RELEASE_CHECKER_TOPIC="template"
|
|
114
|
+
# Or, to test a single repo on the schedule:
|
|
115
|
+
# export RELEASE_CHECKER_REPO="owner/repo-name"
|
|
116
|
+
export GITHUB_TOKEN="your-token"
|
|
117
|
+
export GITHUB_OVERLORD_AI_KEY="your-api-key"
|
|
118
|
+
|
|
119
|
+
# All CLI commands will run on this schedule
|
|
120
|
+
python main.py
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Troubleshooting:**
|
|
124
|
+
|
|
125
|
+
* **"No repositories found with topic"**: Make sure your repos have the correct topic tag in GitHub settings
|
|
126
|
+
* **"API key environment variable is required"**: Set `GITHUB_OVERLORD_AI_KEY` (or `GOOGLE_API_KEY` for the default Gemini model). Get a free Gemini API key from [ai.google.dev](https://ai.google.dev/)
|
|
127
|
+
* **Rate limiting**: The free tier has limits (15-60 requests/minute). Consider adding delays between repos if needed
|
|
128
|
+
* **"Failed to create release"**: Ensure `GITHUB_TOKEN` has `repo` scope permissions
|
|
129
|
+
|
|
130
|
+
### Docker Cron
|
|
131
|
+
|
|
132
|
+
There's a docker container you can use to run this on a cron. [Fits nicely into a orange pi.](https://mikebian.co/pi-hole-tailscale-and-docker-on-an-orange-pi/)
|
|
133
|
+
|
|
134
|
+
Check out [docker-compose.yml](./docker-compose.yml) for an example, or `git pull ghcr.io/iloveitaly/github-overlord:latest`.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
*This project was created from [iloveitaly/python-package-template](https://github.com/iloveitaly/python-package-template)*
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
[](https://github.com/iloveitaly/github-overlord/releases)
|
|
2
|
+
[](https://pepy.tech/project/github-overlord)
|
|
3
|
+
[](https://pypi.org/project/github-overlord)
|
|
4
|
+

|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
# GitHub Overlord
|
|
8
|
+
|
|
9
|
+
GitHub Overlord is a Python script that does a couple things to help manage open source projects on GitHub:
|
|
10
|
+
|
|
11
|
+
* Automatically merges Dependabot PRs in public repositories that have passed CI checks.
|
|
12
|
+
* Comment on PRs that are going to automatically be marked as stale
|
|
13
|
+
* Removes notifications from bot-authored PRs, Release Please PRs, self-authored PR creations without external activity, PRs merged by you, PRs closed by you, PRs approved by you that are now merged, notifications older than two months, and releases on repos you control
|
|
14
|
+
* Automatically creates releases for repositories based on LLM analysis of recent commits
|
|
15
|
+
|
|
16
|
+
This simple project has also given me the chance to iterate on my [nixpacks github actions project](https://github.com/iloveitaly/github-action-nixpacks).
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```shell
|
|
21
|
+
pip install github-overlord
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```shell
|
|
27
|
+
Usage: github-overlord [OPTIONS] COMMAND [ARGS]...
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
--help Show this message and exit.
|
|
31
|
+
|
|
32
|
+
Commands:
|
|
33
|
+
generate-releases Check repositories for release readiness using LLM analysis
|
|
34
|
+
dependabot Automatically merge dependabot PRs in public repos that...
|
|
35
|
+
keep-alive-prs Detect when a bot is about to close a PR for no good reason
|
|
36
|
+
notifications Look at notifications and mark them as read
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Automatic Release Creation
|
|
40
|
+
|
|
41
|
+
The `generate-releases` command uses LLM analysis (via [Pydantic AI](https://ai.pydantic.dev/)) to determine when repositories are ready for a new release. Both `generate-releases` and `keep-alive-prs` share the same unified AI model configuration (defaulting to Google Gemini `gemini-3.8-flash`).
|
|
42
|
+
|
|
43
|
+
This is particularly useful for template repositories, starter projects, etc that don't have automated release workflows and aren't versioned for whatever reason.
|
|
44
|
+
|
|
45
|
+
I have a bunch of these and I wanted an easy way to let the world know when I build something interesting in them.
|
|
46
|
+
|
|
47
|
+
**Usage:**
|
|
48
|
+
|
|
49
|
+
```shell
|
|
50
|
+
# Check all repos with a specific topic
|
|
51
|
+
github-overlord generate-releases --topic auto-release
|
|
52
|
+
|
|
53
|
+
# Check a single repository
|
|
54
|
+
github-overlord generate-releases --repo owner/repo-name
|
|
55
|
+
|
|
56
|
+
# Dry run (see what would happen without creating releases)
|
|
57
|
+
github-overlord generate-releases --topic auto-release --dry-run
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Requirements & Configuration:**
|
|
61
|
+
|
|
62
|
+
* `GITHUB_TOKEN` - GitHub token with repo write permissions
|
|
63
|
+
* `GITHUB_OVERLORD_AI_KEY` - API key for the AI provider (or provider-specific `GOOGLE_API_KEY`, `OPENAI_API_KEY`, etc.)
|
|
64
|
+
* `GITHUB_OVERLORD_MODEL` - (Optional) AI model override (e.g. `gemini-3.8-flash`, `openai:gpt-4o`). Defaults to `gemini-3.8-flash`.
|
|
65
|
+
* `--topic` flag or `RELEASE_CHECKER_TOPIC` - Topic to filter repositories (required unless using `--repo`)
|
|
66
|
+
* `--repo` flag or `RELEASE_CHECKER_REPO` - Single repository to process (useful for testing)
|
|
67
|
+
* `RELEASE_CHECKER_MIN_GAP` - Minimum time between releases for the same repo (defaults to `2w`, e.g. `14d`, `48h`)
|
|
68
|
+
|
|
69
|
+
**How it works:**
|
|
70
|
+
|
|
71
|
+
1. Finds repositories matching the specified topic
|
|
72
|
+
2. For each repo, gets commits since the last release (or since repo creation if no releases)
|
|
73
|
+
3. Analyzes up to the last 50 commits using the configured AI model (defaulting to `gemini-3.8-flash`) to determine if a release is warranted
|
|
74
|
+
4. If the LLM recommends a release, automatically creates one with:
|
|
75
|
+
|
|
76
|
+
* Auto-incremented semantic version (patch/minor/major based on changes)
|
|
77
|
+
* AI-generated release notes highlighting key changes
|
|
78
|
+
* Link to full changelog
|
|
79
|
+
|
|
80
|
+
**Scheduling:**
|
|
81
|
+
To run this weekly, set the `SCHEDULE` environment variable:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Run every Monday at 9 AM
|
|
85
|
+
export SCHEDULE="0 9 * * 1"
|
|
86
|
+
export RELEASE_CHECKER_TOPIC="template"
|
|
87
|
+
# Or, to test a single repo on the schedule:
|
|
88
|
+
# export RELEASE_CHECKER_REPO="owner/repo-name"
|
|
89
|
+
export GITHUB_TOKEN="your-token"
|
|
90
|
+
export GITHUB_OVERLORD_AI_KEY="your-api-key"
|
|
91
|
+
|
|
92
|
+
# All CLI commands will run on this schedule
|
|
93
|
+
python main.py
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Troubleshooting:**
|
|
97
|
+
|
|
98
|
+
* **"No repositories found with topic"**: Make sure your repos have the correct topic tag in GitHub settings
|
|
99
|
+
* **"API key environment variable is required"**: Set `GITHUB_OVERLORD_AI_KEY` (or `GOOGLE_API_KEY` for the default Gemini model). Get a free Gemini API key from [ai.google.dev](https://ai.google.dev/)
|
|
100
|
+
* **Rate limiting**: The free tier has limits (15-60 requests/minute). Consider adding delays between repos if needed
|
|
101
|
+
* **"Failed to create release"**: Ensure `GITHUB_TOKEN` has `repo` scope permissions
|
|
102
|
+
|
|
103
|
+
### Docker Cron
|
|
104
|
+
|
|
105
|
+
There's a docker container you can use to run this on a cron. [Fits nicely into a orange pi.](https://mikebian.co/pi-hole-tailscale-and-docker-on-an-orange-pi/)
|
|
106
|
+
|
|
107
|
+
Check out [docker-compose.yml](./docker-compose.yml) for an example, or `git pull ghcr.io/iloveitaly/github-overlord:latest`.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
*This project was created from [iloveitaly/python-package-template](https://github.com/iloveitaly/python-package-template)*
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from github import Github
|
|
5
|
+
|
|
6
|
+
import github_overlord.patch # noqa: F401
|
|
7
|
+
|
|
8
|
+
from .ai import get_expected_ai_key_var, is_ai_key_configured
|
|
9
|
+
from .dependabot import dependabot
|
|
10
|
+
from .notifications import notifications
|
|
11
|
+
from .release_checker import check_repo_for_release
|
|
12
|
+
from .stale_commenter import inspect_stale_prs
|
|
13
|
+
from .utils import extract_repo_reference_from_github_url, log
|
|
14
|
+
from .version import __version__
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _ensure_ai_key_configured(command_action: str) -> None:
|
|
18
|
+
if not is_ai_key_configured():
|
|
19
|
+
expected_var = get_expected_ai_key_var()
|
|
20
|
+
raise click.ClickException(
|
|
21
|
+
click.style(
|
|
22
|
+
f"{expected_var} (or GITHUB_OVERLORD_AI_KEY) environment variable is required to {command_action}",
|
|
23
|
+
fg="red",
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@click.group()
|
|
29
|
+
@click.version_option(version=__version__)
|
|
30
|
+
def cli():
|
|
31
|
+
"""
|
|
32
|
+
GitHub Overlord is a tool to help manage annoying tasks across your GitHub repositories. Some of this could be done
|
|
33
|
+
by GitHub Actions, but this eliminates the need to carefully configure GH actions for each repo.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@click.command()
|
|
38
|
+
@click.option(
|
|
39
|
+
"--token",
|
|
40
|
+
help="GitHub token, can also be set via GITHUB_TOKEN",
|
|
41
|
+
default=os.getenv("GITHUB_TOKEN"),
|
|
42
|
+
)
|
|
43
|
+
@click.option("--dry-run", is_flag=True, help="Run script without creating comments")
|
|
44
|
+
@click.option("--repo", help="Only process a single repository")
|
|
45
|
+
def keep_alive_prs(token, dry_run, repo):
|
|
46
|
+
"""
|
|
47
|
+
Detect when a bot is about to close a PR for no good reason and make a comment to keep it alive
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
if not token:
|
|
51
|
+
raise click.ClickException(
|
|
52
|
+
click.style("GITHUB_TOKEN environment variable is required", fg="red")
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
_ensure_ai_key_configured("run keep-alive-prs")
|
|
56
|
+
|
|
57
|
+
log.info("checking for stale PRs")
|
|
58
|
+
|
|
59
|
+
github = Github(token)
|
|
60
|
+
user = github.get_user()
|
|
61
|
+
login = user.login
|
|
62
|
+
|
|
63
|
+
repo = extract_repo_reference_from_github_url(repo)
|
|
64
|
+
|
|
65
|
+
result = inspect_stale_prs(github, login, dry_run, repo)
|
|
66
|
+
if dry_run:
|
|
67
|
+
log.info(
|
|
68
|
+
"stale PR check complete",
|
|
69
|
+
dry_run=True,
|
|
70
|
+
prs_inspected=result.inspected,
|
|
71
|
+
would_keep_alive=result.kept_alive,
|
|
72
|
+
skipped=result.skipped,
|
|
73
|
+
failed=result.failed,
|
|
74
|
+
)
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
log.info(
|
|
78
|
+
"stale PR check complete",
|
|
79
|
+
prs_inspected=result.inspected,
|
|
80
|
+
kept_alive=result.kept_alive,
|
|
81
|
+
skipped=result.skipped,
|
|
82
|
+
failed=result.failed,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@click.command(name="generate-releases")
|
|
87
|
+
@click.option("--dry-run", is_flag=True, help="Run script without creating releases")
|
|
88
|
+
@click.option(
|
|
89
|
+
"--topic",
|
|
90
|
+
help="Only process repos with this topic (can also be set via RELEASE_CHECKER_TOPIC)",
|
|
91
|
+
default=os.getenv("RELEASE_CHECKER_TOPIC"),
|
|
92
|
+
)
|
|
93
|
+
@click.option(
|
|
94
|
+
"--repo",
|
|
95
|
+
help="Only process a single repository (can also be set via RELEASE_CHECKER_REPO)",
|
|
96
|
+
default=os.getenv("RELEASE_CHECKER_REPO"),
|
|
97
|
+
)
|
|
98
|
+
@click.option(
|
|
99
|
+
"--max-releases",
|
|
100
|
+
"-m",
|
|
101
|
+
type=int,
|
|
102
|
+
default=lambda: int(os.getenv("RELEASE_CHECKER_MAX_RELEASES", "1")),
|
|
103
|
+
show_default="1",
|
|
104
|
+
help="Maximum number of releases to create in a single run (0 for unlimited)",
|
|
105
|
+
)
|
|
106
|
+
def generate_releases(dry_run, topic, repo, max_releases):
|
|
107
|
+
"""
|
|
108
|
+
Check repositories for release readiness using LLM analysis and create releases when appropriate
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
token = os.getenv("GITHUB_TOKEN")
|
|
112
|
+
if not token:
|
|
113
|
+
raise click.ClickException(
|
|
114
|
+
click.style("GITHUB_TOKEN environment variable is required", fg="red")
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
_ensure_ai_key_configured("use generate-releases")
|
|
118
|
+
|
|
119
|
+
log.info("checking repositories for release readiness")
|
|
120
|
+
|
|
121
|
+
repo = extract_repo_reference_from_github_url(repo)
|
|
122
|
+
|
|
123
|
+
if repo:
|
|
124
|
+
g = Github(token)
|
|
125
|
+
result = check_repo_for_release(g.get_repo(repo), dry_run)
|
|
126
|
+
if result["created"]:
|
|
127
|
+
log.info("release check complete - created 1 release", dry_run=dry_run)
|
|
128
|
+
elif result["failed"]:
|
|
129
|
+
log.info(
|
|
130
|
+
"release check complete - failed to create release", dry_run=dry_run
|
|
131
|
+
)
|
|
132
|
+
else:
|
|
133
|
+
log.info("release check complete - no release needed", dry_run=dry_run)
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
# Topic is required when not specifying a single repo
|
|
137
|
+
if not topic:
|
|
138
|
+
raise click.UsageError(
|
|
139
|
+
"Topic is required when not specifying a single repository (use --topic or set RELEASE_CHECKER_TOPIC)"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
g = Github(token)
|
|
143
|
+
user = g.get_user()
|
|
144
|
+
|
|
145
|
+
log.info("filtering by topic", topic=topic)
|
|
146
|
+
|
|
147
|
+
# Get all public repos owned by user with the specified topic
|
|
148
|
+
repos = g.search_repositories(
|
|
149
|
+
f"user:{user.login} topic:{topic} fork:false is:public",
|
|
150
|
+
sort="updated",
|
|
151
|
+
order="desc",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
def _check_repo_with_log(r):
|
|
155
|
+
log.info("checking repo for release", repo=r.full_name, dry_run=dry_run)
|
|
156
|
+
return check_repo_for_release(r, dry_run=dry_run)
|
|
157
|
+
|
|
158
|
+
results = []
|
|
159
|
+
created_count = 0
|
|
160
|
+
|
|
161
|
+
for r in repos:
|
|
162
|
+
if max_releases > 0 and created_count >= max_releases:
|
|
163
|
+
log.info(
|
|
164
|
+
"reached max releases limit for this run",
|
|
165
|
+
max_releases=max_releases,
|
|
166
|
+
)
|
|
167
|
+
break
|
|
168
|
+
|
|
169
|
+
res = _check_repo_with_log(r)
|
|
170
|
+
results.append(res)
|
|
171
|
+
if res["created"]:
|
|
172
|
+
created_count += 1
|
|
173
|
+
|
|
174
|
+
# Check if any repos were found
|
|
175
|
+
if not results:
|
|
176
|
+
log.warning("no repositories found with topic", topic=topic)
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
# Calculate statistics
|
|
180
|
+
total_checked = sum(1 for r in results if r["checked"])
|
|
181
|
+
total_skipped = sum(1 for r in results if r["skipped"])
|
|
182
|
+
total_created = sum(1 for r in results if r["created"])
|
|
183
|
+
total_failed = sum(1 for r in results if r["failed"])
|
|
184
|
+
|
|
185
|
+
# Log summary
|
|
186
|
+
if dry_run:
|
|
187
|
+
log.info(
|
|
188
|
+
"release check complete",
|
|
189
|
+
dry_run=True,
|
|
190
|
+
checked=total_checked,
|
|
191
|
+
would_create=total_created,
|
|
192
|
+
skipped=total_skipped,
|
|
193
|
+
errors=total_failed,
|
|
194
|
+
)
|
|
195
|
+
else:
|
|
196
|
+
log.info(
|
|
197
|
+
"release check complete",
|
|
198
|
+
checked=total_checked,
|
|
199
|
+
created=total_created,
|
|
200
|
+
skipped=total_skipped,
|
|
201
|
+
failed=total_failed,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
cli.add_command(dependabot)
|
|
206
|
+
cli.add_command(keep_alive_prs)
|
|
207
|
+
cli.add_command(notifications)
|
|
208
|
+
cli.add_command(generate_releases)
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
cli()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""AI agent configuration for github-overlord."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from pydantic_ai import Agent, ModelSettings
|
|
7
|
+
|
|
8
|
+
DEFAULT_MODEL = "google:gemini-3.8-flash"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def normalize_model_name(model_name: str) -> str:
|
|
12
|
+
"""Ensure provider prefix is present for shorthand model names."""
|
|
13
|
+
cleaned = model_name.strip()
|
|
14
|
+
if ":" in cleaned or "/" in cleaned:
|
|
15
|
+
return cleaned
|
|
16
|
+
if cleaned.startswith("gemini"):
|
|
17
|
+
return f"google:{cleaned}"
|
|
18
|
+
if cleaned.startswith(("gpt", "o1", "o3")):
|
|
19
|
+
return f"openai:{cleaned}"
|
|
20
|
+
if cleaned.startswith("claude"):
|
|
21
|
+
return f"anthropic:{cleaned}"
|
|
22
|
+
return cleaned
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def resolve_model_name(model: str | None = None) -> str:
|
|
26
|
+
"""Resolve and normalize active model name from argument, env vars, or default."""
|
|
27
|
+
raw_model = model or os.getenv("GITHUB_OVERLORD_MODEL") or DEFAULT_MODEL
|
|
28
|
+
return normalize_model_name(raw_model)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_expected_ai_key_var(model: str | None = None) -> str:
|
|
32
|
+
"""Return the expected environment variable name for the active model provider."""
|
|
33
|
+
selected_model = resolve_model_name(model)
|
|
34
|
+
provider = selected_model.split(":")[0] if ":" in selected_model else "google"
|
|
35
|
+
return (
|
|
36
|
+
"GOOGLE_API_KEY"
|
|
37
|
+
if provider in ("google", "gemini")
|
|
38
|
+
else f"{provider.upper()}_API_KEY"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def is_ai_key_configured(model: str | None = None) -> bool:
|
|
43
|
+
"""Check if an API key is configured for the active model provider."""
|
|
44
|
+
if os.getenv("GITHUB_OVERLORD_AI_KEY"):
|
|
45
|
+
return True
|
|
46
|
+
return bool(os.getenv(get_expected_ai_key_var(model)))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_agent(
|
|
50
|
+
model: str | None = None,
|
|
51
|
+
output_type: Any = None,
|
|
52
|
+
system_prompt: str | None = None,
|
|
53
|
+
) -> Agent[Any, Any]:
|
|
54
|
+
"""Create a configured Pydantic AI Agent."""
|
|
55
|
+
selected_model = resolve_model_name(model)
|
|
56
|
+
|
|
57
|
+
# Map generic GITHUB_OVERLORD_AI_KEY to provider-specific env var if not already set
|
|
58
|
+
if generic_key := os.getenv("GITHUB_OVERLORD_AI_KEY"):
|
|
59
|
+
os.environ.setdefault(get_expected_ai_key_var(selected_model), generic_key)
|
|
60
|
+
|
|
61
|
+
kwargs: dict[str, Any] = {}
|
|
62
|
+
if output_type is not None:
|
|
63
|
+
kwargs["output_type"] = output_type
|
|
64
|
+
if system_prompt is not None:
|
|
65
|
+
kwargs["system_prompt"] = system_prompt
|
|
66
|
+
if selected_model.startswith("google:"):
|
|
67
|
+
kwargs["model_settings"] = ModelSettings(thinking="low")
|
|
68
|
+
|
|
69
|
+
return Agent(selected_model, **kwargs)
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"""Configuration for github-overlord."""
|
|
2
|
+
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
import jinja2
|
|
5
6
|
|
|
6
7
|
# Set up paths
|
|
7
|
-
|
|
8
|
-
DATA_DIRECTORY =
|
|
8
|
+
PACKAGE_DIRECTORY = Path(__file__).parent.resolve()
|
|
9
|
+
DATA_DIRECTORY = PACKAGE_DIRECTORY / "data"
|
|
10
|
+
if not DATA_DIRECTORY.exists():
|
|
11
|
+
DATA_DIRECTORY = PACKAGE_DIRECTORY.parent / "data"
|
|
12
|
+
|
|
9
13
|
RELEASE_ANALYSIS_PROMPT_TEMPLATE = DATA_DIRECTORY / "release_analysis_prompt.j2"
|
|
10
14
|
|
|
11
15
|
# Jinja2 environment for templates
|
|
@@ -13,5 +17,5 @@ JINJA_ENV = jinja2.Environment(
|
|
|
13
17
|
loader=jinja2.FileSystemLoader(searchpath=str(DATA_DIRECTORY)),
|
|
14
18
|
autoescape=False, # No HTML escaping needed for prompts
|
|
15
19
|
trim_blocks=True,
|
|
16
|
-
lstrip_blocks=True
|
|
20
|
+
lstrip_blocks=True,
|
|
17
21
|
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
You are analyzing commits for an open-source project to determine if a new release should be created.
|
|
2
|
+
|
|
3
|
+
Consider these factors, in order of importance:
|
|
4
|
+
|
|
5
|
+
1. Number of commits (more commits = more likely to release, but even 1-2 significant commits may warrant a release)
|
|
6
|
+
2. Type of changes (features, bug fixes, refactoring, docs, tests, dependencies; new features and bug fixes are more likely to warrant a release)
|
|
7
|
+
3. Impact of changes (major features vs minor tweaks)
|
|
8
|
+
4. Time since last release (longer time = more likely, but do not weight this heavily)
|
|
9
|
+
|
|
10
|
+
Guidelines for `decision`:
|
|
11
|
+
|
|
12
|
+
- "yes" = clear value in creating a release (new features, important fixes, meaningful updates)
|
|
13
|
+
- "no" = minimal changes (typos, minor docs, CI tweaks only)
|
|
14
|
+
- "maybe" = borderline case (some value but not urgent)
|
|
15
|
+
- For dependency updates: patch bump
|
|
16
|
+
- For bug fixes: patch bump
|
|
17
|
+
- For new features: minor bump
|
|
18
|
+
- For breaking changes: major bump
|
|
19
|
+
|
|
20
|
+
Guidelines for `release_notes`:
|
|
21
|
+
|
|
22
|
+
- Write a markdown changelog suitable for a GitHub release
|
|
23
|
+
- Use markdown headers, lists, and formatting
|
|
24
|
+
- Be concise and avoid adjectives
|
|
25
|
+
- Focus on user-facing changes and impacts. Do not include internal dev tooling, documentation, build system, package/dependency updates, etc updates.
|
|
26
|
+
- Organize by type of change (Features, Bug Fixes, etc.) when there are more than a handful of changes.
|
|
27
|
+
|
|
28
|
+
Example format:
|
|
29
|
+
|
|
30
|
+
```markdown
|
|
31
|
+
## What's Changed
|
|
32
|
+
|
|
33
|
+
### Features
|
|
34
|
+
- Added support for Python 3.12
|
|
35
|
+
- New configuration option for custom timeouts
|
|
36
|
+
|
|
37
|
+
### Bug Fixes
|
|
38
|
+
- Fixed authentication bug when using OAuth tokens
|
|
39
|
+
- Resolved memory leak in background worker
|
|
40
|
+
|
|
41
|
+
### Performance
|
|
42
|
+
- Improved commit analysis by 40%
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Repository: {{ repo_name }}
|
|
46
|
+
{{ last_release_info }}
|
|
47
|
+
Number of commits: {{ commit_count }}
|
|
48
|
+
|
|
49
|
+
Commits:
|
|
50
|
+
{{ commit_summary }}
|