Skip to content

Commit ebafee9

Browse files
Contribution guide & automation
1 parent 3c96554 commit ebafee9

File tree

10 files changed

+322
-1
lines changed

10 files changed

+322
-1
lines changed

.github/CONTRIBUTING.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Contributing
2+
3+
## Adding your repository to the list
4+
5+
To add a new repository to the list, find the approrpiate section for the repository's language and add a new entry like this for GitHub-hosted repos:
6+
7+
```
8+
* [username/repo]
9+
```
10+
11+
or like this for non-GH repo
12+
13+
```
14+
* [username/repo](https://not-github.com/username/repo)
15+
```
16+
17+
Don't link to files or folder within the tree since that messes up the badges. Adding your own repos is perfectly fine. If you use multiple languages, than avoid adding your repo to more than 3 sections. Repositories not hosted on GitJuib are fine, but they won't have a "last commit" badge. The last commit times are automatically updated every hour. You don't need to make new PRs to update your entry.
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Update badge action
2+
3+
This action fetches last commit time of repositories and generates the badge for advent-of-code.
4+
5+
## Inputs
6+
7+
### `ghToken`
8+
9+
**Required** GitHub token
10+
11+
### `inputFile`
12+
13+
**Required** File path of README.md
14+
15+
### `outputFile`
16+
17+
**Required** File path of updated README.md
18+
19+
## Outputs
20+
21+
No output. The output file will be changed directly. You can use another action to commit.
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: 'Update badge'
2+
description: 'Fetches last commit time of repositories and generates badges'
3+
inputs:
4+
ghToken:
5+
description: 'GitHub token'
6+
required: true
7+
inputFile:
8+
description: 'File path of README.md'
9+
required: true
10+
default: 'README.md'
11+
outputFile:
12+
description: 'File path of output'
13+
required: true
14+
default: 'README-badge.md'
15+
runs:
16+
using: 'node12'
17+
main: 'index.js'
+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// modifiy based on https://runkit.com/melnikow/version-from-requirements-txt
2+
const axios = require('axios');
3+
const moment = require('moment');
4+
5+
const badgeDefaults = {
6+
schemaVersion: 1,
7+
label: 'last commit',
8+
}
9+
10+
async function handle(octokit, { owner, repo }) {
11+
let lastCommitDayStr
12+
try {
13+
const { data: commitList } = await Promise.race([
14+
new Promise(resolve => setTimeout(resolve, 10 * 1000)),
15+
octokit.repos.listCommits({
16+
owner,
17+
repo
18+
})
19+
])
20+
lastCommitDayStr = commitList[0].commit.author.date
21+
} catch (e) {
22+
if (e.message.indexOf('rate limit exceeded') !== -1) {
23+
throw e;
24+
}
25+
26+
console.log(`[${owner }/${repo}] ${e.message}`)
27+
return { message: 'resource not available', color: 'red' }
28+
}
29+
30+
const message = moment(lastCommitDayStr).format('YYYY-MM-DD')
31+
return {
32+
message,
33+
color: age(message),
34+
}
35+
}
36+
37+
module.exports = async function invokeHandler(octokit, query) {
38+
return Object.assign({}, badgeDefaults, await handle(octokit, query))
39+
}
40+
41+
// copy from https://github.com/badges/shields
42+
function colorScale(steps, colors, reversed) {
43+
if (steps === undefined) {
44+
throw Error('When invoking colorScale, steps should be provided.')
45+
}
46+
47+
const defaultColors = {
48+
1: ['brightgreen', 'red'],
49+
2: ['brightgreen', 'yellow', 'red'],
50+
3: ['brightgreen', 'green', 'yellow', 'red'],
51+
4: ['brightgreen', 'green', 'yellowgreen', 'yellow', 'red'],
52+
5: ['brightgreen', 'green', 'yellowgreen', 'yellow', 'orange', 'red']
53+
}
54+
55+
if (typeof colors === 'undefined') {
56+
if (steps.length in defaultColors) {
57+
colors = defaultColors[steps.length]
58+
} else {
59+
throw Error(`No default colors for ${steps.length} steps.`)
60+
}
61+
}
62+
63+
if (steps.length !== colors.length - 1) {
64+
throw Error(
65+
'When colors are provided, there should be n + 1 colors for n steps.'
66+
)
67+
}
68+
69+
if (reversed) {
70+
colors = Array.from(colors).reverse()
71+
}
72+
73+
return value => {
74+
const stepIndex = steps.findIndex(step => value >= step)
75+
76+
// For any value above the final step, stepIndex is -1, so in all cases this
77+
// expression works swimmingly.
78+
return colors.slice(stepIndex)[0]
79+
}
80+
}
81+
82+
function age(date) {
83+
if (!date.add) {
84+
return "brightgreen"
85+
}
86+
87+
const now = moment()
88+
const then = moment(date)
89+
if (now.diff(date) < 0) date.add(-1, 'y')
90+
91+
const daysElapsed = now.diff(moment(date), 'days')
92+
const colorByAge = colorScale([0, 5, 10, 20, 25], undefined, false)
93+
return colorByAge(daysElapsed)
94+
}

