Skip to content

Commit

Permalink
[WIP] initial commit on coherence PR (#7683)
Browse files Browse the repository at this point in the history
* initial commit on coherence PR

* cleanup & fix postgres-only

* second pass, much better tested. should almost work, except for some cases of replacing the port in the toml

* add deploy doc

* docs updates

* move docs

* undo change to versioned sidebar

* lint fix

* apply suggestions to coherence.js

* reorganize files

* fix stray changes

* style changes to docs

* add builder, moves consts up

* back to working

* add comments, get force working

* get notes working

* finishing changes

* combine writing files into one task

---------

Co-authored-by: Zachary Zaro <zach@withcoherence.com>
Co-authored-by: Dominic Saadi <dominiceliassaadi@gmail.com>
  • Loading branch information
3 people authored Apr 26, 2023
1 parent da6ba49 commit 4916a93
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 0 deletions.
27 changes: 27 additions & 0 deletions docs/docs/deploy/coherence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
description: Serverful deploys on GCP or AWS via Coherence's full-lifecycle environment automation
---

# Deploy to Coherence

[Coherence](https://www.withcoherence.com) delivers automated environments across the full software development lifecycle, without requiring you to glue together your own mess of open source tools to get a world-class develper experience for your team. Coherence is focused on serving startups, who are doing mission-critical work. With one simple configuration, Coherence offers:

- Cloud-hosted development environments, based on VSCode. Similar to gitpod or Github CodeSpaces
- Production-ready CI/CD running in your own GCP/AWS account, including: database migration/seeding/snapshot loading, parallelized tests, container building and docker registry management
- Full-stack branch previews. Vercel/netlify like developer experience for arbitrary container apps, including dependencies such as CDN, redis, and database resources
- Staging and production environment management in your AWS/GCP accounts. Production runs in its own cloud account (AWS) or project (GCP). Integrated secrets management across all environment types with a developer-friendly UI.

## Coherence Prerequisites

You have to have your project hosted on GitHub, and you must have an [AWS](https://docs.withcoherence.com/docs/tutorials/creating-an-app-on-aws) or [GCP](https://docs.withcoherence.com/docs/tutorials/creating-an-app-on-gcp) account.

## Coherence Deploy

If you want to deploy your redwood project on Coherence:
1. If you don't already have one, create a new redwood project: `yarn create redwood-app ./test-deploy`
3. run the command `yarn rw setup deploy coherence`. The command will inspect your `prisma` config to determine if you are using a supported database (`postgres` or `mysql` are supported on Coherence)
4. follow the [Coherence Redwood Deploy Docs](https://docs.withcoherence.com/docs/configuration/frameworks#redwood-js) for more information, including if you want to set up:
- redis server
- database migration/seeding/snapshot loading
- cron jobs or async workers
- object storage using Google Cloud Storage or AWS's S3
5 changes: 5 additions & 0 deletions docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ module.exports = {
items: [
{ type: 'doc', label: 'Introduction', id: 'deploy/introduction' },
{ type: 'doc', label: 'Baremetal', id: 'deploy/baremetal' },
{
type: 'doc',
label: 'GCP or AWS via Coherence',
id: 'deploy/coherence',
},
{
type: 'doc',
label: 'AWS via Flightcontrol',
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/commands/setup/deploy/providers/coherence.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const command = 'coherence'

export const description = 'Setup Coherence deploy'

export function builder(yargs) {
yargs.option('force', {
description: 'Overwrite existing configuration',
type: 'boolean',
default: false,
})
}

export async function handler(options) {
const { handler } = await import('./coherenceHandler')
return handler(options)
}
180 changes: 180 additions & 0 deletions packages/cli/src/commands/setup/deploy/providers/coherenceHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import fs from 'fs'
import path from 'path'

import { getSchema, getConfig } from '@prisma/internals'
import { Listr } from 'listr2'

import {
colors as c,
getPaths,
isTypeScriptProject,
} from '@redwoodjs/cli-helpers'
import { errorTelemetry } from '@redwoodjs/telemetry'

import { printSetupNotes, addFilesTask } from '../helpers'

const redwoodProjectPaths = getPaths()

const EXTENSION = isTypeScriptProject ? 'ts' : 'js'

export async function handler({ force }) {
const addCoherenceFilesTask = await getAddCoherenceFilesTask(force)

const tasks = new Listr(
[
addCoherenceFilesTask,
updateRedwoodTOMLPortsTask(),
printSetupNotes(notes),
],
{ rendererOptions: { collapse: false } }
)

try {
await tasks.run()
} catch (e) {
errorTelemetry(process.argv, e.message)
console.error(c.error(e.message))
process.exit(e?.exitCode || 1)
}
}

// ------------------------
// Tasks and helpers
// ------------------------

async function getAddCoherenceFilesTask(force) {
const files = [
{
path: path.join(redwoodProjectPaths.api.functions, `health.${EXTENSION}`),
content: coherenceFiles.healthCheck,
},
]

const coherenceConfigFile = {
path: path.join(redwoodProjectPaths.base, 'coherence.yml'),
}

coherenceConfigFile.content = await getCoherenceConfigFileContent()

files.push(coherenceConfigFile)

return addFilesTask({
title: `Adding coherence.yml and health.${EXTENSION}`,
files,
force,
})
}

const notes = [
"You're ready to deploy to Coherence!\n",
'Go to https://app.withcoherence.com to create your account and setup your cloud or GitHub connections.',
'Check out the deployment docs at https://docs.withcoherence.com for detailed instructions and more information.\n',
"Reach out to redwood@withcoherence.com with any questions! We're here to support you.",
]

const SUPPORTED_DATABASES = ['mysql', 'postgresql']

/**
* Check the value of `provider` in the datasource block in `schema.prisma`:
*
* ```prisma title="schema.prisma"
* datasource db {
* provider = "sqlite"
* url = env("DATABASE_URL")
* }
* ```
*/
async function getCoherenceConfigFileContent() {
const prismaSchema = await getSchema(redwoodProjectPaths.api.dbSchema)
const prismaConfig = await getConfig({ datamodel: prismaSchema })

let db = prismaConfig.datasources[0].activeProvider

if (!SUPPORTED_DATABASES.includes(db)) {
notes.unshift(
'⚠️ Warning: only mysql and postgresql prisma databases are supported on Coherence at this time.\n'
)
}

if (db === 'postgresql') {
db = 'postgres'
}

return coherenceFiles.yamlTemplate(db)
}

/**
* Updates the ports in redwood.toml to use an environment variable.
*/
function updateRedwoodTOMLPortsTask() {
return {
title: 'Updating ports in redwood.toml...',
task: () => {
const redwoodTOMLPath = path.join(
redwoodProjectPaths.base,
'redwood.toml'
)

let redwoodTOMLContent = fs.readFileSync(redwoodTOMLPath, 'utf-8')

redwoodTOMLContent = redwoodTOMLContent.replace(
/port.*?\n/m,
'port = "${PORT}"\n'
)

fs.writeFileSync(redwoodTOMLPath, redwoodTOMLContent)
},
}
}

// ------------------------
// Files
// ------------------------

const coherenceFiles = {
yamlTemplate(db) {
return `\
api:
type: backend
url_path: "/api"
prod:
command: ["yarn", "rw", "build", "api", "&&", "yarn", "rw", "serve", "api"]
dev:
command: ["yarn", "rw", "build", "api", "&&", "yarn", "rw", "dev", "api"]
local_packages: ["node_modules"]
system:
cpu: 2
memory: 2G
health_check: "/health"
resources:
- name: ${path.basename(redwoodProjectPaths.base)}-db
engine: ${db}
version: 13
type: database
web:
type: frontend
assets_path: "web/dist"
prod:
command: ["yarn", "rw", "serve", "web"]
dev:
command: ["yarn", "rw", "dev", "web", "--fwd=\\"--allowed-hosts all\\""]
build: ["yarn", "rw", "build", "web"]
local_packages: ["node_modules"]
system:
cpu: 2
memory: 2G
`
},
healthCheck: `\
// Coherence health check
export const handler = async () => {
return {
statusCode: 200,
}
}
`,
}

0 comments on commit 4916a93

Please sign in to comment.