amondnet / amondnet/vercel-action

💡 Discussion: Interest in creating an action that updates vercel.json's rewrites?

Open
#82 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
765
Forks
116
PR merge metrics
No merged PRs in 30d

Description

Hey @amondnet !

I love your action and it has inspired me to create one of my own. If you have the same need that I will explain, I could open source it, it would be a great companion to vercel-action IMO

Here's my use case:

I want to have 3 Vercel sites running:

website.com (www.website.com)
app.website.com
api.website.com

and want to have the following rewrites:

website.com/app => app.website.com
website.com/api => api.website.com

My problem is when I have aliases for CI/CD

For example, for PR#222 I have set up (with `vercel-action`):

pr-222.website.com
pr-222-api.website.com
pr-222-app.website.com

I also have branch build for dev, master and release:

(dev branch / dev env)
dev.website.com
dev-api.website.com
dev-app.website.com

(master branch / staging env)
master.website.com
master-api.website.com
master-app.website.com

(release branch / production env)
website.com
api.website.com
app.website.com

The problem with vercel.json rewrites is that they are static

If you deploy pr-222.website.com, pr-222.website.com/api will still point to the rewrites set in the vercel.json

So what I did was to write a custom vercel action that replace the content of vercel.json with a different one depending on the branch:

```ts
import * as core from '@actions/core';
import * as github from '@actions/github';
import { promises as fsp } from 'fs'; // fix for node v12 https://stackoverflow.com/questions/64725249/fs-promises-api-in-typescript-not-compiling-in-javascript-correctly
import { VercelConfig } from './vercel';

try {
const { context } = github;
core.info(`action : ${context.action}`);
core.info(`ref : ${context.ref}`);
core.info(`eventName : ${context.eventName}`);
core.info(`actor : ${context.actor}`);
core.info(`sha : ${context.sha}`);
core.info(`workflow : ${context.workflow}`);

const workingDir = core.getInput('working-directory'); // ./packages/package-name

core.info(`looking for vercel.json files in: ${workingDir}`);

const filenames = ['vercel.json', 'vercel.dev.json', 'vercel.master.json'];
const vercelJsonFileFilenames = filenames.map((filename) => {
return readVercelConfigFile(`${workingDir}/${filename}`);
});

try {
Promise.all(vercelJsonFileFilenames)
.then((values) => values.filter((value) => !!value) as VercelConfig[])
.then((values) => {
if (values.length !== 3) {
const msg = 'vercel.json, vercel.dev.json and vercel.master.json must be provided';
core.error(msg);
throw new Error(msg);
}

const [vercelProdConfig, vercelDevConfig, vercelMasterConfig] = values;
const vercelConfigPath = `${workingDir}/${filenames[0]}`;

core.info(`current branch is: ${github.context.ref}`);
// @ts-ignore
core.info(`target branch is: ${github.context.head_ref}`);

core.debug(`updating config file and returning both the object and the updated file for verification`);

switch (github.context.ref) {
case 'refs/heads/dev': {
const updatedConfigObject = { ...vercelProdConfig, ...vercelDevConfig };
return updateVercelConfigFile(vercelConfigPath, updatedConfigObject).then((updatedConfigFile) => {
return {
updatedConfig: updatedConfigObject,
updatedConfigFile,
};
});
}
case 'refs/heads/master': {
const updatedConfigObject = { ...vercelProdConfig, ...vercelMasterConfig };
return updateVercelConfigFile(vercelConfigPath, updatedConfigObject).then((updatedConfigFile) => {
return {
updatedConfig: updatedConfigObject,
updatedConfigFile,
};
});
}
default:
case 'refs/heads/release': {
console.info('only replacing vercel.json config on branches master and dev');
return readVercelConfigFile(`${workingDir}/${filenames[0]}`).then((updatedConfigFile) => {
return {
updatedConfig: updatedConfigFile,
updatedConfigFile,
};
});
}
}
})
.then(({ updatedConfig, updatedConfigFile }) => {
if (!updatedConfig) {
const msg = 'no updated vercel config object received, aborting';
core.error(msg);
throw new Error(msg);
}

if (!updatedConfigFile) {
const msg = 'no updated vercel config file received, aborting';
core.error(msg);
throw new Error(msg);
}

if (JSON.stringify(updatedConfig) !== JSON.stringify(updatedConfigFile)) {
const msg = 'updated vercel.json file content does not match the updated config object, aborting!';
core.error(msg);
throw new Error(msg);
}

core.info(`updated vercel.json file successfully ✅`);
})
.catch((error) => {
core.error(`could not find vercel.json files in the specified directory`);
core.error(`error: ${error}`);
});
} catch (error) {
core.error(`could not find vercel.json file at the specified path`);
}
} catch (error) {
core.setFailed(error.message);
}

function readVercelConfigFile(path: string) {
core.info(`reading vercel.json file: ${path}`);
return fsp
.readFile(path, 'utf-8')
.then((result) => {
return JSON.parse(result) as VercelConfig;
})
.catch((error) => {
core.error(`could not read file ${path}`);
return undefined;
});
}

function updateVercelConfigFile(path: string, data: VercelConfig) {
core.info(`replacing vercel.json file content`);
return fsp.writeFile(path, JSON.stringify(data, null, 2)).then(() => {
return readVercelConfigFile(path);
});
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.