diff --git a/.github/workflows/desktop-macos-branch-build.yml b/.github/workflows/desktop-macos-branch-build.yml index b989b259..ac3ddd97 100644 --- a/.github/workflows/desktop-macos-branch-build.yml +++ b/.github/workflows/desktop-macos-branch-build.yml @@ -11,7 +11,7 @@ jobs: name: Build macOS desktop artifact runs-on: macos-latest permissions: - contents: read + contents: write steps: - name: Checkout uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 @@ -38,6 +38,10 @@ jobs: run: | SAFE_REF="$(printf '%s' "${GITHUB_REF_NAME}" | tr -c 'A-Za-z0-9._-' '-')" echo "name=CloudCLI-macOS-${SAFE_REF}-${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT" + echo "server_bundle_tag=desktop-server-${SAFE_REF}" >> "$GITHUB_OUTPUT" + + - name: Configure branch server bundle source + run: printf '{"releaseTag":"%s"}\n' "${{ steps.artifact.outputs.server_bundle_tag }}" > electron/server-bundle-config.json - name: Verify signing secrets are configured run: | @@ -62,6 +66,20 @@ jobs: APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + - name: Build branch server bundle + run: node scripts/release/build-server-bundle.js + + - name: Publish branch server bundle + uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 + with: + tag_name: ${{ steps.artifact.outputs.server_bundle_tag }} + name: CloudCLI Desktop Server Bundle (${{ github.ref_name }}) + prerelease: true + fail_on_unmatched_files: false + overwrite_files: true + files: | + release/server-bundles/* + - name: Verify macOS artifacts run: | test -n "$(find release -maxdepth 1 -name '*.dmg' -print -quit)" diff --git a/.github/workflows/desktop-windows-branch-build.yml b/.github/workflows/desktop-windows-branch-build.yml index 26a96bcd..ac5e1213 100644 --- a/.github/workflows/desktop-windows-branch-build.yml +++ b/.github/workflows/desktop-windows-branch-build.yml @@ -11,7 +11,7 @@ jobs: name: Build unsigned Windows desktop artifact runs-on: windows-latest permissions: - contents: read + contents: write steps: - name: Checkout uses: actions/checkout@v6 @@ -39,12 +39,31 @@ jobs: run: | SAFE_REF="$(printf '%s' "${GITHUB_REF_NAME}" | tr -c 'A-Za-z0-9._-' '-')" echo "name=CloudCLI-windows-${SAFE_REF}-${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT" + echo "server_bundle_tag=desktop-server-${SAFE_REF}" >> "$GITHUB_OUTPUT" + + - name: Configure branch server bundle source + shell: bash + run: printf '{"releaseTag":"%s"}\n' "${{ steps.artifact.outputs.server_bundle_tag }}" > electron/server-bundle-config.json - name: Build unsigned Windows artifacts run: npm run desktop:dist:win -- --publish never env: CSC_IDENTITY_AUTO_DISCOVERY: "false" + - name: Build branch server bundle + run: node scripts/release/build-server-bundle.js + + - name: Publish branch server bundle + uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 + with: + tag_name: ${{ steps.artifact.outputs.server_bundle_tag }} + name: CloudCLI Desktop Server Bundle (${{ github.ref_name }}) + prerelease: true + fail_on_unmatched_files: false + overwrite_files: true + files: | + release/server-bundles/* + - name: Verify Windows artifacts shell: bash run: | diff --git a/.gitignore b/.gitignore index 270d2eb4..34be0c9b 100755 --- a/.gitignore +++ b/.gitignore @@ -147,6 +147,7 @@ tasks/ # Local desktop packaging artifacts /.desktop-build/ /release/ +/electron/server-bundle-config.json cloudcli-sidebar-app-source.tar.gz cloudcli-sidebar.html electron/*.tar.gz diff --git a/electron/localServer.js b/electron/localServer.js index 4eca2dbd..3761a299 100644 --- a/electron/localServer.js +++ b/electron/localServer.js @@ -180,6 +180,20 @@ async function pathExists(filePath) { } } +async function readServerBundleConfig(appRoot) { + try { + const raw = await fs.readFile(path.join(appRoot, 'electron', 'server-bundle-config.json'), 'utf8'); + const config = JSON.parse(raw); + return { + releaseTag: typeof config.releaseTag === 'string' && config.releaseTag.trim() + ? config.releaseTag.trim() + : '', + }; + } catch { + return { releaseTag: '' }; + } +} + function getServerCwd(appRoot, serverEntry) { const normalizedEntry = path.resolve(serverEntry); const bundledEntry = path.resolve(appRoot, 'dist-server', 'server', 'index.js'); @@ -371,8 +385,10 @@ export class LocalServerController { if (!this.appVersion) { throw new Error('Cannot install local server: app version is unknown.'); } + const bundleConfig = await readServerBundleConfig(this.appRoot); const installer = new ServerInstaller({ version: this.appVersion, + bundleReleaseTag: bundleConfig.releaseTag, onLog: (line) => this.appendStartupLog(line), }); return installer.ensureInstalled(); diff --git a/electron/serverInstaller.js b/electron/serverInstaller.js index 5aab396e..da76752b 100644 --- a/electron/serverInstaller.js +++ b/electron/serverInstaller.js @@ -35,6 +35,7 @@ export class ServerInstaller { arch = process.arch, installRoot = process.env.CLOUDCLI_SERVER_DIR || DEFAULT_INSTALL_ROOT, bundleBaseUrl = process.env.CLOUDCLI_SERVER_BUNDLE_URL || DEFAULT_BUNDLE_BASE_URL, + bundleReleaseTag = process.env.CLOUDCLI_SERVER_BUNDLE_RELEASE_TAG || '', onLog, } = {}) { if (!version) throw new Error('ServerInstaller requires the app version'); @@ -43,6 +44,7 @@ export class ServerInstaller { this.arch = mapArch(arch); this.installRoot = installRoot; this.bundleBaseUrl = bundleBaseUrl.replace(/\/+$/, ''); + this.bundleReleaseTag = bundleReleaseTag || `v${this.version}`; this.onLog = typeof onLog === 'function' ? onLog : () => {}; } @@ -61,7 +63,7 @@ export class ServerInstaller { } getBundleUrl() { - const url = new URL(`${this.bundleBaseUrl}/v${this.version}/${this.getBundleName()}`); + const url = new URL(`${this.bundleBaseUrl}/${this.bundleReleaseTag}/${this.getBundleName()}`); if (url.protocol !== 'https:' && !(url.protocol === 'http:' && LOCAL_DOWNLOAD_HOSTS.has(url.hostname))) { throw new Error(`Refusing unsupported server bundle URL: ${url.toString()}`); }