cloudron 5.11.2 → 5.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cloudron CHANGED
@@ -22,12 +22,6 @@ if (!semver.satisfies(process.version, require('../package.json').engines.node))
22
22
  }
23
23
 
24
24
  const program = new Command();
25
-
26
- function collectArgs(value, collected) {
27
- collected.push(value);
28
- return collected;
29
- }
30
-
31
25
  program.version(version);
32
26
 
33
27
  // global options
@@ -39,6 +33,7 @@ program.option('--server <server>', 'Cloudron domain')
39
33
  // these are separate binaries since global options are not applicable
40
34
  program.command('appstore', 'Cloudron appstore commands');
41
35
  program.command('repo', 'Cloudron repo commands');
36
+ program.command('build', 'Cloudron build commands');
42
37
 
43
38
  const backupCommand = program.command('backup')
44
39
  .description('App backup commands');
@@ -83,21 +78,6 @@ program.command('completion')
83
78
  .description('Shows completion for your shell')
84
79
  .action(completion);
85
80
 
86
- // should probably move to separate binary since globals don't apply
87
- program.command('build')
88
- .description('Build an app')
89
- .option('--build-arg <namevalue>', 'Build arg passed to docker. Can be used multiple times', collectArgs, [])
90
- .option('--build-service-token <token>', 'Build service token')
91
- .option('-f, --file <dockerfile>', 'Name of the Dockerfile')
92
- .option('--set-repository [repository url]', 'Change the repository')
93
- .option('--set-build-service [buildservice url]', 'Set build service app URL')
94
- .option('--local', 'Build docker images locally')
95
- .option('--no-cache', 'Do not use cache')
96
- .option('--no-push', 'Do not push built image to registry')
97
- .option('--raw', 'Raw output build log')
98
- .option('--tag <docker image tag>', 'Docker image tag. Note that this does not include the repository name')
99
- .action(buildActions.build);
100
-
101
81
  program.command('cancel')
102
82
  .description('Cancels any active or pending app task')
103
83
  .option('--app <id/location>', 'App id or location')
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const { program } = require('commander'),
6
+ buildActions = require('../src/build-actions.js');
7
+
8
+ program.version(require('../package.json').version);
9
+
10
+ function collectArgs(value, collected) {
11
+ collected.push(value);
12
+ return collected;
13
+ }
14
+
15
+ // global options
16
+ program.option('--server <server>', 'Cloudron domain')
17
+ .option('--build-service-token <token>', 'Build service token')
18
+ .option('--build-service, --set-build-service [buildservice url]', 'Set build service app URL. This build service is automatically used for future calls from this project');
19
+
20
+ program.command('build', { isDefault: true })
21
+ .description('Build an app. This is the default subcommand')
22
+ .option('--build-arg <namevalue>', 'Build arg passed to docker. Can be used multiple times', collectArgs, [])
23
+ .option('-f, --file <dockerfile>', 'Name of the Dockerfile')
24
+ .option('--set-repository [repository url]', 'Change the repository. This url is stored for future builds for this project. e.g registry/username/projectname')
25
+ .option('--local', 'Build docker images locally')
26
+ .option('--no-cache', 'Do not use cache')
27
+ .option('--no-push', 'Do not push built image to registry')
28
+ .option('--raw', 'Raw output build log')
29
+ .option('--tag <docker image tag>', 'Docker image tag. Note that this does not include the repository name')
30
+ .action(buildActions.build);
31
+
32
+ program.command('login')
33
+ .description('Login to the build service')
34
+ .option('-u, --username <username>', 'Username')
35
+ .option('-p, --password <password>', 'Password (unsafe)')
36
+ .action(buildActions.login);
37
+
38
+ program.command('logs')
39
+ .description('Build logs. This works only when using the Build Service')
40
+ .option('--id <buildid>', 'Build ID')
41
+ .option('--raw', 'Raw output build log')
42
+ .action(buildActions.logs);
43
+
44
+ program.command('push')
45
+ .description('Push the build image')
46
+ .option('--id <buildid>', 'Build ID')
47
+ .option('--repository [repository url]', 'Set repository to push to. e.g registry/username/projectname')
48
+ .option('--tag <docker image tag>', 'Docker image tag. Note that this does not include the repository name')
49
+ .action(buildActions.push);
50
+
51
+ program.command('status')
52
+ .description('Build status. This works only when using the Build Service')
53
+ .option('--id <buildid>', 'Build ID')
54
+ .action(buildActions.status);
55
+
56
+ program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudron",
3
- "version": "5.11.2",
3
+ "version": "5.11.4",
4
4
  "license": "MIT",
