Standardize copyright year (#3737)

Standardizes all first-party copyrights on a single year, as is done in flutter/flutter and flutter/engine. All code now uses 2013, which is the earliest year that was in any existing copyright notice.

The script checks now enforce the exact format of first-party licenses and copyrights.

Fixes flutter/flutter#78448
This commit is contained in:
stuartmorgan
2021-03-19 13:18:00 -07:00
committed by GitHub
parent c956cb3270
commit aef6008b7b
31 changed files with 126 additions and 128 deletions

View File

@ -1,4 +1,4 @@
// Copyright 2017 The Flutter Authors. All rights reserved.
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -41,34 +41,30 @@ const Set<String> _ignoredFullBasenameList = <String>{
'resource.h', // Generated by VS.
};
// Copyright and license regexes.
// Copyright and license regexes for third-party code.
//
// These are intentionally very simple, since almost all source in this
// repository should be using the same license text, comment style, etc., so
// they shouldn't need to be very flexible. Complexity can be added as-needed
// on a case-by-case basis.
final RegExp _copyrightRegex =
RegExp(r'^(?://|#|<!--) Copyright \d+,? ([^.]+)', multiLine: true);
// Non-Flutter code. When adding license regexes here, include the copyright
// info to ensure that any new additions are flagged for added scrutiny in
// review.
// -----
// These are intentionally very simple, since there is very little third-party
// code in this repository. Complexity can be added as-needed on a case-by-case
// basis.
//
// When adding license regexes here, include the copyright info to ensure that
// any new additions are flagged for added scrutiny in review.
final List<RegExp> _thirdPartyLicenseBlockRegexes = <RegExp>[
// Third-party code used in url_launcher_web.
final RegExp _workivaLicenseRegex = RegExp(
r'^// Copyright 2017 Workiva Inc..*'
'^// Licensed under the Apache License, Version 2.0',
multiLine: true,
dotAll: true);
const String _firstPartyAuthors = 'The Flutter Authors';
RegExp(
r'^// Copyright 2017 Workiva Inc..*'
'^// Licensed under the Apache License, Version 2.0',
multiLine: true,
dotAll: true),
];
// The exact format of the BSD license that our license files should contain.
// Slight variants are not accepted because they may prevent consolidation in
// tools that assemble all licenses used in distributed applications.
//
// TODO(stuartmorgan): Add the copyright string here once that's completely
// standardized.
final String _fullBsdLicenseText = '''
final String _fullBsdLicenseText =
'''Copyright 2013 The Flutter Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@ -132,91 +128,80 @@ class LicenseCheckCommand extends PluginCommand {
}
}
// Creates the expected license block (without copyright) for first-party
// code.
String _generateLicense(String comment, {String suffix = ''}) {
return '${comment}Use of this source code is governed by a BSD-style license that can be\n'
// Creates the expected copyright+license block for first-party code.
String _generateLicenseBlock(
String comment, {
String prefix = '',
String suffix = '',
}) {
return '$prefix${comment}Copyright 2013 The Flutter Authors. All rights reserved.\n'
'${comment}Use of this source code is governed by a BSD-style license that can be\n'
'${comment}found in the LICENSE file.$suffix\n';
}
// Checks all license blocks for [codeFiles], returning false if any of them
// fail validation.
Future<bool> _checkCodeLicenses(Iterable<File> codeFiles) async {
final List<File> filesWithoutDetectedCopyright = <File>[];
final List<File> filesWithoutDetectedLicense = <File>[];
final List<File> misplacedThirdPartyFiles = <File>[];
final List<File> incorrectFirstPartyFiles = <File>[];
final List<File> unrecognizedThirdPartyFiles = <File>[];
// Most code file types in the repository use '//' comments.
final String defaultBsdLicenseBlock = _generateLicense('// ');
final String defaultFirstParyLicenseBlock = _generateLicenseBlock('// ');
// A few file types have a different comment structure.
final Map<String, String> bsdLicenseBlockByExtension = <String, String>{
'.sh': _generateLicense('# '),
'.html': _generateLicense('', suffix: ' -->'),
final Map<String, String> firstPartyLicenseBlockByExtension =
<String, String>{
'.sh': _generateLicenseBlock('# '),
'.html': _generateLicenseBlock('', prefix: '<!-- ', suffix: ' -->'),
};
for (final File file in codeFiles) {
_print('Checking ${file.path}');
final String content = await file.readAsString();
final RegExpMatch copyright = _copyrightRegex.firstMatch(content);
if (copyright == null) {
filesWithoutDetectedCopyright.add(file);
continue;
}
final String author = copyright.group(1);
if (author != _firstPartyAuthors && !_isThirdParty(file)) {
misplacedThirdPartyFiles.add(file);
}
final String bsdLicense =
bsdLicenseBlockByExtension[p.extension(file.path)] ??
defaultBsdLicenseBlock;
if (!content.contains(bsdLicense) &&
!_workivaLicenseRegex.hasMatch(content)) {
filesWithoutDetectedLicense.add(file);
if (_isThirdParty(file)) {
if (!_thirdPartyLicenseBlockRegexes
.any((regex) => regex.hasMatch(content))) {
unrecognizedThirdPartyFiles.add(file);
}
} else {
final String license =
firstPartyLicenseBlockByExtension[p.extension(file.path)] ??
defaultFirstParyLicenseBlock;
if (!content.contains(license)) {
incorrectFirstPartyFiles.add(file);
}
}
}
_print('\n');
// Sort by path for more usable output.
final pathCompare = (File a, File b) => a.path.compareTo(b.path);
filesWithoutDetectedCopyright.sort(pathCompare);
filesWithoutDetectedLicense.sort(pathCompare);
misplacedThirdPartyFiles.sort(pathCompare);
incorrectFirstPartyFiles.sort(pathCompare);
unrecognizedThirdPartyFiles.sort(pathCompare);
if (filesWithoutDetectedCopyright.isNotEmpty) {
_print('No copyright line was found for the following files:');
for (final File file in filesWithoutDetectedCopyright) {
if (incorrectFirstPartyFiles.isNotEmpty) {
_print('The license block for these files is missing or incorrect:');
for (final File file in incorrectFirstPartyFiles) {
_print(' ${file.path}');
}
_print('Please check that they have a copyright and license block. '
'If they do, the license check may need to be updated to recognize its '
'format.\n');
_print('If this third-party code, move it to a "third_party/" directory, '
'otherwise ensure that you are using the exact copyright and license '
'text used by all first-party files in this repository.\n');
}
if (filesWithoutDetectedLicense.isNotEmpty) {
_print('No recognized license was found for the following files:');
for (final File file in filesWithoutDetectedLicense) {
if (unrecognizedThirdPartyFiles.isNotEmpty) {
_print(
'No recognized license was found for the following third-party files:');
for (final File file in unrecognizedThirdPartyFiles) {
_print(' ${file.path}');
}
_print('Please check that they have a license at the top of the file. '
'If they do, the license check may need to be updated to recognize '
'either the license or the specific format of the license '
'block.\n');
'If they do, the license check needs to be updated to recognize '
'the new third-party license block.\n');
}
if (misplacedThirdPartyFiles.isNotEmpty) {
_print('The following files do not have a recognized first-party author '
'but are not in a "third_party/" directory:');
for (final File file in misplacedThirdPartyFiles) {
_print(' ${file.path}');
}
_print('Please move these files to "third_party/".\n');
}
bool succeeded = filesWithoutDetectedCopyright.isEmpty &&
filesWithoutDetectedLicense.isEmpty &&
misplacedThirdPartyFiles.isEmpty;
bool succeeded =
incorrectFirstPartyFiles.isEmpty && unrecognizedThirdPartyFiles.isEmpty;
if (succeeded) {
_print('All source files passed validation!');
}
@ -241,7 +226,8 @@ class LicenseCheckCommand extends PluginCommand {
for (final File file in incorrectLicenseFiles) {
_print(' ${file.path}');
}
_print('Please ensure that they use the exact format used in this repository".\n');
_print(
'Please ensure that they use the exact format used in this repository".\n');
}
bool succeeded = incorrectLicenseFiles.isEmpty;