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
17 changes: 7 additions & 10 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,19 @@ jobs:

steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.14"

# ref: https://github.com/pre-commit/action
- uses: pre-commit/action@v3.0.1
# ref: https://github.com/j178/prek-action
- uses: j178/prek-action@4e14d07f9231acabce116ccfca13b13dd9755ece # v3.0.0
- name: Help message if pre-commit fail
if: ${{ failure() }}
run: |
echo "You can install pre-commit hooks to automatically run formatting"
echo "on each commit with:"
echo " pre-commit install"
echo "You can install pre-commit hooks (with prek or pre-commit) to"
echo "automatically run formatting on each commit with:"
echo " prek install"
echo "or you can run by hand on staged files with"
echo " pre-commit run"
echo " prek run"
echo "or after-the-fact on already committed files with"
echo " pre-commit run --all-files"
echo " prek run --all-files"

tests:
runs-on: ubuntu-24.04
Expand Down
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@
repos:
# Autoformat: Python code
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: f0fe93c067104b76ffb58852abe79673a8429bd1 # frozen: v0.11.8
rev: v0.16.7
hooks:
- id: ruff
- id: ruff-check
args: ["--fix", "--show-fixes", "--exit-non-zero-on-fix"]
- id: ruff-format

# Autoformat: markdown, yaml
- repo: https://github.com/pre-commit/mirrors-prettier
rev: 787fb9f542b140ba0b2aced38e6a3e68021647a3 # frozen: v3.5.3
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.9.6
hooks:
- id: prettier
exclude: tests/.*

# Misc...
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # v6.0.0
rev: v6.0.0 # v6.0.0
# ref: https://github.com/pre-commit/pre-commit-hooks#hooks-available
hooks:
# Sanity checks
Expand Down
2 changes: 1 addition & 1 deletion docs/example_cache.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"metadata": {},
"outputs": [],
"source": [
"from github_activity.cache import get_cache_stats, load_from_cache\n",
"from github_activity.github_activity import get_activity\n",
"from github_activity.cache import load_from_cache, get_cache_stats\n",
"\n",
"activity = get_activity(\"jupyter/repo2docker\", \"2019\", cache=True)"
]
Expand Down
4 changes: 2 additions & 2 deletions docs/use.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ df = get_activity(
until="2023-12-31",
auth="your-github-token",
kind=None,
cache=None
cache=None,
)
```

Expand All @@ -277,5 +277,5 @@ There are some helper functions for this. For example, to extract nested comment
from github_activity import get_activity, extract_comments

df = get_activity(...)
comments_df = extract_comments(df['comments'])
comments_df = extract_comments(df["comments"])
```
4 changes: 2 additions & 2 deletions github_activity/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "1.1.7"
__all__ = ["get_activity", "generate_activity_md"]
__all__ = ["generate_activity_md", "get_activity"]

from .github_activity import get_activity, generate_activity_md
from .github_activity import generate_activity_md, get_activity
2 changes: 1 addition & 1 deletion github_activity/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def _cache_data(query_data, path_cache):
path_repo_cache.mkdir(parents=True, exist_ok=True)

# First pull issues
def _categorize_item(item):
def _categorize_item(item, repo=repo):
if f"{repo}/issues" in item:
out = "issue"
else:
Expand Down
36 changes: 18 additions & 18 deletions github_activity/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
import json
import os
import sys
from subprocess import PIPE
from subprocess import run
from subprocess import PIPE, run

from .git import _git_installed_check
from .git import _git_toplevel_path
from .github_activity import _parse_target
from .github_activity import generate_activity_md
from .github_activity import generate_all_activity_md
from .git import _git_installed_check, _git_toplevel_path
from .github_activity import (
_parse_target,
generate_activity_md,
generate_all_activity_md,
)

DESCRIPTION = "Generate a markdown changelog of GitHub activity within a date window."

