@pagopa/dx-cli 0.21.2 → 0.21.4

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.
Files changed (27) hide show
  1. package/dist/adapters/azure/__tests__/cloud-account-service.test.js +9 -1
  2. package/dist/adapters/azure/cloud-account-service.js +7 -0
  3. package/dist/adapters/commander/commands/__tests__/add.test.js +12 -4
  4. package/dist/adapters/commander/commands/add.js +5 -2
  5. package/dist/adapters/pagopa-technology/__tests__/authorization.test.js +349 -31
  6. package/dist/adapters/pagopa-technology/azure-authorization-config.d.ts +13 -0
  7. package/dist/adapters/pagopa-technology/azure-authorization-config.js +43 -0
  8. package/dist/adapters/pagopa-technology/{authorization.d.ts → azure-authorization.d.ts} +2 -2
  9. package/dist/adapters/pagopa-technology/azure-authorization.js +239 -0
  10. package/dist/adapters/plop/actions/__tests__/init-cloud-accounts.test.js +7 -0
  11. package/dist/adapters/plop/actions/__tests__/provision-terraform-backend.test.js +3 -0
  12. package/dist/adapters/plop/generators/environment/__tests__/actions.test.js +1 -0
  13. package/dist/adapters/plop/generators/environment/__tests__/prompts.test.js +96 -2
  14. package/dist/adapters/plop/generators/environment/prompts.d.ts +1 -0
  15. package/dist/adapters/plop/generators/environment/prompts.js +6 -0
  16. package/dist/domain/authorization.d.ts +6 -9
  17. package/dist/domain/authorization.js +27 -10
  18. package/dist/domain/github.d.ts +1 -0
  19. package/dist/domain/github.js +1 -0
  20. package/dist/index.js +2 -2
  21. package/dist/use-cases/__tests__/request-authorization.test.js +5 -3
  22. package/dist/use-cases/request-authorization.d.ts +2 -2
  23. package/dist/use-cases/request-authorization.js +2 -2
  24. package/package.json +1 -1
  25. package/templates/environment/bootstrapper/{{env.name}}/data.tf.hbs +0 -16
  26. package/templates/environment/bootstrapper/{{env.name}}/main.tf.hbs +2 -14
  27. package/dist/adapters/pagopa-technology/authorization.js +0 -124
@@ -2,14 +2,14 @@
2
2
  * Request Authorization Use Case
3
3
  *
4
4
  * Orchestrates an authorization request by delegating to the
5
- * technology-agnostic AuthorizationService.
5
+ * AuthorizationService.
6
6
  */
7
7
  import { ResultAsync } from "neverthrow";
8
8
  import { AuthorizationError, AuthorizationResult, AuthorizationService, RequestAuthorizationInput } from "../domain/authorization.js";
9
9
  /**
10
10
  * Creates a function that requests authorization for a bootstrap identity.
11
11
  *
12
- * @param authorizationService - The service handling platform-specific authorization logic
12
+ * @param authorizationService - The service handling authorization logic
13
13
  * @returns A function that takes input and returns a ResultAsync with the authorization result
14
14
  */
15
15
  export declare const requestAuthorization: (authorizationService: AuthorizationService) => (input: RequestAuthorizationInput) => ResultAsync<AuthorizationResult, AuthorizationError>;
@@ -2,12 +2,12 @@
2
2
  * Request Authorization Use Case
3
3
  *
4
4
  * Orchestrates an authorization request by delegating to the
5
- * technology-agnostic AuthorizationService.
5
+ * AuthorizationService.
6
6
  */
7
7
  /**
8
8
  * Creates a function that requests authorization for a bootstrap identity.
9
9
  *
10
- * @param authorizationService - The service handling platform-specific authorization logic
10
+ * @param authorizationService - The service handling authorization logic
11
11
  * @returns A function that takes input and returns a ResultAsync with the authorization result
12
12
  */
13
13
  export const requestAuthorization = (authorizationService) => (input) => authorizationService.requestAuthorization(input);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/dx-cli",
3
- "version": "0.21.2",
3
+ "version": "0.21.4",
4
4
  "type": "module",
5
5
  "description": "A CLI useful to manage DX tools.",
