Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 29 additions & 12 deletions src/deploy-web.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,39 @@ const deployWeb = async (config, log) => {
throw new Error(`missing files in ${dist}, maybe you forgot to build your UI ?`)
}

const creds = await getS3Credentials(config)
// Use the same default cache file as TvmClient
const credsCacheFile = (config.s3 && config.s3.credsCacheFile) || '.aws.tmp.creds.json'

const remoteStorage = new RemoteStorage(creds)
const exists = await remoteStorage.folderExists(config.s3.folder + '/')
try {
const creds = await getS3Credentials(config)

if (exists) {
if (log) {
log('warning: an existing deployment will be overwritten')
const remoteStorage = new RemoteStorage(creds)
const exists = await remoteStorage.folderExists(config.s3.folder + '/')

if (exists) {
if (log) {
log('warning: an existing deployment will be overwritten')
}
await remoteStorage.emptyFolder(config.s3.folder + '/')
}
await remoteStorage.emptyFolder(config.s3.folder + '/')
}
const _log = log ? (f) => log(`deploying ${path.relative(dist, f)}`) : null
await remoteStorage.uploadDir(dist, config.s3.folder, config, _log)
const _log = log ? (f) => log(`deploying ${path.relative(dist, f)}`) : null
await remoteStorage.uploadDir(dist, config.s3.folder, config, _log)

const url = `https://${config.ow.namespace}.${config.app.hostname}/index.html`
return url
const url = `https://${config.ow.namespace}.${config.app.hostname}/index.html`
return url
} finally {
// Cleanup TVM credentials cache file
if (credsCacheFile && fs.existsSync(credsCacheFile)) {
try {
fs.removeSync(credsCacheFile)
} catch (err) {
// Ignore cleanup errors - don't fail deployment if cleanup fails
if (log) {
log(`warning: failed to cleanup credentials cache: ${err.message}`)
}
}
}
}
}

module.exports = deployWeb
27 changes: 21 additions & 6 deletions src/undeploy-web.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,36 @@ governing permissions and limitations under the License.

const RemoteStorage = require('../lib/remote-storage')
const getS3Credentials = require('../lib/getS3Creds')
const fs = require('fs-extra')

const undeployWeb = async (config) => {
if (!config || !config.app || !config.app.hasFrontend) {
throw new Error('cannot undeploy web, app has no frontend or config is invalid')
}

const creds = await getS3Credentials(config)
// Use the same default cache file as TvmClient
const credsCacheFile = (config.s3 && config.s3.credsCacheFile) || '.aws.tmp.creds.json'

const remoteStorage = new RemoteStorage(creds)
try {
const creds = await getS3Credentials(config)

if (!(await remoteStorage.folderExists(config.s3.folder + '/'))) {
throw new Error(`cannot undeploy static files, there is no deployment for ${config.s3.folder}`)
}
const remoteStorage = new RemoteStorage(creds)

if (!(await remoteStorage.folderExists(config.s3.folder + '/'))) {
throw new Error(`cannot undeploy static files, there is no deployment for ${config.s3.folder}`)
}

await remoteStorage.emptyFolder(config.s3.folder + '/')
await remoteStorage.emptyFolder(config.s3.folder + '/')
} finally {
// Cleanup TVM credentials cache file
if (credsCacheFile && fs.existsSync(credsCacheFile)) {
try {
fs.removeSync(credsCacheFile)
} catch (err) {
// Ignore cleanup errors - don't fail undeployment if cleanup fails
}
}
}
}

module.exports = undeployWeb
25 changes: 25 additions & 0 deletions test/src/deploy-web.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,29 @@ describe('deploy-web', () => {
expect(folderExists).toHaveBeenLastCalledWith('nsfolder/')
expect(emptyFolder).toHaveBeenLastCalledWith('nsfolder/')
})

test('cleans up credentials cache file', async () => {
fs.removeSync.mockImplementation(() => {})
await deployWeb({ ow: { namespace: 'ns', auth: 'password' }, s3: { credsCacheFile: 'test.json', folder: 'f' }, app: { hasFrontend: true, hostname: 'h' }, web: { distProd: 'dist' } })
expect(fs.removeSync).toHaveBeenCalledWith('test.json')
})

test('logs cleanup errors', async () => {
const log = jest.fn()
fs.removeSync.mockImplementation(() => { throw new Error('fail') })
await deployWeb({ ow: { namespace: 'ns', auth: 'password' }, s3: { credsCacheFile: 'test.json', folder: 'f' }, app: { hasFrontend: true, hostname: 'h' }, web: { distProd: 'dist' } }, log)
expect(log).toHaveBeenCalledWith('warning: failed to cleanup credentials cache: fail')
})

test('ignores cleanup errors without logger', async () => {
fs.removeSync.mockImplementation(() => { throw new Error('fail') })
await expect(deployWeb({ ow: { namespace: 'ns', auth: 'password' }, s3: { credsCacheFile: 'test.json', folder: 'f' }, app: { hasFrontend: true, hostname: 'h' }, web: { distProd: 'dist' } })).resolves.toBeDefined()
})

test('skips cleanup when file missing', async () => {
fs.existsSync.mockImplementation(p => p !== 'test.json')
fs.removeSync.mockClear()
await deployWeb({ ow: { namespace: 'ns', auth: 'password' }, s3: { credsCacheFile: 'test.json', folder: 'f' }, app: { hasFrontend: true, hostname: 'h' }, web: { distProd: 'dist' } })
expect(fs.removeSync).not.toHaveBeenCalled()
})
})
8 changes: 8 additions & 0 deletions test/src/undeploy-web.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,12 @@ describe('undeploy-web', () => {
expect(mockRemoteStorageInstance.folderExists).toHaveBeenCalledWith('somefolder/')
expect(mockRemoteStorageInstance.emptyFolder).not.toHaveBeenCalled()
})

test('cleans up credentials cache file', async () => {
const fs = require('fs-extra')
fs.existsSync = jest.fn().mockReturnValue(true)
fs.removeSync = jest.fn().mockImplementation(() => { throw new Error('fail') })
mockRemoteStorageInstance.folderExists.mockResolvedValue(true)
await expect(undeployWeb({ ow: { namespace: 'ns', auth: 'password' }, s3: { credsCacheFile: 'test.json', folder: 'f' }, app: { hasFrontend: true } })).resolves.toBeUndefined()
})
})