Skip to content
This repository has been archived by the owner on May 3, 2024. It is now read-only.

Commit

Permalink
docs(recipes): update module deployment recipe (#300)
Browse files Browse the repository at this point in the history
  • Loading branch information
infoxicator authored Sep 25, 2020
1 parent 0a1aa68 commit 9abc1c4
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 162 deletions.
160 changes: 0 additions & 160 deletions docs/recipes/Deploying-Modules.md

This file was deleted.

208 changes: 208 additions & 0 deletions docs/recipes/Publishing-Modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
<!--ONE-DOCS-HIDE start-->
[👈 Return to Overview](./README.md)
<!--ONE-DOCS-HIDE end-->

# Publishing Modules

## 📖 Table of Contents
* [Overview](#overview)
* [Creating a Module Map](#creating-a-module-map)
* [Update Module Map Script](#update-module-map-script)
* [Deploying to Vercel with GitHub Actions](#deploying-to-vercel-with-github-actions)
* [Updating the Content Security Policy](#updating-the-content-security-policy)

## Overview

Publishing Holocron modules consists on two steps:

1. Upload the contents of the `build` folder to your CDN of choice. This publishes your module's static assets similar to when you release an npm dependency.
2. Update the [module map](../api/server/Module-Map-Schema.md) to point to the newly published static assets. This will tell One App to consume that published/released module version.

In the following example, we will use Vercel to publish a holocron module and a manual script to deploy the module map.

### Creating a Module Map

If you don't have an existing module map, you can create a `module-map.json` file locally that looks something like this:

```json
{
"modules": {

}
}
```

Then, you can upload it to Vercel using the [Vercel CLI](https://vercel.com/docs/cli#commands/overview/basic-usage) by running `vercel` within the directory that you created the `module-map.json` in. You can now view it at the production URL that `vercel` prints out, you will need that URL for the following steps for the `moduleMapUrl` variable.

> Note: Your One App instance and all your module deployment jobs need to point at the same module map URL. One App requires that URL to be set in the [`HOLOCRON_MODULE_MAP_URL`](../api/server/Environment-Variables.md#holocron_module_map_url) environment variable.
### Update Module Map Script

You can place this script wherever you would like. However, the following step expects it to be in the `scripts/updateModuleMap.js` folder of your module. If you put it in a different location, make sure you update it in the following step.

> Note: The manual script presented below is just an example on how to update the module map by downloading your current module map, updating it to include the new deployed assets and re-uploading it, however, this approach doesn't handle concurrency.
There could be a case where two deployments running on different pipelines try to update the module map at the same time and a race condition might occur where the last deployment will override the contents of the first. If you want to avoid concurrency issues, we recommend to adopt
a "locking" mechanism as part of your CI to prevent the module map from being updated while another deployment is already in progress.

```javascript
const fs = require('fs-extra');
const fetch = require('node-fetch');
const { name, version } = require('../package.json');

// This is created during the build process within the deploy action
const bundleIntegrity = require('../bundle.integrity.manifest.json');

const moduleMapUrl = 'YOUR MODULE MAP URL'; // This is the module map URL you got in the previous step i.e. https://example-module-map.vercel.app

const STATIC_ASSETS_URL = 'URL TO WHERE YOUR MODULE IS DEPLOYED'; // example 'https://my-module.vercel.app'

const updateModuleMap = async () => {
try {
const response = await fetch(moduleMapUrl); // download the current module map

const moduleMapContent = await response.json();
const dir = 'module_map';

moduleMapContent.modules[name] = {
browser: {
url: `${STATIC_ASSETS_URL}/${version}/${name}.browser.js`,
integrity: bundleIntegrity.browser,
},
legacyBrowser: {
url: `${STATIC_ASSETS_URL}/${version}/${name}.legacy.browser.js`,
integrity: bundleIntegrity.legacyBrowser,
},
node: {
url: `${STATIC_ASSETS_URL}/${version}/${name}.node.js`,
integrity: bundleIntegrity.node,
},
};

await fs.ensureDir(dir);
// write the updated module map to a temporary folder
// so it can be re-uploaded by the deploy action
await fs.writeFile(
'./module_map/module-map.json', JSON.stringify(moduleMapContent, null, 2)
);
} catch (error) {
console.log(error);
}
};

updateModuleMap().catch((err) => {
// eslint-disable-next-line no-console
console.log(err);
});
```
>Note: Vercel only allows you to upload a single folder per project, this means that every holocron module will have its own URL and project name. Other CDNs like Cloudfront / Amazon S3 allow you to upload multiple folders.
>In that case, you can adjust the script above to include module name as the folder that contains the static assets for each holocron module.
## Deploying to Vercel with GitHub Actions

[Vercel](https://vercel.com/home) allows you to publish statics assets such as your modules. We will accomplish this by using [GitHub Actions](https://github.com/features/actions).

This deploy action uses the `vercel-cli` so you will need a create [token with Vercel](https://vercel.com/account/tokens) and then add a new variable called `VERCEL_TOKEN` to the `Secrets` section of your repository on Github.

The below action runs whenever a release is created or a push to master occurs. You do not need to install or add `vercel-cli` to your `package.json` as the `vercel` command is available within GitHub actions.

Create a new `.github/workflows/deploy.yml` file in the root folder of your module


```yml
name: Deploy

on:
release:
types: [created]
push:
branches:
- master

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Cache NPM Dependencies
uses: actions/cache@v1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install Dependencies
run: npm install
env:
NODE_ENV: development
- name: Build Module
run: npm run build
env:
NODE_ENV: production
- name: 'Deploy'
run: vercel build --prod --confirm -t $VERCEL_TOKEN --name [YOUR REPO NAME]
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
- name: 'Update Module Map'
run: |
node ./scripts/updateModuleMap.js
vercel module_map --prod --confirm -t $VERCEL_TOKEN --name [NAME OF YOUR MODULE MAP]
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
```
> Note: It is very important to set your `NODE_ENV=production` before running the `npm run build` command so the code is bundled and minified for production.

The `deploy` stage on this action will publish the contents of the `build` folder to Vercel. The `Update Module Map` stage will run the script we created in `./scripts/updateModuleMap.js` and upload the updated module map placed in the temporary `module_map` folder to Vercel.

If you view the action running, during the `Deploy` step, you should see something like:

```bash
Run vercel build --prod --confirm -t $VERCEL_TOKEN --name [NAME OF YOUR MODULE]
Vercel CLI 20.1.0
The "--name" option is deprecated (https://vercel.link/name-flag)
- Setting up project
Linked to [YOUR NOW USERNAME]/[YOUR REPO NAME](created .vercel and added it to .gitignore)
Inspect: https://vercel.com/[YOUR NOW USERNAME]/[YOUR REPO NAME] [1s]
- Queued
- Building
Production: https://[YOUR REPO NAME].vercel.app [6s]
```

You can inspect the contents of your module map deployed to Vercel to verify that it contains the links to the deployed assets of your modules.

The running One App instance that points to your updated module map will automatically pull the newly deployed modules and update your application without the need for a server restart.

## Updating the Content Security Policy

One App enforces the [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) by default. You must add to the CSP the URL where your module's statics are loaded from, otherwise you will get a CSP violation and your modules will fail to load in Production.

For example, to add Vercel to the Content Security Policy, add the `*.vercel.app` domain to the `connectSrc` and `scriptSrc` directives in your Root Module's `appConfig.js` file under the csp section:
```javascript
export default contentSecurityPolicyBuilder({
directives: {
reportUri: process.env.ONE_CLIENT_CSP_REPORTING_URL,
defaultSrc: [
"'self'",
],
scriptSrc: [
"'self'",
'*.vercel.app',
],
imgSrc: [
"'self'",
],
styleSrc: [
"'self'",
"'unsafe-inline'",
],
connectSrc: [
"'self'",
'*.vercel.app',
],
},
});
```
For more information on how to configure One App and the CSP please refer to this section: [App Configuration](../api/modules/App-Configuration.md#csp)
2 changes: 1 addition & 1 deletion docs/recipes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* [Running A One App Instance Locally](./Running-One-App-Locally.md)
* [Running In Production](./Running-In-Production.md) 🔨
* [Monitoring One App](./Monitoring-One-App.md) 📌
* [Deploying Modules](./Deploying-Modules.md) 🔨
* [Publishing Modules](Publishing-Modules.md)

> 🔨 = This guide is a work-in-progress.
> 📌 = This guide needs to be written.
Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/Running-In-Production.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ or the `<moduleName>.node.js` bundle when running on the server.
One App has no opinion on where module bundles are deployed to, the only thing to keep in mind is
that all the assets for a given module must be kept together when deployed, i.e. the file structure
generated by [`one-app-bundler`] must not be modified. This is because the bundles rely on this
structure for knowing where to look for the lang packs. To see examples of how to deploy modules to certain hosting platforms, take a look at our documentation on [deploying modules](./Deploying-Modules.md).
structure for knowing where to look for the lang packs. To see examples of how to deploy modules to certain hosting platforms, take a look at our documentation on [publishing modules](Publishing-Modules.md).

## Building and Deploying a Holocron Module Map

Expand Down

0 comments on commit 9abc1c4

Please sign in to comment.