This repository was archived by the owner on Aug 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathcheck_closure_oss.js
207 lines (189 loc) · 6.62 KB
/
check_closure_oss.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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview A utility to check that all files adhere to Google open-source
* standards.
*/
const {promises: fs} = require('fs');
const {exec} = require('child_process');
const path = require('path');
const {promisify} = require('util');
const IGNORED_EXTENSIONS =
['.gif', '.jpg', '.png', '.txt', '.data', '.json', '.enc', '.exe', '.yml'];
const IGNORED_FILES = [
'AUTHORS',
'CONTRIBUTING',
'LICENSE',
'README.md',
'WORKSPACE',
'.npmignore',
'closure-deps/AUTHORS',
'closure-deps/CONTRIBUTING',
'closure-deps/LICENSE',
'closure-deps/README.md',
'closure/known_issues/testdata/closure_library_warnings.txt',
'closure/goog/BUILD.bazel',
];
const APACHE_LICENSE_REGEXES =
[/apache license.*2\.0/i, /SPDX-License-Identifier: Apache-2.0/];
const CC_BY_LICENSE_REGEXES = [
/Documentation licensed under CC BY 4\.0/,
/License available at https:\/\/creativecommons\.org\/licenses\/by\/4\.0\//
];
const CONFORMANCE_ALLOWLIST_REGEX = /whitelist: \'(?!closurei\/)/i;
const CLOSURE_AUTHORS_COPYRIGHT_REGEX =
/Copyright( 20\d\d)? The Closure Library Authors/;
const DOC_FILE_REGEX = /(?:^doc\/.*\.html|\.md)$/;
const GOOGLE_COPYRIGHT_REGEX = /copyright.{0,70}[^@]google/i;
const CONFORMANCE_PROTO_PATH = 'closure/goog/conformance_proto.txt';
const REPOSITORY_PATH = path.resolve(`${__dirname}/..`);
const execP = promisify(exec);
/**
* Yield open-sourced Closure file paths and contents, one at a time.
*/
async function* yieldClosureFiles() {
const {stdout} = await execP('git ls-files');
const files = stdout.split('\n').filter(x => x);
for (const filePath of files) {
const ext = path.extname(filePath);
if (IGNORED_EXTENSIONS.includes(ext) || IGNORED_FILES.includes(filePath) ||
filePath.startsWith('third_party') || filePath.endsWith('/BUILD'))
continue;
yield {
filePath,
contents: (await fs.readFile(filePath, 'utf8')).split('\n')
};
}
}
/**
* A collection of licensing and file reference checks for open-sourced Closure
* files.
*/
class ClosureOSSChecker {
/**
* Checks whether a conformance textproto file contains only Closure paths.
* @param {string} filePath The path to conformance_proto.txt.
* @param {!Array<string>} contents The contents of conformance_proto.txt.
* @return {!Array<string>} A list of error strings. An empty list indicates
* that the check passes.
*/
static hasOnlyClosurePathsInConformance(filePath, contents) {
const errors = [];
let lineNo = 1;
for (const line of contents) {
if (CONFORMANCE_ALLOWLIST_REGEX.test(line)) {
errors.push(`${filePath}: Non-Closure path found on line ${lineNo}`);
}
lineNo++;
}
return errors;
}
/**
* Checks whether the given file has a copyright notice for Closure Authors.
* @param {string} filePath The path to the file to scan.
* @param {!Array<string>} contents The contents of a file to scan.
* @return {!Array<string>} A list of error strings. An empty list indicates
* that the check passes.
*/
static hasClosureCopyright(filePath, contents) {
for (const line of contents) {
if (CLOSURE_AUTHORS_COPYRIGHT_REGEX.test(line)) {
return [];
}
}
return [`${filePath}: Could not find Closure Authors copyright in file.`];
}
/**
* Checks whether the given file mentions the Apache 2 license.
* @param {string} filePath The path to the file to scan.
* @param {!Array<string>} contents The contents of a file to scan.
* @return {!Array<string>} A list of error strings. An empty list indicates
* that the check passes.
*/
static mentionsApache(filePath, contents) {
for (const line of contents) {
if (APACHE_LICENSE_REGEXES.some(regex => regex.test(line))) {
return [];
}
}
return [`${filePath}: File does not mention Apache License 2.0`];
}
/**
* Checks whether the given file mentions the CC BY license.
* @param {string} filePath The path to the file to scan.
* @param {!Array<string>} contents The contents of a file to scan.
* @return {!Array<string>} A list of error strings. An empty list indicates
* that the check passes.
*/
static mentionsCCBYLicense(filePath, contents) {
let remaining = CC_BY_LICENSE_REGEXES;
for (const line of contents) {
remaining = remaining.filter(regex => !regex.test(line));
if (remaining.length === 0) {
return [];
}
}
return [
`${filePath}: Documentation file does not mention CC BY 4.0 license`
];
}
/**
* Checks whether the given file is free of any Google copyright notices.
* @param {string} filePath The path to the file to scan.
* @param {!Array<string>} contents The contents of a file to scan.
* @return {!Array<string>} A list of error strings. An empty list indicates
* that the check passes.
*/
static omitsGoogleCopyright(filePath, contents) {
const errors = [];
let lineNo = 1;
for (const line of contents) {
if (GOOGLE_COPYRIGHT_REGEX.test(line)) {
errors.push(
`${filePath}: Copyright Google statement found on line ${lineNo}`);
}
lineNo++;
}
return errors;
}
}
/**
* Checks that open-sourced files in this repository satisfy licensing and file
* reference checks.
* @return {!Promise<number>} The exit code.
*/
async function main() {
const errors = [];
const conformanceProto =
(await fs.readFile(
path.relative(`${__dirname}/..`, CONFORMANCE_PROTO_PATH), 'utf8'))
.split('\n');
errors.push(...ClosureOSSChecker.hasOnlyClosurePathsInConformance(
CONFORMANCE_PROTO_PATH, conformanceProto));
for await (const {filePath, contents} of yieldClosureFiles()) {
// We check that there are no Google copyrights in every file except this
// one, because the regex used to match for it (GOOGLE_COPYRIGHT_REGEX)
// matches its own string representation.
const isThisFile = filePath === path.relative(REPOSITORY_PATH, __filename);
if (!isThisFile) {
errors.push(
...ClosureOSSChecker.omitsGoogleCopyright(filePath, contents));
}
if (DOC_FILE_REGEX.test(filePath)) {
errors.push(...ClosureOSSChecker.mentionsCCBYLicense(filePath, contents));
} else if (!filePath.startsWith('doc/')) {
errors.push(...ClosureOSSChecker.mentionsApache(filePath, contents));
errors.push(...ClosureOSSChecker.hasClosureCopyright(filePath, contents));
}
}
if (errors.length > 0) {
console.error(errors.join('\n'));
return 1;
}
console.error('All checks pass');
return 0;
}
main().then(code => process.exit(code));