Skip to content

Commit 185c632

Browse files
Dan Mulleralexeagle
authored andcommitted
perf(cypress): pack cypress runfiles into a single tar
1 parent 799abf5 commit 185c632

File tree

8 files changed

+196
-99
lines changed

8 files changed

+196
-99
lines changed

examples/cypress/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
"scripts": {
1717
"test": "bazel test //..."
1818
}
19-
}
19+
}

packages/cypress/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pkg_npm(
9595
"@build_bazel_rules_nodejs//packages/cypress:internal/plugins/base.js": "TEMPLATED_node_modules_workspace_name//@bazel/cypress:internal/plugins/base.js",
9696
"@build_bazel_rules_nodejs//packages/cypress:internal/plugins/index.template.js": "TEMPLATED_node_modules_workspace_name//@bazel/cypress:internal/plugins/index.template.js",
9797
"@build_bazel_rules_nodejs//packages/cypress:internal/run-cypress.js": "TEMPLATED_node_modules_workspace_name//@bazel/cypress:internal/run-cypress.js",
98+
"TEMPLATED_node_modules_workspace_name//tar": "TEMPLATED_node_modules_workspace_name//@bazel/cypress",
9899
},
99100
deps = [
100101
":npm_version_check",

packages/cypress/internal/install-cypress.js

Lines changed: 62 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,42 @@
99
* repository rule while the file system remains read/write.
1010
*/
1111

12-
const {spawnSync, spawn} = require('child_process');
13-
const {readdirSync, statSync, writeFileSync, mkdirSync} = require('fs');
12+
const {spawnSync} = require('child_process');
13+
const {readdirSync, statSync, writeFileSync, mkdirSync, createWriteStream} = require('fs');
1414
const {
1515
join,
1616
basename,
1717
relative,
1818
dirname,
1919
} = require('path');
20+
2021
const nodePath = process.argv[1];
2122
const cwd = process.cwd();
2223
const cypressBin = process.argv[2];
2324

24-
// Sandboxing doesn't work on windows, so we can just use the global cypress cache.
25-
if (process.platform === 'win32') {
26-
installGlobalCypressCache();
27-
process.exit(0);
28-
}
25+
const nodeModulesPath = join(cypressBin.split('node_modules')[0], 'node_modules');
26+
const tar = require(require.resolve('tar', {
27+
paths: [
28+
join(nodeModulesPath, '@bazel', 'cypress', 'node_modules'),
29+
nodeModulesPath,
30+
]
31+
}));
32+
33+
async function main() {
34+
// Sandboxing doesn't work on windows, so we can just use the global cypress cache.
35+
if (process.platform === 'win32') {
36+
installGlobalCypressCache();
37+
process.exit(0);
38+
}
2939

30-
// Attempt to install the cypress cache within the bazel sandbox and fallback to a global cypress
31-
// cache as a last resort.
32-
try {
33-
installSandboxedCypressCache()
34-
} catch (e) {
35-
console.error('ERROR', e);
36-
installGlobalCypressCache();
40+
// Attempt to install the cypress cache within the bazel sandbox and fallback to a global cypress
41+
// cache as a last resort.
42+
try {
43+
await installSandboxedCypressCache()
44+
} catch (e) {
45+
console.error('ERROR', e);
46+
installGlobalCypressCache();
47+
}
3748
}
3849

3950
function installGlobalCypressCache() {
@@ -70,11 +81,11 @@ cypress_web_test = _cypress_web_test`)
7081
}
7182

7283

73-
function installSandboxedCypressCache() {
74-
mkdirSync(join(cwd, 'cypress-cache'))
84+
async function installSandboxedCypressCache() {
85+
mkdirSync(join(cwd, 'cypress-install'))
7586

7687
const env = {
77-
CYPRESS_CACHE_FOLDER: join(cwd, 'cypress-cache'),
88+
CYPRESS_CACHE_FOLDER: join(cwd, 'cypress-install'),
7889
PATH: `${dirname(nodePath)}:${process.env.PATH}`,
7990
DEBUG: 'cypress:*'
8091
}
@@ -119,19 +130,24 @@ function installSandboxedCypressCache() {
119130
throw new Error(`cypress verify failed`);
120131
}
121132

133+
writeFileSync(join(env.CYPRESS_CACHE_FOLDER, 'bazel_cypress.json'), JSON.stringify({
134+
cypressExecutable: relative(cwd, CYPRESS_RUN_BINARY),
135+
}));
136+
122137
const cacheFiles = [];
123138
walkDir(env.CYPRESS_CACHE_FOLDER, (filePath) => {
124-
cacheFiles.push(filePath);
139+
cacheFiles.push(relative(cwd, filePath));
125140
});
126141

142+
const archiveName = 'cypress.archive';
143+
await createCypressArchive(cacheFiles, join(cwd, archiveName));
144+
127145
writeFileSync('index.bzl', `load(
128146
"//:packages/cypress/internal/cypress_web_test.bzl",
129147
_cypress_web_test = "cypress_web_test",
130148
)
131149
cypress_web_test = _cypress_web_test`)
132-
writeFileSync(
133-
'BUILD.bazel',
134-
`
150+
writeFileSync('BUILD.bazel', `
135151
package(default_visibility = ["//visibility:public"])
136152
137153
exports_files([
@@ -140,62 +156,32 @@ exports_files([
140156
])
141157
142158
filegroup(
143-
name = "cypress_cache",
144-
srcs = ${
145-
process.platform === 'darwin' ?
146-
// On mac we are required to include cache files including spaces. These can only be
147-
// included using a glob.
148-
'glob(["cypress-cache/**/*"]),' :
149-
// On unix the only no files containing spaces are required to run cypress.
150-
` [
151-
${
152-
cacheFiles.filter(f => !f.includes(' '))
153-
.map(f => `"${relative(cwd, f)}"`)
154-
.join(',\n ')}
155-
]`}
156-
)
157-
158-
filegroup(
159-
name = "cypress_executable",
160-
srcs = ["${relative(cwd, CYPRESS_RUN_BINARY)}"]
159+
name = "cypress_archive",
160+
srcs = ["${relative(cwd, archiveName)}"],
161161
)
162162
`.trim())
163+
}
163164

165+
function createCypressArchive(cypressFiles, archiveName) {
166+
return new Promise((resolve, reject) => {
167+
const writeStream = createWriteStream(archiveName);
168+
169+
tar.create(
170+
{
171+
gzip: false,
172+
portable: true,
173+
noMtime: true,
174+
},
175+
cypressFiles)
176+
.pipe(writeStream)
177+
.on('finish', (err) => {
178+
if (err) {
179+
return reject(err);
180+
}
181+
182+
return resolve();
183+
})
184+
});
185+
}
164186

165-
// On mac, the first run of cypress requires write access to the filesystem.
166-
if (process.platform === 'darwin') {
167-
const http = require('http');
168-
const server = http.createServer((_request, response) => {
169-
response.writeHead(200, {'Content-Type': 'text/html'});
170-
response.write('<html><body>hello-world</body></html>\n');
171-
response.end();
172-
})
173-
.listen(0, '127.0.0.1');
174-
server.on('listening', () => {
175-
const baseUrl = `http://127.0.0.1:${server.address().port}`;
176-
writeFileSync(
177-
'cypress.json', JSON.stringify({baseUrl, 'integrationFolder': cwd, video: false}))
178-
writeFileSync('spec.js', `
179-
describe('hello', () => {
180-
it('should find hello', () => {
181-
cy.visit('${baseUrl}');
182-
183-
cy.contains('hello');
184-
});
185-
});
186-
`)
187-
188-
189-
spawn(
190-
`${cypressBin}`, ['run', '--config-file=cypress.json', '--headless', '--spec=spec.js'],
191-
spawnOptions)
192-
.on('exit', (code) => {
193-
server.close();
194-
195-
if (code !== 0) {
196-
throw new Error('Failed to perform a dry-run of cypress')
197-
}
198-
})
199-
})
200-
}
201-
}
187+
main()
Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']);
22
const init = require('cypress/lib/cli').init;
33
const {join} = require('path');
4+
const {readFileSync} = require('fs');
45

5-
const [node, entry, configFilePath, pluginsFilePath, cypressExecutable, ...args] = process.argv;
6-
7-
if (cypressExecutable) {
8-
process.env.CYPRESS_RUN_BINARY =
9-
join(process.cwd(), cypressExecutable.replace('external/', '../'));
10-
process.env.CYPRESS_CACHE_FOLDER =
11-
join(process.env.CYPRESS_RUN_BINARY.split('/cypress-cache/')[0], '/cypress-cache');
12-
process.env.HOME = process.env['TEST_TMPDIR'];
13-
}
6+
const [node, entry, configFilePath, pluginsFilePath, cypressTarPath, cypressBin, ...args] =
7+
process.argv;
148

159
const pluginsFile = runfiles.resolveWorkspaceRelative(pluginsFilePath).replace(process.cwd(), '.');
1610
const configFile = runfiles.resolveWorkspaceRelative(configFilePath).replace(process.cwd(), '.');
1711

18-
function invokeCypressWithCommand(command) {
12+
async function invokeCypressWithCommand(command) {
13+
process.env.HOME = process.env['TEST_TMPDIR'];
14+
15+
if (cypressTarPath) {
16+
const resolvedArchivePath = join(cypressTarPath.replace('external/', '../'));
17+
await untarCypress(resolvedArchivePath, join(process.env['TEST_TMPDIR']))
18+
}
19+
1920
init([
2021
node,
2122
entry,
@@ -28,10 +29,61 @@ function invokeCypressWithCommand(command) {
2829
]);
2930
}
3031

31-
// Detect that we are running as a test, by using well-known environment
32-
// variables. See go/test-encyclopedia
33-
if (!process.env.BUILD_WORKSPACE_DIRECTORY) {
34-
invokeCypressWithCommand('run');
35-
} else {
36-
invokeCypressWithCommand('open');
32+
33+
34+
function untarCypress(cypressTarPath, outputPath) {
35+
return new Promise((resolve, reject) => {
36+
const nodeModulesPath = join(
37+
process.cwd(), cypressBin.replace('external/', '../').split('node_modules')[0],
38+
'node_modules');
39+
40+
const tar = require(require.resolve('tar', {
41+
paths: [
42+
join(nodeModulesPath, '@bazel', 'cypress', 'node_modules'),
43+
nodeModulesPath,
44+
]
45+
}));
46+
47+
48+
tar.x(
49+
{
50+
cwd: outputPath,
51+
file: cypressTarPath,
52+
noMtime: true,
53+
},
54+
err => {
55+
if (err) {
56+
return reject(err);
57+
}
58+
59+
try {
60+
const {cypressExecutable} =
61+
JSON.parse(readFileSync(join(outputPath, 'cypress-install', 'bazel_cypress.json')));
62+
63+
process.env.CYPRESS_RUN_BINARY = join(outputPath, cypressExecutable);
64+
process.env.CYPRESS_CACHE_FOLDER = outputPath;
65+
} catch (err) {
66+
return reject(err)
67+
}
68+
69+
return resolve();
70+
})
71+
});
72+
}
73+
74+
async function main() {
75+
try {
76+
// Detect that we are running as a test, by using well-known environment
77+
// variables. See go/test-encyclopedia
78+
if (!process.env.BUILD_WORKSPACE_DIRECTORY) {
79+
await invokeCypressWithCommand('run');
80+
} else {
81+
await invokeCypressWithCommand('open');
82+
}
83+
} catch (e) {
84+
console.error(e);
85+
process.exit(1)
86+
}
3787
}
88+
89+
main();

packages/cypress/internal/template.cypress_web_test.bzl

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,10 @@ def cypress_web_test(
8080
plugins_file = Label("//plugins/base.js"),
8181
data = [],
8282
templated_args = [],
83-
cypress_cache = Label("//:cypress_cache"),
84-
cypress_executable = Label("//:cypress_executable"),
83+
cypress = Label("TEMPLATED_node_modules_workspace_name//cypress"),
84+
cypress_archive = Label("//:cypress_archive"),
85+
cypress_bin = Label("TEMPLATED_node_modules_workspace_name//:node_modules/cypress/bin/cypress"),
86+
tar = Label("TEMPLATED_node_modules_workspace_name//tar"),
8587
**kwargs):
8688
cypress_plugin = "{name}_cypress_plugin".format(name = name)
8789
tags = kwargs.pop("tags", []) + ["cypress"]
@@ -101,8 +103,10 @@ def cypress_web_test(
101103
tags = tags,
102104
data = data + [
103105
plugins_file,
104-
cypress_cache,
105-
cypress_executable,
106+
cypress_archive,
107+
cypress_bin,
108+
cypress,
109+
tar,
106110
"{cypress_plugin}".format(cypress_plugin = cypress_plugin),
107111
"{config_file}".format(config_file = config_file),
108112
] + srcs,
@@ -111,7 +115,8 @@ def cypress_web_test(
111115
"--nobazel_patch_module_resolver",
112116
"$(rootpath {config_file})".format(config_file = config_file),
113117
"$(rootpath {cypress_plugin})".format(cypress_plugin = cypress_plugin),
114-
"$(rootpath {cypress_executable})".format(cypress_executable = cypress_executable),
118+
"$(rootpath {cypress_archive})".format(cypress_archive = cypress_archive),
119+
"$(rootpath {cypress_bin})".format(cypress_bin = cypress_bin),
115120
] + templated_args,
116121
**kwargs
117122
)

packages/cypress/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"name": "@bazel/cypress",
3+
"dependencies": {
4+
"tar": "6.0.5"
5+
},
36
"peerDependencies": {
47
"cypress": ">=4.7.0"
58
},

packages/cypress/test/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"express": "4.17.1",
66
"cypress": "^5.2.0",
77
"@types/node": "14.0.14",
8-
"typescript": "3.9.5"
8+
"typescript": "3.9.5",
9+
"tar": "6.0.5"
910
}
1011
}

0 commit comments

Comments
 (0)