From 9f4ef267f64de5843796a66883dd3b7976943f2e Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Wed, 22 Apr 2026 19:57:36 +0700 Subject: [PATCH] feat(ci): replace github-pages shortcode with daily workflow Fetch repos server-side via GitHub API using PAGES_FETCH_TOKEN and inject the pages list into content/page/pages/index.md between HTML markers. Runs daily at 00:00 Asia/Saigon and on manual dispatch. --- .github/scripts/update-pages-list.mjs | 96 +++++++++++++++++++++++++++ .github/workflows/update-pages.yml | 49 ++++++++++++++ content/page/pages/index.md | 3 +- layouts/shortcodes/github-pages.html | 69 ------------------- 4 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 .github/scripts/update-pages-list.mjs create mode 100644 .github/workflows/update-pages.yml delete mode 100644 layouts/shortcodes/github-pages.html diff --git a/.github/scripts/update-pages-list.mjs b/.github/scripts/update-pages-list.mjs new file mode 100644 index 0000000..5da1c65 --- /dev/null +++ b/.github/scripts/update-pages-list.mjs @@ -0,0 +1,96 @@ +#!/usr/bin/env node +// Fetch all repos for $GH_USER with has_pages=true and inject a markdown +// table into content/page/pages/index.md between the PAGES_LIST markers. + +import fs from "node:fs/promises"; +import path from "node:path"; + +const token = process.env.GH_TOKEN; +const user = process.env.GH_USER; + +if (!token || !user) { + console.error("GH_TOKEN and GH_USER env vars are required"); + process.exit(1); +} + +const headers = { + Authorization: `Bearer ${token}`, + Accept: "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + "User-Agent": `${user}-pages-updater`, +}; + +async function fetchAllRepos() { + const all = []; + for (let page = 1; page <= 20; page += 1) { + const url = `https://api.github.com/users/${user}/repos?type=all&sort=updated&per_page=100&page=${page}`; + const res = await fetch(url, { headers }); + if (!res.ok) { + const body = await res.text(); + throw new Error(`GitHub API ${res.status} ${res.statusText}: ${body}`); + } + const batch = await res.json(); + if (!Array.isArray(batch) || batch.length === 0) break; + all.push(...batch); + if (batch.length < 100) break; + } + return all; +} + +function toRow(repo) { + const pagesUrl = + repo.homepage && repo.homepage.trim().length > 0 + ? repo.homepage.trim() + : `https://${user}.github.io/${repo.name}`; + const description = (repo.description || "") + .replace(/\|/g, "\\|") + .replace(/\r?\n/g, " ") + .trim(); + return `| [${repo.name}](${repo.html_url}) | ${description} | [${pagesUrl}](${pagesUrl}) |`; +} + +const START = ""; +const END = ""; + +function buildBlock(rows) { + return [ + START, + "", + "| Name | Description | Pages URL |", + "| --- | --- | --- |", + ...rows, + "", + END, + ].join("\n"); +} + +async function main() { + const repos = await fetchAllRepos(); + const pages = repos + .filter((r) => r.has_pages) + .sort((a, b) => a.name.localeCompare(b.name)); + + const rows = pages.map(toRow); + const block = buildBlock(rows); + + const file = path.resolve("content/page/pages/index.md"); + const current = await fs.readFile(file, "utf8"); + + const marker = /[\s\S]*?/; + const next = marker.test(current) + ? current.replace(marker, block) + : `${current.trimEnd()}\n\n${block}\n`; + + if (next === current) { + console.log(`No changes (${pages.length} pages).`); + return; + } + + await fs.writeFile(file, next); + console.log(`Updated ${file} with ${pages.length} pages.`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/.github/workflows/update-pages.yml b/.github/workflows/update-pages.yml new file mode 100644 index 0000000..0727201 --- /dev/null +++ b/.github/workflows/update-pages.yml @@ -0,0 +1,49 @@ +name: Update GitHub Pages list + +on: + schedule: + # 00:00 Asia/Saigon (UTC+7) = 17:00 UTC + - cron: "0 17 * * *" + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: update-pages + cancel-in-progress: false + +jobs: + update: + runs-on: ubuntu-latest + env: + TZ: Asia/Ho_Chi_Minh + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + # Use PAT so the subsequent commit+push triggers the build workflow. + token: ${{ secrets.PAGES_FETCH_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + + - name: Refresh pages list + env: + GH_TOKEN: ${{ secrets.PAGES_FETCH_TOKEN }} + GH_USER: tiennm99 + run: node .github/scripts/update-pages-list.mjs + + - name: Commit and push + run: | + if [[ -z "$(git status --porcelain content/page/pages/index.md)" ]]; then + echo "No changes to commit." + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add content/page/pages/index.md + git commit -m "chore(pages): refresh GitHub Pages list" + git push diff --git a/content/page/pages/index.md b/content/page/pages/index.md index 89f8332..05d2c1e 100644 --- a/content/page/pages/index.md +++ b/content/page/pages/index.md @@ -12,4 +12,5 @@ Danh sách những trang mình host trên [GitHub Pages](https://pages.github.co _(Danh sách này được tạo tự động, thứ tự không quan trọng nhé)_ -{{< github-pages >}} + + diff --git a/layouts/shortcodes/github-pages.html b/layouts/shortcodes/github-pages.html deleted file mode 100644 index 1807e70..0000000 --- a/layouts/shortcodes/github-pages.html +++ /dev/null @@ -1,69 +0,0 @@ -
- - - - - - - - - - -
NameDescriptionPages URL
-
- - \ No newline at end of file