Skip to content

Commit

Permalink
add better build version handling
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffomatic committed Jan 9, 2021
1 parent c7549ac commit d76ad1b
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 154 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"typecheck": "tsc -p . --noEmit",
"lint": "eslint 'src/**/*.{js,ts,tsx}' --quiet --fix",
"test": "jest",
"clean": "rm -rf out .cache dist out"
"clean": "rm -rf out src/web/ephemeral"
},
"repository": {
"type": "git",
Expand Down
46 changes: 10 additions & 36 deletions src/cli/build/buildServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,14 @@ import * as path from 'path'

import * as esbuild from 'esbuild'

import {
buildVersionPath,
gameSrcPath,
serverOutputPath,
webEntrypoints,
} from './common'
import { gameSrcPath, serverBuildVersionPath, serverOutputPath } from './common'

function getMtimeMs(filepath: string): number {
const stats = fs.statSync(filepath)
if (stats.isDirectory()) {
const files = fs.readdirSync(filepath)
const mtimes = files.map((f) => getMtimeMs(path.join(filepath, f)))
return mtimes.reduce((accum, t) => Math.max(accum, t), -1)
}

return stats.mtimeMs
let buildVersion = '(none)'
if (process.argv.length > 2) {
buildVersion = process.argv[2]
}

console.log('Creating bundle...')

// Write build version
const buildVersion = getMtimeMs(gameSrcPath).toString()
fs.mkdirSync(path.normalize(path.join(buildVersionPath, '..')), {
recursive: true,
})
fs.writeFileSync(buildVersionPath, buildVersion)
console.log(`build version '${buildVersion}' written to ${buildVersionPath}`)
console.log(`Creating bundle for build version ${buildVersion}...`)

esbuild.buildSync({
bundle: true,
Expand All @@ -40,15 +21,8 @@ esbuild.buildSync({
target: 'es2019',
})

// Generate new entrypoint HTML with hardcoded build version
Promise.all(
webEntrypoints.map(async (srcPath) => {
await fs.promises.mkdir(path.join(serverOutputPath, srcPath), {
recursive: true,
})
await fs.promises.copyFile(
path.join(gameSrcPath, srcPath, 'index.html'),
path.join(serverOutputPath, srcPath, 'index.html'),
)
}),
)
// Write build version
fs.mkdirSync(path.normalize(path.join(serverBuildVersionPath, '..')), {
recursive: true,
})
fs.writeFileSync(serverBuildVersionPath, buildVersion)
35 changes: 33 additions & 2 deletions src/cli/build/buildWeb.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
import * as fs from 'fs'
import * as path from 'path'

import * as esbuild from 'esbuild'

import { gameSrcPath, webEntrypoints, webOutputPath } from './common'
import {
gameSrcPath,
webEntrypoints,
webEphemeralPath,
webOutputPath,
} from './common'

console.log('Creating bundle...')
let buildVersion = '(none)'
if (process.argv.length > 2) {
buildVersion = process.argv[2]
}

console.log(`Creating bundle for build version ${buildVersion}...`)

// Write build version
fs.mkdirSync(webEphemeralPath, { recursive: true })
fs.writeFileSync(
path.join(webEphemeralPath, 'buildVersion.ts'),
`export const buildVersion = '${buildVersion}'`,
)

esbuild.buildSync({
bundle: true,
Expand All @@ -20,3 +38,16 @@ esbuild.buildSync({
sourcemap: true,
target: ['chrome88', 'firefox84', 'safari14'],
})

// Copy index.html files
Promise.all(
webEntrypoints.map(async (srcPath) => {
await fs.promises.mkdir(path.join(webOutputPath, srcPath), {
recursive: true,
})
await fs.promises.copyFile(
path.join(gameSrcPath, srcPath, 'index.html'),
path.join(webOutputPath, srcPath, 'index.html'),
)
}),
)
6 changes: 5 additions & 1 deletion src/cli/build/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export const gameSrcPath = path.join(projectRootPath, 'src')
export const buildOutputPath = path.join(projectRootPath, 'out')
export const webOutputPath = path.join(buildOutputPath, 'web')
export const serverOutputPath = path.join(buildOutputPath, 'server')
export const buildVersionPath = path.join(serverOutputPath, 'buildVersion')
export const webEphemeralPath = path.join(gameSrcPath, 'web', 'ephemeral')
export const serverBuildVersionPath = path.join(
serverOutputPath,
'buildVersion',
)

export const webEntrypoints = ['client', 'tools/rendertoy']
25 changes: 22 additions & 3 deletions src/cli/build/dev.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ChildProcessWithoutNullStreams, spawn } from 'child_process'
import * as fs from 'fs'
import * as path from 'path'

import * as chokidar from 'chokidar'

import { gameSrcPath, serverOutputPath } from './common'
import { gameSrcPath, serverOutputPath, webEphemeralPath } from './common'

// Removes a newline from the end of a buffer, if it exists.
const trimNewlineSuffix = (data: Buffer): Buffer => {
Expand All @@ -13,6 +14,17 @@ const trimNewlineSuffix = (data: Buffer): Buffer => {
return data
}

function getMtimeMs(filepath: string): number {
const stats = fs.statSync(filepath)
if (stats.isDirectory()) {
const files = fs.readdirSync(filepath)
const mtimes = files.map((f) => getMtimeMs(path.join(filepath, f)))
return mtimes.reduce((accum, t) => Math.max(accum, t), -1)
}

return stats.mtimeMs
}

// global watch state
let building = false // a single-thread mutex around rebuild()
let buildQueued = false
Expand All @@ -26,15 +38,17 @@ const rebuild = async () => {
}

building = true
const buildVersion = getMtimeMs(gameSrcPath).toString()

console.log('Spawning build jobs...')
console.log(`Spawning build jobs for build version ${buildVersion}...`)

// Rebuild client artifacts
const clientBuild = new Promise((resolve) => {
const build = spawn('npx', [
'ts-node',
'--transpile-only',
path.join(gameSrcPath, 'cli', 'build', 'buildWeb.ts'),
buildVersion,
])
build.on('close', resolve)
build.stdout.on('data', (data) =>
Expand All @@ -50,6 +64,7 @@ const rebuild = async () => {
'ts-node',
'--transpile-only',
path.join(gameSrcPath, 'cli', 'build', 'buildServer.ts'),
buildVersion,
])
build.on('close', resolve)
build.stdout.on('data', (data) =>
Expand Down Expand Up @@ -88,7 +103,11 @@ const rebuild = async () => {
let debounce = false
chokidar
.watch(gameSrcPath, { ignoreInitial: true, persistent: true })
.on('all', (_event, _filename) => {
.on('all', (_event, filename) => {
if (filename.startsWith(webEphemeralPath)) {
return
}

if (debounce) {
return
}
Expand Down
72 changes: 0 additions & 72 deletions src/client/autoReload.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

<body>
<script src="/client/main.js"></script>
<!-- DEV SERVER AUTORELOAD PLACEHOLDER -->
</body>

</html>
6 changes: 3 additions & 3 deletions src/client/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { vec2 } from 'gl-matrix'

import * as autoReload from '~/client/autoReload'
import { Client } from '~/client/Client'
import * as autoReload from '~/web/autoReload'

declare global {
interface Window {
Expand Down Expand Up @@ -46,8 +46,8 @@ document.addEventListener('keyup', (event) => {
}
})

// Ensure auto-reloading for dev
autoReload.init({ enabled: true })
// Start auto-reload polling
autoReload.poll()

// Development-related globals
window.client = client
38 changes: 4 additions & 34 deletions src/server/main.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,16 @@
import * as fs from 'fs'
import * as path from 'path'

import * as hapi from '@hapi/hapi'
import inert from '@hapi/inert'
import * as WebSocket from 'ws'

import {
buildVersionPath,
serverOutputPath,
webEntrypoints,
webOutputPath,
} from '~/cli/build/common'
import { updateEntrypointHtmlForAutoReload } from '~/client/autoReload'
import { serverBuildVersionPath, webOutputPath } from '~/cli/build/common'
import { SIMULATION_PERIOD_S } from '~/constants'
import { ClientConnectionWs } from '~/network/ClientConnection'
import { ServerSim } from '~/server/ServerSim'

async function buildVersion(): Promise<string> {
return (await fs.promises.readFile(buildVersionPath)).toString()
}

async function entrypointTemplate(srcPath: string): Promise<string> {
return (
await fs.promises.readFile(
path.join(serverOutputPath, srcPath, 'index.html'),
)
).toString('utf8')
return (await fs.promises.readFile(serverBuildVersionPath)).toString()
}

async function main(): Promise<void> {
Expand All @@ -52,23 +37,6 @@ async function main(): Promise<void> {

await httpServer.register(inert)

for (const entrypoint of webEntrypoints) {
httpServer.route({
method: 'GET',
path: '/' + entrypoint,
handler: async () => {
const [bv, template] = await Promise.all([
buildVersion(),
entrypointTemplate(entrypoint),
])
return updateEntrypointHtmlForAutoReload({
buildVersion: bv,
html: template,
})
},
})
}

httpServer.route({
method: 'GET',
path: '/',
Expand Down Expand Up @@ -131,12 +99,14 @@ async function main(): Promise<void> {
},
})

// Static assets
httpServer.route({
method: 'GET',
path: '/{param*}',
handler: {
directory: {
path: webOutputPath,
index: 'index.html',
},
},
})
Expand Down
1 change: 0 additions & 1 deletion src/tools/rendertoy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

<body>
<script src="/tools/rendertoy/main.js"></script>
<!-- DEV SERVER AUTORELOAD PLACEHOLDER -->
</body>

</html>
1 change: 1 addition & 0 deletions src/web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ephemeral/
Loading

0 comments on commit d76ad1b

Please sign in to comment.