Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deploy to staging. #149

Merged
merged 9 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
# Run CloudFoundry/Cloud.gov deployment
- name: Set up Cloud Foundry CLI
run: |
curl -v -L -o cf-cli_amd64.deb 'https://cli.run.pivotal.io/stable?release=debian64&version=v7&source=github'
curl -v -L -o cf-cli_amd64.deb 'https://packages.cloudfoundry.org/stable?release=debian64&version=v7&source=github'
sudo dpkg -i cf-cli_amd64.deb
cf -v
cf api https://api.fr.cloud.gov
Expand Down
41 changes: 41 additions & 0 deletions .github/workflows/production.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Production Build

# Controls when the action will run.
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Set shared environment variables
env:
APP_VERSION: 2.4.0

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
environment: production

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v4

# Set up node and npm
- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install dependencies
run: npm install --omit=dev
working-directory: app

- name: Remove unnecessary server app files
run: rm -rf prettier.config.js manifest-dev.yml manifest-staging.yml app/tests
working-directory: app

- name: Create production artifact
uses: actions/upload-artifact@v4
with:
name: lew_v${{ env.APP_VERSION }}
path: app
2 changes: 1 addition & 1 deletion .github/workflows/staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:
# Run CloudFoundry/Cloud.gov deployment
- name: Set up Cloud Foundry CLI
run: |
curl -v -L -o cf-cli_amd64.deb 'https://cli.run.pivotal.io/stable?release=debian64&version=v7&source=github'
curl -v -L -o cf-cli_amd64.deb 'https://packages.cloudfoundry.org/stable?release=debian64&version=v7&source=github'
sudo dpkg -i cf-cli_amd64.deb
cf -v
cf api https://api.fr.cloud.gov
Expand Down
12 changes: 11 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,14 @@ npm-debug.log
.DS_Store

# Visual Studio 2015/2017 cache/options directory
.vs/
.vs/

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output
5 changes: 0 additions & 5 deletions .husky/pre-commit

This file was deleted.

2 changes: 2 additions & 0 deletions app/.husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cd app
lint-staged
13 changes: 13 additions & 0 deletions app/.nycrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"all": true,
"include": [
"app/**/*.{ts,js}"
],
"exclude": [
"**/node_modules/**",
"**/coverage/**",
"**/epa-template-files/**",
"**/tests/**",
"**/api-docs/**"
]
}
111 changes: 111 additions & 0 deletions app/app/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
const path = require('path');
const express = require('express');
const helmet = require('helmet');
const noCache = require('nocache');
const cors = require('cors');
const favicon = require('serve-favicon');
const basicAuth = require('express-basic-auth');
const { getEnvironment } = require('./server/utilities/environment');
const logger = require('./server/utilities/logger');
const log = logger.logger;

const app = express();

app.use(
helmet({
contentSecurityPolicy: false,
crossOriginEmbedderPolicy: false,
}),
);
app.use(noCache());
app.use(
helmet.hsts({
maxAge: 31536000,
}),
);

app.use(cors());

/****************************************************************
Which environment
****************************************************************/
const { isLocal, isTest, isDevelopment, isStaging } = getEnvironment();

if (isLocal) {
log.info('Environment = local');
app.enable('isLocal');
} else if (isTest) {
log.info('Environment = test');
app.enable('isTest');
}

if (isDevelopment) log.info('Environment = development');
if (isStaging) log.info('Environment = staging');
if (!isLocal && !isTest && !isDevelopment && !isStaging)
log.info('Environment = preprod or production');

/****************************************************************
Setup basic auth for non-staging and non-production
****************************************************************/
if (isDevelopment || isStaging) {
if (
process.env.LEW_BASIC_AUTH_USER_NAME == null ||
process.env.LEW_BASIC_AUTH_USER_PWD == null
) {
log.error(
'Either the basic LEW user name or password environmental variable is not set.',
);
}

const user_json =
'{"' +
process.env.LEW_BASIC_AUTH_USER_NAME +
'" : "' +
process.env.LEW_BASIC_AUTH_USER_PWD +
'"}';
const user_obj = JSON.parse(user_json);

app.use(
basicAuth({
users: user_obj,
challenge: true,
unauthorizedResponse: getUnauthorizedResponse,
}),
);
}

function getUnauthorizedResponse(req) {
return req.auth ? 'Invalid credentials' : 'No credentials provided';
}

/****************************************************************
Setup server and routes
****************************************************************/
app.use(
'/',
express.static(path.join(__dirname, 'public'), { index: ['index.html'] }),
);
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

/****************************************************************
Enable CORS/Preflight/OPTIONS request
****************************************************************/
app.options('*', cors());

/****************************************************************
Custom application routes
****************************************************************/
require('./server/routes/index')(app);

/****************************************************************
Worst case error handling for 404 and 500 issues
****************************************************************/
app.use(function (req, res, next) {
res.status(404).sendFile(path.join(__dirname, 'public', '404.html'));
});

