Skip to content

Commit

Permalink
rough in shopify support
Browse files Browse the repository at this point in the history
  • Loading branch information
rubys committed Jan 8, 2025
1 parent 39ed3af commit e0dcad0
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 63 deletions.
68 changes: 66 additions & 2 deletions fly.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,21 @@ GDF.extend(class extends GDF {
// create volume for sqlite3
if (this.sqlite3) this.flyMakeVolume()

if (this.setupScriptType === 'dbsetup') {
this.flySetCmd()
}

// setup swap
if (this.options.swap != null) this.flySetSwap()

// attach consul for litefs
if (this.litefs) this.flyAttachConsul(this.flyApp)

// set secrets, healthcheck for remix apps
if (this.remix) {
if (this.shopify) {
this.flyShopifyEnv(this.flyApp)
this.flyShopifyConfig(this.flyApp)
} else if (this.remix) {
this.flyRemixSecrets(this.flyApp)
this.flyHealthCheck('/healthcheck')
}
Expand Down Expand Up @@ -62,7 +69,7 @@ GDF.extend(class extends GDF {
this.flyToml = fs.readFileSync(this.flyTomlFile, 'utf-8')

// parse app name from fly.toml
this.flyApp = this.flyToml.match(/^app\s*=\s*"?([-\w]+)"?/m)?.[1]
this.flyApp = this.flyToml.match(/^app\s*=\s*["']?([-\w]+)["']?/m)?.[1]

// see if flyctl is in the path
const paths = (process.env.PATH || '')
Expand Down Expand Up @@ -159,6 +166,27 @@ GDF.extend(class extends GDF {
}
}

// override command in fly.toml to include dbsetup.js
flySetCmd() {
if (this.flyToml.includes('[processes]')) return

let cmd = this.startCommand

const dockerfile = fs.readFileSync('Dockerfile', 'utf8')

const match = dockerfile.match(/^\s*CMD\s+(\[.*\]|".*")/mi)
if (match) {
try {
cmd = JSON.parse(match[1])
} catch { }
}

if (Array.isArray(cmd)) cmd = cmd.join(' ')
cmd = `${this.bun ? 'bun' : 'node'} ./dbsetup.js ${cmd}`
this.flyToml += `\n[processes]\n app = ${JSON.stringify(cmd)}\n`
fs.writeFileSync(this.flyTomlFile, this.flyToml)
}

// add volume to fly.toml and create it if app exists
flyAttachConsul(app) {
if (!app) return
Expand Down Expand Up @@ -283,6 +311,42 @@ GDF.extend(class extends GDF {
})
}

// set environment and secrets for Shopify apps
flyShopifyEnv(app) {
const env = {
PORT: 3000,
SHOPIFY_APP_URL: `https://${app}.fly.dev`
}

try {
const stdout = execSync('shopify app env show', { encoding: 'utf8' })
for (const match of stdout.matchAll(/^\s*(\w+)=(.*)/mg)) {
if (match[1] === 'SHOPIFY_API_SECRET') {
console.log(`${chalk.bold.green('execute'.padStart(11))} flyctl secrets set SHOPIFY_API_SECRET`)
execSync(`${this.flyctl} secrets set SHOPIFY_API_SECRET=${match[2]} --app ${app}`, { stdio: 'inherit' })
} else {
env[match[1]] = match[2]
}
}
} catch { }

if (this.flyToml.includes('[env]')) return
this.flyToml += '\n[env]\n' + Object.entries(env).map(([key, value]) => ` ${key} = ${JSON.stringify(value)}`).join('\n') + '\n'
fs.writeFileSync(this.flyTomlFile, this.flyToml)
}

// update config for Shopify apps
flyShopifyConfig(app) {
const original = fs.readFileSync('shopify.app.toml', 'utf-8')
const config = original.replaceAll(/"https:\/\/[-\w.]+/g, `"https://${app}.fly.dev`)
if (original !== config) {
console.log(`${chalk.bold.green('update'.padStart(11, ' '))} shopify.app.toml`)
fs.writeFileSync('shopify.app.toml', config)
console.log(`${chalk.bold.green('execute'.padStart(11))} shopify app deploy`)
execSync('shopify app deploy', { stdio: 'inherit' })
}
}

// prep for deployment via GitHub actions, including setting up a staging app
flyGitHubPrep() {
const deploy = fs.readFileSync('.github/workflows/deploy.yml', 'utf-8')
Expand Down
24 changes: 22 additions & 2 deletions gdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ export class GDF {
// exit code
#exitCode = 0

// dockerfile exists at the time of invocation
#dockerfileExists = false

get variant() {
return this.options.alpine ? 'alpine' : 'slim'
}
Expand Down Expand Up @@ -147,6 +150,11 @@ export class GDF {
this.#pj.dependencies?.['@remix-run/node'])
}

// Does this application use shopify?
get shopify() {
return fs.existsSync(path.join(this._appdir, 'shopify.app.toml'))
}

// Is this an EpicStack application?
get epicStack() {
return !!this.#pj['epic-stack']
Expand Down Expand Up @@ -934,6 +942,7 @@ export class GDF {
this.options = options
this._appdir = appdir
this.#pj = JSON.parse(fs.readFileSync(path.join(appdir, 'package.json'), 'utf-8'))
this.#dockerfileExists = fs.existsSync(path.join(appdir, 'Dockerfile'))

// backwards compatibility with previous definition of --build=defer
if (options.build === 'defer') {
Expand Down Expand Up @@ -967,7 +976,11 @@ export class GDF {
}

if (this.entrypoint) {
templates['docker-entrypoint.ejs'] = `${this.configDir}docker-entrypoint.js`
if (!this.#dockerfileExists) {
templates['docker-entrypoint.ejs'] = `${this.configDir}docker-entrypoint.js`
} else if (this.options.skip && fs.existsSync(path.join(appdir, 'fly.toml'))) {
templates['docker-entrypoint.ejs'] = `${this.configDir}dbsetup.js`
}
}

if (this.litefs) {
Expand Down Expand Up @@ -1028,7 +1041,11 @@ export class GDF {
runner.apply(this)
}

process.exit(this.#exitCode)
if (this.#exitCode) process.exit(this.#exitCode)
}

get setupScriptType() {
return (this.options.skip && this.#dockerfileExists) ? 'dbsetup' : 'docker'
}

setExit(code) {
Expand All @@ -1046,6 +1063,9 @@ export class GDF {
if (current === proposed) {
console.log(`${chalk.bold.blue('identical'.padStart(11))} ${name}`)
return dest
} else if (this.options.skip) {
console.log(`${chalk.bold.yellow('skip'.padStart(11))} ${name}`)
return current
}

let prompt
Expand Down
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ const options = yargs((hideBin(process.argv)))
describe: 'expose port',
type: 'integer'
})
.option('skip', {
describe: 'skip overwrite of existing files',
type: 'boolean'
})
.option('swap', {
alias: 's',
describe: 'allocate swap space (eg. 1G, 1GiB, 1024M)',
Expand Down
96 changes: 58 additions & 38 deletions templates/docker-entrypoint.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -42,76 +42,96 @@ if (process.env.DATABASE_URL) {
}

<% } -%>
<% n = typeModule ? 0 : 1 -%>
<% if (!typeModule) { -%>
;(async() => {
<% } -%>
<% if (options.swap && !flySetup()) { -%>
<%= tab(1) %>// allocate swap space
<%= tab(1) %>await exec('fallocate -l <%= options.swap %> /swapfile')
<%= tab(1) %>await exec('chmod 0600 /swapfile')
<%= tab(1) %>await exec('mkswap /swapfile')
<%= tab(1) %>writeFileSync('/proc/sys/vm/swappiness', '10')
<%= tab(1) %>await exec('swapon /swapfile')
<%= tab(1) %>writeFileSync('/proc/sys/vm/overcommit_memory', '1')
<%= tab(n) %>// allocate swap space
<%= tab(n) %>await exec('fallocate -l <%= options.swap %> /swapfile')
<%= tab(n) %>await exec('chmod 0600 /swapfile')
<%= tab(n) %>await exec('mkswap /swapfile')
<%= tab(n) %>writeFileSync('/proc/sys/vm/swappiness', '10')
<%= tab(n) %>await exec('swapon /swapfile')
<%= tab(n) %>writeFileSync('/proc/sys/vm/overcommit_memory', '1')

<% } -%>
<% if (prisma || (build && options.deferBuild) || nextjsGeneration) { -%>
<% if (setupScriptType == 'docker') { -%>
<% if (prisma && sqlite3) { -%>
<%= tab(1) %>// If running the web server then migrate existing database
<%= tab(n) %>// If running the web server then migrate existing database
<% } else { -%>
<%= tab(1) %>// If running the web server then prerender pages
<%= tab(n) %>// If running the web server then prerender pages
<% } -%>
<%= tab(1) %>if (process.argv.slice(2).join(' ') === '<%-
<%= tab(n) %>if (process.argv.slice(2).join(' ') === '<%-
Array.isArray(startCommand) ? startCommand.join(" ") : startCommand
%>'<% if (litefs) { %> && process.env.FLY_REGION === process.env.PRIMARY_REGION<% } %>) {
<% n++ -%>
<% } -%>
<% if (prisma) { -%>
<% if (prismaFile) { -%>
<%= tab(2) %><%= nuxtjs ? 'let' : 'const' %> source = path.resolve('<%- prismaFile %>')
<%= tab(2) %>const target = '/data/' + path.basename(source)
<%= tab(2) %>if (!fs.existsSync(source) && fs.existsSync('/data')) fs.symlinkSync(target, source)
<%= tab(n) %>// place Sqlite3 database on volume
<%= tab(n) %><%= nuxtjs ? 'let' : 'const' %> source = path.resolve('<%- prismaFile %>')
<%= tab(n) %>const target = '/data/' + path.basename(source)
<%= tab(n) %>if (!fs.existsSync(source) && fs.existsSync('/data')) fs.symlinkSync(target, source)
<% if (nuxtjs) { -%>
<%= tab(2) %>source = path.resolve('./.output/server', '<%- prismaFile %>')
<%= tab(2) %>if (!fs.existsSync(source) && fs.existsSync('/data')) fs.symlinkSync(target, source)
<%= tab(n) %>source = path.resolve('./.output/server', '<%- prismaFile %>')
<%= tab(n) %>if (!fs.existsSync(source) && fs.existsSync('/data')) fs.symlinkSync(target, source)
<% } -%>
<% } else if (prismaSeed && sqlite3 && prismaEnv) { -%>
<%= tab(2) %>const url = new URL(process.env.<%= prismaEnv %>)
<%= tab(2) %>const target = url.protocol === 'file:' && url.pathname
<%= tab(2) %><%= litestream ? 'let' : 'const' %> newDb = target && !fs.existsSync(target)
<%= tab(n) %>const url = new URL(process.env.<%= prismaEnv %>)
<%= tab(n) %>const target = url.protocol === 'file:' && url.pathname
<% if (litestream && sqlite3 && (prismaFile || prismaEnv)) { -%>

<%= tab(n) %>// restore database if not present and replica exists
<% } -%>
<% if (prismaFile && prismaSeed && sqlite3) { -%>
<%= tab(2) %><%= litestream ? 'let' : 'const' %> newDb = !fs.existsSync(target)
<% } -%>
<% if (litestream && prismaSeed && sqlite3 && (prismaFile || prismaEnv)) { -%>
<%= tab(2) %>if (newDb && process.env.BUCKET_NAME) {
<%= tab(3) %>await exec(`litestream restore -config litestream.yml -if-replica-exists ${target}`)
<%= tab(3) %>newDb = !fs.existsSync(target)
<%= tab(2) %>}
<% if (sqlite3 && (prismaSeed || litestream) && (prismaFile || prismaEnv)) { -%>
<%= tab(n) %><%= litestream ? 'let' : 'const' %> newDb = <%- !prismaFile ? 'target && ' : '' %>!fs.existsSync(target)
<% } -%>
<% if (sqlite3) { -%>
<%= tab(2) %>await exec('<%= npx %> prisma migrate deploy')
<% if (litestream && sqlite3 && (prismaFile || prismaEnv)) { -%>
<%= tab(n) %>if (newDb && process.env.BUCKET_NAME) {
<%= tab(n+1) %>await exec(`litestream restore -config litestream.yml -if-replica-exists ${target}`)
<% if (prismaSeed && sqlite3 && (prismaFile || prismaEnv)) { -%>
<%= tab(n+1) %>newDb = !fs.existsSync(target)
<% } -%>
<%= tab(n) %>}
<% } -%>
<% if (sqlite3 && !shopify) { -%>
<% if (prismaFile) { -%>

<%= tab(n) %>// prepare database
<% } -%>
<%= tab(n) %>await exec('<%= npx %> prisma migrate deploy')
<% } -%>
<% if (prismaSeed && sqlite3 && (prismaFile || prismaEnv)) { -%>
<%= tab(2) %>if (newDb) await exec('npx prisma db seed')
<%= tab(n) %>if (newDb) await exec('npx prisma db seed')
<% } -%>
<% } -%>
<% if (nextjsGeneration) { -%>
<%= tab(2) %>await exec('<%= npx %> next build --experimental-build-mode generate')
<%= tab(n) %>await exec('<%= npx %> next build --experimental-build-mode generate')
<% } -%>
<% if (build && options.deferBuild) { -%>
<%= tab(2) %>await exec('<%= packager %> run build')
<%= tab(n) %>await exec('<%= packager %> run build')
<% } -%>
<% if (setupScriptType == 'docker') { -%>
<%= tab(--n) %>}
<% } -%>
<%= tab(1) %>}

<% } -%>
<%= tab(1) %>// launch application
<%= tab(n) %>// launch application
<% if (litestream) { -%>
<%= tab(1) %>if (process.env.BUCKET_NAME) {
<%= tab(2) %>await exec(`litestream replicate -config litestream.yml -exec ${JSON.stringify(process.argv.slice(2).join(' '))}`)
<%= tab(1) %>} else {
<%= tab(2) %>await exec(process.argv.slice(2).join(' '))
<%= tab(1) %>}
<%= tab(n) %>if (process.env.BUCKET_NAME) {
<%= tab(n+1) %>await exec(`litestream replicate -config litestream.yml -exec ${JSON.stringify(process.argv.slice(2).join(' '))}`)
<%= tab(n) %>} else {
<%= tab(n+1) %>await exec(process.argv.slice(2).join(' '))
<%= tab(n) %>}
<% } else { -%>
<%= tab(1) %>await exec(process.argv.slice(2).join(' '))
<%= tab(n) %>await exec(process.argv.slice(2).join(' '))
<% } -%>
<% if (!typeModule) { -%>
})()
<% } -%>

<%= tab(0) %>function exec(command) {
<%= tab(1) %>const child = spawn(command, { shell: true, stdio: 'inherit', env })
Expand Down
3 changes: 3 additions & 0 deletions test/base/defer-build/docker-entrypoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ const env = { ...process.env }
;(async() => {
// If running the web server then migrate existing database
if (process.argv.slice(2).join(' ') === 'npm run start') {
// place Sqlite3 database on volume
const source = path.resolve('./dev.db')
const target = '/data/' + path.basename(source)
if (!fs.existsSync(source) && fs.existsSync('/data')) fs.symlinkSync(target, source)

// prepare database
await exec('npx prisma migrate deploy')
await exec('npm run build')
}
Expand Down
3 changes: 3 additions & 0 deletions test/frameworks/nuxt-prisma/docker-entrypoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const env = { ...process.env }
;(async() => {
// If running the web server then migrate existing database
if (process.argv.slice(2).join(' ') === 'node .output/server/index.mjs') {
// place Sqlite3 database on volume
let source = path.resolve('./dev.db')
const target = '/data/' + path.basename(source)
if (!fs.existsSync(source) && fs.existsSync('/data')) fs.symlinkSync(target, source)
Expand All @@ -19,6 +20,8 @@ const env = { ...process.env }
await exec(`litestream restore -config litestream.yml -if-replica-exists ${target}`)
newDb = !fs.existsSync(target)
}

// prepare database
await exec('npx prisma migrate deploy')
if (newDb) await exec('npx prisma db seed')
}
Expand Down
14 changes: 6 additions & 8 deletions test/frameworks/remix-epic/docker-entrypoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import { spawn } from 'node:child_process'

const env = { ...process.env }

;(async() => {
// If running the web server then migrate existing database
if (process.argv.slice(2).join(' ') === 'npm run start' && process.env.FLY_REGION === process.env.PRIMARY_REGION) {
await exec('npx prisma migrate deploy')
}
// If running the web server then migrate existing database
if (process.argv.slice(2).join(' ') === 'npm run start' && process.env.FLY_REGION === process.env.PRIMARY_REGION) {
await exec('npx prisma migrate deploy')
}

// launch application
await exec(process.argv.slice(2).join(' '))
})()
// launch application
await exec(process.argv.slice(2).join(' '))

function exec(command) {
const child = spawn(command, { shell: true, stdio: 'inherit', env })
Expand Down
Loading

0 comments on commit e0dcad0

Please sign in to comment.