From c46ba07151a619f2eda138dc4618d7a9eacf67d2 Mon Sep 17 00:00:00 2001 From: mmalerba Date: Mon, 23 Oct 2017 13:20:29 -0700 Subject: [PATCH] lint: ensure all files are owned by someone (#7937) * lint: ensure all files are owned by someone * address comments --- .github/CODEOWNERS | 230 ++++++++++++++++++++++++++++----------- .gitignore | 1 + package.json | 1 + tools/gulp/tasks/lint.ts | 78 ++++++++++++- 4 files changed, 245 insertions(+), 65 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index abe16bb71d0c..6fc4773ddc31 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,69 +1,177 @@ # Angular Material components -src/lib/list/** @jelbourn @crisbeto -src/lib/tooltip/** @andrewseguin -src/lib/button-toggle/** @tinayuangao -src/lib/snack-bar/** @jelbourn @crisbeto @josephperrott -src/lib/radio/** @tinayuangao @devversion -src/lib/card/** @jelbourn -src/lib/input/** @mmalerba -src/lib/progress-spinner/** @jelbourn @crisbeto @josephperrott -src/lib/datepicker/** @mmalerba -src/lib/sort/** @andrewseguin -src/lib/autocomplete/** @kara @crisbeto -src/lib/chips/** @tinayuangao -src/lib/icon/** @jelbourn -src/lib/dialog/** @jelbourn @crisbeto -src/lib/progress-bar/** @jelbourn @crisbeto @josephperrott -src/lib/grid-list/** @kara @jelbourn -src/lib/select/** @kara @crisbeto -src/lib/expansion/** @josephperrott @jelbourn -src/lib/slide-toggle/** @devversion -src/lib/toolbar/** @devversion -src/lib/button/** @tinayuangao -src/lib/checkbox/** @tinayuangao @devversion -src/lib/table/** @andrewseguin -src/lib/slider/** @mmalerba -src/lib/sidenav/** @mmalerba -src/lib/menu/** @kara @crisbeto -src/lib/paginator/** @andrewseguin -src/lib/tabs/** @andrewseguin +/src/lib/* @jelbourn +/src/lib/autocomplete/** @kara @crisbeto +/src/lib/button-toggle/** @tinayuangao +/src/lib/button/** @tinayuangao +/src/lib/card/** @jelbourn +/src/lib/checkbox/** @tinayuangao @devversion +/src/lib/chips/** @tinayuangao +/src/lib/datepicker/** @mmalerba +/src/lib/dialog/** @jelbourn @crisbeto +/src/lib/expansion/** @josephperrott @jelbourn +/src/lib/form-field/** @mmalerba +/src/lib/grid-list/** @kara @jelbourn +/src/lib/icon/** @jelbourn +/src/lib/input/** @mmalerba +/src/lib/list/** @jelbourn @crisbeto +/src/lib/menu/** @kara @crisbeto +/src/lib/paginator/** @andrewseguin +/src/lib/progress-bar/** @jelbourn @crisbeto @josephperrott +/src/lib/progress-spinner/** @jelbourn @crisbeto @josephperrott +/src/lib/radio/** @tinayuangao @devversion +/src/lib/select/** @kara @crisbeto +/src/lib/sidenav/** @mmalerba +/src/lib/slide-toggle/** @devversion +/src/lib/slider/** @mmalerba +/src/lib/snack-bar/** @jelbourn @crisbeto @josephperrott +/src/lib/sort/** @andrewseguin +/src/lib/stepper/** @mmalerba +/src/lib/table/** @andrewseguin +/src/lib/tabs/** @andrewseguin +/src/lib/toolbar/** @devversion +/src/lib/tooltip/** @andrewseguin # Angular Material core -src/lib/core/selection/** @tinayuangao @jelbourn -src/lib/core/selection/pseudo*/** @crisbeto @jelbourn -src/lib/core/theming/** @jelbourn -src/lib/core/option/** @kara @crisbeto -src/lib/core/rxjs/** @jelbourn -src/lib/core/ripple/** @devversion -src/lib/core/a11y/** @jelbourn @devversion -src/lib/core/compatibility/** @jelbourn -src/lib/core/overlay/** @jelbourn @crisbeto -src/lib/core/overlay/scroll/** @andrewseguin @crisbeto -src/lib/core/platform/** @jelbourn @devversion -src/lib/core/bidi/** @jelbourn -src/lib/core/placeholder/** @kara @mmalerba -src/lib/core/portal/** @jelbourn -src/lib/core/typography/** @crisbeto -src/lib/core/datetime/** @mmalerba +/src/lib/core/* @jelbourn +/src/lib/core/animation/** @jelbourn +/src/lib/core/common-behaviors/** @jelbourn +/src/lib/core/compatibility/** @jelbourn +/src/lib/core/datetime/** @mmalerba +/src/lib/core/error/** @crisbeto @mmalerba +/src/lib/core/gestures/** @jelbourn +/src/lib/core/line/** @jelbourn +/src/lib/core/option/** @kara @crisbeto +/src/lib/core/placeholder/** @kara @mmalerba +/src/lib/core/ripple/** @devversion +/src/lib/core/selection/** @tinayuangao @jelbourn +/src/lib/core/selection/pseudo*/** @crisbeto @jelbourn +/src/lib/core/style/** @jelbourn +/src/lib/core/testing/** @jelbourn +/src/lib/core/theming/** @jelbourn +/src/lib/core/typography/** @crisbeto +/src/lib/core/util/** @jelbourn # CDK -src/cdk/coercion/** @jelbourn -src/cdk/rxjs/** @jelbourn -src/cdk/observers/** @jelbourn @crisbeto -src/cdk/collections/** @jelbourn @crisbeto @andrewseguin -src/cdk/a11y/** @jelbourn @devversion -src/cdk/platform/** @jelbourn @devversion -src/cdk/bidi/** @jelbourn -src/cdk/table/** @andrewseguin -src/cdk/portal/** @jelbourn +/src/cdk/* @jelbourn +/src/cdk/a11y/** @jelbourn @devversion +/src/cdk/accordion/** @josephperrott +/src/cdk/bidi/** @jelbourn +/src/cdk/coercion/** @jelbourn +/src/cdk/collections/** @jelbourn @crisbeto @andrewseguin +/src/cdk/keycodes/** @jelbourn +/src/cdk/layout/** @josephperrott +/src/cdk/observers/** @jelbourn @crisbeto +/src/cdk/overlay/** @jelbourn @crisbeto +/src/cdk/platform/** @jelbourn @devversion +/src/cdk/portal/** @jelbourn +/src/cdk/rxjs/** @jelbourn +/src/cdk/scrolling/** @andrewseguin @crisbeto +/src/cdk/stepper/** @mmalerba +/src/cdk/table/** @andrewseguin +/src/cdk/testing/** @devversion -# Tooling -tools/** @devversion @jelbourn -test/** @devversion @jelbourn -scripts/** @devversion @jelbourn +# Moment adapter package +/src/material-moment-adapter/** @mmalerba -# Docs examples -src/material-examples/** @amcdnl @jelbourn +# Docs examples & guides +/guides/** @amcdnl @jelbourn +/src/material-examples/** @amcdnl @jelbourn -# Moment adapter package -src/material-moment-adapter/** @mmalerba +# Demo app +/src/demo-app/* @jelbourn +/src/demo-app/a11y/** @tinayuangao +/src/demo-app/autocomplete/** @kara @crisbeto +/src/demo-app/baseline/** @mmalerba +/src/demo-app/button-toggle/** @tinayuangao +/src/demo-app/button/** @tinayuangao +/src/demo-app/card/** @jelbourn +/src/demo-app/checkbox/** @tinayuangao @devversion +/src/demo-app/chips/** @tinayuangao +/src/demo-app/dataset/** @andrewseguin +/src/demo-app/datepicker/** @mmalerba +/src/demo-app/demo-app/** @jelbourn +/src/demo-app/dialog/** @jelbourn @crisbeto +/src/demo-app/drawer/** @mmalerba +/src/demo-app/expansion/** @josephperrott +/src/demo-app/focus-origin/** @mmalerba +/src/demo-app/gestures/** @jelbourn +/src/demo-app/grid-list/** @kara @jelbourn +/src/demo-app/icon/** @jelbourn +/src/demo-app/input/** @mmalerba +/src/demo-app/list/** @jelbourn @crisbeto +/src/demo-app/live-announcer/** @jelbourn +/src/demo-app/menu/** @kara @crisbeto +/src/demo-app/overlay/** @jelbourn @crisbeto +/src/demo-app/platform/** @jelbourn @devversion +/src/demo-app/portal/** @jelbourn +/src/demo-app/progress-bar/** @jelbourn @crisbeto @josephperrott +/src/demo-app/progress-spinner/** @jelbourn @crisbeto @josephperrott +/src/demo-app/radio/** @tinayuangao @devversion +/src/demo-app/ripple/** @devversion +/src/demo-app/screen-type/** @josephperrott +/src/demo-app/select/** @kara @crisbeto +/src/demo-app/sidenav/** @mmalerba +/src/demo-app/slide-toggle/** @devversion +/src/demo-app/slider/** @mmalerba +/src/demo-app/snack-bar/** @jelbourn @crisbeto @josephperrott +/src/demo-app/stepper/** @mmalerba +/src/demo-app/table/** @andrewseguin +/src/demo-app/tabs/** @andrewseguin +/src/demo-app/toolbar/** @devversion +/src/demo-app/tooltip/** @andrewseguin +/src/demo-app/typography/** @crisbeto + +# E2E app +/e2e/* @jelbourn +/e2e/components/block-scroll-strategy-e2e.spec.ts @andrewseguin @crisbeto +/e2e/components/button-e2e.spec.ts @tinayuangao +/e2e/components/button-toggle-e2e.spec.ts @tinayuangao +/e2e/components/card-e2e.spec.ts @jelbourn +/e2e/components/checkbox-e2e.spec.ts @tinayuangao @devversion +/e2e/components/dialog-e2e.spec.ts @jelbourn @crisbeto +/e2e/components/expansion-e2e.spec.ts @josephperrott @jelbourn +/e2e/components/fullscreen-e2e.spec.ts @jelbourn +/e2e/components/grid-list-e2e.spec.ts @kara @jelbourn +/e2e/components/icon-e2e.spec.ts @jelbourn +/e2e/components/input-e2e.spec.ts @mmalerba +/e2e/components/list-e2e.spec.ts @jelbourn @crisbeto +/e2e/components/menu-e2e.spec.ts @kara @crisbeto +/e2e/components/progress-bar-e2e.spec.ts @jelbourn @crisbeto @josephperrott +/e2e/components/progress-spinner-e2e.spec.ts @jelbourn @crisbeto @josephperrott +/e2e/components/radio-e2e.spec.ts @tinayuangao @devversion +/e2e/components/sidenav-e2e.spec.ts @mmalerba +/e2e/components/slide-toggle-e2e.spec.ts @devversion +/e2e/components/stepper-e2e.spec.ts @mmalerba +/e2e/components/tabs-e2e.spec.ts @andrewseguin +/e2e/components/toolbar-e2e.spec.ts @devversion +/e2e/util/** @jelbourn +/src/e2e-app/* @jelbourn +/src/e2e-app/block-scroll-strategy/** @andrewseguin @crisbeto +/src/e2e-app/button/** @tinayuangao +/src/e2e-app/checkbox/** @tinayuangao @devversion +/src/e2e-app/dialog/** @jelbourn @crisbeto +/src/e2e-app/e2e-app/** @jelbourn +/src/e2e-app/fullscreen/** @jelbourn +/src/e2e-app/grid-list/** @kara @jelbourn +/src/e2e-app/icon/** @jelbourn +/src/e2e-app/input/** @mmalerba +/src/e2e-app/menu/** @kara @crisbeto +/src/e2e-app/progress-bar/** @jelbourn @crisbeto @josephperrott +/src/e2e-app/progress-spinner/** @jelbourn @crisbeto @josephperrott +/src/e2e-app/radio/** @tinayuangao @devversion +/src/e2e-app/sidenav/** @mmalerba +/src/e2e-app/slide-toggle/** @devversion +/src/e2e-app/tabs/** @andrewseguin + +# Universal app +/src/universal-app/** @jelbourn + +# Tooling +/scripts/** @devversion @jelbourn +/test/** @devversion @jelbourn +/tools/** @devversion @jelbourn + +# Misc +/* @jelbourn +/.github/** @jelbourn +/src/* @jelbourn diff --git a/.gitignore b/.gitignore index 24e821d2e946..9804dad5288e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ node_modules npm-debug.log testem.log /.chrome +/.git diff --git a/package.json b/package.json index d49c4b9ae77d..751486727401 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@types/fs-extra": "^3.0.1", "@types/glob": "^5.0.30", "@types/gulp": "3.8.32", + "@types/gulp-util": "^3.0.31", "@types/hammerjs": "^2.0.34", "@types/jasmine": "2.5.45", "@types/merge2": "^0.3.30", diff --git a/tools/gulp/tasks/lint.ts b/tools/gulp/tasks/lint.ts index b51b84ba654f..aca4854a0995 100644 --- a/tools/gulp/tasks/lint.ts +++ b/tools/gulp/tasks/lint.ts @@ -1,8 +1,11 @@ +import {red} from 'chalk'; +import {readdirSync, readFileSync, statSync} from 'fs'; import {task} from 'gulp'; -import {execNodeTask} from '../util/task_helpers'; -import {join} from 'path'; +import {colors, log} from 'gulp-util'; import {buildConfig} from 'material2-build-tools'; -import {red} from 'chalk'; +import {IMinimatch, Minimatch} from 'minimatch'; +import {join} from 'path'; +import {execNodeTask} from '../util/task_helpers'; // These types lack of type definitions const madge = require('madge'); @@ -19,7 +22,13 @@ const materialOutPath = join(buildConfig.outputDir, 'packages', 'material'); /** Path to the output of the CDK package. */ const cdkOutPath = join(buildConfig.outputDir, 'packages', 'cdk'); -task('lint', ['tslint', 'stylelint', 'madge']); +/** Path for the Github owners file. */ +const ownersFilePath = '.github/CODEOWNERS'; + +/** Path for the .gitignore file. */ +const gitIgnorePath = '.gitignore'; + +task('lint', ['tslint', 'stylelint', 'madge', 'ownerslint']); /** Task to lint Angular Material's scss stylesheets. */ task('stylelint', execNodeTask( @@ -46,6 +55,67 @@ task('madge', ['material:clean-build'], () => { }); }); +task('ownerslint', () => { + let errors = 0; + + let ownedPaths = readFileSync(ownersFilePath, 'utf8').split('\n') + // Trim lines. + .map(line => line.trim()) + // Remove empty lines and comments. + .filter(line => line && !line.startsWith('#')) + // Split off just the path glob. + .map(line => line.split(/\s+/)[0]) + // Turn paths into Minimatch objects. + .map(path => new Minimatch(path, {dot: true, matchBase: true})); + + let ignoredPaths = readFileSync(gitIgnorePath, 'utf8').split('\n') + // Trim lines. + .map(line => line.trim()) + // Remove empty lines and comments. + .filter(line => line && !line.startsWith('#')) + // Turn paths into Minimatch objects. + .map(path => new Minimatch(path, {dot: true, matchBase: true})); + + for (let paths = getChildPaths('.'); paths.length;) { + paths = Array.prototype.concat(...paths + // Remove ignored paths + .filter(path => !ignoredPaths.reduce( + (isIgnored, ignoredPath) => isIgnored || ignoredPath.match('/' + path), false)) + // Remove paths that match an owned path. + .filter(path => !ownedPaths.reduce( + (isOwned, ownedPath) => isOwned || isOwnedBy(path, ownedPath), false)) + // Report an error for any files that didn't match any owned paths. + .filter(path => { + if (statSync(path).isFile()) { + log(colors.red(`No code owner found for "${path}".`)); + errors++; + return false; + } + return true; + }) + // Get the next level of children for any directories. + .map(path => getChildPaths(path))); + } + + if (errors) { + throw Error(`Found ${errors} files with no owner.`); + } +}); + +/** Check if the given path is owned by the given owned path matcher. */ +function isOwnedBy(path: string, ownedPath: IMinimatch) { + // If the owned path ends with `**` its safe to eliminate whole directories. + if (ownedPath.pattern.endsWith('**') || statSync(path).isFile()) { + return ownedPath.match('/' + path); + } + return false; +} + +/** Get the immediate child paths of the given path. */ +function getChildPaths(path: string) { + return readdirSync(path).map(child => join(path, child)); +} + /** Returns a string that formats the graph of circular modules. */ function formatMadgeCircularModules(circularModules: string[][]): string { return circularModules.map((modulePaths: string[]) => `\n - ${modulePaths.join(' > ')}`).join('');