Skip to content

Commit 9aa3d7c

Browse files
committed
Refactored action options & doc
1 parent a11bcc6 commit 9aa3d7c

File tree

8 files changed

+318
-196
lines changed

8 files changed

+318
-196
lines changed

action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ inputs:
1111
description: 'Enable alias feature in the vercel.json'
1212
required: false
1313
default: "true"
14+
failIfAliasNotLinked:
15+
description: 'If true, will throw an error (and crash CI) when there is an error about aliases link'
16+
required: false
17+
default: "false"
1418
outputs:
1519
deployment-url:
1620
description: "Url deployed"

lib/deploy.js

Lines changed: 0 additions & 106 deletions
This file was deleted.

lib/main.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3232
};
3333
Object.defineProperty(exports, "__esModule", { value: true });
3434
const core = __importStar(require("@actions/core"));
35-
const deploy_1 = __importDefault(require("./deploy"));
35+
const vercel_1 = __importDefault(require("./vercel"));
3636
/**
3737
* Runs configuration checks to make sure everything is properly configured.
3838
* If anything isn't properly configured, will stop the workflow.
@@ -56,9 +56,10 @@ const run = () => __awaiter(void 0, void 0, void 0, function* () {
5656
try {
5757
const command = core.getInput('command');
5858
const deploy_alias = core.getInput('deploy_alias') == 'true';
59+
const failIfAliasNotLinked = core.getInput('failIfAliasNotLinked') == 'true';
5960
core.debug(`Received command: ${command}`); // debug is only output if you set the secret `ACTIONS_RUNNER_DEBUG` to true https://github.com/actions/toolkit/blob/master/docs/action-debugging.md#how-to-access-step-debug-logs
6061
core.debug(`Should we deploy aliases ? "${deploy_alias}"`);
61-
yield deploy_1.default(command, deploy_alias);
62+
yield vercel_1.default(command, deploy_alias, failIfAliasNotLinked);
6263
}
6364
catch (error) {
6465
core.setFailed(error.message);

lib/vercel.js

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
"use strict";
2+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3+
if (k2 === undefined) k2 = k;
4+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5+
}) : (function(o, m, k, k2) {
6+
if (k2 === undefined) k2 = k;
7+
o[k2] = m[k];
8+
}));
9+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10+
Object.defineProperty(o, "default", { enumerable: true, value: v });
11+
}) : function(o, v) {
12+
o["default"] = v;
13+
});
14+
var __importStar = (this && this.__importStar) || function (mod) {
15+
if (mod && mod.__esModule) return mod;
16+
var result = {};
17+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18+
__setModuleDefault(result, mod);
19+
return result;
20+
};
21+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
22+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
23+
return new (P || (P = Promise))(function (resolve, reject) {
24+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
25+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
26+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
27+
step((generator = generator.apply(thisArg, _arguments || [])).next());
28+
});
29+
};
30+
var __importDefault = (this && this.__importDefault) || function (mod) {
31+
return (mod && mod.__esModule) ? mod : { "default": mod };
32+
};
33+
Object.defineProperty(exports, "__esModule", { value: true });
34+
const core = __importStar(require("@actions/core"));
35+
const fs_1 = __importDefault(require("fs"));
36+
const node_fetch_1 = __importDefault(require("node-fetch"));
37+
const config_1 = require("./config");
38+
const exec = require('@actions/exec');
39+
const glob = require('@actions/glob');
40+
const exec_command = (command) => __awaiter(void 0, void 0, void 0, function* () {
41+
/**
42+
* When we execute a program, it writes on two outputs : standard and error.
43+
* Initalizing empty variables to receive these outputs
44+
*/
45+
let stdout = '';
46+
let stderr = '';
47+
const options = {};
48+
/**
49+
* Defining actions for both outputs
50+
*/
51+
options.listeners = {
52+
stdout: (data) => {
53+
stdout += data.toString();
54+
},
55+
stderr: (data) => {
56+
stderr += data.toString();
57+
}
58+
};
59+
yield exec.exec(command, [], options);
60+
return stdout;
61+
});
62+
const create_aliases = (deploymentUrl, customDeploymentFile, failIfAliasNotLinked) => __awaiter(void 0, void 0, void 0, function* () {
63+
core.debug(`Starting to link aliases`);
64+
/**
65+
* Globber is a github action tool https://github.com/actions/toolkit/tree/master/packages/glob
66+
* It helps us to find the absolute path for a file. Indeed, because we don't know where the action will be run and we need to find this file, wherever it is.
67+
*/
68+
const globber = yield glob.create(customDeploymentFile);
69+
const vercelConfigFile = (yield globber.glob())[0] || config_1.VERCEL_CONFIG_FILE;
70+
if (vercelConfigFile && fs_1.default.existsSync(vercelConfigFile)) {
71+
core.debug(`Found custom config file: ${vercelConfigFile}`);
72+
core.debug(`Found real path: ${vercelConfigFile}`);
73+
const vercelConfig = JSON.parse(fs_1.default.readFileSync(vercelConfigFile, 'utf8'));
74+
const { id, ownerId } = (yield node_fetch_1.default(`https://api.vercel.com/v11/now/deployments/get?url=${deploymentUrl.replace("https://", "")}`, {
75+
headers: {
76+
Authorization: `Bearer ${process.env.VERCEL_TOKEN}`
77+
},
78+
method: 'GET'
79+
}).then(data => data.json()));
80+
let aliasCreationPromises = [];
81+
for (const alias of vercelConfig.alias) {
82+
console.log(`Creating alias ${alias}`);
83+
aliasCreationPromises.push(node_fetch_1.default(`https://api.vercel.com/v2/now/deployments/${id}/aliases?teamId=${ownerId}`, {
84+
headers: {
85+
Authorization: `Bearer ${process.env.VERCEL_TOKEN}`,
86+
"Content-Type": "application/json"
87+
},
88+
body: JSON.stringify({
89+
alias: alias
90+
}),
91+
method: 'POST'
92+
}).then(data => data.json()));
93+
}
94+
core.debug(`Resolving alias promises`);
95+
const aliasesResponse = yield Promise.all(aliasCreationPromises);
96+
console.log(`Alias creation response: ${aliasesResponse}`);
97+
if (failIfAliasNotLinked && aliasesResponse) {
98+
const failedAliases = aliasesResponse.filter((response) => response.error).map((response) => response.error);
99+
core.setFailed(`Got following errors: ${JSON.stringify(failedAliases)}`);
100+
return;
101+
}
102+
for (const alias of aliasesResponse.filter(response => !response.error)) {
103+
console.log(`Created alias ${alias}`);
104+
}
105+
}
106+
else {
107+
core.setFailed(`Cannot access to vercel config file "${vercelConfigFile}". Deployment succeeded but no aliases has been created.`);
108+
}
109+
});
110+
const deploy = (command, deployAlias, failIfAliasNotLinked) => __awaiter(void 0, void 0, void 0, function* () {
111+
var _a, _b, _c;
112+
const stdout = yield exec_command(command);
113+
/**
114+
* Parsing this huge output by using Regex match.
115+
* match function return strings matching with the pattern.
116+
* Pattern "/https?:\/\/[^ ]+.vercel.app/gi"
117+
* "/https?\/\/:" start matching when we find http:// or https://
118+
* "[^ ]+.vercel.app" will catch everything as a vercel subdomain, so "*.vercel.app"
119+
* "/g" allows us to have many matchess
120+
* "i" make the regex case insensitive. It will match for "https://subDomainApp.vercel.app" and "https://subdomainapp.vercel.app"
121+
* shift returns the first occurence
122+
*/
123+
const deploymentUrl = (_a = stdout.match(/https?:\/\/[^ ]+.vercel.app/gi)) === null || _a === void 0 ? void 0 : _a.shift();
124+
/**
125+
* Locating any custom config file for Vercel (if you are using one file per customer or deployment for the same app)
126+
* match function return strings matching with the pattern.
127+
* Pattern "/--local-config=.[^$]+?.json/gs"
128+
* "/--local-config=" starts matching on finding the argument local-config
129+
* "[^$]+?.json" with a json file provided as value
130+
* "/g" allows us to have many matchess
131+
* "s" reduce match scope on the same line
132+
* shift returns the first occurence
133+
* split isolates the json file
134+
* find automatically finds the matching json file
135+
*/
136+
const customDeploymentFile = (_c = (_b = stdout.match(/--local-config=.[^$]+?.json/gs)) === null || _b === void 0 ? void 0 : _b.shift()) === null || _c === void 0 ? void 0 : _c.split("=").find(el => el.endsWith(".json"));
137+
core.debug(`Command: ${command}`);
138+
core.debug(`Custom deploy file: ${customDeploymentFile}`);
139+
if (deploymentUrl) {
140+
const deploymentDomain = deploymentUrl.replace("https://", "");
141+
console.log(`Found url deployment. Exporting it...`);
142+
console.log(`VERCEL_DEPLOYMENT_URL=${deploymentUrl}`);
143+
core.exportVariable("VERCEL_DEPLOYMENT_URL", deploymentUrl);
144+
core.setOutput("VERCEL_DEPLOYMENT_URL", deploymentUrl);
145+
console.log(`VERCEL_DEPLOYMENT_DOMAIN=${deploymentDomain}`);
146+
core.exportVariable("VERCEL_DEPLOYMENT_DOMAIN", deploymentDomain);
147+
core.setOutput("VERCEL_DEPLOYMENT_DOMAIN", deploymentDomain);
148+
if (deployAlias) {
149+
yield create_aliases(deploymentUrl, customDeploymentFile, failIfAliasNotLinked);
150+
}
151+
}
152+
else {
153+
core.setFailed(`"Error during command execution, cannot find any url matching (using a regex to retrieve a url as "https://*.vercel.app"`);
154+
}
155+
});
156+
exports.default = deploy;

src/deploy.ts

Lines changed: 0 additions & 86 deletions
This file was deleted.

0 commit comments

Comments
 (0)