Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,39 @@
<!-- Explain the problem and the user-visible outcome. -->
<!-- Link issues with "Closes #123". -->

## Release intent

<!-- Keep the fences exactly as they are; CI parses between them. -->
<!-- Set each bump to none, patch, minor, or major. Leave the key names. -->
<!-- `services` must be >= the highest component bump. -->
<!-- Set the title and body to n/a when no bump is a release. -->
<!-- The release version users see is NOT declared here: it patch-bumps by
itself whenever any bump above is a release. -->

<!-- pair-release-intent:v1 -->
### Changelog title
n/a

### Changelog body
n/a

### Bumps
- services: none
- lmstudio-proxy: none
- nvpair-cluster-manager: none
- nvpair-engine-manager: none
- nvpair-errors: none
- nvpair-job-scheduler: none
- nvpair-manual-nodes: none
- nvpair-node-info: none
- nvpair-node-scanner: none
- nvpair-node-settings: none
- nvpair-tui: none
- nvpair-ui-broker: none
- nvpair-workload-manager: none
- ollama-proxy: none
<!-- /pair-release-intent:v1 -->

## Scope

<!-- What is intentionally included and excluded? -->
Expand All @@ -23,4 +56,4 @@
- [ ] Relevant documentation is updated.
- [ ] I checked the diff, changed filenames, and commit messages for credentials, private data, internal URLs, internal issue identifiers, and generated artifacts.
- [ ] I recorded the validation commands and results above.
- [ ] I bumped any affected component in `services/versions.json`, and described user-visible changes above so they reach the release notes.
- [ ] I declared version bumps in the release-intent block above. `services/versions.json` is written by automation — do not edit it by hand.
70 changes: 70 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# The single place the Node and Go versions are declared.
#
# GitHub Actions has no shared constants file: `env:` is per-workflow and one
# workflow cannot read another. Four workflows exist because each needs a
# distinct `on:` block, so without this they would each carry their own copy of
# the versions, and bumping one while forgetting another would have pull request
# checks and release builds running different toolchains.
#
# The caller must check out the repository first — a local action cannot exist
# until its own repo is on disk, so checkout stays in the workflow.
#
# On a fork pull request this file comes from the fork, like every other script
# those jobs run. That adds no exposure: those jobs already execute the pull
# request's `npm ci` and `go test`, hold no secret, and run on hosted runners.
name: Set up toolchains
description: Installs the pinned Node and Go toolchains used across all workflows.

inputs:
node:
description: Install Node.
default: 'true'
go:
description: Install Go.
default: 'false'
npm-cache:
description: Cache the npm download cache. Pointless without an npm install.
default: 'true'

runs:
using: composite
steps:
# desktop/package.json engines requires >=25.5.0. Pinned to a major
# rather than read from that range, which would drift to whatever Node
# is newest.
- if: inputs.node == 'true'
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version: '25'
cache: ${{ inputs.npm-cache == 'true' && 'npm' || '' }}
cache-dependency-path: desktop/package-lock.json

# Forbids the surprise toolchain download, so the version below is the
# one that actually builds. Set before setup-go because setup-go reads
# GOTOOLCHAIN when resolving a version file.
- if: inputs.go == 'true'
shell: bash
run: echo 'GOTOOLCHAIN=local' >> "$GITHUB_ENV"

# Must be >= the highest `go` directive across services/*/go.mod, which
# GOTOOLCHAIN=local would otherwise refuse to satisfy.
#
# cache-dependency-path is required, not an optimization. setup-go
# caches by default and looks for the dependency file at the repository
# root, where there is none — every Go module lives under services/.
# Left unset it reports "Dependencies file is not found" on every run.
# Naming the path also pins the cache key: older versions hash go.sum,
# newer ones hash go.mod.
#
# scripts/ matches nothing today; it is listed so a Go dependency added
# to the support tooling joins the key instead of silently missing it.
- if: inputs.go == 'true'
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: '1.26.7'
cache-dependency-path: |
services/**/go.sum
scripts/**/go.sum
105 changes: 105 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Unsigned installer builds on GitHub-hosted runners.
#
# These are verification and pull-request artifacts, never releases: every
# target ends in `electron-builder --publish never`, and this workflow holds no
# signing or publishing credentials. Signed releases are produced separately
# from a tag, and are rebuilt from source rather than signing anything this
# workflow produced.
name: Build

on:
workflow_dispatch:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
paths:
- '.github/workflows/**'
- 'scripts/**'
- 'services/**'
- 'desktop/package.json'
- 'desktop/package-lock.json'
- 'desktop/src/**'
- 'desktop/scripts/**'
- 'desktop/native/**'
- 'desktop/resources/**'
- 'desktop/tsconfig*.json'
- 'desktop/vite.*.config.ts'
- 'desktop/electron.vite.config.ts'
- 'desktop/electron-builder.config.ts'

permissions:
contents: read

concurrency:
group: build-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# Toolchain versions are declared once, in .github/actions/setup.

jobs:
build:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
timeout-minutes: 60
strategy:
# One platform failing should not hide the state of the other five.
fail-fast: false
matrix:
include:
- name: linux-x64
os: ubuntu-latest
script: build:linux:x64
- name: linux-arm64
os: ubuntu-latest
script: build:linux:arm64
- name: win-x64
os: windows-latest
script: build:win:x64
- name: win-arm64
os: windows-latest
script: build:win:arm64
- name: mac-arm64
os: macos-latest
script: build:mac:arm64
- name: mac-x64
os: macos-15-intel
script: build:mac:x64
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

# Recorded so a failure can be read against the machine it ran on.
- name: Report runner capacity
shell: bash
run: |
echo "label: ${{ matrix.os }}"
echo "kernel: $(uname -s) $(uname -m)"
echo "cpus: $(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo unknown)"
free -h 2>/dev/null || sysctl -n hw.memsize 2>/dev/null || echo "mem: unknown"
df -h . | tail -1

- uses: ./.github/actions/setup
with:
go: 'true'

# electron-builder needs fakeroot and rpm to stage Linux packages;
# ubuntu-latest ships neither.
- name: Install Linux packaging tools
if: runner.os == 'Linux'
run: sudo apt-get update -qq && sudo apt-get install -y -qq fakeroot rpm

- run: npm --prefix desktop ci --prefer-offline

- name: Build ${{ matrix.name }}
run: npm --prefix desktop run ${{ matrix.script }}

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: unsigned-${{ matrix.name }}
path: desktop/release/**
retention-days: 7
if-no-files-found: warn
Loading
Loading