@tolgee/cli 2.15.1 → 2.16.0

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/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import ansi from 'ansi-colors';
13
13
  import { getApiKey, savePak, savePat } from './config/credentials.js';
14
14
  import loadTolgeeRc from './config/tolgeerc.js';
15
15
  import { setDebug, info, error, exitWithError } from './utils/logger.js';
16
- import { API_KEY_OPT, API_URL_OPT, CONFIG_OPT, DEFAULT_NAMESPACE, EXTRACTOR, FILE_PATTERNS, FORMAT_OPT, STRICT_NAMESPACE, PARSER, PROJECT_ID_OPT, STRICT_NAMESPACE_NEGATION, VERBOSE, } from './options.js';
16
+ import { API_KEY_OPT, API_URL_OPT, CONFIG_OPT, DEFAULT_NAMESPACE, EXTRACTOR, FILE_PATTERNS, FORMAT_OPT, STRICT_NAMESPACE, PARSER, PROJECT_ID_OPT, PROJECT_BRANCH, STRICT_NAMESPACE_NEGATION, VERBOSE, } from './options.js';
17
17
  import { API_KEY_PAK_PREFIX, API_KEY_PAT_PREFIX, DEFAULT_API_URL, VERSION, } from './constants.js';
18
18
  import { Login, Logout } from './commands/login.js';
19
19
  import PushCommand from './commands/push.js';
@@ -22,6 +22,8 @@ import ExtractCommand from './commands/extract.js';
22
22
  import CompareCommand from './commands/sync/compare.js';
23
23
  import SyncCommand from './commands/sync/sync.js';
24
24
  import TagCommand from './commands/tag.js';
25
+ import BranchCommand from './commands/branch.js';
26
+ import MergeCommand from './commands/merge.js';
25
27
  import { getSingleOption } from './utils/getSingleOption.js';
26
28
  import { createTolgeeClient } from './client/TolgeeClient.js';
27
29
  import { projectIdFromKey } from './client/ApiClient.js';
