Skip to content
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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 26 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,27 @@ jobs:
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
- run: yarn --cwd aio example-e2e --setup --local --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}

test_docs_examples_ivy:
<<: *job_defaults
docker:
# Needed because the example e2e tests depend on Chrome.
- image: *browsers_docker_image
parallelism: 3
steps:
- checkout:
<<: *post_checkout
- *restore_cache
- attach_workspace:
at: dist
- *define_env_vars
- *download_yarn
# Install aio
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
# Run examples tests with ivy. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
- run: yarn --cwd aio example-e2e --setup --local --ivy --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}

# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
aio_preview:
<<: *job_defaults
Expand Down Expand Up @@ -593,6 +614,9 @@ workflows:
- test_docs_examples:
requires:
- build-npm-packages
- test_docs_examples_ivy:
requires:
- build-npm-packages
- aio_preview:
# Only run on PR builds. (There can be no previews for non-PR builds.)
filters:
Expand All @@ -618,7 +642,8 @@ workflows:
- test_aio_local
- test_aio_local_ivy
- test_docs_examples
# Get the artifacts to publish from the build-npm-packages job
- test_docs_examples_ivy
# Get the artifacts to publish from the build-packages-dist job
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooops!

# since the publishing script expects the legacy outputs layout.
- build-npm-packages
- build-ivy-npm-packages
Expand Down
6 changes: 6 additions & 0 deletions aio/content/examples/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ dist/

# aot
**/*.ngsummary.json
upgrade-module/tsconfig-aot.json
!rollup-config.js
upgrade-module/rollup-config.js
aot-compiler/**/*.d.ts
aot-compiler/**/*.factory.d.ts
upgrade-phonecat-2-hybrid/aot/**/*
Expand All @@ -84,5 +86,9 @@ upgrade-phonecat-2-hybrid/aot/**/*
*stackblitz.no-link.html

# ngUpgrade testing
upgrade-phonecat-1-typescript/tsconfig-aot.json
upgrade-phonecat-1-typescript/rollup-config.js
upgrade-phonecat-3-final/tsconfig-aot.json
upgrade-phonecat-3-final/rollup-config.js
!upgrade-phonecat-*/**/karma.conf.js
!upgrade-phonecat-*/**/karma-test-shim.js
3 changes: 2 additions & 1 deletion aio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"preserve-and-sync": "yarn docs",
"serve-and-sync": "run-p \"start\" \"docs-watch --watch-only\"",
"boilerplate:add": "node ./tools/examples/example-boilerplate add",
"boilerplate:add:ivy": "yarn boilerplate:add --ivy",
"boilerplate:remove": "node ./tools/examples/example-boilerplate remove",
"boilerplate:test": "node tools/examples/test.js",
"generate-stackblitz": "node ./tools/stackblitz-builder/generateStackblitz",
Expand Down Expand Up @@ -167,4 +168,4 @@
"xregexp": "^4.0.0",
"yargs": "^7.0.2"
}
}
}
39 changes: 30 additions & 9 deletions aio/tools/examples/example-boilerplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`);
Copy link
Member

Choose a reason for hiding this comment

The 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 node_modules/.
Let's hope people don't need to do this locally 😁

}

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');
Expand All @@ -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;
Expand All @@ -144,7 +165,7 @@ class ExampleBoilerPlate {
}

loadJsonFile(filePath) {
return fs.readJsonSync(filePath, {throws: false}) || {};
return fs.readJsonSync(filePath, { throws: false }) || {};
}

normalizePath(filePath) {
Expand Down
73 changes: 56 additions & 17 deletions aio/tools/examples/run-example-e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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 });
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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); }
);
}

Expand Down Expand Up @@ -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:');
Copy link
Member

Choose a reason for hiding this comment

The 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);
Expand Down
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
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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

Copy link
Contributor

Choose a reason for hiding this comment

The 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"
}
}