mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-07-10 00:14:27 +00:00
build: publish linux flatpak deb and rpm artifacts
Restore Tauri Debian and RPM assets while adding Flatpak outputs for both desktop variants so Linux users have package-manager and sandboxed alternatives to AppImage. Electron Flatpak packaging now installs the required Freedesktop runtime in CI, and Tauri Flatpak packaging uses a generated manifest around the existing release binary and packaged resources. The Linux dependency list also includes xdg-utils because Tauri AppImage bundling requires xdg-open. Docker-based Ubuntu 24.04 reproductions verified Electron zip/AppImage creation and Tauri AppImage/deb/rpm creation. Local Docker could not complete Flatpak builds because bubblewrap cannot create/seccomp namespaces in this host container environment, which should not apply to native GitHub runners.
This commit is contained in:
parent
cabe89476f
commit
3de5ecb7e9
6 changed files with 161 additions and 6 deletions
36
.github/workflows/build-and-upload.yml
vendored
36
.github/workflows/build-and-upload.yml
vendored
|
|
@ -311,6 +311,16 @@ jobs:
|
|||
- name: Ensure rollup native binary
|
||||
run: npm install @rollup/rollup-linux-x64-gnu --no-save
|
||||
|
||||
- name: Install Flatpak build dependencies (Electron)
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y flatpak flatpak-builder
|
||||
sudo flatpak remote-add --system --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
sudo flatpak install --system --noninteractive flathub \
|
||||
org.freedesktop.Platform//24.08 \
|
||||
org.freedesktop.Sdk//24.08 \
|
||||
org.electronjs.Electron2.BaseApp//24.08
|
||||
|
||||
- name: Build Linux binaries (Electron)
|
||||
run: npm run build:linux --workspace @neuralnomads/codenomad-electron-app
|
||||
|
||||
|
|
@ -322,7 +332,7 @@ jobs:
|
|||
run: |
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
for file in packages/electron-app/release/*.zip packages/electron-app/release/*.AppImage; do
|
||||
for file in packages/electron-app/release/*.zip packages/electron-app/release/*.AppImage packages/electron-app/release/*.flatpak; do
|
||||
[ -f "$file" ] || continue
|
||||
echo "Uploading $file"
|
||||
gh release upload "$TAG" "$file" --clobber
|
||||
|
|
@ -336,6 +346,7 @@ jobs:
|
|||
path: |
|
||||
packages/electron-app/release/*.zip
|
||||
packages/electron-app/release/*.AppImage
|
||||
packages/electron-app/release/*.flatpak
|
||||
retention-days: ${{ inputs.actions_artifacts_retention_days }}
|
||||
if-no-files-found: error
|
||||
|
||||
|
|
@ -637,7 +648,15 @@ jobs:
|
|||
libwebkit2gtk-4.1-dev \
|
||||
libsoup-3.0-dev \
|
||||
libayatana-appindicator3-dev \
|
||||
librsvg2-dev
|
||||
librsvg2-dev \
|
||||
xdg-utils \
|
||||
rpm \
|
||||
flatpak \
|
||||
flatpak-builder
|
||||
sudo flatpak remote-add --system --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
sudo flatpak install --system --noninteractive flathub \
|
||||
org.gnome.Platform//46 \
|
||||
org.gnome.Sdk//46
|
||||
|
||||
- name: Set workspace versions
|
||||
if: ${{ inputs.set_versions && inputs.version != '' }}
|
||||
|
|
@ -673,6 +692,9 @@ jobs:
|
|||
working-directory: packages/tauri-app
|
||||
run: npm exec -- tauri build
|
||||
|
||||
- name: Build Flatpak bundle (Tauri)
|
||||
run: npm run build:flatpak --workspace @codenomad/tauri-app
|
||||
|
||||
- name: Package Tauri artifacts (Linux)
|
||||
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
||||
run: |
|
||||
|
|
@ -688,14 +710,20 @@ jobs:
|
|||
}
|
||||
|
||||
appimage=$(find_one "*.AppImage")
|
||||
deb=$(find_one "*.deb")
|
||||
rpm=$(find_one "*.rpm")
|
||||
flatpak=$(find_one "*.flatpak")
|
||||
fallback_bin="$SEARCH_ROOT/release/codenomad-tauri"
|
||||
|
||||
if [ -z "$appimage" ] || [ ! -f "$fallback_bin" ]; then
|
||||
echo "Missing bundle(s): appimage=${appimage:-none} binary=$fallback_bin" >&2
|
||||
if [ -z "$appimage" ] || [ -z "$deb" ] || [ -z "$rpm" ] || [ -z "$flatpak" ] || [ ! -f "$fallback_bin" ]; then
|
||||
echo "Missing bundle(s): appimage=${appimage:-none} deb=${deb:-none} rpm=${rpm:-none} flatpak=${flatpak:-none} binary=$fallback_bin" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "$appimage" "$ARTIFACT_DIR/CodeNomad-Tauri-linux-x64-${VERSION}.AppImage"
|
||||
cp "$deb" "$ARTIFACT_DIR/CodeNomad-Tauri-linux-x64-${VERSION}.deb"
|
||||
cp "$rpm" "$ARTIFACT_DIR/CodeNomad-Tauri-linux-x64-${VERSION}.rpm"
|
||||
cp "$flatpak" "$ARTIFACT_DIR/CodeNomad-Tauri-linux-x64-${VERSION}.flatpak"
|
||||
zip -j "$ARTIFACT_DIR/CodeNomad-Tauri-linux-x64-${VERSION}.zip" "$fallback_bin"
|
||||
|
||||
- name: Upload Actions artifacts (Tauri Linux)
|
||||
|
|
|
|||
|
|
@ -137,11 +137,30 @@
|
|||
},
|
||||
{
|
||||
"target": "AppImage"
|
||||
},
|
||||
{
|
||||
"target": "flatpak"
|
||||
}
|
||||
],
|
||||
"artifactName": "CodeNomad-Electron-linux-${arch}-${version}.${ext}",
|
||||
"category": "Development",
|
||||
"icon": "electron/resources/icon.png"
|
||||
},
|
||||
"flatpak": {
|
||||
"runtimeVersion": "24.08",
|
||||
"baseVersion": "24.08",
|
||||
"branch": "stable",
|
||||
"useWaylandFlags": true,
|
||||
"finishArgs": [
|
||||
"--socket=wayland",
|
||||
"--socket=x11",
|
||||
"--share=ipc",
|
||||
"--device=dri",
|
||||
"--socket=pulseaudio",
|
||||
"--filesystem=home",
|
||||
"--share=network",
|
||||
"--talk-name=org.freedesktop.Notifications"
|
||||
]
|
||||
}
|
||||
},
|
||||
"private": true
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json"
|
||||
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && npm exec -- tsc -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opencode-ai/plugin": "1.3.7"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
"sync:version": "node ./scripts/sync-tauri-version.js",
|
||||
"prebuild": "node ./scripts/prebuild.js",
|
||||
"bundle:server": "npm run prebuild",
|
||||
"build:flatpak": "node ./scripts/build-flatpak.js",
|
||||
"build": "tauri build",
|
||||
"smoke:resources": "node ../../scripts/smoke-packaged-resources.cjs --resources src-tauri/resources --loading src-tauri/resources/ui-loading"
|
||||
},
|
||||
|
|
|
|||
107
packages/tauri-app/scripts/build-flatpak.js
Normal file
107
packages/tauri-app/scripts/build-flatpak.js
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/env node
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const { spawnSync } = require("child_process")
|
||||
|
||||
const root = path.resolve(__dirname, "..")
|
||||
const workspaceRoot = path.resolve(root, "..", "..")
|
||||
const version = process.env.VERSION || require(path.join(root, "package.json")).version
|
||||
const appId = "ai.neuralnomads.codenomad.client"
|
||||
const buildRoot = path.join(root, "target", "flatpak")
|
||||
const stagingRoot = path.join(buildRoot, "staging")
|
||||
const repoRoot = path.join(buildRoot, "repo")
|
||||
const flatpakBuildRoot = path.join(buildRoot, "build")
|
||||
const manifestPath = path.join(buildRoot, `${appId}.json`)
|
||||
const artifactDir = path.join(root, "target", "release", "bundle", "flatpak")
|
||||
const artifactPath = path.join(artifactDir, `CodeNomad-Tauri-linux-x64-${version}.flatpak`)
|
||||
|
||||
function run(command, args, options = {}) {
|
||||
const result = spawnSync(command, args, { stdio: "inherit", ...options })
|
||||
if (result.error) throw result.error
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`${command} ${args.join(" ")} exited with code ${result.status || 1}`)
|
||||
}
|
||||
}
|
||||
|
||||
function copyRequired(from, to) {
|
||||
if (!fs.existsSync(from)) {
|
||||
throw new Error(`Missing required Flatpak input: ${from}`)
|
||||
}
|
||||
fs.cpSync(from, to, { recursive: true, dereference: true })
|
||||
}
|
||||
|
||||
fs.rmSync(buildRoot, { recursive: true, force: true })
|
||||
fs.mkdirSync(stagingRoot, { recursive: true })
|
||||
fs.mkdirSync(artifactDir, { recursive: true })
|
||||
|
||||
copyRequired(path.join(root, "target", "release", "codenomad-tauri"), path.join(stagingRoot, "codenomad-tauri"))
|
||||
copyRequired(path.join(root, "target", "release", "resources"), path.join(stagingRoot, "resources"))
|
||||
copyRequired(
|
||||
path.join(root, "src-tauri", "icons", "linux", "512x512.png"),
|
||||
path.join(stagingRoot, `${appId}.png`),
|
||||
)
|
||||
|
||||
const desktopFile = [
|
||||
"[Desktop Entry]",
|
||||
"Type=Application",
|
||||
"Name=CodeNomad",
|
||||
"Comment=AI coding assistant",
|
||||
"Categories=Development;IDE;",
|
||||
"Exec=codenomad-tauri",
|
||||
`Icon=${appId}`,
|
||||
"Terminal=false",
|
||||
"",
|
||||
].join("\n")
|
||||
fs.writeFileSync(path.join(stagingRoot, `${appId}.desktop`), desktopFile)
|
||||
|
||||
const manifest = {
|
||||
"app-id": appId,
|
||||
runtime: "org.gnome.Platform",
|
||||
"runtime-version": "46",
|
||||
sdk: "org.gnome.Sdk",
|
||||
branch: "stable",
|
||||
command: "codenomad-tauri",
|
||||
"finish-args": [
|
||||
"--socket=wayland",
|
||||
"--socket=x11",
|
||||
"--share=ipc",
|
||||
"--device=dri",
|
||||
"--socket=pulseaudio",
|
||||
"--filesystem=home",
|
||||
"--share=network",
|
||||
"--talk-name=org.freedesktop.Notifications",
|
||||
],
|
||||
modules: [
|
||||
{
|
||||
name: "codenomad-tauri",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [
|
||||
"install -Dm755 codenomad-tauri /app/bin/codenomad-tauri",
|
||||
"mkdir -p /app/lib/CodeNomad",
|
||||
"cp -a resources /app/lib/CodeNomad/resources",
|
||||
`install -Dm644 ${appId}.desktop /app/share/applications/${appId}.desktop`,
|
||||
`install -Dm644 ${appId}.png /app/share/icons/hicolor/512x512/apps/${appId}.png`,
|
||||
],
|
||||
sources: [
|
||||
{
|
||||
type: "dir",
|
||||
path: stagingRoot,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
|
||||
fs.rmSync(repoRoot, { recursive: true, force: true })
|
||||
fs.rmSync(flatpakBuildRoot, { recursive: true, force: true })
|
||||
fs.rmSync(artifactPath, { force: true })
|
||||
|
||||
run("flatpak-builder", ["--force-clean", `--repo=${repoRoot}`, flatpakBuildRoot, manifestPath], {
|
||||
cwd: workspaceRoot,
|
||||
})
|
||||
run("flatpak", ["build-bundle", repoRoot, artifactPath, appId, "stable"], {
|
||||
cwd: workspaceRoot,
|
||||
})
|
||||
|
||||
console.log(`[flatpak] wrote ${artifactPath}`)
|
||||
|
|
@ -23,7 +23,7 @@ function copyRequiredArtifact(serverRoot, serverDest, name, log) {
|
|||
if (!fs.existsSync(from)) {
|
||||
throw new Error(`Missing required server artifact: ${from}`)
|
||||
}
|
||||
fs.cpSync(from, to, { recursive: true, dereference: true })
|
||||
fs.cpSync(from, to, { recursive: true })
|
||||
log(`copied ${name}`)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue