Enable isolated Capsule Local automatic update testing
This commit is contained in:
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Opt-in Capsule Local automatic-update testing, with a 0.1.0 bootstrap, isolated unsigned test feed, and 0.1.1 update target. Public signing requirements remain enforced.
|
||||||
- Capsule Local 0.1.1 displays “you have updated to 0.1.1” on the dashboard for manual installer upgrade testing.
|
- Capsule Local 0.1.1 displays “you have updated to 0.1.1” on the dashboard for manual installer upgrade testing.
|
||||||
|
|
||||||
- TTP Capsule plugin publishing integration with bounded chunk uploads, JSON-compatible YAML metadata, public artifact verification, and single-range update downloads.
|
- TTP Capsule plugin publishing integration with bounded chunk uploads, JSON-compatible YAML metadata, public artifact verification, and single-range update downloads.
|
||||||
|
|||||||
@ -17,6 +17,8 @@ npm run dev
|
|||||||
|
|
||||||
## Package and update
|
## Package and update
|
||||||
|
|
||||||
|
For automatic-update testing without a signing certificate, use the separate `dist:test-bootstrap` and `dist:test` commands described in [Free automatic-update testing](docs/releases.md#free-automatic-update-testing). Original Local installations require one bootstrap installation; subsequent test updates use the in-app updater.
|
||||||
|
|
||||||
`npm run dist:win` builds a per-user Windows x64 installer at `dist/local/Capsule-Local-0.1.1-x64-Setup.exe` (the filename follows the package version). This unsigned **Capsule Local** build has its own installation and session directory; automatic updates are disabled. To update a 0.1.0 test installation, close Capsule Local and run the 0.1.1 installer under the same Windows account. Reopen it and confirm the dashboard says “you have updated to 0.1.1”; the installer preserves app data. This manual test does not publish to the signed release feed. `npm run pack:win` produces an unpacked app for inspection. `npm test` checks updater behavior and release validation.
|
`npm run dist:win` builds a per-user Windows x64 installer at `dist/local/Capsule-Local-0.1.1-x64-Setup.exe` (the filename follows the package version). This unsigned **Capsule Local** build has its own installation and session directory; automatic updates are disabled. To update a 0.1.0 test installation, close Capsule Local and run the 0.1.1 installer under the same Windows account. Reopen it and confirm the dashboard says “you have updated to 0.1.1”; the installer preserves app data. This manual test does not publish to the signed release feed. `npm run pack:win` produces an unpacked app for inspection. `npm test` checks updater behavior and release validation.
|
||||||
|
|
||||||
Public builds use `npm run release:win`, require a permanent HTTPS update URL and Windows signing identity, and go to `dist/release/`. They download updates in the background and offer **Restart to update**, including before sign-in. Closing the app normally does not install an update. A closed app checks after its next launch.
|
Public builds use `npm run release:win`, require a permanent HTTPS update URL and Windows signing identity, and go to `dist/release/`. They download updates in the background and offer **Restart to update**, including before sign-in. Closing the app normally does not install an update. A closed app checks after its next launch.
|
||||||
|
|||||||
@ -18,8 +18,13 @@ export function validateFeedUrl(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Return a Windows build config; public releases fail closed without signing. */
|
/** Return a Windows build config; public releases fail closed without signing. */
|
||||||
export function createBuildConfig({ release = false, env = process.env } = {}) {
|
export function createBuildConfig({ release = false, testUpdates = false, bootstrap = false, env = process.env } = {}) {
|
||||||
const url = release ? validateFeedUrl(env.CAPSULE_UPDATE_URL) : null
|
if (release && testUpdates) throw new Error('Test updates cannot be combined with a public release.')
|
||||||
|
if (bootstrap && !testUpdates) throw new Error('Bootstrap requires test updates.')
|
||||||
|
const url = testUpdates ? validateFeedUrl(env.CAPSULE_TEST_UPDATE_URL) : release ? validateFeedUrl(env.CAPSULE_UPDATE_URL) : null
|
||||||
|
if (testUpdates && !new URL(url).pathname.endsWith('/capsule/testfeed/')) {
|
||||||
|
throw new Error('Test updates require a separate /capsule/testfeed/ URL.')
|
||||||
|
}
|
||||||
const publisher = env.CAPSULE_PUBLISHER_NAME?.trim()
|
const publisher = env.CAPSULE_PUBLISHER_NAME?.trim()
|
||||||
if (release && !publisher) {
|
if (release && !publisher) {
|
||||||
throw new Error('CAPSULE_PUBLISHER_NAME must match the signing certificate subject CN.')
|
throw new Error('CAPSULE_PUBLISHER_NAME must match the signing certificate subject CN.')
|
||||||
@ -31,7 +36,7 @@ export function createBuildConfig({ release = false, env = process.env } = {}) {
|
|||||||
appId: release ? APP_ID : `${APP_ID}.local`,
|
appId: release ? APP_ID : `${APP_ID}.local`,
|
||||||
productName: release ? 'Capsule' : 'Capsule Local',
|
productName: release ? 'Capsule' : 'Capsule Local',
|
||||||
executableName: release ? 'Capsule' : 'Capsule Local',
|
executableName: release ? 'Capsule' : 'Capsule Local',
|
||||||
directories: { output: release ? 'dist/release' : 'dist/local' },
|
directories: { output: release ? 'dist/release' : testUpdates ? bootstrap ? 'dist/test-bootstrap' : 'dist/test' : 'dist/local' },
|
||||||
files: ['out/**/*', 'package.json'],
|
files: ['out/**/*', 'package.json'],
|
||||||
asar: true,
|
asar: true,
|
||||||
npmRebuild: false,
|
npmRebuild: false,
|
||||||
@ -39,12 +44,13 @@ export function createBuildConfig({ release = false, env = process.env } = {}) {
|
|||||||
extraMetadata: {
|
extraMetadata: {
|
||||||
// Keep the release userData path compatible with the original dev app.
|
// Keep the release userData path compatible with the original dev app.
|
||||||
name: release ? 'capsule' : 'capsule-local',
|
name: release ? 'capsule' : 'capsule-local',
|
||||||
capsuleUpdates: { enabled: release }
|
...(bootstrap ? { version: '0.1.0' } : {}),
|
||||||
|
capsuleUpdates: { enabled: release || testUpdates, testing: testUpdates }
|
||||||
},
|
},
|
||||||
artifactName: release ? 'Capsule-${version}-${arch}-Setup.${ext}' : 'Capsule-Local-${version}-${arch}-Setup.${ext}',
|
artifactName: release || testUpdates ? 'Capsule-${version}-${arch}-Setup.${ext}' : 'Capsule-Local-${version}-${arch}-Setup.${ext}',
|
||||||
win: {
|
win: {
|
||||||
target: [{ target: 'nsis', arch: ['x64'] }],
|
target: [{ target: 'nsis', arch: ['x64'] }],
|
||||||
verifyUpdateCodeSignature: true,
|
verifyUpdateCodeSignature: !testUpdates,
|
||||||
signExecutable: release,
|
signExecutable: release,
|
||||||
...(release ? { signtoolOptions: {
|
...(release ? { signtoolOptions: {
|
||||||
publisherName: publisher,
|
publisherName: publisher,
|
||||||
@ -60,6 +66,6 @@ export function createBuildConfig({ release = false, env = process.env } = {}) {
|
|||||||
runAfterFinish: false,
|
runAfterFinish: false,
|
||||||
shortcutName: release ? 'Capsule' : 'Capsule Local'
|
shortcutName: release ? 'Capsule' : 'Capsule Local'
|
||||||
},
|
},
|
||||||
publish: release ? [{ provider: 'generic', url, channel: 'latest', useMultipleRangeRequest: false }] : null
|
publish: release || testUpdates ? [{ provider: 'generic', url, channel: 'latest', useMultipleRangeRequest: false }] : null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,28 @@ Local builds are unsigned, named **Capsule Local**, and have application ID `com
|
|||||||
|
|
||||||
Public builds use the permanent ID `com.thetempusproject.capsule`, executable `Capsule.exe`, and `%APPDATA%/capsule/session.json`, preserving the original app's session location. Both installers run per user and preserve app data on uninstall. Do not change the public ID, package name, install scope, or data path after shipping without an explicit migration. The current installer uses Electron's default icon; add an approved `build/icon.ico` before public branding is finalized.
|
Public builds use the permanent ID `com.thetempusproject.capsule`, executable `Capsule.exe`, and `%APPDATA%/capsule/session.json`, preserving the original app's session location. Both installers run per user and preserve app data on uninstall. Do not change the public ID, package name, install scope, or data path after shipping without an explicit migration. The current installer uses Electron's default icon; add an approved `build/icon.ico` before public branding is finalized.
|
||||||
|
|
||||||
|
## Free automatic-update testing
|
||||||
|
|
||||||
|
The original Capsule Local 0.1.0 installer cannot check for updates. Install the update-enabled bootstrap once to start a real 0.1.0 to 0.1.1 test:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:CAPSULE_TEST_UPDATE_URL = 'https://ttp.joeykimsey.com/capsule/testfeed/'
|
||||||
|
npm run dist:test-bootstrap
|
||||||
|
npm run dist:test
|
||||||
|
```
|
||||||
|
|
||||||
|
`dist/test-bootstrap/Capsule-0.1.0-x64-Setup.exe` installs as Capsule Local and preserves its existing session location. Close the existing app before installing it. `dist/test/Capsule-0.1.1-x64-Setup.exe` is the update target; do not manually install that target when testing the updater. Both builds use the same Local identity. The dashboard message is hidden in the bootstrap and appears after updating to 0.1.1.
|
||||||
|
|
||||||
|
Test builds explicitly omit Authenticode verification; they rely on HTTPS, the publisher-controlled test feed, and metadata checksums. This is for trusted testers, not public signed releases. Windows can warn or block unsigned executables. The app labels this mode “Unsigned test channel.” Public release builds still require signing and verify publisher signatures. Normal `dist:win` builds still have updates disabled.
|
||||||
|
|
||||||
|
The TTP plugin must include `/capsule/testfeed/` and `/capsule/testupload/` support before publication. Those endpoints use separate storage from the public release channel, with the existing dedicated publisher token. After the operator deploys that code, set `CAPSULE_UPLOAD_URL` to `https://ttp.joeykimsey.com/capsule/testupload/` and load `CAPSULE_UPLOAD_TOKEN` securely, then run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
npm run publish:release -- --test-updates --upload
|
||||||
|
```
|
||||||
|
|
||||||
|
The command publishes only `dist/test`, with metadata last. It never publishes the bootstrap or changes the public feed. Open the installed bootstrap, choose **Check for updates**, wait for **Restart to update**, then verify version 0.1.1, the dashboard message, and the preserved connection. Signed production upgrade acceptance remains a separate test.
|
||||||
|
|
||||||
## 2. Configure the release feed
|
## 2. Configure the release feed
|
||||||
|
|
||||||
Set `CAPSULE_UPDATE_URL` in the build environment to a permanent public HTTPS directory ending in `/`. The TTP Capsule plugin is installed on the LAN demo: use `https://ttp.joeykimsey.com/capsule/feed/` for updates and `https://ttp.joeykimsey.com/capsule/upload/` for uploads. These are environment settings, not hardcoded defaults. See the TTP checkout's `docs/capsule-hosting.md` for provisioning and token handoff. The URL must not contain login credentials, a query, or a fragment. The generated `app-update.yml` is baked into the signed application; site selection, login, and IPC cannot change it.
|
Set `CAPSULE_UPDATE_URL` in the build environment to a permanent public HTTPS directory ending in `/`. The TTP Capsule plugin is installed on the LAN demo: use `https://ttp.joeykimsey.com/capsule/feed/` for updates and `https://ttp.joeykimsey.com/capsule/upload/` for uploads. These are environment settings, not hardcoded defaults. See the TTP checkout's `docs/capsule-hosting.md` for provisioning and token handoff. The URL must not contain login credentials, a query, or a fragment. The generated `app-update.yml` is baked into the signed application; site selection, login, and IPC cannot change it.
|
||||||
|
|||||||
@ -14,6 +14,8 @@
|
|||||||
"smoke": "electron scripts/smoke.cjs",
|
"smoke": "electron scripts/smoke.cjs",
|
||||||
"pack:win": "npm run build && node scripts/package.mjs --dir",
|
"pack:win": "npm run build && node scripts/package.mjs --dir",
|
||||||
"dist:win": "npm run build && node scripts/package.mjs",
|
"dist:win": "npm run build && node scripts/package.mjs",
|
||||||
|
"dist:test": "npm run build && node scripts/package.mjs --test-updates",
|
||||||
|
"dist:test-bootstrap": "npm run build && node scripts/package.mjs --test-updates --bootstrap",
|
||||||
"prerelease:win": "node scripts/invalidate-release.mjs",
|
"prerelease:win": "node scripts/invalidate-release.mjs",
|
||||||
"release:win": "npm test && npm run build && node scripts/package.mjs --release",
|
"release:win": "npm test && npm run build && node scripts/package.mjs --release",
|
||||||
"publish:release": "node scripts/publish.mjs"
|
"publish:release": "node scripts/publish.mjs"
|
||||||
|
|||||||
@ -4,10 +4,13 @@ import { createBuildConfig } from '../build/config.mjs'
|
|||||||
import { writeReleaseManifest } from './releaseArtifacts.mjs'
|
import { writeReleaseManifest } from './releaseArtifacts.mjs'
|
||||||
|
|
||||||
const args = process.argv.slice(2)
|
const args = process.argv.slice(2)
|
||||||
if (args.some((arg) => !['--release', '--dir'].includes(arg))) {
|
if (args.some((arg) => !['--release', '--dir', '--test-updates', '--bootstrap'].includes(arg))) {
|
||||||
throw new Error('Usage: node scripts/package.mjs [--release] [--dir]')
|
throw new Error('Usage: node scripts/package.mjs [--release | --test-updates [--bootstrap]] [--dir]')
|
||||||
}
|
}
|
||||||
const release = args.includes('--release')
|
const release = args.includes('--release')
|
||||||
|
const testUpdates = args.includes('--test-updates')
|
||||||
|
const bootstrap = args.includes('--bootstrap')
|
||||||
|
if (testUpdates && args.includes('--dir')) throw new Error('Test update builds must produce an installer.')
|
||||||
if (release && args.includes('--dir')) throw new Error('Release builds must produce an installer.')
|
if (release && args.includes('--dir')) throw new Error('Release builds must produce an installer.')
|
||||||
// Never let a failed rebuild leave an older directory looking publishable.
|
// Never let a failed rebuild leave an older directory looking publishable.
|
||||||
if (release) {
|
if (release) {
|
||||||
@ -15,12 +18,13 @@ if (release) {
|
|||||||
} else {
|
} else {
|
||||||
process.env.CSC_IDENTITY_AUTO_DISCOVERY = 'false'
|
process.env.CSC_IDENTITY_AUTO_DISCOVERY = 'false'
|
||||||
}
|
}
|
||||||
const config = createBuildConfig({ release })
|
const config = createBuildConfig({ release, testUpdates, bootstrap })
|
||||||
|
if (testUpdates) await rm(`${config.directories.output}/release-ready.json`, { force: true })
|
||||||
await build({
|
await build({
|
||||||
config,
|
config,
|
||||||
targets: Platform.WINDOWS.createTarget(args.includes('--dir') ? ['dir'] : ['nsis'], Arch.x64),
|
targets: Platform.WINDOWS.createTarget(args.includes('--dir') ? ['dir'] : ['nsis'], Arch.x64),
|
||||||
publish: 'never'
|
publish: 'never'
|
||||||
})
|
})
|
||||||
if (release) {
|
if (release || testUpdates) {
|
||||||
await writeReleaseManifest(config.directories.output, config.publish[0].url)
|
await writeReleaseManifest(config.directories.output, config.publish[0].url)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,13 @@ import { verifyRelease } from './releaseArtifacts.mjs'
|
|||||||
import { uploadRelease } from './uploadRelease.mjs'
|
import { uploadRelease } from './uploadRelease.mjs'
|
||||||
|
|
||||||
const args = process.argv.slice(2)
|
const args = process.argv.slice(2)
|
||||||
if (args.some((arg) => arg !== '--upload')) throw new Error('Usage: npm run publish:release -- [--upload]')
|
if (args.some((arg) => !['--upload', '--test-updates'].includes(arg))) throw new Error('Usage: npm run publish:release -- [--test-updates] [--upload]')
|
||||||
const release = await verifyRelease('dist/release')
|
const testing = args.includes('--test-updates')
|
||||||
|
const release = await verifyRelease(testing ? 'dist/test' : 'dist/release')
|
||||||
|
if (testing && (!new URL(release.feedUrl).pathname.endsWith('/capsule/testfeed/') ||
|
||||||
|
(args.includes('--upload') && !new URL(process.env.CAPSULE_UPLOAD_URL).pathname.endsWith('/capsule/testupload/')))) {
|
||||||
|
throw new Error('Test publication requires the separate testfeed and testupload endpoints.')
|
||||||
|
}
|
||||||
console.log(`Capsule ${release.version}: ${release.feedUrl}`)
|
console.log(`Capsule ${release.version}: ${release.feedUrl}`)
|
||||||
if (!args.includes('--upload')) {
|
if (!args.includes('--upload')) {
|
||||||
console.log('Verified. Upload order:')
|
console.log('Verified. Upload order:')
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/** Own update state and retries without exposing updater options to the renderer. */
|
/** Own update state and retries without exposing updater options to the renderer. */
|
||||||
export function createUpdateController({ updater, version, enabled, onState = () => {},
|
export function createUpdateController({ updater, version, enabled, testing = false, onState = () => {},
|
||||||
schedule = setTimeout, cancel = clearTimeout, firstDelay = 15000, interval = 4 * 60 * 60 * 1000 }) {
|
schedule = setTimeout, cancel = clearTimeout, firstDelay = 15000, interval = 4 * 60 * 60 * 1000 }) {
|
||||||
let state = { status: enabled ? 'idle' : 'disabled', currentVersion: version, version: null, percent: 0 }
|
let state = { status: enabled ? 'idle' : 'disabled', currentVersion: version, version: null, percent: 0, testing }
|
||||||
let timer
|
let timer
|
||||||
let busy = false
|
let busy = false
|
||||||
let disposed = false
|
let disposed = false
|
||||||
|
|||||||
@ -8,15 +8,18 @@ import { registerUpdateIpc, isTrustedUpdateSender } from './updateIpc.mjs'
|
|||||||
/** Start updates independently of site sign-in, using only packaged release config. */
|
/** Start updates independently of site sign-in, using only packaged release config. */
|
||||||
export function registerUpdates(rendererUrl) {
|
export function registerUpdates(rendererUrl) {
|
||||||
let enabled = false
|
let enabled = false
|
||||||
|
let testing = false
|
||||||
if (app.isPackaged && process.platform === 'win32') {
|
if (app.isPackaged && process.platform === 'win32') {
|
||||||
const metadata = JSON.parse(readFileSync(join(app.getAppPath(), 'package.json'), 'utf8'))
|
const metadata = JSON.parse(readFileSync(join(app.getAppPath(), 'package.json'), 'utf8'))
|
||||||
enabled = metadata.capsuleUpdates?.enabled === true
|
enabled = metadata.capsuleUpdates?.enabled === true
|
||||||
|
testing = metadata.capsuleUpdates?.testing === true && metadata.name === 'capsule-local'
|
||||||
}
|
}
|
||||||
const { autoUpdater } = electronUpdater
|
const { autoUpdater } = electronUpdater
|
||||||
const controller = createUpdateController({
|
const controller = createUpdateController({
|
||||||
updater: autoUpdater,
|
updater: autoUpdater,
|
||||||
version: app.getVersion(),
|
version: app.getVersion(),
|
||||||
enabled,
|
enabled,
|
||||||
|
testing,
|
||||||
onState(state) {
|
onState(state) {
|
||||||
const windows = BrowserWindow.getAllWindows()
|
const windows = BrowserWindow.getAllWindows()
|
||||||
for (const window of windows) {
|
for (const window of windows) {
|
||||||
|
|||||||
@ -352,7 +352,7 @@
|
|||||||
<div class="m-2 m-lg-4">
|
<div class="m-2 m-lg-4">
|
||||||
<div class="col-12 col-lg-10 mx-auto p-4 rounded shadow-sm context-main-bg">
|
<div class="col-12 col-lg-10 mx-auto p-4 rounded shadow-sm context-main-bg">
|
||||||
<h1 class="h3">Dashboard</h1>
|
<h1 class="h3">Dashboard</h1>
|
||||||
<p class="alert alert-success" id="home-update-notice">you have updated to 0.1.1</p>
|
<p class="alert alert-success" id="home-update-notice" hidden>you have updated to 0.1.1</p>
|
||||||
<p class="text-muted" id="home-note"></p>
|
<p class="text-muted" id="home-note"></p>
|
||||||
<p class="mb-0">
|
<p class="mb-0">
|
||||||
Connected to <a id="home-site" href="#" target="_blank" rel="noreferrer"></a>
|
Connected to <a id="home-site" href="#" target="_blank" rel="noreferrer"></a>
|
||||||
|
|||||||
@ -16,7 +16,8 @@ function renderUpdate(state) {
|
|||||||
installing: 'Restarting to install the update…',
|
installing: 'Restarting to install the update…',
|
||||||
error: 'Could not update Capsule. You can keep working and try again.'
|
error: 'Could not update Capsule. You can keep working and try again.'
|
||||||
}
|
}
|
||||||
label.textContent = `Capsule ${state.currentVersion} · ${messages[state.status] || messages.error}`
|
label.textContent = `Capsule ${state.currentVersion}${state.testing ? ' · Unsigned test channel' : ''} · ${messages[state.status] || messages.error}`
|
||||||
|
document.getElementById('home-update-notice').hidden = state.currentVersion !== '0.1.1'
|
||||||
check.hidden = state.status === 'disabled'
|
check.hidden = state.status === 'disabled'
|
||||||
check.disabled = ['checking', 'downloading', 'ready', 'installing'].includes(state.status)
|
check.disabled = ['checking', 'downloading', 'ready', 'installing'].includes(state.status)
|
||||||
install.hidden = state.status !== 'ready'
|
install.hidden = state.status !== 'ready'
|
||||||
|
|||||||
@ -37,6 +37,28 @@ test('local installer has separate identity, no feed, and cannot wipe app data',
|
|||||||
assert.equal(config.nsis.deleteAppDataOnUninstall, false)
|
assert.equal(config.nsis.deleteAppDataOnUninstall, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('unsigned updates require explicit isolated test mode and retain the Local install identity', () => {
|
||||||
|
const testEnv = { CAPSULE_TEST_UPDATE_URL: 'https://ttp.joeykimsey.com/capsule/testfeed/' }
|
||||||
|
assert.throws(() => createBuildConfig({ testUpdates: true, env: {} }), /HTTPS/)
|
||||||
|
assert.throws(() => createBuildConfig({ release: true, testUpdates: true, env: testEnv }), /combined/)
|
||||||
|
assert.throws(() => createBuildConfig({ bootstrap: true }), /Bootstrap/)
|
||||||
|
assert.throws(() => createBuildConfig({ testUpdates: true, env: { CAPSULE_TEST_UPDATE_URL: feed } }), /separate/)
|
||||||
|
const local = createBuildConfig({ env: {} })
|
||||||
|
const testBuild = createBuildConfig({ testUpdates: true, env: testEnv })
|
||||||
|
assert.equal(testBuild.appId, local.appId)
|
||||||
|
assert.equal(testBuild.extraMetadata.name, local.extraMetadata.name)
|
||||||
|
assert.equal(testBuild.extraMetadata.capsuleUpdates.enabled, true)
|
||||||
|
assert.equal(testBuild.extraMetadata.capsuleUpdates.testing, true)
|
||||||
|
assert.equal(testBuild.win.verifyUpdateCodeSignature, false)
|
||||||
|
assert.equal(testBuild.forceCodeSigning, false)
|
||||||
|
assert.equal(testBuild.publish[0].url, testEnv.CAPSULE_TEST_UPDATE_URL)
|
||||||
|
assert.equal(testBuild.nsis.deleteAppDataOnUninstall, false)
|
||||||
|
const bootstrap = createBuildConfig({ testUpdates: true, bootstrap: true, env: testEnv })
|
||||||
|
assert.equal(bootstrap.extraMetadata.version, '0.1.0')
|
||||||
|
assert.notEqual(bootstrap.directories.output, testBuild.directories.output)
|
||||||
|
assert.equal(createBuildConfig({ release: true, env }).win.verifyUpdateCodeSignature, true)
|
||||||
|
})
|
||||||
|
|
||||||
test('release verification detects tampering and publishes metadata last', async (t) => {
|
test('release verification detects tampering and publishes metadata last', async (t) => {
|
||||||
const directory = await mkdtemp(join(tmpdir(), 'capsule-release-test-'))
|
const directory = await mkdtemp(join(tmpdir(), 'capsule-release-test-'))
|
||||||
t.after(() => rm(directory, { recursive: true, force: true }))
|
t.after(() => rm(directory, { recursive: true, force: true }))
|
||||||
|
|||||||
Reference in New Issue
Block a user