Skip to content

Commit

Permalink
feat: add build envs similar to standard git integration (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
phidol authored Aug 22, 2024
1 parent fbaa971 commit 9902318
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 27 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ This extension contains Azure Pipelines tasks for automatically deploying your A
- [Extension Set Up](#extension-set-up)
- [Basic Pipeline Set Up](#basic-pipeline-set-up)
- [Full Featured Pipeline Set Up](#full-featured-pipeline-set-up)
- [Env Vars](#env-vars)
- [Available Env Vars](#available-env-vars)
- [Extension Reference](#extension-reference)
- [Task: `vercel-deployment-task`](#task-vercel-deployment-task)
- [Task: `vercel-azdo-pr-comment-task`](#task-vercel-azdo-pr-comment-task)
Expand Down Expand Up @@ -92,6 +94,24 @@ This guide will demonstrate how to improve the [Basic Pipeline Set Up](#basic-pi
1. Push these changes to the repository, and set a [Build Policy](#azure-build-policy-set-up) for the `main` branch.
1. Now create a new branch, push a commit, and open a PR against `main`. A new pipeline execution should trigger and it should create a preview deployment on Vercel as well as comment back on the PR with the preview URL.

## Env Vars

The extension provides a set of env vars if the option [Automatically exposing System Environment Variables](https://vercel.com/docs/projects/environment-variables/system-environment-variables) is enabled in the Project Settings. These vars are available in the build step and at run time.

Based on the selected [Framework Preset](https://vercel.com/docs/deployments/configure-a-build#framework-preset) framework-specific env vars are set for the build step like with a [regular integration](https://vercel.com/docs/projects/environment-variables/system-environment-variables#framework-environment-variables).
> Note: This option is currently only available for Next.js.

### Available Env Vars

| DevOps Variable | Generic | Next.js |
| ------------------------------------------------------ | --------------------------- | -------------------------------------- |
| Build.SourceVersion | DEVOPS_GIT_COMMIT_SHA | NEXT_PUBLIC_DEVOPS_GIT_COMMIT_SHA |
| System.PullRequest.SourceBranch/Build.SourceBranchName | DEVOPS_GIT_COMMIT_REF | NEXT_PUBLIC_DEVOPS_GIT_COMMIT_REF |
| System.PullRequest.PullRequestId | DEVOPS_GIT_PULL_REQUEST_ID | NEXT_PUBLIC_DEVOPS_GIT_PULL_REQUEST_ID |
| "devops" | DEVOPS_GIT_PROVIDER | NEXT_PUBLIC_DEVOPS_GIT_PROVIDER |
| System.TeamProjectId | DEVOPS_GIT_REPO_ID | NEXT_PUBLIC_DEVOPS_GIT_REPO_ID |
| System.TeamProject | DEVOPS_GIT_REPO_SLUG | NEXT_PUBLIC_DEVOPS_GIT_REPO_SLUG |

## Extension Reference

### Task: `vercel-deployment-task`
Expand Down
87 changes: 62 additions & 25 deletions vercel-deployment-task-source/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,15 @@ async function getStagingPrefix(orgID: string, token: string): Promise<string> {
return isTeam ? result.stagingPrefix : result.user.stagingPrefix;
}

async function getProjectName(
// https://vercel.com/docs/rest-api/endpoints/projects#find-a-project-by-id-or-name-response
type Framework = 'blitzjs' | 'nextjs' | 'gatsby' | 'remix' | 'astro' | 'hexo' | 'eleventy' | 'docusaurus-2' | 'docusaurus' | 'preact' | 'solidstart-1' | 'solidstart' | 'dojo' | 'ember' | 'vue' | 'scully' | 'ionic-angular' | 'angular' | 'polymer' | 'svelte' | 'sveltekit' | 'sveltekit-1' | 'ionic-react' | 'create-react-app' | 'gridsome' | 'umijs' | 'sapper' | 'saber' | 'stencil' | 'nuxtjs' | 'redwoodjs' | 'hugo' | 'jekyll' | 'brunch' | 'middleman' | 'zola' | 'hydrogen' | 'vite' | 'vitepress' | 'vuepress' | 'parcel' | 'fasthtml' | 'sanity' | 'storybook' | null;
type Project = { autoExposeSystemEnvs: boolean; framework: Framework; name: string; }

async function getProject(
projectId: string,
orgId: string,
token: string
): Promise<string> {
): Promise<Project> {
let apiURL = `https://api.vercel.com/v9/projects/${projectId}`;
if (isTeamID(orgId)) {
apiURL += `?teamId=${orgId}`;
Expand All @@ -72,7 +76,7 @@ async function getProjectName(
);
}

return result.name;
return result;
}

/**
Expand Down Expand Up @@ -195,6 +199,56 @@ async function run() {
if (archive) {
vercelDeployArgs.push("--archive=tgz");
}

const project = await getProject(vercelProjectId, vercelOrgId, vercelToken)

// Get branch name
// If triggered by a PR use `System.PullRequest.SourceBranch` (and replace the `refs/heads/`)
// If not triggered by a PR use `Build.SourceBranchName`
let branchName: string | undefined;
const buildReason = getVariable("Build.Reason");
if (buildReason === "PullRequest") {
branchName = getVariable("System.PullRequest.SourceBranch");
if (branchName) {
branchName = branchName.replace("refs/heads/", "");
}
} else {
branchName = getVariable("Build.SourceBranchName");
}

// adding predefined DevOps variables which can be useful during build as env vars in a similiar style as the regular Vercel git integration would (replacing VERCEL with DEVOPS)
if (project.autoExposeSystemEnvs) {
const addEnvVar = (envVar: string) => {
vercelDeployArgs.push(`--build-env ${envVar}`);
vercelDeployArgs.push(`--env ${envVar}`);
}

const commitSha = getVariable("Build.SourceVersion");
const pullRequestId = getVariable("System.PullRequest.PullRequestId");
const teamProject = getVariable("System.TeamProject");
const teamProjectId = getVariable("System.TeamProjectId");

addEnvVar(`DEVOPS_GIT_COMMIT_SHA=${commitSha}`);
addEnvVar(`DEVOPS_GIT_COMMIT_REF=${branchName}`);
addEnvVar(`DEVOPS_GIT_PULL_REQUEST_ID=${pullRequestId}`);
addEnvVar(`DEVOPS_GIT_PROVIDER=devops`);
addEnvVar(`DEVOPS_GIT_REPO_ID=${teamProjectId}`);
addEnvVar(`DEVOPS_GIT_REPO_SLUG=${teamProject}`);

// adding framework specific vars as with regular integration (currently only Next.js is supported) https://vercel.com/docs/projects/environment-variables/system-environment-variables#framework-environment-variables
switch (project.framework) {
case 'nextjs':
vercelDeployArgs.push(`--build-env NEXT_PUBLIC_DEVOPS_GIT_COMMIT_SHA=${commitSha}`);
vercelDeployArgs.push(`--build-env NEXT_PUBLIC_DEVOPS_GIT_COMMIT_REF=${branchName}`);
vercelDeployArgs.push(`--build-env NEXT_PUBLIC_DEVOPS_GIT_PULL_REQUEST_ID=${pullRequestId}`);
vercelDeployArgs.push(`--build-env NEXT_PUBLIC_DEVOPS_GIT_PROVIDER=devops`);
vercelDeployArgs.push(`--build-env NEXT_PUBLIC_DEVOPS_GIT_REPO_ID=${teamProjectId}`);
vercelDeployArgs.push(`--build-env NEXT_PUBLIC_DEVOPS_GIT_REPO_SLUG=${teamProject}`);
break;
}
}


const vercelDeploy = vercel.arg(vercelDeployArgs);
({ stdout, stderr, code } = vercelDeploy.execSync());

Expand All @@ -207,33 +261,16 @@ async function run() {
let deployURL = stdout;

if (!deployToProduction) {
// Get branch name
// If triggered by a PR use `System.PullRequest.SourceBranch` (and replace the `refs/heads/`)
// If not triggered by a PR use `Build.SourceBranchName`
let branchName: string | undefined;
const buildReason = getVariable("Build.Reason");
if (buildReason && buildReason === "PullRequest") {
branchName = getVariable("System.PullRequest.SourceBranch");
if (branchName) {
branchName = branchName.replace("refs/heads/", "");
}
} else {
branchName = getVariable("Build.SourceBranchName");
}

if (branchName) {
const [projectName, stagingPrefix] = await Promise.all([
getProjectName(vercelProjectId, vercelOrgId, vercelToken),
getStagingPrefix(vercelOrgId, vercelToken),
]);
const stagingPrefix = await getStagingPrefix(vercelOrgId, vercelToken);
const escapedBranchName = branchName.replace(/[^a-zA-Z0-9\-]-?/g, "-");
/**
* Truncating branch name according to RFC 1035 if necessary
* Maximum length is 63 characters.
*
* Read more: https://vercel.com/guides/why-is-my-vercel-deployment-url-being-shortened
*
* projectName has a fixedLength `x`
* project.name has a fixedLength `x`
* stagingPrefix has a fixedLenght `y`
* .vercel.app has a fixedLength `11`
* two dashes
Expand All @@ -254,8 +291,8 @@ async function run() {
* longer-project-name-feature-prefix-12346-my-second-f.vercel.app
*/
const branchNameAllowedLength =
50 - projectName.length - stagingPrefix.length;
let aliasHostname = `${projectName}-${escapedBranchName}-${stagingPrefix}.vercel.app`;
50 - project.name.length - stagingPrefix.length;
let aliasHostname = `${project.name}-${escapedBranchName}-${stagingPrefix}.vercel.app`;

if (escapedBranchName.length > branchNameAllowedLength) {
// Calculate the maximum length of the branchName by removing the stagingPrefix and the dash
Expand All @@ -276,7 +313,7 @@ async function run() {
}

// Remove the stagingPrefix from the aliasHostname and use the extended aliasingBranchName
aliasHostname = `${projectName}-${aliasingBranchName}.vercel.app`;
aliasHostname = `${project.name}-${aliasingBranchName}.vercel.app`;
}

deployURL = `https://${aliasHostname}`;
Expand Down
2 changes: 1 addition & 1 deletion vercel-deployment-task-source/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"author": "Vercel",
"version": {
"Major": 1,
"Minor": 3,
"Minor": 4,
"Patch": 0
},
"instanceNameFormat": "Deploying $(vercelProject) to Vercel",
Expand Down
2 changes: 1 addition & 1 deletion vss-extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"manifestVersion": 1,
"id": "vercel-deployment-extension",
"name": "Vercel Deployment Extension",
"version": "1.3.1",
"version": "1.4.0",
"publisher": "Vercel",
"public": true,
"targets": [
Expand Down

0 comments on commit 9902318

Please sign in to comment.