diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c3e7ae96b..bfd72d99f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -6,20 +6,12 @@ on:
- main
jobs:
- version:
+ docker:
runs-on: ubuntu-latest
- outputs:
- version: ${{ steps.ghd.outputs.describe }}
steps:
- name: Git describe
id: ghd
uses: proudust/gh-describe@v2
-
- docker:
- runs-on: ubuntu-latest
- needs:
- - version
- steps:
- name: Login to GHCR
uses: docker/login-action@v3
with:
@@ -37,38 +29,39 @@ jobs:
push: true
tags: ghcr.io/go-vikunja/vikunja:unstable
build-args: |
- RELEASE_VERSION=${{ needs.version.outputs.version }}
+ RELEASE_VERSION=${{ steps.ghd.outputs.describe }}
desktop:
- runs-on: ubuntu-latest
- needs:
- - version
- container:
- image: electronuserland/builder:wine
+ strategy:
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- - name: Set version
- id: version
- run: |
- if [ "${{ github.ref_type }}" = "tag" ]; then
- VERSION="${{ github.ref_name}}"
- else
- VERSION="unstable"
- fi
- echo version=$VERSION >> "$GITHUB_OUTPUT"
+ - name: Git describe
+ id: ghd
+ uses: proudust/gh-describe@v2
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ package_json_file: desktop/package.json
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: pnpm
+ cache-dependency-path: desktop/pnpm-lock.yaml
- name: Build desktop app
working-directory: desktop
run: |
- wget "https://dl.vikunja.io/frontend/vikunja-frontend-${{ steps.version.outputs.version }}.zip"
- unzip "vikunja-frontend-${{ steps.version.outputs.version }}.zip" -d frontend
- sed -i 's/\/api\/v1//g' frontend/index.html
- sed -i "s/\${version}/${{ needs.version.outputs.version }}/g" package.json
- sed -i "s/\"version\": \".*\"/\"version\": \"${{ needs.version.outputs.version }}\"/" package.json
- npm install -g corepack && corepack enable && pnpm config set store-dir .cache/pnpm
pnpm install --fetch-timeout 100000
- pnpm dist --linux --windows
+ # TODO use the built output from a previous frontend build step
+ node build.js "${{ steps.ghd.outputs.describe }}"
- name: Store release as artifact
uses: actions/upload-artifact@v4
with:
- name: desktop-release
- path: desktop/dist/Vikunja-Desktop*
+ name: desktop-release-${{ runner.os }}
+ path: desktop/dist/Vikunja*
diff --git a/desktop/build.js b/desktop/build.js
new file mode 100644
index 000000000..2e1845924
--- /dev/null
+++ b/desktop/build.js
@@ -0,0 +1,105 @@
+// Vikunja is a to-do list application to facilitate your life.
+// Copyright 2018-present Vikunja and contributors. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public Licensee as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public Licensee for more details.
+//
+// You should have received a copy of the GNU Affero General Public Licensee
+// along with this program. If not, see .
+
+const fs = require('fs');
+const path = require('path');
+const https = require('https');
+const { execSync } = require('child_process');
+const unzipper = require('unzipper');
+
+// Helper function to download a file
+async function downloadFile(url, dest) {
+ return new Promise((resolve, reject) => {
+ const file = fs.createWriteStream(dest);
+ https.get(url, (response) => {
+ if (response.statusCode !== 200) {
+ return reject(new Error(`Failed to download file: ${response.statusCode}`));
+ }
+ response.pipe(file);
+ file.on('finish', () => {
+ file.close(resolve);
+ });
+ }).on('error', (err) => {
+ fs.unlink(dest, () => reject(err));
+ });
+ });
+}
+
+// Helper function to unzip a file to a directory
+async function unzipFile(zipPath, destDir) {
+ return fs.createReadStream(zipPath)
+ .pipe(unzipper.Extract({ path: destDir }))
+ .promise();
+}
+
+// Helper function to replace text in a file
+async function replaceTextInFile(filePath, searchValue, replaceValue) {
+ const data = await fs.promises.readFile(filePath, 'utf8');
+ const result = data.replace(searchValue, replaceValue);
+ await fs.promises.writeFile(filePath, result, 'utf8');
+}
+
+// Main function to execute the script steps
+async function main() {
+ const args = process.argv.slice(2);
+ if (args.length === 0) {
+ console.error("Error: Version placeholder argument is required.");
+ console.error("Usage: node build-script.js ");
+ process.exit(1);
+ }
+
+ const versionPlaceholder = args[0];
+ const frontendZipUrl = "https://dl.vikunja.io/frontend/vikunja-frontend-unstable.zip";
+ const zipFilePath = path.resolve(__dirname, 'vikunja-frontend-unstable.zip');
+ const frontendDir = path.resolve(__dirname, 'frontend');
+ const indexFilePath = path.join(frontendDir, 'index.html');
+ const packageJsonPath = path.join(__dirname, 'package.json');
+
+ console.log(`Building version ${versionPlaceholder}`);
+
+ try {
+ console.log('Step 1: Downloading frontend zip...');
+ await downloadFile(frontendZipUrl, zipFilePath);
+
+ console.log('Step 2: Unzipping frontend package...');
+ await unzipFile(zipFilePath, frontendDir);
+
+ console.log('Step 3: Modifying index.html...');
+ await replaceTextInFile(indexFilePath, /\/api\/v1/g, '');
+
+ console.log('Step 4: Updating version in package.json...');
+ await replaceTextInFile(packageJsonPath, /\${version}/g, versionPlaceholder);
+ await replaceTextInFile(
+ packageJsonPath,
+ /"version": ".*"/,
+ `"version": "${versionPlaceholder}"`
+ );
+
+ console.log('Step 5: Installing dependencies and building...');
+ execSync('pnpm dist', { stdio: 'inherit' });
+
+ console.log('All steps completed successfully!');
+ } catch (err) {
+ console.error('An error occurred:', err.message);
+ } finally {
+ // Cleanup the zip file
+ if (fs.existsSync(zipFilePath)) {
+ fs.unlinkSync(zipFilePath);
+ }
+ }
+}
+
+main();
diff --git a/desktop/package.json b/desktop/package.json
index 89f604c65..0b8a232e8 100644
--- a/desktop/package.json
+++ b/desktop/package.json
@@ -1,10 +1,11 @@
{
"name": "vikunja-desktop",
- "version": "0.21.0",
+ "version": "v0.1.0",
"description": "Vikunja's frontend as a standalone desktop application.",
"main": "main.js",
"repository": "https://code.vikunja.io/desktop",
"license": "GPL-3.0-or-later",
+ "packageManager": "pnpm@9.15.4",
"author": {
"email": "maintainers@vikunja.io",
"name": "Vikunja Team"
@@ -52,7 +53,8 @@
},
"devDependencies": {
"electron": "34.0.1",
- "electron-builder": "25.1.8"
+ "electron-builder": "25.1.8",
+ "unzipper": "^0.12.3"
},
"dependencies": {
"connect-history-api-fallback": "2.0.0",
diff --git a/desktop/pnpm-lock.yaml b/desktop/pnpm-lock.yaml
index 45e1559c0..109d5fe17 100644
--- a/desktop/pnpm-lock.yaml
+++ b/desktop/pnpm-lock.yaml
@@ -21,6 +21,9 @@ importers:
electron-builder:
specifier: 25.1.8
version: 25.1.8(electron-builder-squirrel-windows@24.13.3)
+ unzipper:
+ specifier: ^0.12.3
+ version: 0.12.3
packages:
@@ -561,6 +564,9 @@ packages:
resolution: {integrity: sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==}
engines: {node: '>=10'}
+ duplexer2@0.1.4:
+ resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==}
+
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
@@ -1148,6 +1154,9 @@ packages:
engines: {node: ^12.13 || ^14.13 || >=16}
hasBin: true
+ node-int64@0.4.0:
+ resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
+
nopt@6.0.0:
resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@@ -1550,6 +1559,9 @@ packages:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
+ unzipper@0.12.3:
+ resolution: {integrity: sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA==}
+
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -2383,6 +2395,10 @@ snapshots:
dotenv@9.0.2: {}
+ duplexer2@0.1.4:
+ dependencies:
+ readable-stream: 2.3.8
+
eastasianwidth@0.2.0: {}
ee-first@1.1.1: {}
@@ -3074,6 +3090,8 @@ snapshots:
- bluebird
- supports-color
+ node-int64@0.4.0: {}
+
nopt@6.0.0:
dependencies:
abbrev: 1.1.1
@@ -3497,6 +3515,14 @@ snapshots:
unpipe@1.0.0: {}
+ unzipper@0.12.3:
+ dependencies:
+ bluebird: 3.7.2
+ duplexer2: 0.1.4
+ fs-extra: 11.2.0
+ graceful-fs: 4.2.11
+ node-int64: 0.4.0
+
uri-js@4.4.1:
dependencies:
punycode: 2.3.1