Skip to content

Commit 1fc204b

Browse files
authored
feat(cli): init github/bitbucket/gitlab action (#505)
1 parent c037d95 commit 1fc204b

File tree

5 files changed

+175
-20
lines changed

5 files changed

+175
-20
lines changed

.changeset/quiet-dancers-cover.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"lingo.dev": patch
3+
---
4+
5+
init github/bitbucket/gitlab action

packages/cli/src/cli/cmd/init.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { createAuthenticator } from "../utils/auth";
1313
import findLocaleFiles from "../utils/find-locale-paths";
1414
import { ensurePatterns } from "../utils/ensure-patterns";
1515
import updateGitignore from "../utils/update-gitignore";
16+
import initCICD from "../utils/init-ci-cd";
1617

1718
const openUrl = (path: string) => {
1819
const settings = getSettings(undefined);
@@ -118,9 +119,9 @@ export default new InteractiveCommand()
118119
};
119120
} else {
120121
let selectedPatterns: string[] = [];
121-
const { found, patterns } = findLocaleFiles(options.bucket);
122+
const { patterns, defaultPatterns } = findLocaleFiles(options.bucket);
122123

123-
if (found) {
124+
if (patterns.length > 0) {
124125
spinner.succeed("Found existing locale files:");
125126

126127
selectedPatterns = await checkbox({
@@ -135,11 +136,11 @@ export default new InteractiveCommand()
135136

136137
if (selectedPatterns.length === 0) {
137138
const useDefault = await confirm({
138-
message: `Use (and create) default path ${patterns.join(", ")}?`,
139+
message: `Use (and create) default path ${defaultPatterns.join(", ")}?`,
139140
});
140-
ensurePatterns(patterns, options.source);
141141
if (useDefault) {
142-
selectedPatterns = patterns;
142+
ensurePatterns(defaultPatterns, options.source);
143+
selectedPatterns = defaultPatterns;
143144
}
144145
}
145146

@@ -162,6 +163,8 @@ export default new InteractiveCommand()
162163
spinner.succeed("Lingo.dev project initialized");
163164

164165
if (isInteractive) {
166+
await initCICD(spinner);
167+
165168
const openDocs = await confirm({ message: "Would you like to see our docs?" });
166169
if (openDocs) {
167170
openUrl("/go/docs");

packages/cli/src/cli/utils/find-locale-paths.spec.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ describe("findLocaleFiles", () => {
2929
const result = findLocaleFiles("json");
3030

3131
expect(result).toEqual({
32-
found: true,
3332
patterns: ["src/i18n/[locale].json", "src/translations/[locale].json"],
33+
defaultPatterns: ["i18n/[locale].json"],
3434
});
3535
});
3636

@@ -40,8 +40,8 @@ describe("findLocaleFiles", () => {
4040
const result = findLocaleFiles("yaml");
4141

4242
expect(result).toEqual({
43-
found: true,
4443
patterns: ["locales/[locale].yml", "translations/[locale].yml"],
44+
defaultPatterns: ["i18n/[locale].yml"],
4545
});
4646
});
4747

@@ -51,8 +51,8 @@ describe("findLocaleFiles", () => {
5151
const result = findLocaleFiles("flutter");
5252

5353
expect(result).toEqual({
54-
found: true,
5554
patterns: ["lib/l10n/[locale].arb", "lib/translations/[locale].arb"],
55+
defaultPatterns: ["i18n/[locale].arb"],
5656
});
5757
});
5858

@@ -80,7 +80,6 @@ describe("findLocaleFiles", () => {
8080
const result = findLocaleFiles("json");
8181

8282
expect(result).toEqual({
83-
found: true,
8483
patterns: [
8584
"src/locales/[locale]/messages.json",
8685
"src/i18n/[locale]/strings.json",
@@ -94,17 +93,18 @@ describe("findLocaleFiles", () => {
9493
"bar/[locale]/baz/[locale].json",
9594
"bar/[locale]/[locale].json",
9695
],
96+
defaultPatterns: ["i18n/[locale].json"],
9797
});
9898
});
9999

100-
it("should return default pattern when no files found", () => {
100+
it("should return no patterns when no files found", () => {
101101
vi.mocked(glob.sync).mockReturnValue([]);
102102

103103
const result = findLocaleFiles("json");
104104

105105
expect(result).toEqual({
106-
found: false,
107-
patterns: ["i18n/[locale].json"],
106+
patterns: [],
107+
defaultPatterns: ["i18n/[locale].json"],
108108
});
109109
});
110110

@@ -119,23 +119,23 @@ describe("findLocaleFiles", () => {
119119
const result = findLocaleFiles("xcode-xcstrings");
120120

121121
expect(result).toEqual({
122-
found: true,
123122
patterns: [
124123
"ios/MyApp/Localizable.xcstrings",
125124
"ios/MyApp/Onboarding/Localizable.xcstrings",
126125
"ios/MyApp/xx/Localizable.xcstrings",
127126
],
127+
defaultPatterns: ["Localizable.xcstrings"],
128128
});
129129
});
130130

131-
it("should return default pattern for xcode-xcstrings when no files found", () => {
131+
it("should return no patterns for xcode-xcstrings when no files found", () => {
132132
vi.mocked(glob.sync).mockReturnValue([]);
133133

134134
const result = findLocaleFiles("xcode-xcstrings");
135135

136136
expect(result).toEqual({
137-
found: false,
138-
patterns: ["Localizable.xcstrings"],
137+
patterns: [],
138+
defaultPatterns: ["Localizable.xcstrings"],
139139
});
140140
});
141141

packages/cli/src/cli/utils/find-locale-paths.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,13 @@ function findLocaleFilesWithExtension(ext: string) {
7171

7272
const grouppedFilesAndPatterns = _.groupBy(localeFilesAndPatterns, "pattern");
7373
const patterns = Object.keys(grouppedFilesAndPatterns);
74+
const defaultPatterns = [`i18n/[locale]${ext}`];
7475

7576
if (patterns.length > 0) {
76-
return { found: true, patterns };
77+
return { patterns, defaultPatterns };
7778
}
7879

79-
return { found: false, patterns: [`i18n/[locale]${ext}`] };
80+
return { patterns: [], defaultPatterns };
8081
}
8182

8283
function findLocaleFilesForFilename(fileName: string) {
@@ -91,10 +92,11 @@ function findLocaleFilesForFilename(fileName: string) {
9192
}));
9293
const grouppedFilesAndPatterns = _.groupBy(localeFilesAndPatterns, "pattern");
9394
const patterns = Object.keys(grouppedFilesAndPatterns);
95+
const defaultPatterns = [fileName];
9496

9597
if (patterns.length > 0) {
96-
return { found: true, patterns };
98+
return { patterns, defaultPatterns };
9799
}
98100

99-
return { found: false, patterns: [fileName] };
101+
return { patterns: [], defaultPatterns };
100102
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { checkbox, confirm } from "@inquirer/prompts";
2+
import fs from "fs";
3+
import { Ora } from "ora";
4+
import path from "path";
5+
6+
type Platform = "github" | "bitbucket" | "gitlab";
7+
8+
const platforms: Platform[] = ["github", "bitbucket", "gitlab"];
9+
10+
export default async function initCICD(spinner: Ora) {
11+
const initializers = getPlatformInitializers(spinner);
12+
13+
const init = await confirm({
14+
message: "Would you like to use Lingo.dev in your CI/CD?",
15+
});
16+
17+
if (!init) {
18+
spinner.warn("CI/CD not initialized. To set it up later, see docs: https://docs.lingo.dev/ci-action/overview");
19+
return;
20+
}
21+
22+
const selectedPlatforms: Platform[] = await checkbox({
23+
message: "Please select CI/CD platform(s) you want to use:",
24+
choices: platforms.map((platform) => ({
25+
name: initializers[platform].name,
26+
value: platform,
27+
checked: initializers[platform].isEnabled(),
28+
})),
29+
});
30+
31+
for (const platform of selectedPlatforms) {
32+
await initializers[platform].init();
33+
}
34+
}
35+
36+
function getPlatformInitializers(spinner: Ora) {
37+
return {
38+
github: makeGithubInitializer(spinner),
39+
bitbucket: makeBitbucketInitializer(spinner),
40+
gitlab: makeGitlabInitializer(spinner),
41+
};
42+
}
43+
44+
type PlatformConfig = {
45+
name: string;
46+
checkPath: string;
47+
ciConfigPath: string;
48+
ciConfigContent: string;
49+
};
50+
51+
function makePlatformInitializer(config: PlatformConfig, spinner: Ora) {
52+
return {
53+
name: config.name,
54+
isEnabled: () => {
55+
const filePath = path.join(process.cwd(), config.checkPath);
56+
return fs.existsSync(filePath);
57+
},
58+
init: async () => {
59+
const filePath = path.join(process.cwd(), config.ciConfigPath);
60+
const dirPath = path.dirname(filePath);
61+
if (!fs.existsSync(dirPath)) {
62+
fs.mkdirSync(dirPath, { recursive: true });
63+
}
64+
let canWrite = true;
65+
if (fs.existsSync(filePath)) {
66+
canWrite = await confirm({
67+
message: `File ${filePath} already exists. Do you want to overwrite it?`,
68+
default: false,
69+
});
70+
}
71+
if (canWrite) {
72+
fs.writeFileSync(filePath, config.ciConfigContent);
73+
spinner.succeed(`CI/CD initialized for ${config.name}`);
74+
} else {
75+
spinner.warn(`CI/CD not initialized for ${config.name}`);
76+
}
77+
},
78+
};
79+
}
80+
81+
function makeGithubInitializer(spinner: Ora) {
82+
return makePlatformInitializer(
83+
{
84+
name: "GitHub Action",
85+
checkPath: ".github",
86+
ciConfigPath: ".github/workflows/i18n.yml",
87+
ciConfigContent: `name: Lingo.dev i18n
88+
89+
on:
90+
push:
91+
branches:
92+
- main
93+
94+
permissions:
95+
contents: write
96+
pull-requests: write
97+
98+
jobs:
99+
i18n:
100+
name: Run i18n
101+
runs-on: ubuntu-latest
102+
steps:
103+
- uses: actions/checkout@v4
104+
- uses: lingodotdev/lingo.dev@main
105+
with:
106+
api-key: \${{ secrets.LINGODOTDEV_API_KEY }}
107+
`,
108+
},
109+
spinner,
110+
);
111+
}
112+
113+
function makeBitbucketInitializer(spinner: Ora) {
114+
return makePlatformInitializer(
115+
{
116+
name: "Bitbucket Pipeline",
117+
checkPath: "bitbucket-pipelines.yml",
118+
ciConfigPath: "bitbucket-pipelines.yml",
119+
ciConfigContent: `pipelines:
120+
branches:
121+
main:
122+
- step:
123+
name: Run i18n
124+
script:
125+
- pipe: lingodotdev/lingo.dev:main`,
126+
},
127+
spinner,
128+
);
129+
}
130+
131+
function makeGitlabInitializer(spinner: Ora) {
132+
return makePlatformInitializer(
133+
{
134+
name: "Gitlab CI",
135+
checkPath: ".gitlab-ci.yml",
136+
ciConfigPath: ".gitlab-ci.yml",
137+
ciConfigContent: `lingodotdev:
138+
image: lingodotdev/ci-action:latest
139+
script:
140+
- echo "Done"
141+
`,
142+
},
143+
spinner,
144+
);
145+
}

0 commit comments

Comments
 (0)