deploy: serve desktop installers from container
Some checks failed
CI / verify (push) Has been cancelled
CI / print-rust (push) Has been cancelled

This commit is contained in:
Admin 2026-09-23 21:44:46 +00:00
parent 386ca99889
commit 569d3f140d
7 changed files with 81 additions and 10 deletions

View File

@ -1,7 +1,6 @@
node_modules node_modules
**/node_modules **/node_modules
**/dist **/dist
apps/print
.git .git
.env .env
.env.* .env.*

View File

@ -12,3 +12,4 @@ FIELDLENS_TENANT_ID=fieldlens-production
WEB_ORIGIN=https://fieldlens.example.invalid WEB_ORIGIN=https://fieldlens.example.invalid
FIELDLENS_PRINT_RELEASE_TAG=fieldlens-print-v0.1.0 FIELDLENS_PRINT_RELEASE_TAG=fieldlens-print-v0.1.0
FIELDLENS_PRINT_RELEASE_BASE_URL=https://git.example.invalid/owner/fieldlens/releases/download FIELDLENS_PRINT_RELEASE_BASE_URL=https://git.example.invalid/owner/fieldlens/releases/download
FIELDLENS_PRINT_DOWNLOAD_DIR=downloads

View File

@ -1,3 +1,53 @@
FROM node:24-bookworm AS desktop-build
WORKDIR /workspace
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
clang \
curl \
file \
libayatana-appindicator3-dev \
libssl-dev \
libwebkit2gtk-4.1-dev \
libxdo-dev \
lld \
llvm \
nsis \
patchelf \
pkg-config \
librsvg2-dev \
wget \
&& rm -rf /var/lib/apt/lists/*
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --profile minimal
ENV PATH="/root/.cargo/bin:$PATH"
RUN rustup target add x86_64-pc-windows-msvc \
&& cargo install --locked cargo-xwin
COPY package.json ./
COPY apps/api/package.json apps/api/package.json
COPY apps/web/package.json apps/web/package.json
COPY apps/print/package.json apps/print/package.json
COPY packages/contracts/package.json packages/contracts/package.json
COPY packages/fieldlens-core/package.json packages/fieldlens-core/package.json
RUN npm install
COPY apps/print apps/print
RUN npm run bundle:windows --workspace @fieldlens/print \
&& npm run bundle:deb --workspace @fieldlens/print \
&& mkdir -p /desktop-artifacts \
&& windows_file="$(find apps/print/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis -maxdepth 1 -type f -name '*-setup.exe' -print -quit)" \
&& linux_file="$(find apps/print/src-tauri/target/release/bundle/deb -maxdepth 1 -type f -name '*.deb' -print -quit)" \
&& test -n "$windows_file" \
&& test -n "$linux_file" \
&& cp "$windows_file" /desktop-artifacts/fieldlens-print-windows-x64-setup.exe \
&& cp "$linux_file" /desktop-artifacts/fieldlens-print-linux-x64.deb
FROM node:24-alpine AS build FROM node:24-alpine AS build
WORKDIR /workspace WORKDIR /workspace
@ -29,6 +79,7 @@ WORKDIR /app
ENV NODE_ENV=production ENV NODE_ENV=production
ENV PORT=3000 ENV PORT=3000
ENV STATIC_DIR=/app/web ENV STATIC_DIR=/app/web
ENV FIELDLENS_PRINT_DOWNLOAD_DIR=/app/downloads
COPY --from=build /workspace/package.json /app/package.json COPY --from=build /workspace/package.json /app/package.json
COPY --from=build /workspace/node_modules /app/node_modules COPY --from=build /workspace/node_modules /app/node_modules
@ -38,6 +89,7 @@ COPY --from=build /workspace/migrations /app/migrations
COPY --from=build /workspace/packages/contracts/package.json /app/packages/contracts/package.json COPY --from=build /workspace/packages/contracts/package.json /app/packages/contracts/package.json
COPY --from=build /workspace/packages/contracts/dist /app/packages/contracts/dist COPY --from=build /workspace/packages/contracts/dist /app/packages/contracts/dist
COPY --from=build /workspace/apps/web/dist /app/web COPY --from=build /workspace/apps/web/dist /app/web
COPY --from=desktop-build /desktop-artifacts /app/downloads
USER node USER node
EXPOSE 3000 EXPOSE 3000

View File

@ -13,3 +13,4 @@ WEB_ORIGIN=http://localhost:5173
STATIC_DIR= STATIC_DIR=
FIELDLENS_PRINT_RELEASE_TAG=fieldlens-print-v0.1.0 FIELDLENS_PRINT_RELEASE_TAG=fieldlens-print-v0.1.0
FIELDLENS_PRINT_RELEASE_BASE_URL=https://git.example.invalid/owner/fieldlens/releases/download FIELDLENS_PRINT_RELEASE_BASE_URL=https://git.example.invalid/owner/fieldlens/releases/download
FIELDLENS_PRINT_DOWNLOAD_DIR=downloads

View File

@ -18,6 +18,5 @@ export const config = {
logLevel: process.env.LOG_LEVEL?.trim() || 'info', logLevel: process.env.LOG_LEVEL?.trim() || 'info',
webOrigin: process.env.WEB_ORIGIN?.trim() || 'http://localhost:5173', webOrigin: process.env.WEB_ORIGIN?.trim() || 'http://localhost:5173',
staticDir: process.env.STATIC_DIR?.trim() || '', staticDir: process.env.STATIC_DIR?.trim() || '',
printReleaseTag: process.env.FIELDLENS_PRINT_RELEASE_TAG?.trim() || 'fieldlens-print-v0.1.0', printDownloadDir: process.env.FIELDLENS_PRINT_DOWNLOAD_DIR?.trim() || 'downloads',
printReleaseBaseUrl: process.env.FIELDLENS_PRINT_RELEASE_BASE_URL?.trim() || 'https://git.arrowstack.dev/Admin/fieldlens/releases/download',
} }

View File

@ -1,6 +1,6 @@
import path from 'node:path' import path from 'node:path'
import cors from 'cors' import cors from 'cors'
import express from 'express' import express, { type Response } from 'express'
import helmet from 'helmet' import helmet from 'helmet'
import { FIELDLENS_SCHEMA_VERSION, isSyncRequest, type DeviceRegistration } from '@fieldlens/contracts' import { FIELDLENS_SCHEMA_VERSION, isSyncRequest, type DeviceRegistration } from '@fieldlens/contracts'
import { getAccessRequest, listAccessRequests, requestAccess, reviewAccessRequest } from './access.js' import { getAccessRequest, listAccessRequests, requestAccess, reviewAccessRequest } from './access.js'
@ -61,14 +61,33 @@ app.get('/api/health/live', (_req, res) => {
res.json({ status: 'live', version: process.env.FIELDLENS_VERSION ?? 'dev' }) res.json({ status: 'live', version: process.env.FIELDLENS_VERSION ?? 'dev' })
}) })
const desktopDownloadDir = path.resolve(config.printDownloadDir)
function sendDesktopDownload(res: Response, filename: string) {
const file = path.join(desktopDownloadDir, filename)
res.setHeader('Cache-Control', 'public, max-age=300')
res.download(file, filename, (error) => {
if (!error || res.headersSent) return
log('error', 'desktop_download_missing', { filename, file })
res.status(404).json({ error: 'desktop_download_unavailable' })
})
}
app.get('/downloads/fieldlens-print-windows-x64-setup.exe', (_req, res) => {
sendDesktopDownload(res, 'fieldlens-print-windows-x64-setup.exe')
})
app.get('/downloads/fieldlens-print-linux-x64.deb', (_req, res) => {
sendDesktopDownload(res, 'fieldlens-print-linux-x64.deb')
})
// Backward-compatible aliases used by the first downloads page.
app.get('/downloads/fieldlens-print/windows-x64', (_req, res) => { app.get('/downloads/fieldlens-print/windows-x64', (_req, res) => {
res.setHeader('Cache-Control', 'no-store') sendDesktopDownload(res, 'fieldlens-print-windows-x64-setup.exe')
res.redirect(302, `${config.printReleaseBaseUrl.replace(/\/$/, '')}/${encodeURIComponent(config.printReleaseTag)}/fieldlens-print-windows-x64-setup.exe`)
}) })
app.get('/downloads/fieldlens-print/linux-x64.deb', (_req, res) => { app.get('/downloads/fieldlens-print/linux-x64.deb', (_req, res) => {
res.setHeader('Cache-Control', 'no-store') sendDesktopDownload(res, 'fieldlens-print-linux-x64.deb')
res.redirect(302, `${config.printReleaseBaseUrl.replace(/\/$/, '')}/${encodeURIComponent(config.printReleaseTag)}/fieldlens-print-linux-x64.deb`)
}) })
app.get('/api/health/ready', async (_req, res) => { app.get('/api/health/ready', async (_req, res) => {

View File

@ -2,13 +2,13 @@ import { ArrowLeft, Download, Monitor, Terminal } from 'lucide-react'
const downloads = [ const downloads = [
{ {
href: '/downloads/fieldlens-print/windows-x64', href: '/downloads/fieldlens-print-windows-x64-setup.exe',
label: 'Windows', label: 'Windows',
detail: 'x64 · NSIS installer · current-user install', detail: 'x64 · NSIS installer · current-user install',
icon: Monitor, icon: Monitor,
}, },
{ {
href: '/downloads/fieldlens-print/linux-x64.deb', href: '/downloads/fieldlens-print-linux-x64.deb',
label: 'Linux', label: 'Linux',
detail: 'x64 · Debian / Ubuntu .deb package', detail: 'x64 · Debian / Ubuntu .deb package',
icon: Terminal, icon: Terminal,