app.use(function (err, req, res, next) {
res.status(500).sendFile(path.join(__dirname, 'public', '500.html'));
});

module.exports = app;
128 changes: 16 additions & 112 deletions app/app/server.js
Original file line number Diff line number Diff line change
@@ -1,129 +1,33 @@
const express = require('express');
const helmet = require('helmet');
const noCache = require('nocache');
const cors = require('cors');
const favicon = require('serve-favicon');
const basicAuth = require('express-basic-auth');
const path = require('path');
const logger = require('./server/utilities/logger.js');

const app = express();
app.use(
helmet({
contentSecurityPolicy: false,
crossOriginEmbedderPolicy: false,
}),
);
app.use(noCache());
app.use(
helmet.hsts({
maxAge: 31536000,
}),
);
app.use(cors());
const logger = require('./server/utilities/logger');
const log = logger.logger;
const browserSyncPort = 9091;
let port = process.env.PORT || 9090;
const browserSync_port = 9091;

/****************************************************************
Which environment
****************************************************************/
let isLocal = false;
let isDevelopment = false;
let isStaging = false;

if (process.env.NODE_ENV) {
isLocal = 'local' === process.env.NODE_ENV.toLowerCase();
isDevelopment = 'development' === process.env.NODE_ENV.toLowerCase();
isStaging = 'staging' === process.env.NODE_ENV.toLowerCase();
}

if (isLocal) log.info('Environment = local');
if (isDevelopment) log.info('Environment = development');
if (isStaging) log.info('Environment = staging');
if (!isLocal && !isDevelopment && !isStaging)
log.info('Environment = staging or production');

/****************************************************************
Setup basic auth for non-staging and non-production
****************************************************************/
if (isDevelopment || isStaging) {
if (
process.env.LEW_BASIC_AUTH_USER_NAME == null ||
process.env.LEW_BASIC_AUTH_USER_PWD == null
) {
log.error(
'Either the basic LEW user name or password environmental variable is not set.',
);
}

const user_json =
'{"' +
process.env.LEW_BASIC_AUTH_USER_NAME +
'" : "' +
process.env.LEW_BASIC_AUTH_USER_PWD +
'"}';
const user_obj = JSON.parse(user_json);

app.use(
basicAuth({
users: user_obj,
challenge: true,
unauthorizedResponse: getUnauthorizedResponse,
}),
);
}

function getUnauthorizedResponse(req) {
return req.auth ? 'Invalid credentials' : 'No credentials provided';
const { isLocal } = require('./server/utilities/environment');

let app;
try {
app = require('./app');
} catch (err) {
log.error('Error starting server: ' + err.message);
process.exit();
}

/****************************************************************
Setup server and routes
****************************************************************/
app.use(
'/',
express.static(path.join(__dirname, 'public'), { index: ['index.html'] }),
);
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

/****************************************************************
Enable CORS/Preflight/OPTIONS request
****************************************************************/
app.options('*', cors());

/****************************************************************
Custom application routes
****************************************************************/
require('./server/routes/index')(app);

/****************************************************************
Worst case error handling for 404 and 500 issues
****************************************************************/
app.use(function (req, res, next) {
res.status(404).sendFile(path.join(__dirname, 'public', '400.html'));
});

app.use(function (err, req, res, next) {
res.status(500).sendFile(path.join(__dirname, 'public', '500.html'));
});

//For local testing of the production flow, use the same port as browersync to avoid
//different port usage to confuse testers/developers
if (port === 9090 && isLocal === false) {
port = browserSync_port;
}
// for local testing of the production flow, use the same port as browersync to avoid
// different port usage to confuse testers/developers
if (port === 9090 && !isLocal) port = browserSyncPort;

app.listen(port, function () {
if (isLocal) {
const browserSync = require('browser-sync');

log.info(`Application listening on port ${browserSync_port}`);
log.info(`Application listening on port ${browserSyncPort}`);

browserSync({
files: [path.join(__dirname, '/public/**')],
online: false,
open: false,
port: browserSync_port,
port: browserSyncPort,
proxy: 'localhost:' + port,
ui: false,
});
Expand Down
24 changes: 24 additions & 0 deletions app/app/server/utilities/environment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// determine which environment we are in
exports.getEnvironment = function () {
let isLocal = false;
let isTest = false;
let isDevelopment = false;
let isStaging = false;
let isProduction = false;

if (process.env.NODE_ENV) {
isLocal = 'local' === process.env.NODE_ENV.toLowerCase();
isTest = 'test' === process.env.NODE_ENV.toLowerCase();
isDevelopment = 'development' === process.env.NODE_ENV.toLowerCase();
isStaging = 'staging' === process.env.NODE_ENV.toLowerCase();
isProduction = 'production' === process.env.NODE_ENV.toLowerCase();
}

return {
isLocal,
isTest,
isDevelopment,
isStaging,
isProduction,
};
};
Loading
Loading