diff --git a/.github/workflows/build-linux-deb-arm64.yml b/.github/workflows/build-linux-deb-arm64.yml new file mode 100644 index 00000000..4d16a3ed --- /dev/null +++ b/.github/workflows/build-linux-deb-arm64.yml @@ -0,0 +1,82 @@ +name: Build electron application as deb package on linux (arm64) + +on: + workflow_call: + secrets: + DOCKER_USERNAME: + required: true + DOCKER_PASSWORD: + required: true + token: + required: true + +jobs: + + build-linux-deb-arm64: + runs-on: ubuntu-latest + env: + DOCKER_IMAGE: krux-installer-deb + DOCKER_TARGET_PLATFORM: ubuntu/arm64/v8 + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + + steps: + - uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Setup variables + id: setup + run: | + echo "docker-platform=${DOCKER_TARGET_PLATFORM}" >> $GITHUB_OUTPUT + echo "docker-image=${DOCKER_IMAGE}/${DOCKER_TARGET_PLATFORM}" >> $GITHUB_OUTPUT + echo "docker-version=${GITHUB_RUN_NUMBER}" >> $GITHUB_OUTPUT + KRUX_VERSION=`node -e "console.log(require('./package.json').version)"` + echo "app-version=$KRUX_VERSION" >> $GITHUB_OUTPUT + echo "app-name=krux-installer_${KRUX_VERSION}_arm64" >> $GITHUB_OUTPUT + + - name: Docker Login + run: echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin + + - name: Create release folder + run: mkdir -p ./release/${{ steps.setup.outputs.app-version }} + + - name: Run Buildx + run: | + docker buildx build \ + --file ./dockerfiles/deb/Dockerfile \ + --platform ${{ steps.setup.outputs.docker-platform }} \ + --tag ${{ steps.setup.outputs.docker-image }}:${{ steps.setup.outputs.docker-version }} \ + --output type=local,dest=./release . + + - name: Hash electron app + uses: qlrd/sha256sum-action@v2 + with: + working-directory: release/${{ steps.setup.outputs.app-version }} + file: ${{ steps.setup.outputs.app-name }}.deb + ext: sha256.txt + + - name: List releases + run: ls ./release/${{ steps.setup.outputs.app-version }} + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + if: ${{ github.ref_name == 'main' }} + with: + name: ${{ runner.os }}-${{ steps.setup.outputs.app-name }}-deb + retention-days: 5 + path: | + release/${{ steps.setup.outputs.app-version }}/${{ steps.setup.outputs.app-name }}.deb + release/${{ steps.setup.outputs.app-version }}/${{ steps.setup.outputs.app-name }}.deb.sha256.txt diff --git a/.github/workflows/build-linux-deb.yml b/.github/workflows/build-linux-deb.yml index 3c01d68c..6e364831 100644 --- a/.github/workflows/build-linux-deb.yml +++ b/.github/workflows/build-linux-deb.yml @@ -10,7 +10,7 @@ jobs: build-linux: runs-on: ubuntu-latest - + steps: - name: Checkout Git repository diff --git a/.github/workflows/build-linux-rpm-arm64.yml b/.github/workflows/build-linux-rpm-arm64.yml new file mode 100644 index 00000000..e691b04d --- /dev/null +++ b/.github/workflows/build-linux-rpm-arm64.yml @@ -0,0 +1,82 @@ +name: Build electron application as rpm package on linux (arm64) + +on: + workflow_call: + secrets: + DOCKER_USERNAME: + required: true + DOCKER_PASSWORD: + required: true + token: + required: true + +jobs: + + build-linux-rpm-arm64: + runs-on: ubuntu-latest + env: + DOCKER_IMAGE: krux-installer-rpm + DOCKER_TARGET_PLATFORM: ubuntu/arm64/v8 + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + + steps: + - uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Setup variables + id: setup + run: | + echo "docker-platform=${DOCKER_TARGET_PLATFORM}" >> $GITHUB_OUTPUT + echo "docker-image=${DOCKER_IMAGE}/${DOCKER_TARGET_PLATFORM}" >> $GITHUB_OUTPUT + echo "docker-version=${GITHUB_RUN_NUMBER}" >> $GITHUB_OUTPUT + KRUX_VERSION=`node -e "console.log(require('./package.json').version)"` + echo "app-version=$KRUX_VERSION" >> $GITHUB_OUTPUT + echo "app-name=krux-installer-${KRUX_VERSION}.aarch64" >> $GITHUB_OUTPUT + + - name: Docker Login + run: echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin + + - name: Create release folder + run: mkdir -p ./release/${{ steps.setup.outputs.app-version }} + + - name: Run Buildx + run: | + docker buildx build \ + --file ./dockerfiles/rpm/Dockerfile \ + --platform ${{ steps.setup.outputs.docker-platform }} \ + --tag ${{ steps.setup.outputs.docker-image }}:${{ steps.setup.outputs.docker-version }} \ + --output type=local,dest=./release . + + - name: Hash electron app + uses: qlrd/sha256sum-action@v2 + with: + working-directory: release/${{ steps.setup.outputs.app-version }} + file: ${{ steps.setup.outputs.app-name }}.rpm + ext: sha256.txt + + - name: List releases + run: ls ./release/${{ steps.setup.outputs.app-version }} + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + if: ${{ github.ref_name == 'main' }} + with: + name: ${{ runner.os }}-${{ steps.setup.outputs.app-name }}-rpm + retention-days: 5 + path: | + release/${{ steps.setup.outputs.app-version }}/${{ steps.setup.outputs.app-name }}.rpm + release/${{ steps.setup.outputs.app-version }}/${{ steps.setup.outputs.app-name }}.rpm.sha256.txt diff --git a/.github/workflows/build-linux-rpm.yml b/.github/workflows/build-linux-rpm.yml new file mode 100644 index 00000000..b5824849 --- /dev/null +++ b/.github/workflows/build-linux-rpm.yml @@ -0,0 +1,76 @@ +name: Build electron application as rpm package on linux + +on: + workflow_call: + secrets: + token: + required: true + +jobs: + + build-linux: + runs-on: ubuntu-latest + + steps: + + - name: Checkout Git repository + uses: actions/checkout@v3 + + - name: Install RPM dependencies + run: sudo apt-get install rpm + + - name: Install node + uses: actions/setup-node@v3 + with: + node-version: "20.10.0" + + - name: Variables helpers + id: setup + run: | + KRUX_VERSION=`node -e "console.log(require('./package.json').version)"` + echo "app-version=$KRUX_VERSION" >> $GITHUB_OUTPUT + KRUX_NAME=krux-installer + echo "app-name=${KRUX_NAME}-${KRUX_VERSION}.x86_64" >> $GITHUB_OUTPUT + echo "::group::Variables" + echo "app-version=$KRUX_VERSION" + echo "app-name=$KRUX_NAME" + echo "::endgroup::" + + - name: Install dependencies + run: yarn install + + - name: Build electron app + env: + GH_TOKEN: ${{ secrets.token }} + run: yarn run build --linux rpm + + - name: Hash electron app (Linux) + uses: qlrd/sha256sum-action@v2 + with: + working-directory: ./release/${{ steps.setup.outputs.app-version }} + file: ${{ steps.setup.outputs.app-name }}.rpm + ext: sha256.txt + + - name: List release files + run: | + echo "::group::Release files" + ls -la release/${{ steps.setup.outputs.app-version }} + echo "::endgroup::" + + - name: Install xvfb-maybe to allow headless test + run: yarn add --dev xvfb-maybe + + - name: E2E test electron app + env: + DEBUG: 'krux:*' + run: ./node_modules/.bin/xvfb-maybe ./node_modules/.bin/wdio run wdio.conf.mts + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + if: ${{ github.ref_name == 'main' }} + with: + name: ${{ runner.os }}-${{ steps.setup.outputs.app-name }}-rpm + retention-days: 5 + path: | + release/${{ steps.setup.outputs.app-version }}/${{ steps.setup.outputs.app-name }}.rpm + release/${{ steps.setup.outputs.app-version }}/${{ steps.setup.outputs.app-name }}.rpm.sha256.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a57a4156..fc25b6e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,25 @@ jobs: secrets: token: ${{ secrets.github_token }} + #build-linux-deb-arm64: + # uses: ./.github/workflows/build-linux-deb-arm64.yml + # secrets: + # token: ${{ secrets.github_token }} + # DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + # DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + + #build-linux-rpm-arm64: + # uses: ./.github/workflows/build-linux-rpm-arm64.yml + # secrets: + # token: ${{ secrets.github_token }} + # DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + # DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + + build-linux-rpm: + uses: ./.github/workflows/build-linux-rpm.yml + secrets: + token: ${{ secrets.github_token }} + build-windows-nsis: uses: ./.github/workflows/build-windows-nsis.yml secrets: diff --git a/dockerfiles/deb/Dockerfile b/dockerfiles/deb/Dockerfile new file mode 100644 index 00000000..b9b0caef --- /dev/null +++ b/dockerfiles/deb/Dockerfile @@ -0,0 +1,28 @@ +FROM arm64v8/node AS build-stage +ENV NODE_ENV "test" +ENV NODE_DOCKER true +ENV DEBUG "krux:*" +ENV USE_SYSTEM_FPM true +ENV DOCUMENTS /app/Documents + +ADD . /app +WORKDIR /app +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y \ + libopenjp2-tools \ + ruby \ + ruby-dev + +RUN gem install fpm +RUN yarn install +RUN yarn run build --arm64 --linux deb +#RUN apt-get install --fix-missing -y xvfb +#RUN yarn add --dev xvfb-maybe +#RUN node ./node_modules/.bin/xvfb-maybe \ +# ./node_modules/.bin/wdio \ +# run \ +# wdio.conf.mts + +FROM scratch AS export-stage +COPY --from=build-stage /app/release / diff --git a/dockerfiles/rpm/Dockerfile b/dockerfiles/rpm/Dockerfile new file mode 100644 index 00000000..422debc0 --- /dev/null +++ b/dockerfiles/rpm/Dockerfile @@ -0,0 +1,29 @@ +FROM arm64v8/node AS build-stage +ENV NODE_ENV "test" +ENV NODE_DOCKER true +ENV DEBUG "krux:*" +ENV USE_SYSTEM_FPM true +ENV DOCUMENTS /app/Documents + +ADD . /app +WORKDIR /app +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y \ + libopenjp2-tools \ + ruby \ + ruby-dev \ + rpm + +RUN gem install fpm +RUN yarn install +RUN yarn run build --arm64 --linux rpm +#RUN apt-get install --fix-missing -y xvfb +#RUN yarn add --dev xvfb-maybe +#RUN node ./node_modules/.bin/xvfb-maybe \ +# ./node_modules/.bin/wdio \ +# run \ +# wdio.conf.mts + +FROM scratch AS export-stage +COPY --from=build-stage /app/release / diff --git a/electron-builder.json5 b/electron-builder.json5 index 065c71bb..c34cceea 100644 --- a/electron-builder.json5 +++ b/electron-builder.json5 @@ -26,13 +26,14 @@ "category": "Utility", "icon": "public/icon.png", "desktop": { - "Icon": "org.selfcustody.krux-installer", + "Icon": "/usr/share/icons/hicolor/0x0/apps/krux-installer.png", "Keywords": "electron;krux;vite;vuetify;vue3;vue", "Terminal": false }, - "target": [ "AppImage" ] + "target": ["AppImage", "deb", "rpm"] }, "win": { + "icon": "public/icon.png", "target": [ { "target": "nsis", diff --git a/electron/main/index.ts b/electron/main/index.ts index 1513727a..9276d7e1 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -13,6 +13,7 @@ import StoreGetHandler from '../../lib/store-get' import VerifyOpensslHandler from '../../lib/verify-openssl' import CheckIfItWillFlashHandler from '../../lib/check-if-it-will-flash' import FlashHandler from '../../lib/flash' +import QuitHandler from '../../lib/quit' const { version } = createRequire(import.meta.url)('../../package.json') const kruxInstaller = new App(`KruxInstaller | v${version}`) @@ -71,10 +72,14 @@ kruxInstaller.start(async ({ app, win, ipcMain}) => { const checkIfItWillFlashHandler = new CheckIfItWillFlashHandler(win, app.store, ipcMain) checkIfItWillFlashHandler.build() - // Create flash' handler + // Create 'flash' handler const flashHandler = new FlashHandler(win, app.store, ipcMain) flashHandler.build() + // Create 'quit' handler + const quitHandler = new QuitHandler(win, app.store, ipcMain) + quitHandler.build() + // Create Wdio test handlers // if environment variable WDIO_ELECTRON equals 'true' if (process.env.NODE_ENV === 'test') { diff --git a/lib/flash.ts b/lib/flash.ts index a6728e59..675604a2 100644 --- a/lib/flash.ts +++ b/lib/flash.ts @@ -140,6 +140,15 @@ export default class FlashHandler extends Handler { flasher.stdout.on('data', (data: any) => { output = Buffer.from(data, 'utf-8').toString() + if (output.match(/\[ERROR\].*/g)) { + output = output.replace("\x1b[31m", "") + output = output.replace("\x1b[1m", "") + output = output.replace("\x1b[0m", "") + output = output.replace("\x1b[32m", "") + output = output.replace("\x1b[0m \n", "") + output = output.replace("[ERROR]", "") + err = new Error(output) + } this.send(`${this.name}:data`, output) }) @@ -151,7 +160,7 @@ export default class FlashHandler extends Handler { flasher.on('close', (code: any) => { if (err) { - this.send(`${this.name}:error`, { name: err.name, message: err.message, stack: err.stack }) + this.send(`${this.name}:error`, { done: false , name: err.name, message: err.message, stack: err.stack }) } else { this.send(`${this.name}:success`, { done: true }) } diff --git a/lib/quit.ts b/lib/quit.ts new file mode 100644 index 00000000..f7e0577c --- /dev/null +++ b/lib/quit.ts @@ -0,0 +1,49 @@ +/// + +import { app } from 'electron' +import ElectronStore from 'electron-store' +import Handler from './handler' + +export default class QuitHandler extends Handler { + + constructor (win: Electron.BrowserWindow, storage: ElectronStore, ipcMain: Electron.IpcMain) { + super('krux:quit', win, storage, ipcMain); + } + + /** + * Builds a `handle` method for `ipcMain` to be called + * with `invoke` method in `ipcRenderer`. + * + * @example + * ``` + * // change some key in store + * // some keys are forbidden to change + * // https://api.github.com/repos/selfcustody/krux/git/refs/tags + * methods: { + * async download () { + * await window.api.invoke('krux:quit') + * + * window.api.onSuccess('krux:quit', function(_, isChanged) { + * // ... do something + * }) + * + * window.api.onError('krux:quit', function(_, error) { + * // ... do something + * }) + * } + * } + * + * ``` + */ + build () { + super.build(async (options) => { + try { + this.send(`${this.name}:success`, '👋 Quiting krux-installer...') + app.quit() + process.exit(0) + } catch (error) { + this.send(`${this.name}:error`, { name: error.name, stack: error.stack, message: error.message }) + } + }) + } +} diff --git a/package.json b/package.json index c2129c83..8c538e21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "krux-installer", - "version": "0.0.1", + "version": "0.0.11", "main": "dist-electron/main/index.js", "description": "Graphical User Interface to download, verify and flash Krux´s firmware on Kendryte K210 hardwares as bitcoin signature devices", "author": "qlrd <106913782+qlrd@users.noreply.github.com>", @@ -59,7 +59,7 @@ "@wdio/mocha-framework": "^8.27.0", "@wdio/spec-reporter": "^8.27.0", "chai": "^4.3.7", - "electron": "^28.1.0", + "electron": "^28.1.1", "electron-builder": "^24.4.0", "glob": "^10.3.3", "markdownlint-cli": "^0.38.0", diff --git a/src/App.vue b/src/App.vue index 2eacbfb3..eb020b9c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -54,7 +54,6 @@ import onKruxFlash from './utils/onKruxFlash'; import onKruxFlashData from './utils/onKruxFlashData'; import onKruxUnzipData from './utils/onKruxUnzipData'; - /** * Reference for which component will be used as showing page */ diff --git a/src/pages/ErrorMsg.vue b/src/pages/ErrorMsg.vue index 7e9a53f4..fbd852a6 100644 --- a/src/pages/ErrorMsg.vue +++ b/src/pages/ErrorMsg.vue @@ -2,31 +2,37 @@ - - - - - - Back - - - + + + + + + Back + + + Quit + + + - \ No newline at end of file + diff --git a/src/pages/FlashToDevice.vue b/src/pages/FlashToDevice.vue index 12b2f39f..92628894 100644 --- a/src/pages/FlashToDevice.vue +++ b/src/pages/FlashToDevice.vue @@ -7,8 +7,10 @@ - {{ !done ? 'Flashing...' : 'Done' }} - {{ !done ? 'Do not unplug device or shutdown computer!' : 'Shutdown your device and unplug it' }} + {{ !done ? 'Flashing...' : 'Do not trust, verify! ' }} + {{ !done ? 'Do not unplug device or shutdown computer!' : 'Before quit:' }} + {{ !done ? '' : '(1) Scroll down the output to check what happened to your device;' }} + {{ !done ? '' : '(2) shutdown your device and unplug it' }}
@@ -17,12 +19,22 @@ - Back - + + + + + Quit + @@ -42,6 +54,7 @@ const props = defineProps<{ const { output, done } = toRefs(props) const allOutput: Ref = ref('') + /** Methods */ @@ -50,6 +63,10 @@ async function backToFn () { await window.api.invoke('krux:store:get', { from: 'FlashToDevice', keys: ['device', 'version', 'os', 'isMac10', 'showFlash'] }) } +async function exitAppFn () { + await window.api.invoke('krux:quit') +} + onMounted(async function () { allOutput.value = allOutput.value.split(' ').splice(0).join('') await window.api.invoke('krux:unzip') diff --git a/test/e2e/specs/002.app-startup.spec.mts b/test/e2e/specs/002.app-startup.spec.mts index 4d739a11..3cd2590b 100644 --- a/test/e2e/specs/002.app-startup.spec.mts +++ b/test/e2e/specs/002.app-startup.spec.mts @@ -22,7 +22,7 @@ describe('KruxInstaller start up', () => { const version = await browser.electron.execute(function (electron) { return electron.app.getVersion() }) - expect(version).to.be.equal('0.0.1') + expect(version).to.be.equal('0.0.11') }) }) diff --git a/test/e2e/specs/039-verified-official-release.spec.mts b/test/e2e/specs/039-verified-official-release.spec.mts index 4daf8633..7f35111b 100644 --- a/test/e2e/specs/039-verified-official-release.spec.mts +++ b/test/e2e/specs/039-verified-official-release.spec.mts @@ -187,9 +187,4 @@ describe('KruxInstaller VerifiedOfficialRelease page (show and click back button await instance.verifiedOfficialReleasePage.waitForExist({ reverse: true }) }) - it('should be in Main page', async () => { - await instance.mainPage.waitForExist() - await expect(instance.mainPage).toBeDisplayed() - }) - -}) \ No newline at end of file +}) diff --git a/wdio.conf.mts b/wdio.conf.mts index 706599c9..4a5b25b3 100644 --- a/wdio.conf.mts +++ b/wdio.conf.mts @@ -6,7 +6,7 @@ import { tmpdir, homedir } from 'os' import { createRequire } from 'module' import { osLangSync } from 'os-lang' import createDebug from 'debug' -import { accessSync } from 'fs'; +import { accessSync, readFileSync } from 'fs'; import { exec, execFile } from 'child_process'; const { devDependencies, version } = createRequire(import.meta.url)('./package.json') @@ -18,7 +18,11 @@ const __dirname = dirname(__filename); // for each OS let APP_PATH: string if (process.platform === 'linux') { - APP_PATH = join(__dirname, 'release', version, 'linux-unpacked', 'krux-installer') + if (process.arch === 'arm64') { + APP_PATH = join(__dirname, 'release', version, 'linux-arm64-unpacked', 'krux-installer') + } else { + APP_PATH = join(__dirname, 'release', version, 'linux-unpacked', 'krux-installer') + } } else if (process.platform === 'win32') { APP_PATH = join(__dirname, 'release', version, 'win-unpacked', 'krux-installer.exe') } else if (process.platform === 'darwin') { @@ -58,8 +62,15 @@ if (process.env.CI && process.env.GITHUB_ACTION) { resources = join(home, 'Documents', 'krux-installer') } else if ( lang.match(/pt-*/g)) { resources = join(home, 'Documentos', 'krux-installer') + } else if ( lang.match(/POSIX/) ) { + // Check if is running under docker container (containerized build for arm64) + if (process.env.NODE_DOCKER) { + resources = join(process.env.DOCUMENTS, 'krux-installer') + } else { + throw new Error('Failed to check if is running under docker') + } } else { - throw new Error(`${lang} not implemented. Please implement it with correct \'Documents\' folder name`) + throw new Error(`'${lang}' lang not implemented`) } }