Skip to content

Commit 713fcc2

Browse files
committed
feat: add template variable interpolation for preview deployment subdomains
Support ${prNumber}, ${branchName}, ${appName}, and ${uniqueId} variables in the preview wildcard domain field. When ${prNumber} or ${branchName} is present, no random suffix is appended since these already provide uniqueness. Falls back to existing behavior when no template variables are used. Closes #4283
1 parent 4840abe commit 713fcc2

1 file changed

Lines changed: 56 additions & 10 deletions

File tree

packages/server/src/services/preview-deployment.ts

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -129,21 +129,67 @@ export const findPreviewDeploymentsByApplicationId = async (
129129
return deploymentsList;
130130
};
131131

132+
const slugify = (value: string): string => {
133+
return value
134+
.toLowerCase()
135+
.replace(/[^a-z0-9-]/g, "-")
136+
.replace(/-+/g, "-")
137+
.replace(/^-|-$/g, "")
138+
.slice(0, 63);
139+
};
140+
141+
export const interpolateSubdomainTemplate = (
142+
template: string,
143+
vars: {
144+
appName: string;
145+
prNumber: string;
146+
branchName: string;
147+
uniqueId: string;
148+
},
149+
): string => {
150+
return template
151+
.replace(/\$\{appName\}/g, vars.appName)
152+
.replace(/\$\{prNumber\}/g, vars.prNumber)
153+
.replace(/\$\{branchName\}/g, slugify(vars.branchName))
154+
.replace(/\$\{uniqueId\}/g, vars.uniqueId);
155+
};
156+
132157
export const createPreviewDeployment = async (
133158
schema: typeof apiCreatePreviewDeployment._type,
134159
) => {
135160
const application = await findApplicationById(schema.applicationId);
136-
const appName = `preview-${application.appName}-${generatePassword(6)}`;
161+
const uniqueId = generatePassword(6);
162+
const domainTemplate = application.previewWildcard || "*.traefik.me";
137163

138-
const org = await db.query.organization.findFirst({
139-
where: eq(organization.id, application.environment.project.organizationId),
140-
});
141-
const generateDomain = await generateWildcardDomain(
142-
application.previewWildcard || "*.traefik.me",
143-
appName,
144-
application.server?.ipAddress || "",
145-
org?.ownerId || "",
146-
);
164+
const hasIdentifier =
165+
domainTemplate.includes("${prNumber}") ||
166+
domainTemplate.includes("${branchName}") ||
167+
domainTemplate.includes("${uniqueId}");
168+
169+
let appName: string;
170+
let generateDomain: string;
171+
172+
if (hasIdentifier) {
173+
const interpolated = interpolateSubdomainTemplate(domainTemplate, {
174+
appName: application.appName,
175+
prNumber: schema.pullRequestNumber,
176+
branchName: schema.branch,
177+
uniqueId,
178+
});
179+
generateDomain = interpolated.replace("*", application.appName);
180+
appName = `preview-${application.appName}-${uniqueId}`;
181+
} else {
182+
appName = `preview-${application.appName}-${uniqueId}`;
183+
const org = await db.query.organization.findFirst({
184+
where: eq(organization.id, application.environment.project.organizationId),
185+
});
186+
generateDomain = await generateWildcardDomain(
187+
domainTemplate,
188+
appName,
189+
application.server?.ipAddress || "",
190+
org?.ownerId || "",
191+
);
192+
}
147193

148194
const octokit = authGithub(application?.github as Github);
149195

0 commit comments

Comments
 (0)