Skip to content

Commit 6e98768

Browse files
committed
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.
1 parent affcf5e commit 6e98768

11 files changed

+179
-4
lines changed

scripts/ci/publish-artifacts.sh

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

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

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

3031
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: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {https, config} from 'firebase-functions';
2+
import {verify} from 'jsonwebtoken';
3+
import {setGithubStatus} from './github-status';
4+
5+
/** The repo slug. This is used to validate the JWT is sent from correct repo. */
6+
const repoSlug = config().repoSlug;
7+
8+
/** API token for the Github repository. Required to set the github status on commits and PRs. */
9+
const repoToken = config().repoToken;
10+
11+
/** The JWT secret. This is used to validate JWT. */
12+
const jwtSecret = config().jwtSecret;
13+
14+
export const payloadGithubStatus = https.onRequest(async (request, response) => {
15+
const authToken = request.header('auth-token');
16+
const commitSha = request.header('commit-sha');
17+
const payloadDiff = parseInt(request.header('commit-payload-diff'));
18+
19+
if (!verifyToken(authToken)) {
20+
return response.status(403).json({message: 'Auth token is not valid'});
21+
}
22+
23+
if (!commitSha) {
24+
return response.status(404).json({message: 'No commit has been specified'});
25+
}
26+
27+
if (!payloadDiff || isNaN(payloadDiff)) {
28+
return response.status(400).json({message: 'No valid payload diff has been specified.'});
29+
}
30+
31+
await setGithubStatus(commitSha, repoToken, {
32+
result: true,
33+
name: 'Library Payload',
34+
url: `https://travis-ci.org/angular/material2/jobs/${process.env['TRAVIS_JOB_ID']}`,
35+
description: `${payloadDiff > 0 ? `+` : ''} ${payloadDiff.toFixed(2)}KB`
36+
});
37+
38+
response.json({message: 'Payload Github status successfully set.'});
39+
});
40+
41+
function verifyToken(token: string): boolean {
42+
try {
43+
const tokenPayload = verify(token, jwtSecret, {issuer: 'Travis CI, GmbH'});
44+
if (tokenPayload.slug !== repoSlug) {
45+
console.log(`JWT slugs are not matching. Expected ${repoSlug}`);
46+
}
47+
return true;
48+
} catch (e) {
49+
return false;
50+
}
51+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const request = require('request');
2+
const {version, name} = require('./package.json');
3+
4+
/** Data that must be specified to set a Github PR status. */
5+
export type GithubStatusData = {
6+
result: boolean;
7+
name: string;
8+
description: string;
9+
url: string;
10+
};
11+
12+
/** Function that sets a Github commit status */
13+
export function setGithubStatus(commitSHA: string, authToken: string, data: GithubStatusData) {
14+
const state = data.result ? 'success' : 'failure';
15+
16+
const requestData = JSON.stringify({
17+
state: state,
18+
target_url: data.url,
19+
context: data.name,
20+
description: data.description
21+
});
22+
23+
const headers = {
24+
'Authorization': `token ${authToken}`,
25+
'User-Agent': `${name}/${version}`,
26+
'Content-Type': 'application/json',
27+
'Content-Length': Buffer.byteLength(requestData)
28+
};
29+
30+
return new Promise((resolve) => {
31+
request({
32+
url: `https://api.github.com/repos/angular/material2/statuses/${commitSHA}`,
33+
method: 'POST',
34+
form: requestData,
35+
headers: headers
36+
}, (error: any, response: any) => resolve(response.statusCode));
37+
});
38+
}

tools/dashboard/functions/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
const functionExports = require('./dashboard-functions');
16+
17+
// Re-export every firebase function from TypeScript
18+
Object.keys(functionExports).forEach(fnName => {
19+
module.exports[fnName] = functionExports[fnName];
20+
});
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: 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+
"./dashboard-functions.ts"
15+
]
16+
}

0 commit comments

Comments
 (0)