gitbreathing

gitlab

gitBreathing: A deterministic procedural contribution-art generator that transforms GitHub and GitLab activity into animated Demon Slayer-inspired breathing effects, with customizable SVG and GIF output.

0 stars0 forksupdated 2d agodefault branch: main
clonehttps://gitlab.com/bitzzdev/gitbreathing.git
@ main
stages:
- render
render-gitbreathing:
stage: render
image: node:22-bookworm
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
- if: '$CI_PIPELINE_SOURCE == "web"'
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_MESSAGE !~ /^chore: update GitBreathing artwork$/'
before_script:
- apt-get update -qq && apt-get install -y -qq git
- npm install --no-audit --no-fund
script: |
set -eu
if [ -z "${GITLAB_TOKEN:-}" ]; then
echo "Set a masked GITLAB_TOKEN with API read and repository write access." >&2
exit 1
fi
if [ -z "${CONTRIBUTIONS:-}" ]; then
USERNAME=$(node -p "JSON.parse(require('fs').readFileSync('gitbreathing.json','utf8')).username")
USER_ID=$(curl --fail-with-body -fsS --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --get --data-urlencode "username=${USERNAME}" "${CI_API_V4_URL}/users" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{const a=JSON.parse(s);if(!a.length)process.exit(2);console.log(a[0].id)})")
PAGE=1
TOTAL=0
AFTER=$(date -u -d '1 year ago' +%F)
while :; do
EVENTS=$(curl --fail-with-body -fsS --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --get --data-urlencode "after=${AFTER}" --data-urlencode "per_page=100" --data-urlencode "page=${PAGE}" "${CI_API_V4_URL}/users/${USER_ID}/events")
COUNT=$(printf '%s' "$EVENTS" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>console.log(JSON.parse(s).length))")
TOTAL=$((TOTAL + COUNT))
[ "$COUNT" -lt 100 ] && break
PAGE=$((PAGE + 1))
done
export CONTRIBUTIONS="$TOTAL"
echo "GitLab Events API returned ${TOTAL} activity events in the one-year window. This is an event-count approximation, not a canonical contribution-calendar total."
fi
if [ -n "${GH_TOKEN:-}" ]; then
USERNAME=$(node -p "JSON.parse(require('fs').readFileSync('gitbreathing.json','utf8')).username")
QUERY='query($login:String!){user(login:$login){login name bio location company websiteUrl avatarUrl twitterUsername email createdAt followers(first:1){totalCount} following(first:1){totalCount} repositories(first:100,privacy:PUBLIC,ownerAffiliations:OWNER,orderBy:{field:STARGAZERS,direction:DESC}){totalCount nodes{name url description stargazerCount forkCount primaryLanguage{name color}}} starredRepositories(first:1){totalCount} gists(first:1){totalCount} organizations(first:20){totalCount nodes{login name url}} socialAccounts(first:20){nodes{provider url}} pinnedItems(first:6,types:[REPOSITORY]){nodes{... on Repository{name url description stargazerCount forkCount primaryLanguage{name color}}}} contributionsCollection{totalCommitContributions totalIssueContributions totalPullRequestContributions totalPullRequestReviewContributions contributionCalendar{totalContributions weeks{contributionDays{date contributionCount color}}}}}}'
BODY=$(jq -n --arg q "$QUERY" --arg u "$USERNAME" '{query:$q,variables:{login:$u}}')
RESPONSE=$(curl --fail-with-body -fsS --header "Authorization: bearer $GH_TOKEN" --header 'Content-Type: application/json' --data "$BODY" https://api.github.com/graphql)
printf '%s' "$RESPONSE" | jq -e '.data.user' >/dev/null
mkdir -p dist
printf '%s' "$RESPONSE" | jq '{data:.data}' > dist/profile-data.json
fi
npm run render
npm run render:profile
git config user.name 'GitBreathing CI'
git config user.email 'gitbreathing-bot@users.noreply.gitlab.com'
git add dist/slash.svg dist/slash.gif dist/slash.png dist/metadata.json dist/profile.svg dist/profile-data.json
if ! git diff --cached --quiet; then
git commit -m "chore: update GitBreathing artwork"
git push "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" "HEAD:${CI_DEFAULT_BRANCH}"
else
echo "No artwork changes."
fi