lingo.dev 0.78.17 → 0.79.1

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/build/cli.mjs CHANGED
@@ -620,9 +620,9 @@ function makeGitlabInitializer(spinner) {
620
620
 
621
621
  // src/cli/cmd/init.ts
622
622
  import open2 from "open";
623
- var openUrl = (path14) => {
623
+ var openUrl = (path15) => {
624
624
  const settings = getSettings(void 0);
625
- open2(`${settings.auth.webUrl}${path14}`, { wait: false });
625
+ open2(`${settings.auth.webUrl}${path15}`, { wait: false });
626
626
  };
627
627
  var throwHelpError = (option, value) => {
628
628
  if (value === "help") {
@@ -962,8 +962,8 @@ var files_default = new Command4().command("files").description("Print out the l
962
962
  } else if (type.target) {
963
963
  result.push(...targetPaths);
964
964
  }
965
- result.forEach((path14) => {
966
- console.log(path14);
965
+ result.forEach((path15) => {
966
+ console.log(path15);
967
967
  });
968
968
  }
969
969
  }
@@ -1531,9 +1531,9 @@ function createHtmlLoader() {
1531
1531
  const bDepth = b.split("/").length;
1532
1532
  return aDepth - bDepth;
1533
1533
  });
1534
- paths.forEach((path14) => {
1535
- const value = data[path14];
1536
- const [nodePath, attribute] = path14.split("#");
1534
+ paths.forEach((path15) => {
1535
+ const value = data[path15];
1536
+ const [nodePath, attribute] = path15.split("#");
1537
1537
  const [rootTag, ...indices] = nodePath.split("/");
1538
1538
  let parent = rootTag === "head" ? document.head : document.body;
1539
1539
  let current = parent;
@@ -2597,18 +2597,18 @@ function createRawDatoValue(parsedDatoValue, originalRawDatoValue, isClean = fal
2597
2597
  }
2598
2598
  function serializeStructuredText(rawStructuredText) {
2599
2599
  return serializeStructuredTextNode(rawStructuredText);
2600
- function serializeStructuredTextNode(node, path14 = [], acc = {}) {
2600
+ function serializeStructuredTextNode(node, path15 = [], acc = {}) {
2601
2601
  if ("document" in node) {
2602
- return serializeStructuredTextNode(node.document, [...path14, "document"], acc);
2602
+ return serializeStructuredTextNode(node.document, [...path15, "document"], acc);
2603
2603
  }
2604
2604
  if (!_15.isNil(node.value)) {
2605
- acc[[...path14, "value"].join(".")] = node.value;
2605
+ acc[[...path15, "value"].join(".")] = node.value;
2606
2606
  } else if (_15.get(node, "type") === "block") {
2607
- acc[[...path14, "item"].join(".")] = serializeBlock(node.item);
2607
+ acc[[...path15, "item"].join(".")] = serializeBlock(node.item);
2608
2608
  }
2609
2609
  if (node.children) {
2610
2610
  for (let i = 0; i < node.children.length; i++) {
2611
- serializeStructuredTextNode(node.children[i], [...path14, i.toString()], acc);
2611
+ serializeStructuredTextNode(node.children[i], [...path15, i.toString()], acc);
2612
2612
  }
2613
2613
  }
2614
2614
  return acc;
@@ -2667,8 +2667,8 @@ function deserializeBlockList(parsedBlockList, originalRawBlockList, isClean = f
2667
2667
  }
2668
2668
  function deserializeStructuredText(parsedStructuredText, originalRawStructuredText) {
2669
2669
  const result = _15.cloneDeep(originalRawStructuredText);
2670
- for (const [path14, value] of _15.entries(parsedStructuredText)) {
2671
- const realPath = _15.chain(path14.split(".")).flatMap((s) => !_15.isNaN(_15.toNumber(s)) ? ["children", s] : s).value();
2670
+ for (const [path15, value] of _15.entries(parsedStructuredText)) {
2671
+ const realPath = _15.chain(path15.split(".")).flatMap((s) => !_15.isNaN(_15.toNumber(s)) ? ["children", s] : s).value();
2672
2672
  const deserializedValue = createRawDatoValue(value, _15.get(originalRawStructuredText, realPath), true);
2673
2673
  _15.set(result, realPath, deserializedValue);
2674
2674
  }
@@ -3305,7 +3305,16 @@ function _tryParseJSON(line) {
3305
3305
  }
3306
3306
 
3307
3307
  // src/cli/cmd/i18n.ts
3308
- var i18n_default = new Command6().command("i18n").description("Run Localization engine").helpOption("-h, --help", "Show help").option("--locale <locale>", "Locale to process", (val, prev) => prev ? [...prev, val] : [val]).option("--bucket <bucket>", "Bucket to process", (val, prev) => prev ? [...prev, val] : [val]).option("--key <key>", "Key to process. Process only a specific translation key, useful for debugging or updating a single entry").option("--frozen", `Run in read-only mode - fails if any translations need updating, useful for CI/CD pipelines to detect missing translations`).option("--force", "Ignore lockfile and process all keys, useful for full re-translation").option("--verbose", "Show detailed output including intermediate processing data and API communication details").option("--interactive", "Enable interactive mode for reviewing and editing translations before they are applied").option("--api-key <api-key>", "Explicitly set the API key to use, override the default API key from settings").option("--debug", "Pause execution at start for debugging purposes, waits for user confirmation before proceeding").option("--strict", "Stop processing on first error instead of continuing with other locales/buckets").action(async function(options) {
3308
+ var i18n_default = new Command6().command("i18n").description("Run Localization engine").helpOption("-h, --help", "Show help").option("--locale <locale>", "Locale to process", (val, prev) => prev ? [...prev, val] : [val]).option("--bucket <bucket>", "Bucket to process", (val, prev) => prev ? [...prev, val] : [val]).option(
3309
+ "--key <key>",
3310
+ "Key to process. Process only a specific translation key, useful for debugging or updating a single entry"
3311
+ ).option(
3312
+ "--file [files...]",
3313
+ "File to process. Process only a specific path, may contain asterisk * to match multiple files. Useful if you have a lot of files and want to focus on a specific one. Specify more files separated by commas or spaces."
3314
+ ).option(
3315
+ "--frozen",
3316
+ `Run in read-only mode - fails if any translations need updating, useful for CI/CD pipelines to detect missing translations`
3317
+ ).option("--force", "Ignore lockfile and process all keys, useful for full re-translation").option("--verbose", "Show detailed output including intermediate processing data and API communication details").option("--interactive", "Enable interactive mode for reviewing and editing translations before they are applied").option("--api-key <api-key>", "Explicitly set the API key to use, override the default API key from settings").option("--debug", "Pause execution at start for debugging purposes, waits for user confirmation before proceeding").option("--strict", "Stop processing on first error instead of continuing with other locales/buckets").action(async function(options) {
3309
3318
  updateGitignore();
3310
3319
  const ora = Ora5();
3311
3320
  const flags = parseFlags(options);
@@ -3335,6 +3344,26 @@ var i18n_default = new Command6().command("i18n").description("Run Localization
3335
3344
  buckets = buckets.filter((bucket) => flags.bucket.includes(bucket.type));
3336
3345
  }
3337
3346
  ora.succeed("Buckets retrieved");
3347
+ if (flags.file?.length) {
3348
+ buckets = buckets.map((bucket) => {
3349
+ const config = bucket.config.filter(
3350
+ (config2) => flags.file.find((file) => config2.pathPattern?.match(file))
3351
+ );
3352
+ return { ...bucket, config };
3353
+ }).filter((bucket) => bucket.config.length > 0);
3354
+ if (buckets.length === 0) {
3355
+ ora.fail("No buckets found. All buckets were filtered out by --file option.");
3356
+ process.exit(1);
3357
+ } else {
3358
+ ora.info(`\x1B[36mProcessing only filtered buckets:\x1B[0m`);
3359
+ buckets.map((bucket) => {
3360
+ ora.info(` ${bucket.type}:`);
3361
+ bucket.config.forEach((config) => {
3362
+ ora.info(` - ${config.pathPattern}`);
3363
+ });
3364
+ });
3365
+ }
3366
+ }
3338
3367
  const targetLocales = flags.locale?.length ? flags.locale : i18nConfig.locale.targets;
3339
3368
  const lockfileHelper = createLockfileHelper();
3340
3369
  ora.start("Ensuring i18n.lock exists...");
@@ -3630,6 +3659,7 @@ function parseFlags(options) {
3630
3659
  verbose: Z4.boolean().optional(),
3631
3660
  strict: Z4.boolean().optional(),
3632
3661
  key: Z4.string().optional(),
3662
+ file: Z4.array(Z4.string()).optional(),
3633
3663
  interactive: Z4.boolean().default(false),
3634
3664
  debug: Z4.boolean().default(false)
3635
3665
  }).parse(options);
@@ -3953,10 +3983,637 @@ var mcp_default = new Command9().command("mcp").description("Use Lingo.dev model
3953
3983
  console.log("Lingo.dev MCP Server running on stdio");
3954
3984
  });
3955
3985
 
3986
+ // src/cli/cmd/ci.ts
3987
+ import { Command as Command10 } from "interactive-commander";
3988
+
3989
+ // ../../action/src/main.ts
3990
+ import createOra from "ora";
3991
+
3992
+ // ../../action/src/flows/pull-request.ts
3993
+ import { execSync as execSync3 } from "child_process";
3994
+
3995
+ // ../../action/src/flows/in-branch.ts
3996
+ import { execSync as execSync2 } from "child_process";
3997
+ import path14 from "path";
3998
+
3999
+ // ../../action/src/flows/_base.ts
4000
+ var IntegrationFlow = class {
4001
+ constructor(ora, platformKit) {
4002
+ this.ora = ora;
4003
+ this.platformKit = platformKit;
4004
+ }
4005
+ i18nBranchName;
4006
+ };
4007
+ var gitConfig = {
4008
+ userName: "Lingo.dev",
4009
+ userEmail: "support@lingo.dev"
4010
+ };
4011
+
4012
+ // ../../action/src/flows/in-branch.ts
4013
+ var InBranchFlow = class extends IntegrationFlow {
4014
+ async preRun() {
4015
+ this.ora.start("Configuring git");
4016
+ const canContinue = this.configureGit();
4017
+ this.ora.succeed("Git configured");
4018
+ return canContinue;
4019
+ }
4020
+ async run(forcePush = false) {
4021
+ this.ora.start("Running Lingo.dev");
4022
+ await this.runLingoDotDev();
4023
+ this.ora.succeed("Done running Lingo.dev");
4024
+ execSync2(`rm -f i18n.cache`, { stdio: "inherit" });
4025
+ this.ora.start("Checking for changes");
4026
+ const hasChanges = this.checkCommitableChanges();
4027
+ this.ora.succeed(hasChanges ? "Changes detected" : "No changes detected");
4028
+ if (hasChanges) {
4029
+ this.ora.start("Committing changes");
4030
+ execSync2(`git add .`, { stdio: "inherit" });
4031
+ execSync2(`git status --porcelain`, { stdio: "inherit" });
4032
+ execSync2(`git commit -m "${this.platformKit.config.commitMessage}"`, {
4033
+ stdio: "inherit"
4034
+ });
4035
+ this.ora.succeed("Changes committed");
4036
+ this.ora.start("Pushing changes to remote");
4037
+ const currentBranch = this.i18nBranchName ?? this.platformKit.platformConfig.baseBranchName;
4038
+ execSync2(`git push origin ${currentBranch} ${forcePush ? "--force" : ""}`, {
4039
+ stdio: "inherit"
4040
+ });
4041
+ this.ora.succeed("Changes pushed to remote");
4042
+ }
4043
+ return hasChanges;
4044
+ }
4045
+ checkCommitableChanges() {
4046
+ return execSync2('git status --porcelain || echo "has_changes"', {
4047
+ encoding: "utf8"
4048
+ }).length > 0;
4049
+ }
4050
+ async runLingoDotDev() {
4051
+ execSync2(`npx lingo.dev@latest i18n --api-key ${this.platformKit.config.replexicaApiKey}`, { stdio: "inherit" });
4052
+ }
4053
+ configureGit() {
4054
+ const { processOwnCommits } = this.platformKit.config;
4055
+ const { baseBranchName } = this.platformKit.platformConfig;
4056
+ this.ora.info(`Current working directory:`);
4057
+ execSync2(`pwd`, { stdio: "inherit" });
4058
+ execSync2(`ls -la`, { stdio: "inherit" });
4059
+ execSync2(`git config --global safe.directory ${process.cwd()}`);
4060
+ execSync2(`git config user.name "${gitConfig.userName}"`);
4061
+ execSync2(`git config user.email "${gitConfig.userEmail}"`);
4062
+ this.platformKit?.gitConfig();
4063
+ execSync2(`git fetch origin ${baseBranchName}`, { stdio: "inherit" });
4064
+ execSync2(`git checkout ${baseBranchName} --`, { stdio: "inherit" });
4065
+ if (!processOwnCommits) {
4066
+ const currentAuthor = `${gitConfig.userName} <${gitConfig.userEmail}>`;
4067
+ const authorOfLastCommit = execSync2(`git log -1 --pretty=format:'%an <%ae>'`).toString();
4068
+ if (authorOfLastCommit === currentAuthor) {
4069
+ this.ora.warn(
4070
+ `The last commit was already made by ${currentAuthor}, so this run will be skipped, as running again would have no effect. See docs: https://docs.lingo.dev/ci-action/overview`
4071
+ );
4072
+ return false;
4073
+ }
4074
+ }
4075
+ const workingDir = path14.resolve(process.cwd(), this.platformKit.config.workingDir);
4076
+ if (workingDir !== process.cwd()) {
4077
+ this.ora.info(`Changing to working directory: ${this.platformKit.config.workingDir}`);
4078
+ process.chdir(workingDir);
4079
+ }
4080
+ return true;
4081
+ }
4082
+ };
4083
+
4084
+ // ../../action/src/flows/pull-request.ts
4085
+ var PullRequestFlow = class extends InBranchFlow {
4086
+ async preRun() {
4087
+ const canContinue = await super.preRun?.();
4088
+ if (!canContinue) {
4089
+ return false;
4090
+ }
4091
+ this.ora.start("Calculating automated branch name");
4092
+ this.i18nBranchName = this.calculatePrBranchName();
4093
+ this.ora.succeed(`Automated branch name calculated: ${this.i18nBranchName}`);
4094
+ this.ora.start("Checking if branch exists");
4095
+ const branchExists = await this.checkBranchExistance(this.i18nBranchName);
4096
+ this.ora.succeed(branchExists ? "Branch exists" : "Branch does not exist");
4097
+ if (branchExists) {
4098
+ this.ora.start(`Checking out branch ${this.i18nBranchName}`);
4099
+ this.checkoutI18nBranch(this.i18nBranchName);
4100
+ this.ora.succeed(`Checked out branch ${this.i18nBranchName}`);
4101
+ this.ora.start(`Syncing with ${this.platformKit.platformConfig.baseBranchName}`);
4102
+ this.syncI18nBranch();
4103
+ this.ora.succeed(`Checked out and synced branch ${this.i18nBranchName}`);
4104
+ } else {
4105
+ this.ora.start(`Creating branch ${this.i18nBranchName}`);
4106
+ this.createI18nBranch(this.i18nBranchName);
4107
+ this.ora.succeed(`Created branch ${this.i18nBranchName}`);
4108
+ }
4109
+ return true;
4110
+ }
4111
+ async run() {
4112
+ return super.run(true);
4113
+ }
4114
+ async postRun() {
4115
+ if (!this.i18nBranchName) {
4116
+ throw new Error("i18nBranchName is not set. Did you forget to call preRun?");
4117
+ }
4118
+ this.ora.start("Checking if PR already exists");
4119
+ const pullRequestNumber = await this.ensureFreshPr(this.i18nBranchName);
4120
+ this.ora.succeed(`Pull request ready: ${this.platformKit.buildPullRequestUrl(pullRequestNumber)}`);
4121
+ }
4122
+ calculatePrBranchName() {
4123
+ return `lingo.dev/${this.platformKit.platformConfig.baseBranchName}`;
4124
+ }
4125
+ async checkBranchExistance(prBranchName) {
4126
+ return this.platformKit.branchExists({
4127
+ branch: prBranchName
4128
+ });
4129
+ }
4130
+ async ensureFreshPr(i18nBranchName) {
4131
+ this.ora.start(
4132
+ `Checking for existing PR with head ${i18nBranchName} and base ${this.platformKit.platformConfig.baseBranchName}`
4133
+ );
4134
+ const existingPrNumber = await this.platformKit.getOpenPullRequestNumber({
4135
+ branch: i18nBranchName
4136
+ });
4137
+ this.ora.succeed(existingPrNumber ? "PR found" : "No PR found");
4138
+ if (existingPrNumber) {
4139
+ this.ora.start(`Closing existing PR ${existingPrNumber}`);
4140
+ await this.platformKit.closePullRequest({
4141
+ pullRequestNumber: existingPrNumber
4142
+ });
4143
+ this.ora.succeed(`Closed existing PR ${existingPrNumber}`);
4144
+ }
4145
+ this.ora.start(`Creating new PR`);
4146
+ const newPrNumber = await this.platformKit.createPullRequest({
4147
+ head: i18nBranchName,
4148
+ title: this.platformKit.config.pullRequestTitle,
4149
+ body: this.getPrBodyContent()
4150
+ });
4151
+ this.ora.succeed(`Created new PR ${newPrNumber}`);
4152
+ if (existingPrNumber) {
4153
+ this.ora.start(`Posting comment about outdated PR ${existingPrNumber}`);
4154
+ await this.platformKit.commentOnPullRequest({
4155
+ pullRequestNumber: existingPrNumber,
4156
+ body: `This PR is now outdated. A new version has been created at ${this.platformKit.buildPullRequestUrl(
4157
+ newPrNumber
4158
+ )}`
4159
+ });
4160
+ this.ora.succeed(`Posted comment about outdated PR ${existingPrNumber}`);
4161
+ }
4162
+ return newPrNumber;
4163
+ }
4164
+ checkoutI18nBranch(i18nBranchName) {
4165
+ execSync3(`git fetch origin ${i18nBranchName}`, { stdio: "inherit" });
4166
+ execSync3(`git checkout -b ${i18nBranchName}`, {
4167
+ stdio: "inherit"
4168
+ });
4169
+ }
4170
+ createI18nBranch(i18nBranchName) {
4171
+ try {
4172
+ execSync3(`git fetch origin ${this.platformKit.platformConfig.baseBranchName}`, { stdio: "inherit" });
4173
+ execSync3(`git checkout -b ${i18nBranchName} origin/${this.platformKit.platformConfig.baseBranchName}`, {
4174
+ stdio: "inherit"
4175
+ });
4176
+ } catch (error) {
4177
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
4178
+ this.ora.fail(`Failed to create branch: ${errorMessage}`);
4179
+ this.ora.info(`
4180
+ Troubleshooting tips:
4181
+ 1. Make sure you have permission to create branches
4182
+ 2. Check if the branch already exists locally (try 'git branch -a')
4183
+ 3. Verify connectivity to remote repository
4184
+ `);
4185
+ throw new Error(`Branch creation failed: ${errorMessage}`);
4186
+ }
4187
+ }
4188
+ syncI18nBranch() {
4189
+ if (!this.i18nBranchName) {
4190
+ throw new Error("i18nBranchName is not set");
4191
+ }
4192
+ this.ora.start(`Fetching latest changes from ${this.platformKit.platformConfig.baseBranchName}`);
4193
+ execSync3(`git fetch origin ${this.platformKit.platformConfig.baseBranchName}`, { stdio: "inherit" });
4194
+ this.ora.succeed(`Fetched latest changes from ${this.platformKit.platformConfig.baseBranchName}`);
4195
+ try {
4196
+ this.ora.start("Attempting to rebase branch");
4197
+ execSync3(`git rebase origin/${this.platformKit.platformConfig.baseBranchName}`, { stdio: "inherit" });
4198
+ this.ora.succeed("Successfully rebased branch");
4199
+ } catch (error) {
4200
+ this.ora.warn("Rebase failed, falling back to alternative sync method");
4201
+ this.ora.start("Aborting failed rebase");
4202
+ execSync3("git rebase --abort", { stdio: "inherit" });
4203
+ this.ora.succeed("Aborted failed rebase");
4204
+ this.ora.start(`Resetting to ${this.platformKit.platformConfig.baseBranchName}`);
4205
+ execSync3(`git reset --hard origin/${this.platformKit.platformConfig.baseBranchName}`, { stdio: "inherit" });
4206
+ this.ora.succeed(`Reset to ${this.platformKit.platformConfig.baseBranchName}`);
4207
+ this.ora.start("Restoring target files");
4208
+ const targetFiles = ["i18n.lock"];
4209
+ const targetFileNames = execSync3(
4210
+ `npx lingo.dev@latest show files --target ${this.platformKit.platformConfig.baseBranchName}`,
4211
+ { encoding: "utf8" }
4212
+ ).split("\n").filter(Boolean);
4213
+ targetFiles.push(...targetFileNames);
4214
+ execSync3(`git fetch origin ${this.i18nBranchName}`, { stdio: "inherit" });
4215
+ for (const file of targetFiles) {
4216
+ try {
4217
+ execSync3(`git checkout FETCH_HEAD -- ${file}`, { stdio: "inherit" });
4218
+ } catch (error2) {
4219
+ this.ora.warn(`Skipping non-existent file: ${file}`);
4220
+ continue;
4221
+ }
4222
+ }
4223
+ this.ora.succeed("Restored target files");
4224
+ }
4225
+ this.ora.start("Checking for changes to commit");
4226
+ const hasChanges = this.checkCommitableChanges();
4227
+ if (hasChanges) {
4228
+ execSync3("git add .", { stdio: "inherit" });
4229
+ execSync3(`git commit -m "chore: sync with ${this.platformKit.platformConfig.baseBranchName}"`, {
4230
+ stdio: "inherit"
4231
+ });
4232
+ this.ora.succeed("Committed additional changes");
4233
+ } else {
4234
+ this.ora.succeed("No changes to commit");
4235
+ }
4236
+ }
4237
+ getPrBodyContent() {
4238
+ return `
4239
+ Hey team,
4240
+
4241
+ [**Lingo.dev**](https://lingo.dev) here with fresh translations!
4242
+
4243
+ ### In this update
4244
+
4245
+ - Added missing translations
4246
+ - Performed brand voice, context and glossary checks
4247
+ - Enhanced translations using Lingo.dev Localization Engine
4248
+
4249
+ ### Next Steps
4250
+
4251
+ - [ ] Review the changes
4252
+ - [ ] Merge when ready
4253
+ `.trim();
4254
+ }
4255
+ };
4256
+
4257
+ // ../../action/src/platforms/bitbucket.ts
4258
+ import { execSync as execSync4 } from "child_process";
4259
+ import bbLib from "bitbucket";
4260
+ import Z8 from "zod";
4261
+
4262
+ // ../../action/src/platforms/_base.ts
4263
+ import Z7 from "zod";
4264
+ var defaultMessage = "feat: update translations via @lingodotdev";
4265
+ var PlatformKit = class {
4266
+ gitConfig() {
4267
+ }
4268
+ get config() {
4269
+ const env = Z7.object({
4270
+ LINGODOTDEV_API_KEY: Z7.string(),
4271
+ LINGODOTDEV_PULL_REQUEST: Z7.preprocess((val) => val === "true" || val === true, Z7.boolean()),
4272
+ LINGODOTDEV_COMMIT_MESSAGE: Z7.string().optional(),
4273
+ LINGODOTDEV_PULL_REQUEST_TITLE: Z7.string().optional(),
4274
+ LINGODOTDEV_WORKING_DIRECTORY: Z7.string().optional(),
4275
+ LINGODOTDEV_PROCESS_OWN_COMMITS: Z7.preprocess((val) => val === "true" || val === true, Z7.boolean()).optional()
4276
+ }).parse(process.env);
4277
+ return {
4278
+ replexicaApiKey: env.LINGODOTDEV_API_KEY,
4279
+ isPullRequestMode: env.LINGODOTDEV_PULL_REQUEST,
4280
+ commitMessage: env.LINGODOTDEV_COMMIT_MESSAGE || defaultMessage,
4281
+ pullRequestTitle: env.LINGODOTDEV_PULL_REQUEST_TITLE || defaultMessage,
4282
+ workingDir: env.LINGODOTDEV_WORKING_DIRECTORY || ".",
4283
+ processOwnCommits: env.LINGODOTDEV_PROCESS_OWN_COMMITS || false
4284
+ };
4285
+ }
4286
+ };
4287
+
4288
+ // ../../action/src/platforms/bitbucket.ts
4289
+ var { Bitbucket } = bbLib;
4290
+ var BitbucketPlatformKit = class extends PlatformKit {
4291
+ _bb;
4292
+ get bb() {
4293
+ if (!this._bb) {
4294
+ this._bb = new Bitbucket({
4295
+ auth: { token: this.platformConfig.bbToken || "" }
4296
+ });
4297
+ }
4298
+ return this._bb;
4299
+ }
4300
+ async branchExists({ branch }) {
4301
+ return await this.bb.repositories.getBranch({
4302
+ workspace: this.platformConfig.repositoryOwner,
4303
+ repo_slug: this.platformConfig.repositoryName,
4304
+ name: branch
4305
+ }).then((r) => r.data).then((v) => !!v).catch((r) => r.status === 404 ? false : Promise.reject(r));
4306
+ }
4307
+ async getOpenPullRequestNumber({ branch }) {
4308
+ return await this.bb.repositories.listPullRequests({
4309
+ workspace: this.platformConfig.repositoryOwner,
4310
+ repo_slug: this.platformConfig.repositoryName,
4311
+ state: "OPEN"
4312
+ }).then(({ data: { values } }) => {
4313
+ return values?.find(
4314
+ ({ source, destination }) => source?.branch?.name === branch && destination?.branch?.name === this.platformConfig.baseBranchName
4315
+ );
4316
+ }).then((pr) => pr?.id);
4317
+ }
4318
+ async closePullRequest({ pullRequestNumber }) {
4319
+ await this.bb.repositories.declinePullRequest({
4320
+ workspace: this.platformConfig.repositoryOwner,
4321
+ repo_slug: this.platformConfig.repositoryName,
4322
+ pull_request_id: pullRequestNumber
4323
+ });
4324
+ }
4325
+ async createPullRequest({ title, body, head }) {
4326
+ return await this.bb.repositories.createPullRequest({
4327
+ workspace: this.platformConfig.repositoryOwner,
4328
+ repo_slug: this.platformConfig.repositoryName,
4329
+ _body: {
4330
+ title,
4331
+ description: body,
4332
+ source: { branch: { name: head } },
4333
+ destination: { branch: { name: this.platformConfig.baseBranchName } }
4334
+ }
4335
+ }).then(({ data }) => data.id ?? 0);
4336
+ }
4337
+ async commentOnPullRequest({ pullRequestNumber, body }) {
4338
+ await this.bb.repositories.createPullRequestComment({
4339
+ workspace: this.platformConfig.repositoryOwner,
4340
+ repo_slug: this.platformConfig.repositoryName,
4341
+ pull_request_id: pullRequestNumber,
4342
+ _body: {
4343
+ content: {
4344
+ raw: body
4345
+ }
4346
+ }
4347
+ });
4348
+ }
4349
+ async gitConfig() {
4350
+ execSync4("git config --unset http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy", {
4351
+ stdio: "inherit"
4352
+ });
4353
+ execSync4("git config http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy http://host.docker.internal:29418/", {
4354
+ stdio: "inherit"
4355
+ });
4356
+ }
4357
+ get platformConfig() {
4358
+ const env = Z8.object({
4359
+ BITBUCKET_BRANCH: Z8.string(),
4360
+ BITBUCKET_REPO_FULL_NAME: Z8.string(),
4361
+ BB_TOKEN: Z8.string().optional()
4362
+ }).parse(process.env);
4363
+ const [repositoryOwner, repositoryName] = env.BITBUCKET_REPO_FULL_NAME.split("/");
4364
+ return {
4365
+ baseBranchName: env.BITBUCKET_BRANCH,
4366
+ repositoryOwner,
4367
+ repositoryName,
4368
+ bbToken: env.BB_TOKEN
4369
+ };
4370
+ }
4371
+ buildPullRequestUrl(pullRequestNumber) {
4372
+ const { repositoryOwner, repositoryName } = this.platformConfig;
4373
+ return `https://bitbucket.org/${repositoryOwner}/${repositoryName}/pull-requests/${pullRequestNumber}`;
4374
+ }
4375
+ };
4376
+
4377
+ // ../../action/src/platforms/github.ts
4378
+ import { Octokit } from "octokit";
4379
+ import Z9 from "zod";
4380
+ import { execSync as execSync5 } from "child_process";
4381
+ var GitHubPlatformKit = class extends PlatformKit {
4382
+ _octokit;
4383
+ get octokit() {
4384
+ if (!this._octokit) {
4385
+ this._octokit = new Octokit({ auth: this.platformConfig.ghToken });
4386
+ }
4387
+ return this._octokit;
4388
+ }
4389
+ async branchExists({ branch }) {
4390
+ return await this.octokit.rest.repos.getBranch({
4391
+ branch,
4392
+ owner: this.platformConfig.repositoryOwner,
4393
+ repo: this.platformConfig.repositoryName
4394
+ }).then((r) => r.data).then((v) => !!v).catch((r) => r.status === 404 ? false : Promise.reject(r));
4395
+ }
4396
+ async getOpenPullRequestNumber({ branch }) {
4397
+ return await this.octokit.rest.pulls.list({
4398
+ head: `${this.platformConfig.repositoryOwner}:${branch}`,
4399
+ owner: this.platformConfig.repositoryOwner,
4400
+ repo: this.platformConfig.repositoryName,
4401
+ base: this.platformConfig.baseBranchName,
4402
+ state: "open"
4403
+ }).then(({ data }) => data[0]).then((pr) => pr?.number);
4404
+ }
4405
+ async closePullRequest({ pullRequestNumber }) {
4406
+ await this.octokit.rest.pulls.update({
4407
+ pull_number: pullRequestNumber,
4408
+ owner: this.platformConfig.repositoryOwner,
4409
+ repo: this.platformConfig.repositoryName,
4410
+ state: "closed"
4411
+ });
4412
+ }
4413
+ async createPullRequest({ head, title, body }) {
4414
+ return await this.octokit.rest.pulls.create({
4415
+ head,
4416
+ title,
4417
+ body,
4418
+ owner: this.platformConfig.repositoryOwner,
4419
+ repo: this.platformConfig.repositoryName,
4420
+ base: this.platformConfig.baseBranchName
4421
+ }).then(({ data }) => data.number);
4422
+ }
4423
+ async commentOnPullRequest({ pullRequestNumber, body }) {
4424
+ await this.octokit.rest.issues.createComment({
4425
+ issue_number: pullRequestNumber,
4426
+ body,
4427
+ owner: this.platformConfig.repositoryOwner,
4428
+ repo: this.platformConfig.repositoryName
4429
+ });
4430
+ }
4431
+ async gitConfig() {
4432
+ const { ghToken, repositoryOwner, repositoryName } = this.platformConfig;
4433
+ const { processOwnCommits } = this.config;
4434
+ if (ghToken && processOwnCommits) {
4435
+ console.log("Using provided GH_TOKEN. This will trigger your CI/CD pipeline to run again.");
4436
+ const url = `https://${ghToken}@github.com/${repositoryOwner}/${repositoryName}.git`;
4437
+ execSync5(`git remote set-url origin ${url}`, {
4438
+ stdio: "inherit"
4439
+ });
4440
+ }
4441
+ }
4442
+ get platformConfig() {
4443
+ const env = Z9.object({
4444
+ GITHUB_REPOSITORY: Z9.string(),
4445
+ GITHUB_REPOSITORY_OWNER: Z9.string(),
4446
+ GITHUB_REF_NAME: Z9.string(),
4447
+ GITHUB_HEAD_REF: Z9.string(),
4448
+ GH_TOKEN: Z9.string().optional()
4449
+ }).parse(process.env);
4450
+ const baseBranchName = !env.GITHUB_REF_NAME.endsWith("/merge") ? env.GITHUB_REF_NAME : env.GITHUB_HEAD_REF;
4451
+ return {
4452
+ ghToken: env.GH_TOKEN,
4453
+ baseBranchName,
4454
+ repositoryOwner: env.GITHUB_REPOSITORY_OWNER,
4455
+ repositoryName: env.GITHUB_REPOSITORY.split("/")[1]
4456
+ };
4457
+ }
4458
+ buildPullRequestUrl(pullRequestNumber) {
4459
+ const { repositoryOwner, repositoryName } = this.platformConfig;
4460
+ return `https://github.com/${repositoryOwner}/${repositoryName}/pull/${pullRequestNumber}`;
4461
+ }
4462
+ };
4463
+
4464
+ // ../../action/src/platforms/gitlab.ts
4465
+ import { Gitlab } from "@gitbeaker/rest";
4466
+ import Z10 from "zod";
4467
+ import { execSync as execSync6 } from "child_process";
4468
+ var gl = new Gitlab({ token: "" });
4469
+ var GitlabPlatformKit = class extends PlatformKit {
4470
+ _gitlab;
4471
+ constructor() {
4472
+ super();
4473
+ process.chdir(this.platformConfig.projectDir);
4474
+ }
4475
+ get gitlab() {
4476
+ if (!this._gitlab) {
4477
+ this._gitlab = new Gitlab({
4478
+ token: this.platformConfig.glToken || ""
4479
+ });
4480
+ }
4481
+ return this._gitlab;
4482
+ }
4483
+ get platformConfig() {
4484
+ const env = Z10.object({
4485
+ GL_TOKEN: Z10.string().optional(),
4486
+ CI_COMMIT_BRANCH: Z10.string(),
4487
+ CI_MERGE_REQUEST_SOURCE_BRANCH_NAME: Z10.string().optional(),
4488
+ CI_PROJECT_NAMESPACE: Z10.string(),
4489
+ CI_PROJECT_NAME: Z10.string(),
4490
+ CI_PROJECT_ID: Z10.string(),
4491
+ CI_PROJECT_DIR: Z10.string(),
4492
+ CI_REPOSITORY_URL: Z10.string()
4493
+ }).parse(process.env);
4494
+ const config = {
4495
+ glToken: env.GL_TOKEN,
4496
+ baseBranchName: env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME ?? env.CI_COMMIT_BRANCH,
4497
+ repositoryOwner: env.CI_PROJECT_NAMESPACE,
4498
+ repositoryName: env.CI_PROJECT_NAME,
4499
+ gitlabProjectId: env.CI_PROJECT_ID,
4500
+ projectDir: env.CI_PROJECT_DIR,
4501
+ reporitoryUrl: env.CI_REPOSITORY_URL
4502
+ };
4503
+ return config;
4504
+ }
4505
+ async branchExists({ branch }) {
4506
+ try {
4507
+ await this.gitlab.Branches.show(this.platformConfig.gitlabProjectId, branch);
4508
+ return true;
4509
+ } catch {
4510
+ return false;
4511
+ }
4512
+ }
4513
+ async getOpenPullRequestNumber({ branch }) {
4514
+ const mergeRequests = await this.gitlab.MergeRequests.all({
4515
+ projectId: this.platformConfig.gitlabProjectId,
4516
+ sourceBranch: branch,
4517
+ state: "opened"
4518
+ });
4519
+ return mergeRequests[0]?.iid;
4520
+ }
4521
+ async closePullRequest({ pullRequestNumber }) {
4522
+ await this.gitlab.MergeRequests.edit(this.platformConfig.gitlabProjectId, pullRequestNumber, {
4523
+ stateEvent: "close"
4524
+ });
4525
+ }
4526
+ async createPullRequest({ head, title, body }) {
4527
+ const mr = await this.gitlab.MergeRequests.create(
4528
+ this.platformConfig.gitlabProjectId,
4529
+ head,
4530
+ this.platformConfig.baseBranchName,
4531
+ title,
4532
+ {
4533
+ description: body
4534
+ }
4535
+ );
4536
+ return mr.iid;
4537
+ }
4538
+ async commentOnPullRequest({ pullRequestNumber, body }) {
4539
+ await this.gitlab.MergeRequestNotes.create(this.platformConfig.gitlabProjectId, pullRequestNumber, body);
4540
+ }
4541
+ gitConfig() {
4542
+ const url = `https://oauth2:${this.platformConfig.glToken}@gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}.git`;
4543
+ execSync6(`git remote set-url origin ${url}`, {
4544
+ stdio: "inherit"
4545
+ });
4546
+ }
4547
+ buildPullRequestUrl(pullRequestNumber) {
4548
+ return `https://gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}/-/merge_requests/${pullRequestNumber}`;
4549
+ }
4550
+ };
4551
+
4552
+ // ../../action/src/platforms/index.ts
4553
+ var getPlatformKit = () => {
4554
+ if (process.env.BITBUCKET_PIPELINE_UUID) {
4555
+ return new BitbucketPlatformKit();
4556
+ }
4557
+ if (process.env.GITHUB_ACTION) {
4558
+ return new GitHubPlatformKit();
4559
+ }
4560
+ if (process.env.GITLAB_CI) {
4561
+ return new GitlabPlatformKit();
4562
+ }
4563
+ throw new Error("This platform is not supported");
4564
+ };
4565
+
4566
+ // ../../action/src/main.ts
4567
+ async function main() {
4568
+ const ora = createOra();
4569
+ const platformKit = getPlatformKit();
4570
+ const { isPullRequestMode } = platformKit.config;
4571
+ ora.info(`Pull request mode: ${isPullRequestMode ? "on" : "off"}`);
4572
+ const flow = isPullRequestMode ? new PullRequestFlow(ora, platformKit) : new InBranchFlow(ora, platformKit);
4573
+ const canRun = await flow.preRun?.();
4574
+ if (canRun === false) {
4575
+ return;
4576
+ }
4577
+ const hasChanges = await flow.run();
4578
+ if (!hasChanges) {
4579
+ return;
4580
+ }
4581
+ await flow.postRun?.();
4582
+ }
4583
+
4584
+ // src/cli/cmd/ci.ts
4585
+ var ci_default = new Command10().command("ci").description("Run Lingo.dev CI/CD action").helpOption("-h, --help", "Show help").option("--pull-request", "Create a pull request with the changes", false).option("--commit-message <message>", "Commit message").option("--pull-request-title <title>", "Pull request title").option("--working-directory <dir>", "Working directory").option("--process-own-commits", "Process commits made by this action", false).action(async (options, program) => {
4586
+ const apiKey = program.args[0];
4587
+ const settings = getSettings(apiKey);
4588
+ if (!settings.auth.apiKey) {
4589
+ console.error("No API key provided");
4590
+ return;
4591
+ }
4592
+ const authenticator = createAuthenticator({
4593
+ apiUrl: settings.auth.apiUrl,
4594
+ apiKey: settings.auth.apiKey
4595
+ });
4596
+ const auth = await authenticator.whoami();
4597
+ if (!auth) {
4598
+ console.error("Not authenticated");
4599
+ return;
4600
+ }
4601
+ const env = {
4602
+ LINGODOTDEV_API_KEY: settings.auth.apiKey,
4603
+ LINGODOTDEV_PULL_REQUEST: options.pullRequest?.toString() || "false",
4604
+ ...options.commitMessage && { LINGODOTDEV_COMMIT_MESSAGE: options.commitMessage },
4605
+ ...options.pullRequestTitle && { LINGODOTDEV_PULL_REQUEST_TITLE: options.pullRequestTitle },
4606
+ ...options.workingDirectory && { LINGODOTDEV_WORKING_DIRECTORY: options.workingDirectory },
4607
+ ...options.processOwnCommits && { LINGODOTDEV_PROCESS_OWN_COMMITS: options.processOwnCommits.toString() }
4608
+ };
4609
+ process.env = { ...process.env, ...env };
4610
+ main();
4611
+ });
4612
+
3956
4613
  // package.json
3957
4614
  var package_default = {
3958
4615
  name: "lingo.dev",
3959
- version: "0.78.17",
4616
+ version: "0.79.1",
3960
4617
  description: "Lingo.dev CLI",
3961
4618
  private: false,
3962
4619
  publishConfig: {
@@ -4014,11 +4671,13 @@ var package_default = {
4014
4671
  license: "Apache-2.0",
4015
4672
  dependencies: {
4016
4673
  "@datocms/cma-client-node": "^3.4.0",
4674
+ "@gitbeaker/rest": "^39.34.3",
4017
4675
  "@inquirer/prompts": "^7.2.3",
4018
4676
  "@lingo.dev/_sdk": "workspace:*",
4019
4677
  "@lingo.dev/_spec": "workspace:*",
4020
4678
  "@modelcontextprotocol/sdk": "^1.5.0",
4021
4679
  "@paralleldrive/cuid2": "^2.2.2",
4680
+ bitbucket: "^2.12.0",
4022
4681
  chalk: "^5.4.1",
4023
4682
  cors: "^2.8.5",
4024
4683
  "csv-parse": "^5.6.0",
@@ -4048,6 +4707,7 @@ var package_default = {
4048
4707
  marked: "^15.0.6",
4049
4708
  "node-webvtt": "^1.9.4",
4050
4709
  "object-hash": "^3.0.0",
4710
+ octokit: "^4.0.2",
4051
4711
  open: "^10.1.0",
4052
4712
  ora: "^8.1.1",
4053
4713
  "p-limit": "^6.2.0",
@@ -4107,7 +4767,7 @@ ${vice(
4107
4767
 
4108
4768
  Website: https://lingo.dev
4109
4769
  `
4110
- ).version(`v${package_default.version}`, "-v, --version", "Show version").addCommand(init_default).interactive("-y, --no-interactive", "Disable interactive mode").addCommand(i18n_default).addCommand(auth_default).addCommand(show_default).addCommand(lockfile_default).addCommand(cleanup_default).addCommand(mcp_default).exitOverride((err) => {
4770
+ ).version(`v${package_default.version}`, "-v, --version", "Show version").addCommand(init_default).interactive("-y, --no-interactive", "Disable interactive mode").addCommand(i18n_default).addCommand(auth_default).addCommand(show_default).addCommand(lockfile_default).addCommand(cleanup_default).addCommand(mcp_default).addCommand(ci_default).exitOverride((err) => {
4111
4771
  if (err.code === "commander.helpDisplayed" || err.code === "commander.version" || err.code === "commander.help") {
4112
4772
  process.exit(0);
4113
4773
  }