@pagopa/dx-cli 0.23.0 → 0.23.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/commander/commands/init.js +62 -27
- package/dist/adapters/commander/index.js +1 -1
- package/dist/adapters/plop/index.d.ts +19 -1
- package/dist/adapters/plop/index.js +19 -6
- package/package.json +5 -5
- package/templates/monorepo/nx.json +0 -1
- package/dist/adapters/azure/__tests__/cloud-account-repository.test.d.ts +0 -1
- package/dist/adapters/azure/__tests__/cloud-account-repository.test.js +0 -95
- package/dist/adapters/azure/__tests__/cloud-account-service.test.d.ts +0 -1
- package/dist/adapters/azure/__tests__/cloud-account-service.test.js +0 -378
- package/dist/adapters/codemods/__tests__/registry.test.d.ts +0 -1
- package/dist/adapters/codemods/__tests__/registry.test.js +0 -56
- package/dist/adapters/codemods/__tests__/use-azure-appsvc.test.d.ts +0 -1
- package/dist/adapters/codemods/__tests__/use-azure-appsvc.test.js +0 -77
- package/dist/adapters/codemods/__tests__/use-pnpm.test.d.ts +0 -1
- package/dist/adapters/codemods/__tests__/use-pnpm.test.js +0 -148
- package/dist/adapters/commander/__tests__/env.test.d.ts +0 -1
- package/dist/adapters/commander/__tests__/env.test.js +0 -45
- package/dist/adapters/commander/__tests__/error-reporting.test.d.ts +0 -1
- package/dist/adapters/commander/__tests__/error-reporting.test.js +0 -63
- package/dist/adapters/commander/__tests__/exit-with-error.test.d.ts +0 -1
- package/dist/adapters/commander/__tests__/exit-with-error.test.js +0 -92
- package/dist/adapters/commander/__tests__/global-options.test.d.ts +0 -1
- package/dist/adapters/commander/__tests__/global-options.test.js +0 -33
- package/dist/adapters/commander/__tests__/spec.test.d.ts +0 -8
- package/dist/adapters/commander/__tests__/spec.test.js +0 -264
- package/dist/adapters/commander/commands/__tests__/add.test.d.ts +0 -4
- package/dist/adapters/commander/commands/__tests__/add.test.js +0 -167
- package/dist/adapters/commander/commands/__tests__/init-command.test.d.ts +0 -4
- package/dist/adapters/commander/commands/__tests__/init-command.test.js +0 -44
- package/dist/adapters/commander/commands/__tests__/init.test.d.ts +0 -4
- package/dist/adapters/commander/commands/__tests__/init.test.js +0 -48
- package/dist/adapters/commander/commands/__tests__/preconditions.test.d.ts +0 -1
- package/dist/adapters/commander/commands/__tests__/preconditions.test.js +0 -32
- package/dist/adapters/commander/commands/__tests__/savemoney.test.d.ts +0 -8
- package/dist/adapters/commander/commands/__tests__/savemoney.test.js +0 -60
- package/dist/adapters/commander/commands/__tests__/spec.test.d.ts +0 -7
- package/dist/adapters/commander/commands/__tests__/spec.test.js +0 -85
- package/dist/adapters/commander/presenters/__tests__/index.test.d.ts +0 -1
- package/dist/adapters/commander/presenters/__tests__/index.test.js +0 -23
- package/dist/adapters/commander/presenters/__tests__/json.test.d.ts +0 -1
- package/dist/adapters/commander/presenters/__tests__/json.test.js +0 -108
- package/dist/adapters/commander/presenters/__tests__/text.test.d.ts +0 -1
- package/dist/adapters/commander/presenters/__tests__/text.test.js +0 -60
- package/dist/adapters/github/__tests__/github-repo.spec.d.ts +0 -1
- package/dist/adapters/github/__tests__/github-repo.spec.js +0 -67
- package/dist/adapters/node/__tests__/data.d.ts +0 -18
- package/dist/adapters/node/__tests__/data.js +0 -22
- package/dist/adapters/node/__tests__/package-json.test.d.ts +0 -1
- package/dist/adapters/node/__tests__/package-json.test.js +0 -86
- package/dist/adapters/node/__tests__/repository.test.d.ts +0 -1
- package/dist/adapters/node/__tests__/repository.test.js +0 -77
- package/dist/adapters/node/fs/__tests__/file-reader.test.d.ts +0 -1
- package/dist/adapters/node/fs/__tests__/file-reader.test.js +0 -80
- package/dist/adapters/node/json/__tests__/index.test.d.ts +0 -1
- package/dist/adapters/node/json/__tests__/index.test.js +0 -14
- package/dist/adapters/octokit/__tests__/index.test.d.ts +0 -1
- package/dist/adapters/octokit/__tests__/index.test.js +0 -414
- package/dist/adapters/pagopa-technology/__tests__/authorization.test.d.ts +0 -4
- package/dist/adapters/pagopa-technology/__tests__/authorization.test.js +0 -548
- package/dist/adapters/plop/__tests__/run-actions.test.d.ts +0 -1
- package/dist/adapters/plop/__tests__/run-actions.test.js +0 -68
- package/dist/adapters/plop/actions/__tests__/init-cloud-accounts.test.d.ts +0 -1
- package/dist/adapters/plop/actions/__tests__/init-cloud-accounts.test.js +0 -171
- package/dist/adapters/plop/actions/__tests__/provision-terraform-backend.test.d.ts +0 -1
- package/dist/adapters/plop/actions/__tests__/provision-terraform-backend.test.js +0 -134
- package/dist/adapters/plop/generators/__tests__/temp-dir.d.ts +0 -2
- package/dist/adapters/plop/generators/__tests__/temp-dir.js +0 -13
- package/dist/adapters/plop/generators/environment/__tests__/actions.test.d.ts +0 -2
- package/dist/adapters/plop/generators/environment/__tests__/actions.test.js +0 -92
- package/dist/adapters/plop/generators/environment/__tests__/generation.test.d.ts +0 -1
- package/dist/adapters/plop/generators/environment/__tests__/generation.test.js +0 -213
- package/dist/adapters/plop/generators/environment/__tests__/prompts.test.d.ts +0 -1
- package/dist/adapters/plop/generators/environment/__tests__/prompts.test.js +0 -182
- package/dist/adapters/plop/generators/monorepo/__tests__/generation.test.d.ts +0 -1
- package/dist/adapters/plop/generators/monorepo/__tests__/generation.test.js +0 -79
- package/dist/adapters/plop/helpers/__tests__/resource-prefix.test.d.ts +0 -1
- package/dist/adapters/plop/helpers/__tests__/resource-prefix.test.js +0 -113
- package/dist/adapters/plop/helpers/__tests__/terraform-state-key.test.d.ts +0 -1
- package/dist/adapters/plop/helpers/__tests__/terraform-state-key.test.js +0 -35
- package/dist/adapters/plop/helpers/__tests__/validate-prompt.test.d.ts +0 -1
- package/dist/adapters/plop/helpers/__tests__/validate-prompt.test.js +0 -60
- package/dist/adapters/yaml/__tests__/index.test.d.ts +0 -1
- package/dist/adapters/yaml/__tests__/index.test.js +0 -53
- package/dist/domain/__tests__/data.d.ts +0 -15
- package/dist/domain/__tests__/data.js +0 -26
- package/dist/domain/__tests__/environment.test.d.ts +0 -1
- package/dist/domain/__tests__/environment.test.js +0 -332
- package/dist/domain/__tests__/info.test.d.ts +0 -1
- package/dist/domain/__tests__/info.test.js +0 -77
- package/dist/domain/__tests__/package-json.test.d.ts +0 -1
- package/dist/domain/__tests__/package-json.test.js +0 -39
- package/dist/domain/__tests__/repository.test.d.ts +0 -1
- package/dist/domain/__tests__/repository.test.js +0 -111
- package/dist/domain/__tests__/workspace.test.d.ts +0 -1
- package/dist/domain/__tests__/workspace.test.js +0 -57
- package/dist/use-cases/__tests__/apply-codemod.test.d.ts +0 -1
- package/dist/use-cases/__tests__/apply-codemod.test.js +0 -71
- package/dist/use-cases/__tests__/list-codemods.test.d.ts +0 -1
- package/dist/use-cases/__tests__/list-codemods.test.js +0 -37
- package/dist/use-cases/__tests__/request-authorization.test.d.ts +0 -4
- package/dist/use-cases/__tests__/request-authorization.test.js +0 -43
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
import { DefaultAzureCredential } from "@azure/identity";
|
|
2
|
-
import { test as baseTest, beforeEach, describe, expect, vi } from "vitest";
|
|
3
|
-
import { AzureCloudAccountService } from "../cloud-account-service.js";
|
|
4
|
-
const { queryResources } = vi.hoisted(() => ({
|
|
5
|
-
queryResources: vi.fn().mockRejectedValue(new Error("Not implemented")),
|
|
6
|
-
}));
|
|
7
|
-
const { mockProviderGet, mockProviderRegister } = vi.hoisted(() => ({
|
|
8
|
-
mockProviderGet: vi
|
|
9
|
-
.fn()
|
|
10
|
-
.mockResolvedValue({ registrationState: "Registered" }),
|
|
11
|
-
mockProviderRegister: vi.fn().mockResolvedValue({}),
|
|
12
|
-
}));
|
|
13
|
-
const { mockRoleAssignmentsCreate, mockRoleAssignmentsListForScope } = vi.hoisted(() => ({
|
|
14
|
-
mockRoleAssignmentsCreate: vi.fn().mockResolvedValue({}),
|
|
15
|
-
mockRoleAssignmentsListForScope: vi.fn(),
|
|
16
|
-
}));
|
|
17
|
-
const { mockCreateFederatedIdentityCredential, mockCreateIdentity, mockCreateKeyVault, mockCreateResourceGroup, mockDeleteResourceGroup, mockGetSubscription, mockKeyVaultNameAvailability, mockSetSecret, } = vi.hoisted(() => ({
|
|
18
|
-
mockCreateFederatedIdentityCredential: vi.fn().mockResolvedValue({}),
|
|
19
|
-
mockCreateIdentity: vi
|
|
20
|
-
.fn()
|
|
21
|
-
.mockResolvedValue({ clientId: "client-1", principalId: "principal-1" }),
|
|
22
|
-
mockCreateKeyVault: vi.fn().mockResolvedValue({}),
|
|
23
|
-
mockCreateResourceGroup: vi.fn().mockResolvedValue({}),
|
|
24
|
-
mockDeleteResourceGroup: vi.fn().mockResolvedValue({}),
|
|
25
|
-
mockGetSubscription: vi.fn().mockResolvedValue({ tenantId: "tenant-1" }),
|
|
26
|
-
mockKeyVaultNameAvailability: vi.fn().mockResolvedValue({
|
|
27
|
-
nameAvailable: true,
|
|
28
|
-
}),
|
|
29
|
-
mockSetSecret: vi.fn().mockResolvedValue({}),
|
|
30
|
-
}));
|
|
31
|
-
vi.mock("@azure/arm-authorization", () => ({
|
|
32
|
-
AuthorizationManagementClient: class {
|
|
33
|
-
roleAssignments = {
|
|
34
|
-
create: mockRoleAssignmentsCreate,
|
|
35
|
-
listForScope: mockRoleAssignmentsListForScope,
|
|
36
|
-
};
|
|
37
|
-
},
|
|
38
|
-
}));
|
|
39
|
-
vi.mock("@azure/arm-keyvault", () => ({
|
|
40
|
-
KeyVaultManagementClient: class {
|
|
41
|
-
vaults = {
|
|
42
|
-
beginCreateOrUpdateAndWait: mockCreateKeyVault,
|
|
43
|
-
checkNameAvailability: mockKeyVaultNameAvailability,
|
|
44
|
-
};
|
|
45
|
-
},
|
|
46
|
-
}));
|
|
47
|
-
vi.mock("@azure/arm-msi", () => ({
|
|
48
|
-
ManagedServiceIdentityClient: class {
|
|
49
|
-
federatedIdentityCredentials = {
|
|
50
|
-
createOrUpdate: mockCreateFederatedIdentityCredential,
|
|
51
|
-
};
|
|
52
|
-
userAssignedIdentities = {
|
|
53
|
-
createOrUpdate: mockCreateIdentity,
|
|
54
|
-
};
|
|
55
|
-
},
|
|
56
|
-
}));
|
|
57
|
-
vi.mock("@azure/identity", () => ({
|
|
58
|
-
DefaultAzureCredential: vi.fn(),
|
|
59
|
-
}));
|
|
60
|
-
vi.mock("@azure/arm-resourcegraph", () => ({
|
|
61
|
-
ResourceGraphClient: class {
|
|
62
|
-
resources = queryResources;
|
|
63
|
-
},
|
|
64
|
-
}));
|
|
65
|
-
vi.mock("@azure/arm-resources", () => ({
|
|
66
|
-
ResourceManagementClient: class {
|
|
67
|
-
providers = {
|
|
68
|
-
get: mockProviderGet,
|
|
69
|
-
register: mockProviderRegister,
|
|
70
|
-
};
|
|
71
|
-
resourceGroups = {
|
|
72
|
-
beginDeleteAndWait: mockDeleteResourceGroup,
|
|
73
|
-
createOrUpdate: mockCreateResourceGroup,
|
|
74
|
-
};
|
|
75
|
-
},
|
|
76
|
-
}));
|
|
77
|
-
vi.mock("@azure/arm-resources-subscriptions", () => ({
|
|
78
|
-
SubscriptionClient: class {
|
|
79
|
-
subscriptions = {
|
|
80
|
-
get: mockGetSubscription,
|
|
81
|
-
};
|
|
82
|
-
},
|
|
83
|
-
}));
|
|
84
|
-
vi.mock("@azure/keyvault-secrets", () => ({
|
|
85
|
-
SecretClient: class {
|
|
86
|
-
setSecret = mockSetSecret;
|
|
87
|
-
},
|
|
88
|
-
}));
|
|
89
|
-
const test = baseTest.extend({
|
|
90
|
-
// the empty pattern is required by vitest!!!
|
|
91
|
-
// eslint-disable-next-line no-empty-pattern
|
|
92
|
-
cloudAccountService: async ({}, use) => {
|
|
93
|
-
const cloudAccountService = new AzureCloudAccountService(new DefaultAzureCredential());
|
|
94
|
-
await use(cloudAccountService);
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
beforeEach(() => {
|
|
98
|
-
queryResources.mockReset();
|
|
99
|
-
queryResources.mockResolvedValue({ data: [], totalRecords: 0 });
|
|
100
|
-
mockCreateFederatedIdentityCredential.mockClear();
|
|
101
|
-
mockCreateIdentity.mockClear();
|
|
102
|
-
mockCreateIdentity.mockResolvedValue({
|
|
103
|
-
clientId: "client-1",
|
|
104
|
-
principalId: "principal-1",
|
|
105
|
-
});
|
|
106
|
-
mockCreateKeyVault.mockClear();
|
|
107
|
-
mockCreateResourceGroup.mockClear();
|
|
108
|
-
mockDeleteResourceGroup.mockClear();
|
|
109
|
-
mockGetSubscription.mockClear();
|
|
110
|
-
mockGetSubscription.mockResolvedValue({ tenantId: "tenant-1" });
|
|
111
|
-
mockKeyVaultNameAvailability.mockClear();
|
|
112
|
-
mockKeyVaultNameAvailability.mockResolvedValue({ nameAvailable: true });
|
|
113
|
-
mockProviderGet.mockClear();
|
|
114
|
-
mockProviderGet.mockResolvedValue({ registrationState: "Registered" });
|
|
115
|
-
mockProviderRegister.mockClear();
|
|
116
|
-
mockRoleAssignmentsCreate.mockClear();
|
|
117
|
-
mockRoleAssignmentsListForScope.mockReset();
|
|
118
|
-
mockSetSecret.mockClear();
|
|
119
|
-
});
|
|
120
|
-
describe("getTerraformBackend", () => {
|
|
121
|
-
test("returns undefined when no matching storage account is found", async ({ cloudAccountService, }) => {
|
|
122
|
-
queryResources.mockResolvedValueOnce({
|
|
123
|
-
data: [],
|
|
124
|
-
totalRecords: 0,
|
|
125
|
-
});
|
|
126
|
-
const result = await cloudAccountService.getTerraformBackend("sub-1", {
|
|
127
|
-
name: "dev",
|
|
128
|
-
prefix: "dx",
|
|
129
|
-
});
|
|
130
|
-
expect(result).toBeUndefined();
|
|
131
|
-
});
|
|
132
|
-
test("return the only matching storage account", async ({ cloudAccountService, }) => {
|
|
133
|
-
queryResources.mockResolvedValueOnce({
|
|
134
|
-
data: [
|
|
135
|
-
{
|
|
136
|
-
location: "italynorth",
|
|
137
|
-
name: "dxditntfstatest01",
|
|
138
|
-
resourceGroup: "dx-d-itn-tfstate-rg-01",
|
|
139
|
-
subscriptionId: "sub-1",
|
|
140
|
-
type: "microsoft.storage/storageaccounts",
|
|
141
|
-
},
|
|
142
|
-
],
|
|
143
|
-
totalRecords: 1,
|
|
144
|
-
});
|
|
145
|
-
const result = await cloudAccountService.getTerraformBackend("sub-1", {
|
|
146
|
-
name: "dev",
|
|
147
|
-
prefix: "dx",
|
|
148
|
-
});
|
|
149
|
-
expect(result).toEqual(expect.objectContaining({
|
|
150
|
-
resourceGroupName: "dx-d-itn-tfstate-rg-01",
|
|
151
|
-
storageAccountName: "dxditntfstatest01",
|
|
152
|
-
type: "azurerm",
|
|
153
|
-
}));
|
|
154
|
-
});
|
|
155
|
-
test("returns the best matching storage account among multiple", async ({ cloudAccountService, }) => {
|
|
156
|
-
queryResources.mockResolvedValueOnce({
|
|
157
|
-
data: [
|
|
158
|
-
{
|
|
159
|
-
location: "italynorth",
|
|
160
|
-
name: "dxditntfstatest01",
|
|
161
|
-
resourceGroup: "dx-d-itn-tfstate-rg-01",
|
|
162
|
-
subscriptionId: "sub-1",
|
|
163
|
-
type: "microsoft.storage/storageaccounts",
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
location: "italynorth",
|
|
167
|
-
name: "dxditntfstatest02",
|
|
168
|
-
resourceGroup: "dx-d-itn-tfstate-rg-01",
|
|
169
|
-
subscriptionId: "sub-1",
|
|
170
|
-
type: "microsoft.storage/storageaccounts",
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
location: "westeurope",
|
|
174
|
-
name: "dxdweutfstatest01",
|
|
175
|
-
resourceGroup: "dx-d-weu-tfstate-rg-01",
|
|
176
|
-
subscriptionId: "sub-1",
|
|
177
|
-
type: "microsoft.storage/storageaccounts",
|
|
178
|
-
},
|
|
179
|
-
],
|
|
180
|
-
totalRecords: 3,
|
|
181
|
-
});
|
|
182
|
-
const result = await cloudAccountService.getTerraformBackend("sub-1", {
|
|
183
|
-
name: "dev",
|
|
184
|
-
prefix: "dx",
|
|
185
|
-
});
|
|
186
|
-
expect(result).toEqual(expect.objectContaining({
|
|
187
|
-
resourceGroupName: "dx-d-itn-tfstate-rg-01",
|
|
188
|
-
storageAccountName: "dxditntfstatest02",
|
|
189
|
-
type: "azurerm",
|
|
190
|
-
}));
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
describe("isInitialized", () => {
|
|
194
|
-
test("returns true when both bootstrap identity and common Key Vault exist and all providers are registered", async ({ cloudAccountService, }) => {
|
|
195
|
-
// First call: identity query → found
|
|
196
|
-
queryResources.mockResolvedValueOnce({ data: [], totalRecords: 1 });
|
|
197
|
-
// Second call: key vault query → found
|
|
198
|
-
queryResources.mockResolvedValueOnce({ data: [], totalRecords: 1 });
|
|
199
|
-
// Provider checks → all registered (default mock)
|
|
200
|
-
const result = await cloudAccountService.isInitialized("sub-1", {
|
|
201
|
-
name: "dev",
|
|
202
|
-
prefix: "dx",
|
|
203
|
-
});
|
|
204
|
-
expect(result).toBe(true);
|
|
205
|
-
});
|
|
206
|
-
test("returns false when bootstrap identity exists but common Key Vault does not", async ({ cloudAccountService, }) => {
|
|
207
|
-
// First call: identity query → found
|
|
208
|
-
queryResources.mockResolvedValueOnce({ data: [], totalRecords: 1 });
|
|
209
|
-
// Second call: key vault query → not found
|
|
210
|
-
queryResources.mockResolvedValueOnce({ data: [], totalRecords: 0 });
|
|
211
|
-
const result = await cloudAccountService.isInitialized("sub-1", {
|
|
212
|
-
name: "dev",
|
|
213
|
-
prefix: "dx",
|
|
214
|
-
});
|
|
215
|
-
expect(result).toBe(false);
|
|
216
|
-
});
|
|
217
|
-
test("returns false when common Key Vault exists but bootstrap identity does not", async ({ cloudAccountService, }) => {
|
|
218
|
-
// First call: identity query → not found
|
|
219
|
-
queryResources.mockResolvedValueOnce({ data: [], totalRecords: 0 });
|
|
220
|
-
// Second call: key vault query → found
|
|
221
|
-
queryResources.mockResolvedValueOnce({ data: [], totalRecords: 1 });
|
|
222
|
-
const result = await cloudAccountService.isInitialized("sub-1", {
|
|
223
|
-
name: "dev",
|
|
224
|
-
prefix: "dx",
|
|
225
|
-
});
|
|
226
|
-
expect(result).toBe(false);
|
|
227
|
-
});
|
|
228
|
-
test("returns false when resources exist but a required provider is not registered", async ({ cloudAccountService, }) => {
|
|
229
|
-
// Both resources exist
|
|
230
|
-
queryResources.mockResolvedValueOnce({ data: [], totalRecords: 1 });
|
|
231
|
-
queryResources.mockResolvedValueOnce({ data: [], totalRecords: 1 });
|
|
232
|
-
// One provider is not registered
|
|
233
|
-
mockProviderGet.mockResolvedValueOnce({
|
|
234
|
-
registrationState: "NotRegistered",
|
|
235
|
-
});
|
|
236
|
-
const result = await cloudAccountService.isInitialized("sub-1", {
|
|
237
|
-
name: "dev",
|
|
238
|
-
prefix: "dx",
|
|
239
|
-
});
|
|
240
|
-
expect(result).toBe(false);
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
describe("initialize", () => {
|
|
244
|
-
test("assigns bootstrap roles and creates bootstrap environment secrets", async ({ cloudAccountService, }) => {
|
|
245
|
-
const createOrUpdateEnvironmentSecret = vi
|
|
246
|
-
.fn()
|
|
247
|
-
.mockResolvedValue(undefined);
|
|
248
|
-
await cloudAccountService.initialize({
|
|
249
|
-
csp: "azure",
|
|
250
|
-
defaultLocation: "italynorth",
|
|
251
|
-
displayName: "Test subscription",
|
|
252
|
-
id: "sub-1",
|
|
253
|
-
}, {
|
|
254
|
-
name: "dev",
|
|
255
|
-
prefix: "dx",
|
|
256
|
-
}, {
|
|
257
|
-
clientId: "app-client-id",
|
|
258
|
-
id: "app-id",
|
|
259
|
-
installationId: "installation-id",
|
|
260
|
-
key: "private-key\n",
|
|
261
|
-
}, {
|
|
262
|
-
owner: "pagopa",
|
|
263
|
-
repo: "dx",
|
|
264
|
-
}, {
|
|
265
|
-
createBranch: vi.fn(),
|
|
266
|
-
createOrUpdateEnvironmentSecret,
|
|
267
|
-
createPullRequest: vi.fn(),
|
|
268
|
-
getFileContent: vi.fn(),
|
|
269
|
-
getRepository: vi.fn(),
|
|
270
|
-
updateFile: vi.fn(),
|
|
271
|
-
});
|
|
272
|
-
expect(mockRoleAssignmentsCreate).toHaveBeenCalledTimes(3);
|
|
273
|
-
expect(mockRoleAssignmentsCreate).toHaveBeenCalledWith("/subscriptions/sub-1", expect.any(String), expect.objectContaining({
|
|
274
|
-
principalId: "principal-1",
|
|
275
|
-
principalType: "ServicePrincipal",
|
|
276
|
-
roleDefinitionId: "/subscriptions/sub-1/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168",
|
|
277
|
-
}));
|
|
278
|
-
expect(mockRoleAssignmentsCreate).toHaveBeenCalledWith("/subscriptions/sub-1", expect.any(String), expect.objectContaining({
|
|
279
|
-
principalId: "principal-1",
|
|
280
|
-
principalType: "ServicePrincipal",
|
|
281
|
-
roleDefinitionId: "/subscriptions/sub-1/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",
|
|
282
|
-
}));
|
|
283
|
-
expect(mockRoleAssignmentsCreate).toHaveBeenCalledWith("/subscriptions/sub-1", expect.any(String), expect.objectContaining({
|
|
284
|
-
principalId: "principal-1",
|
|
285
|
-
principalType: "ServicePrincipal",
|
|
286
|
-
roleDefinitionId: "/subscriptions/sub-1/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe",
|
|
287
|
-
}));
|
|
288
|
-
expect(mockCreateFederatedIdentityCredential).toHaveBeenCalledWith("dx-d-itn-common-rg-01", "dx-d-itn-bootstrap-id-01", "dx-bootstrapper-dev-cd", {
|
|
289
|
-
audiences: ["api://AzureADTokenExchange"],
|
|
290
|
-
issuer: "https://token.actions.githubusercontent.com",
|
|
291
|
-
subject: "repo:pagopa/dx:environment:bootstrapper-dev-cd",
|
|
292
|
-
});
|
|
293
|
-
expect(createOrUpdateEnvironmentSecret).toHaveBeenCalledTimes(7);
|
|
294
|
-
expect(createOrUpdateEnvironmentSecret).toHaveBeenCalledWith({
|
|
295
|
-
environmentName: "bootstrapper-dev-cd",
|
|
296
|
-
owner: "pagopa",
|
|
297
|
-
repo: "dx",
|
|
298
|
-
secretName: "ARM_CLIENT_ID",
|
|
299
|
-
secretValue: "client-1",
|
|
300
|
-
});
|
|
301
|
-
expect(createOrUpdateEnvironmentSecret).toHaveBeenCalledWith({
|
|
302
|
-
environmentName: "bootstrapper-dev-cd",
|
|
303
|
-
owner: "pagopa",
|
|
304
|
-
repo: "dx",
|
|
305
|
-
secretName: "ARM_TENANT_ID",
|
|
306
|
-
secretValue: "tenant-1",
|
|
307
|
-
});
|
|
308
|
-
expect(createOrUpdateEnvironmentSecret).toHaveBeenCalledWith({
|
|
309
|
-
environmentName: "bootstrapper-dev-cd",
|
|
310
|
-
owner: "pagopa",
|
|
311
|
-
repo: "dx",
|
|
312
|
-
secretName: "ARM_SUBSCRIPTION_ID",
|
|
313
|
-
secretValue: "sub-1",
|
|
314
|
-
});
|
|
315
|
-
expect(createOrUpdateEnvironmentSecret).toHaveBeenCalledWith({
|
|
316
|
-
environmentName: "bootstrapper-dev-cd",
|
|
317
|
-
owner: "pagopa",
|
|
318
|
-
repo: "dx",
|
|
319
|
-
secretName: "GH_APP_ID",
|
|
320
|
-
secretValue: "app-id",
|
|
321
|
-
});
|
|
322
|
-
expect(createOrUpdateEnvironmentSecret).toHaveBeenCalledWith({
|
|
323
|
-
environmentName: "bootstrapper-dev-cd",
|
|
324
|
-
owner: "pagopa",
|
|
325
|
-
repo: "dx",
|
|
326
|
-
secretName: "GH_APP_CLIENT_ID",
|
|
327
|
-
secretValue: "app-client-id",
|
|
328
|
-
});
|
|
329
|
-
expect(createOrUpdateEnvironmentSecret).toHaveBeenCalledWith({
|
|
330
|
-
environmentName: "bootstrapper-dev-cd",
|
|
331
|
-
owner: "pagopa",
|
|
332
|
-
repo: "dx",
|
|
333
|
-
secretName: "GH_APP_INSTALLATION_ID",
|
|
334
|
-
secretValue: "installation-id",
|
|
335
|
-
});
|
|
336
|
-
expect(createOrUpdateEnvironmentSecret).toHaveBeenCalledWith({
|
|
337
|
-
environmentName: "bootstrapper-dev-cd",
|
|
338
|
-
owner: "pagopa",
|
|
339
|
-
repo: "dx",
|
|
340
|
-
secretName: "GH_APP_KEY",
|
|
341
|
-
secretValue: "private-key",
|
|
342
|
-
});
|
|
343
|
-
});
|
|
344
|
-
test("uses a repository-specific federated credential name", async ({ cloudAccountService, }) => {
|
|
345
|
-
const createOrUpdateEnvironmentSecret = vi
|
|
346
|
-
.fn()
|
|
347
|
-
.mockResolvedValue(undefined);
|
|
348
|
-
await cloudAccountService.initialize({
|
|
349
|
-
csp: "azure",
|
|
350
|
-
defaultLocation: "italynorth",
|
|
351
|
-
displayName: "Test subscription",
|
|
352
|
-
id: "sub-1",
|
|
353
|
-
}, {
|
|
354
|
-
name: "dev",
|
|
355
|
-
prefix: "dx",
|
|
356
|
-
}, {
|
|
357
|
-
clientId: "app-client-id",
|
|
358
|
-
id: "app-id",
|
|
359
|
-
installationId: "installation-id",
|
|
360
|
-
key: "private-key\n",
|
|
361
|
-
}, {
|
|
362
|
-
owner: "pagopa",
|
|
363
|
-
repo: "dx-playground",
|
|
364
|
-
}, {
|
|
365
|
-
createBranch: vi.fn(),
|
|
366
|
-
createOrUpdateEnvironmentSecret,
|
|
367
|
-
createPullRequest: vi.fn(),
|
|
368
|
-
getFileContent: vi.fn(),
|
|
369
|
-
getRepository: vi.fn(),
|
|
370
|
-
updateFile: vi.fn(),
|
|
371
|
-
});
|
|
372
|
-
expect(mockCreateFederatedIdentityCredential).toHaveBeenCalledWith("dx-d-itn-common-rg-01", "dx-d-itn-bootstrap-id-01", "dx-playground-bootstrapper-dev-cd", {
|
|
373
|
-
audiences: ["api://AzureADTokenExchange"],
|
|
374
|
-
issuer: "https://token.actions.githubusercontent.com",
|
|
375
|
-
subject: "repo:pagopa/dx-playground:environment:bootstrapper-dev-cd",
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { ok } from "neverthrow";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
import { LocalCodemodRegistry } from "../registry.js";
|
|
4
|
-
describe("LocalCodemodRegistry", () => {
|
|
5
|
-
const makeCodemod = (id, description = id) => ({
|
|
6
|
-
apply: vi.fn().mockResolvedValue(undefined),
|
|
7
|
-
description,
|
|
8
|
-
id,
|
|
9
|
-
});
|
|
10
|
-
it("returns empty list when no codemods are registered", async () => {
|
|
11
|
-
const registry = new LocalCodemodRegistry();
|
|
12
|
-
const result = await registry.getAll();
|
|
13
|
-
expect(result).toStrictEqual(ok([]));
|
|
14
|
-
});
|
|
15
|
-
it("adds codemods and lists them via getAll", async () => {
|
|
16
|
-
const registry = new LocalCodemodRegistry();
|
|
17
|
-
const a = makeCodemod("a", "A");
|
|
18
|
-
const b = makeCodemod("b", "B");
|
|
19
|
-
registry.add(a);
|
|
20
|
-
registry.add(b);
|
|
21
|
-
const result = await registry.getAll();
|
|
22
|
-
expect.assertions(3);
|
|
23
|
-
expect(result.isOk()).toBe(true);
|
|
24
|
-
const expected = result.unwrapOr([]);
|
|
25
|
-
expect(expected).toHaveLength(2);
|
|
26
|
-
expect(expected).toEqual(expect.arrayContaining([
|
|
27
|
-
expect.objectContaining({ id: "a" }),
|
|
28
|
-
expect.objectContaining({ id: "b" }),
|
|
29
|
-
]));
|
|
30
|
-
});
|
|
31
|
-
it("retrieves a codemod by id and returns undefined when missing", async () => {
|
|
32
|
-
const registry = new LocalCodemodRegistry();
|
|
33
|
-
const a = makeCodemod("a", "A");
|
|
34
|
-
registry.add(a);
|
|
35
|
-
const found = await registry.getById("a");
|
|
36
|
-
expect(found.isOk()).toBe(true);
|
|
37
|
-
const expected = found.unwrapOr(undefined);
|
|
38
|
-
expect(expected).toBe(a);
|
|
39
|
-
const missing = await registry.getById("nope");
|
|
40
|
-
expect(missing.isOk()).toBe(true);
|
|
41
|
-
const missingValue = missing.unwrapOr(undefined);
|
|
42
|
-
expect(missingValue).toBeUndefined();
|
|
43
|
-
});
|
|
44
|
-
it("overwrites an existing codemod when adding with the same id", async () => {
|
|
45
|
-
const registry = new LocalCodemodRegistry();
|
|
46
|
-
const first = makeCodemod("a", "first");
|
|
47
|
-
const second = makeCodemod("a", "second");
|
|
48
|
-
registry.add(first);
|
|
49
|
-
registry.add(second);
|
|
50
|
-
const byId = await registry.getById("a");
|
|
51
|
-
expect(byId.isOk()).toBe(true);
|
|
52
|
-
const byIdValue = byId.unwrapOr(undefined);
|
|
53
|
-
expect(byIdValue).toBe(second);
|
|
54
|
-
expect(byIdValue).not.toBe(first);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { migrateWorkflow } from "../use-azure-appsvc.js";
|
|
3
|
-
describe("migrateWorkflow", () => {
|
|
4
|
-
const sha = "testsha";
|
|
5
|
-
const migrate = migrateWorkflow(sha);
|
|
6
|
-
it("adds disable_auto_staging_deploy under with for web_app_deploy", () => {
|
|
7
|
-
const input = `
|
|
8
|
-
jobs:
|
|
9
|
-
deploy:
|
|
10
|
-
uses: pagopa/dx/.github/workflows/web_app_deploy.yaml@main
|
|
11
|
-
with:
|
|
12
|
-
web_app_name: my-app
|
|
13
|
-
use_staging_slot: true
|
|
14
|
-
`;
|
|
15
|
-
const output = migrate(input, "workflow.yaml");
|
|
16
|
-
expect(output).toContain("disable_auto_staging_deploy: true");
|
|
17
|
-
});
|
|
18
|
-
it("adds disable_auto_staging_deploy under with for function_app_deploy", () => {
|
|
19
|
-
const input = `
|
|
20
|
-
jobs:
|
|
21
|
-
deploy:
|
|
22
|
-
uses: pagopa/dx/.github/workflows/function_app_deploy.yaml@main
|
|
23
|
-
with:
|
|
24
|
-
function_app_name: my-func
|
|
25
|
-
use_staging_slot: true
|
|
26
|
-
`;
|
|
27
|
-
const output = migrate(input, "workflow.yaml");
|
|
28
|
-
expect(output).toContain("disable_auto_staging_deploy: true");
|
|
29
|
-
});
|
|
30
|
-
it("renames function_app_name to web_app_name", () => {
|
|
31
|
-
const input = `
|
|
32
|
-
jobs:
|
|
33
|
-
deploy:
|
|
34
|
-
uses: pagopa/dx/.github/workflows/function_app_deploy.yaml@main
|
|
35
|
-
with:
|
|
36
|
-
function_app_name: my-func
|
|
37
|
-
`;
|
|
38
|
-
const output = migrate(input, "workflow.yaml");
|
|
39
|
-
expect(output).toContain("web_app_name: my-func");
|
|
40
|
-
expect(output).not.toContain("function_app_name:");
|
|
41
|
-
});
|
|
42
|
-
it("removes use_staging_slot", () => {
|
|
43
|
-
const input = `
|
|
44
|
-
jobs:
|
|
45
|
-
deploy:
|
|
46
|
-
uses: pagopa/dx/.github/workflows/web_app_deploy.yaml@main
|
|
47
|
-
with:
|
|
48
|
-
web_app_name: my-app
|
|
49
|
-
use_staging_slot: true
|
|
50
|
-
`;
|
|
51
|
-
const output = migrate(input, "workflow.yaml");
|
|
52
|
-
expect(output).not.toContain("use_staging_slot:");
|
|
53
|
-
});
|
|
54
|
-
it("updates uses to release-azure-appsvc.yaml@sha", () => {
|
|
55
|
-
const input = `
|
|
56
|
-
jobs:
|
|
57
|
-
deploy:
|
|
58
|
-
uses: pagopa/dx/.github/workflows/web_app_deploy.yaml@main
|
|
59
|
-
with:
|
|
60
|
-
web_app_name: my-app
|
|
61
|
-
`;
|
|
62
|
-
const output = migrate(input, "workflow.yaml");
|
|
63
|
-
expect(output).toContain(`uses: pagopa/dx/.github/workflows/release-azure-appsvc-v1.yaml@${sha}`);
|
|
64
|
-
});
|
|
65
|
-
it("does not change unrelated workflows", () => {
|
|
66
|
-
const input = `
|
|
67
|
-
jobs:
|
|
68
|
-
build:
|
|
69
|
-
runs-on: ubuntu-latest
|
|
70
|
-
steps:
|
|
71
|
-
- name: Checkout
|
|
72
|
-
uses: actions/checkout@v3
|
|
73
|
-
`;
|
|
74
|
-
const output = migrate(input, "workflow.yaml");
|
|
75
|
-
expect(output).toBe(input);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
|
-
import YAML from "yaml";
|
|
3
|
-
import { extractPackageExtensions, NPM, preparePackageJsonForPnpm, usePnpm, writePnpmWorkspaceFile, Yarn, } from "../use-pnpm.js";
|
|
4
|
-
const { readFile, stdout, writeFile } = vi.hoisted(() => ({
|
|
5
|
-
readFile: vi.fn(async () => ""),
|
|
6
|
-
stdout: vi.fn(),
|
|
7
|
-
writeFile: vi.fn(),
|
|
8
|
-
}));
|
|
9
|
-
vi.mock("execa", () => ({
|
|
10
|
-
$: vi.fn((opts) => {
|
|
11
|
-
const hasOptions = typeof opts === "object" && Object.hasOwn(opts, "lines");
|
|
12
|
-
if (hasOptions) {
|
|
13
|
-
return () => stdout().then((output) => ({
|
|
14
|
-
stdout: output.split("\n"),
|
|
15
|
-
}));
|
|
16
|
-
}
|
|
17
|
-
return stdout().then((output) => ({
|
|
18
|
-
stdout: output,
|
|
19
|
-
}));
|
|
20
|
-
}),
|
|
21
|
-
}));
|
|
22
|
-
vi.mock("node:fs/promises", () => ({
|
|
23
|
-
default: {
|
|
24
|
-
appendFile: () => Promise.resolve(),
|
|
25
|
-
readFile,
|
|
26
|
-
rm: () => Promise.resolve(),
|
|
27
|
-
stat: () => Promise.resolve({
|
|
28
|
-
isFile: () => true,
|
|
29
|
-
}),
|
|
30
|
-
writeFile,
|
|
31
|
-
},
|
|
32
|
-
}));
|
|
33
|
-
vi.mock("../git.js", async () => ({
|
|
34
|
-
getLatestCommitShaOrRef: async () => "dummy-sha",
|
|
35
|
-
}));
|
|
36
|
-
describe("pm", () => {
|
|
37
|
-
test("yarn workspaces are parsed correctly", async () => {
|
|
38
|
-
const yarn = new Yarn();
|
|
39
|
-
stdout.mockResolvedValueOnce(`{"location":".","name":"io-messages"}\n{"location":"apps/citizen-func","name":"citizen-func"}`);
|
|
40
|
-
const workspaces = await yarn.listWorkspaces();
|
|
41
|
-
expect(workspaces).toEqual(["io-messages", "citizen-func"]);
|
|
42
|
-
});
|
|
43
|
-
test("npm workspaces are parsed correctly", async () => {
|
|
44
|
-
const npm = new NPM();
|
|
45
|
-
stdout.mockResolvedValueOnce(`[{ "name": "io-messages", "location": "." },{ "name": "citizen-func", "location": "apps/citizen-func" }]`);
|
|
46
|
-
const workspaces = await npm.listWorkspaces();
|
|
47
|
-
expect(workspaces).toEqual(["io-messages", "citizen-func"]);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
describe("extractPackageExtensions", () => {
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
vi.clearAllMocks();
|
|
53
|
-
});
|
|
54
|
-
test("returns packageExtensions only if present", async () => {
|
|
55
|
-
const yarnrc = {
|
|
56
|
-
packageExtensions: {
|
|
57
|
-
"some-package@*": {
|
|
58
|
-
peerDependencies: {
|
|
59
|
-
react: "*",
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
readFile.mockResolvedValueOnce(YAML.stringify(yarnrc));
|
|
65
|
-
await expect(extractPackageExtensions()).resolves.toEqual(yarnrc.packageExtensions);
|
|
66
|
-
expect(readFile).toHaveBeenCalledOnce();
|
|
67
|
-
});
|
|
68
|
-
test("returns undefined if packageExtensions is not present", async () => {
|
|
69
|
-
const yarnrc = {
|
|
70
|
-
someOtherField: {},
|
|
71
|
-
};
|
|
72
|
-
readFile.mockResolvedValueOnce(YAML.stringify(yarnrc));
|
|
73
|
-
await expect(extractPackageExtensions()).resolves.toBeUndefined();
|
|
74
|
-
expect(readFile).toHaveBeenCalledOnce();
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
describe("preparePackageJsonForPnpm", () => {
|
|
78
|
-
beforeEach(() => {
|
|
79
|
-
vi.clearAllMocks();
|
|
80
|
-
});
|
|
81
|
-
test("removes packageManager field and keeps workspaces", async () => {
|
|
82
|
-
const packageJson = {
|
|
83
|
-
name: "test-monorepo",
|
|
84
|
-
packageManager: "yarn@1.22.10",
|
|
85
|
-
version: "1.0.0",
|
|
86
|
-
workspaces: ["packages/*"],
|
|
87
|
-
};
|
|
88
|
-
readFile.mockResolvedValueOnce(JSON.stringify(packageJson, null, 2));
|
|
89
|
-
const workspaces = await preparePackageJsonForPnpm();
|
|
90
|
-
expect(workspaces).toEqual(["packages/*"]);
|
|
91
|
-
expect(readFile).toHaveBeenCalledOnce();
|
|
92
|
-
expect(writeFile).toHaveBeenCalledWith("package.json", JSON.stringify({
|
|
93
|
-
name: packageJson.name,
|
|
94
|
-
version: packageJson.version,
|
|
95
|
-
}, null, 2));
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
describe("writePnpmWorkspaceFile", () => {
|
|
99
|
-
beforeEach(() => {
|
|
100
|
-
vi.clearAllMocks();
|
|
101
|
-
});
|
|
102
|
-
test("writes pnpm-workspace.yaml with given workspaces", async () => {
|
|
103
|
-
const workspaces = ["packages/*", "apps/*"];
|
|
104
|
-
const packageExtensions = {
|
|
105
|
-
"express@*": {
|
|
106
|
-
peerDependencies: {
|
|
107
|
-
typescript: "*",
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
await writePnpmWorkspaceFile(workspaces, packageExtensions);
|
|
112
|
-
expect(writeFile).toHaveBeenCalledWith("pnpm-workspace.yaml", expect.stringContaining(YAML.stringify({ packages: workspaces })), "utf-8");
|
|
113
|
-
expect(writeFile).toHaveBeenCalledWith("pnpm-workspace.yaml", expect.stringContaining(YAML.stringify({ packageExtensions })), "utf-8");
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
describe("usePnpm", () => {
|
|
117
|
-
beforeEach(() => {
|
|
118
|
-
vi.resetAllMocks();
|
|
119
|
-
});
|
|
120
|
-
test("rejects if the project is already using pnpm", async () => {
|
|
121
|
-
await expect(usePnpm("pnpm", "10.20")).rejects.toThrow();
|
|
122
|
-
});
|
|
123
|
-
test.each([
|
|
124
|
-
{ expected: false, version: "18.0.0" },
|
|
125
|
-
{ expected: false, version: "20.10.0" },
|
|
126
|
-
{ expected: false, version: "20.19.4" },
|
|
127
|
-
{ expected: true, version: "22.0.0" },
|
|
128
|
-
{ expected: true, version: "20.19.5" },
|
|
129
|
-
])("rejects if Node.js version is less than required ($version)", async ({ expected, version }) => {
|
|
130
|
-
stdout.mockResolvedValue("[]");
|
|
131
|
-
readFile.mockResolvedValueOnce(JSON.stringify({ name: "test-monorepo", workspaces: [] }));
|
|
132
|
-
await expect(usePnpm("npm", version).then(() => true, () => false)).resolves.toBe(expected);
|
|
133
|
-
});
|
|
134
|
-
test("moves workspaces from package.json to pnpm-workspace.yaml", async () => {
|
|
135
|
-
const packageJson = {
|
|
136
|
-
name: "test-monorepo",
|
|
137
|
-
packageManager: "yarn@1.22.10",
|
|
138
|
-
version: "1.0.0",
|
|
139
|
-
workspaces: ["packages/*"],
|
|
140
|
-
};
|
|
141
|
-
readFile.mockResolvedValueOnce(JSON.stringify(packageJson, null, 2));
|
|
142
|
-
stdout.mockResolvedValue(`[]`); // npm list workspaces
|
|
143
|
-
await usePnpm("npm", "22.0.0");
|
|
144
|
-
expect(writeFile).toHaveBeenNthCalledWith(2, "pnpm-workspace.yaml", expect.stringContaining(YAML.stringify({
|
|
145
|
-
packages: packageJson.workspaces,
|
|
146
|
-
})), "utf-8");
|
|
147
|
-
});
|
|
148
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|