Skip to content

Commit ed792ed

Browse files
authored
Grant all existing org members with the All-repository triage role (#66)
* Ensure All Members Have All-Repo Triage * Fixes * Update triage-contributors.yml * Update triage-contributors.yml * Update workflow to enforce all-repo triage roles * Fix * Update Readme.md * Prevent overlapping runs and adjust priv
1 parent 4a1a6a6 commit ed792ed

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
name: Grant All-repository triage role
2+
on:
3+
schedule:
4+
- cron: '0 3 * * 1' # Weekly, Monday 03:00 UTC
5+
workflow_dispatch:
6+
inputs:
7+
dry_run:
8+
description: 'Simulate without changing roles'
9+
required: false
10+
default: 'false'
11+
exclude:
12+
description: 'Comma-separated usernames to exclude'
13+
required: false
14+
default: ''
15+
16+
permissions:
17+
contents: read
18+
19+
jobs:
20+
enforce-triage:
21+
name: "Enforce Triage"
22+
runs-on: ubuntu-latest
23+
concurrency:
24+
group: org-triage-${{ github.workflow }}-${{ github.ref }}
25+
cancel-in-progress: false
26+
steps:
27+
- name: Ensure all members have All-repo triage
28+
uses: actions/github-script@v7
29+
with:
30+
github-token: ${{ secrets.ORG_INVITE }}
31+
script: |
32+
const org = 'armbian';
33+
const dryRun = '${{ github.event.inputs.dry_run }}' === 'true';
34+
const excludeCsv = '${{ github.event.inputs.exclude }}'.trim();
35+
const EXCLUDE = new Set(
36+
excludeCsv ? excludeCsv.split(',').map(s => s.trim().toLowerCase()) : []
37+
);
38+
39+
// 1) Find the "All-repository triage" role
40+
let triageRole = null;
41+
try {
42+
const { data } = await github.request('GET /orgs/{org}/organization-roles', { org });
43+
triageRole = (data.roles || []).find(r =>
44+
(r.name && r.name.toLowerCase() === 'all-repository triage') ||
45+
(r.slug && r.slug.toLowerCase() === 'all-repository-triage') ||
46+
/all.*triage/i.test(r.name || '')
47+
);
48+
if (!triageRole) throw new Error('Org role not found: All-repository triage');
49+
core.info(`Found role: ${triageRole.name} (id=${triageRole.id})`);
50+
} catch (e) {
51+
core.setFailed(`Unable to list/find org roles: ${e.message}`);
52+
return;
53+
}
54+
55+
// 2) Get all regular members (exclude owners by default)
56+
const members = await github.paginate(github.rest.orgs.listMembers, {
57+
org,
58+
per_page: 100,
59+
role: 'member'
60+
});
61+
62+
const includeOwners = false; // set true if you want owners too
63+
let owners = [];
64+
if (includeOwners) {
65+
owners = await github.paginate(github.rest.orgs.listMembers, {
66+
org,
67+
per_page: 100,
68+
role: 'admin'
69+
});
70+
}
71+
72+
const targets = [...members, ...owners]
73+
.map(u => u.login.toLowerCase())
74+
.filter(u => !EXCLUDE.has(u));
75+
76+
if (!targets.length) {
77+
core.info('No eligible members to process.');
78+
return;
79+
}
80+
81+
let table = '\n| User | Action |\n|---|---|\n';
82+
83+
for (const username of targets) {
84+
try {
85+
// 3) Get current role assignments
86+
let currentRoles = [];
87+
try {
88+
const { data } = await github.request(
89+
'GET /orgs/{org}/organization-roles/users/{username}',
90+
{ org, username }
91+
);
92+
currentRoles = Array.isArray(data) ? data : (data.roles || []);
93+
} catch (e) {
94+
if (e.status !== 404) {
95+
table += `| ${username} | ⚠️ read roles failed: ${e.message} |\n`;
96+
continue;
97+
}
98+
}
99+
100+
const hasTriage = currentRoles.some(r => (r.id || r.role_id) === triageRole.id);
101+
if (hasTriage) {
102+
table += `| ${username} | ➖ already has All-repo triage |\n`;
103+
continue;
104+
}
105+
106+
if (dryRun) {
107+
table += `| ${username} | 🛑 dry-run: would assign All-repo triage |\n`;
108+
continue;
109+
}
110+
111+
// ✅ Correct endpoint
112+
await github.request(
113+
'PUT /orgs/{org}/organization-roles/users/{username}/{role_id}',
114+
{ org, username, role_id: triageRole.id }
115+
);
116+
117+
table += `| ${username} | ✅ assigned All-repo triage |\n`;
118+
} catch (e) {
119+
table += `| ${username} | ❌ assignment failed: ${e.status || ''} ${e.message} |\n`;
120+
}
121+
}
122+
123+
await core.summary
124+
.addHeading('All-repository triage enforcement')
125+
.addRaw(table, true)
126+
.write();

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ It also produces [data exchange files](https://github.armbian.com/) used for aut
7979
<a href=https://github.com/armbian/armbian.github.io/actions/workflows/invite-contributors.yml><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/armbian/armbian.github.io/invite-contributors.yml?logo=githubactions&label=Status&style=for-the-badge&branch=main&logoColor=white"></a>
8080
Automatically invites external contributors to join the [Armbian GitHub organization](https://github.com/orgs/armbian/people).
8181

82+
- **Ensure All Members Have All-Repo Triage**
83+
<a href=https://github.com/armbian/armbian.github.io/actions/workflows/triage-contributors.yml><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/armbian/armbian.github.io/triage-contributors.yml?logo=githubactions&label=Status&style=for-the-badge&branch=main&logoColor=white"></a>
84+
Automatically grants the All-repository triage role to all existing organization members, ensuring consistent permissions across the Armbian project.
85+
8286
- **Generate Self Hosted Runners Status page**
8387
<a href=https://github.com/armbian/armbian.github.io/actions/workflows/generate-runners-status.yml><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/armbian/armbian.github.io/generate-runners-status.yml?logo=githubactions&label=Status&style=for-the-badge&branch=main&logoColor=white"></a>
8488
Generates a table of Self Hosted runners with CPU, memory, storage, runner status, and totals.

0 commit comments

Comments
 (0)