@@ -136,6 +138,7 @@ function run() {
136
138
  program.addOption(API_URL_OPT.default((_a = config.apiUrl) !== null && _a !== void 0 ? _a : DEFAULT_API_URL));
137
139
  program.addOption(API_KEY_OPT.default(config.apiKey));
138
140
  program.addOption(PROJECT_ID_OPT.default((_b = config.projectId) !== null && _b !== void 0 ? _b : -1));
141
+ program.addOption(PROJECT_BRANCH.default(config.branch));
139
142
  program.addOption(FORMAT_OPT.default((_c = config.format) !== null && _c !== void 0 ? _c : 'JSON_TOLGEE'));
140
143
  program.addOption(EXTRACTOR.default(config.extractor));
141
144
  program.addOption(FILE_PATTERNS.default(config.patterns));
@@ -152,6 +155,8 @@ function run() {
152
155
  program.addCommand(CompareCommand(config).configureHelp({ showGlobalOptions: true }));
153
156
  program.addCommand(SyncCommand(config).configureHelp({ showGlobalOptions: true }));
154
157
  program.addCommand(TagCommand(config).configureHelp({ showGlobalOptions: true }));
158
+ program.addCommand(BranchCommand(config).configureHelp({ showGlobalOptions: true }));
159
+ program.addCommand(MergeCommand(config).configureHelp({ showGlobalOptions: true }));
155
160
  yield program.parseAsync();
156
161
  }
157
162
  catch (e) {
@@ -27,6 +27,9 @@ export const errorFromLoadable = (loadable) => {
27
27
  // Forbidden
28
28
  case 403:
29
29
  return `You are not allowed to perform this operation ${addErrorDetails(loadable)}`;
30
+ // Not found (e.g. branch in project)
31
+ case 404:
32
+ return `Requested data not found. Please check your inputs ${addErrorDetails(loadable)}`;
30
33
  // Rate limited
31
34
  case 429:
32
35
  return `You've been rate limited. Please try again later ${addErrorDetails(loadable)}`;
@@ -0,0 +1,94 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Command, Option } from 'commander';
11
+ import { handleLoadableError } from '../client/TolgeeClient.js';
12
+ import { fetchBranches, listBranches } from '../utils/branch.js';
13
+ import { error, exitWithError, loading, success } from '../utils/logger.js';
14
+ function resolveTargetNames(opts, createArg) {
15
+ var _a;
16
+ const createName = (_a = opts.create) !== null && _a !== void 0 ? _a : createArg;
17
+ const deleteName = opts.delete;
18
+ if (opts.create && createArg) {
19
+ exitWithError("error: use either the '[branch]' arg to create branch or the option '-c, --create <branch>'");
20
+ }
21
+ if (createArg && deleteName) {
22
+ exitWithError("error: '[branch]' arg to create branch cannot be used together with option '-d, --delete <branch>'");
23
+ }
24
+ // opts.create && opts.delete use is already sanitized by commander
25
+ return { createName, deleteName };
26
+ }
27
+ function resolveOriginId(opts, branches) {
28
+ return __awaiter(this, void 0, void 0, function* () {
29
+ const originName = opts.origin;
30
+ if (originName) {
31
+ const origin = branches.find((b) => b.name === originName);
32
+ if (!origin) {
33
+ error(`Origin branch "${originName}" was not found.`);
34
+ listBranches(branches);
35
+ exitWithError('Use --origin <branch> to specify an existing base branch.');
36
+ }
37
+ return origin.id;
38
+ }
39
+ const defaultBranch = branches.find((b) => b.isDefault);
40
+ if (!defaultBranch) {
41
+ error('Cannot determine default branch for the project.');
42
+ listBranches(branches);
43
+ exitWithError('Specify --origin <branch>.');
44
+ }
45
+ return defaultBranch.id;
46
+ });
47
+ }
48
+ const branchHandler = (config) => function () {
49
+ return __awaiter(this, void 0, void 0, function* () {
50
+ const opts = this.optsWithGlobals();
51
+ const createArg = this.processedArgs[0];
52
+ const { createName, deleteName } = resolveTargetNames(opts, createArg);
53
+ const branches = yield loading('Fetching project branches...', fetchBranches(opts));
54
+ if (!createName && !deleteName) {
55
+ listBranches(branches);
56
+ return;
57
+ }
58
+ if (createName) {
59
+ const originId = yield resolveOriginId(opts, branches);
60
+ const loadable = yield loading(`Creating branch "${createName}"...`, opts.client.POST('/v2/projects/{projectId}/branches', {
61
+ params: { path: { projectId: opts.client.getProjectId() } },
62
+ body: { name: createName, originBranchId: originId },
63
+ }));
64
+ handleLoadableError(loadable);
65
+ success(`Branch "${createName}" created.`);
66
+ return;
67
+ }
68
+ if (deleteName) {
69
+ const target = branches.find((b) => b.name === deleteName);
70
+ if (!target) {
71
+ error(`Branch "${deleteName}" was not found.`);
72
+ listBranches(branches);
73
+ exitWithError('Specify an existing branch.');
74
+ }
75
+ const loadable = yield loading(`Deleting branch "${deleteName}"...`, opts.client.DELETE('/v2/projects/{projectId}/branches/{branchId}', {
76
+ params: {
77
+ path: {
78
+ projectId: opts.client.getProjectId(),
79
+ branchId: target.id,
80
+ },
81
+ },
82
+ }));
83
+ handleLoadableError(loadable);
84
+ success(`Branch "${deleteName}" deleted.`);
85
+ }
86
+ });
87
+ };
88
+ export default (config) => new Command('branch')
89
+ .description('Create or delete project branches')
90
+ .argument('[branch]', 'Branch name to create')
91
+ .addOption(new Option('--create <branch>', 'create a new branch').conflicts('delete'))
92
+ .addOption(new Option('-d, --delete <branch>', 'delete an existing branch').conflicts('create'))
93
+ .addOption(new Option('-o, --origin <branch>', 'origin branch to fork from (defaults to project default branch)'))
94
+ .action(branchHandler(config));
@@ -0,0 +1,118 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Command } from 'commander';
11
+ import { handleLoadableError } from '../client/TolgeeClient.js';
12
+ import { fetchBranches, listBranches } from '../utils/branch.js';
13
+ import { error, exitWithError, info, loading, success, } from '../utils/logger.js';
14
+ function resolveBranchName(opts, branchArg, branchExplicit) {
15
+ if (branchArg && branchExplicit) {
16
+ exitWithError("error: use either the '[branch]' arg or the option '--branch <branch>'");
17
+ }
18
+ const branchName = branchArg !== null && branchArg !== void 0 ? branchArg : (branchExplicit ? opts.branch : undefined);
19
+ if (!branchName) {
20
+ exitWithError('Specify a branch to merge.');
21
+ }
22
+ return branchName;
23
+ }
24
+ function renderChangeType(change) {
25
+ switch (change.type) {
26
+ case 'ADD':
27
+ return '+';
28
+ case 'UPDATE':
29
+ return '~';
30
+ case 'DELETE':
31
+ return '-';
32
+ case 'CONFLICT':
33
+ return 'x';
34
+ }
35
+ }
36
+ function renderChange(change) {
37
+ var _a, _b, _c, _d, _e;
38
+ const keyName = (_d = (_b = (_a = change.sourceKey) === null || _a === void 0 ? void 0 : _a.keyName) !== null && _b !== void 0 ? _b : (_c = change.targetKey) === null || _c === void 0 ? void 0 : _c.keyName) !== null && _d !== void 0 ? _d : 'unknown';
39
+ const languages = ((_e = change.changedTranslations) === null || _e === void 0 ? void 0 : _e.length)
40
+ ? ` (${change.changedTranslations.join(', ')})`
41
+ : '';
42
+ return `${renderChangeType(change)} ${keyName}${languages}`;
43
+ }
44
+ function buildMergeUrl(opts, mergeId) {
45
+ return new URL(`/projects/${opts.projectId}/branches/merge/${mergeId}`, opts.apiUrl // API and frontend URLs may differ, but commonly they are the same
46
+ ).toString();
47
+ }
48
+ const mergeHandler = (config) => function () {
49
+ return __awaiter(this, void 0, void 0, function* () {
50
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
51
+ const opts = this.optsWithGlobals();
52
+ const branchArg = this.processedArgs[0];
53
+ const branchExplicit = ((_a = this.parent) === null || _a === void 0 ? void 0 : _a.getOptionValueSource('branch')) === 'cli';
54
+ const branchName = resolveBranchName(opts, branchArg, branchExplicit);
55
+ const branches = yield loading('Fetching project branches...', fetchBranches(opts));
56
+ const mergedBranch = branches.find((branch) => branch.name === branchName);
57
+ if (!mergedBranch) {
58
+ error(`Branch "${branchName}" was not found.`);
59
+ listBranches(branches);
60
+ exitWithError('Specify an existing branch to merge.');
61
+ }
62
+ if (mergedBranch.isDefault) {
63
+ exitWithError('Cannot merge the default branch.');
64
+ }
65
+ const previewLoadable = yield loading(`Preparing merge of "${branchName}"...`, opts.client.POST('/v2/projects/{projectId}/branches/merge/preview', {
66
+ params: { path: { projectId: opts.client.getProjectId() } },
67
+ body: { sourceBranchId: mergedBranch.id },
68
+ }));
69
+ handleLoadableError(previewLoadable);
70
+ const mergeRef = previewLoadable.data;
71
+ const refreshLoadable = yield loading(`Refreshing merge changes...`, opts.client.POST('/v2/projects/{projectId}/branches/merge/{mergeId}/refresh', {
72
+ params: {
73
+ path: {
74
+ projectId: opts.client.getProjectId(),
75
+ mergeId: mergeRef.id,
76
+ },
77
+ },
78
+ }));
79
+ handleLoadableError(refreshLoadable);
80
+ const changesLoadable = yield loading(`Checking merge changes...`, opts.client.GET('/v2/projects/{projectId}/branches/merge/{mergeId}/changes', {
81
+ params: {
82
+ path: {
83
+ projectId: opts.client.getProjectId(),
84
+ mergeId: mergeRef.id,
85
+ },
86
+ },
87
+ }));
88
+ handleLoadableError(changesLoadable);
89
+ const changes = (_d = (_c = (_b = changesLoadable.data) === null || _b === void 0 ? void 0 : _b._embedded) === null || _c === void 0 ? void 0 : _c.branchMergeChanges) !== null && _d !== void 0 ? _d : [];
90
+ const totalChanges = (_g = (_f = (_e = changesLoadable.data) === null || _e === void 0 ? void 0 : _e.page) === null || _f === void 0 ? void 0 : _f.totalElements) !== null && _g !== void 0 ? _g : changes.length;
91
+ console.log('Changed keys:');
92
+ changes.forEach((change) => {
93
+ console.log(renderChange(change));
94
+ });
95
+ if (totalChanges > changes.length) {
96
+ console.log(`...and ${totalChanges - changes.length} more`);
97
+ }
98
+ if (((_j = (_h = refreshLoadable.data) === null || _h === void 0 ? void 0 : _h.keyUnresolvedConflictsCount) !== null && _j !== void 0 ? _j : changes.filter((change) => change.type === 'CONFLICT' && !change.resolution).length) > 0) {
99
+ error(`Unresolved merge conflicts detected between "${branchName}" and "${mergeRef.targetBranchName}".`);
100
+ info(`Finish merge in web app: ${buildMergeUrl(opts, mergeRef.id)}`);
101
+ exitWithError('Resolve changes before merging.');
102
+ }
103
+ const applyLoadable = yield loading(`Merging branch "${branchName}"...`, opts.client.POST('/v2/projects/{projectId}/branches/merge/{mergeId}/apply', {
104
+ params: {
105
+ path: {
106
+ projectId: opts.client.getProjectId(),
107
+ mergeId: mergeRef.id,
108
+ },
109
+ },
110
+ }));
111
+ handleLoadableError(applyLoadable);
112
+ success(`Branch "${branchName}" merged into "${mergeRef.targetBranchName}".`);
113
+ });
114
+ };
115
+ export default (config) => new Command('merge')
116
+ .description('Merge a branch into its origin branch')
117
+ .argument('[branch]', 'Branch name to merge')
118
+ .action(mergeHandler(config));
@@ -14,6 +14,7 @@ import { exitWithError, info, loading, success } from '../utils/logger.js';
14
14
  import { checkPathNotAFile } from '../utils/checkPathNotAFile.js';
15
15
  import { mapExportFormat } from '../utils/mapExportFormat.js';
16
16
  import { handleLoadableError } from '../client/TolgeeClient.js';
17
+ import { printBranchInfo } from '../utils/branch.js';
17
18
  import { startWatching } from '../utils/pullWatch/watchHandler.js';
18
19
  import { getETag } from '../utils/eTagStorage.js';
19
20
  export default (config) => {
@@ -44,6 +45,7 @@ const pullHandler = () => function () {
44
45
  exitWithError('Missing option --path <path> or `pull.path` in tolgee config');
45
46
  }
46
47
  yield checkPathNotAFile(opts.path);
48
+ printBranchInfo(opts.branch);
47
49
  if (!opts.watch) {
48
50
  yield doPull(opts);
49
51
  success('Done!');
@@ -93,6 +95,7 @@ function fetchZipBlob(opts, ifNoneMatch) {
93
95
  fileStructureTemplate: opts.fileStructureTemplate,
94
96
  escapeHtml: false,
95
97
  ifNoneMatch,
98
+ filterBranch: opts.branch,
96
99
  });
97
100
  handleLoadableError(loadable);
98
101
  const etag = loadable.response
@@ -19,6 +19,7 @@ import { handleLoadableError } from '../client/TolgeeClient.js';
19
19
  import { findFilesByTemplate } from '../utils/filesTemplate.js';
20
20
  import { valueToArray } from '../utils/valueToArray.js';
21
21
  import { printUnresolvedConflicts } from '../utils/printFailedKeys.js';
22
+ import { printBranchInfo } from '../utils/branch.js';
22
23
  function allInPattern(pattern) {
23
24
  return __awaiter(this, void 0, void 0, function* () {
24
25
  const files = [];
@@ -126,6 +127,7 @@ const pushHandler = (config) => function () {
126
127
  }
127
128
  return true;
128
129
  });
130
+ printBranchInfo(opts.branch);
129
131
  const files = yield loading('Reading files...', readRecords(filteredMatchers));
130
132
  if (files.length === 0) {
131
133
  error('Nothing to import.');
@@ -149,6 +151,7 @@ const pushHandler = (config) => function () {
149
151
  overrideKeyDescriptions: opts.overrideKeyDescriptions,
150
152
  convertPlaceholdersToIcu: opts.convertPlaceholdersToIcu,
151
153
  tagNewKeys: (_d = opts.tagNewKeys) !== null && _d !== void 0 ? _d : [],
154
+ branch: opts.branch,
152
155
  overrideMode: (_e = opts.overrideMode) !== null && _e !== void 0 ? _e : 'RECOMMENDED',
153
156
  fileMappings: files.map((f) => {
154
157
  var _a;
@@ -14,14 +14,18 @@ import { extractKeysOfFiles, filterExtractionResult, } from '../../extractor/run
14
14
  import { dumpWarnings } from '../../extractor/warnings.js';
15
15
  import { loading } from '../../utils/logger.js';
16
16
  import { handleLoadableError } from '../../client/TolgeeClient.js';
17
+ import { printBranchInfo } from '../../utils/branch.js';
17
18
  const asyncHandler = (config) => function () {
18
19
  return __awaiter(this, void 0, void 0, function* () {
19
20
  var _a;
20
21
  const opts = this.optsWithGlobals();
22
+ printBranchInfo(opts.branch);
21
23
  const rawKeys = yield loading('Analyzing code...', extractKeysOfFiles(opts));
22
24
  dumpWarnings(rawKeys);
23
25
  const localKeys = filterExtractionResult(rawKeys);
24
- const loadable = yield opts.client.GET('/v2/projects/{projectId}/all-keys', { params: { path: { projectId: opts.client.getProjectId() } } });
26
+ const loadable = yield loading('Fetching Tolgee keys...', opts.client.GET('/v2/projects/{projectId}/all-keys', {
27
+ params: Object.assign({ path: { projectId: opts.client.getProjectId() } }, (!!opts.branch && { query: { branch: opts.branch } })),
28
+ }));
25
29
  handleLoadableError(loadable);
26
30
  const remoteKeys = (_a = loadable.data._embedded.keys) !== null && _a !== void 0 ? _a : [];
27
31
  const diff = compareKeys(localKeys, remoteKeys);
@@ -17,7 +17,8 @@ import { unzipBuffer } from '../../utils/zip.js';
17
17
  import { askBoolean } from '../../utils/ask.js';
18
18
  import { loading, exitWithError } from '../../utils/logger.js';
19
19
  import { handleLoadableError, } from '../../client/TolgeeClient.js';
20
- function backup(client, dest) {
20
+ import { printBranchInfo } from '../../utils/branch.js';
21
+ function backup(client, dest, branch) {
21
22
  return __awaiter(this, void 0, void 0, function* () {
22
23
  const loadable = yield client.export.export({
23
24
  format: 'JSON',
@@ -25,6 +26,7 @@ function backup(client, dest) {
25
26
  filterState: ['UNTRANSLATED', 'TRANSLATED', 'REVIEWED'],
26
27
  structureDelimiter: '',
27
28
  escapeHtml: false,
29
+ filterBranch: branch,
28
30
  });
29
31
  handleLoadableError(loadable);
30
32
  const blob = loadable.data;
@@ -49,6 +51,7 @@ const syncHandler = (config) => function () {
49
51
  return __awaiter(this, void 0, void 0, function* () {
50
52
  var _a, _b, _c, _d, _e, _f;
51
53
  const opts = this.optsWithGlobals();
54
+ printBranchInfo(opts.branch);
52
55
  const rawKeys = yield loading('Analyzing code...', extractKeysOfFiles(opts));
53
56
  const warnCount = dumpWarnings(rawKeys);
54
57
  if (!opts.continueOnWarning && warnCount) {
@@ -63,9 +66,9 @@ const syncHandler = (config) => function () {
63
66
  }
64
67
  }
65
68
  }
66
- const allKeysLoadable = yield opts.client.GET('/v2/projects/{projectId}/all-keys', {
67
- params: { path: { projectId: opts.client.getProjectId() } },
68
- });
69
+ const allKeysLoadable = yield loading('Fetching Tolgee keys...', opts.client.GET('/v2/projects/{projectId}/all-keys', {
70
+ params: Object.assign({ path: { projectId: opts.client.getProjectId() } }, (!!opts.branch && { query: { branch: opts.branch } })),
71
+ }));
69
72
  handleLoadableError(allKeysLoadable);
70
73
  let remoteKeys = (_e = (_d = (_c = allKeysLoadable.data) === null || _c === void 0 ? void 0 : _c._embedded) === null || _d === void 0 ? void 0 : _d.keys) !== null && _e !== void 0 ? _e : [];
71
74
  if ((_f = opts.namespaces) === null || _f === void 0 ? void 0 : _f.length) {
@@ -92,7 +95,7 @@ const syncHandler = (config) => function () {
92
95
  // Prepare backup
93
96
  if (opts.backup) {
94
97
  yield prepareDir(opts.backup, opts.yes);
95
- yield loading('Backing up Tolgee project', backup(opts.client, opts.backup));
98
+ yield loading('Backing up Tolgee project...', backup(opts.client, opts.backup, opts.branch));
96
99
  }
97
100
  // Create new keys
98
101
  if (diff.added.length) {
@@ -108,7 +111,7 @@ const syncHandler = (config) => function () {
108
111
  tags: opts.tagNewKeys,
109
112
  }));
110
113
  const loadable = yield loading('Creating missing keys...', opts.client.POST('/v2/projects/{projectId}/keys/import', {
111
- params: { path: { projectId: opts.client.getProjectId() } },
114
+ params: Object.assign({ path: { projectId: opts.client.getProjectId() } }, (!!opts.branch && { query: { branch: opts.branch } })),
112
115
  body: { keys },
113
116
  }));
114
117
  handleLoadableError(loadable);
@@ -11,9 +11,11 @@ import { Command, Option } from 'commander';
11
11
  import { handleLoadableError } from '../client/TolgeeClient.js';
12
12
  import { exitWithError, loading, success } from '../utils/logger.js';
13
13
  import { extractKeysOfFiles } from '../extractor/runner.js';
14
+ import { printBranchInfo } from '../utils/branch.js';
14
15
  const tagHandler = (config) => function () {
15
16
  return __awaiter(this, void 0, void 0, function* () {
16
17
  const opts = this.optsWithGlobals();
18
+ printBranchInfo(opts.branch);
17
19
  let extractedKeys;
18
20
  if (opts.filterExtracted || opts.filterNotExtracted) {
19
21
  if (opts.filterExtracted && opts.filterNotExtracted) {
@@ -27,7 +29,7 @@ const tagHandler = (config) => function () {
27
29
  }));
28
30
  }
29
31
  const loadable = yield loading('Tagging...', opts.client.PUT('/v2/projects/{projectId}/tag-complex', {
30
- params: { path: { projectId: opts.client.getProjectId() } },
32
+ params: Object.assign({ path: { projectId: opts.client.getProjectId() } }, (!!opts.branch && { query: { branch: opts.branch } })),
31
33
  body: {
32
34
  filterTag: opts.filterTag,
33
35
  filterTagNot: opts.filterNoTag,
@@ -7,12 +7,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
11
- import { readFile } from 'fs/promises';
12
- import { fileURLToPath } from 'url';
13
- import { dirname, join, resolve } from 'path';
10
+ import { cosmiconfig, defaultLoaders, } from 'cosmiconfig';
11
+ import { existsSync } from 'node:fs';
12
+ import { readFile } from 'node:fs/promises';
13
+ import { fileURLToPath } from 'node:url';
14
+ import { dirname, join, resolve } from 'node:path';
14
15
  import { error, exitWithError } from '../utils/logger.js';
15
- import { existsSync } from 'fs';
16
16
  import { valueToArray } from '../utils/valueToArray.js';
17
17
  import { Ajv } from 'ajv';
18
18
  const explorer = cosmiconfig('tolgee', {
@@ -37,6 +37,12 @@ function parseConfig(input, configDir) {
37
37
  throw new Error("Invalid config: 'projectId' should be an integer representing your project Id");
38
38
  }
39
39
  }
40
+ if (rc.branch !== undefined) {
41
+ if (typeof rc.branch !== 'string' || !rc.branch.trim().length) {
42
+ throw new Error("Invalid config: 'branch' should be a non-empty string representing your project branch");
43
+ }
44
+ rc.branch = rc.branch.trim();
45
+ }
40
46
  // convert relative paths in config to absolute
41
47
  if (rc.extractor !== undefined) {
42
48
  rc.extractor = resolve(configDir, rc.extractor).replace(/\\/g, '/');
package/dist/options.js CHANGED
@@ -26,6 +26,7 @@ function parsePath(v) {
26
26
  export const API_KEY_OPT = new Option('-ak, --api-key <key>', 'Tolgee API Key. Can be a Project API Key or a Personal Access Token.').env('TOLGEE_API_KEY');
27
27
  export const PROJECT_ID_OPT = new Option('-p, --project-id <id>', 'Project ID. Only required when using a Personal Access Token.').argParser(parseProjectId);
28
28
  export const API_URL_OPT = new Option('-au, --api-url <url>', 'The url of Tolgee API.').argParser(parseUrlArgument);
29
+ export const PROJECT_BRANCH = new Option('-b, --branch <name>', 'Project branch. Use when branching enabled for the project.').env('TOLGEE_BRANCH');
29
30
  export const EXTRACTOR = new Option('-e, --extractor <extractor>', `A path to a custom extractor to use instead of the default one.`).argParser(parsePath);
30
31
  export const CONFIG_OPT = new Option('-c, --config [config]', 'A path to tolgeerc config file.').argParser(parsePath);
31
32
  export const FORMAT_OPT = new Option('--format <format>', 'Localization files format.').choices([
@@ -0,0 +1,50 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { handleLoadableError } from '../client/TolgeeClient.js';
11
+ import { info, success } from './logger.js';
12
+ export function printBranchInfo(branch) {
13
+ if (branch) {
14
+ info(`Using branch "${branch}"`);
15
+ }
16
+ }
17
+ export function fetchBranches(cmd) {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ var _a, _b, _c;
20
+ const loadable = yield cmd.client.GET('/v2/projects/{projectId}/branches', {
21
+ params: {
22
+ path: { projectId: cmd.client.getProjectId() },
23
+ query: { size: 10000, activeOnly: true },
24
+ },
25
+ });
26
+ handleLoadableError(loadable);
27
+ return (_c = (_b = (_a = loadable.data) === null || _a === void 0 ? void 0 : _a._embedded) === null || _b === void 0 ? void 0 : _b.branches) !== null && _c !== void 0 ? _c : [];
28
+ });
29
+ }
30
+ export function listBranches(branches) {
31
+ branches = branches.filter((b) => !b.merge || !b.merge.mergedAt);
32
+ if (!branches.length) {
33
+ success('No branches found.');
34
+ return;
35
+ }
36
+ console.log('Branches:');
37
+ branches.forEach((b) => {
38
+ const markers = [];
39
+ if (b.isDefault)
40
+ markers.push('default');
41
+ if (b.isProtected)
42
+ markers.push('protected');
43
+ if (b.active === false)
44
+ markers.push('inactive');
45
+ if (b.merge)
46
+ markers.push('ongoing merge');
47
+ const suffix = markers.length ? ` (${markers.join(', ')})` : '';
48
+ console.log(`- ${b.name}${suffix}`);
49
+ });
50
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tolgee/cli",
3
- "version": "2.15.1",
3
+ "version": "2.16.0",
4
4
  "type": "module",
5
5
  "description": "A tool to interact with the Tolgee Platform through CLI",
6
6
  "repository": {
package/schema.json CHANGED
@@ -5,6 +5,11 @@
5
5
  "description": "Project ID. Only required when using a Personal Access Token.",
6
6
  "type": ["number", "string"]
7
7
  },
8
+ "branch": {
9
+ "description": "Project branch. Use when branching enabled for the project.",
10
+ "type": "string",
11
+ "minLength": 1
12
+ },
8
13
  "apiUrl": {
9
14
  "description": "The url of Tolgee API.",
10
15
  "type": "string"