-
Notifications
You must be signed in to change notification settings - Fork 10
/
report.js
167 lines (153 loc) · 7.64 KB
/
report.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* eslint-env browser */
let data;
const errortypes = {
"now3cjson": "No w3c.json file",
"invalidw3cjson": "Invalid data in w3c.json",
"incompletew3cjson": "Missing data in w3c.json",
"noashnazg": "Not configured with the Repo Manager",
"inconsistentstatus": "Inconsistent rec-track status",
"inconsistentgroups": "Inconsistent groups info w3c.json / repo-manager",
"defaultbranchismaster": "Default branch is named “master”",
"unprotectedbranch": "Missing branch protection rule",
"unprotectedbranchforadmin": "Missing admin enforced branch protection",
"norequiredreview": "Missing required review setting",
"nocontributing": "No CONTRIBUTING.md file",
"missingashnazghook": "Configured with the Repo Manager, but missing the github webhook",
"duplicateashnazghooks": "Duplicate Repo Manager webhooks",
// "invalidcontributing": "Invalid CONTRIBUTING.MD file",
"nolicense": "No LICENSE.md file",
"noautopublish": "No .github/workflows/auto-publish.yml file",
"usetravisci": "Configured to use travis CI",
"nocodeofconduct": "No CODE_OF_CONDUCT.md file",
"invalidlicense": "Invalid LICENSE.md file",
"noreadme": "No README.md file"
};
const defaultReport = ["now3cjson", "inconsistentgroups", "invalidw3cjson", "incompletew3cjson", "noashnazg", "inconsistentstatus", "defaultbranchismaster", "unprotectedbranch", "missingashnazghook"];
// from https://stackoverflow.com/questions/10970078/modifying-a-query-string-without-reloading-the-page
function insertUrlParam(key, value) {
if (history.pushState) {
const searchParams = new URLSearchParams(window.location.search);
searchParams.set(key, value);
const newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + searchParams.toString();
window.history.pushState({path: newurl}, '', newurl);
}
}
// from https://stackoverflow.com/a/5158301
function getUrlParam(name) {
const searchParams = new URLSearchParams(window.location.search);
return searchParams.get(name);
}
// Add filter UI
const filterUI = document.getElementById("filter");
const errorSelectorHtml = Object.keys(errortypes).map(t => `<label><input name='filter' type='checkbox' value='${t}'>${errortypes[t]}</label>`).join(' ');
filterUI.innerHTML = `
<fieldset><legend>Filter report</legend>
<label for=grouptype>Group type:</label> <select id='grouptype' name='grouptype'><option value=''>All</option><option value=workinggroup>Working Group</option><option value=interestgroup>Interest Group</option><option value=communitygroup>Community Group</option></select></label>
<label for='errors'>Errors:</label> <span id='errors'>${errorSelectorHtml}</span></fieldset>`;
const groupSelector = document.getElementById('grouptype');
const errorSelector = document.getElementById('errors');
if (getUrlParam("grouptype")) {
[...groupSelector.querySelectorAll('option')].forEach(o => o.selected = false);
(groupSelector.querySelector(`option[value='${getUrlParam("grouptype")}']`) || {}).selected = true;
}
if (getUrlParam("filter")) {
const errorTypes = getUrlParam("filter").split(',');
[...errorSelector.querySelectorAll('input')].forEach(inp => inp.checked = errorTypes.includes(inp.value));
} else {
[...errorSelector.querySelectorAll('input')].forEach(inp => inp.checked = defaultReport.includes(inp.value));
}
groupSelector.addEventListener("change", () => {
insertUrlParam("grouptype", groupSelector.value);
writeReport();
});
errorSelector.addEventListener("input", () => {
insertUrlParam("filter", [...errorSelector.querySelectorAll('input:checked')].map(inp => inp.value).join(','));
writeReport();
});
const writeErrorEntry = (name, list, details) => {
const li = document.createElement('li');
const link = document.createElement('a');
link.href = 'https://github.com/' + name;
link.appendChild(document.createTextNode(name));
li.appendChild(link);
if (details) {
li.appendChild(document.createTextNode(': ' + details));
}
list.appendChild(li);
};
function writeReport() {
if (!data) {
return;
}
const mentionedRepos = new Set();
const groupFilter = gid => getUrlParam("grouptype") ? (groups[gid].type || '').replace(' ', '') === getUrlParam("grouptype") : true;
const errorFilter = new Set((getUrlParam("filter") || defaultReport.join(',')).split(",").filter(e => e !== ''));
const report = document.getElementById('report');
report.innerHTML = '';
const timestamp = document.createElement('p');
timestamp.textContent = `Report last updated on ${data.lastModified}`;
report.appendChild(timestamp);
const stats = document.createElement('p');
stats.textContent = `${data.repos.filter(r => r.owner.login === 'w3c' && !r.isArchived).length} active repos in the w3c github organization; overall, ${Object.values(data.groups).filter(g => g.type === 'working group').reduce((acc, g) => acc + g.repos.length, 0)} known repos associated with Working Groups, ${Object.values(data.groups).filter(g => g.type === 'community group').reduce((acc, g) => acc + g.repos.length, 0)} associated with Community Groups`;
report.appendChild(stats);
const groups = data.groups;
Object.keys(groups).sort((a, b) => groups[a].name.localeCompare(groups[b].name))
.forEach(groupId => {
const section = document.createElement('section');
const title = document.createElement('h2');
title.appendChild(document.createTextNode(groups[groupId].name));
if (groupFilter(groupId)) {
section.appendChild(title);
}
if (groups[groupId].type === "working group" && !groups[groupId].repos.some(r => r.hasRecTrack)) {
const p = document.createElement('p');
p.appendChild(document.createTextNode('No identified repo for rec-track spec.'));
section.appendChild(p);
}
if (groups[groupId].repos.length) {
Object.keys(errortypes).filter(t => errorFilter.size === 0 || errorFilter.has(t))
.forEach(err => {
const repos = data.errors[err].filter(r => groups[groupId].repos.find(x => x.fullName === r || x.fullName === r.repo));
if (repos.length) {
const errsection = document.createElement('section');
const errtitle = document.createElement('h3');
errtitle.appendChild(document.createTextNode(errortypes[err]));
errsection.appendChild(errtitle);
const list = document.createElement('ul');
repos.forEach(repo => {
const repoName = typeof repo === "string" ? repo : repo.repo;
mentionedRepos.add(repoName);
writeErrorEntry(repoName, list, repo.error);
});
errsection.appendChild(list);
if (groupFilter(groupId)) {
section.appendChild(errsection);
}
}
});
}
report.appendChild(section);
});
const section = document.createElement('section');
const title = document.createElement('h2');
title.appendChild(document.createTextNode("No w3c.json"));
section.appendChild(title);
const ul = document.createElement('ul');
data.errors.incompletew3cjson
.filter(x => x.error === "group")
.forEach(x => writeErrorEntry(x.repo, ul, "missing group in w3c.json"));
data.errors.illformedw3cjson
.forEach(x => writeErrorEntry(x, ul, "ill-formed JSON"));
data.errors.now3cjson
.filter(x => x.startsWith('w3c/') || x.startsWith('WICG') || x.startsWith('WebAudio'))
.filter(x => !mentionedRepos.has(x))
.forEach(x => writeErrorEntry(x, ul, "no w3c.json"));
section.appendChild(ul);
report.appendChild(section);
}
fetch("report.json")
.then(async r => {
data = await r.json();
data.lastModified = r.headers.get('Last-Modified');
writeReport();
});