@pagopa/dx-cli 0.14.4 → 0.15.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/README.md +3 -0
- package/bin/index.js +103 -28
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -122,10 +122,13 @@ This command will:
|
|
|
122
122
|
|
|
123
123
|
- Check that required tools (e.g., Terraform CLI) are installed
|
|
124
124
|
- Interactively prompt for project metadata (cloud provider, region, environments, cost center, etc.)
|
|
125
|
+
- **Check that the target GitHub repository does not already exist before proceeding**
|
|
125
126
|
- Generate a monorepo structure following PagoPA DevEx guidelines
|
|
126
127
|
- Create a remote GitHub repository using Terraform
|
|
127
128
|
- Push the initial codebase to the newly created repository
|
|
128
129
|
|
|
130
|
+
If the specified GitHub repository already exists, the command will fail early with a clear error message, preventing accidental overwrites.
|
|
131
|
+
|
|
129
132
|
**Example usage:**
|
|
130
133
|
|
|
131
134
|
```bash
|
package/bin/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import "core-js/actual/set/index.js";
|
|
5
5
|
import { configure, getConsoleSink } from "@logtape/logtape";
|
|
6
|
-
import { Octokit as
|
|
6
|
+
import { Octokit as Octokit3 } from "octokit";
|
|
7
7
|
|
|
8
8
|
// src/adapters/codemods/registry.ts
|
|
9
9
|
import { okAsync } from "neverthrow";
|
|
@@ -765,10 +765,38 @@ import loadMonorepoScaffolder, {
|
|
|
765
765
|
import chalk from "chalk";
|
|
766
766
|
import { Command as Command4 } from "commander";
|
|
767
767
|
import { $ as $3 } from "execa";
|
|
768
|
-
import { okAsync as okAsync2, ResultAsync as ResultAsync6 } from "neverthrow";
|
|
768
|
+
import { errAsync, okAsync as okAsync2, ResultAsync as ResultAsync6 } from "neverthrow";
|
|
769
769
|
import * as path from "path";
|
|
770
770
|
import { oraPromise } from "ora";
|
|
771
771
|
|
|
772
|
+
// src/domain/github.ts
|
|
773
|
+
var PullRequest = class {
|
|
774
|
+
constructor(url) {
|
|
775
|
+
this.url = url;
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
var Repository = class {
|
|
779
|
+
constructor(name, owner) {
|
|
780
|
+
this.name = name;
|
|
781
|
+
this.owner = owner;
|
|
782
|
+
}
|
|
783
|
+
get fullName() {
|
|
784
|
+
return `${this.owner}/${this.name}`;
|
|
785
|
+
}
|
|
786
|
+
get ssh() {
|
|
787
|
+
return `git@github.com:${this.owner}/${this.name}.git`;
|
|
788
|
+
}
|
|
789
|
+
get url() {
|
|
790
|
+
return `https://github.com/${this.owner}/${this.name}`;
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
var RepositoryNotFoundError = class extends Error {
|
|
794
|
+
constructor(owner, name) {
|
|
795
|
+
super(`Repository ${owner}/${name} not found`);
|
|
796
|
+
this.name = "RepositoryNotFoundError";
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
|
|
772
800
|
// src/adapters/execa/terraform.ts
|
|
773
801
|
import { $ as $2 } from "execa";
|
|
774
802
|
var tf$ = $2({
|
|
@@ -804,18 +832,6 @@ var decode = (schema) => (data) => ResultAsync5.fromPromise(
|
|
|
804
832
|
);
|
|
805
833
|
|
|
806
834
|
// src/adapters/commander/commands/init.ts
|
|
807
|
-
var Repository = class {
|
|
808
|
-
constructor(name, owner) {
|
|
809
|
-
this.name = name;
|
|
810
|
-
this.owner = owner;
|
|
811
|
-
}
|
|
812
|
-
get ssh() {
|
|
813
|
-
return `git@github.com:${this.owner}/${this.name}.git`;
|
|
814
|
-
}
|
|
815
|
-
get url() {
|
|
816
|
-
return `https://github.com/${this.owner}/${this.name}`;
|
|
817
|
-
}
|
|
818
|
-
};
|
|
819
835
|
var withSpinner = (text, successText, failText, promise) => ResultAsync6.fromPromise(
|
|
820
836
|
oraPromise(promise, {
|
|
821
837
|
failText,
|
|
@@ -827,7 +843,20 @@ var withSpinner = (text, successText, failText, promise) => ResultAsync6.fromPro
|
|
|
827
843
|
return new Error(failText, { cause });
|
|
828
844
|
}
|
|
829
845
|
);
|
|
830
|
-
var validateAnswers = (
|
|
846
|
+
var validateAnswers = (githubService) => (answers) => ResultAsync6.fromPromise(
|
|
847
|
+
githubService.getRepository(answers.repoOwner, answers.repoName),
|
|
848
|
+
(error) => error
|
|
849
|
+
).andThen(
|
|
850
|
+
({ fullName }) => errAsync(new Error(`Repository ${fullName} already exists.`))
|
|
851
|
+
).orElse(
|
|
852
|
+
(error) => error instanceof RepositoryNotFoundError ? (
|
|
853
|
+
// If repository is not found, it's safe to proceed
|
|
854
|
+
okAsync2(answers)
|
|
855
|
+
) : (
|
|
856
|
+
// Otherwise, propagate the error
|
|
857
|
+
errAsync(error)
|
|
858
|
+
)
|
|
859
|
+
).map(() => answers);
|
|
831
860
|
var runGeneratorActions = (generator) => (answers) => withSpinner(
|
|
832
861
|
"Creating workspace files...",
|
|
833
862
|
"Workspace files created successfully!",
|
|
@@ -919,8 +948,8 @@ var initializeGitRepository = (repository) => {
|
|
|
919
948
|
pushToOrigin()
|
|
920
949
|
).map(() => ({ branchName, repository }));
|
|
921
950
|
};
|
|
922
|
-
var handleNewGitHubRepository = (
|
|
923
|
-
(localWorkspace) => createPullRequest(
|
|
951
|
+
var handleNewGitHubRepository = (githubService) => (answers) => createRemoteRepository(answers).andThen(initializeGitRepository).andThen(
|
|
952
|
+
(localWorkspace) => createPullRequest(githubService)(localWorkspace).map((pr) => ({
|
|
924
953
|
pr,
|
|
925
954
|
repository: localWorkspace.repository
|
|
926
955
|
}))
|
|
@@ -936,14 +965,14 @@ var makeInitResult = (answers, { pr, repository }) => {
|
|
|
936
965
|
repository
|
|
937
966
|
};
|
|
938
967
|
};
|
|
939
|
-
var createPullRequest = (
|
|
968
|
+
var createPullRequest = (githubService) => ({
|
|
940
969
|
branchName,
|
|
941
970
|
repository
|
|
942
971
|
}) => withSpinner(
|
|
943
972
|
"Creating Pull Request...",
|
|
944
973
|
"Pull Request created successfully!",
|
|
945
974
|
"Failed to create Pull Request.",
|
|
946
|
-
|
|
975
|
+
githubService.createPullRequest({
|
|
947
976
|
base: "main",
|
|
948
977
|
body: "This PR contains the scaffolded monorepo structure.",
|
|
949
978
|
head: branchName,
|
|
@@ -951,9 +980,9 @@ var createPullRequest = (octokit2) => ({
|
|
|
951
980
|
repo: repository.name,
|
|
952
981
|
title: "Scaffold repository"
|
|
953
982
|
})
|
|
954
|
-
).
|
|
983
|
+
).orElse(() => okAsync2(void 0));
|
|
955
984
|
var makeInitCommand = ({
|
|
956
|
-
|
|
985
|
+
gitHubService: gitHubService2
|
|
957
986
|
}) => new Command4().name("init").description(
|
|
958
987
|
"Command to initialize resources (like projects, subscriptions, ...)"
|
|
959
988
|
).addCommand(
|
|
@@ -961,10 +990,10 @@ var makeInitCommand = ({
|
|
|
961
990
|
await checkPreconditions().andThen(initPlop).andTee(loadMonorepoScaffolder).andThen((plop) => getGenerator(plop)(PLOP_MONOREPO_GENERATOR_NAME)).andThen(
|
|
962
991
|
(generator) => (
|
|
963
992
|
// Ask the user the questions defined in the plop generator
|
|
964
|
-
getPrompts(generator).andThen(decode(answersSchema)).andThen(validateAnswers).andThen(runGeneratorActions(generator))
|
|
993
|
+
getPrompts(generator).andThen(decode(answersSchema)).andThen(validateAnswers(gitHubService2)).andThen(runGeneratorActions(generator))
|
|
965
994
|
)
|
|
966
995
|
).andThen(
|
|
967
|
-
(answers) => handleNewGitHubRepository(
|
|
996
|
+
(answers) => handleNewGitHubRepository(gitHubService2)(answers).map(
|
|
968
997
|
(repoPr) => makeInitResult(answers, repoPr)
|
|
969
998
|
)
|
|
970
999
|
).match(displaySummary, exitWithError(this));
|
|
@@ -1004,7 +1033,7 @@ var makeSavemoneyCommand = () => new Command5("savemoney").description(
|
|
|
1004
1033
|
// src/adapters/commander/index.ts
|
|
1005
1034
|
var makeCli = (deps2, config2, cliDeps) => {
|
|
1006
1035
|
const program2 = new Command6();
|
|
1007
|
-
program2.name("dx").description("The CLI for DX-Platform").version("0.
|
|
1036
|
+
program2.name("dx").description("The CLI for DX-Platform").version("0.15.0");
|
|
1008
1037
|
program2.addCommand(makeDoctorCommand(deps2, config2));
|
|
1009
1038
|
program2.addCommand(makeCodemodCommand(cliDeps));
|
|
1010
1039
|
program2.addCommand(makeInitCommand(deps2));
|
|
@@ -1152,6 +1181,51 @@ var makeRepositoryReader = () => ({
|
|
|
1152
1181
|
readFile
|
|
1153
1182
|
});
|
|
1154
1183
|
|
|
1184
|
+
// src/adapters/octokit/index.ts
|
|
1185
|
+
import { RequestError } from "octokit";
|
|
1186
|
+
var OctokitGitHubService = class {
|
|
1187
|
+
#octokit;
|
|
1188
|
+
constructor(octokit2) {
|
|
1189
|
+
this.#octokit = octokit2;
|
|
1190
|
+
}
|
|
1191
|
+
async createPullRequest(params) {
|
|
1192
|
+
try {
|
|
1193
|
+
const { data } = await this.#octokit.rest.pulls.create({
|
|
1194
|
+
base: params.base,
|
|
1195
|
+
body: params.body,
|
|
1196
|
+
head: params.head,
|
|
1197
|
+
owner: params.owner,
|
|
1198
|
+
repo: params.repo,
|
|
1199
|
+
title: params.title
|
|
1200
|
+
});
|
|
1201
|
+
return new PullRequest(data.html_url);
|
|
1202
|
+
} catch (error) {
|
|
1203
|
+
throw new Error(
|
|
1204
|
+
`Failed to create pull request in ${params.owner}/${params.repo}`,
|
|
1205
|
+
{
|
|
1206
|
+
cause: error
|
|
1207
|
+
}
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
async getRepository(owner, name) {
|
|
1212
|
+
try {
|
|
1213
|
+
const { data } = await this.#octokit.rest.repos.get({
|
|
1214
|
+
owner,
|
|
1215
|
+
repo: name
|
|
1216
|
+
});
|
|
1217
|
+
return new Repository(data.name, data.owner.login);
|
|
1218
|
+
} catch (error) {
|
|
1219
|
+
if (error instanceof RequestError && error.status === 404) {
|
|
1220
|
+
throw new RepositoryNotFoundError(owner, name);
|
|
1221
|
+
}
|
|
1222
|
+
throw new Error(`Failed to fetch repository ${owner}/${name}`, {
|
|
1223
|
+
cause: error
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
|
|
1155
1229
|
// src/config.ts
|
|
1156
1230
|
var getConfig = () => ({
|
|
1157
1231
|
minVersions: {
|
|
@@ -1160,9 +1234,9 @@ var getConfig = () => ({
|
|
|
1160
1234
|
});
|
|
1161
1235
|
|
|
1162
1236
|
// src/use-cases/apply-codemod.ts
|
|
1163
|
-
import { errAsync, okAsync as okAsync4, ResultAsync as ResultAsync9 } from "neverthrow";
|
|
1237
|
+
import { errAsync as errAsync2, okAsync as okAsync4, ResultAsync as ResultAsync9 } from "neverthrow";
|
|
1164
1238
|
var getCodemodById = (registry2, id) => registry2.getById(id).andThen(
|
|
1165
|
-
(codemod) => codemod ? okAsync4(codemod) :
|
|
1239
|
+
(codemod) => codemod ? okAsync4(codemod) : errAsync2(new Error(`Codemod with id ${id} not found`))
|
|
1166
1240
|
);
|
|
1167
1241
|
var safeGetInfo = (getInfo2) => ResultAsync9.fromPromise(
|
|
1168
1242
|
getInfo2(),
|
|
@@ -1203,11 +1277,12 @@ await configure({
|
|
|
1203
1277
|
var repositoryReader = makeRepositoryReader();
|
|
1204
1278
|
var packageJsonReader = makePackageJsonReader();
|
|
1205
1279
|
var validationReporter = makeValidationReporter();
|
|
1206
|
-
var octokit = new
|
|
1280
|
+
var octokit = new Octokit3({
|
|
1207
1281
|
auth: process.env.GITHUB_TOKEN
|
|
1208
1282
|
});
|
|
1283
|
+
var gitHubService = new OctokitGitHubService(octokit);
|
|
1209
1284
|
var deps = {
|
|
1210
|
-
|
|
1285
|
+
gitHubService,
|
|
1211
1286
|
packageJsonReader,
|
|
1212
1287
|
repositoryReader,
|
|
1213
1288
|
validationReporter
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagopa/dx-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A CLI useful to manage DX tools.",
|
|
6
6
|
"repository": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"semver": "^7.7.2",
|
|
34
34
|
"yaml": "^2.8.2",
|
|
35
35
|
"zod": "^4.2.1",
|
|
36
|
-
"@pagopa/
|
|
37
|
-
"@pagopa/
|
|
36
|
+
"@pagopa/dx-savemoney": "^0.1.4",
|
|
37
|
+
"@pagopa/monorepo-generator": "^0.15.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@tsconfig/node22": "22.0.2",
|