Expand Down Expand Up @@ -191,7 +191,7 @@ def main():
if not args.target:
err = "Could not automatically detect remote, and none was given."
try:
out = run("git remote -v".split(), stdout=PIPE)
out = run(["git", "remote", "-v"], stdout=PIPE, check=False)
remotes = out.stdout.decode().split("\n")
remotes = [ii for ii in remotes if ii]
remotes = {
Expand All @@ -214,16 +214,16 @@ def main():
except Exception:
raise ValueError(err)

common_kwargs = dict(
kind=args.kind,
auth=args.auth,
tags=tags,
include_issues=bool(args.include_issues),
include_opened=bool(args.include_opened),
strip_brackets=bool(args.strip_brackets),
branch=args.branch,
ignored_contributors=args.ignore_contributor,
)
common_kwargs = {
"kind": args.kind,
"auth": args.auth,
"tags": tags,
"include_issues": bool(args.include_issues),
"include_opened": bool(args.include_opened),
"strip_brackets": bool(args.strip_brackets),
"branch": args.branch,
"ignored_contributors": args.ignore_contributor,
}

# Wrap in a try/except so we don't have an ugly stack trace if there's an error
try:
Expand Down
76 changes: 38 additions & 38 deletions github_activity/github_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
import sys
from collections import OrderedDict
from json import loads
from subprocess import CalledProcessError
from subprocess import PIPE
from subprocess import run
from subprocess import PIPE, CalledProcessError, run
from tempfile import TemporaryDirectory

import dateutil.parser
Expand All @@ -25,7 +23,6 @@
from .cache import _cache_data
from .graphql import GitHubGraphQlQuery


# The tags and description to use in creating subsets of PRs
TAGS_METADATA_BASE = OrderedDict(
[
Expand Down Expand Up @@ -175,7 +172,9 @@ def get_activity(
if auth is None:
# Attempt to use the gh cli if installed
try:
p = run(["gh", "auth", "token"], text=True, capture_output=True)
p = run(
["gh", "auth", "token"], text=True, capture_output=True, check=False
)
auth = p.stdout.strip()
except CalledProcessError:
print(
Expand Down Expand Up @@ -270,7 +269,7 @@ def generate_all_activity_md(
include_opened=False,
strip_brackets=False,
branch=None,
ignored_contributors: list[str] = None,
ignored_contributors: list[str] | None = None,
):
"""Generate a full markdown changelog of GitHub activity of a repo based on release tags.

Expand Down Expand Up @@ -316,10 +315,12 @@ def generate_all_activity_md(
# Get the sha and tag name for each tag in the target repo
with TemporaryDirectory() as td:
subprocess.run(
shlex.split(f"git clone https://github.com/{target} repo"), cwd=td
shlex.split(f"git clone https://github.com/{target} repo"),
cwd=td,
check=False,
)
repo = os.path.join(td, "repo")
subprocess.run(shlex.split("git fetch origin --tags"), cwd=repo)
subprocess.run(shlex.split("git fetch origin --tags"), cwd=repo, check=False)

cmd = 'git log --tags --simplify-by-decoration --pretty="format:%h | %D"'
data = (
Expand Down Expand Up @@ -402,8 +403,7 @@ def add(self, contributor):
def __iter__(self):
if self.author:
yield self.author
for item in sorted(self.other - {self.author}):
yield item
yield from sorted(self.other - {self.author})


def generate_activity_md(
Expand All @@ -418,7 +418,7 @@ def generate_activity_md(
strip_brackets=False,
heading_level=1,
branch=None,
ignored_contributors: list[str] = None,
ignored_contributors: list[str] | None = None,
):
"""Generate a markdown changelog of GitHub activity within a date window.

Expand Down Expand Up @@ -536,12 +536,10 @@ def ignored_user(username):
return True

# Check against user-specified ignored contributors
if ignored_contributors and any(
fnmatch.fnmatch(username, user) for user in ignored_contributors
):
return True

return False
return bool(
ignored_contributors
and any(fnmatch.fnmatch(username, user) for user in ignored_contributors)
)

def filter_ignored(userlist):
return {user for user in userlist if not ignored_user(user)}
Expand Down Expand Up @@ -617,7 +615,7 @@ def filter_ignored(userlist):
comment_contributors = comment_contributor_counts[
comment_contributor_counts >= comment_others_cutoff
].index.tolist()
all_contributors |= set(c for c in comment_contributors if isinstance(c, str))
all_contributors |= {c for c in comment_contributors if isinstance(c, str)}

closed_mask, opened_mask = _activity_window_masks(
data, data.since_dt_str, data.until_dt_str, data.since_is_git_ref
Expand Down Expand Up @@ -646,7 +644,7 @@ def filter_ignored(userlist):
# Add any contributors to a merged PR to our contributors list
# Filter out NaN values and non-strings
pr_contributors = closed_prs["contributors"].explode().unique().tolist()
all_contributors |= set(c for c in pr_contributors if isinstance(c, str))
all_contributors |= {c for c in pr_contributors if isinstance(c, str)}

# Define categories for a few labels
if tags is None:
Expand All @@ -660,7 +658,7 @@ def filter_ignored(userlist):
tags_metadata = {key: val for key, val in TAGS_METADATA_BASE.items() if key in tags}

# Initialize our tags with empty metadata
for key, vals in tags_metadata.items():
for vals in tags_metadata.values():
vals.update(
{
"mask": None,
Expand All @@ -673,14 +671,18 @@ def filter_ignored(userlist):
# Track which PRs have already been assigned to prevent duplicates
assigned_prs = set()

for kind, kindmeta in tags_metadata.items():
for kindmeta in tags_metadata.values():
# First find the PRs based on tag
mask = closed_prs["labels"].map(
lambda a: any(ii == jj for ii in kindmeta["tags"] for jj in a)
lambda a, kindmeta=kindmeta: any(
ii == jj for ii in kindmeta["tags"] for jj in a
)
)
# Now find PRs based on prefix
mask_pre = closed_prs["title"].map(
lambda title: any(f"{ipre}:" in title for ipre in kindmeta["pre"])
lambda title, kindmeta=kindmeta: any(
f"{ipre}:" in title for ipre in kindmeta["pre"]
)
)
mask = mask | mask_pre

Expand All @@ -705,39 +707,39 @@ def filter_ignored(userlist):

# Add some optional kinds of PRs / issues
tags_metadata.update(
dict(others={"description": other_description, "md": [], "data": others})
{"others": {"description": other_description, "md": [], "data": others}}
)
if include_issues:
tags_metadata.update(
dict(
closed_issues={
{
"closed_issues": {
"description": "Closed issues",
"md": [],
"data": closed_issues,
}
)
}
)
if include_opened:
tags_metadata.update(
dict(
opened_issues={
{
"opened_issues": {
"description": "Opened issues",
"md": [],
"data": opened_issues,
}
)
}
)
if include_opened:
tags_metadata.update(
dict(opened_prs={"description": "Opened PRs", "md": [], "data": opened_prs})
{"opened_prs": {"description": "Opened PRs", "md": [], "data": opened_prs}}
)

# Generate the markdown
prs = tags_metadata

extra_head = "#" * (heading_level - 1)

for kind, items in prs.items():
for items in prs.values():
n_orgs = len(items["data"]["org"].unique())
for org, idata in items["data"].groupby("org"):
if n_orgs > 1:
Expand Down Expand Up @@ -794,7 +796,7 @@ def filter_ignored(userlist):
"",
f"([full changelog]({changelog_url}))",
]
for kind, info in prs.items():
for info in prs.values():
if len(info["md"]) > 0:
md += [""]
md.append(f"{extra_head}## {info['description']}")
Expand Down Expand Up @@ -943,9 +945,7 @@ def _get_datetime_and_type(org, repo, datetime_or_git_ref, auth):
return (dt, False)
except Exception:
raise ValueError(
"{0} not found as a ref or valid date format".format(
datetime_or_git_ref
)
f"{datetime_or_git_ref} not found as a ref or valid date format"
)


Expand Down Expand Up @@ -981,7 +981,7 @@ def _get_latest_release_tag(org, repo):
]
print(f"Auto-detecting latest release tag for: {org}/{repo}", file=sys.stderr)
print(f"Running command: {' '.join(cmd)}", file=sys.stderr)
out = run(cmd, stdout=PIPE)
out = run(cmd, stdout=PIPE, check=False)
try:
json = out.stdout.decode()
release_data = loads(json)
Expand All @@ -998,6 +998,6 @@ def _get_latest_release_tag(org, repo):
f"Error getting latest release tag for {org}/{repo}: {e}", file=sys.stderr
)
print("Reverting to using latest local git tag...", file=sys.stderr)
out = run("git describe --tags".split(), stdout=PIPE)
out = run(["git", "describe", "--tags"], stdout=PIPE, check=False)
tag = out.stdout.decode().rsplit("-", 2)[0]
return tag
Loading
Loading