Skip to content

Commit 91d4c0f

Browse files
devversionjelbourn
authored andcommitted
build: share release output between circle jobs (#13838)
Also support parallel Karma instances on CircleCI
1 parent b3a0037 commit 91d4c0f

File tree

7 files changed

+121
-38
lines changed

7 files changed

+121
-38
lines changed

.circleci/config.yml

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ var_9: &docker-firefox-image
6060
# See the PR that fixes this: https://github.com/angular/angular/pull/26435
6161
- image: circleci/node:10.12-browsers
6262

63+
# Attaches the release output which has been stored in the workspace to the current job.
64+
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
65+
var_10: &attach_release_output
66+
attach_workspace:
67+
at: dist/releases
68+
6369
# -----------------------------
6470
# Container version of CircleCI
6571
# -----------------------------
@@ -86,8 +92,6 @@ jobs:
8692
- run: bazel build src/...
8793
- run: bazel test src/...
8894

89-
- *save_cache
90-
9195
# ------------------------------------------------------------------------------------------
9296
# Job that runs the unit tests on locally installed browsers (Chrome and Firefox headless).
9397
# The available browsers are installed through the angular/ngcontainer Docker image.
@@ -106,15 +110,11 @@ jobs:
106110
# variable which has been configured above
107111
- run: yarn gulp ci:test
108112

109-
- *save_cache
110-
111113
# ----------------------------------------------------------------
112-
# Job that runs the e2e tests with Protractor and Chrome w/ Xvfb. We cannot use
113-
# Chrome headless because our tests rely on APIs which are not testable in headless mode.
114-
# For example: the Fullscreen browser API.
114+
# Job that runs the e2e tests with Protractor and Chrome Headless
115115
# ----------------------------------------------------------------
116116
e2e_tests:
117-
docker: *docker-firefox-image
117+
<<: *job_defaults
118118
resource_class: xlarge
119119
steps:
120120
- *checkout_code
@@ -123,8 +123,6 @@ jobs:
123123

124124
- run: yarn gulp ci:e2e
125125

126-
- *save_cache
127-
128126
# ----------------------------------------------------------------------------
129127
# Job that runs the unit tests on Browserstack. The browsers that will be used
130128
# to run the unit tests on Browserstack are set in: test/browser-providers.js
@@ -135,46 +133,41 @@ jobs:
135133
environment:
136134
BROWSER_STACK_USERNAME: "angularteam1"
137135
BROWSER_STACK_ACCESS_KEY: "CaXMeMHD9pr5PHg8N7Jq"
136+
parallelism: 2
138137
steps:
139138
- *checkout_code
140139
- *restore_cache
141140
- *yarn_install
142141

143142
- run: ./scripts/circleci/run-browserstack-tests.sh
144143

145-
- *save_cache
146-
147-
# --------------------------------------
148-
# Job that builds the demo-app with AOT
149-
# --------------------------------------
150-
demo_app_aot:
144+
# -----------------------------------------------------------------------------------------
145+
# Job that builds the demo-app with AOT. In order to speed up this job, the release output
146+
# from the workspace storage will be attached to this job.
147+
# -----------------------------------------------------------------------------------------
148+
build_demoapp_aot:
151149
<<: *job_defaults
152-
resource_class: xlarge
153150
steps:
154151
- *checkout_code
155152
- *restore_cache
156153
- *yarn_install
154+
- *attach_release_output
157155

158156
- run: yarn gulp ci:aot
159157

160-
- *save_cache
161-
162158
# -------------------------------------------------------------------------
163159
# Job that pre-render's the universal app with `@angular/platform-server`.
164160
# This verifies that Angular Material can be rendered within Node.
165161
# -------------------------------------------------------------------------
166162
prerender_build:
167163
<<: *job_defaults
168-
resource_class: xlarge
169164
steps:
170165
- *checkout_code
171166
- *restore_cache
172167
- *yarn_install
173168

174169
- run: yarn gulp ci:prerender
175170

176-
- *save_cache
177-
178171
# ----------------------------------
179172
# Lint job. Runs the gulp lint task.
180173
# ----------------------------------
@@ -187,6 +180,27 @@ jobs:
187180

188181
- run: yarn gulp ci:lint
189182

183+
# -------------------------------------------------------------------------------------------
184+
# Job that builds all release packages with Gulp. The built packages can be then used in the
185+
# same workflow to publish snapshot builds or test the demo-app with the release packages.
186+
# -------------------------------------------------------------------------------------------
187+
build_release_packages:
188+
<<: *job_defaults
189+
resource_class: xlarge
190+
steps:
191+
- *checkout_code
192+
- *restore_cache
193+
- *yarn_install
194+
195+
- run: yarn gulp ci:build-release-packages
196+
197+
# Store the release output in the workspace storage. This means that other jobs
198+
# in the same workflow can attach the release output to their job.
199+
- persist_to_workspace:
200+
root: dist/releases
201+
paths:
202+
- "**/*"
203+
190204
- *save_cache
191205

192206
# ----------------------------------------------------------------------------------------
@@ -214,7 +228,13 @@ workflows:
214228
jobs:
215229
- e2e_tests
216230
- prerender_build
217-
- demo_app_aot
231+
232+
release_output:
233+
jobs:
234+
- build_release_packages
235+
- build_demoapp_aot:
236+
requires:
237+
- build_release_packages
218238

219239
# Lint workflow. As we want to lint in one job, this is a workflow with just one job.
220240
lint:

scripts/browserstack/start-tunnel.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ rm ${tunnelFileName}
3131
ARGS=""
3232

3333
if [ ! -z "${CIRCLE_BUILD_NUM}" ]; then
34-
ARGS="${ARGS} --local-identifier ${CIRCLE_BUILD_NUM}"
34+
ARGS="${ARGS} --local-identifier ${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}"
3535
fi
3636

3737
echo "Starting Browserstack Local in the background, logging into: ${tunnelLogFile}"

src/demo-app/tsconfig-aot.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
"outDir": "../../dist/packages/demo-app",
1515
"rootDirs": [
1616
".",
17+
// Include the package output here because otherwise NGC won't be able to load
18+
// the SCSS files.
1719
"../../dist/packages/demo-app"
1820
],
1921
"paths": {
@@ -26,7 +28,7 @@
2628
"@angular/cdk-experimental/*": ["../../dist/releases/cdk-experimental/*"],
2729
"@angular/cdk-experimental": ["../../dist/releases/cdk-experimental"],
2830
"@angular/material-moment-adapter": ["../../dist/releases/material-moment-adapter"],
29-
"@angular/material-examples": ["../../dist/packages/material-examples"]
31+
"@angular/material-examples": ["../../dist/releases/material-examples"]
3032
}
3133
},
3234
"files": [

test/karma.conf.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ module.exports = (config) => {
101101
});
102102

