From 16310704f8b35aa01f84f117d733ab6e06dd9e19 Mon Sep 17 00:00:00 2001 From: heapwolf Date: Tue, 30 Apr 2024 13:58:29 +0200 Subject: [PATCH] feature(build-infra): ui to submit builds --- src/components/account.js | 12 +-- src/components/confirm.js | 3 +- src/components/project.js | 2 +- src/components/properties.js | 10 +- src/css/component-confirm.css | 8 +- src/index.js | 188 +++++++++++++++++++++++++--------- 6 files changed, 162 insertions(+), 61 deletions(-) diff --git a/src/components/account.js b/src/components/account.js index 69b9fe0..fd9939a 100644 --- a/src/components/account.js +++ b/src/components/account.js @@ -30,8 +30,6 @@ class DialogAccount extends TonicDialog { return } - console.log('GOT RESPONSE FROM STRIPE PAGE', event.data) - try { const res = await fetch('https://api.socketsupply.co/signup', { method: 'POST', @@ -40,19 +38,19 @@ class DialogAccount extends TonicDialog { body: JSON.stringify(event.data) }) - console.log('SIGNUP RESPONSE', res) - if (res.ok) { - const { data: dataUser } = await this.db.state.get('user') + const app = this.props.parent + const { data: dataUser } = await app.db.state.get('user') dataUser.buildKeys = await res.json() - console.log('dataUser', dataUser) - await this.db.state.put('user', dataUser) + await app.db.state.put('user', dataUser) await this.hide() + return this.resolve({ data: true }) } } catch (err) { if (err.name === "AbortError") { this.resolve({ data: event.data }) await this.hide() + return this.resolve({ err: true }) } console.log(err) } diff --git a/src/components/confirm.js b/src/components/confirm.js index f12e64a..52acf62 100644 --- a/src/components/confirm.js +++ b/src/components/confirm.js @@ -1,3 +1,4 @@ +import Tonic from '@socketsupply/tonic' import { TonicDialog } from '@socketsupply/components/dialog' class DialogConfirm extends TonicDialog { @@ -51,7 +52,7 @@ class DialogConfirm extends TonicDialog {
${title}
- ${message} + ${Tonic.unsafeRawString(message)}
${this.renderCheckbox()}
diff --git a/src/components/project.js b/src/components/project.js index d6e222d..1f5fc80 100644 --- a/src/components/project.js +++ b/src/components/project.js @@ -641,7 +641,7 @@ class AppProject extends Tonic { const fullPath = path.join(dirPath, entry.name) const oldChild = this.getNodeByProperty('id', fullPath, oldState) - if (entry.name === '.git') continue + if (entry.name[0] === '.') continue const child = { id: fullPath, diff --git a/src/components/properties.js b/src/components/properties.js index f26010d..030f4bc 100644 --- a/src/components/properties.js +++ b/src/components/properties.js @@ -99,11 +99,11 @@ class AppProperties extends Tonic { id="build-target" label="Build" > - - - - - + + + + + co.value) + .map(co => co.dataset.arch) + + const invalidMacOS = new Set(['linux', 'win32']) + const invalidWin32 = new Set(['ios', 'darwin', 'linux']) + const invalidLinux = new Set(['ios', 'darwin', 'win32']) + + let needsBuildService = false + + if (process.platform === 'darwin' && architectures.some(s => invalidMacOS.has(s))) needsBuildService = true + if (process.platform === 'win32' && architectures.some(s => invalidWin32.has(s))) needsBuildService = true + if (process.platform === 'linux' && architectures.some(s => invalidLinux.has(s))) needsBuildService = true + + if (needsBuildService) { + const platform = process.platform === 'darwin' ? 'MacOS' : process.platform + const coDialogConfirm = document.querySelector('dialog-confirm') + const result = await coDialogConfirm.prompt({ + type: 'question', + message: ` + You're using ${platform} but you're trying to build for other operating systems or compute architectures. +

+ Do you want to use our Build Service to handle this for you? + `, + buttons: [ + { label: 'cancel', value: 'abandon' }, + { label: 'ok', value: 'consent' } + ] + }) + + if (!result.abandon && !result.consent) return + + if (result.consent) { + // + // Check if the user has an account if not, sign up for one + // + if (!dataUser.buildKeys) { + const coDialogAccount = document.querySelector('dialog-account') + const { err } = await coDialogAccount.prompt() + + if (err) { + await coDialogConfirm.prompt({ + type: 'question', + message: err.message, + buttons: [ + { label: 'ok', value: 'cancel' } + ] + }) + + this.buildProject() + return + } } - } - // - // User has an account. go ahead and submit the request for a build - // - + // + // Apparently tar has been available on windows since v10. Tar it, + // base64 encode it, and add it to the manifest as a payload. + // + const tar = spawn('tar', ['-cvf', '-', this.state.currentProject.id]) + const buffer = Buffer.alloc(0) + + tar.stdout.on('data', data => { + buffer = Buffer.concat([buffer, data]) + }) + + tar.stderr.on('data', data => { + term.error(data.toString()) + }) + + tar.on('close', (code) => { + term.info(`tar process exited with code ${code}`) + }) + + // + // This tells us what architectures you want to build for, what time + // you're trying to send it, your public key and the payload you want built. + // + const manifest = { + architectures, + ctime: Date.now(), + pk: dataUser.buildKeys.pk, + data: buffer.toString('base64') + } + + const bytes = Buffer.from(JSON.stringify(manifest)) + manifest.sig = Encryption.sign(bytes, dataUser.buildKeys.sk) + + try { + const res = await fetch('https://api.socketsupply.co/build', { + method: 'POST', + mode: 'cors', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(manifest) + }) + + if (res.ok) { + const app = this.props.parent + const { data: dataUser } = await app.db.state.get('user') + dataUser.buildKeys = await res.json() + await app.db.state.put('user', dataUser) + await this.hide() + return this.resolve({ data: true }) + } + } catch (err) { + console.log(err) + } + } + return } + + // + // run the build locally. also, it's going to be ready quite + // quickly, so just reveal it when it's ready. + await this.spawnSSC('build') + + const w = await application.getCurrentWindow() + await w.revealFile(this.state.currentProject.id) } - async runSSC (...args) { + async spawnSSC (...args) { + const { promise, resolve } = Promise.withResolvers() const term = document.querySelector('app-terminal') - term.info(`ssc ${args.join(' ')}`) + term.info(`${await this.getBin()} ${args.join(' ')}`) if (this.childprocess && !this.childprocess.killed && this.childprocess.exitCode !== null) { this.childprocess.kill('SIGKILL') @@ -541,7 +632,7 @@ class AppView extends Tonic { term.info('Running new instance of app') const cwd = this.state.currentProject.id const env = { SSC_PARENT_LOG_SOCKET: process.env.SSC_LOG_SOCKET } - const c = this.childprocess = await spawn('ssc', args, { cwd, env }) + const c = this.childprocess = await spawn(await this.getBin(), args, { cwd, env }) c.stdout.on('data', data => { term.writeln(Buffer.from(data).toString().trim()) @@ -554,12 +645,16 @@ class AppView extends Tonic { c.once('exit', (code) => { term.writeln(`OK! ${code}`) this.childprocess = null + resolve() }) c.once('error', (code) => { term.writeln(`NOT OK! ${code}`) this.childprocess = null + resolve() }) + + return promise } async initMenu () { @@ -756,11 +851,11 @@ class AppView extends Tonic { // TODO(@heapwolf) check all checked archs in properties panel - this.runSSC(...args) + await this.spawnSSC(...args) } if (event === 'package') { - this.packageProject() + this.buildProject() } if (event === 'preview-mode') { @@ -886,6 +981,7 @@ class AppView extends Tonic { id="dialog-account" width="70%" height="55%" + parent=${this} >