@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 +21 -0
- package/.github/workflows/ci.yml +63 -0
- package/.github/workflows/publish.yml +35 -0
- package/LICENSE +21 -0
- package/README.md +496 -0
- package/dist/alias.d.ts +104 -0
- package/dist/alias.d.ts.map +1 -0
- package/dist/alias.js +215 -0
- package/dist/alias.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/examples/README.md +67 -0
- package/examples/conditional-alias/Pulumi.dev.yaml +2 -0
- package/examples/conditional-alias/Pulumi.shared.yaml +2 -0
- package/examples/conditional-alias/Pulumi.staging.yaml +2 -0
- package/examples/conditional-alias/Pulumi.yaml +4 -0
- package/examples/conditional-alias/index.ts +30 -0
- package/examples/simple-alias/Pulumi.dev.yaml +4 -0
- package/examples/simple-alias/Pulumi.yaml +4 -0
- package/examples/simple-alias/index.ts +23 -0
- package/package.json +47 -0
- package/src/alias.ts +204 -0
- package/src/index.ts +33 -0
- package/src/types.ts +77 -0
- package/tests/alias.test.ts +463 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +19 -0
package/src/alias.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
2
|
+
import {
|
|
3
|
+
AliasConfig,
|
|
4
|
+
AliasExports,
|
|
5
|
+
ConditionalAliasConfig,
|
|
6
|
+
} from "./types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a stack alias that re-exports outputs from a target stack
|
|
10
|
+
*
|
|
11
|
+
* This is the core function of the Producer-Controlled Proxy pattern.
|
|
12
|
+
* It creates a StackReference to the target stack and re-exports specified outputs.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // In alias stack infrastructure/dev
|
|
17
|
+
* import { createStackAlias } from "@egulatee/pulumi-stack-alias";
|
|
18
|
+
*
|
|
19
|
+
* const alias = createStackAlias({
|
|
20
|
+
* targetProject: "infrastructure",
|
|
21
|
+
* targetStack: "shared",
|
|
22
|
+
* outputs: ["vpcId", "clusterName", "endpoint"]
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* export const vpcId = alias.vpcId;
|
|
26
|
+
* export const clusterName = alias.clusterName;
|
|
27
|
+
* export const endpoint = alias.endpoint;
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @param config - Alias configuration
|
|
31
|
+
* @returns Record of Pulumi Outputs for each specified output name
|
|
32
|
+
*/
|
|
33
|
+
export function createStackAlias(config: AliasConfig): AliasExports {
|
|
34
|
+
const org = config.targetOrg || pulumi.getOrganization();
|
|
35
|
+
const targetStackName = `${org}/${config.targetProject}/${config.targetStack}`;
|
|
36
|
+
const targetStack = new pulumi.StackReference(targetStackName);
|
|
37
|
+
|
|
38
|
+
const exports: AliasExports = {};
|
|
39
|
+
for (const outputName of config.outputs) {
|
|
40
|
+
exports[outputName] = targetStack.requireOutput(outputName);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return exports;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates a conditional alias based on pattern matching
|
|
48
|
+
*
|
|
49
|
+
* Evaluates pattern rules in order and uses the first matching target.
|
|
50
|
+
* Falls back to defaultTarget if no pattern matches.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* // In infrastructure/index.ts
|
|
55
|
+
* import { createConditionalAlias } from "@egulatee/pulumi-stack-alias";
|
|
56
|
+
*
|
|
57
|
+
* const alias = createConditionalAlias({
|
|
58
|
+
* targetProject: "infrastructure",
|
|
59
|
+
* patterns: [
|
|
60
|
+
* { pattern: "*\/prod", target: "prod" },
|
|
61
|
+
* { pattern: "*\/staging", target: "shared" },
|
|
62
|
+
* { pattern: "*\/*-ephemeral", target: "shared" }
|
|
63
|
+
* ],
|
|
64
|
+
* defaultTarget: "shared",
|
|
65
|
+
* outputs: ["vpcId", "endpoint"]
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* export const vpcId = alias.vpcId;
|
|
69
|
+
* export const endpoint = alias.endpoint;
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @param config - Conditional alias configuration
|
|
73
|
+
* @returns Record of Pulumi Outputs for each specified output name
|
|
74
|
+
*/
|
|
75
|
+
export function createConditionalAlias(config: ConditionalAliasConfig): AliasExports {
|
|
76
|
+
const currentProject = pulumi.getProject();
|
|
77
|
+
const currentStack = pulumi.getStack();
|
|
78
|
+
|
|
79
|
+
// Find first matching pattern
|
|
80
|
+
let targetStack = config.defaultTarget;
|
|
81
|
+
for (const rule of config.patterns) {
|
|
82
|
+
if (matchesPattern(rule.pattern, currentProject, currentStack)) {
|
|
83
|
+
targetStack = rule.target;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!targetStack) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`No matching pattern found for ${currentProject}/${currentStack} ` +
|
|
91
|
+
`and no defaultTarget specified.`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return createStackAlias({
|
|
96
|
+
targetProject: config.targetProject,
|
|
97
|
+
targetStack: targetStack,
|
|
98
|
+
targetOrg: config.targetOrg,
|
|
99
|
+
outputs: config.outputs,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates a simple alias using a simplified API
|
|
105
|
+
*
|
|
106
|
+
* Convenience wrapper around createStackAlias for common use cases.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* import { createSimpleAlias } from "@egulatee/pulumi-stack-alias";
|
|
111
|
+
*
|
|
112
|
+
* const alias = createSimpleAlias("infrastructure", "shared", ["vpcId"]);
|
|
113
|
+
* export const vpcId = alias.vpcId;
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* @param targetProject - Target project name
|
|
117
|
+
* @param targetStack - Target stack name
|
|
118
|
+
* @param outputs - List of output names to re-export
|
|
119
|
+
* @returns Record of Pulumi Outputs for each specified output name
|
|
120
|
+
*/
|
|
121
|
+
export function createSimpleAlias(
|
|
122
|
+
targetProject: string,
|
|
123
|
+
targetStack: string,
|
|
124
|
+
outputs: string[]
|
|
125
|
+
): AliasExports {
|
|
126
|
+
return createStackAlias({ targetProject, targetStack, outputs });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Pattern matching with wildcard support
|
|
131
|
+
*
|
|
132
|
+
* Supports wildcards (*) in project and stack positions, as well as
|
|
133
|
+
* prefix and suffix wildcards.
|
|
134
|
+
*
|
|
135
|
+
* Pattern format: "projectPattern/stackPattern"
|
|
136
|
+
*
|
|
137
|
+
* Wildcard rules:
|
|
138
|
+
* - "*" matches any value
|
|
139
|
+
* - "*-suffix" matches any string ending with "-suffix"
|
|
140
|
+
* - "prefix-*" matches any string starting with "prefix-"
|
|
141
|
+
* - "exact" matches exactly "exact"
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* matchesPattern("myproject/*", "myproject", "dev") // true
|
|
146
|
+
* matchesPattern("*\/dev", "anyproject", "dev") // true
|
|
147
|
+
* matchesPattern("*\/*-dev", "app", "service-dev") // true
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* @param pattern - Pattern string (e.g., "myproject/*", "*\/dev")
|
|
151
|
+
* @param project - Current project name to match against
|
|
152
|
+
* @param stack - Current stack name to match against
|
|
153
|
+
* @returns True if pattern matches the project/stack combination
|
|
154
|
+
*/
|
|
155
|
+
export function matchesPattern(pattern: string, project: string, stack: string): boolean {
|
|
156
|
+
const [projectPattern, stackPattern] = pattern.split("/");
|
|
157
|
+
|
|
158
|
+
if (!projectPattern || !stackPattern) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Invalid pattern format: "${pattern}". Expected "projectPattern/stackPattern".`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const projectMatches = matchesWildcard(projectPattern, project);
|
|
165
|
+
const stackMatches = matchesWildcard(stackPattern, stack);
|
|
166
|
+
|
|
167
|
+
return projectMatches && stackMatches;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Wildcard matching for a single component
|
|
172
|
+
*
|
|
173
|
+
* Supports:
|
|
174
|
+
* - "*" matches anything
|
|
175
|
+
* - "*-suffix" matches strings ending with suffix
|
|
176
|
+
* - "prefix-*" matches strings starting with prefix
|
|
177
|
+
* - "exact" matches exactly
|
|
178
|
+
*
|
|
179
|
+
* @internal
|
|
180
|
+
* @param pattern - Pattern string (may contain wildcards)
|
|
181
|
+
* @param value - Value to match against pattern
|
|
182
|
+
* @returns True if value matches pattern
|
|
183
|
+
*/
|
|
184
|
+
function matchesWildcard(pattern: string, value: string): boolean {
|
|
185
|
+
// Exact wildcard - matches anything
|
|
186
|
+
if (pattern === "*") {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Suffix wildcard: *-dev matches foo-dev, bar-dev
|
|
191
|
+
if (pattern.startsWith("*")) {
|
|
192
|
+
const suffix = pattern.substring(1);
|
|
193
|
+
return value.endsWith(suffix);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Prefix wildcard: prod-* matches prod-us, prod-eu
|
|
197
|
+
if (pattern.endsWith("*")) {
|
|
198
|
+
const prefix = pattern.substring(0, pattern.length - 1);
|
|
199
|
+
return value.startsWith(prefix);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Exact match
|
|
203
|
+
return pattern === value;
|
|
204
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @egulatee/pulumi-stack-alias
|
|
3
|
+
*
|
|
4
|
+
* Producer-side stack aliasing for Pulumi using lightweight proxy stacks.
|
|
5
|
+
*
|
|
6
|
+
* Consumers use standard StackReference (no library dependency).
|
|
7
|
+
* Producers use this library to create alias stacks that re-export outputs.
|
|
8
|
+
*
|
|
9
|
+
* ## Producer-Controlled Proxy Pattern
|
|
10
|
+
*
|
|
11
|
+
* Alias stacks re-export outputs from canonical stacks. Consumers use standard
|
|
12
|
+
* Pulumi StackReference without any library dependency. Producers use this
|
|
13
|
+
* library to create proxy stacks that keep outputs in sync.
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
createStackAlias,
|
|
20
|
+
createConditionalAlias,
|
|
21
|
+
createSimpleAlias,
|
|
22
|
+
matchesPattern,
|
|
23
|
+
} from "./alias";
|
|
24
|
+
|
|
25
|
+
export type {
|
|
26
|
+
AliasConfig,
|
|
27
|
+
AliasExports,
|
|
28
|
+
PatternRule,
|
|
29
|
+
ConditionalAliasConfig,
|
|
30
|
+
} from "./types";
|
|
31
|
+
|
|
32
|
+
// Re-export Pulumi types for convenience
|
|
33
|
+
export { Output, StackReference } from "@pulumi/pulumi";
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for creating a stack alias
|
|
5
|
+
*/
|
|
6
|
+
export interface AliasConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Target organization name (defaults to current organization)
|
|
9
|
+
*/
|
|
10
|
+
targetOrg?: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Target project name
|
|
14
|
+
*/
|
|
15
|
+
targetProject: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Target stack name
|
|
19
|
+
*/
|
|
20
|
+
targetStack: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* List of output names to re-export from the target stack
|
|
24
|
+
*/
|
|
25
|
+
outputs: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Record of aliased outputs (all are Pulumi Outputs)
|
|
30
|
+
*/
|
|
31
|
+
export type AliasExports = Record<string, pulumi.Output<any>>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A pattern rule for conditional aliasing
|
|
35
|
+
*/
|
|
36
|
+
export interface PatternRule {
|
|
37
|
+
/**
|
|
38
|
+
* Pattern in format "project/stack" with wildcard support
|
|
39
|
+
* Examples: "* /prod", "myproject/ *", "* / *-ephemeral" (without spaces)
|
|
40
|
+
*/
|
|
41
|
+
pattern: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Target stack name to use when this pattern matches
|
|
45
|
+
*/
|
|
46
|
+
target: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Configuration for creating a conditional alias based on pattern matching
|
|
51
|
+
*/
|
|
52
|
+
export interface ConditionalAliasConfig {
|
|
53
|
+
/**
|
|
54
|
+
* Target project name
|
|
55
|
+
*/
|
|
56
|
+
targetProject: string;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Target organization name (defaults to current organization)
|
|
60
|
+
*/
|
|
61
|
+
targetOrg?: string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* List of pattern rules (evaluated in order, first match wins)
|
|
65
|
+
*/
|
|
66
|
+
patterns: PatternRule[];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Default target stack if no pattern matches (optional)
|
|
70
|
+
*/
|
|
71
|
+
defaultTarget?: string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* List of output names to re-export from the target stack
|
|
75
|
+
*/
|
|
76
|
+
outputs: string[];
|
|
77
|
+
}
|