Skip to content

Commit

Permalink
🎉: Implement API token auth
Browse files Browse the repository at this point in the history
  • Loading branch information
arddluma committed Feb 6, 2023
1 parent 9de612f commit 80dba2b
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 37 deletions.
Binary file added .github/images/create-cf-token.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@

Wait for Cloudflare Pages build to finish and send Slack notification

Recommended:
Generate API Token go to https://dash.cloudflare.com/profile/api-tokens
`Create Custom Token`
![Create CF Token](.github/images/create-cf-token.png)


## Usage
```yml
- name: Await CF Pages and send Slack notification
uses: arddluma/cloudflare-pages-slack-notification@v2.5
uses: arddluma/cloudflare-pages-slack-notification@v3
with:
accountEmail: ${{ secrets.CF_ACCOUNT_EMAIL }}
apiKey: ${{ secrets.CF_API_KEY }}
# Uncomment these two lines if you wish to use the Global API Key (Not recommended!)
# accountEmail: ${{ secrets.CF_ACCOUNT_EMAIL }}
# apiKey: ${{ secrets.CF_API_KEY }
# Use an API token (Recommended!)
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACC_ID }}
# CloudFlare Pages project name
project: ${{ secrets.CF_PAGES_PROJECT }}
Expand All @@ -32,10 +41,14 @@ jobs:
- uses: actions/checkout@v3
- name: Await CF Pages and send Slack notification
id: cf-pages
uses: arddluma/cloudflare-pages-slack-notification@v2.5
uses: arddluma/cloudflare-pages-slack-notification@v3
with:
accountEmail: ${{ secrets.CF_ACCOUNT_EMAIL }}
apiKey: ${{ secrets.CF_API_KEY }}
# Uncomment these two lines if you wish to use the Global API Key (Not recommended!)
# accountEmail: ${{ secrets.CF_ACCOUNT_EMAIL }}
# apiKey: ${{ secrets.CF_API_KEY }}

# Use an API token (Recommended!)
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACC_ID }}
# CloudFlare Pages project name
project: ${{ secrets.CF_PAGES_PROJECT }}
Expand Down
11 changes: 7 additions & 4 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ description: 'Wait for Cloudflare Pages to build and send Slack notification'
author: 'arddluma'
inputs:
accountEmail:
description: 'Cloudflare account email (Needed for auth)'
required: true
description: 'Cloudflare account email (Not recommended)'
required: false
apiKey:
description: 'Cloudflare API Key (Tokens aren''t supported in the API yet :( )'
required: true
description: 'Cloudflare API Key (Not recommended)'
required: false
apiToken:
description: 'Cloudflare API Token (Recommended)'
required: false
accountId:
description: 'Cloudflare account ID'
required: true
Expand Down
30 changes: 21 additions & 9 deletions dist/action.js
Original file line number Diff line number Diff line change
Expand Up @@ -12767,19 +12767,24 @@ var waiting = true;
var ghDeployment;
var markedAsInProgress = false;
async function run() {
const accountEmail = core.getInput("accountEmail", { required: true, trimWhitespace: true });
const apiKey = core.getInput("apiKey", { required: true, trimWhitespace: true });
const accountEmail = core.getInput("accountEmail", { required: false, trimWhitespace: true });
const apiKey = core.getInput("apiKey", { required: false, trimWhitespace: true });
const apiToken = core.getInput("apiToken", { required: false, trimWhitespace: true });
const accountId = core.getInput("accountId", { required: true, trimWhitespace: true });
const project = core.getInput("project", { required: true, trimWhitespace: true });
const token = core.getInput("githubToken", { required: false, trimWhitespace: true });
const commitHash = core.getInput("commitHash", { required: false, trimWhitespace: true });
const slackWebHook = core.getInput("slackWebHook", { required: false, trimWhitespace: true });
const slack = esm_default(slackWebHook);
if (!validateAuthInputs(apiToken, accountEmail, apiKey)) {
return;
}
const authHeaders = apiToken !== "" ? { Authorization: `Bearer ${apiToken}` } : { "X-Auth-Email": accountEmail, "X-Auth-Key": apiKey };
console.log("Waiting for Pages to finish building...");
let lastStage = "";
while (waiting) {
await sleep();
const deployment = await pollApi(accountEmail, apiKey, accountId, project, commitHash);
const deployment = await pollApi(authHeaders, accountId, project, commitHash);
if (!deployment) {
console.log("Waiting for the deployment to start...");
continue;
Expand All @@ -12793,7 +12798,7 @@ async function run() {
markedAsInProgress = true;
}
}
if (latestStage.status === "failure") {
if (latestStage.status === "failed" || latestStage.status === "failure") {
waiting = false;
slack.send(`:x: CloudFlare Pages \`${latestStage.name}\` pipeline for project *${project}* \`FAILED\`!
Environment: *${deployment.environment}*
Expand Down Expand Up @@ -12837,16 +12842,23 @@ Checkout <https://dash.cloudflare.com?to=/${accountId}/pages/view/${deployment.p
}
}
}
async function pollApi(accountEmail, apiKey, accountId, project, commitHash) {
function validateAuthInputs(token, email, key) {
if (token !== "") {
return true;
}
if (email !== "" && key !== "") {
return true;
}
core.setFailed("Please specify authentication details! Set either `apiToken` or `accountEmail` + `accountKey`!");
return false;
}
async function pollApi(authHeaders, accountId, project, commitHash) {
var _a2, _b, _c;
let res;
let body;
try {
res = await fetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/pages/projects/${project}/deployments?sort_by=created_on&sort_order=desc`, {
headers: {
"X-Auth-Email": accountEmail,
"X-Auth-Key": apiKey
}
headers: { ...authHeaders }
});
} catch (e) {
core.error(`Failed to send request to CF API - network issue? ${e.message}`);
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cf-pages-await",
"version": "2.0.5",
"name": "cloudflare-pages-slack-notification",
"version": "3.0.0",
"description": "Wait for a Cloudflare Pages build to finish and send Slack notification",
"main": "index.js",
"keywords": [],
Expand Down
54 changes: 38 additions & 16 deletions src/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,41 @@ import * as github from '@actions/github';
import fetch, { Response } from 'node-fetch';

import { context } from '@actions/github/lib/utils';
import { ApiResponse, Deployment } from './types';

import { ApiResponse, AuthHeaders, Deployment } from './types';
import SlackNotify from 'slack-notify';

let waiting = true;
// @ts-ignore - Typing GitHub's responses is a pain in the ass
let ghDeployment;
let markedAsInProgress = false;
import { ApiResponse, Deployment } from './types';

export default async function run() {
const accountEmail = core.getInput('accountEmail', { required: true, trimWhitespace: true });
const apiKey = core.getInput('apiKey', { required: true, trimWhitespace: true });
const accountEmail = core.getInput('accountEmail', { required: false, trimWhitespace: true });
const apiKey = core.getInput('apiKey', { required: false, trimWhitespace: true });
const apiToken = core.getInput('apiToken', { required: false, trimWhitespace: true })

const accountId = core.getInput('accountId', { required: true, trimWhitespace: true });
const project = core.getInput('project', { required: true, trimWhitespace: true });
const token = core.getInput('githubToken', { required: false, trimWhitespace: true });
const commitHash = core.getInput('commitHash', { required: false, trimWhitespace: true });
const slackWebHook = core.getInput('slackWebHook', { required: false, trimWhitespace: true });
const slack = SlackNotify(slackWebHook);

// Validate we have either token or both email + key
if (!validateAuthInputs(apiToken, accountEmail, apiKey)) {
return;
}

const authHeaders: AuthHeaders = apiToken !== '' ? { Authorization: `Bearer ${apiToken}` } : { 'X-Auth-Email': accountEmail, 'X-Auth-Key': apiKey };

console.log('Waiting for Pages to finish building...');
let lastStage = '';

while (waiting) {
// We want to wait a few seconds, don't want to spam the API :)
await sleep();

const deployment: Deployment|undefined = await pollApi(accountEmail, apiKey, accountId, project, commitHash);
const deployment: Deployment|undefined = await pollApi(authHeaders, accountId, project, commitHash);
if (!deployment) {
console.log('Waiting for the deployment to start...');
continue;
Expand All @@ -40,13 +48,14 @@ export default async function run() {
if (latestStage.name !== lastStage) {
lastStage = deployment.latest_stage.name;
console.log('# Now at stage: ' + lastStage);

if (!markedAsInProgress) {
await updateDeployment(token, deployment, 'in_progress');
markedAsInProgress = true;
}
}

if (latestStage.status === 'failure') {
if (latestStage.status === 'failed' || latestStage.status === 'failure') {
waiting = false;
slack.send(`:x: CloudFlare Pages \`${latestStage.name}\` pipeline for project *${project}* \`FAILED\`!\nEnvironment: *${deployment.environment}*\nCommit: ${context.payload.head_commit.url}\nActor: *${context.actor}*\nDeployment ID: *${deployment.id}*\nCheckout <https://dash.cloudflare.com?to=/${accountId}/pages/view/${deployment.project_name}/${deployment.id}|build logs>`).then(() => {
console.log(`Slack message for ${latestStage.name} failed pipeline sent!`);
Expand All @@ -60,7 +69,9 @@ export default async function run() {

if (latestStage.name === 'deploy' && ['success', 'failed'].includes(latestStage.status)) {
waiting = false;

const aliasUrl = deployment.aliases && deployment.aliases.length > 0 ? deployment.aliases[0] : deployment.url;

// Set outputs
core.setOutput('id', deployment.id);
core.setOutput('environment', deployment.environment);
Expand All @@ -75,8 +86,6 @@ export default async function run() {
console.error(err);
});
}


// Update deployment (if enabled)
if (token !== '') {
await updateDeployment(token, deployment, latestStage.status === 'success' ? 'success' : 'failure');
Expand All @@ -85,7 +94,20 @@ export default async function run() {
}
}

async function pollApi(accountEmail: string, apiKey: string, accountId: string, project: string, commitHash: string): Promise<Deployment|undefined> {
function validateAuthInputs(token: string, email: string, key: string) {
if (token !== '') {
return true;
}

if (email !== '' && key !== '') {
return true;
}

core.setFailed('Please specify authentication details! Set either `apiToken` or `accountEmail` + `accountKey`!');
return false;
}

async function pollApi(authHeaders: AuthHeaders, accountId: string, project: string, commitHash: string): Promise<Deployment|undefined> {
// curl -X GET "https://api.cloudflare.com/client/v4/accounts/:account_id/pages/projects/:project_name/deployments" \
// -H "X-Auth-Email: [email protected]" \
// -H "X-Auth-Key: c2547eb745079dac9320b638f5e225cf483cc5cfdda41"
Expand All @@ -94,10 +116,7 @@ async function pollApi(accountEmail: string, apiKey: string, accountId: string,
// Try and fetch, may fail due to a network issue
try {
res = await fetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/pages/projects/${project}/deployments?sort_by=created_on&sort_order=desc`, {
headers: {
'X-Auth-Email': accountEmail,
'X-Auth-Key': apiKey,
}
headers: { ...authHeaders },
});
} catch(e) {
// @ts-ignore
Expand Down Expand Up @@ -147,6 +166,7 @@ async function updateDeployment(token: string, deployment: Deployment, state: 's
repo: context.repo.repo,
};

// @ts-ignore
if (!ghDeployment) {
const { data } = await octokit.rest.repos.createDeployment({
...sharedOptions,
Expand Down Expand Up @@ -179,7 +199,9 @@ async function updateDeployment(token: string, deployment: Deployment, state: 's
try {
run();
} catch(e) {
console.error('Please report this! Issues: https://github.com/WalshyDev/cf-pages-await/issues')
console.error('Please report this! Issues: https://github.com/WalshyDev/cf-pages-await/issues');
// @ts-ignore
core.setFailed(e);
// @ts-ignore
console.error(e.message + '\n' + e.stack);
}
}
6 changes: 6 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export interface ApiResponse {
result: Deployment[];
}

export interface AuthHeaders {
Authorization?: string;
'X-Auth-Email'?: string;
'X-Auth-Key'?: string;
}

export interface Deployment {
id: string;
short_id: string;
Expand Down

0 comments on commit 80dba2b

Please sign in to comment.