6
6
  "repository": {
@@ -13,10 +13,6 @@ data "azuread_group" "externals" {
13
13
  {{/with}}
14
14
 
15
15
  {{#with includesProdIO}}
16
- data "azurerm_subscription" "PROD_IO" {
17
- provider = azurerm.PROD-IO
18
- }
19
-
20
16
  data "azurerm_resource_group" "common_itn_01" {
21
17
  provider = azurerm.PROD-IO
22
18
  name = "io-p-itn-common-rg-01"
@@ -38,21 +34,9 @@ data "azurerm_container_app_environment" "runner" {
38
34
  resource_group_name = "io-p-itn-github-runner-rg-01"
39
35
  }
40
36
 
41
- data "azurerm_api_management" "apim" {
42
- provider = azurerm.PROD-IO
43
- name = "io-p-itn-apim-01"
44
- resource_group_name = data.azurerm_resource_group.common_itn_01.name
45
- }
46
-
47
37
  data "azurerm_key_vault" "common" {
48
38
  provider = azurerm.PROD-IO
49
39
  name = "io-p-kv-common"
50
40
  resource_group_name = data.azurerm_resource_group.common_weu.name
51
41
  }
52
-
53
- data "azurerm_virtual_network" "common" {
54
- provider = azurerm.PROD-IO
55
- name = "io-p-itn-common-vnet-01"
56
- resource_group_name = data.azurerm_resource_group.common_itn_01.name
57
- }
58
42
  {{/with}}
@@ -8,7 +8,7 @@ locals {
8
8
  {{#if (eq displayName "PROD-IO")}}
9
9
  module "azure-{{displayName}}_bootstrap" {
10
10
  source = "pagopa-dx/azure-github-environment-bootstrap/azurerm"
11
- version = "~> 3.0"
11
+ version = "~> 4.0"
12
12
 
13
13
  providers = {
14
14
  azurerm = azurerm.{{displayName}}
@@ -16,9 +16,6 @@ module "azure-{{displayName}}_bootstrap" {
16
16
 
17
17
  environment = merge(local.environment, local.azure_accounts.{{displayName}})
18
18
 
19
- subscription_id = data.azurerm_subscription.PROD_IO.id
20
- tenant_id = data.azurerm_subscription.PROD_IO.tenant_id
21
-
22
19
  entraid_groups = {
23
20
  admins_object_id = data.azuread_group.admins.object_id
24
21
  devs_object_id = data.azuread_group.developers.object_id
@@ -37,7 +34,6 @@ module "azure-{{displayName}}_bootstrap" {
37
34
 
38
35
  github_private_runner = {
39
36
  container_app_environment_id = data.azurerm_container_app_environment.runner.id
40
- container_app_environment_location = data.azurerm_container_app_environment.runner.location
41
37
  key_vault = {
42
38
  name = data.azurerm_key_vault.common.name
43
39
  resource_group_name = data.azurerm_key_vault.common.resource_group_name
@@ -45,10 +41,7 @@ module "azure-{{displayName}}_bootstrap" {
45
41
  use_github_app = true
46
42
  }
47
43
 
48
- apim_id = data.azurerm_api_management.apim.id
49
- pep_vnet_id = data.azurerm_virtual_network.common.id
50
44
  private_dns_zone_resource_group_id = data.azurerm_resource_group.common_weu.id
51
- nat_gateway_resource_group_id = data.azurerm_resource_group.common_itn_01.id
52
45
  opex_resource_group_id = data.azurerm_resource_group.dashboards.id
53
46
 
54
47
  tags = local.bootstrapper_tags
@@ -73,7 +66,7 @@ module "azure-{{displayName}}_core_values" {
73
66
 
74
67
  module "azure-{{displayName}}_bootstrap" {
75
68
  source = "pagopa-dx/azure-github-environment-bootstrap/azurerm"
76
- version = "~> 3.0"
69
+ version = "~> 4.0"
77
70
 
78
71
  providers = {
79
72
  azurerm = azurerm.{{displayName}}
@@ -81,9 +74,6 @@ module "azure-{{displayName}}_bootstrap" {
81
74
 
82
75
  environment = merge(local.environment, local.azure_accounts.{{displayName}})
83
76
 
84
- subscription_id = module.azure-{{displayName}}_core_values.subscription_id
85
- tenant_id = module.azure-{{displayName}}_core_values.tenant_id
86
-
87
77
  entraid_groups = {
88
78
  admins_object_id = data.azuread_group.admins.object_id
89
79
  devs_object_id = data.azuread_group.developers.object_id
@@ -102,7 +92,6 @@ module "azure-{{displayName}}_bootstrap" {
102
92
 
103
93
  github_private_runner = {
104
94
  container_app_environment_id = module.azure-{{displayName}}_core_values.github_runner.environment_id
105
- container_app_environment_location = local.azure_accounts.{{displayName}}.location
106
95
  labels = [
107
96
  "{{@root.env.name}}"
108
97
  ]
@@ -114,7 +103,6 @@ module "azure-{{displayName}}_bootstrap" {
114
103
  use_github_app = true
115
104
  }
116
105
 
117
- pep_vnet_id = module.azure-{{displayName}}_core_values.common_vnet.id
118
106
  private_dns_zone_resource_group_id = module.azure-{{displayName}}_core_values.network_resource_group_id
119
107
  opex_resource_group_id = module.azure-{{displayName}}_core_values.opex_resource_group_id
120
108
 
@@ -1,124 +0,0 @@
1
- /**
2
- * PagoPA Technology Authorization Adapter
3
- *
4
- * Implements the AuthorizationService interface for the PagoPA Azure
5
- * authorization workflow. Encapsulates all platform-specific details:
6
- * the target GitHub repository, file paths, branch naming, JSON file
7
- * parsing, and pull request creation.
8
- */
9
- import { getLogger } from "@logtape/logtape";
10
- import { err, errAsync, ok, okAsync, ResultAsync } from "neverthrow";
11
- import { z } from "zod";
12
- import { AuthorizationError, AuthorizationResult, IdentityAlreadyExistsError, InvalidAuthorizationFileFormatError, } from "../../domain/authorization.js";
13
- const authorizationFileSchema = z
14
- .object({
15
- directory_readers: z
16
- .object({
17
- service_principals_name: z.array(z.string()),
18
- })
19
- .loose(),
20
- })
21
- .loose();
22
- const addIdentity = (content, identityId) => {
23
- let parsed;
24
- try {
25
- parsed = JSON.parse(content);
26
- }
27
- catch {
28
- return err(new InvalidAuthorizationFileFormatError("File content is not valid JSON"));
29
- }
30
- const result = authorizationFileSchema.safeParse(parsed);
31
- if (!result.success) {
32
- return err(new InvalidAuthorizationFileFormatError("Could not find directory_readers.service_principals_name list"));
33
- }
34
- const jsonContent = result.data;
35
- if (jsonContent.directory_readers.service_principals_name.includes(identityId)) {
36
- return err(new IdentityAlreadyExistsError(identityId));
37
- }
38
- const updated = {
39
- ...jsonContent,
40
- directory_readers: {
41
- ...jsonContent.directory_readers,
42
- service_principals_name: [
43
- ...jsonContent.directory_readers.service_principals_name,
44
- identityId,
45
- ],
46
- },
47
- };
48
- return ok(JSON.stringify(updated, null, 2));
49
- };
50
- const REPO_OWNER = "pagopa";
51
- const REPO_NAME = "eng-azure-authorization";
52
- const BASE_BRANCH = "main";
53
- export const makeAuthorizationService = (gitHubService) => ({
54
- requestAuthorization(input) {
55
- const logger = getLogger(["dx-cli", "pagopa-authorization"]);
56
- const { bootstrapIdentityId, repoName, subscriptionName } = input;
57
- const filePath = `src/azure-subscriptions/subscriptions/${subscriptionName}/terraform.tfvars.json`;
58
- const branchName = `feats/add-${repoName}-${subscriptionName}-bootstrap-identity`;
59
- return (
60
- // Step 1: Create branch first to avoid race condition with main branch updates
61
- ResultAsync.fromPromise(gitHubService.createBranch({
62
- branchName,
63
- fromRef: BASE_BRANCH,
64
- owner: REPO_OWNER,
65
- repo: REPO_NAME,
66
- }), () => new AuthorizationError(`Unable to create branch ${branchName} in ${REPO_OWNER}/${REPO_NAME}`))
67
- .orTee((error) => {
68
- logger.error(error.message);
69
- })
70
- // Step 2: Fetch file content from the newly created branch
71
- .andThen(() => ResultAsync.fromPromise(gitHubService.getFileContent({
72
- owner: REPO_OWNER,
73
- path: filePath,
74
- ref: branchName,
75
- repo: REPO_NAME,
76
- }), () => new AuthorizationError(`Unable to get ${filePath} in ${REPO_OWNER}/${REPO_NAME}`)))
77
- .orTee((error) => {
78
- logger.error(error.message);
79
- })
80
- // Modify the file content, detecting duplicates and format errors
81
- .andThen(({ content, sha }) => addIdentity(content, bootstrapIdentityId)
82
- .mapErr((error) => {
83
- if (error instanceof IdentityAlreadyExistsError) {
84
- logger.warn("Identity already exists", {
85
- identityId: bootstrapIdentityId,
86
- subscription: subscriptionName,
87
- });
88
- }
89
- else {
90
- logger.error("Failed to modify tfvars", {
91
- error: error.message,
92
- });
93
- }
94
- return error;
95
- })
96
- .match((updatedContent) => okAsync({ sha, updatedContent }), (error) => errAsync(error)))
97
- // Update the file on the new branch
98
- .andThen(({ sha, updatedContent }) => ResultAsync.fromPromise(gitHubService.updateFile({
99
- branch: branchName,
100
- content: updatedContent,
101
- message: `Add directory reader for ${subscriptionName}`,
102
- owner: REPO_OWNER,
103
- path: filePath,
104
- repo: REPO_NAME,
105
- sha,
106
- }), () => new AuthorizationError(`Unable to update ${filePath} on branch ${branchName} in ${REPO_OWNER}/${REPO_NAME}`)))
107
- .orTee((error) => {
108
- logger.error(error.message);
109
- })
110
- // Create a pull request for review
111
- .andThen(() => ResultAsync.fromPromise(gitHubService.createPullRequest({
112
- base: BASE_BRANCH,
113
- body: `This PR adds the bootstrap identity \`${bootstrapIdentityId}\` to the directory readers for subscription \`${subscriptionName}\`.`,
114
- head: branchName,
115
- owner: REPO_OWNER,
116
- repo: REPO_NAME,
117
- title: `Add directory reader for ${subscriptionName}`,
118
- }), () => new AuthorizationError(`Unable to create pull request from ${branchName} to ${BASE_BRANCH} in ${REPO_OWNER}/${REPO_NAME}`)))
119
- .orTee((error) => {
120
- logger.error(error.message);
121
- })
122
- .map((pr) => new AuthorizationResult(pr.url)));
123
- },
124
- });