Commit df5a994

Anton Golub <antongolub@antongolub.com>
2023-10-11 21:52:44
feat: introduce `tiers` option to pick only the highest matched badges for readme
1 parent 6ea4b35
src/all-badges/abc-commit/abc-commit.ts
@@ -4,13 +4,14 @@ import { BadgePresenter, ID, Present } from '../../badges.js'
 export default new (class implements BadgePresenter {
   url = new URL(import.meta.url)
   badges = [
-    'a-commit',
-    'ab-commit',
-    'abc-commit',
-    'abcd-commit',
-    'abcde-commit',
     'abcdef-commit',
+    'abcde-commit',
+    'abcd-commit',
+    'abc-commit',
+    'ab-commit',
+    'a-commit',
   ] as const
+  tiers = true as const
   present: Present = (data, grant) => {
     const types: [string, ID][] = [
       ['abcdef', 'abcdef-commit'],
src/all-badges/chore-commit/chore-commit.ts
@@ -3,6 +3,7 @@ import { BadgePresenter, Present } from '../../badges.js'
 export default new (class implements BadgePresenter {
   url = new URL(import.meta.url)
   badges = ['chore-commit'] as const
+  tiers = false
   present: Present = (data, grant) => {
     for (const repo of data.repos) {
       for (const commit of repo.commits) {
src/all-badges/fix-commit/fix-commit.ts
@@ -11,6 +11,7 @@ export default new (class implements BadgePresenter {
     'fix-6',
     'fix-6+', // For more than 6
   ] as const
+  tiers = true
   present: Present = (data, grant) => {
     for (const repo of data.repos) {
       const sequences: Commit[][] = []
src/all-badges/mass-delete-commit/mass-delete-commit.ts
@@ -2,7 +2,11 @@ import { BadgePresenter, Present } from '../../badges.js'
 
 export default new (class implements BadgePresenter {
   url = new URL(import.meta.url)
-  badges = ['mass-delete-commit', 'mass-delete-commit-10k'] as const
+  badges = [
+    'mass-delete-commit-10k',
+    'mass-delete-commit'
+  ] as const
+  tiers = true
   present: Present = (data, grant) => {
     for (const repo of data.repos) {
       for (const commit of repo.commits) {
src/all-badges/stars/stars.ts
@@ -11,6 +11,7 @@ export default new (class implements BadgePresenter {
     'stars-500',
     'stars-100',
   ] as const
+  tiers = true
   present: Present = (data, grant) => {
     let totalStars = 0
 
src/all-badges/time-of-commit/time-of-commit.ts
@@ -1,5 +1,5 @@
 import { BadgePresenter, Present } from '../../badges.js'
-import { Commit, Data, User } from '../../collect/collect.js'
+import { Commit, User } from '../../collect/collect.js'
 
 export default new (class implements BadgePresenter {
   url = new URL(import.meta.url)
src/badges.ts
@@ -15,6 +15,7 @@ export type ID = (typeof allBadges)[number]['default']['badges'][number]
 export interface BadgePresenter {
   url: URL
   badges: unknown
+  tiers?: boolean
   present: Present
 }
 
src/main.ts
@@ -15,7 +15,7 @@ void (async function main() {
   const { env } = process
   const argv = minimist(process.argv.slice(2), {
     string: ['data', 'repo', 'token', 'size', 'user', 'pick', 'omit'],
-    boolean: ['dryrun'],
+    boolean: ['dryrun', 'tiers'],
   })
   const {
     token = env.GITHUB_TOKEN,
@@ -26,6 +26,7 @@ void (async function main() {
     dryrun,
     pick,
     omit,
+    tiers,
   } = argv
   const [owner, repo] = repository?.split('/', 2) || [username, username]
   const pickBadges = pick ? pick.split(',') : names
@@ -107,6 +108,6 @@ void (async function main() {
 
   if (owner && repo) {
     await updateBadges(octokit, owner, repo, badges, oldJson, jsonSha, dryrun)
-    await updateReadme(octokit, owner, repo, badges, size, dryrun)
+    await updateReadme(octokit, owner, repo, badges, size, dryrun, tiers)
   }
 })()
src/update-readme.ts
@@ -1,37 +1,36 @@
 import { Octokit } from 'octokit'
 import { Badge } from './badges.js'
 import { quoteAttr, upload } from './utils.js'
+import { allBadges } from './all-badges/index.js'
 
-export async function updateReadme(
-  octokit: Octokit,
-  owner: string,
-  repo: string,
+export function generateReadme(
+  readme: string,
   badges: Badge[],
   size: number | string = 64,
-  dryrun: boolean,
+  tiers: boolean,
 ) {
-  console.log('Loading README.md')
-  const readme = await octokit.request<'readme'>(
-    'GET /repos/{owner}/{repo}/readme',
-    {
-      owner,
-      repo,
-    },
-  )
-
   const startString = '<!-- my-badges start -->'
   const endString = '<!-- my-badges end -->'
 
-  let content = Buffer.from(readme.data.content, 'base64').toString('utf8')
+  let content = readme
 
   const start = content.indexOf(startString)
   const end = content.indexOf(endString)
   const needToAddNewLine = content[end + endString.length + 1] !== '\n'
+  const highestBadges = allBadges.flatMap(({ default: { badges: _badges , tiers: _tiers} }) =>
+    _tiers
+      ? _badges.find((badge) => badges.some(({ id }) => id === badge))
+      : _badges,
+  )
+  const filter = tiers
+    ? ({ id }: Badge) => highestBadges.includes(id)
+    : () => true
 
   if (start !== -1 && end !== -1) {
     content = content.slice(0, start) + content.slice(end + endString.length)
 
     const badgesHtml = badges
+      .filter(filter)
       .map((badge) => {
         const desc = quoteAttr(badge.desc)
         // prettier-ignore
@@ -49,6 +48,32 @@ export async function updateReadme(
       content.slice(start)
   }
 
+  return content
+}
+
+export async function updateReadme(
+  octokit: Octokit,
+  owner: string,
+  repo: string,
+  badges: Badge[],
+  size: number | string,
+  dryrun: boolean,
+  tiers: boolean,
+) {
+  const readme = await octokit.request<'readme'>(
+    'GET /repos/{owner}/{repo}/readme',
+    {
+      owner,
+      repo,
+    },
+  )
+  const content = await generateReadme(
+    Buffer.from(readme.data.content, 'base64').toString('utf8'),
+    badges,
+    size,
+    tiers,
+  )
+
   await upload(
     octokit,
     'PUT /repos/{owner}/{repo}/contents/{path}',
test/badges.test.ts
@@ -56,18 +56,18 @@ describe('badges', () => {
 
     assert.deepEqual(badges, [
       {
-        id: 'a-commit',
+        id: 'abcd-commit',
         desc: 'test',
         body: '',
         image:
-          'https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/a-commit.png?raw=true',
+          'https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/abcd-commit.png?raw=true',
       },
       {
-        id: 'abcd-commit',
+        id: 'a-commit',
         desc: 'test',
         body: '',
         image:
-          'https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/abcd-commit.png?raw=true',
+          'https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/a-commit.png?raw=true',
       },
     ])
   })
test/update-readme.test.ts
@@ -0,0 +1,53 @@
+import * as assert from 'node:assert'
+import { describe, it } from 'node:test'
+import { generateReadme } from '../src/update-readme.js'
+import type { Badge } from '../src/badges.js'
+import abcPresenter from '../src/all-badges/abc-commit/abc-commit.js'
+import { badgeCollection } from '../src/badges.js'
+import { names } from '../src/all-badges/index.js'
+
+describe('generateReadme()', () => {
+  it('injects badges to md contents', () => {
+    const readme = `
+<!-- my-badges start -->
+<!-- my-badges end -->
+`
+    const badges: Badge[] = []
+    const presenter = abcPresenter
+    const grant = badgeCollection(badges, presenter.url, names, [])
+
+    abcPresenter.badges.forEach((badge) => grant(badge, 'test'))
+    assert.equal(badges.length, 6)
+
+    const contents1 = generateReadme(readme, badges, 64, true)
+    assert.equal(
+      contents1,
+      `
+<!-- my-badges start -->
+<h4><a href="https://github.com/my-badges/my-badges">My Badges</a></h4>
+
+<a href="my-badges/abcdef-commit.md"><img src="https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/abcdef-commit.png?raw=true" alt="test" title="test" width="64"></a>
+<!-- my-badges end -->
+
+`,
+    )
+
+    const contents2 = generateReadme(readme, badges, 64, false)
+    assert.equal(
+      contents2,
+      `
+<!-- my-badges start -->
+<h4><a href="https://github.com/my-badges/my-badges">My Badges</a></h4>
+
+<a href="my-badges/abcdef-commit.md"><img src="https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/abcdef-commit.png?raw=true" alt="test" title="test" width="64"></a>
+<a href="my-badges/abcde-commit.md"><img src="https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/abcde-commit.png?raw=true" alt="test" title="test" width="64"></a>
+<a href="my-badges/abcd-commit.md"><img src="https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/abcd-commit.png?raw=true" alt="test" title="test" width="64"></a>
+<a href="my-badges/abc-commit.md"><img src="https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/abc-commit.png?raw=true" alt="test" title="test" width="64"></a>
+<a href="my-badges/ab-commit.md"><img src="https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/ab-commit.png?raw=true" alt="test" title="test" width="64"></a>
+<a href="my-badges/a-commit.md"><img src="https://github.com/my-badges/my-badges/blob/master/src/all-badges/abc-commit/a-commit.png?raw=true" alt="test" title="test" width="64"></a>
+<!-- my-badges end -->
+
+`,
+    )
+  })
+})
README.md
@@ -111,17 +111,17 @@ jobs:
 - Start **my-badges** workflow, or wait for it to run automatically.
 
 ## Configuration
-
-| Param    | ENV alias      | Description                                                                                                                                       | Default       |
-|----------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
-| `token`  | `GITHUB_TOKEN` | Auth token                                                                                                                                        |               |
-| `user`   | `GITHUB_USER`  | Username                                                                                                                                          |               |
-| `repo`   | `GITHUB_REPO`  | Repository name to push badges                                                                                                                    | `{user/user}` |
-| `data`   |                | Path to JSON to generate badges. If empty, required data will be obtained from the GH API                                                         |               |
-| `size`   |                | Badge size for README.md, px                                                                                                                      | 64            |
-| `dryrun` |                | Generate badges, but skip pushing to git                                                                                                          |               |
-| `pick`   |                | List of badges to pick. Pass `--pick="a-commit,ab-commit,revert-revert-commit"` to generate only the specified entries. If empty gets all of them |               |
-| `omit`   |                | List of badges to exclude. For example, if you're too shy to flex your stars: `--omit:stars-100,stars-500,stars-1000`                             |               |                                                                                                         
+| Param    | ENV alias      | Description                                                                                                                                             | Default       |
+|----------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
+| `token`  | `GITHUB_TOKEN` | Auth token                                                                                                                                              |               |
+| `user`   | `GITHUB_USER`  | Username                                                                                                                                                |               |
+| `repo`   | `GITHUB_REPO`  | Repository name to push badges                                                                                                                          | `{user/user}` |
+| `data`   |                | Path to JSON to generate badges. If empty, required data will be obtained from the GH API                                                               |               |
+| `size`   |                | Badge size for README.md, px                                                                                                                            | 64            |
+| `dryrun` |                | Generate badges, but skip pushing to git                                                                                                                |               |
+| `pick`   |                | List of badges to pick. Pass `--pick="a-commit,ab-commit,revert-revert-commit"` to generate only the specified entries. If empty gets all of them       |               |
+| `omit`   |                | List of badges to exclude. For example, if you're too shy to flex your stars: `--omit:stars-100,stars-500,stars-1000`                                   |               |
+| `tiers`  |                | Represent the highest tier badges in README.md. For example, If you have both `stars-100` and `stars-500` achievements, only the last one will be shown |               |
 
 ## Contributing badges