gitlogstats 1.5.0__tar.gz → 1.5.2__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.
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/PKG-INFO +1 -1
- gitlogstats-1.5.2/docs/examples/README.md +70 -0
- gitlogstats-1.5.2/docs/examples/assignment-stats.sh +117 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/pyproject.toml +1 -1
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.claude/CLAUDE.md +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.claude/agents/developer-agent.md +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.claude/agents/spec-reviewer.md +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.claude/hooks/format-edited.sh +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.claude/hooks/guard-paths.sh +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.claude/hooks/guard-paths.test.mjs +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.claude/hooks/verify.sh +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.claude/settings.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.claude/skills/phase-handoff/SKILL.md +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.claude/skills/stale-check/SKILL.md +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.github/workflows/build.yaml +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.gitignore +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/.prettierrc.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/LICENSE +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/Pipfile +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/Pipfile.lock +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/README.md +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/docs/SPEC.md +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/package-lock.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/package.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/repos.txt +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/requirements.txt +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/scripts/board/config.mjs +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/scripts/board/derive.mjs +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/scripts/board/derive.test.mjs +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/scripts/board/manifest.yaml +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/scripts/board/sync.mjs +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/src/gitlogstats/__init__.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/src/gitlogstats/__main__.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/src/gitlogstats/clones.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/src/gitlogstats/git_logs_parser.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/src/gitlogstats/github.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/src/gitlogstats/github_stats.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/src/gitlogstats/identity.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/fake_gh.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/fixtures/github/commit_linked.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/fixtures/github/commit_unlinked.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/fixtures/github/contributors.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/fixtures/github/contributors_page2.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/fixtures/github/pulls_page1.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/fixtures/github/pulls_page2.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/fixtures/github/user_full.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/fixtures/github/user_private.json +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/test_clobber.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/test_git_logs_parser.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/test_github.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/test_identity.py +0 -0
- {gitlogstats-1.5.0 → gitlogstats-1.5.2}/tests/test_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: gitlogstats
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.2
|
|
4
4
|
Summary: Track developers' contributions to one or more git repositories within a particular time range.
|
|
5
5
|
Project-URL: Homepage, https://github.com/bloombar/gitlogstats
|
|
6
6
|
Project-URL: Repository, https://github.com/bloombar/gitlogstats.git
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
## `assignment-stats.sh`: statistics for one assignment
|
|
4
|
+
|
|
5
|
+
Reports every student's contributions to one assignment: all the repositories in a GitHub organization whose names start with the same prefix (e.g. `web-app-exercise-`), over a date range. It works in bash on macOS, Linux, WSL and Git Bash on Windows.
|
|
6
|
+
|
|
7
|
+
### Requirements
|
|
8
|
+
|
|
9
|
+
- Python 3
|
|
10
|
+
- git
|
|
11
|
+
- The [GitHub CLI](https://cli.github.com) (`gh`), signed in with `gh auth login`
|
|
12
|
+
|
|
13
|
+
You don't need to install `gitlogstats` yourself: the script installs or upgrades it each time it runs (see [What the script does](#what-the-script-does)).
|
|
14
|
+
|
|
15
|
+
### Usage
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
./assignment-stats.sh ORGANIZATION_NAME REPO_PREFIX START_DATE END_DATE [gitlogstats options...]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Argument | Meaning | Example |
|
|
22
|
+
| ------------------- | ---------------------------------------------------------------------- | ---------------------------- |
|
|
23
|
+
| `ORGANIZATION_NAME` | The GitHub organization that holds the students' repositories | `software-students-fall2029` |
|
|
24
|
+
| `REPO_PREFIX` | The start of the repository names for this assignment | `web-app-exercise-` |
|
|
25
|
+
| `START_DATE` | First day of the period, `mm/dd/yyyy` | `09/01/2029` |
|
|
26
|
+
| `END_DATE` | Last day of the period, `mm/dd/yyyy` (included) | `10/16/2029` |
|
|
27
|
+
| options | Anything after the four arguments is passed to `gitlogstats` unchanged | `-f markdown` |
|
|
28
|
+
|
|
29
|
+
Run it from any directory. The results are printed to standard output, so redirect them to save them; progress messages go to standard error and stay on screen.
|
|
30
|
+
|
|
31
|
+
### Examples
|
|
32
|
+
|
|
33
|
+
Save a CSV of one assignment's statistics:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
./assignment-stats.sh software-students-fall2029 web-app-exercise- 09/01/2029 10/16/2029 > web-app-exercise.csv
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Print a Markdown table instead:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
./assignment-stats.sh software-students-fall2029 web-app-exercise- 09/01/2029 10/16/2029 -f markdown
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Leave out students with no activity in the period, as JSON:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
./assignment-stats.sh software-students-fall2029 web-app-exercise- 09/01/2029 10/16/2029 -c -f json > web-app-exercise.json
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
To run it from elsewhere, give its path, e.g. `bash docs/examples/assignment-stats.sh ...`. On Windows, run it from Git Bash or WSL, not from Command Prompt or PowerShell.
|
|
52
|
+
|
|
53
|
+
See the main [README](../../README.md) for every `gitlogstats` option and what each column means.
|
|
54
|
+
|
|
55
|
+
### What the script does
|
|
56
|
+
|
|
57
|
+
1. Checks the arguments and that Python, git and `gh` are installed and `gh` is signed in. If anything is missing it stops with a message saying what.
|
|
58
|
+
2. Installs or upgrades `gitlogstats` in a Python environment of its own, `~/.gitlogstats-venv`, reused by later runs. Set `GITLOGSTATS_VENV` to keep it elsewhere. A separate environment is needed because Homebrew Python and recent Debian and Ubuntu releases refuse a system-wide `pip install`.
|
|
59
|
+
3. Lists the organization's repositories whose names start with `REPO_PREFIX`, into a temporary file that is deleted when the script ends, even after an error. An existing `repos.txt` in your directory is never touched. If no repository matches, it stops with an error.
|
|
60
|
+
4. Runs `gitlogstats` on those repositories with `--github` and `--clobber`. `--github` adds the GitHub columns (pull requests, pushes, website commits and emails) and combines each student's git names into one row; `--clobber` deletes the clones it makes in `./repos/` once the results are printed.
|
|
61
|
+
|
|
62
|
+
### Troubleshooting
|
|
63
|
+
|
|
64
|
+
| Message | What to do |
|
|
65
|
+
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
66
|
+
| `'...' is not a date in mm/dd/yyyy format` | Write dates with two-digit month and day and a four-digit year, e.g. `09/01/2029` |
|
|
67
|
+
| `the GitHub CLI is not signed in` | Run `gh auth login` |
|
|
68
|
+
| `no repositories in '...' start with '...'` | Check the organization name and prefix; the prefix is case-sensitive |
|
|
69
|
+
| `could not create a virtual environment` | On Debian or Ubuntu, install the venv module: `sudo apt install python3-venv` |
|
|
70
|
+
| `Permission denied` when running `./assignment-stats.sh` | Make it executable with `chmod +x assignment-stats.sh`, or run it with `bash assignment-stats.sh ...` |
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Report every student's contributions to one assignment: all repositories in
|
|
4
|
+
# a GitHub organization whose names start with a given prefix, over a date
|
|
5
|
+
# range. Works in bash on macOS, Linux, other Unix systems, WSL and Git Bash.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# ./assignment-stats.sh ORGANIZATION_NAME REPO_PREFIX START_DATE END_DATE [gitlogstats options...]
|
|
9
|
+
#
|
|
10
|
+
# Example:
|
|
11
|
+
# ./assignment-stats.sh software-students-fall2029 web-app-exercise- 09/01/2029 10/16/2029 > stats.csv
|
|
12
|
+
# ./assignment-stats.sh software-students-fall2029 web-app-exercise- 09/01/2029 10/16/2029 -f markdown
|
|
13
|
+
#
|
|
14
|
+
# Dates are mm/dd/yyyy and both days are included. gitlogstats always runs
|
|
15
|
+
# with --github, so the GitHub columns are included. Any options after the four
|
|
16
|
+
# arguments are passed to gitlogstats unchanged (e.g. -f json, -c).
|
|
17
|
+
# The results go to standard output; progress messages go to standard error.
|
|
18
|
+
#
|
|
19
|
+
# Requires: Python 3, git, and the GitHub CLI (gh), signed in with
|
|
20
|
+
# `gh auth login` (gh is used to list the organization's repositories).
|
|
21
|
+
# gitlogstats is installed or upgraded automatically in its own Python
|
|
22
|
+
# environment (~/.gitlogstats-venv, or $GITLOGSTATS_VENV).
|
|
23
|
+
|
|
24
|
+
set -euo pipefail
|
|
25
|
+
|
|
26
|
+
usage() {
|
|
27
|
+
echo "usage: $(basename "$0") ORGANIZATION_NAME REPO_PREFIX START_DATE END_DATE [gitlogstats options...]" >&2
|
|
28
|
+
echo "example: $(basename "$0") software-students-fall2029 web-app-exercise- 09/01/2029 10/16/2029" >&2
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if [ "$#" -lt 4 ]; then
|
|
32
|
+
usage
|
|
33
|
+
exit 2
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# retrieve command-line arguments
|
|
37
|
+
ORGANIZATION_NAME=$1
|
|
38
|
+
REPO_PREFIX=$2
|
|
39
|
+
START_DATE=$3
|
|
40
|
+
END_DATE=$4
|
|
41
|
+
shift 4 # anything left is passed to gitlogstats
|
|
42
|
+
|
|
43
|
+
# check the dates before doing any work
|
|
44
|
+
for date in "$START_DATE" "$END_DATE"; do
|
|
45
|
+
if ! [[ "$date" =~ ^[0-9]{2}/[0-9]{2}/[0-9]{4}$ ]]; then
|
|
46
|
+
echo "error: '$date' is not a date in mm/dd/yyyy format" >&2
|
|
47
|
+
usage
|
|
48
|
+
exit 2
|
|
49
|
+
fi
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
# find a Python 3 interpreter: python3 on macOS and Linux, often python on Git Bash
|
|
53
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
54
|
+
PYTHON=python3
|
|
55
|
+
elif command -v python >/dev/null 2>&1; then
|
|
56
|
+
PYTHON=python
|
|
57
|
+
else
|
|
58
|
+
echo "error: Python 3 is not installed" >&2
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
for tool in git gh; do
|
|
63
|
+
if ! command -v "$tool" >/dev/null 2>&1; then
|
|
64
|
+
echo "error: '$tool' is not installed" >&2
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
done
|
|
68
|
+
|
|
69
|
+
if ! gh auth status >/dev/null 2>&1; then
|
|
70
|
+
echo "error: the GitHub CLI is not signed in; run 'gh auth login' first" >&2
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# install or upgrade gitlogstats in a virtual environment of its own: many
|
|
75
|
+
# systems (Homebrew Python, recent Debian and Ubuntu) refuse a system-wide
|
|
76
|
+
# pip install. The environment is kept and reused by later runs; set
|
|
77
|
+
# GITLOGSTATS_VENV to put it somewhere else.
|
|
78
|
+
VENV=${GITLOGSTATS_VENV:-"$HOME/.gitlogstats-venv"}
|
|
79
|
+
if [ ! -d "$VENV" ]; then
|
|
80
|
+
echo "Creating a Python environment for gitlogstats in $VENV..." >&2
|
|
81
|
+
if ! "$PYTHON" -m venv "$VENV"; then
|
|
82
|
+
echo "error: could not create a virtual environment (on Debian or Ubuntu, install python3-venv)" >&2
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
fi
|
|
86
|
+
# the environment's Python is in bin/ on macOS, Linux and WSL, Scripts/ on Windows
|
|
87
|
+
if [ -x "$VENV/bin/python" ]; then
|
|
88
|
+
VENV_PYTHON="$VENV/bin/python"
|
|
89
|
+
else
|
|
90
|
+
VENV_PYTHON="$VENV/Scripts/python.exe"
|
|
91
|
+
fi
|
|
92
|
+
echo "Installing the latest gitlogstats..." >&2
|
|
93
|
+
"$VENV_PYTHON" -m pip install --quiet --disable-pip-version-check --upgrade gitlogstats >&2
|
|
94
|
+
|
|
95
|
+
# list the assignment's repositories in a temporary file, so an existing
|
|
96
|
+
# repos.txt in this directory is never overwritten
|
|
97
|
+
REPOS_FILE=$(mktemp "${TMPDIR:-/tmp}/gitlogstats-repos.XXXXXX")
|
|
98
|
+
trap 'rm -f "$REPOS_FILE"' EXIT
|
|
99
|
+
|
|
100
|
+
# the prefix is read by gh's built-in jq from an environment variable, so it
|
|
101
|
+
# needs no quoting and jq does not have to be installed
|
|
102
|
+
REPO_PREFIX="$REPO_PREFIX" gh repo list "$ORGANIZATION_NAME" --limit 1000 \
|
|
103
|
+
--json name,url \
|
|
104
|
+
--jq '.[] | select(.name | startswith(env.REPO_PREFIX)) | .url' >"$REPOS_FILE"
|
|
105
|
+
|
|
106
|
+
count=$(grep -c . "$REPOS_FILE" || true)
|
|
107
|
+
if [ "$count" -eq 0 ]; then
|
|
108
|
+
echo "error: no repositories in '$ORGANIZATION_NAME' start with '$REPO_PREFIX'" >&2
|
|
109
|
+
exit 1
|
|
110
|
+
fi
|
|
111
|
+
echo "Analyzing $count repositories from $START_DATE to $END_DATE..." >&2
|
|
112
|
+
|
|
113
|
+
# --github adds the GitHub columns (pull requests, pushes, website commits,
|
|
114
|
+
# emails) and combines each student's git names into one row; --clobber
|
|
115
|
+
# deletes the clones once the results are printed
|
|
116
|
+
"$VENV_PYTHON" -m gitlogstats -rf "$REPOS_FILE" -s "$START_DATE" -e "$END_DATE" \
|
|
117
|
+
--github --clobber "$@"
|
|
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gitlogstats"
|
|
7
7
|
description = "Track developers' contributions to one or more git repositories within a particular time range."
|
|
8
|
-
version = "1.5.
|
|
8
|
+
version = "1.5.2"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="Tobody Tothing", email="nobody.nothing@nowhere.com" },
|
|
11
11
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|