.github/actions/update-badge/index.js

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
const fs = require('fs')
2+
const core = require('@actions/core')
3+
const github = require('@actions/github')
4+
const endpoint = require('./endpoint')
5+
6+
let octokit
7+
8+
(async function() {
9+
try {
10+
const ghToken = core.getInput('ghToken')
11+
const inputFile = core.getInput('inputFile')
12+
const outputFile = core.getInput('outputFile')
13+
14+
const content = fs.readFileSync(inputFile, 'utf8')
15+
const lines = content.split('\n')
16+
const repos = await core.group('Extracting repos...', () => extractRepositories(lines))
17+
core.info(`count=${repos.length}`)
18+
19+
octokit = github.getOctokit(ghToken)
20+
await core.group('Fetching repositories & updating lines...', async () => {
21+
for (const repo of repos) {
22+
const line = await generateLine(repo.repoStr)
23+
if (otherDomain(lines[repo.index])) {
24+
core.info(`...line refers to non-GH domain: '${lines[repo.index]}'`)
25+
} else if (shouldUpdate(lines[repo.index], line)) {
26+
lines[repo.index] = line
27+
core.info(`...updated line: '${line}'`)
28+
} else {
29+
core.info(`...skipped line: '${line}'`)
30+
}
31+
}
32+
})
33+
34+
await core.group('Writing README...', () => {
35+
fs.writeFileSync(outputFile, lines.join('\n'))
36+
core.info(`Finished writing to ${outputFile}`)
37+
})
38+
} catch (error) {
39+
core.setFailed(error.message)
40+
}
41+
})()
42+
43+
function extractRepositories(lines) {
44+
const repos = []
45+
46+
let collect = false
47+
lines.some((line, index) => {
48+
if (line === '### Solutions') {
49+
collect = true
50+
} else if (line === '### Live Streams') {
51+
collect = false
52+
} else if (collect) {
53+
const idx1 = line.indexOf('[')
54+
const idx2 = line.indexOf(']')
55+
if (idx1 >= 0 && idx2 >= 0) {
56+
repos.push({
57+
index,
58+
repoStr: line.slice(idx1 + 1, idx2)
59+
})
60+
}
61+
}
62+
63+
return false
64+
})
65+
66+
return repos
67+
}
68+
69+
async function generateLine(repoStr) {
70+
const badge = await generateBadge(repoStr)
71+
return `* [${repoStr}](https://github.com/${repoStr}) ![Last Commit on GitHub](${badge})`
72+
}
73+
74+
async function generateBadge(repoStr) {
75+
const [owner, repo] = repoStr.split('/')
76+
const { label, message, color } = await endpoint(octokit, { owner, repo })
77+
78+
core.info(`...fetched repo ${repoStr}`)
79+
80+
return 'https://img.shields.io/badge/' + [label, message, color]
81+
.map(s => encodeURIComponent(s.replace(/\-/g, '--')))
82+
.join('-')
83+
}
84+
85+
function shouldUpdate(oldLine, newLine) {
86+
const lastReg = /Last Commit on GitHub/;
87+
const badDateReg = /red\)$/;
88+
return !lastReg.test(oldLine) ||
89+
!badDateReg.test(newLine) ||
90+
badDateReg.test(oldLine);
91+
}
92+
93+
function otherDomain(line) {
94+
return line.indexOf("gitlab.com") !== -1 ||
95+
line.indexOf("gist.github.com") !== -1;
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"dependencies": {
3+
"@actions/core": "^1.2.6",
4+
"@actions/github": "3.0.0",
5+
"axios": "^0.21.1",
6+
"moment": "^2.26.0"
7+
}
8+
}

.github/workflows/update-badge.yml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Update AoC badge
2+
3+
on:
4+
push:
5+
schedule:
6+
- cron: '0 0 * 1-11 1'
7+
- cron: '0 0 * 12 *'
8+
9+
jobs:
10+
update:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Check out repository
14+
uses: actions/checkout@v2
15+
with:
16+
ref: ${{ github.ref }}
17+
fetch-depth: 0
18+
19+
- name: Install packages
20+
run: yarn
21+
working-directory: ./.github/actions/update-badge
22+
23+
- name: Use local action
24+
uses: ./.github/actions/update-badge
25+
with:
26+
ghToken: ${{ secrets.GITHUB_TOKEN }}
27+
inputFile: README.md
28+
outputFile: README.md
29+
30+
- name: Git commit and push
31+
run: |
32+
git config user.email '[email protected]'
33+
git config user.name 'github-actions'
34+
if git commit -am 'update badges'; then
35+
git push origin HEAD:${{ github.ref }}
36+
fi

.gitignore

Whitespace-only changes.

README.md

+20-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,20 @@
1-
# aoc-2022
1+
# Advent of Code 2022 -- devcord
2+
3+
Feel free to open Pull Requests on this repository to be able to add your solutions to the [SOLUTIONS.md](SOLUTIONS.md) file.
4+
5+
6+
## Adding your repository to the list
7+
8+
To add a new repository to the list, find the approrpiate section for the repository's language and add a new entry like this for GitHub-hosted repos:
9+
10+
```
11+
* [username/repo]
12+
```
13+
14+
or like this for non-GH repo
15+
16+
```
17+
* [username/repo](https://not-github.com/username/repo)
18+
```
19+
20+
Don't link to files or folder within the tree since that messes up the badges. Adding your own repos is perfectly fine. If you use multiple languages, than avoid adding your repo to more than 3 sections. Repositories not hosted on GitJuib are fine, but they won't have a "last commit" badge. The last commit times are automatically updated every hour. You don't need to make new PRs to update your entry.

SOLUTIONS.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# SOLUTIONS
2+
3+
## JavaScript/TypeScript
4+
<!-- JS/TS repos go here -->
5+
6+
### Python
7+
<!-- Python repos go here -->
8+
9+
## Rust
10+
<!-- Rust repos go here -->
11+
12+
## C/C++/C#
13+
<!-- C language repos go here-->

0 commit comments

Comments
 (0)