@pagopa/dx-cli 0.15.2 → 0.15.3
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/index.js +8 -1280
- package/dist/adapters/azure/__tests__/cloud-account-repository.test.d.ts +1 -0
- package/dist/adapters/azure/__tests__/cloud-account-repository.test.js +95 -0
- package/dist/adapters/azure/__tests__/cloud-account-service.test.d.ts +1 -0
- package/dist/adapters/azure/__tests__/cloud-account-service.test.js +95 -0
- package/dist/adapters/azure/cloud-account-repository.d.ts +12 -0
- package/dist/adapters/azure/cloud-account-repository.js +23 -0
- package/dist/adapters/azure/cloud-account-service.d.ts +22 -0
- package/dist/adapters/azure/cloud-account-service.js +255 -0
- package/dist/adapters/azure/locations.d.ts +7 -0
- package/dist/adapters/azure/locations.js +21 -0
- package/dist/adapters/codemods/__tests__/registry.test.d.ts +1 -0
- package/dist/adapters/codemods/__tests__/registry.test.js +59 -0
- package/dist/adapters/codemods/__tests__/use-azure-appsvc.test.d.ts +1 -0
- package/dist/adapters/codemods/__tests__/use-azure-appsvc.test.js +77 -0
- package/dist/adapters/codemods/__tests__/use-pnpm.test.d.ts +1 -0
- package/dist/adapters/codemods/__tests__/use-pnpm.test.js +148 -0
- package/dist/adapters/codemods/git.d.ts +2 -0
- package/dist/adapters/codemods/git.js +18 -0
- package/dist/adapters/codemods/index.d.ts +3 -0
- package/dist/adapters/codemods/index.js +9 -0
- package/dist/adapters/codemods/registry.d.ts +8 -0
- package/dist/adapters/codemods/registry.js +16 -0
- package/dist/adapters/codemods/update-code-review.d.ts +3 -0
- package/dist/adapters/codemods/update-code-review.js +60 -0
- package/dist/adapters/codemods/use-azure-appsvc.d.ts +3 -0
- package/dist/adapters/codemods/use-azure-appsvc.js +84 -0
- package/dist/adapters/codemods/use-pnpm.d.ts +22 -0
- package/dist/adapters/codemods/use-pnpm.js +214 -0
- package/dist/adapters/codemods/yaml.d.ts +2 -0
- package/dist/adapters/codemods/yaml.js +8 -0
- package/dist/adapters/commander/commands/codemod.d.ts +8 -0
- package/dist/adapters/commander/commands/codemod.js +22 -0
- package/dist/adapters/commander/commands/doctor.d.ts +4 -0
- package/dist/adapters/commander/commands/doctor.js +12 -0
- package/dist/adapters/commander/commands/info.d.ts +3 -0
- package/dist/adapters/commander/commands/info.js +9 -0
- package/dist/adapters/commander/commands/init.d.ts +7 -0
- package/dist/adapters/commander/commands/init.js +126 -0
- package/dist/adapters/commander/commands/savemoney.d.ts +2 -0
- package/dist/adapters/commander/commands/savemoney.js +26 -0
- package/dist/adapters/commander/index.d.ts +7 -0
- package/dist/adapters/commander/index.js +19 -0
- package/dist/adapters/execa/terraform.d.ts +27 -0
- package/dist/adapters/execa/terraform.js +28 -0
- package/dist/adapters/github/__tests__/github-repo.spec.d.ts +1 -0
- package/dist/adapters/github/__tests__/github-repo.spec.js +67 -0
- package/dist/adapters/github/github-repo.d.ts +2 -0
- package/dist/adapters/github/github-repo.js +31 -0
- package/dist/adapters/logtape/validation-reporter.d.ts +2 -0
- package/dist/adapters/logtape/validation-reporter.js +14 -0
- package/dist/adapters/node/__tests__/data.d.ts +18 -0
- package/dist/adapters/node/__tests__/data.js +22 -0
- package/dist/adapters/node/__tests__/package-json.test.d.ts +1 -0
- package/dist/adapters/node/__tests__/package-json.test.js +86 -0
- package/dist/adapters/node/__tests__/repository.test.d.ts +1 -0
- package/dist/adapters/node/__tests__/repository.test.js +77 -0
- package/dist/adapters/node/fs/__tests__/file-reader.test.d.ts +1 -0
- package/dist/adapters/node/fs/__tests__/file-reader.test.js +80 -0
- package/dist/adapters/node/fs/file-reader.d.ts +24 -0
- package/dist/adapters/node/fs/file-reader.js +26 -0
- package/dist/adapters/node/json/__tests__/index.test.d.ts +1 -0
- package/dist/adapters/node/json/__tests__/index.test.js +14 -0
- package/dist/adapters/node/json/index.d.ts +2 -0
- package/dist/adapters/node/json/index.js +2 -0
- package/dist/adapters/node/package-json.d.ts +2 -0
- package/dist/adapters/node/package-json.js +22 -0
- package/dist/adapters/node/release.d.ts +2 -0
- package/dist/adapters/node/release.js +33 -0
- package/dist/adapters/node/repository.d.ts +2 -0
- package/dist/adapters/node/repository.js +47 -0
- package/dist/adapters/octokit/__tests__/index.test.d.ts +1 -0
- package/dist/adapters/octokit/__tests__/index.test.js +197 -0
- package/dist/adapters/octokit/index.d.ts +24 -0
- package/dist/adapters/octokit/index.js +65 -0
- package/dist/adapters/plop/actions/__tests__/init-cloud-accounts.test.d.ts +1 -0
- package/dist/adapters/plop/actions/__tests__/init-cloud-accounts.test.js +115 -0
- package/dist/adapters/plop/actions/__tests__/provision-terraform-backend.test.d.ts +1 -0
- package/dist/adapters/plop/actions/__tests__/provision-terraform-backend.test.js +116 -0
- package/dist/adapters/plop/actions/fetch-github-release.d.ts +12 -0
- package/dist/adapters/plop/actions/fetch-github-release.js +20 -0
- package/dist/adapters/plop/actions/get-node-version.d.ts +2 -0
- package/dist/adapters/plop/actions/get-node-version.js +9 -0
- package/dist/adapters/plop/actions/get-terraform-backend.d.ts +3 -0
- package/dist/adapters/plop/actions/get-terraform-backend.js +17 -0
- package/dist/adapters/plop/actions/init-cloud-accounts.d.ts +5 -0
- package/dist/adapters/plop/actions/init-cloud-accounts.js +13 -0
- package/dist/adapters/plop/actions/provision-terraform-backend.d.ts +10 -0
- package/dist/adapters/plop/actions/provision-terraform-backend.js +16 -0
- package/dist/adapters/plop/actions/semver.d.ts +19 -0
- package/dist/adapters/plop/actions/semver.js +27 -0
- package/dist/adapters/plop/actions/setup-pnpm.d.ts +2 -0
- package/dist/adapters/plop/actions/setup-pnpm.js +26 -0
- package/dist/adapters/plop/generators/environment/__tests__/actions.test.d.ts +2 -0
- package/dist/adapters/plop/generators/environment/__tests__/actions.test.js +55 -0
- package/dist/adapters/plop/generators/environment/actions.d.ts +2 -0
- package/dist/adapters/plop/generators/environment/actions.js +54 -0
- package/dist/adapters/plop/generators/environment/index.d.ts +3 -0
- package/dist/adapters/plop/generators/environment/index.js +19 -0
- package/dist/adapters/plop/generators/environment/prompts.d.ts +66 -0
- package/dist/adapters/plop/generators/environment/prompts.js +166 -0
- package/dist/adapters/plop/generators/monorepo/actions.d.ts +51 -0
- package/dist/adapters/plop/generators/monorepo/actions.js +35 -0
- package/dist/adapters/plop/generators/monorepo/index.d.ts +6 -0
- package/dist/adapters/plop/generators/monorepo/index.js +17 -0
- package/dist/adapters/plop/generators/monorepo/prompts.d.ts +10 -0
- package/dist/adapters/plop/generators/monorepo/prompts.js +31 -0
- package/dist/adapters/plop/helpers/__tests__/resource-prefix.test.d.ts +1 -0
- package/dist/adapters/plop/helpers/__tests__/resource-prefix.test.js +113 -0
- package/dist/adapters/plop/helpers/env-short.d.ts +3 -0
- package/dist/adapters/plop/helpers/env-short.js +9 -0
- package/dist/adapters/plop/helpers/resource-prefix.d.ts +5 -0
- package/dist/adapters/plop/helpers/resource-prefix.js +18 -0
- package/dist/adapters/plop/index.d.ts +8 -0
- package/dist/adapters/plop/index.js +24 -0
- package/dist/adapters/terraform/fmt.d.ts +1 -0
- package/dist/adapters/terraform/fmt.js +17 -0
- package/dist/adapters/yaml/__tests__/index.test.d.ts +1 -0
- package/dist/adapters/yaml/__tests__/index.test.js +53 -0
- package/dist/adapters/yaml/index.d.ts +8 -0
- package/dist/adapters/yaml/index.js +9 -0
- package/dist/adapters/zod/index.d.ts +23 -0
- package/dist/adapters/zod/index.js +22 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +5 -0
- package/dist/domain/__tests__/data.d.ts +17 -0
- package/dist/domain/__tests__/data.js +27 -0
- package/dist/domain/__tests__/environment.test.d.ts +1 -0
- package/dist/domain/__tests__/environment.test.js +282 -0
- package/dist/domain/__tests__/info.test.d.ts +1 -0
- package/dist/domain/__tests__/info.test.js +77 -0
- package/dist/domain/__tests__/package-json.test.d.ts +1 -0
- package/dist/domain/__tests__/package-json.test.js +39 -0
- package/dist/domain/__tests__/repository.test.d.ts +1 -0
- package/dist/domain/__tests__/repository.test.js +101 -0
- package/dist/domain/__tests__/workspace.test.d.ts +1 -0
- package/dist/domain/__tests__/workspace.test.js +57 -0
- package/dist/domain/cloud-account.d.ts +28 -0
- package/dist/domain/cloud-account.js +12 -0
- package/dist/domain/codemod.d.ts +11 -0
- package/dist/domain/codemod.js +1 -0
- package/dist/domain/dependencies.d.ts +10 -0
- package/dist/domain/dependencies.js +1 -0
- package/dist/domain/doctor.d.ts +10 -0
- package/dist/domain/doctor.js +50 -0
- package/dist/domain/environment.d.ts +40 -0
- package/dist/domain/environment.js +57 -0
- package/dist/domain/github-repo.d.ts +6 -0
- package/dist/domain/github-repo.js +8 -0
- package/dist/domain/github.d.ts +37 -0
- package/dist/domain/github.js +29 -0
- package/dist/domain/info.d.ts +11 -0
- package/dist/domain/info.js +52 -0
- package/dist/domain/package-json.d.ts +42 -0
- package/dist/domain/package-json.js +69 -0
- package/dist/domain/remote-backend.d.ts +8 -0
- package/dist/domain/remote-backend.js +9 -0
- package/dist/domain/repository.d.ts +13 -0
- package/dist/domain/repository.js +72 -0
- package/dist/domain/validation.d.ts +16 -0
- package/dist/domain/validation.js +1 -0
- package/dist/domain/workspace.d.ts +9 -0
- package/dist/domain/workspace.js +32 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +35 -0
- package/dist/use-cases/__tests__/apply-codemod.test.d.ts +1 -0
- package/dist/use-cases/__tests__/apply-codemod.test.js +73 -0
- package/dist/use-cases/__tests__/list-codemods.test.d.ts +1 -0
- package/dist/use-cases/__tests__/list-codemods.test.js +38 -0
- package/dist/use-cases/apply-codemod.d.ts +5 -0
- package/dist/use-cases/apply-codemod.js +14 -0
- package/dist/use-cases/list-codemods.d.ts +4 -0
- package/dist/use-cases/list-codemods.js +1 -0
- package/package.json +19 -8
- package/templates/environment/bootstrapper/{{env.name}}/data.tf.hbs +13 -0
- package/templates/environment/bootstrapper/{{env.name}}/main.tf.hbs +70 -0
- package/templates/environment/bootstrapper/{{env.name}}/providers.tf.hbs +26 -0
- package/templates/environment/core/{{env.name}}/main.tf.hbs +21 -0
- package/templates/environment/core/{{env.name}}/outputs.tf.hbs +8 -0
- package/templates/environment/core/{{env.name}}/providers.tf.hbs +21 -0
- package/templates/environment/shared/backend.tf.hbs +14 -0
- package/templates/environment/shared/locals.tf.hbs +26 -0
- package/templates/monorepo/.editorconfig +8 -0
- package/templates/monorepo/.node-version.hbs +1 -0
- package/templates/monorepo/.pre-commit-config.yaml.hbs +34 -0
- package/templates/monorepo/.prettierignore +5 -0
- package/templates/monorepo/.terraform-version.hbs +1 -0
- package/templates/monorepo/.trivyignore +16 -0
- package/templates/monorepo/README.md.hbs +163 -0
- package/templates/monorepo/infra/repository/main.tf.hbs +11 -0
- package/templates/monorepo/infra/repository/outputs.tf.hbs +11 -0
- package/templates/monorepo/infra/repository/providers.tf.hbs +13 -0
- package/templates/monorepo/package.json.hbs +7 -0
- package/templates/monorepo/pnpm-workspace.yaml +7 -0
- package/templates/monorepo/turbo.json +29 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { err, ok } from "neverthrow";
|
|
2
|
+
import { RequestError } from "octokit";
|
|
3
|
+
import { SemVer } from "semver";
|
|
4
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { mockDeep, mockReset } from "vitest-mock-extended";
|
|
6
|
+
import { PullRequest, Repository, RepositoryNotFoundError, } from "../../../domain/github.js";
|
|
7
|
+
import { fetchLatestRelease, fetchLatestTag } from "../index.js";
|
|
8
|
+
import { OctokitGitHubService } from "../index.js";
|
|
9
|
+
const makeEnv = () => {
|
|
10
|
+
const mockOctokit = mockDeep();
|
|
11
|
+
const githubService = new OctokitGitHubService(mockOctokit);
|
|
12
|
+
return {
|
|
13
|
+
githubService,
|
|
14
|
+
mockOctokit,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
describe("OctokitGitHubService", () => {
|
|
18
|
+
describe("getRepository", () => {
|
|
19
|
+
it("should return a Repository when the repository exists", async () => {
|
|
20
|
+
const { githubService, mockOctokit } = makeEnv();
|
|
21
|
+
const owner = "pagopa";
|
|
22
|
+
const name = "dx";
|
|
23
|
+
const mockResponse = {
|
|
24
|
+
data: {
|
|
25
|
+
full_name: "pagopa/dx",
|
|
26
|
+
name: "dx",
|
|
27
|
+
owner: {
|
|
28
|
+
login: "pagopa",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
mockOctokit.rest.repos.get.mockResolvedValue(mockResponse);
|
|
33
|
+
const result = await githubService.getRepository(owner, name);
|
|
34
|
+
expect(result).toBeInstanceOf(Repository);
|
|
35
|
+
expect(result.name).toBe("dx");
|
|
36
|
+
expect(result.owner).toBe("pagopa");
|
|
37
|
+
expect(result.fullName).toBe("pagopa/dx");
|
|
38
|
+
expect(result.url).toBe("https://github.com/pagopa/dx");
|
|
39
|
+
expect(result.ssh).toBe("git@github.com:pagopa/dx.git");
|
|
40
|
+
expect(mockOctokit.rest.repos.get).toHaveBeenCalledWith({
|
|
41
|
+
owner,
|
|
42
|
+
repo: name,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
it("should throw an error when the repository does not exist (404)", async () => {
|
|
46
|
+
const { githubService, mockOctokit } = makeEnv();
|
|
47
|
+
const owner = "pagopa";
|
|
48
|
+
const name = "non-existent";
|
|
49
|
+
const error = new RequestError("Not Found", 404, {
|
|
50
|
+
request: {
|
|
51
|
+
headers: {},
|
|
52
|
+
method: "GET",
|
|
53
|
+
url: "https://api.github.com/repos/pagopa/non-existent",
|
|
54
|
+
},
|
|
55
|
+
response: {
|
|
56
|
+
data: { message: "Not Found" },
|
|
57
|
+
headers: {},
|
|
58
|
+
status: 404,
|
|
59
|
+
url: "https://api.github.com/repos/pagopa/non-existent",
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
mockOctokit.rest.repos.get.mockRejectedValue(error);
|
|
63
|
+
await expect(githubService.getRepository(owner, name)).rejects.toThrow(RepositoryNotFoundError);
|
|
64
|
+
await expect(githubService.getRepository(owner, name)).rejects.toThrowError("Repository pagopa/non-existent not found");
|
|
65
|
+
});
|
|
66
|
+
it("should throw an error when the API call fails", async () => {
|
|
67
|
+
const { githubService, mockOctokit } = makeEnv();
|
|
68
|
+
const owner = "pagopa";
|
|
69
|
+
const name = "dx";
|
|
70
|
+
const error = new RequestError("API Error", 500, {
|
|
71
|
+
request: {
|
|
72
|
+
headers: {},
|
|
73
|
+
method: "GET",
|
|
74
|
+
url: "https://api.github.com/repos/pagopa/dx",
|
|
75
|
+
},
|
|
76
|
+
response: {
|
|
77
|
+
data: { message: "API Error" },
|
|
78
|
+
headers: {},
|
|
79
|
+
status: 500,
|
|
80
|
+
url: "https://api.github.com/repos/pagopa/dx",
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
mockOctokit.rest.repos.get.mockRejectedValue(error);
|
|
84
|
+
await expect(githubService.getRepository(owner, name)).rejects.toThrowError("Failed to fetch repository pagopa/dx");
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("createPullRequest", () => {
|
|
88
|
+
it("should return a PullRequest when creation succeeds", async () => {
|
|
89
|
+
const { githubService, mockOctokit } = makeEnv();
|
|
90
|
+
const params = {
|
|
91
|
+
base: "main",
|
|
92
|
+
body: "This is a test PR",
|
|
93
|
+
head: "feature-branch",
|
|
94
|
+
owner: "pagopa",
|
|
95
|
+
repo: "dx",
|
|
96
|
+
title: "Test Pull Request",
|
|
97
|
+
};
|
|
98
|
+
const mockResponse = {
|
|
99
|
+
data: {
|
|
100
|
+
html_url: "https://github.com/pagopa/dx/pull/123",
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
mockOctokit.rest.pulls.create.mockResolvedValue(mockResponse);
|
|
104
|
+
const result = await githubService.createPullRequest(params);
|
|
105
|
+
expect(result).toBeInstanceOf(PullRequest);
|
|
106
|
+
expect(result.url).toBe("https://github.com/pagopa/dx/pull/123");
|
|
107
|
+
expect(mockOctokit.rest.pulls.create).toHaveBeenCalledWith({
|
|
108
|
+
base: params.base,
|
|
109
|
+
body: params.body,
|
|
110
|
+
head: params.head,
|
|
111
|
+
owner: params.owner,
|
|
112
|
+
repo: params.repo,
|
|
113
|
+
title: params.title,
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
it("should throw an error when PR creation fails", async () => {
|
|
117
|
+
const { githubService, mockOctokit } = makeEnv();
|
|
118
|
+
const params = {
|
|
119
|
+
base: "main",
|
|
120
|
+
body: "This is a test PR",
|
|
121
|
+
head: "feature-branch",
|
|
122
|
+
owner: "pagopa",
|
|
123
|
+
repo: "dx",
|
|
124
|
+
title: "Test Pull Request",
|
|
125
|
+
};
|
|
126
|
+
const error = new RequestError("Validation Failed", 422, {
|
|
127
|
+
request: {
|
|
128
|
+
headers: {},
|
|
129
|
+
method: "POST",
|
|
130
|
+
url: "https://api.github.com/repos/pagopa/dx/pulls",
|
|
131
|
+
},
|
|
132
|
+
response: {
|
|
133
|
+
data: { message: "Validation Failed" },
|
|
134
|
+
headers: {},
|
|
135
|
+
status: 422,
|
|
136
|
+
url: "https://api.github.com/repos/pagopa/dx/pulls",
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
mockOctokit.rest.pulls.create.mockRejectedValue(error);
|
|
140
|
+
await expect(githubService.createPullRequest(params)).rejects.toThrowError("Failed to create pull request in pagopa/dx");
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe("octokit adapter", () => {
|
|
145
|
+
const owner = "test-owner";
|
|
146
|
+
const repo = "test-repo";
|
|
147
|
+
const client = mockDeep();
|
|
148
|
+
beforeEach(() => {
|
|
149
|
+
mockReset(client);
|
|
150
|
+
});
|
|
151
|
+
describe("fetchLatestTag", () => {
|
|
152
|
+
it("should return the highest valid semver tag", async () => {
|
|
153
|
+
client.request.mockResolvedValueOnce({
|
|
154
|
+
data: [
|
|
155
|
+
{ name: "v1.0.0" },
|
|
156
|
+
{ name: "2.0.0-beta.1" },
|
|
157
|
+
{ name: "2.0.0" },
|
|
158
|
+
{ name: "not-a-version" },
|
|
159
|
+
],
|
|
160
|
+
headers: {},
|
|
161
|
+
status: 200,
|
|
162
|
+
url: "",
|
|
163
|
+
});
|
|
164
|
+
const result = await fetchLatestTag({ client, owner, repo });
|
|
165
|
+
expect(result).toStrictEqual(ok(new SemVer("2.0.0")));
|
|
166
|
+
expect(client.request).toHaveBeenCalledWith("GET /repos/{owner}/{repo}/tags", { owner, repo });
|
|
167
|
+
});
|
|
168
|
+
it("returns null if there are no valid semver tags", async () => {
|
|
169
|
+
client.request.mockResolvedValueOnce({
|
|
170
|
+
data: [{ name: "foo" }, { name: "bar" }],
|
|
171
|
+
headers: {},
|
|
172
|
+
status: 200,
|
|
173
|
+
url: "",
|
|
174
|
+
});
|
|
175
|
+
const result = await fetchLatestTag({ client, owner, repo });
|
|
176
|
+
expect(result).toStrictEqual(ok(null));
|
|
177
|
+
});
|
|
178
|
+
it("should return an error when client promise fails", async () => {
|
|
179
|
+
client.request.mockRejectedValueOnce(new Error("an error"));
|
|
180
|
+
const result = await fetchLatestTag({ client, owner, repo });
|
|
181
|
+
expect(result).toStrictEqual(err(new Error("Failed to fetch tags for test-owner/test-repo")));
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
describe("fetchLatestRelease", () => {
|
|
185
|
+
it("should return parsed semver for the latest release tag", async () => {
|
|
186
|
+
client.request.mockResolvedValueOnce({
|
|
187
|
+
data: { tag_name: "v1.2.3" },
|
|
188
|
+
headers: {},
|
|
189
|
+
status: 200,
|
|
190
|
+
url: "",
|
|
191
|
+
});
|
|
192
|
+
const result = await fetchLatestRelease({ client, owner, repo });
|
|
193
|
+
expect(result).toStrictEqual(ok(new SemVer("v1.2.3")));
|
|
194
|
+
expect(client.request).toHaveBeenCalledWith("GET /repos/{owner}/{repo}/releases/latest", { owner, repo });
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ResultAsync } from "neverthrow";
|
|
2
|
+
import { Octokit } from "octokit";
|
|
3
|
+
import { GitHubService, PullRequest, Repository } from "../../domain/github.js";
|
|
4
|
+
type GitHubReleaseParam = {
|
|
5
|
+
client: Octokit;
|
|
6
|
+
owner: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
};
|
|
9
|
+
export declare class OctokitGitHubService implements GitHubService {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(octokit: Octokit);
|
|
12
|
+
createPullRequest(params: {
|
|
13
|
+
base: string;
|
|
14
|
+
body: string;
|
|
15
|
+
head: string;
|
|
16
|
+
owner: string;
|
|
17
|
+
repo: string;
|
|
18
|
+
title: string;
|
|
19
|
+
}): Promise<PullRequest>;
|
|
20
|
+
getRepository(owner: string, name: string): Promise<Repository>;
|
|
21
|
+
}
|
|
22
|
+
export declare const fetchLatestTag: ({ client, owner, repo }: GitHubReleaseParam) => ResultAsync<import("semver").SemVer | null, Error>;
|
|
23
|
+
export declare const fetchLatestRelease: ({ client, owner, repo, }: GitHubReleaseParam) => ResultAsync<import("semver").SemVer | null, Error>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ResultAsync } from "neverthrow";
|
|
2
|
+
import { RequestError } from "octokit";
|
|
3
|
+
import semverParse from "semver/functions/parse.js";
|
|
4
|
+
import semverSort from "semver/functions/sort.js";
|
|
5
|
+
import { PullRequest, Repository, RepositoryNotFoundError, } from "../../domain/github.js";
|
|
6
|
+
export class OctokitGitHubService {
|
|
7
|
+
#octokit;
|
|
8
|
+
constructor(octokit) {
|
|
9
|
+
this.#octokit = octokit;
|
|
10
|
+
}
|
|
11
|
+
async createPullRequest(params) {
|
|
12
|
+
try {
|
|
13
|
+
const { data } = await this.#octokit.rest.pulls.create({
|
|
14
|
+
base: params.base,
|
|
15
|
+
body: params.body,
|
|
16
|
+
head: params.head,
|
|
17
|
+
owner: params.owner,
|
|
18
|
+
repo: params.repo,
|
|
19
|
+
title: params.title,
|
|
20
|
+
});
|
|
21
|
+
return new PullRequest(data.html_url);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new Error(`Failed to create pull request in ${params.owner}/${params.repo}`, {
|
|
25
|
+
cause: error,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async getRepository(owner, name) {
|
|
30
|
+
try {
|
|
31
|
+
const { data } = await this.#octokit.rest.repos.get({
|
|
32
|
+
owner,
|
|
33
|
+
repo: name,
|
|
34
|
+
});
|
|
35
|
+
return new Repository(data.name, data.owner.login);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error instanceof RequestError && error.status === 404) {
|
|
39
|
+
throw new RepositoryNotFoundError(owner, name);
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`Failed to fetch repository ${owner}/${name}`, {
|
|
42
|
+
cause: error,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export const fetchLatestTag = ({ client, owner, repo }) => ResultAsync.fromPromise(
|
|
48
|
+
// Get repository tags
|
|
49
|
+
client.request("GET /repos/{owner}/{repo}/tags", {
|
|
50
|
+
owner,
|
|
51
|
+
repo,
|
|
52
|
+
}), () => new Error(`Failed to fetch tags for ${owner}/${repo}`))
|
|
53
|
+
.map(({ data }) => data.map(({ name }) => name))
|
|
54
|
+
// Filter out tags that are not valid semver
|
|
55
|
+
.map((tags) => tags.map((tag) => semverParse(tag)).filter((tag) => tag !== null))
|
|
56
|
+
// Sort tags in ascending order
|
|
57
|
+
.map(semverSort)
|
|
58
|
+
// Get the latest tag
|
|
59
|
+
.map((tags) => tags.pop() ?? null);
|
|
60
|
+
export const fetchLatestRelease = ({ client, owner, repo, }) => ResultAsync.fromPromise(
|
|
61
|
+
// Get the latest release for a repository
|
|
62
|
+
client.request("GET /repos/{owner}/{repo}/releases/latest", {
|
|
63
|
+
owner,
|
|
64
|
+
repo,
|
|
65
|
+
}), () => new Error(`Failed to fetch latest release for ${owner}/${repo}`)).map(({ data }) => semverParse(data.tag_name));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { initCloudAccounts } from "../init-cloud-accounts.js";
|
|
3
|
+
const createMockCloudAccountService = (overrides = {}) => ({
|
|
4
|
+
getTerraformBackend: vi.fn().mockResolvedValue(undefined),
|
|
5
|
+
hasUserPermissionToInitialize: vi.fn().mockResolvedValue(true),
|
|
6
|
+
initialize: vi.fn().mockResolvedValue(undefined),
|
|
7
|
+
isInitialized: vi.fn().mockResolvedValue(true),
|
|
8
|
+
provisionTerraformBackend: vi.fn().mockResolvedValue(undefined),
|
|
9
|
+
...overrides,
|
|
10
|
+
});
|
|
11
|
+
const createMockCloudAccount = (overrides = {}) => ({
|
|
12
|
+
csp: "azure",
|
|
13
|
+
defaultLocation: "westeurope",
|
|
14
|
+
displayName: "Test-Account",
|
|
15
|
+
id: "test-subscription-id",
|
|
16
|
+
...overrides,
|
|
17
|
+
});
|
|
18
|
+
const createMockPayload = (overrides = {}) => ({
|
|
19
|
+
env: {
|
|
20
|
+
cloudAccounts: [createMockCloudAccount()],
|
|
21
|
+
name: "dev",
|
|
22
|
+
prefix: "dx",
|
|
23
|
+
},
|
|
24
|
+
github: {
|
|
25
|
+
owner: "pagopa",
|
|
26
|
+
repo: "dx",
|
|
27
|
+
},
|
|
28
|
+
init: {
|
|
29
|
+
cloudAccountsToInitialize: [],
|
|
30
|
+
terraformBackend: {
|
|
31
|
+
cloudAccount: createMockCloudAccount(),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
tags: {},
|
|
35
|
+
workspace: {
|
|
36
|
+
domain: "test",
|
|
37
|
+
},
|
|
38
|
+
...overrides,
|
|
39
|
+
});
|
|
40
|
+
describe("initCloudAccounts", () => {
|
|
41
|
+
it("should initialize all cloud accounts in cloudAccountsToInitialize", async () => {
|
|
42
|
+
const cloudAccount1 = createMockCloudAccount({ id: "account-1" });
|
|
43
|
+
const cloudAccount2 = createMockCloudAccount({ id: "account-2" });
|
|
44
|
+
const initializeMock = vi.fn().mockResolvedValue(undefined);
|
|
45
|
+
const mockService = createMockCloudAccountService({
|
|
46
|
+
initialize: initializeMock,
|
|
47
|
+
});
|
|
48
|
+
const payload = createMockPayload({
|
|
49
|
+
env: {
|
|
50
|
+
cloudAccounts: [],
|
|
51
|
+
name: "prod",
|
|
52
|
+
prefix: "io",
|
|
53
|
+
},
|
|
54
|
+
init: {
|
|
55
|
+
cloudAccountsToInitialize: [cloudAccount1, cloudAccount2],
|
|
56
|
+
terraformBackend: {
|
|
57
|
+
cloudAccount: createMockCloudAccount(),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
await initCloudAccounts(payload, mockService);
|
|
62
|
+
expect(initializeMock).toHaveBeenCalledTimes(2);
|
|
63
|
+
expect(initializeMock).toHaveBeenCalledWith(cloudAccount1, expect.objectContaining({ name: "prod", prefix: "io" }), {});
|
|
64
|
+
expect(initializeMock).toHaveBeenCalledWith(cloudAccount2, expect.objectContaining({ name: "prod", prefix: "io" }), {});
|
|
65
|
+
});
|
|
66
|
+
it("should not call initialize when cloudAccountsToInitialize is empty", async () => {
|
|
67
|
+
const initializeMock = vi.fn().mockResolvedValue(undefined);
|
|
68
|
+
const mockService = createMockCloudAccountService({
|
|
69
|
+
initialize: initializeMock,
|
|
70
|
+
});
|
|
71
|
+
const payload = createMockPayload({
|
|
72
|
+
init: {
|
|
73
|
+
cloudAccountsToInitialize: [],
|
|
74
|
+
terraformBackend: {
|
|
75
|
+
cloudAccount: createMockCloudAccount(),
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
await initCloudAccounts(payload, mockService);
|
|
80
|
+
expect(initializeMock).not.toHaveBeenCalled();
|
|
81
|
+
});
|
|
82
|
+
it("should not call initialize when payload.init is undefined", async () => {
|
|
83
|
+
const initializeMock = vi.fn().mockResolvedValue(undefined);
|
|
84
|
+
const mockService = createMockCloudAccountService({
|
|
85
|
+
initialize: initializeMock,
|
|
86
|
+
});
|
|
87
|
+
const payload = createMockPayload({
|
|
88
|
+
init: undefined,
|
|
89
|
+
});
|
|
90
|
+
await initCloudAccounts(payload, mockService);
|
|
91
|
+
expect(initializeMock).not.toHaveBeenCalled();
|
|
92
|
+
});
|
|
93
|
+
it("should use prefix and environment name from payload.env", async () => {
|
|
94
|
+
const cloudAccount = createMockCloudAccount();
|
|
95
|
+
const initializeMock = vi.fn().mockResolvedValue(undefined);
|
|
96
|
+
const mockService = createMockCloudAccountService({
|
|
97
|
+
initialize: initializeMock,
|
|
98
|
+
});
|
|
99
|
+
const payload = createMockPayload({
|
|
100
|
+
env: {
|
|
101
|
+
cloudAccounts: [],
|
|
102
|
+
name: "uat",
|
|
103
|
+
prefix: "pagopa",
|
|
104
|
+
},
|
|
105
|
+
init: {
|
|
106
|
+
cloudAccountsToInitialize: [cloudAccount],
|
|
107
|
+
terraformBackend: {
|
|
108
|
+
cloudAccount: createMockCloudAccount(),
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
await initCloudAccounts(payload, mockService);
|
|
113
|
+
expect(initializeMock).toHaveBeenCalledWith(cloudAccount, expect.objectContaining({ name: "uat", prefix: "pagopa" }), {});
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { provisionTerraformBackend } from "../provision-terraform-backend.js";
|
|
3
|
+
const createMockCloudAccountService = (overrides = {}) => ({
|
|
4
|
+
getTerraformBackend: vi.fn().mockResolvedValue(undefined),
|
|
5
|
+
hasUserPermissionToInitialize: vi.fn().mockResolvedValue(true),
|
|
6
|
+
initialize: vi.fn().mockResolvedValue(undefined),
|
|
7
|
+
isInitialized: vi.fn().mockResolvedValue(true),
|
|
8
|
+
provisionTerraformBackend: vi.fn().mockResolvedValue(undefined),
|
|
9
|
+
...overrides,
|
|
10
|
+
});
|
|
11
|
+
const createMockCloudAccount = (overrides = {}) => ({
|
|
12
|
+
csp: "azure",
|
|
13
|
+
defaultLocation: "westeurope",
|
|
14
|
+
displayName: "Test-Account",
|
|
15
|
+
id: "test-subscription-id",
|
|
16
|
+
...overrides,
|
|
17
|
+
});
|
|
18
|
+
const createMockTerraformBackend = (overrides = {}) => ({
|
|
19
|
+
resourceGroupName: "dx-d-itn-tf-rg",
|
|
20
|
+
storageAccountName: "dxditntfst",
|
|
21
|
+
subscriptionId: "00000000-0000-0000-0000-000000000000",
|
|
22
|
+
type: "azurerm",
|
|
23
|
+
...overrides,
|
|
24
|
+
});
|
|
25
|
+
const createMockPayload = (overrides = {}) => ({
|
|
26
|
+
env: {
|
|
27
|
+
cloudAccounts: [createMockCloudAccount()],
|
|
28
|
+
name: "dev",
|
|
29
|
+
prefix: "dx",
|
|
30
|
+
},
|
|
31
|
+
github: {
|
|
32
|
+
owner: "pagopa",
|
|
33
|
+
repo: "dx",
|
|
34
|
+
},
|
|
35
|
+
init: {
|
|
36
|
+
cloudAccountsToInitialize: [],
|
|
37
|
+
terraformBackend: {
|
|
38
|
+
cloudAccount: createMockCloudAccount(),
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
tags: {},
|
|
42
|
+
workspace: {
|
|
43
|
+
domain: "test",
|
|
44
|
+
},
|
|
45
|
+
...overrides,
|
|
46
|
+
});
|
|
47
|
+
describe("provisionTerraformBackend", () => {
|
|
48
|
+
it("should provision terraform backend and return it", async () => {
|
|
49
|
+
const mockBackend = createMockTerraformBackend();
|
|
50
|
+
const provisionMock = vi.fn().mockResolvedValue(mockBackend);
|
|
51
|
+
const mockService = createMockCloudAccountService({
|
|
52
|
+
provisionTerraformBackend: provisionMock,
|
|
53
|
+
});
|
|
54
|
+
const payload = createMockPayload();
|
|
55
|
+
const result = await provisionTerraformBackend(payload, mockService);
|
|
56
|
+
expect(result).toEqual(mockBackend);
|
|
57
|
+
});
|
|
58
|
+
it("should call provisionTerraformBackend with correct parameters", async () => {
|
|
59
|
+
const cloudAccount = createMockCloudAccount({ id: "my-subscription" });
|
|
60
|
+
const provisionMock = vi
|
|
61
|
+
.fn()
|
|
62
|
+
.mockResolvedValue(createMockTerraformBackend());
|
|
63
|
+
const mockService = createMockCloudAccountService({
|
|
64
|
+
provisionTerraformBackend: provisionMock,
|
|
65
|
+
});
|
|
66
|
+
const payload = createMockPayload({
|
|
67
|
+
env: {
|
|
68
|
+
cloudAccounts: [cloudAccount],
|
|
69
|
+
name: "prod",
|
|
70
|
+
prefix: "io",
|
|
71
|
+
},
|
|
72
|
+
init: {
|
|
73
|
+
cloudAccountsToInitialize: [],
|
|
74
|
+
terraformBackend: {
|
|
75
|
+
cloudAccount,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
await provisionTerraformBackend(payload, mockService);
|
|
80
|
+
expect(provisionMock).toHaveBeenCalledWith(cloudAccount, expect.objectContaining({ name: "prod", prefix: "io" }), {});
|
|
81
|
+
});
|
|
82
|
+
it("should throw an error when payload.init is undefined", async () => {
|
|
83
|
+
const mockService = createMockCloudAccountService();
|
|
84
|
+
const payload = createMockPayload({
|
|
85
|
+
init: undefined,
|
|
86
|
+
});
|
|
87
|
+
await expect(provisionTerraformBackend(payload, mockService)).rejects.toThrow("This action requires initialization data in the payload");
|
|
88
|
+
});
|
|
89
|
+
it("should use the terraformBackend cloudAccount from init", async () => {
|
|
90
|
+
const envCloudAccount = createMockCloudAccount({ id: "env-account" });
|
|
91
|
+
const backendCloudAccount = createMockCloudAccount({
|
|
92
|
+
id: "backend-account",
|
|
93
|
+
});
|
|
94
|
+
const provisionMock = vi
|
|
95
|
+
.fn()
|
|
96
|
+
.mockResolvedValue(createMockTerraformBackend());
|
|
97
|
+
const mockService = createMockCloudAccountService({
|
|
98
|
+
provisionTerraformBackend: provisionMock,
|
|
99
|
+
});
|
|
100
|
+
const payload = createMockPayload({
|
|
101
|
+
env: {
|
|
102
|
+
cloudAccounts: [envCloudAccount],
|
|
103
|
+
name: "uat",
|
|
104
|
+
prefix: "dx",
|
|
105
|
+
},
|
|
106
|
+
init: {
|
|
107
|
+
cloudAccountsToInitialize: [],
|
|
108
|
+
terraformBackend: {
|
|
109
|
+
cloudAccount: backendCloudAccount,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
await provisionTerraformBackend(payload, mockService);
|
|
114
|
+
expect(provisionMock).toHaveBeenCalledWith(backendCloudAccount, expect.objectContaining({ name: "uat", prefix: "dx" }), {});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type NodePlopAPI } from "node-plop";
|
|
2
|
+
import { Octokit } from "octokit";
|
|
3
|
+
import { z } from "zod/v4";
|
|
4
|
+
export declare const semverFetchOptionsSchema: z.ZodObject<{
|
|
5
|
+
repository: z.ZodObject<{
|
|
6
|
+
name: z.ZodString;
|
|
7
|
+
owner: z.ZodString;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
resultKey: z.ZodString;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type SemverFetchOptions = z.infer<typeof semverFetchOptionsSchema>;
|
|
12
|
+
export default function (plop: NodePlopAPI, octokit: Octokit): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import { fetchLatestRelease } from "../../octokit/index.js";
|
|
3
|
+
import { fetchLatestSemver } from "./semver.js";
|
|
4
|
+
export const semverFetchOptionsSchema = z.object({
|
|
5
|
+
repository: z.object({
|
|
6
|
+
name: z.string(),
|
|
7
|
+
owner: z.string(),
|
|
8
|
+
}),
|
|
9
|
+
resultKey: z.string(),
|
|
10
|
+
});
|
|
11
|
+
export default function (plop, octokit) {
|
|
12
|
+
plop.setActionType("fetchGithubRelease", async (data, ctx) => {
|
|
13
|
+
const { repository, resultKey } = semverFetchOptionsSchema.parse(ctx);
|
|
14
|
+
return fetchLatestSemver(() => fetchLatestRelease({
|
|
15
|
+
client: octokit,
|
|
16
|
+
owner: repository.owner,
|
|
17
|
+
repo: repository.name,
|
|
18
|
+
}), data, resultKey);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ResultAsync } from "neverthrow";
|
|
2
|
+
import { getLatestByCodename } from "../../node/release.js";
|
|
3
|
+
import { fetchLatestSemver } from "./semver.js";
|
|
4
|
+
const fetchNodeVersion = () => ResultAsync.fromPromise(
|
|
5
|
+
// Jod is the codename for Node.js 22 LTS
|
|
6
|
+
getLatestByCodename("Jod"), (e) => new Error("Failed to fetch Node.js releases", { cause: e }));
|
|
7
|
+
export default function (plop) {
|
|
8
|
+
plop.setActionType("getNodeVersion", async (data) => fetchLatestSemver(fetchNodeVersion, data, "nodeVersion"));
|
|
9
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getLogger } from "@logtape/logtape";
|
|
2
|
+
import { getTerraformBackend } from "../../../domain/environment.js";
|
|
3
|
+
import { payloadSchema } from "../generators/environment/prompts.js";
|
|
4
|
+
export default function (plop, cloudAccountService) {
|
|
5
|
+
plop.setActionType("getTerraformBackend", async (data) => {
|
|
6
|
+
const logger = getLogger(["gen", "env"]);
|
|
7
|
+
if (data.terraform?.backend) {
|
|
8
|
+
return "Terraform Backend Retrieved";
|
|
9
|
+
}
|
|
10
|
+
const payload = payloadSchema.parse(data);
|
|
11
|
+
const backend = await getTerraformBackend(cloudAccountService, payload.env);
|
|
12
|
+
logger.debug("Retrieved terraform backend {backend}", { backend });
|
|
13
|
+
data.terraform ??= {};
|
|
14
|
+
data.terraform.backend = backend;
|
|
15
|
+
return "Terraform Backend Retrieved";
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type NodePlopAPI } from "node-plop";
|
|
2
|
+
import { CloudAccountService } from "../../../domain/cloud-account.js";
|
|
3
|
+
import { type Payload } from "../generators/environment/prompts.js";
|
|
4
|
+
export declare const initCloudAccounts: (payload: Payload, cloudAccountService: CloudAccountService) => Promise<void>;
|
|
5
|
+
export default function (plop: NodePlopAPI, cloudAccountService: CloudAccountService): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { payloadSchema, } from "../generators/environment/prompts.js";
|
|
2
|
+
export const initCloudAccounts = async (payload, cloudAccountService) => {
|
|
3
|
+
if (payload.init) {
|
|
4
|
+
await Promise.all(payload.init.cloudAccountsToInitialize.map((cloudAccount) => cloudAccountService.initialize(cloudAccount, payload.env, payload.tags)));
|
|
5
|
+
}
|
|
6
|
+
};
|
|
7
|
+
export default function (plop, cloudAccountService) {
|
|
8
|
+
plop.setActionType("initCloudAccounts", async (data) => {
|
|
9
|
+
const payload = payloadSchema.parse(data);
|
|
10
|
+
await initCloudAccounts(payload, cloudAccountService);
|
|
11
|
+
return "Cloud Accounts Initialized";
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type NodePlopAPI } from "node-plop";
|
|
2
|
+
import { CloudAccountService } from "../../../domain/cloud-account.js";
|
|
3
|
+
import { type Payload } from "../generators/environment/prompts.js";
|
|
4
|
+
export declare const provisionTerraformBackend: (payload: Payload, cloudAccountService: CloudAccountService) => Promise<{
|
|
5
|
+
resourceGroupName: string;
|
|
6
|
+
storageAccountName: string;
|
|
7
|
+
subscriptionId: string;
|
|
8
|
+
type: "azurerm";
|
|
9
|
+
}>;
|
|
10
|
+
export default function (plop: NodePlopAPI, cloudAccountService: CloudAccountService): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as assert from "node:assert/strict";
|
|
2
|
+
import { payloadSchema, } from "../generators/environment/prompts.js";
|
|
3
|
+
export const provisionTerraformBackend = async (payload, cloudAccountService) => {
|
|
4
|
+
assert.ok(payload.init, "This action requires initialization data in the payload");
|
|
5
|
+
assert.ok(payload.init.terraformBackend, "This action requires terraformBackend data in the payload");
|
|
6
|
+
const terraformBackend = await cloudAccountService.provisionTerraformBackend(payload.init.terraformBackend.cloudAccount, payload.env, payload.tags);
|
|
7
|
+
return terraformBackend;
|
|
8
|
+
};
|
|
9
|
+
export default function (plop, cloudAccountService) {
|
|
10
|
+
plop.setActionType("provisionTerraformBackend", async (data) => {
|
|
11
|
+
const payload = payloadSchema.parse(data);
|
|
12
|
+
data.terraform ??= {};
|
|
13
|
+
data.terraform.backend = await provisionTerraformBackend(payload, cloudAccountService);
|
|
14
|
+
return "Terraform Backend Provisioned";
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ResultAsync } from "neverthrow";
|
|
2
|
+
import { SemVer } from "semver";
|
|
3
|
+
export type FetchSemverFn = () => ResultAsync<null | SemVer, Error>;
|
|
4
|
+
/**
|
|
5
|
+
* Fetches the latest semantic version using the provided fetch function and writes
|
|
6
|
+
* a formatted version string into the given `answers` object under `answerKey`.
|
|
7
|
+
*
|
|
8
|
+
* @param fetchSemverFn - A zero-arg function that returns a `ResultAsync` resolving
|
|
9
|
+
* to a `SemVer` (or `null`) or rejecting with an `Error`. Typically wraps an
|
|
10
|
+
* Octokit call to fetch a release or tag and parse its semver.
|
|
11
|
+
* @param answers - Mutable answers object (plop prompts) where the resulting
|
|
12
|
+
* formatted version will be stored.
|
|
13
|
+
* @param answerKey - Key name to assign the formatted version into `answers`.
|
|
14
|
+
* @param semverFormatFn - Optional formatter that converts the `SemVer` into
|
|
15
|
+
* the desired string representation (defaults to `semver.toString()`).
|
|
16
|
+
* @returns A human-readable message indicating the fetched version. Throws an
|
|
17
|
+
* `Error` if the fetch fails or yields an invalid version.
|
|
18
|
+
*/
|
|
19
|
+
export declare const fetchLatestSemver: (fetchSemverFn: FetchSemverFn, answers: Record<string, unknown>, answerKey: string, semverFormatFn?: (semver: SemVer) => string) => Promise<string>;
|