extra index template for production
This commit is contained in:
parent
63ca4c094e
commit
f7d65deb5e
|
@ -32,4 +32,36 @@ jobs:
|
|||
- run: yarn test
|
||||
env:
|
||||
CI: true
|
||||
name: Budibase CI
|
||||
name: Budibase CI
|
||||
|
||||
- name: Login to Amazon ECR
|
||||
id: login-ecr
|
||||
uses: aws-actions/amazon-ecr-login@v1
|
||||
|
||||
- name: Build, tag, and push image to Amazon ECR
|
||||
id: build-image
|
||||
env:
|
||||
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
|
||||
ECR_REPOSITORY: my-ecr-repo
|
||||
IMAGE_TAG: ${{ github.sha }}
|
||||
run: |
|
||||
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
|
||||
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
|
||||
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
|
||||
|
||||
- name: Fill in the new image ID in the Amazon ECS task definition
|
||||
id: task-def
|
||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||
with:
|
||||
task-definition: task-definition.json
|
||||
container-name: my-container
|
||||
image: ${{ steps.build-image.outputs.image }}
|
||||
|
||||
- name: Deploy Amazon ECS task definition
|
||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
||||
with:
|
||||
task-definition: ${{ steps.task-def.outputs.task-definition }}
|
||||
service: my-service
|
||||
cluster: my-cluster
|
||||
wait-for-service-stability: true
|
||||
|
|
@ -23,8 +23,7 @@ export const fetchComponentLibModules = async application => {
|
|||
const allLibraries = {}
|
||||
|
||||
for (let libraryName of application.componentLibraries) {
|
||||
// const LIBRARY_URL = `/${application._id}/componentlibrary?library=${libraryName}`
|
||||
const LIBRARY_URL = `/assets/componentlibrary/${libraryName}/dist/index.js`
|
||||
const LIBRARY_URL = `/${application._id}/componentlibrary?library=${libraryName}`
|
||||
const libraryModule = await import(LIBRARY_URL)
|
||||
allLibraries[libraryName] = libraryModule
|
||||
}
|
||||
|
|
|
@ -3,10 +3,15 @@
|
|||
import { store } from "builderStore"
|
||||
import { notifier } from "builderStore/store/notifications"
|
||||
import api from "builderStore/api"
|
||||
import Spinner from "components/common/Spinner.svelte"
|
||||
|
||||
let deployed = false
|
||||
let loading = false
|
||||
|
||||
$: appId = $store.appId
|
||||
|
||||
async function deployApp() {
|
||||
loading = true
|
||||
const DEPLOY_URL = `/deploy`
|
||||
|
||||
try {
|
||||
|
@ -14,12 +19,15 @@
|
|||
const response = await api.post(DEPLOY_URL)
|
||||
const json = await response.json()
|
||||
if (response.status !== 200) {
|
||||
throw new Error
|
||||
throw new Error()
|
||||
}
|
||||
|
||||
notifier.success(`Deployment Complete. View your app at blah URL https://${appId}.app.budi.live/${appId}`)
|
||||
notifier.success(`Your Deployment is Complete.`)
|
||||
deployed = true
|
||||
loading = false
|
||||
} catch (err) {
|
||||
notifier.danger("Deployment unsuccessful. Please try again later.")
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -27,9 +35,18 @@
|
|||
<section>
|
||||
<div>
|
||||
<h4>It's time to shine!</h4>
|
||||
<Button secondary medium on:click={deployApp}>
|
||||
Deploy App
|
||||
</Button>
|
||||
{#if deployed}
|
||||
<a target="_blank" href={`https://${appId}.app.budi.live/${appId}`}>
|
||||
View App
|
||||
</a>
|
||||
{:else}
|
||||
<Button secondary medium on:click={deployApp}>
|
||||
Deploy App
|
||||
{#if loading}
|
||||
<Spinner ratio={"0.5"} />
|
||||
{/if}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
<img src="/_builder/assets/deploy-rocket.jpg" />
|
||||
</section>
|
||||
|
@ -53,11 +70,11 @@
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 20%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 50%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -8,7 +8,7 @@ module.exports = ({ dir }) => {
|
|||
|
||||
// dont make this a variable or top level require
|
||||
// ti will cause environment module to be loaded prematurely
|
||||
require("@budibase/server/src/app")().then(server => {
|
||||
return require("@budibase/server/src/app")().then(server => {
|
||||
server.on("close", () => console.log("Server Closed"))
|
||||
console.log(`Budibase running on ${JSON.stringify(server.address())}`)
|
||||
})
|
||||
|
|
|
@ -20,10 +20,10 @@ export const loadBudibase = async opts => {
|
|||
|
||||
for (let library of libraries) {
|
||||
// fetch the JavaScript for the component libraries from the server
|
||||
// componentLibraryModules[library] = await import(
|
||||
// `/componentlibrary?library=${encodeURI(library)}`
|
||||
// )
|
||||
componentLibraryModules[library] = await import(`/assets/componentlibrary/${library}/dist/index.js`)
|
||||
componentLibraryModules[library] = await import(
|
||||
`/componentlibrary?library=${encodeURI(library)}`
|
||||
)
|
||||
// componentLibraryModules[library] = await import(`/assets/componentlibrary/${library}/dist/index.js`)
|
||||
}
|
||||
|
||||
componentLibraryModules[builtinLibName] = builtins(_window)
|
||||
|
|
|
@ -12,4 +12,8 @@ JWT_SECRET={{cookieKey1}}
|
|||
PORT=4001
|
||||
|
||||
# error level for koa-pino
|
||||
LOG_LEVEL=error
|
||||
LOG_LEVEL=error
|
||||
|
||||
DEPLOYMENT_CF_DISTRIBUTION_ID=
|
||||
DEPLOYMENT_APP_ASSETS_BUCKET=g
|
||||
DEPLOYMENT_CREDENTIALS_URL="https://dt4mpwwap8.execute-api.eu-west-1.amazonaws.com/prod/"
|
|
@ -2,6 +2,8 @@ FROM node:12-alpine
|
|||
|
||||
WORKDIR /app
|
||||
|
||||
ENV CLOUD=1
|
||||
|
||||
# copy files and install dependencies
|
||||
COPY . ./
|
||||
RUN yarn
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# url of couch db, including username and password
|
||||
# http://admin:password@localhost:5984
|
||||
COUCH_DB_URL=
|
||||
# identifies a client database - i.e. group of apps
|
||||
CLIENT_ID=1
|
||||
# used to create cookie hashes
|
||||
JWT_SECRET=4715888e-144f-4802-b8d8-862a1b8365dd
|
||||
# port to run http server on
|
||||
PORT=4001
|
||||
|
||||
# error level for koa-pino
|
||||
LOG_LEVEL=error
|
||||
|
||||
COUCH_DB_REMOTE=https://admin:afasdgafgF342G@couchdb.budi.live:5984
|
||||
BUDIBASE_APP_ASSETS_BUCKET=prod-budi-app-assets
|
||||
BUDIBASE_API_KEY=d498278c-4ab4-144b-c212-b8f9e6da5c2b
|
||||
|
|
@ -5,15 +5,28 @@ const {
|
|||
budibaseAppsDir,
|
||||
} = require("../../../utilities/budibaseDir")
|
||||
|
||||
async function invalidateCDN(appId) {
|
||||
const cf = new AWS.CloudFront({})
|
||||
|
||||
return cf.createInvalidation({
|
||||
DistributionId: process.env.DEPLOYMENT_CF_DISTRIBUTION_ID,
|
||||
InvalidationBatch: {
|
||||
CallerReference: appId,
|
||||
Paths: {
|
||||
Quantity: 1,
|
||||
Items: [
|
||||
`/assets/${appId}/*`
|
||||
]
|
||||
}
|
||||
}
|
||||
}).promise()
|
||||
}
|
||||
|
||||
async function fetchTemporaryCredentials() {
|
||||
const CREDENTIALS_URL = "https://dt4mpwwap8.execute-api.eu-west-1.amazonaws.com/prod/"
|
||||
|
||||
const BUDIBASE_API_KEY = process.env.BUDIBASE_API_KEY
|
||||
|
||||
const response = await fetch(CREDENTIALS_URL, {
|
||||
const response = await fetch(process.env.DEPLOYMENT_CREDENTIALS_URL, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
apiKey: BUDIBASE_API_KEY
|
||||
apiKey: process.env.BUDIBASE_API_KEY
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -32,6 +45,27 @@ const CONTENT_TYPE_MAP = {
|
|||
js: "application/javascript"
|
||||
};
|
||||
|
||||
/**
|
||||
* Recursively walk a directory tree and execute a callback on all files.
|
||||
* @param {Re} dirPath - Directory to traverse
|
||||
* @param {*} callback - callback to execute on files
|
||||
*/
|
||||
function walkDir(dirPath, callback) {
|
||||
for (let filename of fs.readdirSync(dirPath)) {
|
||||
const filePath = `${dirPath}/${filename}`
|
||||
const stat = fs.lstatSync(filePath)
|
||||
|
||||
if (stat.isFile()) {
|
||||
callback({
|
||||
bytes: fs.readFileSync(filePath),
|
||||
filename
|
||||
})
|
||||
} else {
|
||||
walkDir(filePath, callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.uploadAppAssets = async function ({ appId }) {
|
||||
const { credentials, accountId } = await fetchTemporaryCredentials()
|
||||
|
||||
|
@ -43,7 +77,7 @@ exports.uploadAppAssets = async function ({ appId }) {
|
|||
|
||||
const s3 = new AWS.S3({
|
||||
params: {
|
||||
Bucket: process.env.BUDIBASE_APP_ASSETS_BUCKET
|
||||
Bucket: process.env.DEPLOYMENT_APP_ASSETS_BUCKET
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -54,34 +88,27 @@ exports.uploadAppAssets = async function ({ appId }) {
|
|||
const uploads = []
|
||||
|
||||
for (let page of appPages) {
|
||||
for (let filename of fs.readdirSync(`${appAssetsPath}/${page}`)) {
|
||||
const filePath = `${appAssetsPath}/${page}/${filename}`
|
||||
const stat = await fs.lstatSync(filePath)
|
||||
|
||||
// TODO: need to account for recursively traversing dirs
|
||||
if (stat.isFile()) {
|
||||
const fileBytes = fs.readFileSync(`${appAssetsPath}/${page}/${filename}`)
|
||||
walkDir(`${appAssetsPath}/${page}`, function prepareUploadsForS3({ bytes, filename }) {
|
||||
const fileExtension = [...filename.split(".")].pop()
|
||||
|
||||
console.log(`${appId}/${page}/${filename}`)
|
||||
const upload = s3.upload({
|
||||
Key: `assets/${appId}/${page}/${filename}`,
|
||||
Body: bytes,
|
||||
ContentType: CONTENT_TYPE_MAP[fileExtension],
|
||||
Metadata: {
|
||||
accountId
|
||||
}
|
||||
}).promise()
|
||||
|
||||
const fileExtension = [...filename.split(".")].pop()
|
||||
|
||||
const upload = s3.upload({
|
||||
Key: `assets/${appId}/${page}/${filename}`,
|
||||
Body: fileBytes,
|
||||
ContentType: CONTENT_TYPE_MAP[fileExtension],
|
||||
Metadata: {
|
||||
accountId
|
||||
}
|
||||
}).promise()
|
||||
|
||||
uploads.push(upload)
|
||||
}
|
||||
}
|
||||
uploads.push(upload)
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
return Promise.all(uploads)
|
||||
const uploadAllFiles = Promise.all(uploads)
|
||||
const invalidateCloudfront = invalidateCDN(appId)
|
||||
await uploadAllFiles
|
||||
await invalidateCloudfront
|
||||
} catch (err) {
|
||||
console.error("Error uploading budibase app assets to s3", err)
|
||||
throw err
|
||||
|
|
|
@ -44,19 +44,15 @@ exports.serveApp = async function(ctx) {
|
|||
})
|
||||
}
|
||||
|
||||
const { file = "index.html" } = ctx
|
||||
|
||||
|
||||
if (ctx.isCloud) {
|
||||
const S3_URL = `https://${ctx.params.appId}.app.budi.live/assets/${ctx.params.appId}/${mainOrAuth}/${file}`
|
||||
console.log("Serving: " + S3_URL)
|
||||
if (process.env.CLOUD) {
|
||||
const S3_URL = `https://${ctx.params.appId}.app.budi.live/assets/${ctx.params.appId}/${mainOrAuth}/${ctx.file || "index.production.html"}`
|
||||
const response = await fetch(S3_URL)
|
||||
const body = await response.text()
|
||||
ctx.body = body
|
||||
return
|
||||
}
|
||||
|
||||
await send(ctx, file, { root: ctx.devPath || appPath })
|
||||
await send(ctx, ctx.file || "index.html", { root: ctx.devPath || appPath })
|
||||
}
|
||||
|
||||
exports.serveAppAsset = async function(ctx) {
|
||||
|
@ -70,15 +66,7 @@ exports.serveAppAsset = async function(ctx) {
|
|||
mainOrAuth
|
||||
)
|
||||
|
||||
// if (ctx.isCloud) {
|
||||
// const requestUrl = `${S3_URL_PREFIX}/${appId}/public/${mainOrAuth}/${ctx.file || "index.html"}`
|
||||
// console.log('request url:' , requestUrl)
|
||||
// const response = await fetch(requestUrl)
|
||||
// const body = await response.text()
|
||||
// ctx.body = body
|
||||
// } else {
|
||||
await send(ctx, ctx.file, { root: ctx.devPath || appPath })
|
||||
// }
|
||||
await send(ctx, ctx.file, { root: ctx.devPath || appPath })
|
||||
}
|
||||
|
||||
exports.serveComponentLibrary = async function(ctx) {
|
||||
|
@ -99,16 +87,15 @@ exports.serveComponentLibrary = async function(ctx) {
|
|||
)
|
||||
}
|
||||
|
||||
// if (ctx.isCloud) {
|
||||
// const appId = ctx.user.appId
|
||||
// const requestUrl = encodeURI(`${S3_URL_PREFIX}/${appId}/node_modules/${ctx.query.library}/dist/index.js`)
|
||||
// console.log('request url components: ', requestUrl)
|
||||
// const response = await fetch(requestUrl)
|
||||
// const body = await response.text()
|
||||
// ctx.type = 'application/javascript'
|
||||
// ctx.body = body;
|
||||
// return
|
||||
// }
|
||||
if (process.env.CLOUD) {
|
||||
const appId = ctx.user.appId
|
||||
const S3_URL = encodeURI(`https://${appId}.app.budi.live/assets/componentlibrary/${ctx.query.library}/dist/index.js`)
|
||||
const response = await fetch(S3_URL)
|
||||
const body = await response.text()
|
||||
ctx.type = 'application/javascript'
|
||||
ctx.body = body;
|
||||
return
|
||||
}
|
||||
|
||||
await send(ctx, "/index.js", { root: componentLibraryPath })
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ router
|
|||
useAppRootPath: true,
|
||||
}
|
||||
ctx.isDev = env.NODE_ENV !== "production" && env.NODE_ENV !== "jest"
|
||||
// ctx.isCloud = true
|
||||
await next()
|
||||
})
|
||||
.use(authenticated)
|
||||
|
|
|
@ -57,8 +57,6 @@ const buildIndexHtml = async (config, appId, pageName, appPath, pkg) => {
|
|||
pageStyle: pkg.page._css,
|
||||
appId,
|
||||
pageName,
|
||||
// TODO: don't hardcode
|
||||
production: true
|
||||
}
|
||||
|
||||
const indexHtmlTemplate = await readFile(
|
||||
|
@ -67,10 +65,16 @@ const buildIndexHtml = async (config, appId, pageName, appPath, pkg) => {
|
|||
)
|
||||
|
||||
const indexHtmlPath = join(appPublicPath, "index.html")
|
||||
const deployableHtmlPath = join(appPublicPath, "index.production.html")
|
||||
|
||||
const indexHtml = sqrl.Render(indexHtmlTemplate, templateObj)
|
||||
const deployableHtml = sqrl.Render(indexHtmlTemplate, {
|
||||
...templateObj,
|
||||
production: true
|
||||
})
|
||||
|
||||
await writeFile(indexHtmlPath, indexHtml, { flag: "w+" })
|
||||
await writeFile(deployableHtmlPath, deployableHtml, { flag: "w+" })
|
||||
}
|
||||
|
||||
const buildFrontendAppDefinition = async (config, appId, pageName, pkg) => {
|
||||
|
|
|
@ -27,29 +27,34 @@
|
|||
{{ /each }}
|
||||
|
||||
{{ each(options.screenStyles) }}
|
||||
{{ if(options.production) }}
|
||||
<link rel='stylesheet' href='/assets/{{ appId }}/{{ pageName }}{{ @this }}'>
|
||||
{{#else}}
|
||||
<link rel='stylesheet' href='/assets{{ @this }}'>
|
||||
{{ /if }}
|
||||
{{ /each }}
|
||||
|
||||
{{ if(options.pageStyle) }}
|
||||
{{ if(options.production) }}
|
||||
<link rel='stylesheet' href='/assets/{{ appId }}/{{ pageName }}{{ pageStyle }}'>
|
||||
{{#else}}
|
||||
<link rel='stylesheet' href='/assets{{ pageStyle }}'>
|
||||
{{ /if }}
|
||||
{{ /if }}
|
||||
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Mono">
|
||||
|
||||
{{ if(options.production) }}
|
||||
<script src='/assets/{{ options.appId }}/{{ pageName }}/clientFrontendDefinition.js'></script>
|
||||
<script src='/assets/{{ options.appId }}/{{ pageName }}/budibase-client.js'></script>
|
||||
{{ else }}
|
||||
<script src='/assets/clientFrontendDefinition.js'></script>
|
||||
<script src='/assets/budibase-client.js'></script>
|
||||
{{ /if }}
|
||||
<script src='/assets/{{ appId }}/{{ pageName }}/clientFrontendDefinition.js'></script>
|
||||
<script src='/assets/{{ appId }}/{{ pageName }}/budibase-client.js'></script>
|
||||
{{#else}}
|
||||
<script src='/assets/clientFrontendDefinition.js'></script>
|
||||
<script src='/assets/budibase-client.js'></script>
|
||||
{{ /if }}
|
||||
</head>
|
||||
|
||||
<body id="app">
|
||||
|
||||
<script>
|
||||
loadBudibase();
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue