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