Skip to content

Commit 32bcc88

Browse files
devversionjelbourn
authored andcommitted
build: share release output between circle jobs (#13838)
Also support parallel Karma instances on CircleCI
1 parent 73a9417 commit 32bcc88

File tree

7 files changed

+194
-47
lines changed

7 files changed

+194
-47
lines changed

.circleci/config.yml

Lines changed: 82 additions & 9 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,7 +123,50 @@ jobs:
123123

124124
- run: yarn gulp ci:e2e
125125

126-
- *save_cache
126+
# ----------------------------------------------------------------------------
127+
# Job that runs the unit tests on Browserstack. The browsers that will be used
128+
# to run the unit tests on Browserstack are set in: test/browser-providers.js
129+
# ----------------------------------------------------------------------------
130+
tests_browserstack:
131+
<<: *job_defaults
132+
resource_class: xlarge
133+
environment:
134+
BROWSER_STACK_USERNAME: "angularteam1"
135+
BROWSER_STACK_ACCESS_KEY: "CaXMeMHD9pr5PHg8N7Jq"
136+
parallelism: 2
137+
steps:
138+
- *checkout_code
139+
- *restore_cache
140+
- *yarn_install
141+
142+
- run: ./scripts/circleci/run-browserstack-tests.sh
143+
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:
149+
<<: *job_defaults
150+
steps:
151+
- *checkout_code
152+
- *restore_cache
153+
- *yarn_install
154+
- *attach_release_output
155+
156+
- run: yarn gulp ci:aot
157+
158+
# -------------------------------------------------------------------------
159+
# Job that pre-render's the universal app with `@angular/platform-server`.
160+
# This verifies that Angular Material can be rendered within Node.
161+
# -------------------------------------------------------------------------
162+
prerender_build:
163+
<<: *job_defaults
164+
steps:
165+
- *checkout_code
166+
- *restore_cache
167+
- *yarn_install
168+
169+
- run: yarn gulp ci:prerender
127170

128171
# ----------------------------------
129172
# Lint job. Runs the gulp lint task.
@@ -137,6 +180,27 @@ jobs:
137180

138181
- run: yarn gulp ci:lint
139182

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+
140204
- *save_cache
141205

142206
# ----------------------------------------------------------------------------------------
@@ -158,10 +222,19 @@ workflows:
158222
unit_tests:
159223
jobs:
160224
- tests_local_browsers
225+
- tests_browserstack
161226

162227
integration_tests:
163228
jobs:
164229
- e2e_tests
230+
- prerender_build
231+
232+
release_output:
233+
jobs:
234+
- build_release_packages
235+
- build_demoapp_aot:
236+
requires:
237+
- build_release_packages
165238

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

scripts/browserstack/start-tunnel.sh

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,48 @@
22

33
set -e -o pipefail
44

5-
# Workaround for Travis CI cookbook https://github.com/travis-ci/travis-ci/issues/4862,
6-
# where $PATH will be extended with relative paths to the NPM binaries.
7-
PATH=`echo ${PATH} | sed -e 's/:\.\/node_modules\/\.bin//'`
5+
tunnelFileName="BrowserStackLocal-linux-x64.zip"
6+
tunnelUrl="https://www.browserstack.com/browserstack-local/${tunnelFileName}"
87

9-
TUNNEL_FILE="BrowserStackLocal-linux-x64.zip"
10-
TUNNEL_URL="https://www.browserstack.com/browserstack-local/${TUNNEL_FILE}"
11-
TUNNEL_DIR="/tmp/browserstack-tunnel"
12-
TUNNEL_LOG="${LOGS_DIR}/browserstack-tunnel.log"
13-
14-
BROWSER_STACK_ACCESS_KEY=`echo ${BROWSER_STACK_ACCESS_KEY} | rev`
8+
tunnelTmpDir="/tmp/material-browserstack"
9+
tunnelLogFile="${tunnelTmpDir}/browserstack-local.log"
10+
tunnelReadyFile="${tunnelTmpDir}/readyfile"
11+
tunnelErrorFile="${tunnelTmpDir}/errorfile"
1512

1613
# Cleanup and create the folder structure for the tunnel connector.
17-
rm -rf ${TUNNEL_DIR} ${BROWSER_PROVIDER_READY_FILE}
18-
mkdir -p ${TUNNEL_DIR}
19-
touch ${TUNNEL_LOG}
14+
rm -rf ${tunnelTmpDir} ${tunnelReadyFile} ${tunnelErrorFile}
15+
mkdir -p ${tunnelTmpDir}
16+
touch ${tunnelLogFile}
2017

21-
cd ${TUNNEL_DIR}
18+
# Go into temporary tunnel directory.
19+
cd ${tunnelTmpDir}
2220

2321
# Download the browserstack local binaries.
24-
curl ${TUNNEL_URL} -o ${TUNNEL_FILE} 2> /dev/null 1> /dev/null
22+
curl ${tunnelUrl} -o ${tunnelFileName} 2> /dev/null 1> /dev/null
2523

2624
# Extract the browserstack local binaries from the tarball.
2725
mkdir -p browserstack-tunnel
28-
unzip -q ${TUNNEL_FILE} -d browserstack-tunnel
26+
unzip -q ${tunnelFileName} -d browserstack-tunnel
2927

30-
# Cleanup the download directory.
31-
rm ${TUNNEL_FILE}
28+
# Cleanup the downloaded zip archive.
29+
rm ${tunnelFileName}
3230

3331
ARGS=""
3432

35-
# Set tunnel-id only on Travis, to make local testing easier.
36-
if [ ! -z "${TRAVIS_JOB_ID}" ]; then
37-
ARGS="${ARGS} --local-identifier ${TRAVIS_JOB_ID}"
33+
if [ ! -z "${CIRCLE_BUILD_NUM}" ]; then
34+
ARGS="${ARGS} --local-identifier ${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}"
3835
fi
3936

40-
echo "Starting Browserstack Local in the background, logging into: ${TUNNEL_LOG}"
37+
echo "Starting Browserstack Local in the background, logging into: ${tunnelLogFile}"
4138

4239
# Extension to the BrowserStackLocal binaries, because those can't create a readyfile.
4340
function create_ready_file {
41+
# Process ID for the BrowserStack local asynchronous instance.
42+
tunnelProcessPid=${1}
4443

4544
# To be able to exit the tail properly we need to have a sub shell spawned, which is
4645
# used to track the state of tail.
47-
{ sleep 120; touch ${BROWSER_PROVIDER_ERROR_FILE}; } &
46+
{ sleep 120; touch ${tunnelErrorFile}; } &
4847

4948
TIMER_PID=${!}
5049

@@ -54,16 +53,18 @@ function create_ready_file {
5453

5554
# When the tail recognizes the `Ctrl-C` log message the BrowserStack Tunnel is up.
5655
{
57-
tail -n0 -f ${TUNNEL_LOG} --pid ${TIMER_PID} | { sed '/Ctrl/q' && kill -9 ${TIMER_PID}; };
56+
tail -n0 -f ${tunnelLogFile} --pid ${TIMER_PID} | { sed '/Ctrl/q' && kill -9 ${TIMER_PID}; };
5857
} &> /dev/null
5958

6059
echo
6160
echo "BrowserStack Tunnel ready"
6261

63-
touch ${BROWSER_PROVIDER_READY_FILE}
62+
# Create the readyfile and write the PID for BrowserStack Local into it.
63+
echo ${tunnelProcessPid} > ${tunnelReadyFile}
6464
}
6565

66-
browserstack-tunnel/BrowserStackLocal -k ${BROWSER_STACK_ACCESS_KEY} ${ARGS} 2>&1 >> ${TUNNEL_LOG} &
66+
browserstack-tunnel/BrowserStackLocal -k ${BROWSER_STACK_ACCESS_KEY} ${ARGS} 2>&1 >> \
67+
${tunnelLogFile} &
6768

6869
# Wait for the tunnel to be ready and create the readyfile with the Browserstack PID
69-
create_ready_file &
70+
create_ready_file ${!} &

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: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,24 @@ module.exports = (config) => {
101101
});
102102

103103
if (process.env['CIRCLECI']) {
104-
config.browsers = platformMap[process.env['TEST_PLATFORM']];
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}`;
107+
const buildIdentifier = `angular-material-${tunnelIdentifier}`;
108+
const testPlatform = process.env['TEST_PLATFORM'];
109+
110+
if (testPlatform === 'browserstack') {
111+
config.browserStack.build = buildIdentifier;
112+
config.browserStack.tunnelIdentifier = tunnelIdentifier;
113+
}
114+
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];
105122
}
106123

107124
if (process.env['TRAVIS']) {
@@ -140,3 +157,21 @@ module.exports = (config) => {
140157
config.browsers = platformMap[platform];
141158
}
142159
};
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',
@@ -23,15 +39,15 @@ task('aot:deps', sequenceTask(
2339
'build-examples-module',
2440
'material-examples:build-release',
2541
],
26-
// Build the assets after the releases have been built, because the demo-app assets import
27-
// SCSS files from the release packages.
28-
[':build:devapp:assets', ':build:devapp:scss'],
2942
));
3043

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

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

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)