5
5
  "description": "Cloudron Commandline Tool",
6
6
  "main": "main.js",
@@ -25,7 +25,6 @@
25
25
  "ejs": "^3.1.10",
26
26
  "eventsource": "^2.0.2",
27
27
  "micromatch": "^4.0.7",
28
- "once": "^1.4.0",
29
28
  "open": "^8.4.0",
30
29
  "progress": "^2.0.3",
31
30
  "progress-stream": "^2.0.0",
@@ -1,7 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  exports = module.exports = {
4
- build
4
+ login,
5
+ build,
6
+ logs,
7
+ status,
8
+ push
5
9
  };
6
10
 
7
11
  const assert = require('assert'),
@@ -11,7 +15,6 @@ const assert = require('assert'),
11
15
  execSync = require('child_process').execSync,
12
16
  exit = require('./helper.js').exit,
13
17
  fs = require('fs'),
14
- once = require('once'),
15
18
  helper = require('./helper.js'),
16
19
  manifestFormat = require('cloudron-manifestformat'),
17
20
  micromatch = require('micromatch'),
@@ -20,78 +23,55 @@ const assert = require('assert'),
20
23
  os = require('os'),
21
24
  path = require('path'),
22
25
  safe = require('safetydance'),
26
+ stream = require('stream/promises'),
23
27
  tar = require('tar-fs'),
