Skip to content
Snippets Groups Projects
Commit 32b50eb5 authored by richard.petersen's avatar richard.petersen :sailboat:
Browse files

Improve the version detection

parent c6794d7d
No related branches found
No related tags found
No related merge requests found
......@@ -37,7 +37,7 @@ describe('Responses contain custom headers', function () {
it('index.html has version', async function () {
const response = await request(app).get('/index.html')
expect(response.statusCode).to.equal(200)
expect(response.headers.version).to.equal('3038606729')
expect(response.headers.version).to.equal('3215668592')
})
it('javascript file contains dependencies', async function () {
......@@ -45,4 +45,75 @@ describe('Responses contain custom headers', function () {
expect(response.statusCode).to.equal(200)
expect(response.headers.dependencies).to.equal('main.css')
})
describe('with different files', function () {
beforeEach(async function () {
td.reset()
mockConfig({ urls: ['http://ui-server/'] })
mockFetch({
'http://ui-server': {
'/manifest.json': generateSimpleViteManifest({
'index.html': {}
}),
'/index.html': () => new Response('<html><head></head><body>it\'s me</body></html>', { headers: { 'content-type': 'text/html' } })
}
})
app = await mockApp()
})
it('index.html has version', async function () {
const response = await request(app).get('/index.html')
expect(response.statusCode).to.equal(200)
// important here is, that it is different than in the test without meta.json
expect(response.headers.version).to.equal('3961519424')
})
})
describe('with meta.json', function () {
beforeEach(async function () {
td.reset()
mockConfig({ urls: ['http://ui-server/'] })
mockFetch({
'http://ui-server': {
'/manifest.json': generateSimpleViteManifest({
'index.html': {}
}),
'/index.html': () => new Response('<html><head></head><body>it\'s me</body></html>', { headers: { 'content-type': 'text/html' } }),
'/meta.json': { commitSha: '1234567890' }
}
})
app = await mockApp()
})
it('index.html has version', async function () {
const response = await request(app).get('/index.html')
expect(response.statusCode).to.equal(200)
// important here is, that it is different than in the test without meta.json
expect(response.headers.version).to.equal('1487554813')
})
})
describe('with different meta.json', function () {
beforeEach(async function () {
td.reset()
mockConfig({ urls: ['http://ui-server/'] })
mockFetch({
'http://ui-server': {
'/manifest.json': generateSimpleViteManifest({
'index.html': {}
}),
'/index.html': () => new Response('<html><head></head><body>it\'s me</body></html>', { headers: { 'content-type': 'text/html' } }),
'/meta.json': { commitSha: '0987654321' }
}
})
app = await mockApp()
})
it('index.html has version', async function () {
const response = await request(app).get('/index.html')
expect(response.statusCode).to.equal(200)
// important here is, that it is different than in the test without meta.json
expect(response.headers.version).to.equal('319344871')
})
})
})
......@@ -42,7 +42,7 @@ export function createApp () {
const startupCheck = new health.StartupCheck('warmup cache', async function () {
const stopTimer = startUpTimeGauge.startTimer()
try {
const viteManifests = await loadViteManifests()
const viteManifests = await loadViteManifests({ warmUp: false })
// also need to load ox manifests here, to make sure the cache is warm
await getOxManifests()
const deps = viteManifestToDeps(viteManifests)
......
......@@ -6,17 +6,42 @@ import { hash, viteManifestToDeps } from './util.js'
import { logger } from './logger.js'
export const loadViteManifests = (() => {
let cachePromise
let lastManifest
let lastCacheTime
let lastHash
async function getHash () {
await config.load()
const infos = await Promise.all(config.urls.map(async baseUrl => {
try {
const response = await fetch(new URL('meta.json', baseUrl))
if (!response.ok) throw new Error()
const meta = await response.json()
const version = meta.commitSha || meta.buildDate || meta.version
if (!version) throw new Error()
return version
} catch (err) {
logger.debug(`UI container at ${baseUrl} does not have meta.json. Fall back to version hash based on manifest.`)
}
try {
const response = await fetch(new URL('manifest.json', baseUrl))
if (!response.ok) throw new Error()
const manifest = await response.json()
return hash(manifest)
} catch (err) {
logger.error(`Cannot fetch manifest from ${baseUrl}. Version info will not be correct.`)
}
}))
return hash(infos)
}
async function reload () {
await config.load()
// vite manifests contains a set of objects with the vite-manifests
// from the corresponding registered services
const viteManifests = await Promise.all(config.urls.map(async baseUrl => {
// fetch the manifests
const result = await fetch(new URL('/manifest.json', baseUrl))
const result = await fetch(new URL('manifest.json', baseUrl))
if (!result.ok) throw new Error(`Failed to load manifest for url ${result.url} (Status: ${result.status}: ${result.statusText})`)
try {
const manifest = await result.json()
......@@ -32,55 +57,52 @@ export const loadViteManifests = (() => {
}))
// combine all manifests by keys. With duplicates, last wins
const viteManifest = viteManifests.reduce((memo, manifest) => Object.assign(memo, manifest), {})
Object.defineProperty(viteManifest, '__hash__', {
enumerable: false,
writable: true
})
try {
viteManifest.__hash__ = hash(viteManifests)
} catch (err) {
logger.error(`Failed to calculate hash: ${err.message}`)
}
return viteManifest
return viteManifests.reduce((memo, manifest) => Object.assign(memo, manifest), {})
}
return function loadViteManifests ({ useCache = true } = {}) {
return function loadViteManifests ({ useCache = true, warmUp = true } = {}) {
const CACHE_TTL = parseInt(process.env.CACHE_TTL)
const timeElapsed = () => +((+new Date() - lastCacheTime) / 1000).toFixed(2)
if (!cachePromise || useCache === false || +new Date() > lastCacheTime + CACHE_TTL) {
cachePromise = reload()
cachePromise.then(manifests => {
if (!lastManifest || useCache === false || +new Date() > lastCacheTime + CACHE_TTL) {
const promise = (async () => {
const newHash = await getHash()
if (lastManifest && lastHash === newHash) return
const manifest = await reload()
if (useCache) logger.info(`reloaded manifests after ${timeElapsed()} seconds`)
// update cache promise
const newHash = manifests.__hash__
if (newHash !== lastHash) {
if (lastHash) {
const deps = viteManifestToDeps(manifests)
// asynchronously rewarm the cache
fileCache.warmUp(manifests, deps)
}
lastHash = newHash
// cache data
lastHash = newHash
lastManifest = manifest
if (warmUp) {
const deps = viteManifestToDeps(manifest)
// asynchronously rewarm the cache
fileCache.warmUp(manifest, deps)
}
})
Object.defineProperty(manifest, '__hash__', {
enumerable: false,
writable: true,
value: newHash
})
return manifest
})().catch(err => logger.error(`Could not reload manifests: ${err}`))
lastManifest = lastManifest || promise
lastCacheTime = +new Date()
}
return cachePromise
return lastManifest
}
})()
export async function getOxManifests () {
await loadViteManifests().catch(() => {})
export function getOxManifests () {
loadViteManifests()
return fileCache.oxManifests
}
export async function getDependencies () {
// simply catch the error here. This might happen, when one of the UI containers is temporarily not available
await loadViteManifests().catch(() => {})
export function getDependencies () {
loadViteManifests()
return fileCache.dependencies
}
......@@ -90,7 +112,7 @@ export async function getCSSDependenciesFor (file) {
return dependencies.filter(dep => /\.css/i.test(dep))
}
export async function getVersion () {
await loadViteManifests().catch(() => {})
export function getVersion () {
loadViteManifests()
return fileCache.hash
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment