@stackgenhq/backstage-plugin-stackgen-backend 0.3.6-beta.1
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 +485 -0
- package/config.d.ts +39 -0
- package/dist/actions/core/assign_project_members_action.cjs.js +227 -0
- package/dist/actions/core/assign_project_members_action.cjs.js.map +1 -0
- package/dist/actions/core/create_appstack_action.cjs.js +953 -0
- package/dist/actions/core/create_appstack_action.cjs.js.map +1 -0
- package/dist/actions/core/create_project_action.cjs.js +243 -0
- package/dist/actions/core/create_project_action.cjs.js.map +1 -0
- package/dist/actions/core/download_iac_action.cjs.js +133 -0
- package/dist/actions/core/download_iac_action.cjs.js.map +1 -0
- package/dist/actions/core/set_environment_variable_values_action.cjs.js +121 -0
- package/dist/actions/core/set_environment_variable_values_action.cjs.js.map +1 -0
- package/dist/actions/core/set_state_backend_action.cjs.js +174 -0
- package/dist/actions/core/set_state_backend_action.cjs.js.map +1 -0
- package/dist/actions/exporter/export_iac_to_git_action.cjs.js +290 -0
- package/dist/actions/exporter/export_iac_to_git_action.cjs.js.map +1 -0
- package/dist/actions/exporter/list_vault_secrets_action.cjs.js +39 -0
- package/dist/actions/exporter/list_vault_secrets_action.cjs.js.map +1 -0
- package/dist/config/constants.cjs.js +23 -0
- package/dist/config/constants.cjs.js.map +1 -0
- package/dist/exporter/exportOverrides.cjs.js +27 -0
- package/dist/exporter/exportOverrides.cjs.js.map +1 -0
- package/dist/exporter/gitExporterConfigHttp.cjs.js +105 -0
- package/dist/exporter/gitExporterConfigHttp.cjs.js.map +1 -0
- package/dist/exporter/http.cjs.js +14 -0
- package/dist/exporter/http.cjs.js.map +1 -0
- package/dist/exporter/paths.cjs.js +25 -0
- package/dist/exporter/paths.cjs.js.map +1 -0
- package/dist/exporter/projectGitExporterTemplate.cjs.js +62 -0
- package/dist/exporter/projectGitExporterTemplate.cjs.js.map +1 -0
- package/dist/exporter/topologyResolve.cjs.js +71 -0
- package/dist/exporter/topologyResolve.cjs.js.map +1 -0
- package/dist/index.cjs.js +31 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +224 -0
- package/dist/lib/extractArchive.cjs.js +62 -0
- package/dist/lib/extractArchive.cjs.js.map +1 -0
- package/dist/modules/stackgenExporterScaffolderModule.cjs.js +29 -0
- package/dist/modules/stackgenExporterScaffolderModule.cjs.js.map +1 -0
- package/dist/modules/stackgenUiProxiesModule.cjs.js +366 -0
- package/dist/modules/stackgenUiProxiesModule.cjs.js.map +1 -0
- package/dist/plugin/plugin.cjs.js +136 -0
- package/dist/plugin/plugin.cjs.js.map +1 -0
- package/dist/plugin/router.cjs.js +341 -0
- package/dist/plugin/router.cjs.js.map +1 -0
- package/dist/services/MappingsService/mappingsService.cjs.js +149 -0
- package/dist/services/MappingsService/mappingsService.cjs.js.map +1 -0
- package/dist/services/StackgenService/stackgenService.cjs.js +267 -0
- package/dist/services/StackgenService/stackgenService.cjs.js.map +1 -0
- package/dist/utils/api-objects.cjs.js +205 -0
- package/dist/utils/api-objects.cjs.js.map +1 -0
- package/dist/vendor/stackgen-api-client/index.cjs.js +47681 -0
- package/dist/vendor/stackgen-api-client/index.cjs.js.map +1 -0
- package/migrations/20250131055302_init.js +24 -0
- package/migrations/20250305123456_alter_type_associations.js +23 -0
- package/package.json +86 -0
- package/templates/appstack-configure-environment.yaml +81 -0
- package/templates/appstack-from-template.yaml +76 -0
- package/templates/appstack-with-modules.yaml +69 -0
- package/templates/appstack-with-resources.yaml +81 -0
- package/templates/export-iac.yaml +97 -0
- package/templates/full-infra-setup.yaml +156 -0
- package/templates/git_export_exporter_multi_scm.yaml +123 -0
- package/templates/project-advanced.yaml +77 -0
- package/templates/project-basic.yaml +39 -0
- package/templates/project-with-members.yaml +86 -0
package/README.md
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# Backstage Plugin StackGen Backend
|
|
2
|
+
|
|
3
|
+
Published to **[npmjs.org](https://www.npmjs.com/package/@stackgenhq/backstage-plugin-stackgen-backend)** as `@stackgenhq/backstage-plugin-stackgen-backend`. Install works without a GitHub PAT. Release process: [docs/PUBLISH.md](docs/PUBLISH.md).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
This plugin is installed via the `@stackgenhq/backstage-plugin-stackgen-backend` package. To install it to your backend package, run the following command:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# From your root directory
|
|
11
|
+
yarn --cwd packages/backend add @stackgenhq/backstage-plugin-stackgen-backend
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
No `.yarnrc.yml` GitHub Packages scope is required for released versions.
|
|
15
|
+
Then add the plugin to your backend in `packages/backend/src/index.ts`:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
const backend = createBackend();
|
|
19
|
+
// ...
|
|
20
|
+
backend.add(import('@stackgenhq/backstage-plugin-stackgen-backend'));
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
add configuration required for plugin in backstage app-config.yaml
|
|
25
|
+
```yaml
|
|
26
|
+
stackGen:
|
|
27
|
+
# The base URL for the StackGen API. Default: `https://cloud.stackgen.com`
|
|
28
|
+
baseUrl: "${BACKSTAGE_ADAPTER_URL}"
|
|
29
|
+
apiToken: "${STACKGEN_PAT}"
|
|
30
|
+
# Optional: role assigned when createAppStack auto-adds PAT user to project/team
|
|
31
|
+
# Defaults to admin role 00000000-0000-0000-0000-000000000002
|
|
32
|
+
projectMemberRoleId: "${STACKGEN_PROJECT_MEMBER_ROLE_ID}"
|
|
33
|
+
allowedTeams: ""
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
allowedTeams is optional so by default, all the teams the STACKGEN_PAT has access will be available.
|
|
37
|
+
Access can be restricted to a few teams by adding their ids in the configuration. exa:
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
stackGen:
|
|
41
|
+
baseUrl: "${BACKSTAGE_ADAPTER_URL}"
|
|
42
|
+
apiToken: "${STACKGEN_PAT}"
|
|
43
|
+
allowedTeams: "20f0e211-15ce-4d2c-1e12-0555bffee7bd,20f0e211-15ce-4d2c-2e22-0555bffee7bd"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## API specification
|
|
47
|
+
|
|
48
|
+
The canonical Integrations Gateway API spec is maintained in the [integrations](https://github.com/appcd-dev/integrations) repo:
|
|
49
|
+
|
|
50
|
+
**https://github.com/appcd-dev/integrations/blob/main/api-docs/api-spec.yml**
|
|
51
|
+
|
|
52
|
+
Use that spec as the source of truth for request/response schemas and endpoints. This plugin is not generated from the spec; implementation is in code and may be updated independently.
|
|
53
|
+
|
|
54
|
+
## Compatibility
|
|
55
|
+
|
|
56
|
+
| Package | Tested / required notes |
|
|
57
|
+
|---------|-------------------------|
|
|
58
|
+
| `@backstage/plugin-scaffolder-node` | Declared dependency **`^0.12.5`** (tested). Schema declarations use the **function form** (`schema.input: () => zodObject` or `(z) => z.object(…)`) required by `parseSchemas`. Do not pass raw `ZodObject` instances — modern scaffolder-node ignores them and leaves `schema.input` / `schema.output` undefined. Older scaffolder-node **0.6.x** accepted raw Zod objects via `safeParseAsync` detection; that path is obsolete. |
|
|
59
|
+
| Node.js | `>=20` |
|
|
60
|
+
|
|
61
|
+
**ENG-3394:** Releases **≤0.3.2** (built against scaffolder-node 0.6.x with raw ZodObject schemas) show blank input fields on Backstage “Installed actions” when paired with scaffolder-node **≥0.8** (including **0.12.5**) and skip scaffolder upfront validation. Fixed in **0.3.3** (package track `0.3.3-beta.3+`): function-form schemas + scaffolder-node `^0.12.5`.
|
|
62
|
+
|
|
63
|
+
## Available Actions
|
|
64
|
+
|
|
65
|
+
### Setup
|
|
66
|
+
|
|
67
|
+
To use these actions, add the following code in your backend in `packages/backend/src/index.ts`:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { createAppStackAction, createProjectAction, downloadIaCAction, exportAppStackToGitAction } from '@stackgenhq/backstage-plugin-stackgen-backend';
|
|
71
|
+
import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node';
|
|
72
|
+
import { coreServices, createBackendModule } from '@backstage/backend-plugin-api';
|
|
73
|
+
|
|
74
|
+
...
|
|
75
|
+
|
|
76
|
+
export const scaffolderCustomExtension = createBackendModule({
|
|
77
|
+
pluginId: 'scaffolder',
|
|
78
|
+
moduleId: 'custom-extensions',
|
|
79
|
+
register(env) {
|
|
80
|
+
env.registerInit({
|
|
81
|
+
deps: {
|
|
82
|
+
scaffolder: scaffolderActionsExtensionPoint,
|
|
83
|
+
config: coreServices.rootConfig,
|
|
84
|
+
logger: coreServices.logger,
|
|
85
|
+
},
|
|
86
|
+
async init({ scaffolder, config, logger }) {
|
|
87
|
+
scaffolder.addActions(
|
|
88
|
+
createProjectAction(config, logger),
|
|
89
|
+
createAppStackAction(config, logger),
|
|
90
|
+
downloadIaCAction(config, logger),
|
|
91
|
+
exportAppStackToGitAction(config, logger),
|
|
92
|
+
// ...other actions
|
|
93
|
+
);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
backend.add(scaffolderCustomExtension);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### stackGen:createProject
|
|
105
|
+
|
|
106
|
+
Creates a Project in StackGen with optional git and environment configuration.
|
|
107
|
+
|
|
108
|
+
A project in StackGen maps to a team. After creation, the `teamId` output can be passed to `stackGen:createAppStack` to create an appstack under that project/team.
|
|
109
|
+
|
|
110
|
+
**Inputs:**
|
|
111
|
+
|
|
112
|
+
| Field | Type | Required | Description |
|
|
113
|
+
|-------|------|----------|-------------|
|
|
114
|
+
| `project.name` | string | yes | Project name |
|
|
115
|
+
| `project.description` | string | yes | Project description |
|
|
116
|
+
| `project.gitConfig` | object | no | Default git configuration (scmType, repoUrl, targetBranch, path, vaultReference) |
|
|
117
|
+
| `project.environmentConfig` | object | no | Default environment configuration (templates, variables, overrides) |
|
|
118
|
+
|
|
119
|
+
**Outputs:**
|
|
120
|
+
|
|
121
|
+
| Field | Type | Description |
|
|
122
|
+
|-------|------|-------------|
|
|
123
|
+
| `projectId` | string | UUID of the created project |
|
|
124
|
+
| `projectName` | string | Name of the project |
|
|
125
|
+
| `teamId` | string | UUID of the team (same as project; use this for appstack creation) |
|
|
126
|
+
| `alreadyExisted` | boolean | `true` if a project with this name already existed (409), `false` if newly created (201) |
|
|
127
|
+
|
|
128
|
+
**Status code handling:**
|
|
129
|
+
|
|
130
|
+
- **201** -- Project created. Outputs `projectId`, `projectName`, `teamId`, `alreadyExisted: false`.
|
|
131
|
+
- **409** -- Project already exists. Treated as success. Outputs `projectId`, `projectName`, `teamId`, `alreadyExisted: true`.
|
|
132
|
+
- **403** -- Throws error: invalid token permissions.
|
|
133
|
+
- **400** -- Throws error: invalid project name.
|
|
134
|
+
|
|
135
|
+
Important behavior for project initialization:
|
|
136
|
+
|
|
137
|
+
- Auxiliary initialization settings (for example environment templates, variables, overrides, default configs like `gitConfig`/`environmentConfig`) are only applied when the plugin actually creates a new project.
|
|
138
|
+
- If the requested project already exists (`409` path), the action returns that project/team identifiers and proceeds, but it does **not** patch/update the existing project with those extra settings.
|
|
139
|
+
- Updating existing projects with those parameters is planned for a future update.
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
```yaml
|
|
143
|
+
steps:
|
|
144
|
+
- id: create-project
|
|
145
|
+
name: Create StackGen Project
|
|
146
|
+
action: stackGen:createProject
|
|
147
|
+
input:
|
|
148
|
+
project:
|
|
149
|
+
name: ${{ parameters.projectName }}
|
|
150
|
+
description: "Project created via Backstage"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Example with git and environment config:
|
|
154
|
+
```yaml
|
|
155
|
+
steps:
|
|
156
|
+
- id: create-project
|
|
157
|
+
name: Create StackGen Project
|
|
158
|
+
action: stackGen:createProject
|
|
159
|
+
input:
|
|
160
|
+
project:
|
|
161
|
+
name: ${{ parameters.projectName }}
|
|
162
|
+
description: "Project created via Backstage"
|
|
163
|
+
gitConfig:
|
|
164
|
+
name: "default-git-config"
|
|
165
|
+
type: "git"
|
|
166
|
+
parameters:
|
|
167
|
+
scmType: "GITHUB"
|
|
168
|
+
repoUrl: "https://github.com/org/repo.git"
|
|
169
|
+
targetBranch: "main"
|
|
170
|
+
vaultReference: "550e8400-e29b-41d4-a716-446655440000"
|
|
171
|
+
default: true
|
|
172
|
+
environmentConfig:
|
|
173
|
+
name: "default-env-config"
|
|
174
|
+
environmentTemplates:
|
|
175
|
+
- name: "production"
|
|
176
|
+
color: "#FF0000"
|
|
177
|
+
stateBackendTemplate:
|
|
178
|
+
type: "S3"
|
|
179
|
+
configJson: '{"bucket": "my-terraform-state"}'
|
|
180
|
+
variables:
|
|
181
|
+
- name: "region"
|
|
182
|
+
type: "string"
|
|
183
|
+
defaultValueJson: '"us-east-1"'
|
|
184
|
+
description: "AWS region for deployment"
|
|
185
|
+
variableOverrides:
|
|
186
|
+
- variableName: "region"
|
|
187
|
+
environmentTemplateName: "production"
|
|
188
|
+
valueJson: '"us-west-2"'
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Environment variable value types (`defaultValueJson` and `valueJson`):
|
|
192
|
+
|
|
193
|
+
- These fields are **JSON literals encoded as strings** in YAML.
|
|
194
|
+
- The plugin passes them through to StackGen, so you can use non-string JSON values.
|
|
195
|
+
- Set `variables[].type` to match your intended semantic type in StackGen.
|
|
196
|
+
|
|
197
|
+
Examples:
|
|
198
|
+
|
|
199
|
+
```yaml
|
|
200
|
+
variables:
|
|
201
|
+
- name: "region"
|
|
202
|
+
type: "string"
|
|
203
|
+
defaultValueJson: '"us-east-1"'
|
|
204
|
+
- name: "replicas"
|
|
205
|
+
type: "number"
|
|
206
|
+
defaultValueJson: '3'
|
|
207
|
+
- name: "enableAutoscaling"
|
|
208
|
+
type: "boolean"
|
|
209
|
+
defaultValueJson: 'true'
|
|
210
|
+
- name: "subnetIds"
|
|
211
|
+
type: "list"
|
|
212
|
+
defaultValueJson: '["subnet-1","subnet-2"]'
|
|
213
|
+
- name: "extraTags"
|
|
214
|
+
type: "object"
|
|
215
|
+
defaultValueJson: '{"team":"platform","env":"dev"}'
|
|
216
|
+
|
|
217
|
+
variableOverrides:
|
|
218
|
+
- variableName: "replicas"
|
|
219
|
+
environmentTemplateName: "prod"
|
|
220
|
+
valueJson: '5'
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### stackGen:createAppStack
|
|
224
|
+
|
|
225
|
+
Creates an AppStack in StackGen for a given team with specified resources and configurations.
|
|
226
|
+
|
|
227
|
+
Behavior by input type:
|
|
228
|
+
|
|
229
|
+
- **Before appstack creation**: action resolves the PAT user via `GET /appcd/api/v1/auth/me` and attempts to assign that user to the target project/team (`appstack.teamId`) via `POST /appcd/api/v1/orgs/{projectId}/users` using role `projectMemberRoleId` (default admin). If this preflight assignment fails, appstack creation still proceeds and the membership error is logged as a warning—this usually happens if the PAT user was already a member of the project. If the PAT was newly added, it **stays** a project member after the action completes (no automatic removal).
|
|
230
|
+
- **When no `resourcePackId` is present**: uses the legacy create path `POST /stackgen/v1/appstacks` with payload shape `name`, `coreConfig` (provider, targetCompute), `resources`, and `teamId`.
|
|
231
|
+
- **When `templateAppstackId` is set** (and `resources` is empty): loads the source AppStack via `GET /appcd/api/v1/appstacks/{uuid}`, creates a blank appstack through integrations, then clones topology with `POST /iac-gen/v1/topologies?orgId=…` (`appstackRefId`). Deployment type and IaC type come from the source `coreConfig` (defaults: `k8s`, `Terraform` if missing).
|
|
232
|
+
- **When any `resourcePackId` is present**: uses a two-step integrations-compatible flow:
|
|
233
|
+
1. Create a blank appstack via `POST /integrations/api/v1/projects/{teamId}/appstacks` (empty topology)
|
|
234
|
+
2. Add each resource pack into that topology via `POST /iac-gen/v1/topologies/{topologyId}/resources?orgId={orgScope}`, preserving `resourcePackId` semantics.
|
|
235
|
+
|
|
236
|
+
**Inputs:**
|
|
237
|
+
|
|
238
|
+
| Field | Type | Required | Description |
|
|
239
|
+
|-------|------|----------|-------------|
|
|
240
|
+
| `appstack.name` or `appstack.appstackName` | string | yes | AppStack name |
|
|
241
|
+
| `appstack.teamId` | string | yes | Team UUID (use `teamId` output from `stackGen:createProject`) |
|
|
242
|
+
| `appstack.cloudProvider` | string | conditional | Required unless `templateAppstackId` is set (then read from appcd `coreConfig.provider`) |
|
|
243
|
+
| `appstack.templateAppstackId` | string (UUID) | no | If set: load source AppStack from appcd, then clone topology via iac-gen (`appstackRefId`). Cannot be used with a non-empty `resources` array |
|
|
244
|
+
| `appstack.resources` | array | yes | List of resources (can be empty `[]`) |
|
|
245
|
+
|
|
246
|
+
Integrations `POST …/appstacks` only accepts `name`, `cloudProvider`, and `topology` (no extra metadata fields).
|
|
247
|
+
|
|
248
|
+
Each resource in the array:
|
|
249
|
+
|
|
250
|
+
| Field | Type | Required | Description |
|
|
251
|
+
|-------|------|----------|-------------|
|
|
252
|
+
| `resourceType` | string | yes | Valid StackGen resource type |
|
|
253
|
+
| `configuration` | object | no | Resource configuration (mutually exclusive with `resourcePackId`) |
|
|
254
|
+
| `tfVars` | object | no | Terraform variables |
|
|
255
|
+
| `resourcePackId` | string | no | Resource pack ID (mutually exclusive with `configuration`) |
|
|
256
|
+
| `children` | array | no | Child resources for group resource types |
|
|
257
|
+
|
|
258
|
+
**Outputs:**
|
|
259
|
+
|
|
260
|
+
| Field | Type | Description |
|
|
261
|
+
|-------|------|-------------|
|
|
262
|
+
| `appstackId` | string | UUID of the created appstack |
|
|
263
|
+
| `appstackName` | string | Name of the created appstack |
|
|
264
|
+
| `appStackURL` | string | Direct URL to the created appstack in StackGen |
|
|
265
|
+
|
|
266
|
+
Example (chained with project creation):
|
|
267
|
+
```yaml
|
|
268
|
+
steps:
|
|
269
|
+
- id: create-project
|
|
270
|
+
name: Create StackGen Project
|
|
271
|
+
action: stackGen:createProject
|
|
272
|
+
input:
|
|
273
|
+
project:
|
|
274
|
+
name: ${{ parameters.projectName }}
|
|
275
|
+
description: "Project created via Backstage"
|
|
276
|
+
|
|
277
|
+
- id: stackGen
|
|
278
|
+
name: Create AppStack
|
|
279
|
+
action: stackGen:createAppStack
|
|
280
|
+
input:
|
|
281
|
+
appstack:
|
|
282
|
+
name: "MyAppStack"
|
|
283
|
+
# teamId comes from the output of the create-project step
|
|
284
|
+
teamId: ${{ steps['create-project'].output.teamId }}
|
|
285
|
+
cloudProvider: "aws"
|
|
286
|
+
resources:
|
|
287
|
+
- resourceType: "aws_s3"
|
|
288
|
+
# Only one of 'configuration' or 'resourcePackId' should be provided
|
|
289
|
+
configuration:
|
|
290
|
+
bucket_name: "mybucket"
|
|
291
|
+
- resourceType: "resourcePack"
|
|
292
|
+
resourcePackId: "20f0e212-15ce-4d2c-1e12-0555bffee7bd"
|
|
293
|
+
# No configuration here because resourcePackId is used (legacy path preserves resourcePackId)
|
|
294
|
+
- resourceType: "helm_workload"
|
|
295
|
+
children:
|
|
296
|
+
- resourceType: "helm_workload"
|
|
297
|
+
configuration:
|
|
298
|
+
name: test-workload
|
|
299
|
+
image: test-image
|
|
300
|
+
- resourceType: helm_service
|
|
301
|
+
configuration:
|
|
302
|
+
name: test-service
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Example output:
|
|
306
|
+
```yaml
|
|
307
|
+
output:
|
|
308
|
+
links:
|
|
309
|
+
- title: 'View Created AppStack in StackGen'
|
|
310
|
+
url: '${{ steps.stackGen.output.appStackURL }}'
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Example templates walkthrough
|
|
314
|
+
|
|
315
|
+
The templates in `examples/template/` demonstrate end-to-end StackGen scaffolding flows:
|
|
316
|
+
|
|
317
|
+
| Template | Description |
|
|
318
|
+
|----------|-------------|
|
|
319
|
+
| `stackgen-export-iac` | Manually fetch IaC from an AppStack and push it to GitHub via Backstage — useful when you prefer Backstage to handle the git push instead of StackGen's native git integration. |
|
|
320
|
+
| `stackgen-appstack-with-resources` | Creates a project with environments, then an AppStack with a resource pack, standalone S3 bucket, and custom Terraform module (via `templateId`). |
|
|
321
|
+
| `stackgen-full-setup` | The most comprehensive template: project + Git config + AppStack (resource pack + S3 + custom module) + production/staging environments with per-environment TF variables + S3 state backends. |
|
|
322
|
+
| `stackgen-project-basic` | Creates an empty project — a blank starting point. |
|
|
323
|
+
| `stackgen-project-advanced` | Creates a project with Git integration, environments, variables, and per-environment overrides. |
|
|
324
|
+
|
|
325
|
+
Project initialization in these templates can include:
|
|
326
|
+
|
|
327
|
+
- Environment templates (for example `dev`, `stage`, `prod`).
|
|
328
|
+
- Default variables and per-environment variable overrides.
|
|
329
|
+
- Git configuration via `project.gitConfig`.
|
|
330
|
+
- Per-environment storage backend configuration via `stateBackendTemplate`.
|
|
331
|
+
|
|
332
|
+
## Disabling the Frontend Plugin
|
|
333
|
+
|
|
334
|
+
The StackGen plugin includes an optional frontend component that adds a "StackGen" item to the Backstage sidebar. If you only need the backend scaffolder actions (templates) and don't want the sidebar entry, you can toggle it off by commenting out two lines:
|
|
335
|
+
|
|
336
|
+
**1. Remove the sidebar item** in `packages/app/src/components/Root/Root.tsx`:
|
|
337
|
+
```tsx
|
|
338
|
+
// Comment out this line:
|
|
339
|
+
{/* <SidebarItem icon={StackGenIcon} to="stackgen" text="StackGen"/> */}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**2. Remove the route** in `packages/app/src/App.tsx`:
|
|
343
|
+
```tsx
|
|
344
|
+
// Comment out this line:
|
|
345
|
+
{/* <Route path="/stackgen" element={<StackGenPage />} /> */}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
The imports can remain in place — they'll be unused but won't cause errors. To re-enable the frontend later, simply uncomment both lines and restart Backstage.
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
### stackGen:downloadIaC
|
|
352
|
+
|
|
353
|
+
This action downloads and extracts the Infrastructure as Code (IaC) files for a specified AppStack from StackGen.
|
|
354
|
+
|
|
355
|
+
Example input:
|
|
356
|
+
```yaml
|
|
357
|
+
steps:
|
|
358
|
+
- id: download-iac
|
|
359
|
+
name: Download IaC
|
|
360
|
+
action: stackGen:downloadIaC
|
|
361
|
+
input:
|
|
362
|
+
appstackName: "MyAppStack"
|
|
363
|
+
# Optional: teamId can be omitted if not needed
|
|
364
|
+
teamId: "team-123"
|
|
365
|
+
# Optional: path where IaC files should be extracted, defaults to "./"
|
|
366
|
+
extractedIaCPath: "./infrastructure"
|
|
367
|
+
```
|
|
368
|
+
### stackGen:exportToGit
|
|
369
|
+
|
|
370
|
+
This action is a compatibility wrapper over the new stack-exporter flow.
|
|
371
|
+
It resolves topology for `teamId + appstackName`, selects an existing git exporter
|
|
372
|
+
config for the team (or uses an explicit id), and triggers exporter `POST /export`.
|
|
373
|
+
|
|
374
|
+
**Inputs (high level)**
|
|
375
|
+
|
|
376
|
+
| Field | Required | Description |
|
|
377
|
+
|-------|----------|-------------|
|
|
378
|
+
| `appstackName`, `teamId` | yes | AppStack and team / org id |
|
|
379
|
+
| `configId` | no | When set (non-empty), used as stack-exporter git `config_id`; skips listing, `chooseConfig`, and bootstrap. **Precedence:** `configId` overrides `gitConfig`. In Software Templates, map from the frontend field **`existingExporterConfigId`** (`ExporterGitConfigSelect`). |
|
|
380
|
+
| `topologyId`, `appstackId` | no | Optional topology / appstack hints for resolution |
|
|
381
|
+
| `gitConfig` | no | Bootstrap shape (same as `createProject.project.gitConfig`). Ignored when `configId` is set. |
|
|
382
|
+
| `baseBranch` | no | Branch to base the change on (PR base / commit destination). Maps to the exporter `target_branch` override. Blank uses the git config default. Prefill via `ExporterBaseBranch`. |
|
|
383
|
+
| `pushBranch` | no | Branch StackGen pushes the generated IaC to (source/head branch). Maps to the exporter `branch_format` override. Blank creates a new one. Prefill via `ExporterPushBranch.branch`. |
|
|
384
|
+
| `useSameBranch` | no | When `true`, sets exporter `use_same_branch=true` (reuse previous export head). Set by `ExporterPushBranch` when the history suggestion is left unchanged. |
|
|
385
|
+
| `createPr` | no | Boolean, default `true`. When `false`, changes are committed directly to the branch without opening a PR (exporter `commit_only`). |
|
|
386
|
+
| `overrides` | no | Raw exporter overrides (e.g. `pr_title`, `commit_msg`). An explicit entry wins over the derived key for the same override. |
|
|
387
|
+
|
|
388
|
+
These give the Backstage plugin parity with the StackGen UI git export (base branch, push branch, create PR). `baseBranch`, `pushBranch`, `useSameBranch`, and `createPr` are translated to exporter override keys in one place (`buildExportOverrides`), so templates do not need to know the internal keys.
|
|
389
|
+
|
|
390
|
+
**Outputs**
|
|
391
|
+
|
|
392
|
+
| Field | When present | Description |
|
|
393
|
+
|-------|--------------|-------------|
|
|
394
|
+
| `pullRequestUrl`, `externalLink` | PR created (`createPr` true) | URL of the opened pull request |
|
|
395
|
+
| `committedBranch` | commit-only (`createPr` false) | Branch StackGen committed the changes to |
|
|
396
|
+
| `notes` | always (when returned) | Human-readable export notes (e.g. "committed to branch X") |
|
|
397
|
+
|
|
398
|
+
> A commit-only export (`createPr: false`) does not open a pull request, so `pullRequestUrl` is absent; use `committedBranch` / `notes` in that case.
|
|
399
|
+
|
|
400
|
+
Notes:
|
|
401
|
+
- **Explicit `configId`:** Use when the user picks a config in Backstage (`ExporterGitConfigSelect` writes the config uuid to `existingExporterConfigId`; pass it as `configId` in the action input). **Precedence:** `configId` overrides `gitConfig`.
|
|
402
|
+
- **Listing configs:** If `configId` and `gitConfig` are omitted, `GET .../exporter/.../config?orgId={teamId}&type=git&appstackId={appstackId}` — if a single/default git config exists for that AppStack, it is used for `POST .../export`. Multiple configs require `configId`.
|
|
403
|
+
- **Bootstrap (no git config for the AppStack yet):** Project-level `gitConfig` from `stackGen:createProject` is stored as stack-exporter **templates** (`GET .../exporter/.../config/template?orgId={teamId}`). When the list step finds nothing usable, this action loads that template and `POST .../exporter/.../config?orgId={teamId}` **with `appstack_id`** so the config is scoped to the AppStack.
|
|
404
|
+
- **Optional `gitConfig` on this action:** Same shape as `createProject.project.gitConfig`. When provided (and `configId` is not set), creates a new appstack-scoped config from that input and uses it. Not used when `configId` is set.
|
|
405
|
+
- **`appstackId`:** Optional. When omitted, resolved from `teamId` + `appstackName` via appcd `GET /appstacks` (`appstackId` for exporter binding, `uuid` for topology).
|
|
406
|
+
|
|
407
|
+
Example input:
|
|
408
|
+
```yaml
|
|
409
|
+
steps:
|
|
410
|
+
- id: exportToGit
|
|
411
|
+
name: Export AppStack to Git via StackGen
|
|
412
|
+
action: stackGen:exportToGit
|
|
413
|
+
input:
|
|
414
|
+
appstackName: ${{ parameters.appstackName }}
|
|
415
|
+
teamId: ${{ parameters.teamId }}
|
|
416
|
+
# Optional: explicit exporter git config (from ExporterGitConfigSelect → existingExporterConfigId)
|
|
417
|
+
configId: ${{ parameters.existingExporterConfigId }}
|
|
418
|
+
# gitConfig omitted: uses project template from createProject when no appstack config exists
|
|
419
|
+
# First-class git export controls (parity with the StackGen UI):
|
|
420
|
+
baseBranch: ${{ parameters.baseBranch }} # PR base / commit destination (target_branch)
|
|
421
|
+
pushBranch: ${{ parameters.pushBranch.branch }} # branch StackGen pushes to (branch_format)
|
|
422
|
+
useSameBranch: ${{ parameters.pushBranch.useSameBranch }} # reuse history head when unchanged
|
|
423
|
+
createPr: ${{ parameters.createPr }} # false => commit_only (no PR)
|
|
424
|
+
overrides:
|
|
425
|
+
- key: pr_title
|
|
426
|
+
value: Export IaC from Backstage
|
|
427
|
+
- key: commit_msg
|
|
428
|
+
value: chore: export iac
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
Example output:
|
|
433
|
+
```
|
|
434
|
+
output:
|
|
435
|
+
links:
|
|
436
|
+
- title: 'Pull Request'
|
|
437
|
+
url: '${{ steps.exportToGit.output.pullRequestUrl }}'
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Development
|
|
441
|
+
|
|
442
|
+
Use the [stackgen-backstage](https://github.com/appcd-dev/stackgen-backstage) app to run Backstage with your local backend plugin build.
|
|
443
|
+
|
|
444
|
+
1. **Lay out repositories** so the backend plugin and the Backstage app are siblings (adjust paths below if yours differ):
|
|
445
|
+
|
|
446
|
+
```text
|
|
447
|
+
<workspace>/
|
|
448
|
+
stackgen-backstage/
|
|
449
|
+
backstage-plugin-stackgen-backend/
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
2. **Point the backend at your local plugin** in `stackgen-backstage/packages/backend/package.json`:
|
|
453
|
+
|
|
454
|
+
Set the dependency to a Yarn link relative to `packages/backend` (three levels up to the parent of `stackgen-backstage`, then into the backend plugin repo):
|
|
455
|
+
|
|
456
|
+
```json
|
|
457
|
+
"@stackgenhq/backstage-plugin-stackgen-backend": "link:../../../backstage-plugin-stackgen-backend"
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
If your clone lives elsewhere, change the `link:` path accordingly (it must resolve from `packages/backend` to the root of `backstage-plugin-stackgen-backend`).
|
|
461
|
+
|
|
462
|
+
3. **Build the backend plugin**:
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
yarn install
|
|
466
|
+
yarn tsc && yarn build
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
4. **Install and run stackgen-backstage** from the Backstage repo root:
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
yarn install
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Configure `app-config.yaml` (or local overrides) with `stackGen.baseUrl`, `stackGen.apiToken`, and any optional keys you need (see [Installation](#installation)). The stackgen-backstage README also documents env vars such as `STACKGEN_API_TOKEN` if you wire config that way.
|
|
476
|
+
|
|
477
|
+
5. **Start the app** (frontend + backend in parallel):
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
yarn dev
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Alternatively, run workspaces separately: `yarn start-backend` and `yarn start` (app only), as described in the stackgen-backstage `package.json` scripts.
|
|
484
|
+
|
|
485
|
+
6. **After backend plugin code changes**, rebuild the plugin (`yarn tsc && yarn build` in `backstage-plugin-stackgen-backend`), then install the dependencies again.
|
package/config.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface Config {
|
|
2
|
+
/**
|
|
3
|
+
* @visibility backend
|
|
4
|
+
*/
|
|
5
|
+
stackGen: {
|
|
6
|
+
/**
|
|
7
|
+
* @visibility secret
|
|
8
|
+
*/
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
/**
|
|
11
|
+
* @visibility secret
|
|
12
|
+
*/
|
|
13
|
+
apiToken: string;
|
|
14
|
+
/**
|
|
15
|
+
* @visibility backend
|
|
16
|
+
*/
|
|
17
|
+
allowedTeams: string;
|
|
18
|
+
/**
|
|
19
|
+
* Default StackGen org id when actions omit orgId (optional).
|
|
20
|
+
* @visibility backend
|
|
21
|
+
*/
|
|
22
|
+
orgId?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Base path for iac-gen HTTP API (default /iac-gen).
|
|
25
|
+
* @visibility backend
|
|
26
|
+
*/
|
|
27
|
+
iacGenBasePath?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Base path for stack-exporter HTTP API (default /exporter/api/v1).
|
|
30
|
+
* @visibility backend
|
|
31
|
+
*/
|
|
32
|
+
exporterBasePath?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Base path for stackgen-vault HTTP API (default /api/vault/v1).
|
|
35
|
+
* @visibility backend
|
|
36
|
+
*/
|
|
37
|
+
vaultBasePath?: string;
|
|
38
|
+
};
|
|
39
|
+
}
|