ci(docs-infra): rename 'upload-server' to 'preview-server'
The server no longer has files uploaded to it. Instead it is more accurate to refer to it as dealing with "previews" of PRs.
This commit is contained in:
parent
2f791ce68b
commit
1c34e02ae6
@ -38,12 +38,12 @@ ARG AIO_SIGNIFICANT_FILES_PATTERN='^(?:aio|packages)/(?!.*[._]spec\\.[jt]s$
|
|||||||
ARG TEST_AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN
|
ARG TEST_AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN
|
||||||
ARG AIO_TRUSTED_PR_LABEL="aio: preview"
|
ARG AIO_TRUSTED_PR_LABEL="aio: preview"
|
||||||
ARG TEST_AIO_TRUSTED_PR_LABEL="aio: preview"
|
ARG TEST_AIO_TRUSTED_PR_LABEL="aio: preview"
|
||||||
ARG AIO_UPLOAD_HOSTNAME=upload.localhost
|
ARG AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
|
||||||
ARG TEST_AIO_UPLOAD_HOSTNAME=upload.localhost
|
ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
|
||||||
ARG AIO_UPLOAD_MAX_SIZE=20971520
|
ARG AIO_ARTIFACT_MAX_SIZE=20971520
|
||||||
ARG TEST_AIO_UPLOAD_MAX_SIZE=200
|
ARG TEST_AIO_ARTIFACT_MAX_SIZE=200
|
||||||
ARG AIO_UPLOAD_PORT=3000
|
ARG AIO_PREVIEW_SERVER_PORT=3000
|
||||||
ARG TEST_AIO_UPLOAD_PORT=3001
|
ARG TEST_AIO_PREVIEW_SERVER_PORT=3001
|
||||||
|
|
||||||
ENV AIO_ARTIFACT_PATH=$AIO_ARTIFACT_PATH TEST_AIO_ARTIFACT_PATH=$TEST_AIO_ARTIFACT_PATH \
|
ENV AIO_ARTIFACT_PATH=$AIO_ARTIFACT_PATH TEST_AIO_ARTIFACT_PATH=$TEST_AIO_ARTIFACT_PATH \
|
||||||
AIO_BUILDS_DIR=$AIO_BUILDS_DIR TEST_AIO_BUILDS_DIR=$TEST_AIO_BUILDS_DIR \
|
AIO_BUILDS_DIR=$AIO_BUILDS_DIR TEST_AIO_BUILDS_DIR=$TEST_AIO_BUILDS_DIR \
|
||||||
@ -60,9 +60,9 @@ ENV AIO_ARTIFACT_PATH=$AIO_ARTIFACT_PATH TEST_AIO_ARTIF
|
|||||||
AIO_SCRIPTS_SH_DIR=/usr/share/aio-scripts-sh \
|
AIO_SCRIPTS_SH_DIR=/usr/share/aio-scripts-sh \
|
||||||
AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN TEST_AIO_SIGNIFICANT_FILES_PATTERN=$TEST_AIO_SIGNIFICANT_FILES_PATTERN \
|
AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN TEST_AIO_SIGNIFICANT_FILES_PATTERN=$TEST_AIO_SIGNIFICANT_FILES_PATTERN \
|
||||||
AIO_TRUSTED_PR_LABEL=$AIO_TRUSTED_PR_LABEL TEST_AIO_TRUSTED_PR_LABEL=$TEST_AIO_TRUSTED_PR_LABEL \
|
AIO_TRUSTED_PR_LABEL=$AIO_TRUSTED_PR_LABEL TEST_AIO_TRUSTED_PR_LABEL=$TEST_AIO_TRUSTED_PR_LABEL \
|
||||||
AIO_UPLOAD_HOSTNAME=$AIO_UPLOAD_HOSTNAME TEST_AIO_UPLOAD_HOSTNAME=$TEST_AIO_UPLOAD_HOSTNAME \
|
AIO_PREVIEW_SERVER_HOSTNAME=$AIO_PREVIEW_SERVER_HOSTNAME TEST_AIO_PREVIEW_SERVER_HOSTNAME=$TEST_AIO_PREVIEW_SERVER_HOSTNAME \
|
||||||
AIO_UPLOAD_MAX_SIZE=$AIO_UPLOAD_MAX_SIZE TEST_AIO_UPLOAD_MAX_SIZE=$TEST_AIO_UPLOAD_MAX_SIZE \
|
AIO_ARTIFACT_MAX_SIZE=$AIO_ARTIFACT_MAX_SIZE TEST_AIO_ARTIFACT_MAX_SIZE=$TEST_AIO_ARTIFACT_MAX_SIZE \
|
||||||
AIO_UPLOAD_PORT=$AIO_UPLOAD_PORT TEST_AIO_UPLOAD_PORT=$TEST_AIO_UPLOAD_PORT \
|
AIO_PREVIEW_SERVER_PORT=$AIO_PREVIEW_SERVER_PORT TEST_AIO_PREVIEW_SERVER_PORT=$TEST_AIO_PREVIEW_SERVER_PORT \
|
||||||
AIO_WWW_USER=www-data \
|
AIO_WWW_USER=www-data \
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
@ -108,9 +108,9 @@ RUN printenv | grep AIO_ >> /etc/environment
|
|||||||
# Set up dnsmasq
|
# Set up dnsmasq
|
||||||
COPY dnsmasq/dnsmasq.conf /etc/
|
COPY dnsmasq/dnsmasq.conf /etc/
|
||||||
RUN sed -i "s|{{\$AIO_NGINX_HOSTNAME}}|$AIO_NGINX_HOSTNAME|g" /etc/dnsmasq.conf
|
RUN sed -i "s|{{\$AIO_NGINX_HOSTNAME}}|$AIO_NGINX_HOSTNAME|g" /etc/dnsmasq.conf
|
||||||
RUN sed -i "s|{{\$AIO_UPLOAD_HOSTNAME}}|$AIO_UPLOAD_HOSTNAME|g" /etc/dnsmasq.conf
|
RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_HOSTNAME}}|$AIO_PREVIEW_SERVER_HOSTNAME|g" /etc/dnsmasq.conf
|
||||||
RUN sed -i "s|{{\$TEST_AIO_NGINX_HOSTNAME}}|$TEST_AIO_NGINX_HOSTNAME|g" /etc/dnsmasq.conf
|
RUN sed -i "s|{{\$TEST_AIO_NGINX_HOSTNAME}}|$TEST_AIO_NGINX_HOSTNAME|g" /etc/dnsmasq.conf
|
||||||
RUN sed -i "s|{{\$TEST_AIO_UPLOAD_HOSTNAME}}|$TEST_AIO_UPLOAD_HOSTNAME|g" /etc/dnsmasq.conf
|
RUN sed -i "s|{{\$TEST_AIO_PREVIEW_SERVER_HOSTNAME}}|$TEST_AIO_PREVIEW_SERVER_HOSTNAME|g" /etc/dnsmasq.conf
|
||||||
|
|
||||||
|
|
||||||
# Set up SSL/TLS certificates
|
# Set up SSL/TLS certificates
|
||||||
@ -134,9 +134,9 @@ RUN sed -i "s|{{\$AIO_LOCALCERTS_DIR}}|$AIO_LOCALCERTS_DIR|g" /etc/nginx/conf.d/
|
|||||||
RUN sed -i "s|{{\$AIO_NGINX_LOGS_DIR}}|$AIO_NGINX_LOGS_DIR|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
RUN sed -i "s|{{\$AIO_NGINX_LOGS_DIR}}|$AIO_NGINX_LOGS_DIR|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
||||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTP}}|$AIO_NGINX_PORT_HTTP|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTP}}|$AIO_NGINX_PORT_HTTP|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
||||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTPS}}|$AIO_NGINX_PORT_HTTPS|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTPS}}|$AIO_NGINX_PORT_HTTPS|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
||||||
RUN sed -i "s|{{\$AIO_UPLOAD_HOSTNAME}}|$AIO_UPLOAD_HOSTNAME|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_HOSTNAME}}|$AIO_PREVIEW_SERVER_HOSTNAME|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
||||||
RUN sed -i "s|{{\$AIO_UPLOAD_MAX_SIZE}}|$AIO_UPLOAD_MAX_SIZE|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
RUN sed -i "s|{{\$AIO_ARTIFACT_MAX_SIZE}}|$AIO_ARTIFACT_MAX_SIZE|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
||||||
RUN sed -i "s|{{\$AIO_UPLOAD_PORT}}|$AIO_UPLOAD_PORT|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_PORT}}|$AIO_PREVIEW_SERVER_PORT|g" /etc/nginx/conf.d/aio-builds-prod.conf
|
||||||
|
|
||||||
COPY nginx/aio-builds.conf /etc/nginx/conf.d/aio-builds-test.conf
|
COPY nginx/aio-builds.conf /etc/nginx/conf.d/aio-builds-test.conf
|
||||||
RUN sed -i "s|{{\$AIO_BUILDS_DIR}}|$TEST_AIO_BUILDS_DIR|g" /etc/nginx/conf.d/aio-builds-test.conf
|
RUN sed -i "s|{{\$AIO_BUILDS_DIR}}|$TEST_AIO_BUILDS_DIR|g" /etc/nginx/conf.d/aio-builds-test.conf
|
||||||
@ -145,9 +145,9 @@ RUN sed -i "s|{{\$AIO_LOCALCERTS_DIR}}|$TEST_AIO_LOCALCERTS_DIR|g" /etc/nginx/co
|
|||||||
RUN sed -i "s|{{\$AIO_NGINX_LOGS_DIR}}|$TEST_AIO_NGINX_LOGS_DIR|g" /etc/nginx/conf.d/aio-builds-test.conf
|
RUN sed -i "s|{{\$AIO_NGINX_LOGS_DIR}}|$TEST_AIO_NGINX_LOGS_DIR|g" /etc/nginx/conf.d/aio-builds-test.conf
|
||||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTP}}|$TEST_AIO_NGINX_PORT_HTTP|g" /etc/nginx/conf.d/aio-builds-test.conf
|
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTP}}|$TEST_AIO_NGINX_PORT_HTTP|g" /etc/nginx/conf.d/aio-builds-test.conf
|
||||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTPS}}|$TEST_AIO_NGINX_PORT_HTTPS|g" /etc/nginx/conf.d/aio-builds-test.conf
|
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTPS}}|$TEST_AIO_NGINX_PORT_HTTPS|g" /etc/nginx/conf.d/aio-builds-test.conf
|
||||||
RUN sed -i "s|{{\$AIO_UPLOAD_HOSTNAME}}|$TEST_AIO_UPLOAD_HOSTNAME|g" /etc/nginx/conf.d/aio-builds-test.conf
|
RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_HOSTNAME}}|$TEST_AIO_PREVIEW_SERVER_HOSTNAME|g" /etc/nginx/conf.d/aio-builds-test.conf
|
||||||
RUN sed -i "s|{{\$AIO_UPLOAD_MAX_SIZE}}|$TEST_AIO_UPLOAD_MAX_SIZE|g" /etc/nginx/conf.d/aio-builds-test.conf
|
RUN sed -i "s|{{\$AIO_ARTIFACT_MAX_SIZE}}|$TEST_AIO_ARTIFACT_MAX_SIZE|g" /etc/nginx/conf.d/aio-builds-test.conf
|
||||||
RUN sed -i "s|{{\$AIO_UPLOAD_PORT}}|$TEST_AIO_UPLOAD_PORT|g" /etc/nginx/conf.d/aio-builds-test.conf
|
RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_PORT}}|$TEST_AIO_PREVIEW_SERVER_PORT|g" /etc/nginx/conf.d/aio-builds-test.conf
|
||||||
|
|
||||||
|
|
||||||
# Set up pm2
|
# Set up pm2
|
||||||
|
@ -8,9 +8,9 @@ listen-address=127.0.0.1
|
|||||||
|
|
||||||
# Force an IP address for these domains.
|
# Force an IP address for these domains.
|
||||||
address=/{{$AIO_NGINX_HOSTNAME}}/127.0.0.1
|
address=/{{$AIO_NGINX_HOSTNAME}}/127.0.0.1
|
||||||
address=/{{$AIO_UPLOAD_HOSTNAME}}/127.0.0.1
|
address=/{{$AIO_PREVIEW_SERVER_HOSTNAME}}/127.0.0.1
|
||||||
address=/{{$TEST_AIO_NGINX_HOSTNAME}}/127.0.0.1
|
address=/{{$TEST_AIO_NGINX_HOSTNAME}}/127.0.0.1
|
||||||
address=/{{$TEST_AIO_UPLOAD_HOSTNAME}}/127.0.0.1
|
address=/{{$TEST_AIO_PREVIEW_SERVER_HOSTNAME}}/127.0.0.1
|
||||||
|
|
||||||
# Run as root (required from inside docker container).
|
# Run as root (required from inside docker container).
|
||||||
user=root
|
user=root
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/var/log/aio/upload-server-*.log {
|
/var/log/aio/preview-server-*.log {
|
||||||
compress
|
compress
|
||||||
copytruncate
|
copytruncate
|
||||||
delaycompress
|
delaycompress
|
@ -76,7 +76,7 @@ server {
|
|||||||
proxy_pass_request_headers on;
|
proxy_pass_request_headers on;
|
||||||
proxy_redirect off;
|
proxy_redirect off;
|
||||||
proxy_method POST;
|
proxy_method POST;
|
||||||
proxy_pass http://{{$AIO_UPLOAD_HOSTNAME}}:{{$AIO_UPLOAD_PORT}}$request_uri;
|
proxy_pass http://{{$AIO_PREVIEW_SERVER_HOSTNAME}}:{{$AIO_PREVIEW_SERVER_PORT}}$request_uri;
|
||||||
|
|
||||||
resolver 127.0.0.1;
|
resolver 127.0.0.1;
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ server {
|
|||||||
proxy_pass_request_headers on;
|
proxy_pass_request_headers on;
|
||||||
proxy_redirect off;
|
proxy_redirect off;
|
||||||
proxy_method POST;
|
proxy_method POST;
|
||||||
proxy_pass http://{{$AIO_UPLOAD_HOSTNAME}}:{{$AIO_UPLOAD_PORT}}$request_uri;
|
proxy_pass http://{{$AIO_PREVIEW_SERVER_HOSTNAME}}:{{$AIO_PREVIEW_SERVER_PORT}}$request_uri;
|
||||||
|
|
||||||
resolver 127.0.0.1;
|
resolver 127.0.0.1;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ export const AIO_NGINX_PORT_HTTP = +getEnvVar('AIO_NGINX_PORT_HTTP');
|
|||||||
export const AIO_NGINX_PORT_HTTPS = +getEnvVar('AIO_NGINX_PORT_HTTPS');
|
export const AIO_NGINX_PORT_HTTPS = +getEnvVar('AIO_NGINX_PORT_HTTPS');
|
||||||
export const AIO_SIGNIFICANT_FILES_PATTERN = getEnvVar('AIO_SIGNIFICANT_FILES_PATTERN');
|
export const AIO_SIGNIFICANT_FILES_PATTERN = getEnvVar('AIO_SIGNIFICANT_FILES_PATTERN');
|
||||||
export const AIO_TRUSTED_PR_LABEL = getEnvVar('AIO_TRUSTED_PR_LABEL');
|
export const AIO_TRUSTED_PR_LABEL = getEnvVar('AIO_TRUSTED_PR_LABEL');
|
||||||
export const AIO_UPLOAD_HOSTNAME = getEnvVar('AIO_UPLOAD_HOSTNAME');
|
export const AIO_PREVIEW_SERVER_HOSTNAME = getEnvVar('AIO_PREVIEW_SERVER_HOSTNAME');
|
||||||
export const AIO_UPLOAD_PORT = +getEnvVar('AIO_UPLOAD_PORT');
|
export const AIO_PREVIEW_SERVER_PORT = +getEnvVar('AIO_PREVIEW_SERVER_PORT');
|
||||||
export const AIO_UPLOAD_MAX_SIZE = +getEnvVar('AIO_UPLOAD_MAX_SIZE');
|
export const AIO_ARTIFACT_MAX_SIZE = +getEnvVar('AIO_ARTIFACT_MAX_SIZE');
|
||||||
export const AIO_WWW_USER = getEnvVar('AIO_WWW_USER');
|
export const AIO_WWW_USER = getEnvVar('AIO_WWW_USER');
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {assert, assertNotMissingOrEmpty} from '../common/utils';
|
|
||||||
import {GithubApi} from './github-api';
|
import {GithubApi} from './github-api';
|
||||||
|
import {assert, assertNotMissingOrEmpty} from './utils';
|
||||||
|
|
||||||
export interface PullRequest {
|
export interface PullRequest {
|
||||||
number: number;
|
number: number;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {assertNotMissingOrEmpty} from '../common/utils';
|
|
||||||
import {GithubApi} from './github-api';
|
import {GithubApi} from './github-api';
|
||||||
|
import {assertNotMissingOrEmpty} from './utils';
|
||||||
|
|
||||||
export interface Team {
|
export interface Team {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -7,7 +7,7 @@ import * as shell from 'shelljs';
|
|||||||
import {HIDDEN_DIR_PREFIX} from '../common/constants';
|
import {HIDDEN_DIR_PREFIX} from '../common/constants';
|
||||||
import {assertNotMissingOrEmpty, computeShortSha, createLogger} from '../common/utils';
|
import {assertNotMissingOrEmpty, computeShortSha, createLogger} from '../common/utils';
|
||||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from './build-events';
|
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from './build-events';
|
||||||
import {UploadError} from './upload-error';
|
import {PreviewServerError} from './preview-error';
|
||||||
|
|
||||||
// Classes
|
// Classes
|
||||||
export class BuildCreator extends EventEmitter {
|
export class BuildCreator extends EventEmitter {
|
||||||
@ -36,7 +36,7 @@ export class BuildCreator extends EventEmitter {
|
|||||||
then(([prDirExisted, shaDirExisted]) => {
|
then(([prDirExisted, shaDirExisted]) => {
|
||||||
if (shaDirExisted) {
|
if (shaDirExisted) {
|
||||||
const publicOrNot = isPublic ? 'public' : 'non-public';
|
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||||
throw new UploadError(409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
throw new PreviewServerError(409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
dirToRemoveOnError = prDirExisted ? shaDir : prDir;
|
dirToRemoveOnError = prDirExisted ? shaDir : prDir;
|
||||||
@ -52,8 +52,8 @@ export class BuildCreator extends EventEmitter {
|
|||||||
shell.rm('-rf', dirToRemoveOnError);
|
shell.rm('-rf', dirToRemoveOnError);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(err instanceof UploadError)) {
|
if (!(err instanceof PreviewServerError)) {
|
||||||
err = new UploadError(500, `Error while uploading to directory: ${shaDir}\n${err}`);
|
err = new PreviewServerError(500, `Error while creating preview at: ${shaDir}\n${err}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw err;
|
throw err;
|
||||||
@ -71,7 +71,8 @@ export class BuildCreator extends EventEmitter {
|
|||||||
return false;
|
return false;
|
||||||
} else if (targetVisPrDirExisted) {
|
} else if (targetVisPrDirExisted) {
|
||||||
// Error: Directories for both visibilities exist.
|
// Error: Directories for both visibilities exist.
|
||||||
throw new UploadError(409, `Request to move '${otherVisPrDir}' to existing directory '${targetVisPrDir}'.`);
|
throw new PreviewServerError(409,
|
||||||
|
`Request to move '${otherVisPrDir}' to existing directory '${targetVisPrDir}'.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visibility change: Moving `otherVisPrDir` to `targetVisPrDir`.
|
// Visibility change: Moving `otherVisPrDir` to `targetVisPrDir`.
|
||||||
@ -82,8 +83,8 @@ export class BuildCreator extends EventEmitter {
|
|||||||
then(() => true);
|
then(() => true);
|
||||||
}).
|
}).
|
||||||
catch(err => {
|
catch(err => {
|
||||||
if (!(err instanceof UploadError)) {
|
if (!(err instanceof PreviewServerError)) {
|
||||||
err = new UploadError(500, `Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\n${err}`);
|
err = new PreviewServerError(500, `Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\n${err}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw err;
|
throw err;
|
@ -5,7 +5,7 @@ import {mkdir} from 'shelljs';
|
|||||||
import {promisify} from 'util';
|
import {promisify} from 'util';
|
||||||
import {CircleCiApi} from '../common/circle-ci-api';
|
import {CircleCiApi} from '../common/circle-ci-api';
|
||||||
import {assert, assertNotMissingOrEmpty, computeArtifactDownloadPath, createLogger} from '../common/utils';
|
import {assert, assertNotMissingOrEmpty, computeArtifactDownloadPath, createLogger} from '../common/utils';
|
||||||
import {UploadError} from '../upload-server/upload-error';
|
import {PreviewServerError} from './preview-error';
|
||||||
|
|
||||||
export interface GithubInfo {
|
export interface GithubInfo {
|
||||||
org: string;
|
org: string;
|
||||||
@ -58,7 +58,7 @@ export class BuildRetriever {
|
|||||||
const url = await this.api.getBuildArtifactUrl(buildNum, artifactPath);
|
const url = await this.api.getBuildArtifactUrl(buildNum, artifactPath);
|
||||||
const response = await fetch(url, {size: this.downloadSizeLimit});
|
const response = await fetch(url, {size: this.downloadSizeLimit});
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
throw new UploadError(response.status, `Error ${response.status} - ${response.statusText}`);
|
throw new PreviewServerError(response.status, `Error ${response.status} - ${response.statusText}`);
|
||||||
}
|
}
|
||||||
const buffer = await response.buffer();
|
const buffer = await response.buffer();
|
||||||
mkdir('-p', dirname(outPath));
|
mkdir('-p', dirname(outPath));
|
||||||
@ -68,7 +68,7 @@ export class BuildRetriever {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.warn(error);
|
this.logger.warn(error);
|
||||||
const status = (error.type === 'max-size') ? 413 : 500;
|
const status = (error.type === 'max-size') ? 413 : 500;
|
||||||
throw new UploadError(status, `CircleCI artifact download failed (${error.message || error})`);
|
throw new PreviewServerError(status, `CircleCI artifact download failed (${error.message || error})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
// Imports
|
// Imports
|
||||||
import {AIO_DOWNLOADS_DIR} from '../common/constants';
|
import {AIO_DOWNLOADS_DIR} from '../common/constants';
|
||||||
import {
|
import {
|
||||||
|
AIO_ARTIFACT_MAX_SIZE,
|
||||||
AIO_ARTIFACT_PATH,
|
AIO_ARTIFACT_PATH,
|
||||||
AIO_BUILDS_DIR,
|
AIO_BUILDS_DIR,
|
||||||
AIO_CIRCLE_CI_TOKEN,
|
AIO_CIRCLE_CI_TOKEN,
|
||||||
@ -9,26 +10,25 @@ import {
|
|||||||
AIO_GITHUB_REPO,
|
AIO_GITHUB_REPO,
|
||||||
AIO_GITHUB_TEAM_SLUGS,
|
AIO_GITHUB_TEAM_SLUGS,
|
||||||
AIO_GITHUB_TOKEN,
|
AIO_GITHUB_TOKEN,
|
||||||
|
AIO_PREVIEW_SERVER_HOSTNAME,
|
||||||
|
AIO_PREVIEW_SERVER_PORT,
|
||||||
AIO_SIGNIFICANT_FILES_PATTERN,
|
AIO_SIGNIFICANT_FILES_PATTERN,
|
||||||
AIO_TRUSTED_PR_LABEL,
|
AIO_TRUSTED_PR_LABEL,
|
||||||
AIO_UPLOAD_HOSTNAME,
|
|
||||||
AIO_UPLOAD_MAX_SIZE,
|
|
||||||
AIO_UPLOAD_PORT,
|
|
||||||
} from '../common/env-variables';
|
} from '../common/env-variables';
|
||||||
import {UploadServerFactory} from './upload-server-factory';
|
import {PreviewServerFactory} from './preview-server-factory';
|
||||||
|
|
||||||
// Run
|
// Run
|
||||||
_main();
|
_main();
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
function _main(): void {
|
function _main(): void {
|
||||||
UploadServerFactory
|
PreviewServerFactory
|
||||||
.create({
|
.create({
|
||||||
buildArtifactPath: AIO_ARTIFACT_PATH,
|
buildArtifactPath: AIO_ARTIFACT_PATH,
|
||||||
buildsDir: AIO_BUILDS_DIR,
|
buildsDir: AIO_BUILDS_DIR,
|
||||||
circleCiToken: AIO_CIRCLE_CI_TOKEN,
|
circleCiToken: AIO_CIRCLE_CI_TOKEN,
|
||||||
domainName: AIO_DOMAIN_NAME,
|
domainName: AIO_DOMAIN_NAME,
|
||||||
downloadSizeLimit: AIO_UPLOAD_MAX_SIZE,
|
downloadSizeLimit: AIO_ARTIFACT_MAX_SIZE,
|
||||||
downloadsDir: AIO_DOWNLOADS_DIR,
|
downloadsDir: AIO_DOWNLOADS_DIR,
|
||||||
githubOrg: AIO_GITHUB_ORGANIZATION,
|
githubOrg: AIO_GITHUB_ORGANIZATION,
|
||||||
githubRepo: AIO_GITHUB_REPO,
|
githubRepo: AIO_GITHUB_REPO,
|
||||||
@ -37,5 +37,5 @@ function _main(): void {
|
|||||||
significantFilesPattern: AIO_SIGNIFICANT_FILES_PATTERN,
|
significantFilesPattern: AIO_SIGNIFICANT_FILES_PATTERN,
|
||||||
trustedPrLabel: AIO_TRUSTED_PR_LABEL,
|
trustedPrLabel: AIO_TRUSTED_PR_LABEL,
|
||||||
})
|
})
|
||||||
.listen(AIO_UPLOAD_PORT, AIO_UPLOAD_HOSTNAME);
|
.listen(AIO_PREVIEW_SERVER_PORT, AIO_PREVIEW_SERVER_HOSTNAME);
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
// Classes
|
// Classes
|
||||||
export class UploadError extends Error {
|
export class PreviewServerError extends Error {
|
||||||
// Constructor
|
// Constructor
|
||||||
constructor(public status: number = 500, message?: string) {
|
constructor(public status: number = 500, message?: string) {
|
||||||
super(message);
|
super(message);
|
||||||
Object.setPrototypeOf(this, UploadError.prototype);
|
Object.setPrototypeOf(this, PreviewServerError.prototype);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@ import {respondWithError, throwRequestError} from './utils';
|
|||||||
const AIO_PREVIEW_JOB = 'aio_preview';
|
const AIO_PREVIEW_JOB = 'aio_preview';
|
||||||
|
|
||||||
// Interfaces - Types
|
// Interfaces - Types
|
||||||
export interface UploadServerConfig {
|
export interface PreviewServerConfig {
|
||||||
downloadsDir: string;
|
downloadsDir: string;
|
||||||
downloadSizeLimit: number;
|
downloadSizeLimit: number;
|
||||||
buildArtifactPath: string;
|
buildArtifactPath: string;
|
||||||
@ -31,12 +31,12 @@ export interface UploadServerConfig {
|
|||||||
trustedPrLabel: string;
|
trustedPrLabel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const logger = createLogger('UploadServer');
|
const logger = createLogger('PreviewServer');
|
||||||
|
|
||||||
// Classes
|
// Classes
|
||||||
export class UploadServerFactory {
|
export class PreviewServerFactory {
|
||||||
// Methods - Public
|
// Methods - Public
|
||||||
public static create(cfg: UploadServerConfig): http.Server {
|
public static create(cfg: PreviewServerConfig): http.Server {
|
||||||
assertNotMissingOrEmpty('domainName', cfg.domainName);
|
assertNotMissingOrEmpty('domainName', cfg.domainName);
|
||||||
|
|
||||||
const circleCiApi = new CircleCiApi(cfg.githubOrg, cfg.githubRepo, cfg.circleCiToken);
|
const circleCiApi = new CircleCiApi(cfg.githubOrg, cfg.githubRepo, cfg.circleCiToken);
|
||||||
@ -46,9 +46,9 @@ export class UploadServerFactory {
|
|||||||
|
|
||||||
const buildRetriever = new BuildRetriever(circleCiApi, cfg.downloadSizeLimit, cfg.downloadsDir);
|
const buildRetriever = new BuildRetriever(circleCiApi, cfg.downloadSizeLimit, cfg.downloadsDir);
|
||||||
const buildVerifier = new BuildVerifier(prs, teams, cfg.githubTeamSlugs, cfg.trustedPrLabel);
|
const buildVerifier = new BuildVerifier(prs, teams, cfg.githubTeamSlugs, cfg.trustedPrLabel);
|
||||||
const buildCreator = UploadServerFactory.createBuildCreator(prs, cfg.buildsDir, cfg.domainName);
|
const buildCreator = PreviewServerFactory.createBuildCreator(prs, cfg.buildsDir, cfg.domainName);
|
||||||
|
|
||||||
const middleware = UploadServerFactory.createMiddleware(buildRetriever, buildVerifier, buildCreator, cfg);
|
const middleware = PreviewServerFactory.createMiddleware(buildRetriever, buildVerifier, buildCreator, cfg);
|
||||||
const httpServer = http.createServer(middleware as any);
|
const httpServer = http.createServer(middleware as any);
|
||||||
|
|
||||||
httpServer.on('listening', () => {
|
httpServer.on('listening', () => {
|
||||||
@ -60,7 +60,7 @@ export class UploadServerFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static createMiddleware(buildRetriever: BuildRetriever, buildVerifier: BuildVerifier,
|
public static createMiddleware(buildRetriever: BuildRetriever, buildVerifier: BuildVerifier,
|
||||||
buildCreator: BuildCreator, cfg: UploadServerConfig): express.Express {
|
buildCreator: BuildCreator, cfg: PreviewServerConfig): express.Express {
|
||||||
const middleware = express();
|
const middleware = express();
|
||||||
const jsonParser = bodyParser.json();
|
const jsonParser = bodyParser.json();
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ export class UploadServerFactory {
|
|||||||
middleware.all('*', req => throwRequestError(404, 'Unknown resource', req));
|
middleware.all('*', req => throwRequestError(404, 'Unknown resource', req));
|
||||||
middleware.use((err: any, _req: any, res: express.Response, _next: any) => {
|
middleware.use((err: any, _req: any, res: express.Response, _next: any) => {
|
||||||
const statusText = http.STATUS_CODES[err.status] || '???';
|
const statusText = http.STATUS_CODES[err.status] || '???';
|
||||||
logger.error(`Upload error: ${err.status} - ${statusText}:`, err.message);
|
logger.error(`Preview server error: ${err.status} - ${statusText}:`, err.message);
|
||||||
respondWithError(res, err);
|
respondWithError(res, err);
|
||||||
});
|
});
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import {promisify} from 'util';
|
import {promisify} from 'util';
|
||||||
import {UploadError} from './upload-error';
|
import {PreviewServerError} from './preview-error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the response to report that an error has occurred.
|
* Update the response to report that an error has occurred.
|
||||||
@ -8,8 +8,8 @@ import {UploadError} from './upload-error';
|
|||||||
* @param err The error that needs to be reported.
|
* @param err The error that needs to be reported.
|
||||||
*/
|
*/
|
||||||
export async function respondWithError(res: express.Response, err: any): Promise<void> {
|
export async function respondWithError(res: express.Response, err: any): Promise<void> {
|
||||||
if (!(err instanceof UploadError)) {
|
if (!(err instanceof PreviewServerError)) {
|
||||||
err = new UploadError(500, String((err && err.message) || err));
|
err = new PreviewServerError(500, String((err && err.message) || err));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(err.status);
|
res.status(err.status);
|
||||||
@ -25,5 +25,5 @@ export async function respondWithError(res: express.Response, err: any): Promise
|
|||||||
export function throwRequestError(status: number, error: string, req: express.Request): never {
|
export function throwRequestError(status: number, error: string, req: express.Request): never {
|
||||||
const message = `${error} in request: ${req.method} ${req.originalUrl}` +
|
const message = `${error} in request: ${req.method} ${req.originalUrl}` +
|
||||||
(!req.body ? '' : ` ${JSON.stringify(req.body)}`);
|
(!req.body ? '' : ` ${JSON.stringify(req.body)}`);
|
||||||
throw new UploadError(status, message);
|
throw new PreviewServerError(status, message);
|
||||||
}
|
}
|
@ -6,9 +6,9 @@ import {createLogger, getEnvVar} from '../common/utils';
|
|||||||
import {BuildNums, PrNums, SHA} from './constants';
|
import {BuildNums, PrNums, SHA} from './constants';
|
||||||
|
|
||||||
// We are using the `nock` library to fake responses from REST requests, when testing.
|
// We are using the `nock` library to fake responses from REST requests, when testing.
|
||||||
// This is necessary, because the test upload-server runs as a separate node process to
|
// This is necessary, because the test preview-server runs as a separate node process to
|
||||||
// the test harness, so we do not have direct access to the code (e.g. for mocking).
|
// the test harness, so we do not have direct access to the code (e.g. for mocking).
|
||||||
// (See also 'lib/verify-setup/start-test-upload-server.ts'.)
|
// (See also 'lib/verify-setup/start-test-preview-server.ts'.)
|
||||||
|
|
||||||
// Each of the potential requests to an external API (e.g. Github or CircleCI) are mocked
|
// Each of the potential requests to an external API (e.g. Github or CircleCI) are mocked
|
||||||
// below and return a suitable response. This is quite complicated to setup since the
|
// below and return a suitable response. This is quite complicated to setup since the
|
||||||
|
@ -266,7 +266,7 @@ describe(`nginx`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass requests through to the upload server', done => {
|
it('should pass requests through to the preview server', done => {
|
||||||
h.runCmd(`curl -iLX POST ${scheme}://${host}/circle-build`).
|
h.runCmd(`curl -iLX POST ${scheme}://${host}/circle-build`).
|
||||||
then(h.verifyResponse(400, /Incorrect body content. Expected JSON/)).
|
then(h.verifyResponse(400, /Incorrect body content. Expected JSON/)).
|
||||||
then(done);
|
then(done);
|
||||||
@ -304,7 +304,7 @@ describe(`nginx`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass requests through to the upload server', done => {
|
it('should pass requests through to the preview server', done => {
|
||||||
const cmdPrefix = `curl -iLX POST --header "Content-Type: application/json"`;
|
const cmdPrefix = `curl -iLX POST --header "Content-Type: application/json"`;
|
||||||
|
|
||||||
const cmd1 = `${cmdPrefix} ${url}`;
|
const cmd1 = `${cmdPrefix} ${url}`;
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
// Imports
|
// Imports
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
import {AIO_UPLOAD_HOSTNAME, AIO_UPLOAD_PORT, AIO_WWW_USER} from '../common/env-variables';
|
import {AIO_PREVIEW_SERVER_HOSTNAME, AIO_PREVIEW_SERVER_PORT, AIO_WWW_USER} from '../common/env-variables';
|
||||||
import {computeShortSha} from '../common/utils';
|
import {computeShortSha} from '../common/utils';
|
||||||
import {ALT_SHA, BuildNums, PrNums, SHA, SIMILAR_SHA} from './constants';
|
import {ALT_SHA, BuildNums, PrNums, SHA, SIMILAR_SHA} from './constants';
|
||||||
import {helper as h, makeCurl, payload} from './helper';
|
import {helper as h, makeCurl, payload} from './helper';
|
||||||
import {customMatchers} from './jasmine-custom-matchers';
|
import {customMatchers} from './jasmine-custom-matchers';
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('upload-server', () => {
|
describe('preview-server', () => {
|
||||||
const hostname = AIO_UPLOAD_HOSTNAME;
|
const hostname = AIO_PREVIEW_SERVER_HOSTNAME;
|
||||||
const port = AIO_UPLOAD_PORT;
|
const port = AIO_PREVIEW_SERVER_PORT;
|
||||||
const host = `http://${hostname}:${port}`;
|
const host = `http://${hostname}:${port}`;
|
||||||
|
|
||||||
beforeEach(() => jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000);
|
beforeEach(() => jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000);
|
||||||
@ -122,7 +122,7 @@ describe('upload-server', () => {
|
|||||||
|
|
||||||
describe(`for ${label} builds`, () => {
|
describe(`for ${label} builds`, () => {
|
||||||
|
|
||||||
it('should extract the contents of the uploaded file', async () => {
|
it('should extract the contents of the build artifact', async () => {
|
||||||
await curl(payload(build))
|
await curl(payload(build))
|
||||||
.then(h.verifyResponse(statusCode));
|
.then(h.verifyResponse(statusCode));
|
||||||
expect(h.readBuildFile(prNum, SHA, 'index.html', isPublic))
|
expect(h.readBuildFile(prNum, SHA, 'index.html', isPublic))
|
||||||
@ -148,7 +148,7 @@ describe('upload-server', () => {
|
|||||||
expect({ prNum, isPublic }).toExistAsABuild();
|
expect({ prNum, isPublic }).toExistAsABuild();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete the uploaded file', async () => {
|
it('should delete the build artifact file', async () => {
|
||||||
await curl(payload(build))
|
await curl(payload(build))
|
||||||
.then(h.verifyResponse(statusCode));
|
.then(h.verifyResponse(statusCode));
|
||||||
expect({ prNum, SHA }).not.toExistAsAnArtifact();
|
expect({ prNum, SHA }).not.toExistAsAnArtifact();
|
@ -25,7 +25,7 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`integration (on ${scheme
|
|||||||
|
|
||||||
describe('for a new/non-existing PR', () => {
|
describe('for a new/non-existing PR', () => {
|
||||||
|
|
||||||
it('should be able to upload and serve a public build', async () => {
|
it('should be able to create and serve a public preview', async () => {
|
||||||
const BUILD = BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
const BUILD = BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
||||||
const PR = PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
const PR = PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`integration (on ${scheme
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should be able to upload but not serve a hidden build', async () => {
|
it('should be able to create but not serve a hidden preview', async () => {
|
||||||
const BUILD = BuildNums.TRUST_CHECK_UNTRUSTED;
|
const BUILD = BuildNums.TRUST_CHECK_UNTRUSTED;
|
||||||
const PR = PrNums.TRUST_CHECK_UNTRUSTED;
|
const PR = PrNums.TRUST_CHECK_UNTRUSTED;
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`integration (on ${scheme
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject an upload if verification fails', async () => {
|
it('should reject if verification fails', async () => {
|
||||||
const BUILD = BuildNums.TRUST_CHECK_ERROR;
|
const BUILD = BuildNums.TRUST_CHECK_ERROR;
|
||||||
const PR = PrNums.TRUST_CHECK_ERROR;
|
const PR = PrNums.TRUST_CHECK_ERROR;
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`integration (on ${scheme
|
|||||||
|
|
||||||
describe('for an existing PR', () => {
|
describe('for an existing PR', () => {
|
||||||
|
|
||||||
it('should be able to upload and serve a public build', async () => {
|
it('should be able to create and serve a public preview', async () => {
|
||||||
const BUILD = BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
const BUILD = BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
||||||
const PR = PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
const PR = PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`integration (on ${scheme
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should be able to upload but not serve a hidden build', async () => {
|
it('should be able to create but not serve a hidden preview', async () => {
|
||||||
const BUILD = BuildNums.TRUST_CHECK_UNTRUSTED;
|
const BUILD = BuildNums.TRUST_CHECK_UNTRUSTED;
|
||||||
const PR = PrNums.TRUST_CHECK_UNTRUSTED;
|
const PR = PrNums.TRUST_CHECK_UNTRUSTED;
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`integration (on ${scheme
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject an upload if verification fails', async () => {
|
it('should reject if verification fails', async () => {
|
||||||
const BUILD = BuildNums.TRUST_CHECK_ERROR;
|
const BUILD = BuildNums.TRUST_CHECK_ERROR;
|
||||||
const PR = PrNums.TRUST_CHECK_ERROR;
|
const PR = PrNums.TRUST_CHECK_ERROR;
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`integration (on ${scheme
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should not be able to overwrite an existing public build', async () => {
|
it('should not be able to overwrite an existing public preview', async () => {
|
||||||
const BUILD = BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
const BUILD = BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
||||||
const PR = PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
const PR = PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER;
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`integration (on ${scheme
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should not be able to overwrite an existing hidden build', async () => {
|
it('should not be able to overwrite an existing hidden preview', async () => {
|
||||||
const BUILD = BuildNums.TRUST_CHECK_UNTRUSTED;
|
const BUILD = BuildNums.TRUST_CHECK_UNTRUSTED;
|
||||||
const PR = PrNums.TRUST_CHECK_UNTRUSTED;
|
const PR = PrNums.TRUST_CHECK_UNTRUSTED;
|
||||||
h.createDummyBuild(PR, SHA, false);
|
h.createDummyBuild(PR, SHA, false);
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
import '../upload-server';
|
import '../preview-server';
|
||||||
import './mock-external-apis';
|
import './mock-external-apis';
|
@ -5,10 +5,10 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as shell from 'shelljs';
|
import * as shell from 'shelljs';
|
||||||
import {SHORT_SHA_LEN} from '../../lib/common/constants';
|
import {SHORT_SHA_LEN} from '../../lib/common/constants';
|
||||||
import {BuildCreator} from '../../lib/upload-server/build-creator';
|
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
||||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/upload-server/build-events';
|
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
||||||
import {UploadError} from '../../lib/upload-server/upload-error';
|
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||||
import {expectToBeUploadError} from './helpers';
|
import {expectToBePreviewServerError} from './helpers';
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('BuildCreator', () => {
|
describe('BuildCreator', () => {
|
||||||
@ -134,7 +134,7 @@ describe('BuildCreator', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if changing the PR\'s visibility fails', done => {
|
it('should abort and skip further operations if changing the PR\'s visibility fails', done => {
|
||||||
const mockError = new UploadError(543, 'Test');
|
const mockError = new PreviewServerError(543, 'Test');
|
||||||
bcUpdatePrVisibilitySpy.and.callFake(() => Promise.reject(mockError));
|
bcUpdatePrVisibilitySpy.and.callFake(() => Promise.reject(mockError));
|
||||||
|
|
||||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
@ -154,7 +154,7 @@ describe('BuildCreator', () => {
|
|||||||
existsValues[shaDir] = true;
|
existsValues[shaDir] = true;
|
||||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
const publicOrNot = isPublic ? 'public' : 'non-public';
|
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||||
expectToBeUploadError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
expectToBePreviewServerError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
@ -171,7 +171,7 @@ describe('BuildCreator', () => {
|
|||||||
|
|
||||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
const publicOrNot = isPublic ? 'public' : 'non-public';
|
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||||
expectToBeUploadError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
expectToBePreviewServerError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
@ -222,20 +222,20 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject with an UploadError', done => {
|
it('should reject with an PreviewServerError', done => {
|
||||||
// tslint:disable-next-line: no-string-throw
|
// tslint:disable-next-line: no-string-throw
|
||||||
shellMkdirSpy.and.callFake(() => { throw 'Test'; });
|
shellMkdirSpy.and.callFake(() => { throw 'Test'; });
|
||||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
expectToBeUploadError(err, 500, `Error while uploading to directory: ${shaDir}\nTest`);
|
expectToBePreviewServerError(err, 500, `Error while creating preview at: ${shaDir}\nTest`);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass UploadError instances unmodified', done => {
|
it('should pass PreviewServerError instances unmodified', done => {
|
||||||
shellMkdirSpy.and.callFake(() => { throw new UploadError(543, 'Test'); });
|
shellMkdirSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
||||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
expectToBeUploadError(err, 543, 'Test');
|
expectToBePreviewServerError(err, 543, 'Test');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -376,7 +376,8 @@ describe('BuildCreator', () => {
|
|||||||
it('should abort and skip further operations if both directories exist', done => {
|
it('should abort and skip further operations if both directories exist', done => {
|
||||||
bcExistsSpy.and.returnValue(true);
|
bcExistsSpy.and.returnValue(true);
|
||||||
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||||
expectToBeUploadError(err, 409, `Request to move '${oldPrDir}' to existing directory '${newPrDir}'.`);
|
expectToBePreviewServerError(err, 409,
|
||||||
|
`Request to move '${oldPrDir}' to existing directory '${newPrDir}'.`);
|
||||||
expect(shellMvSpy).not.toHaveBeenCalled();
|
expect(shellMvSpy).not.toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
@ -407,20 +408,21 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject with an UploadError', done => {
|
it('should reject with an PreviewServerError', done => {
|
||||||
// tslint:disable-next-line: no-string-throw
|
// tslint:disable-next-line: no-string-throw
|
||||||
shellMvSpy.and.callFake(() => { throw 'Test'; });
|
shellMvSpy.and.callFake(() => { throw 'Test'; });
|
||||||
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||||
expectToBeUploadError(err, 500, `Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\nTest`);
|
expectToBePreviewServerError(err, 500,
|
||||||
|
`Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\nTest`);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass UploadError instances unmodified', done => {
|
it('should pass PreviewServerError instances unmodified', done => {
|
||||||
shellMvSpy.and.callFake(() => { throw new UploadError(543, 'Test'); });
|
shellMvSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
||||||
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||||
expectToBeUploadError(err, 543, 'Test');
|
expectToBePreviewServerError(err, 543, 'Test');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -528,7 +530,7 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should delete the uploaded file on success', done => {
|
it('should delete the build artifact file on success', done => {
|
||||||
(bc as any).extractArchive('input/file', 'output/dir').
|
(bc as any).extractArchive('input/file', 'output/dir').
|
||||||
then(() => expect(shellRmSpy).toHaveBeenCalledWith('-f', 'input/file')).
|
then(() => expect(shellRmSpy).toHaveBeenCalledWith('-f', 'input/file')).
|
||||||
then(done);
|
then(done);
|
||||||
@ -568,7 +570,7 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and reject if it fails to remove the uploaded file', done => {
|
it('should abort and reject if it fails to remove the build artifact file', done => {
|
||||||
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
||||||
expect(shellChmodSpy).toHaveBeenCalled();
|
expect(shellChmodSpy).toHaveBeenCalled();
|
||||||
expect(shellRmSpy).toHaveBeenCalled();
|
expect(shellRmSpy).toHaveBeenCalled();
|
@ -1,5 +1,5 @@
|
|||||||
// Imports
|
// Imports
|
||||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/upload-server/build-events';
|
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('ChangedPrVisibilityEvent', () => {
|
describe('ChangedPrVisibilityEvent', () => {
|
@ -1,7 +1,7 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as nock from 'nock';
|
import * as nock from 'nock';
|
||||||
import {BuildInfo, CircleCiApi} from '../../lib/common/circle-ci-api';
|
import {BuildInfo, CircleCiApi} from '../../lib/common/circle-ci-api';
|
||||||
import {BuildRetriever} from '../../lib/upload-server/build-retriever';
|
import {BuildRetriever} from '../../lib/preview-server/build-retriever';
|
||||||
|
|
||||||
describe('BuildRetriever', () => {
|
describe('BuildRetriever', () => {
|
||||||
const MAX_DOWNLOAD_SIZE = 10000;
|
const MAX_DOWNLOAD_SIZE = 10000;
|
@ -2,7 +2,7 @@
|
|||||||
import {GithubApi} from '../../lib/common/github-api';
|
import {GithubApi} from '../../lib/common/github-api';
|
||||||
import {GithubPullRequests, PullRequest} from '../../lib/common/github-pull-requests';
|
import {GithubPullRequests, PullRequest} from '../../lib/common/github-pull-requests';
|
||||||
import {GithubTeams} from '../../lib/common/github-teams';
|
import {GithubTeams} from '../../lib/common/github-teams';
|
||||||
import {BuildVerifier} from '../../lib/upload-server/build-verifier';
|
import {BuildVerifier} from '../../lib/preview-server/build-verifier';
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('BuildVerifier', () => {
|
describe('BuildVerifier', () => {
|
@ -0,0 +1,11 @@
|
|||||||
|
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||||
|
|
||||||
|
export const expectToBePreviewServerError = (actual: PreviewServerError, status?: number, message?: string) => {
|
||||||
|
expect(actual).toEqual(jasmine.any(PreviewServerError));
|
||||||
|
if (status != null) {
|
||||||
|
expect(actual.status).toBe(status);
|
||||||
|
}
|
||||||
|
if (message != null) {
|
||||||
|
expect(actual.message).toBe(message);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,39 @@
|
|||||||
|
// Imports
|
||||||
|
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||||
|
|
||||||
|
// Tests
|
||||||
|
describe('PreviewServerError', () => {
|
||||||
|
let err: PreviewServerError;
|
||||||
|
|
||||||
|
beforeEach(() => err = new PreviewServerError(999, 'message'));
|
||||||
|
|
||||||
|
|
||||||
|
it('should extend Error', () => {
|
||||||
|
expect(err).toEqual(jasmine.any(PreviewServerError));
|
||||||
|
expect(err).toEqual(jasmine.any(Error));
|
||||||
|
|
||||||
|
expect(Object.getPrototypeOf(err)).toBe(PreviewServerError.prototype);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should have a \'status\' property', () => {
|
||||||
|
expect(err.status).toBe(999);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should have a \'message\' property', () => {
|
||||||
|
expect(err.message).toBe('message');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should have a 500 \'status\' by default', () => {
|
||||||
|
expect(new PreviewServerError().status).toBe(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should have an empty \'message\' by default', () => {
|
||||||
|
expect(new PreviewServerError().message).toBe('');
|
||||||
|
expect(new PreviewServerError(999).message).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -7,11 +7,11 @@ import {CircleCiApi} from '../../lib/common/circle-ci-api';
|
|||||||
import {GithubApi} from '../../lib/common/github-api';
|
import {GithubApi} from '../../lib/common/github-api';
|
||||||
import {GithubPullRequests} from '../../lib/common/github-pull-requests';
|
import {GithubPullRequests} from '../../lib/common/github-pull-requests';
|
||||||
import {GithubTeams} from '../../lib/common/github-teams';
|
import {GithubTeams} from '../../lib/common/github-teams';
|
||||||
import {BuildCreator} from '../../lib/upload-server/build-creator';
|
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
||||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/upload-server/build-events';
|
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
||||||
import {BuildRetriever, GithubInfo} from '../../lib/upload-server/build-retriever';
|
import {BuildRetriever, GithubInfo} from '../../lib/preview-server/build-retriever';
|
||||||
import {BuildVerifier} from '../../lib/upload-server/build-verifier';
|
import {BuildVerifier} from '../../lib/preview-server/build-verifier';
|
||||||
import {UploadServerConfig, UploadServerFactory} from '../../lib/upload-server/upload-server-factory';
|
import {PreviewServerConfig, PreviewServerFactory} from '../../lib/preview-server/preview-server-factory';
|
||||||
|
|
||||||
interface CircleCiWebHookPayload {
|
interface CircleCiWebHookPayload {
|
||||||
payload: {
|
payload: {
|
||||||
@ -23,8 +23,8 @@ interface CircleCiWebHookPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('uploadServerFactory', () => {
|
describe('PreviewServerFactory', () => {
|
||||||
const defaultConfig: UploadServerConfig = {
|
const defaultConfig: PreviewServerConfig = {
|
||||||
buildArtifactPath: 'artifact/path.zip',
|
buildArtifactPath: 'artifact/path.zip',
|
||||||
buildsDir: 'builds/dir',
|
buildsDir: 'builds/dir',
|
||||||
circleCiToken: 'CIRCLE_CI_TOKEN',
|
circleCiToken: 'CIRCLE_CI_TOKEN',
|
||||||
@ -40,8 +40,8 @@ describe('uploadServerFactory', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
const createUploadServer = (partialConfig: Partial<UploadServerConfig> = {}) =>
|
const createPreviewServer = (partialConfig: Partial<PreviewServerConfig> = {}) =>
|
||||||
UploadServerFactory.create({...defaultConfig, ...partialConfig});
|
PreviewServerFactory.create({...defaultConfig, ...partialConfig});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(console, 'error');
|
spyOn(console, 'error');
|
||||||
@ -53,64 +53,64 @@ describe('uploadServerFactory', () => {
|
|||||||
let usfCreateMiddlewareSpy: jasmine.Spy;
|
let usfCreateMiddlewareSpy: jasmine.Spy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
usfCreateMiddlewareSpy = spyOn(UploadServerFactory, 'createMiddleware').and.callThrough();
|
usfCreateMiddlewareSpy = spyOn(PreviewServerFactory, 'createMiddleware').and.callThrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should throw if \'buildsDir\' is missing or empty', () => {
|
it('should throw if \'buildsDir\' is missing or empty', () => {
|
||||||
expect(() => createUploadServer({buildsDir: ''})).
|
expect(() => createPreviewServer({buildsDir: ''})).
|
||||||
toThrowError('Missing or empty required parameter \'buildsDir\'!');
|
toThrowError('Missing or empty required parameter \'buildsDir\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should throw if \'domainName\' is missing or empty', () => {
|
it('should throw if \'domainName\' is missing or empty', () => {
|
||||||
expect(() => createUploadServer({domainName: ''})).
|
expect(() => createPreviewServer({domainName: ''})).
|
||||||
toThrowError('Missing or empty required parameter \'domainName\'!');
|
toThrowError('Missing or empty required parameter \'domainName\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should throw if \'githubToken\' is missing or empty', () => {
|
it('should throw if \'githubToken\' is missing or empty', () => {
|
||||||
expect(() => createUploadServer({githubToken: ''})).
|
expect(() => createPreviewServer({githubToken: ''})).
|
||||||
toThrowError('Missing or empty required parameter \'githubToken\'!');
|
toThrowError('Missing or empty required parameter \'githubToken\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should throw if \'githubOrg\' is missing or empty', () => {
|
it('should throw if \'githubOrg\' is missing or empty', () => {
|
||||||
expect(() => createUploadServer({githubOrg: ''})).
|
expect(() => createPreviewServer({githubOrg: ''})).
|
||||||
toThrowError('Missing or empty required parameter \'githubOrg\'!');
|
toThrowError('Missing or empty required parameter \'githubOrg\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should throw if \'githubTeamSlugs\' is missing or empty', () => {
|
it('should throw if \'githubTeamSlugs\' is missing or empty', () => {
|
||||||
expect(() => createUploadServer({githubTeamSlugs: []})).
|
expect(() => createPreviewServer({githubTeamSlugs: []})).
|
||||||
toThrowError('Missing or empty required parameter \'allowedTeamSlugs\'!');
|
toThrowError('Missing or empty required parameter \'allowedTeamSlugs\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should throw if \'githubRepo\' is missing or empty', () => {
|
it('should throw if \'githubRepo\' is missing or empty', () => {
|
||||||
expect(() => createUploadServer({githubRepo: ''})).
|
expect(() => createPreviewServer({githubRepo: ''})).
|
||||||
toThrowError('Missing or empty required parameter \'githubRepo\'!');
|
toThrowError('Missing or empty required parameter \'githubRepo\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should throw if \'trustedPrLabel\' is missing or empty', () => {
|
it('should throw if \'trustedPrLabel\' is missing or empty', () => {
|
||||||
expect(() => createUploadServer({trustedPrLabel: ''})).
|
expect(() => createPreviewServer({trustedPrLabel: ''})).
|
||||||
toThrowError('Missing or empty required parameter \'trustedPrLabel\'!');
|
toThrowError('Missing or empty required parameter \'trustedPrLabel\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return an http.Server', () => {
|
it('should return an http.Server', () => {
|
||||||
const httpCreateServerSpy = spyOn(http, 'createServer').and.callThrough();
|
const httpCreateServerSpy = spyOn(http, 'createServer').and.callThrough();
|
||||||
const server = createUploadServer();
|
const server = createPreviewServer();
|
||||||
|
|
||||||
expect(server).toBe(httpCreateServerSpy.calls.mostRecent().returnValue);
|
expect(server).toBe(httpCreateServerSpy.calls.mostRecent().returnValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should create and use an appropriate BuildCreator', () => {
|
it('should create and use an appropriate BuildCreator', () => {
|
||||||
const usfCreateBuildCreatorSpy = spyOn(UploadServerFactory, 'createBuildCreator').and.callThrough();
|
const usfCreateBuildCreatorSpy = spyOn(PreviewServerFactory, 'createBuildCreator').and.callThrough();
|
||||||
|
|
||||||
createUploadServer();
|
createPreviewServer();
|
||||||
const buildRetriever = jasmine.any(BuildRetriever);
|
const buildRetriever = jasmine.any(BuildRetriever);
|
||||||
const buildVerifier = jasmine.any(BuildVerifier);
|
const buildVerifier = jasmine.any(BuildVerifier);
|
||||||
const prs = jasmine.any(GithubPullRequests);
|
const prs = jasmine.any(GithubPullRequests);
|
||||||
@ -124,7 +124,7 @@ describe('uploadServerFactory', () => {
|
|||||||
it('should create and use an appropriate middleware', () => {
|
it('should create and use an appropriate middleware', () => {
|
||||||
const httpCreateServerSpy = spyOn(http, 'createServer').and.callThrough();
|
const httpCreateServerSpy = spyOn(http, 'createServer').and.callThrough();
|
||||||
|
|
||||||
createUploadServer();
|
createPreviewServer();
|
||||||
|
|
||||||
const buildRetriever = jasmine.any(BuildRetriever);
|
const buildRetriever = jasmine.any(BuildRetriever);
|
||||||
const buildVerifier = jasmine.any(BuildVerifier);
|
const buildVerifier = jasmine.any(BuildVerifier);
|
||||||
@ -137,14 +137,14 @@ describe('uploadServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should log the server address info on \'listening\'', () => {
|
it('should log the server address info on \'listening\'', () => {
|
||||||
const server = createUploadServer();
|
const server = createPreviewServer();
|
||||||
server.address = () => ({address: 'foo', family: '', port: 1337});
|
server.address = () => ({address: 'foo', family: '', port: 1337});
|
||||||
|
|
||||||
expect(console.info).not.toHaveBeenCalled();
|
expect(console.info).not.toHaveBeenCalled();
|
||||||
|
|
||||||
server.emit('listening');
|
server.emit('listening');
|
||||||
expect(console.info).toHaveBeenCalledWith(
|
expect(console.info).toHaveBeenCalledWith(
|
||||||
jasmine.any(String), 'UploadServer: ', 'Up and running (and listening on foo:1337)...');
|
jasmine.any(String), 'PreviewServer: ', 'Up and running (and listening on foo:1337)...');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -158,7 +158,7 @@ describe('uploadServerFactory', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const api = new GithubApi(defaultConfig.githubToken);
|
const api = new GithubApi(defaultConfig.githubToken);
|
||||||
const prs = new GithubPullRequests(api, defaultConfig.githubOrg, defaultConfig.githubRepo);
|
const prs = new GithubPullRequests(api, defaultConfig.githubOrg, defaultConfig.githubRepo);
|
||||||
buildCreator = UploadServerFactory.createBuildCreator(prs, defaultConfig.buildsDir, defaultConfig.domainName);
|
buildCreator = PreviewServerFactory.createBuildCreator(prs, defaultConfig.buildsDir, defaultConfig.domainName);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass the \'buildsDir\' to the BuildCreator', () => {
|
it('should pass the \'buildsDir\' to the BuildCreator', () => {
|
||||||
@ -256,7 +256,7 @@ describe('uploadServerFactory', () => {
|
|||||||
buildVerifier = new BuildVerifier(prs, teams, defaultConfig.githubTeamSlugs, defaultConfig.trustedPrLabel);
|
buildVerifier = new BuildVerifier(prs, teams, defaultConfig.githubTeamSlugs, defaultConfig.trustedPrLabel);
|
||||||
buildCreator = new BuildCreator(defaultConfig.buildsDir);
|
buildCreator = new BuildCreator(defaultConfig.buildsDir);
|
||||||
|
|
||||||
const middleware = UploadServerFactory.createMiddleware(buildRetriever, buildVerifier, buildCreator,
|
const middleware = PreviewServerFactory.createMiddleware(buildRetriever, buildVerifier, buildCreator,
|
||||||
defaultConfig);
|
defaultConfig);
|
||||||
agent = supertest.agent(middleware);
|
agent = supertest.agent(middleware);
|
||||||
});
|
});
|
||||||
@ -359,7 +359,7 @@ describe('uploadServerFactory', () => {
|
|||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(204);
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(204);
|
||||||
expect(getGithubInfoSpy).not.toHaveBeenCalled();
|
expect(getGithubInfoSpy).not.toHaveBeenCalled();
|
||||||
expect(getSignificantFilesChangedSpy).not.toHaveBeenCalled();
|
expect(getSignificantFilesChangedSpy).not.toHaveBeenCalled();
|
||||||
expect(console.log).toHaveBeenCalledWith(jasmine.any(String), 'UploadServer: ',
|
expect(console.log).toHaveBeenCalledWith(jasmine.any(String), 'PreviewServer: ',
|
||||||
'Build:12345, Job:lint -', 'Skipping preview processing because this is not the "aio_preview" job.');
|
'Build:12345, Job:lint -', 'Skipping preview processing because this is not the "aio_preview" job.');
|
||||||
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
||||||
expect(getPrIsTrustedSpy).not.toHaveBeenCalled();
|
expect(getPrIsTrustedSpy).not.toHaveBeenCalled();
|
||||||
@ -371,7 +371,7 @@ describe('uploadServerFactory', () => {
|
|||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(204);
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(204);
|
||||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||||
expect(getSignificantFilesChangedSpy).toHaveBeenCalledWith(PR, jasmine.any(RegExp));
|
expect(getSignificantFilesChangedSpy).toHaveBeenCalledWith(PR, jasmine.any(RegExp));
|
||||||
expect(console.log).toHaveBeenCalledWith(jasmine.any(String), 'UploadServer: ',
|
expect(console.log).toHaveBeenCalledWith(jasmine.any(String), 'PreviewServer: ',
|
||||||
'PR:777, Build:12345 - Skipping preview processing because this PR did not touch any significant files.');
|
'PR:777, Build:12345 - Skipping preview processing because this PR did not touch any significant files.');
|
||||||
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
||||||
expect(getPrIsTrustedSpy).not.toHaveBeenCalled();
|
expect(getPrIsTrustedSpy).not.toHaveBeenCalled();
|
@ -1,8 +1,8 @@
|
|||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import {UploadError} from '../../lib/upload-server/upload-error';
|
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||||
import {respondWithError, throwRequestError} from '../../lib/upload-server/utils';
|
import {respondWithError, throwRequestError} from '../../lib/preview-server/utils';
|
||||||
|
|
||||||
describe('upload-server/utils', () => {
|
describe('preview-server/utils', () => {
|
||||||
describe('respondWithError', () => {
|
describe('respondWithError', () => {
|
||||||
let endSpy: jasmine.Spy;
|
let endSpy: jasmine.Spy;
|
||||||
let statusSpy: jasmine.Spy;
|
let statusSpy: jasmine.Spy;
|
||||||
@ -15,12 +15,12 @@ describe('upload-server/utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should set the status on the response', () => {
|
it('should set the status on the response', () => {
|
||||||
respondWithError(response, new UploadError(505, 'TEST MESSAGE'));
|
respondWithError(response, new PreviewServerError(505, 'TEST MESSAGE'));
|
||||||
expect(statusSpy).toHaveBeenCalledWith(505);
|
expect(statusSpy).toHaveBeenCalledWith(505);
|
||||||
expect(endSpy).toHaveBeenCalledWith('TEST MESSAGE', jasmine.any(Function));
|
expect(endSpy).toHaveBeenCalledWith('TEST MESSAGE', jasmine.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert non-UploadError errors to 500 UploadErrors', () => {
|
it('should convert non-PreviewServerError errors to 500 PreviewServerErrors', () => {
|
||||||
respondWithError(response, new Error('OTHER MESSAGE'));
|
respondWithError(response, new Error('OTHER MESSAGE'));
|
||||||
expect(statusSpy).toHaveBeenCalledWith(500);
|
expect(statusSpy).toHaveBeenCalledWith(500);
|
||||||
expect(endSpy).toHaveBeenCalledWith('OTHER MESSAGE', jasmine.any(Function));
|
expect(endSpy).toHaveBeenCalledWith('OTHER MESSAGE', jasmine.any(Function));
|
||||||
@ -39,7 +39,7 @@ describe('upload-server/utils', () => {
|
|||||||
throwRequestError(505, 'ERROR MESSAGE', request);
|
throwRequestError(505, 'ERROR MESSAGE', request);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
caught = true;
|
caught = true;
|
||||||
expect(error).toEqual(jasmine.any(UploadError));
|
expect(error).toEqual(jasmine.any(PreviewServerError));
|
||||||
expect(error.status).toEqual(505);
|
expect(error.status).toEqual(505);
|
||||||
expect(error.message).toEqual(`ERROR MESSAGE in request: POST some.domain.com/path "The request body"`);
|
expect(error.message).toEqual(`ERROR MESSAGE in request: POST some.domain.com/path "The request body"`);
|
||||||
}
|
}
|
@ -1,11 +0,0 @@
|
|||||||
import {UploadError} from '../../lib/upload-server/upload-error';
|
|
||||||
|
|
||||||
export const expectToBeUploadError = (actual: UploadError, status?: number, message?: string) => {
|
|
||||||
expect(actual).toEqual(jasmine.any(UploadError));
|
|
||||||
if (status != null) {
|
|
||||||
expect(actual.status).toBe(status);
|
|
||||||
}
|
|
||||||
if (message != null) {
|
|
||||||
expect(actual.message).toBe(message);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,39 +0,0 @@
|
|||||||
// Imports
|
|
||||||
import {UploadError} from '../../lib/upload-server/upload-error';
|
|
||||||
|
|
||||||
// Tests
|
|
||||||
describe('UploadError', () => {
|
|
||||||
let err: UploadError;
|
|
||||||
|
|
||||||
beforeEach(() => err = new UploadError(999, 'message'));
|
|
||||||
|
|
||||||
|
|
||||||
it('should extend Error', () => {
|
|
||||||
expect(err).toEqual(jasmine.any(UploadError));
|
|
||||||
expect(err).toEqual(jasmine.any(Error));
|
|
||||||
|
|
||||||
expect(Object.getPrototypeOf(err)).toBe(UploadError.prototype);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should have a \'status\' property', () => {
|
|
||||||
expect(err.status).toBe(999);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should have a \'message\' property', () => {
|
|
||||||
expect(err.message).toBe('message');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should have a 500 \'status\' by default', () => {
|
|
||||||
expect(new UploadError().status).toBe(500);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should have an empty \'message\' by default', () => {
|
|
||||||
expect(new UploadError().message).toBe('');
|
|
||||||
expect(new UploadError(999).message).toBe('');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -30,7 +30,7 @@ done
|
|||||||
|
|
||||||
# Check servers
|
# Check servers
|
||||||
origins=(
|
origins=(
|
||||||
http://$AIO_UPLOAD_HOSTNAME:$AIO_UPLOAD_PORT
|
http://$AIO_PREVIEW_SERVER_HOSTNAME:$AIO_PREVIEW_SERVER_PORT
|
||||||
http://$AIO_NGINX_HOSTNAME:$AIO_NGINX_PORT_HTTP
|
http://$AIO_NGINX_HOSTNAME:$AIO_NGINX_PORT_HTTP
|
||||||
https://$AIO_NGINX_HOSTNAME:$AIO_NGINX_PORT_HTTPS
|
https://$AIO_NGINX_HOSTNAME:$AIO_NGINX_PORT_HTTPS
|
||||||
)
|
)
|
||||||
|
@ -14,5 +14,5 @@ service cron start
|
|||||||
service dnsmasq start
|
service dnsmasq start
|
||||||
service nginx start
|
service nginx start
|
||||||
service pm2-root start
|
service pm2-root start
|
||||||
aio-upload-server-prod start
|
aio-preview-server-prod start
|
||||||
echo [`date`] - Services started successfully.
|
echo [`date`] - Services started successfully.
|
||||||
|
@ -5,10 +5,10 @@ set -eu -o pipefail
|
|||||||
export AIO_CIRCLE_CI_TOKEN=$(head -c -1 /aio-secrets/CIRCLE_CI_TOKEN 2>/dev/null || echo "MISSING_CIRCLE_CI_TOKEN")
|
export AIO_CIRCLE_CI_TOKEN=$(head -c -1 /aio-secrets/CIRCLE_CI_TOKEN 2>/dev/null || echo "MISSING_CIRCLE_CI_TOKEN")
|
||||||
export AIO_GITHUB_TOKEN=$(head -c -1 /aio-secrets/GITHUB_TOKEN 2>/dev/null || echo "MISSING_GITHUB_TOKEN")
|
export AIO_GITHUB_TOKEN=$(head -c -1 /aio-secrets/GITHUB_TOKEN 2>/dev/null || echo "MISSING_GITHUB_TOKEN")
|
||||||
|
|
||||||
# Start the upload-server instance
|
# Start the preview-server instance
|
||||||
action=$([ "$1" == "stop" ] && echo "stop" || echo "start")
|
action=$([ "$1" == "stop" ] && echo "stop" || echo "start")
|
||||||
pm2 $action $AIO_SCRIPTS_JS_DIR/dist/lib/upload-server \
|
pm2 $action $AIO_SCRIPTS_JS_DIR/dist/lib/preview-server \
|
||||||
--uid $AIO_WWW_USER \
|
--uid $AIO_WWW_USER \
|
||||||
--log /var/log/aio/upload-server-prod.log \
|
--log /var/log/aio/preview-server-prod.log \
|
||||||
--name aio-upload-server-prod \
|
--name aio-preview-server-prod \
|
||||||
${@:2}
|
${@:2}
|
@ -1,15 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -eu -o pipefail
|
set -eu -o pipefail
|
||||||
|
|
||||||
# Start the upload-server instance
|
# Start the preview-server instance
|
||||||
appName=aio-upload-server-test
|
appName=aio-preview-server-test
|
||||||
if [[ "$1" == "stop" ]]; then
|
if [[ "$1" == "stop" ]]; then
|
||||||
pm2 delete $appName
|
pm2 delete $appName
|
||||||
else
|
else
|
||||||
source aio-test-env
|
source aio-test-env
|
||||||
pm2 start $AIO_SCRIPTS_JS_DIR/dist/lib/verify-setup/start-test-upload-server.js \
|
pm2 start $AIO_SCRIPTS_JS_DIR/dist/lib/verify-setup/start-test-preview-server.js \
|
||||||
--uid $AIO_WWW_USER \
|
--uid $AIO_WWW_USER \
|
||||||
--log /var/log/aio/upload-server-test.log \
|
--log /var/log/aio/preview-server-test.log \
|
||||||
--name $appName \
|
--name $appName \
|
||||||
--no-autorestart \
|
--no-autorestart \
|
||||||
${@:2}
|
${@:2}
|
@ -11,9 +11,9 @@
|
|||||||
export AIO_GITHUB_TEAM_SLUGS=$TEST_AIO_GITHUB_TEAM_SLUGS
|
export AIO_GITHUB_TEAM_SLUGS=$TEST_AIO_GITHUB_TEAM_SLUGS
|
||||||
export AIO_SIGNIFICANT_FILES_PATTERN=$TEST_AIO_SIGNIFICANT_FILES_PATTERN
|
export AIO_SIGNIFICANT_FILES_PATTERN=$TEST_AIO_SIGNIFICANT_FILES_PATTERN
|
||||||
export AIO_TRUSTED_PR_LABEL=$TEST_AIO_TRUSTED_PR_LABEL
|
export AIO_TRUSTED_PR_LABEL=$TEST_AIO_TRUSTED_PR_LABEL
|
||||||
export AIO_UPLOAD_HOSTNAME=$TEST_AIO_UPLOAD_HOSTNAME
|
export AIO_PREVIEW_SERVER_HOSTNAME=$TEST_AIO_PREVIEW_SERVER_HOSTNAME
|
||||||
export AIO_UPLOAD_PORT=$TEST_AIO_UPLOAD_PORT
|
export AIO_PREVIEW_SERVER_PORT=$TEST_AIO_PREVIEW_SERVER_PORT
|
||||||
export AIO_UPLOAD_MAX_SIZE=$TEST_AIO_UPLOAD_MAX_SIZE
|
export AIO_ARTIFACT_MAX_SIZE=$TEST_AIO_ARTIFACT_MAX_SIZE
|
||||||
|
|
||||||
export AIO_CIRCLE_CI_TOKEN=TEST_CIRCLE_CI_TOKEN
|
export AIO_CIRCLE_CI_TOKEN=TEST_CIRCLE_CI_TOKEN
|
||||||
export AIO_GITHUB_TOKEN=TEST_GITHUB_TOKEN
|
export AIO_GITHUB_TOKEN=TEST_GITHUB_TOKEN
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
aio-verify-setup
|
aio-verify-setup
|
||||||
ls -t /var/log/aio/upload-server-verify* | head -1 | xargs cat
|
ls -t /var/log/aio/preview-server-verify* | head -1 | xargs cat
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
set -eu -o pipefail
|
set -eu -o pipefail
|
||||||
|
|
||||||
logFile=/var/log/aio/verify-setup.log
|
logFile=/var/log/aio/verify-setup.log
|
||||||
uploadServerLogFile=/var/log/aio/upload-server-verify-setup.log
|
previewServerLogFile=/var/log/aio/preview-server-verify-setup.log
|
||||||
|
|
||||||
exec 3>&1
|
exec 3>&1
|
||||||
exec >> $logFile
|
exec >> $logFile
|
||||||
@ -24,18 +24,18 @@ function countdown {
|
|||||||
|
|
||||||
function onExit {
|
function onExit {
|
||||||
echo -e "Stopping Test Server"
|
echo -e "Stopping Test Server"
|
||||||
aio-upload-server-test stop
|
aio-preview-server-test stop
|
||||||
echo -e "Full logs in '$logFile'.\n" > /dev/fd/3
|
echo -e "Full logs in '$logFile'.\n" > /dev/fd/3
|
||||||
}
|
}
|
||||||
|
|
||||||
# Setup EXIT trap
|
# Setup EXIT trap
|
||||||
trap 'onExit' EXIT
|
trap 'onExit' EXIT
|
||||||
|
|
||||||
# Start an upload-server instance for testing
|
# Start an preview-server instance for testing
|
||||||
echo -e "Starting Test Server"
|
echo -e "Starting Test Server"
|
||||||
aio-upload-server-test start --log $uploadServerLogFile
|
aio-preview-server-test start --log $previewServerLogFile
|
||||||
|
|
||||||
# Give the upload-server some time to start :(
|
# Give the preview-server some time to start :(
|
||||||
countdown "Starting" 5 > /dev/fd/3
|
countdown "Starting" 5 > /dev/fd/3
|
||||||
|
|
||||||
# Run the tests
|
# Run the tests
|
||||||
|
@ -54,14 +54,14 @@ you don't need to specify values for those.
|
|||||||
build artifacts publicly served. This is useful for enabling previews for any PR (not only those
|
build artifacts publicly served. This is useful for enabling previews for any PR (not only those
|
||||||
from trusted authors).
|
from trusted authors).
|
||||||
|
|
||||||
- `AIO_UPLOAD_HOSTNAME`:
|
- `AIO_PREVIEW_SERVER_HOSTNAME`:
|
||||||
The internal hostname for accessing the Node.js upload-server. This is used by nginx for
|
The internal hostname for accessing the Node.js preview-server. This is used by nginx for
|
||||||
delegating upload requests and also for performing a periodic health-check.
|
delegating web-hook requests and also for performing a periodic health-check.
|
||||||
|
|
||||||
- `AIO_UPLOAD_MAX_SIZE`:
|
- `AIO_ARTIFACT_MAX_SIZE`:
|
||||||
The maximum allowed size for the uploaded gzip archive containing the build artifacts. Files
|
The maximum allowed size for the gzip archive containing the build artifacts.
|
||||||
larger than this will be rejected.
|
Files larger than this will be rejected.
|
||||||
|
|
||||||
- `AIO_UPLOAD_PORT`:
|
- `AIO_PREVIEW_SERVER_PORT`:
|
||||||
The port number on which the Node.js upload-server listens for HTTP connections. This is used by
|
The port number on which the Node.js preview-server listens for HTTP connections. This is used by
|
||||||
nginx for delegating upload requests and also for performing a periodic health-check.
|
nginx for delegating web-hook requests and also for performing a periodic health-check.
|
||||||
|
@ -5,14 +5,14 @@ TODO (gkalpak): Add docs. Mention:
|
|||||||
- `aio-health-check`
|
- `aio-health-check`
|
||||||
- `aio-verify-setup`
|
- `aio-verify-setup`
|
||||||
- Test nginx accessible at:
|
- Test nginx accessible at:
|
||||||
- `http://$TEST_AIO_NGINX_HOTNAME:$TEST_AIO_NGINX_PORT_HTTP`
|
- `http://$TEST_AIO_NGINX_HOSTNAME:$TEST_AIO_NGINX_PORT_HTTP`
|
||||||
- `https://$TEST_AIO_NGINX_HOTNAME:$TEST_AIO_NGINX_PORT_HTTPS`
|
- `https://$TEST_AIO_NGINX_HOSTNAME:$TEST_AIO_NGINX_PORT_HTTPS`
|
||||||
- Test upload-server accessible at:
|
- Test preview-server accessible at:
|
||||||
- `http://$TEST_AIO_UPLOAD_HOTNAME:$TEST_AIO_UPLOAD_PORT`
|
- `http://$TEST_AIO_PREVIEW_SERVER_HOSTNAME:$TEST_AIO_PREVIEW_SERVER_PORT`
|
||||||
- Local DNS (via dnsmasq) maps the above hostnames to 127.0.0.1
|
- Local DNS (via dnsmasq) maps the above hostnames to 127.0.0.1
|
||||||
|
|
||||||
|
|
||||||
## Developing the upload server TypeScript files
|
## Developing the preview server TypeScript files
|
||||||
|
|
||||||
If you are running Docker on OS/X then you can benefit from linking the built TypeScript
|
If you are running Docker on OS/X then you can benefit from linking the built TypeScript
|
||||||
files (i.e. `script-js/dist`) to the JavaScript files inside the Docker container.
|
files (i.e. `script-js/dist`) to the JavaScript files inside the Docker container.
|
||||||
@ -39,9 +39,9 @@ aio-verify-setup
|
|||||||
```
|
```
|
||||||
|
|
||||||
Sometimes, the errors in the unit test log are not enough to tell you what went wrong.
|
Sometimes, the errors in the unit test log are not enough to tell you what went wrong.
|
||||||
In that case you can also look at the log of the upload-server itself.
|
In that case you can also look at the log of the preview-server itself.
|
||||||
A helper script that runs the unit tests (i.e. `aio-verify-setup`) and displays the
|
A helper script that runs the unit tests (i.e. `aio-verify-setup`) and displays the
|
||||||
last relevant test-upload-server log is:
|
last relevant test-preview-server log is:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
aio-verify-setup-and-log
|
aio-verify-setup-and-log
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
## Objective
|
## Objective
|
||||||
Whenever a PR job is run on the CI infrastructure (e.g. CircleCI), we want to build `angular.io`
|
Whenever a PR job is run on the CI infrastructure (e.g. CircleCI), we want to build `angular.io`
|
||||||
and upload the build artifacts to a publicly accessible server so that collaborators (developers,
|
and host the build artifacts on a publicly accessible server so that collaborators (developers,
|
||||||
designers, authors, etc) can preview the changes without having to checkout and build the app
|
designers, authors, etc) can preview the changes without having to checkout and build the app
|
||||||
locally.
|
locally.
|
||||||
|
|
||||||
@ -40,31 +40,31 @@ container:
|
|||||||
- The CI script checks whether the PR has touched any files that might affect the angular.io app
|
- The CI script checks whether the PR has touched any files that might affect the angular.io app
|
||||||
(currently the `aio/` or `packages/` directories, ignoring spec files).
|
(currently the `aio/` or `packages/` directories, ignoring spec files).
|
||||||
- The CI script gzips and stores the build artifacts in the CI infrastructure.
|
- The CI script gzips and stores the build artifacts in the CI infrastructure.
|
||||||
- When the build completes CircleCI triggers a webhook on the upload-server.
|
- When the build completes CircleCI triggers a webhook on the preview-server.
|
||||||
|
|
||||||
More info on how to set things up on CI can be found [here](misc--integrate-with-ci.md).
|
More info on how to set things up on CI can be found [here](misc--integrate-with-ci.md).
|
||||||
|
|
||||||
|
|
||||||
### Hosting build artifacts
|
### Hosting build artifacts
|
||||||
|
|
||||||
- nginx receives the webhook trigger and passes it through to the upload server.
|
- nginx receives the webhook trigger and passes it through to the preview server.
|
||||||
- The upload-server makes a request to CircleCI for the URL of the AIO build artifacts.
|
- The preview-server makes a request to CircleCI for the URL of the AIO build artifacts.
|
||||||
- The upload-server makes a request to this URL to receive the artifact - failing if the size
|
- The preview-server makes a request to this URL to receive the artifact - failing if the size
|
||||||
exceeds the specified max file size - and stores it in a temporary location.
|
exceeds the specified max file size - and stores it in a temporary location.
|
||||||
- The upload-server runs several checks to determine whether the request should be accepted and
|
- The preview-server runs several checks to determine whether the request should be accepted and
|
||||||
whether it should be publicly accessible or stored for later verification (more details can be
|
whether it should be publicly accessible or stored for later verification (more details can be
|
||||||
found [here](overview--security-model.md)).
|
found [here](overview--security-model.md)).
|
||||||
- The upload-server changes the "visibility" of the associated PR, if necessary. For example, if
|
- The preview-server changes the "visibility" of the associated PR, if necessary. For example, if
|
||||||
builds for the same PR had been previously deployed as non-public and the current build has been
|
builds for the same PR had been previously deployed as non-public and the current build has been
|
||||||
automatically verified, all previous builds are made public as well.
|
automatically verified, all previous builds are made public as well.
|
||||||
If the PR transitions from "non-public" to "public", the upload-server posts a comment on the
|
If the PR transitions from "non-public" to "public", the preview-server posts a comment on the
|
||||||
corresponding PR on GitHub mentioning the SHAs and the links where the previews can be found.
|
corresponding PR on GitHub mentioning the SHAs and the links where the previews can be found.
|
||||||
- The upload-server verifies that the uploaded file is not trying to overwrite an existing build.
|
- The preview-server verifies that it is not trying to overwrite an existing build.
|
||||||
- The upload-server deploys the artifacts to a sub-directory named after the PR number and the first
|
- The preview-server deploys the artifacts to a sub-directory named after the PR number and the first
|
||||||
few characters of the SHA: `<PR>/<SHA>/`
|
few characters of the SHA: `<PR>/<SHA>/`
|
||||||
(Non-publicly accessible PRs will be stored in a different location, but again derived from the PR
|
(Non-publicly accessible PRs will be stored in a different location, but again derived from the PR
|
||||||
number and SHA.)
|
number and SHA.)
|
||||||
- If the PR is publicly accessible, the upload-server posts a comment on the corresponding PR on
|
- If the PR is publicly accessible, the preview-server posts a comment on the corresponding PR on
|
||||||
GitHub mentioning the SHA and the link where the preview can be found.
|
GitHub mentioning the SHA and the link where the preview can be found.
|
||||||
|
|
||||||
More info on the possible HTTP status codes and their meaning can be found
|
More info on the possible HTTP status codes and their meaning can be found
|
||||||
@ -73,24 +73,24 @@ More info on the possible HTTP status codes and their meaning can be found
|
|||||||
|
|
||||||
### Updating PR visibility
|
### Updating PR visibility
|
||||||
- nginx receives a natification that a PR has been updated and passes it through to the
|
- nginx receives a natification that a PR has been updated and passes it through to the
|
||||||
upload-server. This could, for example, be sent by a GitHub webhook every time a PR's labels
|
preview-server. This could, for example, be sent by a GitHub webhook every time a PR's labels
|
||||||
change.
|
change.
|
||||||
E.g.: `ngbuilds.io/pr-updated` (payload: `{"number":<PR>,"action":"labeled"}`)
|
E.g.: `ngbuilds.io/pr-updated` (payload: `{"number":<PR>,"action":"labeled"}`)
|
||||||
- The request contains the PR number (as `number`) and optionally the action that triggered the
|
- The request contains the PR number (as `number`) and optionally the action that triggered the
|
||||||
request (as `action`) in the payload.
|
request (as `action`) in the payload.
|
||||||
- The upload-server verifies the payload and determines whether the `action` (if specified) could
|
- The preview-server verifies the payload and determines whether the `action` (if specified) could
|
||||||
have led to PR visibility changes. Only requests that omit the `action` field altogether or
|
have led to PR visibility changes. Only requests that omit the `action` field altogether or
|
||||||
specify an action that can affect visibility are further processed.
|
specify an action that can affect visibility are further processed.
|
||||||
(Currently, the only actions that are considered capable of affecting visibility are `labeled` and
|
(Currently, the only actions that are considered capable of affecting visibility are `labeled` and
|
||||||
`unlabeled`.)
|
`unlabeled`.)
|
||||||
- The upload-server re-checks and if necessary updates the PR's visibility.
|
- The preview-server re-checks and if necessary updates the PR's visibility.
|
||||||
|
|
||||||
More info on the possible HTTP status codes and their meaning can be found
|
More info on the possible HTTP status codes and their meaning can be found
|
||||||
[here](overview--http-status-codes.md).
|
[here](overview--http-status-codes.md).
|
||||||
|
|
||||||
|
|
||||||
### Serving build artifacts
|
### Serving build artifacts
|
||||||
- nginx receives a request for an uploaded resource on a subdomain corresponding to the PR and SHA.
|
- nginx receives a request for a hosted preview resource on a subdomain corresponding to the PR and SHA.
|
||||||
E.g.: `pr<PR>-<SHA>.ngbuilds.io/path/to/resource`
|
E.g.: `pr<PR>-<SHA>.ngbuilds.io/path/to/resource`
|
||||||
- nginx maps the subdomain to the correct sub-directory and serves the resource.
|
- nginx maps the subdomain to the correct sub-directory and serves the resource.
|
||||||
E.g.: `/<PR>/<SHA>/path/to/resource`
|
E.g.: `/<PR>/<SHA>/path/to/resource`
|
||||||
@ -108,4 +108,4 @@ that do not correspond with an open PR.
|
|||||||
### Health-check
|
### Health-check
|
||||||
The docker service runs a periodic health-check that verifies the running conditions of the
|
The docker service runs a periodic health-check that verifies the running conditions of the
|
||||||
container. This includes verifying the status of specific system services, the responsiveness of
|
container. This includes verifying the status of specific system services, the responsiveness of
|
||||||
nginx and the upload-server and internet connectivity.
|
nginx and the preview-server and internet connectivity.
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# Overview - HTTP Status Codes
|
# Overview - HTTP Status Codes
|
||||||
|
|
||||||
|
|
||||||
This is a list of all the possible HTTP status codes returned by the nginx anf upload servers, along
|
This is a list of all the possible HTTP status codes returned by the nginx and preview servers, along
|
||||||
with a bried explanation of what they mean:
|
with a brief explanation of what they mean:
|
||||||
|
|
||||||
|
|
||||||
## `http://*.ngbuilds.io/*`
|
## `http://*.ngbuilds.io/*`
|
||||||
|
@ -40,23 +40,23 @@ purposes. Each command is backed by a corresponding script inside
|
|||||||
Initializes the container (mainly by starting the necessary services).
|
Initializes the container (mainly by starting the necessary services).
|
||||||
_It is run (by default) when starting the container._
|
_It is run (by default) when starting the container._
|
||||||
|
|
||||||
- `aio-upload-server-prod`:
|
- `aio-preview-server-prod`:
|
||||||
Spins up a Node.js upload-server instance.
|
Spins up a Node.js preview-server instance.
|
||||||
_It is used in `aio-init` (see above) during initialization._
|
_It is used in `aio-init` (see above) during initialization._
|
||||||
|
|
||||||
|
|
||||||
## Developer Commands
|
## Developer Commands
|
||||||
|
|
||||||
- `aio-upload-server-test`:
|
- `aio-preview-server-test`:
|
||||||
Spins up a Node.js upload-server instance for tests.
|
Spins up a Node.js preview-server instance for tests.
|
||||||
_It is used in `aio-verify-setup` (see below) for running tests._
|
_It is used in `aio-verify-setup` (see below) for running tests._
|
||||||
|
|
||||||
- `aio-verify-setup`:
|
- `aio-verify-setup`:
|
||||||
Runs a suite of e2e-like tests, mainly verifying the correct (inter)operation of nginx and the
|
Runs a suite of e2e-like tests, mainly verifying the correct (inter)operation of nginx and the
|
||||||
Node.js upload-server.
|
Node.js preview-server.
|
||||||
|
|
||||||
- `aio-verify-setup-and-log`:
|
- `aio-verify-setup-and-log`:
|
||||||
Runs the `aio-verify-setup` command but also then dumps the logs from the upload server, which
|
Runs the `aio-verify-setup` command but also then dumps the logs from the preview server, which
|
||||||
gives additional useful debugging information. See the [debugging docs](misc--debug-docker-container.md)
|
gives additional useful debugging information. See the [debugging docs](misc--debug-docker-container.md)
|
||||||
for more info.
|
for more info.
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ The implemented approach can be broken up to the following sub-tasks:
|
|||||||
5. Deploy the artifacts to the corresponding PR's directory.
|
5. Deploy the artifacts to the corresponding PR's directory.
|
||||||
6. Prevent overwriting previously deployed artifacts (which ensures that the guarantees established
|
6. Prevent overwriting previously deployed artifacts (which ensures that the guarantees established
|
||||||
during deployment will remain valid until the artifacts are removed).
|
during deployment will remain valid until the artifacts are removed).
|
||||||
7. Prevent uploaded files from accessing anything outside their directory.
|
7. Prevent hosted preview files from accessing anything outside their directory.
|
||||||
|
|
||||||
|
|
||||||
### Implementation details
|
### Implementation details
|
||||||
@ -56,7 +56,7 @@ This section describes how each of the aforementioned sub-tasks is accomplished:
|
|||||||
|
|
||||||
0. **Receive notification from CircleCI of a completed build**
|
0. **Receive notification from CircleCI of a completed build**
|
||||||
|
|
||||||
CircleCI is configured to trigger a webhook on our upload-server whenever a build completes.
|
CircleCI is configured to trigger a webhook on our preview-server whenever a build completes.
|
||||||
The payload contains the number of the build that completed.
|
The payload contains the number of the build that completed.
|
||||||
|
|
||||||
1. **Verify that the build is valid and download the artifact.**
|
1. **Verify that the build is valid and download the artifact.**
|
||||||
@ -71,7 +71,7 @@ This section describes how each of the aforementioned sub-tasks is accomplished:
|
|||||||
Next we make another call to the CircleCI API to get a list of the URLS for artifacts of that
|
Next we make another call to the CircleCI API to get a list of the URLS for artifacts of that
|
||||||
build. If there is one that matches the configured artifact path, we download the contents of the
|
build. If there is one that matches the configured artifact path, we download the contents of the
|
||||||
build artifact and store it in a local folder. This download has a maximum size limit to prevent
|
build artifact and store it in a local folder. This download has a maximum size limit to prevent
|
||||||
PRs from producing artifacts that are so large they would cause the upload server to crash.
|
PRs from producing artifacts that are so large they would cause the preview server to crash.
|
||||||
|
|
||||||
2. **Fetch the PR's metadata, including author and labels**.
|
2. **Fetch the PR's metadata, including author and labels**.
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ This section describes how each of the aforementioned sub-tasks is accomplished:
|
|||||||
Once we have determined whether the PR is considered "trusted", we update its "visibility" (i.e.
|
Once we have determined whether the PR is considered "trusted", we update its "visibility" (i.e.
|
||||||
whether it is publicly accessible or not), based on the new verification status. For example, if
|
whether it is publicly accessible or not), based on the new verification status. For example, if
|
||||||
a PR was initially considered "not trusted" but the check triggered by a new build determined
|
a PR was initially considered "not trusted" but the check triggered by a new build determined
|
||||||
otherwise, the PR (and all the previously uploaded previews) are made public. It works the same
|
otherwise, the PR (and all the previously hosted previews) are made public. It works the same
|
||||||
way if a PR has gone from "trusted" to "not trusted".
|
way if a PR has gone from "trusted" to "not trusted".
|
||||||
|
|
||||||
5. **Deploy the artifacts to the corresponding PR's directory.**
|
5. **Deploy the artifacts to the corresponding PR's directory.**
|
||||||
@ -117,23 +117,23 @@ This section describes how each of the aforementioned sub-tasks is accomplished:
|
|||||||
Express server) rejects builds that have already been handled.
|
Express server) rejects builds that have already been handled.
|
||||||
_Note: A PR can contain multiple builds; one for each SHA that was built on CircleCI._
|
_Note: A PR can contain multiple builds; one for each SHA that was built on CircleCI._
|
||||||
|
|
||||||
7. **Prevent uploaded files from accessing anything outside their directory.**
|
7. **Prevent hosted preview files from accessing anything outside their directory.**
|
||||||
|
|
||||||
Nginx (which is used to serve the uploaded artifacts) has been configured to not follow symlinks
|
Nginx (which is used to serve the hosted preview) has been configured to not follow symlinks
|
||||||
outside of the directory where the build artifacts are stored.
|
outside of the directory where the preview files are stored.
|
||||||
|
|
||||||
|
|
||||||
## Assumptions / Things to keep in mind
|
## Assumptions / Things to keep in mind
|
||||||
|
|
||||||
- Other than the initial webhook trigger, which provides a build number, all requests for data come
|
- Other than the initial webhook trigger, which provides a build number, all requests for data come
|
||||||
from the upload-server making requests to well defined API endpoints (e.g. CircleCI and Github).
|
from the preview-server making requests to well defined API endpoints (e.g. CircleCI and Github).
|
||||||
This means that any secret access keys need only be stored on the upload-server and not on any of
|
This means that any secret access keys need only be stored on the preview-server and not on any of
|
||||||
the CI build infrastructure (e.g. CircleCI).
|
the CI build infrastructure (e.g. CircleCI).
|
||||||
|
|
||||||
- Each trusted PR author has full control over the content that is uploaded for their PRs. Part of
|
- Each trusted PR author has full control over the content that is hosted as a preview for their PRs.
|
||||||
the security model relies on the trustworthiness of these authors.
|
Part of the security model relies on the trustworthiness of these authors.
|
||||||
|
|
||||||
- Adding the specified label on a PR and marking it as trusted, gives the author full control over
|
- Adding the specified label on a PR to mark it as trusted, gives the author full control over
|
||||||
the content that is uploaded for the specific PR (e.g. by pushing more commits to it). The user
|
the content that is hosted for the specific PR preview (e.g. by pushing more commits to it).
|
||||||
adding the label is responsible for ensuring that this control is not abused and that the PR is
|
The user adding the label is responsible for ensuring that this control is not abused and that
|
||||||
either closed (one way of another) or the access is revoked.
|
the PR is either closed (one way of another) or the access is revoked.
|
||||||
|
@ -12,8 +12,8 @@ More info on how to create `secrets` directory and files can be found
|
|||||||
|
|
||||||
|
|
||||||
## Create directory for build artifacts
|
## Create directory for build artifacts
|
||||||
The uploaded build artifacts should be kept on a directory outside the docker container, so it is
|
The build artifacts should be kept on a directory outside the docker container, so it is
|
||||||
easier to replace the container without losing the uploaded builds. For portability across VMs a
|
easier to replace the container without losing the builds. For portability across VMs a
|
||||||
persistent disk can be used (as described [here](vm-setup--attach-persistent-disk.md)).
|
persistent disk can be used (as described [here](vm-setup--attach-persistent-disk.md)).
|
||||||
|
|
||||||
**Note:** The directories created inside that directory will be owned by user `www-data`.
|
**Note:** The directories created inside that directory will be owned by user `www-data`.
|
||||||
@ -21,7 +21,7 @@ persistent disk can be used (as described [here](vm-setup--attach-persistent-dis
|
|||||||
|
|
||||||
## Create SSL certificates (Optional for dev)
|
## Create SSL certificates (Optional for dev)
|
||||||
The host VM can attach a directory containing the SSL certificate and key to be used by the nginx
|
The host VM can attach a directory containing the SSL certificate and key to be used by the nginx
|
||||||
server for serving the uploaded build artifacts. More info on how to attach the directory when
|
server for serving the hosted previews. More info on how to attach the directory when
|
||||||
starting the container can be found [here](vm-setup--start-docker-container.md).
|
starting the container can be found [here](vm-setup--start-docker-container.md).
|
||||||
|
|
||||||
In order for the container to be able to find the certificate and key, they should be named
|
In order for the container to be able to find the certificate and key, they should be named
|
||||||
@ -61,15 +61,15 @@ The following log files are kept in this directory:
|
|||||||
used when running tests locally from inside the container, e.g. with the `aio-verify-setup`
|
used when running tests locally from inside the container, e.g. with the `aio-verify-setup`
|
||||||
command. (See [here](overview--scripts-and-commands.md) for more info.)
|
command. (See [here](overview--scripts-and-commands.md) for more info.)
|
||||||
|
|
||||||
- `upload-server-{prod,test,verify-setup}-*.log`:
|
- `preview-server-{prod,test,verify-setup}-*.log`:
|
||||||
The logs produced by the Node.js upload-server while serving either:
|
The logs produced by the Node.js preview-server while serving either:
|
||||||
- `-prod`: "Production" files (g.g during normal operation).
|
- `-prod`: "Production" files (g.g during normal operation).
|
||||||
- `-test`: "Test" files (e.g. when a test instance is started with the `aio-upload-server-test`
|
- `-test`: "Test" files (e.g. when a test instance is started with the `aio-preview-server-test`
|
||||||
command).
|
command).
|
||||||
- `-verify-setup`: "Test" files, but while running `aio-verify-setup`.
|
- `-verify-setup`: "Test" files, but while running `aio-verify-setup`.
|
||||||
|
|
||||||
(See [here](overview--scripts-and-commands.md) for more info the commands mentioned above.)
|
(See [here](overview--scripts-and-commands.md) for more info the commands mentioned above.)
|
||||||
|
|
||||||
- `verify-setup.log`:
|
- `verify-setup.log`:
|
||||||
The output of the `aio-verify-setup` command (e.g. Jasmine output), except for upload-server
|
The output of the `aio-verify-setup` command (e.g. Jasmine output), except for preview-server
|
||||||
output which is logged to `upload-server-verify-setup-*.log` (see above).
|
output which is logged to `preview-server-verify-setup-*.log` (see above).
|
||||||
|
@ -31,7 +31,7 @@ sudo docker run \
|
|||||||
--detach \
|
--detach \
|
||||||
|
|
||||||
# Use the local DNS server.
|
# Use the local DNS server.
|
||||||
# (This is necessary for mapping internal URLs, e.g. for the Node.js upload-server.)
|
# (This is necessary for mapping internal URLs, e.g. for the Node.js preview-server.)
|
||||||
--dns 127.0.0.1 \
|
--dns 127.0.0.1 \
|
||||||
|
|
||||||
# USe `<instance-name>` as an alias for the container.
|
# USe `<instance-name>` as an alias for the container.
|
||||||
@ -50,7 +50,7 @@ sudo docker run \
|
|||||||
# (See [here](vm-setup--set-up-secrets.md) for more info.)
|
# (See [here](vm-setup--set-up-secrets.md) for more info.)
|
||||||
--volume <host-secrets-dir>:/aio-secrets:ro \
|
--volume <host-secrets-dir>:/aio-secrets:ro \
|
||||||
|
|
||||||
# The uploaded build artifacts will stored to and served from this directory.
|
# The build artifacts and hosted previews will stored to and served from this directory.
|
||||||
# (If you are using a persistent disk - as described [here](vm-setup--attach-persistent-disk.md) -
|
# (If you are using a persistent disk - as described [here](vm-setup--attach-persistent-disk.md) -
|
||||||
# this will be a directory inside the disk.)
|
# this will be a directory inside the disk.)
|
||||||
--volume <host-builds-dir>:/var/www/aio-builds \
|
--volume <host-builds-dir>:/var/www/aio-builds \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user