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
20 changes: 13 additions & 7 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Generate Stats Images

on:
push:
branches: [ master ]
branches: [ master, main ]
schedule:
- cron: "5 0 * * *"
workflow_dispatch:
Expand Down Expand Up @@ -39,18 +39,24 @@ jobs:
python3 generate_images.py
env:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
EXCLUDE_FORKED_REPOS: false
# Optional secrets - Python code handles None/empty values gracefully
EXCLUDED: ${{ secrets.EXCLUDED }}
EXCLUDED_LANGS: ${{ secrets.EXCLUDED_LANGS }}
EXCLUDE_FORKED_REPOS: false
GIT_EMAILS: ${{ secrets.GIT_EMAILS }}

# Commit all changed files to the repository
- name: Commit to the repo
run: |
git config --global user.name "AxelPCG/gitgub-stats-modified"
git config --global user.name "AxelPCG/github-stats-modified"
git config --global user.email "axelchepanski@hotmail.com"
git add .
# Force the build to succeed, even if no files were changed
git commit -m 'Update generated files' || true
git push
git add generated/
# Only commit if there are changes
if git diff --staged --quiet; then
echo "No changes to commit"
else
git commit -m 'Update generated files [skip ci]' || exit 1
git push || exit 1
fi
141 changes: 99 additions & 42 deletions generate_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,48 +32,76 @@ async def generate_overview(s: Stats) -> None:
Generate an SVG badge with summary statistics
:param s: Represents user's GitHub statistics
"""
with open("templates/overview.svg", "r") as f:
output = f.read()

output = re.sub("{{ name }}", await s.name, output)
output = re.sub("{{ stars }}", f"{await s.stargazers:,}", output)
output = re.sub("{{ forks }}", f"{await s.forks:,}", output)
output = re.sub("{{ contributions }}", f"{await s.total_contributions:,}", output)
output = re.sub("{{ views }}", f"{await s.views:,}", output)
output = re.sub("{{ repos }}", f"{len(await s.repos):,}", output)
commits = await s.total_commits
output = re.sub("{{ commits }}", f"{commits:,}", output)
output = re.sub("{{ prs }}", f"{await s.prs:,}", output)
output = re.sub("{{ issues }}", f"{await s.issues:,}", output)

generate_output_folder()
with open("generated/overview.svg", "w") as f:
f.write(output)
try:
print("Starting generation of overview.svg...")
with open("templates/overview.svg", "r") as f:
output = f.read()

print("Fetching statistics data...")
output = re.sub("{{ name }}", await s.name, output)
output = re.sub("{{ stars }}", f"{await s.stargazers:,}", output)
output = re.sub("{{ forks }}", f"{await s.forks:,}", output)
output = re.sub("{{ contributions }}", f"{await s.total_contributions:,}", output)
output = re.sub("{{ views }}", f"{await s.views:,}", output)
output = re.sub("{{ repos }}", f"{len(await s.repos):,}", output)
commits = await s.total_commits
output = re.sub("{{ commits }}", f"{commits:,}", output)
output = re.sub("{{ prs }}", f"{await s.prs:,}", output)
output = re.sub("{{ issues }}", f"{await s.issues:,}", output)

generate_output_folder()
output_path = "generated/overview.svg"
with open(output_path, "w", encoding="utf-8") as f:
f.write(output)

# Verify file was created and has content
if not os.path.exists(output_path):
raise FileNotFoundError(f"Failed to create {output_path}")
file_size = os.path.getsize(output_path)
if file_size == 0:
raise ValueError(f"Generated {output_path} is empty!")
print(f"Successfully generated overview.svg ({file_size} bytes)")
except Exception as e:
print(f"ERROR generating overview.svg: {e}")
import traceback
traceback.print_exc()
raise


async def generate_languages(s: Stats) -> None:
"""
Generate an SVG badge with summary languages used
:param s: Represents user's GitHub statistics
"""
with open("templates/languages.svg", "r") as f:
output = f.read()

progress = ""
lang_list = ""
sorted_languages = sorted(
(await s.languages).items(), reverse=True, key=lambda t: t[1].get("size")
)
delay_between = 150
for i, (lang, data) in enumerate(sorted_languages):
color = data.get("color")
color = color if color is not None else "#000000"
progress += (
f'<span style="background-color: {color};'
f'width: {data.get("prop", 0):0.3f}%;" '
f'class="progress-item"></span>'
)
lang_list += f"""
try:
print("Starting generation of languages.svg...")
with open("templates/languages.svg", "r") as f:
output = f.read()

print("Fetching languages data...")
languages = await s.languages
print(f"Found {len(languages)} languages")

if not languages:
print("WARNING: No languages found! Generating empty languages.svg")
progress = ""
lang_list = ""
else:
progress = ""
lang_list = ""
sorted_languages = sorted(
languages.items(), reverse=True, key=lambda t: t[1].get("size")
)
delay_between = 150
for i, (lang, data) in enumerate(sorted_languages):
color = data.get("color")
color = color if color is not None else "#000000"
progress += (
f'<span style="background-color: {color};'
f'width: {data.get("prop", 0):0.3f}%;" '
f'class="progress-item"></span>'
)
lang_list += f"""
<li style="animation-delay: {i * delay_between}ms;">
<svg xmlns="http://www.w3.org/2000/svg" class="octicon" style="fill:{color};"
viewBox="0 0 16 16" version="1.1" width="16" height="16"><path
Expand All @@ -84,12 +112,26 @@ async def generate_languages(s: Stats) -> None:

"""

output = re.sub(r"{{ progress }}", progress, output)
output = re.sub(r"{{ lang_list }}", lang_list, output)

generate_output_folder()
with open("generated/languages.svg", "w") as f:
f.write(output)
output = re.sub(r"{{ progress }}", progress, output)
output = re.sub(r"{{ lang_list }}", lang_list, output)

generate_output_folder()
output_path = "generated/languages.svg"
with open(output_path, "w", encoding="utf-8") as f:
f.write(output)

# Verify file was created and has content
if not os.path.exists(output_path):
raise FileNotFoundError(f"Failed to create {output_path}")
file_size = os.path.getsize(output_path)
if file_size == 0:
raise ValueError(f"Generated {output_path} is empty!")
print(f"Successfully generated languages.svg ({file_size} bytes)")
except Exception as e:
print(f"ERROR generating languages.svg: {e}")
import traceback
traceback.print_exc()
raise


################################################################################
Expand Down Expand Up @@ -137,7 +179,22 @@ async def main() -> None:
ignore_forked_repos=ignore_forked_repos,
emails=email_list,
)
await asyncio.gather(generate_languages(s), generate_overview(s))
try:
# Pre-fetch stats to ensure data is loaded before generating images
print("Pre-fetching repository statistics...")
await s.get_stats()
print(f"Stats loaded: {len(await s.repos)} repos, {len(await s.languages)} languages")

# Generate both images (stats already loaded, so parallel is safe now)
await generate_overview(s)
await generate_languages(s)

print("All images generated successfully!")
except Exception as e:
print(f"FATAL ERROR during image generation: {e}")
import traceback
traceback.print_exc()
raise


if __name__ == "__main__":
Expand Down
14 changes: 7 additions & 7 deletions generated/overview.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading