-
Notifications
You must be signed in to change notification settings - Fork 24.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ci(docs-infra): build examples with Ivy #28463
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,22 +78,37 @@ BOILERPLATE_PATHS.universal = [ | |
'package.json' | ||
]; | ||
|
||
BOILERPLATE_PATHS.ivy = { | ||
systemjs: [ | ||
'rollup-config.js', | ||
'tsconfig-aot.json' | ||
], | ||
cli: [ | ||
'src/tsconfig.app.json' | ||
] | ||
}; | ||
|
||
const EXAMPLE_CONFIG_FILENAME = 'example-config.json'; | ||
|
||
class ExampleBoilerPlate { | ||
/** | ||
* Add boilerplate files to all the examples | ||
*/ | ||
add() { | ||
add(ivy = false) { | ||
// Get all the examples folders, indicated by those that contain a `example-config.json` file | ||
const exampleFolders = this.getFoldersContaining(EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, 'node_modules'); | ||
exampleFolders.forEach(exampleFolder => { | ||
const exampleConfig = this.loadJsonFile(path.resolve(exampleFolder, EXAMPLE_CONFIG_FILENAME)); | ||
|
||
if (!fs.existsSync(SHARED_NODE_MODULES_PATH)) { | ||
throw new Error(`The shared node_modules folder for the examples (${SHARED_NODE_MODULES_PATH}) is missing.\n` + | ||
if (!fs.existsSync(SHARED_NODE_MODULES_PATH)) { | ||
throw new Error(`The shared node_modules folder for the examples (${SHARED_NODE_MODULES_PATH}) is missing.\n` + | ||
`Perhaps you need to run "yarn example-use-npm" or "yarn example-use-local" to install the dependencies?`); | ||
} | ||
} | ||
|
||
if (ivy) { | ||
shelljs.exec(`yarn --cwd ${SHARED_PATH} ivy-ngcc`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, so this means that once you run examples with ivy, they will silently stay that way until you re-install |
||
} | ||
|
||
exampleFolders.forEach(exampleFolder => { | ||
const exampleConfig = this.loadJsonFile(path.resolve(exampleFolder, EXAMPLE_CONFIG_FILENAME)); | ||
|
||
// Link the node modules - requires admin access (on Windows) because it adds symlinks | ||
const destinationNodeModules = path.resolve(exampleFolder, 'node_modules'); | ||
|
@@ -107,20 +122,26 @@ class ExampleBoilerPlate { | |
|
||
// Copy the boilerplate common files | ||
BOILERPLATE_PATHS.common.forEach(filePath => this.copyFile(BOILERPLATE_COMMON_BASE_PATH, exampleFolder, filePath)); | ||
|
||
// Copy Ivy specific files | ||
if (ivy && BOILERPLATE_PATHS.ivy[boilerPlateType]) { | ||
const ivyBoilerPlateBasePath = path.resolve(BOILERPLATE_BASE_PATH, 'ivy', boilerPlateType); | ||
BOILERPLATE_PATHS.ivy[boilerPlateType].forEach(filePath => this.copyFile(ivyBoilerPlateBasePath, exampleFolder, filePath)); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Remove all the boilerplate files from all the examples | ||
*/ | ||
remove() { | ||
shelljs.exec('git clean -xdfq', {cwd: EXAMPLES_BASE_PATH}); | ||
shelljs.exec('git clean -xdfq', { cwd: EXAMPLES_BASE_PATH }); | ||
} | ||
|
||
main() { | ||
yargs | ||
.usage('$0 <cmd> [args]') | ||
.command('add', 'add the boilerplate to each example', () => this.add()) | ||
.command('add', 'add the boilerplate to each example', (yrgs) => this.add(yrgs.argv.ivy)) | ||
.command('remove', 'remove the boilerplate from each example', () => this.remove()) | ||
.demandCommand(1, 'Please supply a command from the list above') | ||
.argv; | ||
|
@@ -144,7 +165,7 @@ class ExampleBoilerPlate { | |
} | ||
|
||
loadJsonFile(filePath) { | ||
return fs.readJsonSync(filePath, {throws: false}) || {}; | ||
return fs.readJsonSync(filePath, { throws: false }) || {}; | ||
} | ||
|
||
normalizePath(filePath) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,28 @@ const IGNORED_EXAMPLES = [ // temporary ignores | |
'setup', | ||
]; | ||
|
||
const fixmeIvyExamples = [ | ||
// fixmeIvy('unknown') movie list route is not rendering | ||
'ajs-quick-reference', | ||
// fixmeIvy('unknown') version value goes undefined when clicking Major button after clicking Minor button twice | ||
'component-interaction', | ||
// fixmeIvy('unknown') failed content projection and applied styles | ||
'component-styles', | ||
// fails due to incompatible AoT code with a tree-shakable provider and a non-type constructor argument | ||
'dependency-injection', | ||
// fixmeIvy('unknown') Error: Internal Error: The name heroForm is already defined in scope to be [object Object] | ||
'forms', | ||
// fixmeIvy('unknown') app fails at runtime due to missing external service (goog is undefined) | ||
'i18n', | ||
// fixmeIvy('unknown') ERROR in HostResourceResolver: could not resolve app/app.component.css | ||
// in context of aio/content/examples/styleguide/src/01-01/app/heroes/hero.component.avoid.ts) | ||
'styleguide' | ||
]; | ||
|
||
if (argv.ivy) { | ||
IGNORED_EXAMPLES.push(...fixmeIvyExamples); | ||
} | ||
|
||
/** | ||
* Run Protractor End-to-End Tests for Doc Samples | ||
* | ||
|
@@ -44,7 +66,7 @@ function runE2e() { | |
// Run setup. | ||
console.log('runE2e: setup boilerplate'); | ||
const installPackagesCommand = `example-use-${argv.local ? 'local' : 'npm'}`; | ||
const addBoilerplateCommand = 'boilerplate:add'; | ||
const addBoilerplateCommand = `boilerplate:add${argv.ivy ? ':ivy' : ''}`; | ||
shelljs.exec(`yarn ${installPackagesCommand}`, { cwd: AIO_PATH }); | ||
shelljs.exec(`yarn ${addBoilerplateCommand}`, { cwd: AIO_PATH }); | ||
} | ||
|
@@ -102,16 +124,16 @@ function findAndRunE2eTests(filter, outputFile, shard) { | |
}); | ||
}); | ||
}, Promise.resolve()) | ||
.then(() => { | ||
return e2eSpecPaths.cli.reduce((promise, specPath) => { | ||
return promise.then(() => { | ||
return runE2eTestsCLI(specPath, outputFile).then(ok => { | ||
const arr = ok ? status.passed : status.failed; | ||
arr.push(specPath); | ||
.then(() => { | ||
return e2eSpecPaths.cli.reduce((promise, specPath) => { | ||
return promise.then(() => { | ||
return runE2eTestsCLI(specPath, outputFile).then(ok => { | ||
const arr = ok ? status.passed : status.failed; | ||
arr.push(specPath); | ||
}); | ||
}); | ||
}); | ||
}, Promise.resolve()); | ||
}); | ||
}, Promise.resolve()); | ||
}); | ||
}) | ||
.then(() => { | ||
const stopTime = new Date().getTime(); | ||
|
@@ -172,8 +194,8 @@ function runProtractorSystemJS(prepPromise, appDir, appRunSpawnInfo, outputFile) | |
}); | ||
}) | ||
.then( | ||
function () { return finish(appRunSpawnInfo.proc.pid, true); }, | ||
function () { return finish(appRunSpawnInfo.proc.pid, false); } | ||
function () { return finish(appRunSpawnInfo.proc.pid, true); }, | ||
function () { return finish(appRunSpawnInfo.proc.pid, false); } | ||
); | ||
} | ||
|
||
|
@@ -206,25 +228,42 @@ function runE2eTestsCLI(appDir, outputFile) { | |
console.log(`\n\n=========== Running aio example tests for: ${appDir}`); | ||
// `--no-webdriver-update` is needed to preserve the ChromeDriver version already installed. | ||
const config = loadExampleConfig(appDir); | ||
const commands = config.e2e || [{ cmd: 'yarn', args: ['e2e', '--no-webdriver-update'] }]; | ||
const commands = config.e2e || [{ cmd: 'yarn', args: ['e2e', (argv.ivy ? '--prod' : ''), '--no-webdriver-update'] }]; | ||
|
||
const e2eSpawnPromise = commands.reduce((prevSpawnPromise, { cmd, args }) => { | ||
return prevSpawnPromise.then(() => { | ||
const currSpawn = spawnExt(cmd, args, { cwd: appDir }); | ||
return currSpawn.promise.then( | ||
() => Promise.resolve(finish(currSpawn.proc.pid, true)), | ||
() => Promise.reject(finish(currSpawn.proc.pid, false))); | ||
() => Promise.resolve(finish(currSpawn.proc.pid, true)), | ||
() => Promise.reject(finish(currSpawn.proc.pid, false))); | ||
}); | ||
}, Promise.resolve()); | ||
|
||
return e2eSpawnPromise.then( | ||
() => { fs.appendFileSync(outputFile, `Passed: ${appDir}\n\n`); return true; }, | ||
() => { fs.appendFileSync(outputFile, `Failed: ${appDir}\n\n`); return false; }); | ||
() => { fs.appendFileSync(outputFile, `Passed: ${appDir}\n\n`); return true; }, | ||
() => { fs.appendFileSync(outputFile, `Failed: ${appDir}\n\n`); return false; }); | ||
} | ||
|
||
// Report final status. | ||
function reportStatus(status, outputFile) { | ||
let log = ['']; | ||
|
||
log.push('Suites ignored due to legacy guides:'); | ||
IGNORED_EXAMPLES | ||
.filter(example => !fixmeIvyExamples.find(ex => ex.startsWith(example))) | ||
.forEach(function (val) { | ||
log.push(' ' + val); | ||
}); | ||
|
||
if (argv.ivy) { | ||
log.push(''); | ||
log.push('Suites ignored due to breakage with Ivy:'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❤️ |
||
fixmeIvyExamples.forEach(function (val) { | ||
log.push(' ' + val); | ||
}); | ||
} | ||
|
||
log.push(''); | ||
log.push('Suites passed:'); | ||
status.passed.forEach(function (val) { | ||
log.push(' ' + val); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"extends": "../tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "../out-tsc/app", | ||
"types": [] | ||
}, | ||
"exclude": [ | ||
"test.ts", | ||
"**/*.spec.ts" | ||
], | ||
"angularCompilerOptions": { | ||
"enableIvy": "ngtsc", | ||
"allowEmptyCodegenFiles": true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this to enable shims? if so, we should add a todo and file an issue/jira ticket to remove this before v8 launch because we'd prefer to launch with shims turned off by default. but it is ok to merge this PR initially with shims if that makes more tests pass. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is necessary for lazy loading to work per angular/angular-cli#13524 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah. right. thnx! |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// #docregion | ||
import rollup from 'rollup' | ||
import nodeResolve from 'rollup-plugin-node-resolve' | ||
import commonjs from 'rollup-plugin-commonjs'; | ||
import uglify from 'rollup-plugin-uglify' | ||
|
||
//paths are relative to the execution path | ||
export default { | ||
entry: 'app/main.js', | ||
dest: 'aot/dist/build.js', // output a single application bundle | ||
sourceMap: true, | ||
sourceMapFile: 'aot/dist/build.js.map', | ||
format: 'iife', | ||
plugins: [ | ||
nodeResolve({ jsnext: true, module: true }), | ||
commonjs({ | ||
include: ['node_modules/rxjs/**'] | ||
}), | ||
uglify() | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "es5", | ||
"module": "es2015", | ||
"moduleResolution": "node", | ||
"sourceMap": true, | ||
"emitDecoratorMetadata": true, | ||
"experimentalDecorators": true, | ||
"lib": [ | ||
"es2015", | ||
"dom" | ||
], | ||
"removeComments": false, | ||
"noImplicitAny": true, | ||
"skipLibCheck": true, | ||
"suppressImplicitAnyIndexErrors": true | ||
}, | ||
"files": [ | ||
"app/app.module.ts", | ||
"app/main.ts" | ||
], | ||
"angularCompilerOptions": { | ||
"skipMetadataEmit": true, | ||
"enableIvy": "ngtsc" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooops!