Skip to content

Commit 964033c

Browse files
devversionmmalerba
authored andcommitted
build: add dashboard firebase functions (#4806)
* build: add dashboard firebase functions * Adds a new folder for the dashboard project that for now only contains firebase functions. * The firebase functions will be used to securely make API calls to the Github API (e.g payload status) * Functions will be automatically deployed as same as screenshot functions. * Address feedback
1 parent f169152 commit 964033c

13 files changed

+182
-4
lines changed

scripts/ci/publish-artifacts.sh

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ $(npm bin)/gulp docs
2222

2323
# Run publishing of artifacts in parallel.
2424
# This is possible because the output has been built before.
25-
./scripts/release/publish-build-artifacts.sh --no-build &
26-
./scripts/release/publish-docs-content.sh --no-build &
25+
./scripts/deploy/publish-build-artifacts.sh --no-build &
26+
./scripts/deploy/publish-docs-content.sh --no-build &
2727

28-
# Deploy the screenshot functions for each push build.
29-
./scripts/release/deploy-screenshot-functions.sh &
28+
# Deploy the screenshot and dashboard functions for each push build.
29+
./scripts/deploy/deploy-screenshot-functions.sh &
30+
./scripts/deploy/deploy-dashboard-functions.sh &
3031

3132
wait
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
3+
# Go to the project root directory
4+
cd $(dirname ${0})/../..
5+
6+
# Paths to the dashboard and functions directories.
7+
dashboardFolder=tools/dashboard
8+
9+
# Go to the dashboard folder because otherwise the Firebase CLI tries to deploy the wrong project.
10+
cd ${dashboardFolder}
11+
12+
# Install node modules for dashboard functions. Firebase CLI needs to execute the functions
13+
# before it can collect all functions and deploy them.
14+
(cd functions; npm install)
15+
16+
if [ -z ${MATERIAL2_DASHBOARD_ACCESS_TOKEN} ]; then
17+
echo "Error: No access token for firebase specified." \
18+
"Please set the environment variable 'MATERIAL2_DASHBOARD_ACCESS_TOKEN'."
19+
exit 1
20+
fi
21+
22+
# Deploy the dashboard functions to Firebase. For now only the functions will be deployed.
23+
$(npm bin)/firebase deploy \
24+
--only functions --token ${MATERIAL2_DASHBOARD_ACCESS_TOKEN} --project material2-dashboard

tools/dashboard/firebase.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"hosting": {
3+
"public": "dist",
4+
"rewrites": [
5+
{
6+
"source": "**",
7+
"destination": "/index.html"
8+
}
9+
]
10+
}
11+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {payloadGithubStatus} from './payload-github-status';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {config} from 'firebase-functions';
2+
3+
const request = require('request');
4+
const {version, name} = require('../package.json');
5+
6+
/** API token for the Github repository. Required to set the github status on commits and PRs. */
7+
const repoToken = config().repoToken;
8+
9+
/** Data that must be specified to set a Github PR status. */
10+
export type GithubStatusData = {
11+
result: boolean;
12+
name: string;
13+
description: string;
14+
url: string;
15+
};
16+
17+
/** Function that sets a Github commit status */
18+
export function setGithubStatus(commitSHA: string, data: GithubStatusData) {
19+
const state = data.result ? 'success' : 'failure';
20+
21+
const requestData = JSON.stringify({
22+
state: state,
23+
target_url: data.url,
24+
context: data.name,
25+
description: data.description
26+
});
27+
28+
const headers = {
29+
'Authorization': `token ${repoToken}`,
30+
'User-Agent': `${name}/${version}`,
31+
'Content-Type': 'application/json',
32+
'Content-Length': Buffer.byteLength(requestData)
33+
};
34+
35+
return new Promise((resolve) => {
36+
request({
37+
url: `https://api.github.com/repos/angular/material2/statuses/${commitSHA}`,
38+
method: 'POST',
39+
form: requestData,
40+
headers: headers
41+
}, (error: any, response: any) => resolve(response.statusCode));
42+
});
43+
}

tools/dashboard/functions/index.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Entry point for the Firebase functions of the dashboard app. Firebase functions only support
3+
* JavaScript files and therefore the TypeScript files needs to be transpiled.
4+
*/
5+
6+
'use strict';
7+
8+
const path = require('path');
9+
10+
// Enable TypeScript compilation at runtime using ts-node.
11+
require('ts-node').register({
12+
project: path.join(__dirname, 'tsconfig.json')
13+
});
14+
15+
// Export all functions from the TypeScript source.
16+
Object.assign(exports, require('./functions'));
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {verify} from 'jsonwebtoken';
2+
import {config} from 'firebase-functions';
3+
4+
/** The JWT secret. This is used to validate JWT. */
5+
const jwtSecret = config().jwtSecret;
6+
7+
/** The repo slug. This is used to validate the JWT is sent from correct repo. */
8+
const repoSlug = config().repoSlug;
9+
10+
export function verifyToken(token: string): boolean {
11+
try {
12+
const tokenPayload = verify(token, jwtSecret, {issuer: 'Travis CI, GmbH'});
13+
14+
if (tokenPayload.slug !== repoSlug) {
15+
console.log(`JWT slugs are not matching. Expected ${repoSlug}`);
16+
}
17+
18+
return true;
19+
} catch (e) {
20+
return false;
21+
}
22+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "material2-dashboard-functions",
3+
"version": "0.0.1",
4+
"main": "index.js",
5+
"dependencies": {
6+
"@types/jsonwebtoken": "^7.2.0",
7+
"firebase-admin": "~4.2.1",
8+
"firebase-functions": "^0.5.7",
9+
"jsonwebtoken": "^7.4.1",
10+
"request": "^2.81.0",
11+
"ts-node": "^3.0.4",
12+
"typescript": "^2.3.3"
13+
}
14+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {https} from 'firebase-functions';
2+
import {verifyToken} from './jwt/verify-token';
3+
import {setGithubStatus} from './github/github-status';
4+
5+
export const payloadGithubStatus = https.onRequest(async (request, response) => {
6+
const authToken = request.header('auth-token');
7+
const commitSha = request.header('commit-sha');
8+
const payloadDiff = parseInt(request.header('commit-payload-diff'));
9+
10+
if (!verifyToken(authToken)) {
11+
return response.status(403).json({message: 'Auth token is not valid'});
12+
}
13+
14+
if (!commitSha) {
15+
return response.status(404).json({message: 'No commit has been specified'});
16+
}
17+
18+
if (!payloadDiff || isNaN(payloadDiff)) {
19+
return response.status(400).json({message: 'No valid payload diff has been specified.'});
20+
}
21+
22+
await setGithubStatus(commitSha, {
23+
result: true,
24+
name: 'Library Payload',
25+
url: `https://travis-ci.org/angular/material2/jobs/${process.env['TRAVIS_JOB_ID']}`,
26+
description: `${payloadDiff > 0 ? `+` : ''} ${payloadDiff.toFixed(2)}KB`
27+
});
28+
29+
response.json({message: 'Payload Github status successfully set.'});
30+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"compilerOptions": {
3+
"lib": ["es2015", "es2016", "dom"],
4+
"module": "commonjs",
5+
"moduleResolution": "node",
6+
"noEmitOnError": true,
7+
"noImplicitAny": true,
8+
"sourceMap": true,
9+
"target": "es5",
10+
"baseUrl": "",
11+
"outDir": "../../../dist/dashboard-functions/"
12+
},
13+
"files": [
14+
"./functions.ts"
15+
]
16+
}

0 commit comments

Comments
 (0)