103103
if (process.env['CIRCLECI']) {
104-
const tunnelIdentifier = process.env['CIRCLE_BUILD_NUM'];
104+
const instanceIndex = Number(process.env['CIRCLE_NODE_INDEX']);
105+
const maxParallelInstances = Number(process.env['CIRCLE_NODE_TOTAL']);
106+
const tunnelIdentifier = `${process.env['CIRCLE_BUILD_NUM']}-${instanceIndex}`;
105107
const buildIdentifier = `angular-material-${tunnelIdentifier}`;
106108
const testPlatform = process.env['TEST_PLATFORM'];
107109

@@ -110,8 +112,13 @@ module.exports = (config) => {
110112
config.browserStack.tunnelIdentifier = tunnelIdentifier;
111113
}
112114

113-
// Configure Karma launch the browsers that belong to the given test platform.
114-
config.browsers = platformMap[testPlatform];
115+
const platformBrowsers = platformMap[testPlatform];
116+
const browserInstanceChunks = splitBrowsersIntoInstances(
117+
platformBrowsers, maxParallelInstances);
118+
119+
// Configure Karma to launch the browsers that belong to the given test platform
120+
// and instance.
121+
config.browsers = browserInstanceChunks[instanceIndex];
115122
}
116123

117124
if (process.env['TRAVIS']) {
@@ -150,3 +157,21 @@ module.exports = (config) => {
150157
config.browsers = platformMap[platform];
151158
}
152159
};
160+
161+
/**
162+
* Splits the specified browsers into a maximum amount of chunks. The chunk of browsers
163+
* are being created deterministically and therefore we get reproducible tests when executing
164+
* the same CircleCI instance multiple times.
165+
*/
166+
function splitBrowsersIntoInstances(browsers, maxInstances) {
167+
let chunks = [];
168+
let assignedBrowsers = 0;
169+
170+
for (let i = 0; i < maxInstances; i++) {
171+
const chunkSize = Math.floor((browsers.length - assignedBrowsers) / (maxInstances - i));
172+
chunks[i] = browsers.slice(assignedBrowsers, assignedBrowsers + chunkSize);
173+
assignedBrowsers += chunkSize;
174+
}
175+
176+
return chunks;
177+
}

