Skip to content

Analyze Repositories with PAT #109

Analyze Repositories with PAT

Analyze Repositories with PAT #109

Workflow file for this run

name: Analyze Repositories with PAT
on:
schedule:
- cron: "0 0 * * 0"
workflow_dispatch:
permissions:
contents: write
jobs:
count-lines:
# Keep this workflow copyable without running PAT analysis in this demo repo.
if: github.repository != 'arhamkhnz/github-code-analyzer'
runs-on: ubuntu-latest
env:
# Use cloc language names. Highlighted languages get their own README row.
HIGHLIGHT_LANGS: "JavaScript,TypeScript,JSX,Vuejs Component,PHP,C#"
# These languages are excluded from the cloc total.
IGNORE_LANGS: "JSON,HTML,CSS,SCSS,Sass,Markdown,SVG,XML,YAML,TOML,CSV,Text,Properties"
steps:
- name: Checkout report repository
uses: actions/checkout@v7
with:
persist-credentials: false
- name: Install cloc and jq
run: |
sudo apt-get update
sudo apt-get install -y cloc jq
- name: Discover repositories accessible to the PAT
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
shell: bash
run: |
set -euo pipefail
if [[ -z "$GH_TOKEN" ]]; then
echo "Add a GH_PAT repository secret before running this workflow" >&2
exit 1
fi
# The authenticated-user endpoint can include owned, collaborator,
# public, and private repositories. Fetch every page, then skip forks.
if ! gh api --paginate --slurp 'user/repos?per_page=100' > "$RUNNER_TEMP/pat-repos.json" 2>/dev/null; then
echo "Could not list repositories. Check GH_PAT access." >&2
exit 1
fi
jq -e 'type == "array" and all(.[]; type == "array")' "$RUNNER_TEMP/pat-repos.json" > /dev/null
if ! jq -e 'any(.[][]; .private == true and .fork == false)' "$RUNNER_TEMP/pat-repos.json" > /dev/null; then
echo "::warning::No private non-fork repositories were returned. Check the token's resource owner and repository access if you expected private repos."
fi
jq -r '.[][] | select(.fork == false) | .full_name' \
"$RUNNER_TEMP/pat-repos.json" > "$RUNNER_TEMP/pat-source-repos.txt"
- name: Clone default branches
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
shell: bash
run: |
set -euo pipefail
gh auth setup-git --hostname github.com --force
mapfile -t repos < "$RUNNER_TEMP/pat-source-repos.txt"
clone_root="$RUNNER_TEMP/pat-code-repos"
mkdir -p "$clone_root"
for index in "${!repos[@]}"; do
repo="${repos[$index]}"
if ! git clone --quiet --depth 1 --single-branch \
"https://github.com/$repo.git" "$clone_root/$index" > /dev/null 2>&1; then
echo "A repository could not be cloned. Check GH_PAT access." >&2
exit 1
fi
done
- name: Count code lines
shell: bash
run: |
set -euo pipefail
mkdir -p output
if [[ -s "$RUNNER_TEMP/pat-source-repos.txt" ]]; then
if ! cloc "$RUNNER_TEMP/pat-code-repos" --json \
--report-file=output/cloc-output.json \
--exclude-dir=.git --exclude-lang="$IGNORE_LANGS" > /dev/null 2>&1; then
echo "Could not count code lines; no stats were published." >&2
exit 1
fi
else
jq -n '{SUM: {code: 0}}' > output/cloc-output.json
fi
jq 'del(.header)' output/cloc-output.json > "$RUNNER_TEMP/pat-cloc-summary.json"
mv "$RUNNER_TEMP/pat-cloc-summary.json" output/cloc-output.json
- name: Update PAT section in README
shell: python
run: |
import json
import os
import re
from pathlib import Path
report = json.loads(Path("output/cloc-output.json").read_text())
total = report.get("SUM", {}).get("code")
if not isinstance(total, int) or total < 0:
raise ValueError("cloc did not produce a valid code total")
languages = []
for name, counts in report.items():
if name in {"header", "SUM"}:
continue
code = counts.get("code")
if not isinstance(code, int) or code < 0:
raise ValueError(f"Invalid cloc count for {name}")
if code:
languages.append((name, code))
if sum(code for _, code in languages) != total:
raise ValueError("Language counts do not match the cloc total")
highlights = {name.strip() for name in os.environ["HIGHLIGHT_LANGS"].split(",")}
named = sorted(
((name, code) for name, code in languages if name in highlights),
key=lambda item: (-item[1], item[0]),
)
other = sum(code for name, code in languages if name not in highlights)
width = max([12, *(len(name) for name, _ in named)])
fence = chr(96) * 3
rows = [fence, "[ LANGUAGES BREAKDOWN ]", ""]
rows += [f"{name:<{width}} --> {code:,} lines" for name, code in named]
if other:
rows.append(f"{'Others':<{width}} --> {other:,} lines")
rows += ["", f"[ TOTAL LINES OF CODE: {total:,} ]", fence]
start = re.compile(r"^<!-- LANGUAGES BREAKDOWN START -->$", re.MULTILINE)
end = re.compile(r"^<!-- LANGUAGES BREAKDOWN END -->$", re.MULTILINE)
readme = Path("README.md")
content = readme.read_text()
starts = list(start.finditer(content))
ends = list(end.finditer(content))
if len(starts) != 1 or len(ends) != 1 or starts[0].end() >= ends[0].start():
raise ValueError("README must contain exactly one ordered PAT stats marker pair")
updated = (
content[: starts[0].end()]
+ "\n"
+ "\n".join(rows)
+ "\n"
+ content[ends[0].start() :]
)
if updated != content:
readme.write_text(updated)
- name: Commit changed PAT stats
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
git add README.md output/cloc-output.json
if git diff --cached --quiet; then
echo "PAT code statistics are unchanged"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -m "chore: update PAT repository code stats"
gh auth setup-git --hostname github.com --force
git push origin "HEAD:$GITHUB_REF"