From 649eb224b7fc81a06d69a6a2eb9849496271fa4d Mon Sep 17 00:00:00 2001
From: Patrick Kollitsch <patrick@davids-neighbour.com>
Date: Thu, 30 Jan 2025 23:46:16 +0000
Subject: [PATCH] ci(workflow): add CODEOWNERS for i18n

---
 .github/CODEOWNERS                       |   37 ++++++++++++
 .github/scripts/codeowners.mjs           |   93 +++++++++++++++++++++++++++++++
 .github/workflows/update-codeowners.yaml |   37 ++++++++++++
 3 files changed, 167 insertions(+), 0 deletions(-)

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

--
Gitblit v1.10.0