tools/gulp/packages.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,13 @@ cdkPackage.copySecondaryEntryPointStylesToRoot = true;
2424
// Build and copy the schematics of the CDK and Material package.
2525
cdkPackage.hasSchematics = true;
2626
materialPackage.hasSchematics = true;
27+
28+
/** List of all build packages defined for this project. */
29+
export const allBuildPackages = [
30+
cdkPackage,
31+
materialPackage,
32+
cdkExperimentalPackage,
33+
materialExperimentalPackage,
34+
momentAdapterPackage,
35+
examplesPackage
36+
];

tools/gulp/tasks/aot.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,24 @@ const demoAppSource = join(packagesDir, 'demo-app');
1111
/** Path to the tsconfig file that builds the AOT files. */
1212
const tsconfigFile = join(demoAppSource, 'tsconfig-aot.json');
1313

14+
/**
15+
* Build the demo-app wit the release output in order confirm that the library is
16+
* working with AOT compilation enabled.
17+
*/
18+
task('build-aot', sequenceTask(
19+
'clean',
20+
['build-aot:release-packages', 'build-aot:assets'],
21+
'build-aot:compiler-cli'
22+
));
23+
24+
/**
25+
* Task that can be used to build the demo-app with AOT without building the
26+
* release output. This can be run if the release output is already built.
27+
*/
28+
task('build-aot:no-release-build', sequenceTask('build-aot:assets', 'build-aot:compiler-cli'));
29+
1430
/** Builds the demo-app assets and builds the required release packages. */
15-
task('aot:deps', sequenceTask(
31+
task('build-aot:release-packages', sequenceTask(
1632
[
1733
'cdk:build-release',
1834
'material:build-release',
@@ -21,15 +37,15 @@ task('aot:deps', sequenceTask(
2137
'material-moment-adapter:build-release',
2238
'material-examples:build-release',
2339
],
24-
// Build the assets after the releases have been built, because the demo-app assets import
25-
// SCSS files from the release packages.
26-
[':build:devapp:assets', ':build:devapp:scss'],
2740
));
2841

29-
/** Build the demo-app and a release to confirm that the library is AOT-compatible. */
30-
task('aot:build', sequenceTask('clean', 'aot:deps', 'aot:compiler-cli'));
42+
/**
43+
* Task that builds the assets which are required for building with AOT. Since the demo-app uses
44+
* Sass files, we need to provide the transpiled CSS sources in the package output.
45+
*/
46+
task('build-aot:assets', [':build:devapp:assets', ':build:devapp:scss']);
3147

3248
/** Build the demo-app and a release to confirm that the library is AOT-compatible. */
33-
task('aot:compiler-cli', execNodeTask(
49+
task('build-aot:compiler-cli', execNodeTask(
3450
'@angular/compiler-cli', 'ngc', ['-p', tsconfigFile]
3551
));

tools/gulp/tasks/ci.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {task} from 'gulp';
2-
2+
import {sequenceTask} from 'material2-build-tools';
3+
import {allBuildPackages} from '../packages';
34

45
task('ci:lint', ['lint']);
56

@@ -8,8 +9,11 @@ task('ci:test', ['test:single-run'], () => process.exit(0));
89

910
task('ci:e2e', ['e2e']);
1011

11-
/** Task to verify that all components work with AOT compilation. */
12-
task('ci:aot', ['aot:build']);
12+
/**
13+
* Task to verify that all components work with AOT compilation. This task requires the
14+
* release output to be built already.
15+
*/
16+
task('ci:aot', ['build-aot:no-release-build']);
1317

1418
/** Task which reports the size of the library and stores it in a database. */
1519
task('ci:payload', ['payload']);
@@ -19,3 +23,9 @@ task('ci:coverage', ['coverage:upload']);
1923

2024
/** Task that verifies if all Material components are working with platform-server. */
2125
task('ci:prerender', ['prerender']);
26+
27+
/** Task that builds all release packages. */
28+
task('ci:build-release-packages', sequenceTask(
29+
'clean',
30+
allBuildPackages.map(buildPackage => `${buildPackage.name}:build-release`)
31+
));

0 commit comments

Comments
 (0)