24
- url = require('url'),
25
- util = require('util');
26
-
27
- function superagentEndBuildService(requestFactory, callback) {
28
- assert.strictEqual(typeof requestFactory, 'function');
29
- assert.strictEqual(typeof callback, 'function');
30
-
31
- requestFactory().end(function (error, result) {
32
- if (error && !error.response) return callback(error);
33
- if (result.statusCode === 400) return authenticateBuildService({ error: true }, superagentEndBuildService.bind(null, requestFactory, callback));
34
- if (result.statusCode === 401) return authenticateBuildService({ error: true }, superagentEndBuildService.bind(null, requestFactory, callback));
35
- if (result.statusCode === 403) return callback(new Error(result.type === 'application/javascript' ? JSON.stringify(result.body) : result.text));
36
- callback(error, result);
37
- });
28
+ url = require('url');
29
+
30
+ function requestError(response) {
31
+ if (response.statusCode === 401 || response.statusCode === 403) return 'Invalid token. Use cloudron build login again.';
32
+
33
+ return `${response.statusCode} message: ${response.body?.message || null}`;
38
34
  }
39
35
 
40
- function authenticateBuildService(options, callback) {
36
+ async function login(options) {
41
37
  assert.strictEqual(typeof options, 'object');
42
- assert.strictEqual(typeof callback, 'function');
43
38
 
44
- var buildServiceConfig = config.getBuildServiceConfig();
39
+ const buildServiceConfig = config.getBuildServiceConfig();
45
40
 
46
41
  if (!options.hideBanner) console.log('Build Service login' + ` (${buildServiceConfig.url}):`);
47
42
 
48
- var username = options.username || readlineSync.question('Username: ', {});
49
- var password = options.password || readlineSync.question('Password: ', { noEchoBack: true });
43
+ const username = options.username || readlineSync.question('Username: ', {});
44
+ const password = options.password || readlineSync.question('Password: ', { noEchoBack: true });
50
45
 
51
46
  // reset token
52
47
  buildServiceConfig.token = null;
53
48
  config.setBuildServiceConfig(buildServiceConfig);
54
49
 
55
- superagent.post(`${buildServiceConfig.url}/api/v1/login`).send({ username: username, password: password }).end(function (error, result) {
56
- if (error && !error.response) exit(error);
57
-
58
- if (result.statusCode !== 200 && result.statusCode !== 201) {
59
- console.log('Login failed.');
60
-
61
- options.hideBanner = true;
62
- options.username = '';
63
- options.password = '';
50
+ const response = await superagent.post(`${buildServiceConfig.url}/api/v1/login`).send({ username: username, password: password }).ok(() => true);
51
+ if (response.statusCode === 401 || response.statusCode === 403) return exit(`Authentication error: ${requestError(response)}}`);
52
+ if (response.statusCode !== 200 && response.statusCode !== 201) return exit(`Unexpected response: ${requestError(response)}`);
64
53
 
65
- return authenticateBuildService(options, callback);
66
- }
67
-
68
- buildServiceConfig.token = result.body.accessToken;
69
- config.setBuildServiceConfig(buildServiceConfig);
70
-
71
- console.log('Login successful.');
54
+ buildServiceConfig.token = response.body.accessToken;
55
+ config.setBuildServiceConfig(buildServiceConfig);
72
56
 
73
- if (typeof callback === 'function') callback();
74
- });
57
+ console.log('Login successful.');
75
58
  }
76
59
 
77
- function followBuildLog(buildId, raw, callback) {
60
+ async function followBuildLog(buildId, raw) {
78
61
  assert.strictEqual(typeof buildId, 'string');
79
62
  assert.strictEqual(typeof raw, 'boolean');
80
63
 
81
- // ensure callback is only ever called once
82
- callback = once(callback);
83
-
84
64
  // EventSource always requires http
85
65
  let tmp = url.parse(config.getBuildServiceConfig().url);
86
66
  if (tmp.protocol !== 'https:' && tmp.protocol !== 'http:') tmp = url.parse('http://' + config.getBuildServiceConfig().url);
87
67
 
88
- var es = new EventSource(`${tmp.href}api/v1/builds/${buildId}/logstream?accessToken=${config.getBuildServiceConfig().token}`);
89
- var prevId = null, prevWasStatus = false;
68
+ const es = new EventSource(`${tmp.href}api/v1/builds/${buildId}/logstream?accessToken=${config.getBuildServiceConfig().token}`);
69
+ let prevId = null, prevWasStatus = false;
90
70
 
91
71
  es.on('message', function (e) {
92
72
  if (raw) return console.dir(e);
93
73
 
94
- var data = safe.JSON.parse(e.data);
74
+ const data = safe.JSON.parse(e.data);
95
75
  if (!data) return; // this is a bug in docker or our build server
96
76
 
97
77
  if (data.status) { // image push log
@@ -131,25 +111,39 @@ function followBuildLog(buildId, raw, callback) {
131
111
  console.error(data.error);
132
112
  }
133
113
  });
134
- es.on('error', function (error) {
135
- if (raw) console.dir(error);
136
114
 
137
- // We sometimes get { type: 'error' } from es module when the server closes the socket. not clear why
138
- if (error && !error.status && error.type === 'error') error = null;
115
+ let didConnect = false;
116
+ es.once('open', () => didConnect = true);
117
+
118
+ return new Promise((resolve, reject) => {
119
+ es.once('error', function (error) { // server close or network error or some interruption
120
+ if (raw) console.dir(error);
139
121
 
140
- callback(error && error.status ? error : null); // eventsource module really needs to give us better errors
122
+ es.close();
123
+ if (didConnect) resolve(); else reject(new Error('Failed to connect'));
124
+ });
141
125
  });
142
126
  }
143
127
 
128
+ async function getStatus(buildId) {
129
+ const buildServiceConfig = config.getBuildServiceConfig();
130
+
131
+ const response2 = await superagent.get(`${buildServiceConfig.url}/api/v1/builds/${buildId}`)
132
+ .query({ accessToken: buildServiceConfig.token })
133
+ .ok(() => true);
134
+ if (response2.statusCode !== 200) throw new Error(`Failed to get status: ${requestError(response2)}`);
135
+ return response2.body.status;
136
+ }
137
+
144
138
  function dockerignoreMatcher(dockerignorePath) {
145
- var patterns = [];
139
+ let patterns = [];
146
140
 
147
141
  if (fs.existsSync(dockerignorePath)) {
148
142
  patterns = fs.readFileSync(dockerignorePath, 'utf8').split('\n');
149
143
 
150
144
  patterns = patterns.filter(function (line) { return line[0] !== '#'; });
151
145
  patterns = patterns.map(function (line) {
152
- var l = line.trim();
146
+ let l = line.trim();
153
147
 
154
148
  while (l[0] === '/') l = l.slice(1);
155
149
  while (l[l.length-1] === '/') l = l.slice(0, -1);
@@ -164,7 +158,7 @@ function dockerignoreMatcher(dockerignorePath) {
164
158
  };
165
159
  }
166
160
 
167
- function buildLocal(manifest, sourceDir, appConfig, options) {
161
+ async function buildLocal(manifest, sourceDir, appConfig, options) {
168
162
  let tag;
169
163
  if (options.tag) {
170
164
  tag = options.tag;
@@ -180,7 +174,7 @@ function buildLocal(manifest, sourceDir, appConfig, options) {
180
174
  console.log('Building locally as %s', dockerImage);
181
175
  console.log();
182
176
 
183
- let buildArgsCmdLine = options.buildArg.map(function (a) { return `--build-arg "${a}"`; }).join(' ');
177
+ const buildArgsCmdLine = options.buildArg.map(function (a) { return `--build-arg "${a}"`; }).join(' ');
184
178
 
185
179
  let dockerfile = 'Dockerfile';
186
180
  if (options.file) dockerfile = options.file;
@@ -196,9 +190,9 @@ function buildLocal(manifest, sourceDir, appConfig, options) {
196
190
  if (safe.error) exit('Failed to push image (are you logged in? if not, use "docker login")');
197
191
  }
198
192
 
199
- let result = safe.child_process.execSync(`docker inspect --format="{{index .RepoDigests 0}}" ${dockerImage}`, { encoding: 'utf8' });
193
+ const result = safe.child_process.execSync(`docker inspect --format="{{index .RepoDigests 0}}" ${dockerImage}`, { encoding: 'utf8' });
200
194
  if (safe.error) exit('Failed to inspect image');
201
- let match = /.*@sha256:(.*)/.exec(result.trim());
195
+ const match = /.*@sha256:(.*)/.exec(result.trim());
202
196
  if (!match) exit('Failed to detect sha256');
203
197
 
204
198
  appConfig.dockerImage = `${dockerImage}`;
@@ -206,7 +200,7 @@ function buildLocal(manifest, sourceDir, appConfig, options) {
206
200
  config.setAppConfig(sourceDir, appConfig);
207
201
  }
208
202
 
209
- function buildRemote(manifest, sourceDir, appConfig, options) {
203
+ async function buildRemote(manifest, sourceDir, appConfig, options) {
210
204
  console.log('Using build service', config.getBuildServiceConfig().url);
211
205
 
212
206
  let tag;
@@ -223,21 +217,21 @@ function buildRemote(manifest, sourceDir, appConfig, options) {
223
217
 
224
218
  console.log('Building %s', dockerImage);
225
219
 
226
- var sourceArchiveFilePath = path.join(os.tmpdir(), path.basename(sourceDir) + '.tar.gz');
227
- var dockerignoreFilePath = path.join(sourceDir, '.dockerignore');
228
- var ignoreMatcher = dockerignoreMatcher(dockerignoreFilePath);
220
+ const sourceArchiveFilePath = path.join(os.tmpdir(), path.basename(sourceDir) + '.tar.gz');
221
+ const dockerignoreFilePath = path.join(sourceDir, '.dockerignore');
222
+ const ignoreMatcher = dockerignoreMatcher(dockerignoreFilePath);
229
223
 
230
224
  console.log('Uploading source tarball...');
231
225
 
232
- var stream = tar.pack(sourceDir, {
226
+ const tarStream = tar.pack(sourceDir, {
233
227
  ignore: function (name) {
234
228
  return ignoreMatcher(name.slice(sourceDir.length + 1)); // make name as relative path
235
229
  }
236
- }).pipe(fs.createWriteStream(sourceArchiveFilePath));
237
-
238
- stream.on('error', function (error) {
239
- exit('Failed to create application source archive: ' + error);
240
230
  });
231
+ const sourceArchiveStream = fs.createWriteStream(sourceArchiveFilePath);
232
+
233
+ const [tarError] = await safe(stream.pipeline(tarStream, sourceArchiveStream));
234
+ if (tarError) return exit(`Could not tar: ${tarError.message}`);
241
235
 
242
236
  let dockerfile = 'Dockerfile';
243
237
  if (options.file) dockerfile = options.file;
@@ -251,49 +245,38 @@ function buildRemote(manifest, sourceDir, appConfig, options) {
251
245
  buildArgsObject[key] = value;
252
246
  });
253
247
 
254
- stream.on('finish', function () {
255
- superagentEndBuildService(function () {
256
- let buildServiceConfig = config.getBuildServiceConfig();
257
- return superagent.post(`${buildServiceConfig.url}/api/v1/builds`)
258
- .query({ accessToken: buildServiceConfig.token, noCache: !options.cache, dockerfile: dockerfile, noPush: !options.push })
259
- .field('dockerImageRepo', appConfig.repository)
260
- .field('dockerImageTag', tag)
261
- .field('buildArgs', JSON.stringify(buildArgsObject))
262
- .attach('sourceArchive', sourceArchiveFilePath);
263
- }, function (error, result) {
264
- if (error && !error.response) return exit(util.format('Failed to build app: %s', error.message));
265
- if (result.statusCode === 413) exit('Failed to build app. The app source is too large.\nPlease adjust your .dockerignore file to only include neccessary files.');
266
- if (result.statusCode !== 201) exit(util.format('Failed to build app (statusCode %s): \n%s', result.statusCode, result.body && result.body.message ? result.body.message : result.text));
267
-
268
- var buildId = result.body.id;
269
-
270
- followBuildLog(buildId, false, function (error) {
271
- if (error) return console.error(error);
272
-
273
- superagentEndBuildService(function () {
274
- let buildServiceConfig = config.getBuildServiceConfig();
275
- return superagent.get(`${buildServiceConfig.url}/api/v1/builds/${buildId}`)
276
- .query({ accessToken: buildServiceConfig.token });
277
- }, function (error, result) {
278
- if (error && !error.response) return exit(util.format('Failed to build app: %s', error.message));
279
- if (result.statusCode !== 200) exit(util.format('Failed to build app (statusCode %s): \n%s', result.statusCode, result.body && result.body.message ? result.body.message : result.text));
280
- if (result.body.status !== 'success') exit('Failed to build app. See log output above.');
281
-
282
- appConfig.dockerImage = dockerImage;
283
- // appConfig.dockerImageSha256 = match[1]; // stash this separately for now
284
- config.setAppConfig(sourceDir, appConfig);
285
-
286
- console.log(dockerImage);
287
- console.log('\nBuild successful');
288
-
289
- exit();
290
- });
291
- });
292
- });
293
- });
248
+ const buildServiceConfig = config.getBuildServiceConfig();
249
+ const response = await superagent.post(`${buildServiceConfig.url}/api/v1/builds`)
250
+ .query({ accessToken: buildServiceConfig.token, noCache: !options.cache, dockerfile: dockerfile, noPush: !options.push })
251
+ .field('dockerImageRepo', appConfig.repository)
252
+ .field('dockerImageTag', tag)
253
+ .field('buildArgs', JSON.stringify(buildArgsObject))
254
+ .attach('sourceArchive', sourceArchiveFilePath)
255
+ .ok(() => true);
256
+ if (response.statusCode === 413) return exit('Failed to build app. The app source is too large.\nPlease adjust your .dockerignore file to only include neccessary files.');
257
+ if (response.statusCode !== 201) return exit(`Failed to upload app for building: ${requestError(response)}`);
258
+
259
+ const buildId = response.body.id;
260
+ console.log(`BuildId: ${buildId}`);
261
+
262
+ const [logsError] = await safe(followBuildLog(buildId, !!options.raw));
263
+ if (logsError) console.log(`Failed to get logs: ${logsError.message}`);
264
+
265
+ const [statusError, status] = await safe(getStatus(buildId));
266
+ if (statusError) return exit(`Failed to get status: ${statusError.message}`);
267
+ if (status !== 'success') return exit('Failed to build app. See log output above.');
268
+
269
+ appConfig.dockerImage = dockerImage;
270
+ // appConfig.dockerImageSha256 = match[1]; // stash this separately for now
271
+ config.setAppConfig(sourceDir, appConfig);
272
+
273
+ console.log(`Docker image: ${dockerImage}`);
274
+ console.log('\nBuild successful');
275
+
276
+ exit();
294
277
  }
295
278
 
296
- function build(options) {
279
+ async function build(options) {
297
280
  // try to find the manifest of this project
298
281
  const manifestFilePath = helper.locateManifest();
299
282
  if (!manifestFilePath) return exit('No CloudronManifest.json found');
@@ -301,12 +284,12 @@ function build(options) {
301
284
  const result = manifestFormat.parseFile(manifestFilePath);
302
285
  if (result.error) return exit('Error in CloudronManifest.json: ' + result.error.message);
303
286
 
304
- let manifest = result.manifest;
287
+ const manifest = result.manifest;
305
288
  const sourceDir = path.dirname(manifestFilePath);
306
289
 
307
290
  const appConfig = config.getAppConfig(sourceDir);
308
291
 
309
- let buildService = config.getBuildServiceConfig();
292
+ const buildService = config.getBuildServiceConfig();
310
293
  if (!buildService.type) buildService.type = 'local'; // default
311
294
 
312
295
  if (options.local) {
@@ -346,10 +329,46 @@ function build(options) {
346
329
  }
347
330
 
348
331
  if (buildService.type === 'local') {
349
- buildLocal(manifest, sourceDir, appConfig, options);
332
+ await buildLocal(manifest, sourceDir, appConfig, options);
350
333
  } else if (buildService.type === 'remote' && buildService.url) {
351
- buildRemote(manifest, sourceDir, appConfig, options);
334
+ await buildRemote(manifest, sourceDir, appConfig, options);
352
335
  } else {
353
336
  exit('Unknown build service type or missing build service url. Rerun with --reset-build-service');
354
337
  }
355
338
  }
339
+
340
+ async function logs(options) {
341
+ if (!options.id) return exit('buildId is required');
342
+
343
+ const [logsError] = await safe(followBuildLog(options.id, !!options.raw));
344
+ if (logsError) console.log(`Failed to get logs: ${logsError.message}`);
345
+ }
346
+
347
+ async function status(options) {
348
+ if (!options.id) return exit('buildId is required');
349
+
350
+ const [statusError, status] = await safe(getStatus(options.id));
351
+ if (statusError) return exit(`Failed to get status: ${statusError.message}`);
352
+ console.log(status);
353
+ }
354
+
355
+ async function push(options) {
356
+ if (!options.id) return exit('buildId is required');
357
+ if (!options.repository) return exit('repository is required');
358
+ if (!options.tag) return exit('tag is required');
359
+
360
+ const buildServiceConfig = config.getBuildServiceConfig();
361
+
362
+ const response = await superagent.post(`${buildServiceConfig.url}/api/v1/builds/${options.id}/push`)
363
+ .query({ accessToken: buildServiceConfig.token })
364
+ .send({ dockerImageRepo: options.repository, dockerImageTag: options.tag })
365
+ .ok(() => true);
366
+ if (response.statusCode !== 201) return exit(`Failed to push: ${requestError(response)}`);
367
+
368
+ const [logsError] = await safe(followBuildLog(options.id, !!options.raw));
369
+ if (logsError) console.log(`Failed to get logs: ${logsError.message}`);
370
+
371
+ const [statusError, status] = await safe(getStatus(options.id));
372
+ if (statusError) return exit(`Failed to get status: ${statusError.message}`);
373
+ if (status !== 'success') return exit('Failed to push app. See log output above.');
374
+ }
@@ -66,15 +66,15 @@ async function tag(version, options) {
66
66
  manifest.upstreamVersion = upstreamVersion;
67
67
  fs.writeFileSync('CloudronManifest.json', JSON.stringify(manifest, null, 2));
68
68
 
69
- // set GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL if needed
70
- const rawBranch = safe.child_process.execSync('git branch --show-current', { encoding: 'utf8' }); // master or main
69
+ // git branch --show-current does not work in CI :/
70
+ const mainOrMaster = safe.child_process.execSync('git branch -r --list origin/master origin/main', { encoding: 'utf8' });
71
71
  if (safe.error) return exit('Could not determine branch name');
72
- const branch = rawBranch.trim();
72
+ const branch = mainOrMaster.includes('master') ? 'master' : 'main';
73
73
 
74
74
  execSync(`git commit -a -m 'Version ${version}'`, { encoding: 'utf8' });
75
75
  execSync(`git tag v${version} -a -m 'Version ${version}'`, { encoding: 'utf8' });
76
76
  console.log(`git push --atomic origin ${branch} v${version}`);
77
- execSync(`git push --atomic origin ${branch} v${version}`, { encoding: 'utf8' }); // push this tag only. in CI, we might have a git cache
77
+ execSync(`git push --atomic origin HEAD:${branch} v${version}`, { encoding: 'utf8' }); // push this tag only. in CI, we might have a git cache
78
78
  if (safe.error) return exit(`Failed to push tag v${version} and branch ${branch}: ${safe.error.message}`, { encoding: 'utf8' });
79
79
 
80
80
  console.log(`Created tag v${version} and pushed branch ${branch}`);