@egulatee/pulumi-stack-alias 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "parser": "@typescript-eslint/parser",
3
+ "extends": [
4
+ "eslint:recommended",
5
+ "plugin:@typescript-eslint/recommended"
6
+ ],
7
+ "plugins": ["@typescript-eslint"],
8
+ "env": {
9
+ "node": true,
10
+ "es6": true
11
+ },
12
+ "parserOptions": {
13
+ "ecmaVersion": 2020,
14
+ "sourceType": "module"
15
+ },
16
+ "rules": {
17
+ "@typescript-eslint/no-explicit-any": "warn",
18
+ "@typescript-eslint/explicit-function-return-type": "off",
19
+ "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
20
+ }
21
+ }
@@ -0,0 +1,63 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ name: Test
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: actions/setup-node@v4
20
+ with:
21
+ node-version: "20"
22
+ cache: "npm"
23
+
24
+ - name: Install dependencies
25
+ run: npm ci
26
+
27
+ - name: Run linter
28
+ run: npm run lint
29
+
30
+ - name: Run tests
31
+ run: npm test
32
+
33
+ - name: Run tests with coverage
34
+ run: npm run test:coverage
35
+
36
+ - name: Upload coverage
37
+ uses: codecov/codecov-action@v3
38
+ with:
39
+ files: ./coverage/coverage-final.json
40
+
41
+ build:
42
+ name: Build
43
+ runs-on: ubuntu-latest
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+
47
+ - uses: actions/setup-node@v4
48
+ with:
49
+ node-version: "20"
50
+ cache: "npm"
51
+
52
+ - name: Install dependencies
53
+ run: npm ci
54
+
55
+ - name: Build TypeScript
56
+ run: npm run build
57
+
58
+ - name: Check build output
59
+ run: |
60
+ test -f dist/index.js || exit 1
61
+ test -f dist/index.d.ts || exit 1
62
+ test -f dist/alias.js || exit 1
63
+ test -f dist/types.js || exit 1
@@ -0,0 +1,35 @@
1
+ name: Publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ publish:
12
+ name: Publish to npm
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: actions/setup-node@v4
18
+ with:
19
+ node-version: "20"
20
+ registry-url: "https://registry.npmjs.org"
21
+ cache: "npm"
22
+
23
+ - name: Install dependencies
24
+ run: npm ci
25
+
26
+ - name: Run tests
27
+ run: npm test
28
+
29
+ - name: Build
30
+ run: npm run build
31
+
32
+ - name: Publish to npm
33
+ run: npm publish --access public
34
+ env:
35
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Eric Gulatee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,496 @@
1
+ # @egulatee/pulumi-stack-alias
2
+
3
+ Producer-side stack aliasing for Pulumi using lightweight proxy stacks. Consumers use standard `StackReference` (zero library dependency), while producers use this library to create alias stacks that re-export outputs from canonical stacks.
4
+
5
+ [![CI](https://github.com/egulatee/pulumi-stack-alias/actions/workflows/ci.yml/badge.svg)](https://github.com/egulatee/pulumi-stack-alias/actions/workflows/ci.yml)
6
+ [![npm version](https://badge.fury.io/js/@egulatee%2Fpulumi-stack-alias.svg)](https://www.npmjs.com/package/@egulatee/pulumi-stack-alias)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ ## Problem
10
+
11
+ When managing infrastructure across multiple environments (dev, staging, prod), consumer projects need to reference shared infrastructure stacks. Traditional approaches either:
12
+ - Force consumers to know which stack holds the real resources
13
+ - Scatter mapping logic across every consumer project
14
+ - Require complex consumer-side resolvers with library dependencies
15
+
16
+ **The aliasing decision belongs with the producer (infrastructure project), not the consumer.**
17
+
18
+ ## Solution: Producer-Controlled Proxy Stacks
19
+
20
+ Alias stacks re-export outputs from canonical stacks. Consumers use standard Pulumi `StackReference` with **no library dependency**. Producers use this library to create lightweight proxy stacks.
21
+
22
+ ### How It Works
23
+
24
+ ```
25
+ infrastructure/shared → canonical stack, exports real resources
26
+ infrastructure/dev → proxy stack, re-exports outputs from shared
27
+ infrastructure/staging → proxy stack, re-exports outputs from shared
28
+ infrastructure/prod → canonical stack, exports real resources
29
+ ```
30
+
31
+ Consumer uses standard Pulumi:
32
+ ```typescript
33
+ const stack = new pulumi.StackReference(`org/infrastructure/${pulumi.getStack()}`);
34
+ const vpcId = stack.requireOutput("vpcId");
35
+ ```
36
+
37
+ **Zero consumer dependencies!** The consumer has no knowledge of aliasing. When `application/dev` deploys, it reads `infrastructure/dev`, which is a proxy stack that re-exports outputs from `infrastructure/shared`.
38
+
39
+ ## Installation
40
+
41
+ **Producer projects only:**
42
+ ```bash
43
+ npm install @egulatee/pulumi-stack-alias
44
+ ```
45
+
46
+ **Consumer projects:** No installation needed! Use standard Pulumi `StackReference`.
47
+
48
+ ## Usage
49
+
50
+ ### Producer Side (Infrastructure Project)
51
+
52
+ Create alias stacks that re-export outputs from canonical stacks:
53
+
54
+ #### Simple Alias
55
+
56
+ ```typescript
57
+ // infrastructure/index.ts
58
+ import { createStackAlias } from "@egulatee/pulumi-stack-alias";
59
+ import * as pulumi from "@pulumi/pulumi";
60
+
61
+ const config = new pulumi.Config();
62
+ const aliasTarget = config.get("aliasTarget");
63
+
64
+ if (aliasTarget) {
65
+ // This is an alias stack — re-export outputs from target
66
+ const alias = createStackAlias({
67
+ targetProject: "infrastructure",
68
+ targetStack: aliasTarget,
69
+ outputs: ["vpcId", "endpoint", "clusterName"],
70
+ });
71
+
72
+ export const vpcId = alias.vpcId;
73
+ export const endpoint = alias.endpoint;
74
+ export const clusterName = alias.clusterName;
75
+ } else {
76
+ // This is a canonical stack — create actual resources
77
+ const vpc = new aws.ec2.Vpc("main", {
78
+ cidrBlock: "10.0.0.0/16",
79
+ });
80
+
81
+ export const vpcId = vpc.id;
82
+ export const endpoint = pulumi.output("https://api.example.com");
83
+ export const clusterName = pulumi.output("my-cluster");
84
+ }
85
+ ```
86
+
87
+ Configure your stack files:
88
+
89
+ ```yaml
90
+ # infrastructure/Pulumi.shared.yaml
91
+ config:
92
+ # No aliasTarget — this is the canonical stack
93
+
94
+ # infrastructure/Pulumi.dev.yaml
95
+ config:
96
+ infrastructure:aliasTarget: shared
97
+
98
+ # infrastructure/Pulumi.staging.yaml
99
+ config:
100
+ infrastructure:aliasTarget: shared
101
+
102
+ # infrastructure/Pulumi.prod.yaml
103
+ config:
104
+ # No aliasTarget — this is a canonical stack (separate from shared)
105
+ ```
106
+
107
+ #### Conditional Alias (Pattern-Based)
108
+
109
+ For automatic aliasing without config:
110
+
111
+ ```typescript
112
+ // infrastructure/index.ts
113
+ import { createConditionalAlias } from "@egulatee/pulumi-stack-alias";
114
+
115
+ const alias = createConditionalAlias({
116
+ targetProject: "infrastructure",
117
+ patterns: [
118
+ { pattern: "*/prod", target: "prod" },
119
+ { pattern: "*/staging", target: "shared" },
120
+ { pattern: "*/dev", target: "shared" },
121
+ { pattern: "*/*-ephemeral", target: "shared" },
122
+ ],
123
+ defaultTarget: "shared",
124
+ outputs: ["vpcId", "endpoint", "clusterName"],
125
+ });
126
+
127
+ export const vpcId = alias.vpcId;
128
+ export const endpoint = alias.endpoint;
129
+ export const clusterName = alias.clusterName;
130
+ ```
131
+
132
+ #### Simple API
133
+
134
+ Simplified API for common cases:
135
+
136
+ ```typescript
137
+ import { createSimpleAlias } from "@egulatee/pulumi-stack-alias";
138
+
139
+ const alias = createSimpleAlias("infrastructure", "shared", ["vpcId"]);
140
+ export const vpcId = alias.vpcId;
141
+ ```
142
+
143
+ ### Consumer Side (Application Project)
144
+
145
+ Use standard Pulumi `StackReference` — **no library dependency**:
146
+
147
+ ```typescript
148
+ // application/index.ts
149
+ import * as pulumi from "@pulumi/pulumi";
150
+ import * as aws from "@pulumi/aws";
151
+
152
+ // Standard Pulumi StackReference — no special library needed!
153
+ const infraStack = new pulumi.StackReference(
154
+ `${pulumi.getOrganization()}/infrastructure/${pulumi.getStack()}`
155
+ );
156
+
157
+ const vpcId = infraStack.requireOutput("vpcId");
158
+ const endpoint = infraStack.requireOutput("endpoint");
159
+
160
+ const subnet = new aws.ec2.Subnet("app-subnet", {
161
+ vpcId: vpcId,
162
+ cidrBlock: "10.0.1.0/24",
163
+ });
164
+ ```
165
+
166
+ The consumer has **zero knowledge** of aliasing. When `application/dev` deploys:
167
+ 1. Reads `infrastructure/dev` (a proxy stack)
168
+ 2. Gets outputs (re-exported from `infrastructure/shared`)
169
+ 3. No library dependency required!
170
+
171
+ ## Deployment Flow
172
+
173
+ ### Initial Setup
174
+
175
+ ```bash
176
+ # Deploy canonical stacks (creates real resources)
177
+ pulumi up --stack shared
178
+ pulumi up --stack prod
179
+
180
+ # Deploy alias stacks (creates proxy outputs)
181
+ pulumi up --stack dev
182
+ pulumi up --stack staging
183
+
184
+ # Consumer deployments — no alias awareness needed
185
+ cd application && pulumi up --stack dev
186
+ ```
187
+
188
+ ### Keeping Outputs Fresh
189
+
190
+ Alias stacks capture outputs at deploy time. When canonical stack outputs change, redeploy aliases:
191
+
192
+ ```bash
193
+ # Update canonical stack
194
+ pulumi up --stack shared
195
+
196
+ # Sync alias stacks (fast — no real resources)
197
+ pulumi up --stack dev
198
+ pulumi up --stack staging
199
+ ```
200
+
201
+ ### CI/CD Orchestration
202
+
203
+ Automate alias synchronization in CI/CD:
204
+
205
+ ```yaml
206
+ # .github/workflows/deploy.yml
207
+ jobs:
208
+ deploy-canonical:
209
+ runs-on: ubuntu-latest
210
+ steps:
211
+ - name: Deploy shared stack
212
+ run: pulumi up --stack shared --yes
213
+
214
+ sync-aliases:
215
+ needs: deploy-canonical
216
+ runs-on: ubuntu-latest
217
+ strategy:
218
+ matrix:
219
+ stack: [dev, staging]
220
+ steps:
221
+ - name: Sync alias stack
222
+ run: pulumi up --stack ${{ matrix.stack }} --yes
223
+ ```
224
+
225
+ Alias deployments are fast (seconds) since they create no real resources — just re-export outputs.
226
+
227
+ ## API Reference
228
+
229
+ ### `createStackAlias(config)`
230
+
231
+ Creates a stack alias that re-exports outputs from a target stack.
232
+
233
+ **Parameters:**
234
+ - `config.targetProject` (string) - Target project name
235
+ - `config.targetStack` (string) - Target stack name
236
+ - `config.targetOrg` (optional string) - Target organization (defaults to current org)
237
+ - `config.outputs` (string[]) - List of output names to re-export
238
+
239
+ **Returns:** `AliasExports` - Record of Pulumi Outputs
240
+
241
+ **Example:**
242
+ ```typescript
243
+ const alias = createStackAlias({
244
+ targetProject: "infrastructure",
245
+ targetStack: "shared",
246
+ outputs: ["vpcId", "endpoint"],
247
+ });
248
+
249
+ export const vpcId = alias.vpcId;
250
+ export const endpoint = alias.endpoint;
251
+ ```
252
+
253
+ ### `createConditionalAlias(config)`
254
+
255
+ Creates a conditional alias based on pattern matching.
256
+
257
+ **Parameters:**
258
+ - `config.targetProject` (string) - Target project name
259
+ - `config.patterns` (PatternRule[]) - Pattern matching rules (evaluated in order, first match wins)
260
+ - `config.defaultTarget` (optional string) - Default target if no pattern matches
261
+ - `config.targetOrg` (optional string) - Target organization (defaults to current org)
262
+ - `config.outputs` (string[]) - List of output names to re-export
263
+
264
+ **Returns:** `AliasExports` - Record of Pulumi Outputs
265
+
266
+ **Example:**
267
+ ```typescript
268
+ const alias = createConditionalAlias({
269
+ targetProject: "infrastructure",
270
+ patterns: [
271
+ { pattern: "*/prod", target: "prod" },
272
+ { pattern: "*/dev", target: "shared" },
273
+ ],
274
+ defaultTarget: "shared",
275
+ outputs: ["vpcId"],
276
+ });
277
+ ```
278
+
279
+ ### `createSimpleAlias(targetProject, targetStack, outputs)`
280
+
281
+ Simplified API for creating aliases.
282
+
283
+ **Parameters:**
284
+ - `targetProject` (string) - Target project name
285
+ - `targetStack` (string) - Target stack name
286
+ - `outputs` (string[]) - List of output names to re-export
287
+
288
+ **Returns:** `AliasExports` - Record of Pulumi Outputs
289
+
290
+ **Example:**
291
+ ```typescript
292
+ const alias = createSimpleAlias("infrastructure", "shared", ["vpcId"]);
293
+ export const vpcId = alias.vpcId;
294
+ ```
295
+
296
+ ### `matchesPattern(pattern, project, stack)`
297
+
298
+ Pattern matching with wildcard support.
299
+
300
+ **Wildcard rules:**
301
+ - `*` matches any value
302
+ - `*-suffix` matches strings ending with `-suffix`
303
+ - `prefix-*` matches strings starting with `prefix-`
304
+ - `exact` matches exactly
305
+
306
+ **Example:**
307
+ ```typescript
308
+ import { matchesPattern } from "@egulatee/pulumi-stack-alias";
309
+
310
+ matchesPattern("*/dev", "myproject", "dev") // true
311
+ matchesPattern("*/*-ephemeral", "app", "pr-123-ephemeral") // true
312
+ matchesPattern("app-*/prod-*", "app-api", "prod-us") // true
313
+ ```
314
+
315
+ ## Pattern Format
316
+
317
+ Patterns follow the format: `"projectPattern/stackPattern"`
318
+
319
+ Examples:
320
+ - `"*/dev"` - Any project, stack must be "dev"
321
+ - `"myproject/*"` - Project must be "myproject", any stack
322
+ - `"*/*-ephemeral"` - Any project, stack must end with "-ephemeral"
323
+ - `"app-*/prod-*"` - Project must start with "app-", stack must start with "prod-"
324
+
325
+ ## Comparison with Other Approaches
326
+
327
+ | Concern | Producer Redirect (v0.1.0) | Producer Proxy (v0.2.0) | Consumer Config |
328
+ |---|---|---|---|
329
+ | Who owns mapping | Producer ✓ | Producer ✓ | Consumer |
330
+ | Consumer library dependency | Yes | **None** ✓ | Varies |
331
+ | Alias stack weight | Trivial (one pointer) | Light (output refs) | N/A |
332
+ | Output freshness | Always live ✓ | Stale until redeployed* | Always live ✓ |
333
+ | Consumer code | `resolveStackRef(...)` | `new StackReference(...)` ✓ | Custom |
334
+ | CI/CD orchestration | Not needed | Recommended | Not needed |
335
+
336
+ \* *Mitigated with CI/CD automation — alias deployments are fast (seconds)*
337
+
338
+ ## Benefits
339
+
340
+ ✅ **Zero consumer dependencies** - Consumers use standard Pulumi `StackReference`
341
+ ✅ **Producer-controlled** - Infrastructure projects own aliasing decisions
342
+ ✅ **Type-safe** - Full TypeScript support
343
+ ✅ **Flexible patterns** - Wildcard pattern matching for conditional aliasing
344
+ ✅ **CI/CD friendly** - Fast alias deployments (no real resources)
345
+ ✅ **Simple API** - Three functions cover all use cases
346
+
347
+ ## Trade-offs
348
+
349
+ **Pros:**
350
+ - Zero consumer library dependency
351
+ - Standard Pulumi patterns
352
+ - Simple mental model
353
+
354
+ **Cons:**
355
+ - Alias stacks need redeployment when canonical outputs change (mitigated with CI/CD)
356
+ - Slightly more operational overhead than redirect pattern (but eliminates consumer dependencies)
357
+
358
+ ## Use Cases
359
+
360
+ ### Shared Development Infrastructure
361
+
362
+ Deploy infrastructure once, route dev/staging to it:
363
+
364
+ ```typescript
365
+ const alias = createConditionalAlias({
366
+ targetProject: "infrastructure",
367
+ patterns: [
368
+ { pattern: "*/prod", target: "prod" },
369
+ ],
370
+ defaultTarget: "shared",
371
+ outputs: ["vpcId", "endpoint"],
372
+ });
373
+ ```
374
+
375
+ ### Ephemeral PR Environments
376
+
377
+ Route ephemeral stacks to shared infrastructure:
378
+
379
+ ```typescript
380
+ const alias = createConditionalAlias({
381
+ targetProject: "infrastructure",
382
+ patterns: [
383
+ { pattern: "*/*-ephemeral", target: "shared" },
384
+ { pattern: "*/prod", target: "prod" },
385
+ ],
386
+ defaultTarget: "shared",
387
+ outputs: ["vpcId"],
388
+ });
389
+ ```
390
+
391
+ ### Multi-Region Deployments
392
+
393
+ Route regional stacks to regional infrastructure:
394
+
395
+ ```typescript
396
+ const alias = createConditionalAlias({
397
+ targetProject: "infrastructure",
398
+ patterns: [
399
+ { pattern: "*/us-*", target: "us-east" },
400
+ { pattern: "*/eu-*", target: "eu-west" },
401
+ ],
402
+ outputs: ["vpcId"],
403
+ });
404
+ ```
405
+
406
+ ## Examples
407
+
408
+ See the [examples](./examples) directory for complete implementations:
409
+ - [Simple Alias](./examples/simple-alias) - Basic proxy stack
410
+ - [Conditional Alias](./examples/conditional-alias) - Pattern-based aliasing
411
+
412
+ ## Migration Guide
413
+
414
+ ### From v0.1.0 (Redirect Pattern) to v0.2.0 (Proxy Pattern)
415
+
416
+ This is a **breaking change** — complete API rewrite.
417
+
418
+ #### Producer Changes
419
+
420
+ **Before (v0.1.0):**
421
+ ```typescript
422
+ // infrastructure/index.ts
423
+ const config = new pulumi.Config();
424
+ const aliasTarget = config.get("aliasTarget");
425
+
426
+ if (aliasTarget) {
427
+ export const _canonicalStack = aliasTarget;
428
+ } else {
429
+ export const vpcId = vpc.id;
430
+ }
431
+ ```
432
+
433
+ **After (v0.2.0):**
434
+ ```typescript
435
+ // infrastructure/index.ts
436
+ import { createStackAlias } from "@egulatee/pulumi-stack-alias";
437
+
438
+ const config = new pulumi.Config();
439
+ const aliasTarget = config.get("aliasTarget");
440
+
441
+ if (aliasTarget) {
442
+ const alias = createStackAlias({
443
+ targetProject: "infrastructure",
444
+ targetStack: aliasTarget,
445
+ outputs: ["vpcId", "endpoint"],
446
+ });
447
+
448
+ export const vpcId = alias.vpcId;
449
+ export const endpoint = alias.endpoint;
450
+ } else {
451
+ export const vpcId = vpc.id;
452
+ export const endpoint = /* ... */;
453
+ }
454
+ ```
455
+
456
+ #### Consumer Changes
457
+
458
+ **Before (v0.1.0):**
459
+ ```typescript
460
+ import { resolveStackRef } from "@egulatee/pulumi-stack-alias";
461
+
462
+ const infraStack = resolveStackRef("infrastructure");
463
+ const vpcId = infraStack.apply(ref => ref.requireOutput("vpcId"));
464
+ ```
465
+
466
+ **After (v0.2.0):**
467
+ ```typescript
468
+ // NO LIBRARY IMPORT NEEDED!
469
+ const infraStack = new pulumi.StackReference(
470
+ `${pulumi.getOrganization()}/infrastructure/${pulumi.getStack()}`
471
+ );
472
+ const vpcId = infraStack.requireOutput("vpcId");
473
+ ```
474
+
475
+ #### Migration Steps
476
+
477
+ 1. **Update package.json**: Bump to `@egulatee/pulumi-stack-alias@^0.2.0`
478
+ 2. **Update producer code**: Replace `_canonicalStack` exports with `createStackAlias()` calls
479
+ 3. **Update consumer code**: Replace `resolveStackRef()` with standard `new StackReference()`
480
+ 4. **Remove consumer dependency**: Uninstall `@egulatee/pulumi-stack-alias` from consumer projects
481
+ 5. **Redeploy all stacks**: Alias stacks need one-time redeployment to export new outputs
482
+ 6. **Set up CI/CD**: Add alias sync jobs to keep outputs fresh
483
+
484
+ ## Contributing
485
+
486
+ Contributions are welcome! Please see the [GitHub issues](https://github.com/egulatee/pulumi-stack-alias/issues) for planned features and enhancements.
487
+
488
+ ## License
489
+
490
+ MIT © Eric Gulatee
491
+
492
+ ## Links
493
+
494
+ - [GitHub Repository](https://github.com/egulatee/pulumi-stack-alias)
495
+ - [npm Package](https://www.npmjs.com/package/@egulatee/pulumi-stack-alias)
496
+ - [Issue Tracker](https://github.com/egulatee/pulumi-stack-alias/issues)