From 649eb224b7fc81a06d69a6a2eb9849496271fa4d Mon Sep 17 00:00:00 2001 From: Patrick Kollitsch Date: Fri, 31 Jan 2025 06:46:05 +0700 Subject: [PATCH] ci(workflow): add CODEOWNERS for i18n Signed-off-by: Patrick Kollitsch --- .github/CODEOWNERS | 37 ++++++++++ .github/scripts/codeowners.mjs | 93 ++++++++++++++++++++++++ .github/workflows/update-codeowners.yaml | 37 ++++++++++ 3 files changed, 167 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/scripts/codeowners.mjs create mode 100644 .github/workflows/update-codeowners.yaml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..d2708b2 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,37 @@ +# Lines starting with '#' are comments. +# Each line is a file pattern followed by one or more owners. +# These owners will be the default owners for everything in the repo. +# * <@insert_github_username> +# +# Order is important. The last matching pattern has the most precedence. + +# This file is also being managed automatically by the gitown tool. +* @davidsneighbour + +# i18n - automatically created +/i18n/ca.toml @mrodriguezestivill +/i18n/es.toml @eddex @hello @jerryzihye @regisphilibert @melvinsalasrivera @mrodriguezestivill @sarahrhgrefalda +/i18n/pl.toml @izikeros +/i18n/cs.toml @petr.vala +/i18n/bg.toml @foxbg @jerryzihye @sarahrhgrefalda +/i18n/he.toml @MeirKriheli +/i18n/hi.toml @neerajsharma.9 +/i18n/fi.toml @github +/i18n/ru.toml @365029+uonick @agrrh @eddex @jerryzihye @sarahrhgrefalda +/i18n/tr.toml @sarahrhgrefalda @yagizhan49 +/i18n/en.toml @eddex @jan.steinke @jerryzihye @kinski @regisphilibert @sarahrhgrefalda +/i18n/zh-tw.toml @peter-jerry-ye @s104213083 @sarahrhgrefalda +/i18n/uk.toml @jerryzihye @mivanchenko @sarahrhgrefalda +/i18n/nl.toml @eddex @jerryzihye @johan.vervloet @sarahrhgrefalda +/i18n/fr.toml @charles @eddex @jerryzihye @login @manu @sarahrhgrefalda +/i18n/oc.toml @59049879+ensag-dev +/i18n/sv.toml @36725726+besynnerlig @jerryzihye @sarahrhgrefalda @thenajnth +/i18n/it.toml @alainpoeta @jerryzihye @login @sarahrhgrefalda +/i18n/no.toml @login @sarahrhgrefalda @sh @xaner4 +/i18n/pt.toml @37813631+eriicf @eddex @jerryzihye @lazarodm @login @sarahrhgrefalda +/i18n/de.toml @46083627+thomham @dominik.infuehr @eddex @jan.steinke @jerryzihye @kinski @login @sarahrhgrefalda +/i18n/hu.toml @73439774+winux1 @sarahrhgrefalda +/i18n/ja.toml @86867075+ssatosays +/i18n/zh.toml @435878393 @eddex @jerryzihye @jerryzihye @o18382 @sarahrhgrefalda +/i18n/id.toml @63356065+fitrarhm +# i18n - end diff --git a/.github/scripts/codeowners.mjs b/.github/scripts/codeowners.mjs new file mode 100644 index 0000000..8439a57 --- /dev/null +++ b/.github/scripts/codeowners.mjs @@ -0,0 +1,93 @@ +import { execSync } from "child_process"; +import fs from "fs"; +import path from "path"; +import dotenv from "dotenv"; +import fetch from "node-fetch"; + +// Load environment variables +dotenv.config(); + +// GitHub API Token (required) +const GITHUB_TOKEN = process.env.GITHUB_TOKEN; +if (!GITHUB_TOKEN) { + console.error("❌ ERROR: GITHUB_TOKEN is not set. Add it to .env."); + process.exit(1); +} + +// Directory to scan (i18n folder) +const TARGET_DIR = process.argv[2] || "i18n"; +const SECTION = process.argv[3] || path.basename(TARGET_DIR); +const REPO_ROOT = execSync("git rev-parse --show-toplevel").toString().trim(); +const CODEOWNERS_FILE = path.join(REPO_ROOT, ".github", "CODEOWNERS"); + +// Section markers +const SECTION_START = `# ${SECTION} - automatically created`; +const SECTION_END = `# ${SECTION} - end`; + +// Get commit authors for a specific file +const getCommitAuthors = (file) => { + try { + return execSync(`git log --format='%ae' -- "${file}" | sort -u`) + .toString() + .trim() + .split("\n") + .filter((email) => email); + } catch (error) { + return []; + } +}; + +// Fetch GitHub username from email +const fetchGitHubUsername = async (email) => { + try { + const url = `https://api.github.com/search/users?q=${email}+in:email`; + const response = await fetch(url, { + headers: { Authorization: `token ${GITHUB_TOKEN}` }, + }); + const data = await response.json(); + if (data.items && data.items.length > 0) { + return `@${data.items[0].login}`; + } + return `@${email.split("@")[0]}`; // Fallback + } catch (error) { + console.error(`❌ ERROR fetching GitHub username for ${email}:`, error); + return `@${email.split("@")[0]}`; + } +}; + +// Process files and generate CODEOWNERS entries +const generateCodeowners = async () => { + const files = execSync(`find ${TARGET_DIR} -type f`) + .toString() + .trim() + .split("\n"); + let codeownersContent = ""; + + for (const file of files) { + const authors = await Promise.all( + getCommitAuthors(file).map(fetchGitHubUsername), + ); + + if (authors.length > 0) { + codeownersContent += `/${file} ${authors.join(" ")}\n`; + } + } + + if (!fs.existsSync(path.dirname(CODEOWNERS_FILE))) { + fs.mkdirSync(path.dirname(CODEOWNERS_FILE), { recursive: true }); + } + + let existingContent = fs.existsSync(CODEOWNERS_FILE) + ? fs.readFileSync(CODEOWNERS_FILE, "utf8") + : ""; + existingContent = existingContent + .replace(new RegExp(`\n?${SECTION_START}[\\s\\S]*?${SECTION_END}`, "g"), "") + .trim(); + + const updatedContent = `${existingContent}\n\n${SECTION_START}\n${codeownersContent}${SECTION_END}\n`; + fs.writeFileSync(CODEOWNERS_FILE, updatedContent.trim() + "\n"); + + console.log(`✅ CODEOWNERS updated for section: [${SECTION}]`); +}; + +generateCodeowners(); diff --git a/.github/workflows/update-codeowners.yaml b/.github/workflows/update-codeowners.yaml new file mode 100644 index 0000000..5e96ede --- /dev/null +++ b/.github/workflows/update-codeowners.yaml @@ -0,0 +1,37 @@ +name: Update CODEOWNERS + +on: + push: + branches: + - main + schedule: + - cron: "0 0 1 * *" # runs on the first day of the month + workflow_dispatch: # allow manual runs + +jobs: + update-codeowners: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "18" + + - name: Install Dependencies + run: npm install node-fetch dotenv + + - name: Run Generate CODEOWNERS + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: node .github/scripts/codeowners.mjs i18n + + - name: Commit and Push Changes + run: | + git config --global user.name "Patrick Kollitsch" + git config --global user.email "83281+davidsneighbour@users.noreply.github.com" + git add .github/CODEOWNERS + git commit -m "chore(codeowners): update CODEOWNERS for i18n" || exit 0 + git push