| .github/CODEOWNERS | ●●●●● patch | view | raw | blame | history | |
| .github/scripts/codeowners.mjs | ●●●●● patch | view | raw | blame | history | |
| .github/workflows/update-codeowners.yaml | ●●●●● patch | view | raw | blame | history |
.github/CODEOWNERS
New file @@ -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 .github/scripts/codeowners.mjs
New file @@ -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(); .github/workflows/update-codeowners.yaml
New file @@ -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