chore: use Bun package management and Turbo remote caching (#6978)
* feat: migrate tooling and runtimes to bun * fix: serialize act installs and adapt backup checks * fix: finish bun scripts and rootless act setup * fix: support bun images in runtime benchmarks * fix: keep a single bun toolchain configuration * fix: include docs mascot sources in workshop build * docs: use bun in remaining tool commands * fix: complete bun sqlite development tooling * docs: explain sqlite development adapter * fix: align remaining commands and local ci validation * fix: use renovate regex fallback with bun * fix: finalize bun sqlite connections during cleanup * fix: preserve sqlite foreign key enforcement * fix: align docker build frontends * fix: prepare release versions with bun * Add Bun benchmarks and preserve SQLite connection defaults * Benchmark Bun installs, conflicts and production workloads * Force Bun workspace tools and HTTP/1.1 upstream streaming * Exclude benchmark evidence from Docker contexts * Expose branch benchmarks through CI * Verify hydrated widget data in production benchmarks * Measure image archives and document benchmark protocol * Audit dependency sharing across every workspace * Pin pnpm for nested baseline benchmark scripts * Preserve nested manager pinning on benchmark resume * Include application and runtime image footprints * Fail benchmark gates on candidate command errors * Report complete build overhead and sampled memory bounds * Validate migration comparisons across pinned revisions * Record configurable runtime memory budgets * Retain out-of-memory kills in runtime budget probes * Reject OOM events consistently across runtime metrics * Record complete Bun migration benchmarks * Record memory diagnosis and correct Turbo caching * Use Bun package management while preserving Node runtimes * Keep native build fallbacks and exclude development artifacts * Fix Node ESM metadata and isolate local CI configuration * Measure development performance and isolate act environment * Support isolated environment files in rootless act * Validate the full migration range with act * Preserve native SQLite binding for bundled migrations * Use documented dev defaults and report benchmark scope * Validate dev defaults and isolate restart measurements * Record package-manager and development benchmarks * Keep benchmark measurements and link full command artifacts * Clean up temporary act environment files * Validate Node runtimes in package-manager comparisons * Validate every workspace in manual CI runs * Retain patched nested brace expansion versions * Complete package-manager and Node performance report * Normalize archived benchmark logs * Record final hosted validation * Cache both Next builds with platform isolation * Prime cache with write-only mode * Split Next artifacts below Cloudflare upload limit * Assemble coherent standalone cache outputs * Preserve traced middleware entrypoints * Assemble traced directory links before files * Separate cached dependencies from assembled output * Record signed Turbo cache benchmarks * Remove archived benchmark captures Full benchmark evidence is preserved at 8f7d3bcd14109bfb13fa664d31710b88d820ad66. * Remove migration benchmark harness * fix: correct migration launch and local Act prerequisites * Consolidate Turbo caches and production artifacts * Exclude generated files from Docker cache inputs * fix: address Bun migration review findings * fix: check restored compiler bundle manifests
This commit is contained in:
1 parent
dc5f382875
commit
229bd4b3ed
109 files changed
+8507
-28875
No files matched your search
@@ -17,11 +17,11 @@ Orient from the current checkout before editing. Treat package manifests, export
|
||||
|
||||
## Current toolchain
|
||||
|
||||
- Use Node `24.18.0` from `mise.toml` and pnpm `11.15.1` from `package.json`.
|
||||
- Use pnpm workspaces with Turborepo. Workspace packages use the `@homarr/` scope and catalog-managed dependencies.
|
||||
- Use Node `24.18.0` for application runtimes and Bun `1.4.2` for package management, pinned in `mise.toml` and `package.json`.
|
||||
- Use Bun workspaces with Turborepo and a shared hoisted install. Workspace packages use the `@homarr/` scope and catalog-managed dependencies.
|
||||
- Use Next.js App Router, TypeScript, tRPC, Drizzle, Mantine v9, Tabler icons, Jotai, TanStack Query, next-intl, Vitest, and Playwright.
|
||||
- Use oxlint and oxfmt. Mantine is the application UI system; Tailwind is limited to the docs app.
|
||||
- `pnpm dev` starts only `@homarr/nextjs`. Start a standalone runtime explicitly when the task needs it.
|
||||
- `bun run dev` starts only `@homarr/nextjs`. Start a standalone runtime explicitly when the task needs it.
|
||||
|
||||
## High-value seams
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
| `apps/websocket` | tRPC WebSocket subscriptions on port 3001 |
|
||||
| `apps/workshop` | Go/PocketBase Workshop service and provider integration |
|
||||
|
||||
`pnpm dev` runs only `@homarr/nextjs`. Use `pnpm --filter @homarr/tasks dev`, `pnpm --filter @homarr/websocket dev`, or `pnpm dev:docs` when a task needs those runtimes.
|
||||
`bun run dev` runs only `@homarr/nextjs`. Use `bun run --filter @homarr/tasks dev`, `bun run --filter @homarr/websocket dev`, or `bun run dev:docs` when a task needs those runtimes.
|
||||
|
||||
In production, `apps/nextjs/src/instrumentation-node.ts` starts the tasks and WebSocket runtimes inside the Node process. Nginx listens on port 7575, proxies `/websockets` to port 3001, and sends other traffic to port 3000. Redis can run internally or externally.
|
||||
In production, `apps/nextjs/src/instrumentation-node.ts` starts the tasks and WebSocket runtimes inside the Bun process. Nginx listens on port 7575, proxies `/websockets` to port 3001, and sends other traffic to port 3000. Redis can run internally or externally.
|
||||
|
||||
## Package map
|
||||
|
||||
@@ -34,7 +34,7 @@ In production, `apps/nextjs/src/instrumentation-node.ts` starts the tasks and We
|
||||
|
||||
### Data, identity, and transport
|
||||
|
||||
- `db`: Drizzle schemas, queries, migrations, and three database drivers.
|
||||
- `db`: Drizzle schemas, queries, migrations, and SQLite and PostgreSQL drivers.
|
||||
- `auth`: Auth.js/NextAuth configuration, sessions, providers, and API keys.
|
||||
- `api`: tRPC routers, contexts, MCP metadata extraction, OpenAPI, and client/server entrypoints.
|
||||
- `redis`: Redis clients, pub/sub channels, and caches.
|
||||
@@ -57,7 +57,7 @@ In production, `apps/nextjs/src/instrumentation-node.ts` starts the tasks and We
|
||||
- `settings`, `spotlight`, `notifications`, `onboarding`: cross-feature UI flows.
|
||||
- `widgets`: widget definitions, dynamic component loaders, and shared widget UI.
|
||||
- `translation`: next-intl configuration and locale catalogs.
|
||||
- `cli`: Node operations CLI. `tools/homarr-dev` is the separate Go developer CLI.
|
||||
- `cli`: Bun operations CLI. `tools/homarr-dev` is the separate Go developer CLI.
|
||||
|
||||
## Dependency direction
|
||||
|
||||
@@ -103,8 +103,8 @@ User routes live below `apps/nextjs/src/app/[locale]/`; the locale segment does
|
||||
## Commands and conventions
|
||||
|
||||
- Read current scripts from the root or package `package.json`; avoid caching commands that do not exist.
|
||||
- Use `pnpm dev` for Next.js, `pnpm dev:docs` for docs, and `pnpm dev:cli -- <args>` for the Go developer CLI.
|
||||
- Use `pnpm docker:dev:up` for the Redis development dependency.
|
||||
- Use `bun run dev` for Next.js, `bun run dev:docs` for docs, and `bun run dev:cli -- <args>` for the Go developer CLI.
|
||||
- Use `bun run docker:dev:up` for the Redis development dependency.
|
||||
- Follow the Testing section in `AGENTS.md` for test creation and validation scope.
|
||||
- Use `~/*` only inside the Next.js app. Use public `@homarr/*` entrypoints across package boundaries.
|
||||
- Keep Mantine application styles separate from the Tailwind-based docs app.
|
||||
@@ -53,5 +53,5 @@ For a widget:
|
||||
1. Every passage adds information a user needs and cannot reasonably infer from the interface. Remove obvious, repetitive, or unnecessary prose.
|
||||
2. Keep names, defaults, paths, screenshots, links, and prerequisites consistent with code.
|
||||
3. Update every affected page and remove superseded guidance.
|
||||
4. Run the narrowest useful docs validation. Use `pnpm turbo build --filter=@homarr/docs` when links, MDX, generated definitions, or navigation can fail; otherwise run the docs package formatter on touched files.
|
||||
5. Treat broken links and anchors as failures; run `pnpm --filter @homarr/docs validate:links` after the static build.
|
||||
4. Run the narrowest useful docs validation. Use `bun run turbo build --filter=@homarr/docs` when links, MDX, generated definitions, or navigation can fail; otherwise run the docs package formatter on touched files.
|
||||
5. Treat broken links and anchors as failures; run `bun run --filter @homarr/docs validate:links` after the static build.
|
||||
@@ -93,6 +93,6 @@ Register the smallest router that owns the enabled procedures. If a parent route
|
||||
1. Confirm the procedure still enforces its ordinary auth and permission boundary.
|
||||
2. Confirm `packages/api/src/mcp.ts` eagerly includes the procedure.
|
||||
3. Confirm the generated tool name, description, and JSON schema are unambiguous.
|
||||
4. When validating tool-list or extraction changes, use the focused API MCP spec: `pnpm test packages/api/src/test/mcp.spec.ts`.
|
||||
5. When validating protocol or transport changes, use the focused route spec: `pnpm test apps/nextjs/src/app/api/mcp/[transport]/route.spec.ts`.
|
||||
4. When validating tool-list or extraction changes, use the focused API MCP spec: `bun run test packages/api/src/test/mcp.spec.ts`.
|
||||
5. When validating protocol or transport changes, use the focused route spec: `bun run test apps/nextjs/src/app/api/mcp/[transport]/route.spec.ts`.
|
||||
6. If manually probing `/api/mcp/<transport>`, use a scoped API key and avoid printing credentials or secret-bearing results.
|
||||
+16
-2
@@ -5,9 +5,18 @@ node_modules
|
||||
npm-debug.log
|
||||
README.md
|
||||
.next
|
||||
**/.output
|
||||
.turbo
|
||||
.pnpm-store
|
||||
**/.pnpm-store
|
||||
**/.turbo
|
||||
**/next-env.d.ts
|
||||
packages/db/migrations/*/migrate.cjs
|
||||
packages/cli/cli.cjs
|
||||
apps/tasks/tasks.cjs
|
||||
apps/tasks/tasks.css
|
||||
apps/websocket/wssServer.cjs
|
||||
apps/websocket/wssServer.css
|
||||
.bun-cache
|
||||
**/.bun-cache
|
||||
coverage
|
||||
**/coverage
|
||||
**/.docusaurus
|
||||
@@ -23,3 +32,8 @@ apps/nextjs/.next
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
benchmark-results
|
||||
scripts/benchmarks/results
|
||||
**/__pycache__
|
||||
**/*.tsbuildinfo
|
||||
@@ -3,18 +3,20 @@
|
||||
Install [Act](https://nektosact.com/installation/index.html) and ensure Docker is running, then use:
|
||||
|
||||
```bash
|
||||
pnpm ci:act:fast
|
||||
pnpm ci:act:docker
|
||||
pnpm ci:act:all
|
||||
pnpm ci:act:cleanup
|
||||
bun run ci:act:fast
|
||||
bun run ci:act:docker
|
||||
bun run ci:act:all
|
||||
bun run ci:act:cleanup
|
||||
```
|
||||
|
||||
`fast` runs the quality gate. `docker` and `all` run the gate followed by the single container build, E2E suite, and memory regression test. `cleanup` validates the manual cleanup path without calling GitHub. The wrapper supports regular clones and Git worktrees, including non-default Docker contexts. Linux dependencies are kept in Docker volumes instead of overwriting host `node_modules`. The first run downloads the Act runner and Playwright browser dependencies.
|
||||
`fast` runs the database checks and quality gate and requires an `origin/dev` tracking ref. `docker` and `all` use the manual validation event to run those checks, build the container once, and run the converter boot/restart smoke test. `cleanup` validates the manual cleanup path without calling GitHub. The wrapper supports regular clones and Git worktrees, including non-default Docker contexts. Linux dependencies are kept in Docker volumes instead of overwriting host `node_modules`. The first run downloads the Act runner and Playwright browser dependencies.
|
||||
|
||||
For rootless Docker, set `ACT_TESTCONTAINERS_HOST` to a host address reachable from the runner when loopback callbacks are unavailable.
|
||||
|
||||
Remote caching is optional locally. Copy `.github/act/.secrets.example` to `.github/act/.secrets`, populate it with rotated credentials, and run with:
|
||||
|
||||
```bash
|
||||
ACT_SECRET_FILE=.github/act/.secrets pnpm ci:act:all
|
||||
ACT_SECRET_FILE=.github/act/.secrets bun run ci:act:all
|
||||
```
|
||||
|
||||
Act never publishes or deletes GHCR images. The secret file is ignored by Git.
|
||||
|
||||
@@ -19,11 +19,6 @@ inputs:
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Prebuilt debian dependencies
|
||||
uses: ./.github/actions/prebuilt-debian
|
||||
id: prebuilt-debian
|
||||
with:
|
||||
architecture: ${{ inputs.architecture }}
|
||||
- name: Start docker container for ${{ inputs.architecture }}
|
||||
run: |
|
||||
docker run --name homarr \
|
||||
@@ -43,8 +38,6 @@ runs:
|
||||
shell: bash
|
||||
- name: Extract source from ${{ inputs.architecture }} container (debian)
|
||||
run: |
|
||||
docker cp ${{ steps.prebuilt-debian.outputs.path }}/. homarr:/app/build && \
|
||||
docker cp ${{ steps.prebuilt-debian.outputs.path }}/. homarr:/app/node_modules/better-sqlite3/build/Release && \
|
||||
docker exec homarr tar -czf extraction-debian.tar.gz -C /app . && \
|
||||
docker cp homarr:/app/extraction-debian.tar.gz ${{ runner.temp }}/extraction/${{ inputs.architecture }}/build-debian-${{ inputs.architecture }}.tar.gz
|
||||
shell: bash
|
||||
|
||||
+1
-13
@@ -6,11 +6,6 @@
|
||||
matchPackageNames: ["^@homarr/"],
|
||||
enabled: false,
|
||||
},
|
||||
// Reenable once https://github.com/privatenumber/tsx/issues/737 is fixed
|
||||
{
|
||||
matchPackageNames: ["tsx"],
|
||||
enabled: false,
|
||||
},
|
||||
// Reenable once bug with undici v8 is fixed (https://github.com/homarr-labs/homarr/issues/5467)
|
||||
{
|
||||
matchPackageNames: ["undici"],
|
||||
@@ -22,11 +17,10 @@
|
||||
automerge: true,
|
||||
},
|
||||
{
|
||||
// disallow automerge for allowBuilds dependencies (native builds need manual verification)
|
||||
// disallow automerge for trustedDependencies (native builds need manual verification)
|
||||
matchPackageNames: [
|
||||
"@tree-sitter-grammars/tree-sitter-yaml",
|
||||
"bcrypt",
|
||||
"better-sqlite3",
|
||||
"cpu-features",
|
||||
"esbuild",
|
||||
"sharp",
|
||||
@@ -37,12 +31,6 @@
|
||||
automerge: false,
|
||||
},
|
||||
],
|
||||
// Updates lockfile for prebuilt-debian after if package.json is updated, otherwise only root lockfile is updated
|
||||
postUpgradeTasks: {
|
||||
commands: ["cd deployments/prebuilt-debian && pnpm install --ignore-workspace --lockfile-only"],
|
||||
fileFilters: ["deployments/prebuilt-debian/pnpm-lock.yaml"],
|
||||
executionMode: "update",
|
||||
},
|
||||
updateInternalDeps: true,
|
||||
rangeStrategy: "bump",
|
||||
automerge: false,
|
||||
|
||||
@@ -32,4 +32,4 @@ jobs:
|
||||
run: |
|
||||
git diff --name-only --diff-filter=ACMR -z "origin/${BASE_REF}...HEAD" -- \
|
||||
'*.css' '*.js' '*.json' '*.jsonc' '*.jsx' '*.md' '*.mdx' '*.scss' '*.ts' '*.tsx' '*.yaml' '*.yml' \
|
||||
| xargs -0 -r pnpm exec oxfmt --check --no-error-on-unmatched-pattern --
|
||||
| xargs -0 -r bun x oxfmt --check --no-error-on-unmatched-pattern --
|
||||
+22
-21
@@ -67,7 +67,6 @@ jobs:
|
||||
TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
|
||||
TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before || 'HEAD~1' }}
|
||||
TURBO_SCM_HEAD: ${{ github.sha }}
|
||||
TURBO_PLATFORM: linux/amd64
|
||||
steps:
|
||||
- name: Require database validation
|
||||
env:
|
||||
@@ -92,26 +91,34 @@ jobs:
|
||||
|
||||
- name: Setup
|
||||
uses: ./tooling/github/setup
|
||||
with:
|
||||
next-cache: "all"
|
||||
turbo-cache: "true"
|
||||
|
||||
- name: Copy environment defaults
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Lint, typecheck, and build affected workspaces
|
||||
run: pnpm turbo run lint typecheck build --affected --continue=dependencies-successful --concurrency=2
|
||||
- name: Lint, typecheck, and build workspaces
|
||||
run: |
|
||||
scope=(--affected)
|
||||
if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
|
||||
scope=()
|
||||
fi
|
||||
bun run turbo run lint typecheck build "${scope[@]}" --continue=dependencies-successful --concurrency=2
|
||||
|
||||
- name: Validate workspace configuration
|
||||
run: pnpm lint:ws
|
||||
run: bun run lint:ws
|
||||
|
||||
- name: Validate Custom Widget architecture
|
||||
run: pnpm check:custom-widgets
|
||||
run: bun run check:custom-widgets
|
||||
|
||||
- name: Validate checked-in OpenAPI schema
|
||||
run: pnpm check:openapi
|
||||
run: bun run check:openapi
|
||||
|
||||
- name: Validate Custom Widget bundle budget
|
||||
run: |
|
||||
pnpm turbo run build --filter=@homarr/nextjs
|
||||
pnpm check:custom-widget-bundle
|
||||
bun run turbo run build --filter=@homarr/nextjs
|
||||
bun run check:custom-widget-bundle
|
||||
|
||||
container:
|
||||
name: Container and migration smoke
|
||||
@@ -180,14 +187,8 @@ jobs:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
|
||||
run: |
|
||||
secret_args=()
|
||||
for secret in TURBO_API TURBO_TEAM TURBO_TOKEN TURBO_REMOTE_CACHE_SIGNATURE_KEY; do
|
||||
if [ -n "${!secret:-}" ]; then
|
||||
secret_args+=(--secret "id=${secret},env=${secret}")
|
||||
fi
|
||||
done
|
||||
DOCKER_BUILDKIT=1 docker build --platform linux/amd64 \
|
||||
--tag homarr-ci --tag homarr-e2e "${secret_args[@]}" .
|
||||
bash tooling/github/docker-build.sh --platform linux/amd64 \
|
||||
--tag homarr-ci --tag homarr-e2e .
|
||||
|
||||
- name: Cache Playwright browsers
|
||||
if: "!env.ACT"
|
||||
@@ -195,24 +196,24 @@ jobs:
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
key: playwright-${{ runner.os }}-${{ hashFiles('bun.lock') }}
|
||||
|
||||
- name: Install Playwright Chromium
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true' || env.ACT
|
||||
run: pnpm exec playwright install --with-deps chromium
|
||||
run: bun x playwright install --with-deps chromium
|
||||
|
||||
- name: Install Playwright system deps
|
||||
if: steps.playwright-cache.outputs.cache-hit == 'true'
|
||||
run: pnpm exec playwright install-deps chromium
|
||||
run: bun x playwright install-deps chromium
|
||||
|
||||
- name: Install standalone converter test dependencies
|
||||
run: npm ci --prefix tools/mysql-to-sqlite
|
||||
run: bun install --cwd tools/mysql-to-sqlite --frozen-lockfile
|
||||
|
||||
- name: Verify converted database boots and survives restart
|
||||
env:
|
||||
NODE_ENV: development
|
||||
CI: "true"
|
||||
run: pnpm exec vitest run --project e2e e2e/mysql-conversion.spec.ts --maxWorkers 1
|
||||
run: bun x vitest run --project e2e e2e/mysql-conversion.spec.ts --maxWorkers 1
|
||||
|
||||
- name: Upload E2E artifacts
|
||||
if: failure() && !env.ACT
|
||||
|
||||
@@ -20,6 +20,7 @@ jobs:
|
||||
migrations: ${{ steps.select.outputs.migrations }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
@@ -43,7 +44,7 @@ jobs:
|
||||
git diff --name-only -z "$comparison" > "$changed_files"
|
||||
while IFS= read -r -d '' file; do
|
||||
case "$file" in
|
||||
packages/db/*|packages/core/src/infrastructure/db/*|packages/core/src/infrastructure/certificates/*|packages/auth/*|packages/api/src/router/onboard*|packages/api/src/router/test/onboard*|packages/onboarding/*|apps/nextjs/src/app/api/backup/*|apps/nextjs/src/app/*/init/*|Dockerfile|scripts/run.sh|scripts/entrypoint.sh|deployments/*|package.json|packages/core/package.json|packages/common/package.json|packages/definitions/package.json|packages/server-settings/package.json|packages/validation/package.json|mise.toml|pnpm-lock.yaml|pnpm-workspace.yaml|patches/*|vitest*|tooling/github/*|.github/workflows/database.yml|.github/workflows/ci.yml)
|
||||
packages/db/*|packages/core/src/infrastructure/db/*|packages/core/src/infrastructure/certificates/*|packages/auth/*|packages/api/src/router/onboard*|packages/api/src/router/test/onboard*|packages/onboarding/*|apps/nextjs/src/app/api/backup/*|apps/nextjs/src/app/*/init/*|Dockerfile|scripts/run.sh|scripts/entrypoint.sh|deployments/*|package.json|packages/core/package.json|packages/common/package.json|packages/definitions/package.json|packages/server-settings/package.json|packages/validation/package.json|mise.toml|bun.lock|bunfig.toml|patches/*|vitest*|tooling/github/*|.github/workflows/database.yml|.github/workflows/ci.yml)
|
||||
migrations=true
|
||||
;;
|
||||
esac
|
||||
@@ -59,10 +60,11 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: ./tooling/github/setup
|
||||
- run: pnpm exec vitest run --project db-node packages/db/test/migration-journal.spec.ts
|
||||
- run: bun x vitest run --project db-node packages/db/test/migration-journal.spec.ts
|
||||
|
||||
migrations:
|
||||
name: SQLite and PostgreSQL migrations
|
||||
@@ -72,12 +74,13 @@ jobs:
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: ./tooling/github/setup
|
||||
- name: Reject retired database configuration
|
||||
run: pnpm exec vitest run --project dom packages/core/src/infrastructure/db/env.spec.ts
|
||||
- run: pnpm exec vitest run --project db-node --project integration packages/db/test/sqlite-migration.spec.ts packages/db/test/postgresql-migration.spec.ts packages/db/proxy-reader.integration.spec.ts --maxWorkers 1
|
||||
run: bun x vitest run --project dom packages/core/src/infrastructure/db/env.spec.ts
|
||||
- run: bun x vitest run --project db-node --project integration packages/db/test/sqlite-migration.spec.ts packages/db/test/postgresql-migration.spec.ts packages/db/proxy-reader.integration.spec.ts --maxWorkers 1
|
||||
|
||||
database-gate:
|
||||
name: Database gate
|
||||
|
||||
@@ -63,13 +63,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- uses: pnpm/action-setup@v6
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
with:
|
||||
node-version: 24.18.0
|
||||
package-manager-cache: false
|
||||
bun-version: 1.4.2
|
||||
- name: Verify major release intent
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
run: |
|
||||
@@ -92,7 +89,7 @@ jobs:
|
||||
exit 1
|
||||
- name: Install dependencies
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
run: pnpm install --frozen-lockfile
|
||||
run: bun install --frozen-lockfile
|
||||
- name: Run Semantic Release
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
env:
|
||||
@@ -102,7 +99,7 @@ jobs:
|
||||
GIT_COMMITTER_NAME: "Releases Homarr"
|
||||
GIT_COMMITTER_EMAIL: "175486441+homarr-releases[bot]@users.noreply.github.com"
|
||||
run: |
|
||||
pnpm release
|
||||
bun run release
|
||||
- name: Read semver output
|
||||
# We read the last tag either from the created release or from the current branch, this is to rerun the deployment job for the currently released version when it failed
|
||||
if: env.SKIP_RELEASE == 'false' || github.ref_name == 'main' || github.ref_name == 'beta'
|
||||
|
||||
@@ -5,7 +5,13 @@ on:
|
||||
branches: ["dev"]
|
||||
paths:
|
||||
- "apps/docs/**"
|
||||
- "packages/definitions/src/docs/**"
|
||||
- "packages/**"
|
||||
- "tooling/**"
|
||||
- "tools/motion-reel/three/**"
|
||||
- "package.json"
|
||||
- "bun.lock"
|
||||
- "bunfig.toml"
|
||||
- "turbo.json"
|
||||
- ".github/workflows/deployment-docs.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -33,15 +39,18 @@ jobs:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Setup
|
||||
uses: ./tooling/github/setup
|
||||
with:
|
||||
next-cache: "docs"
|
||||
turbo-cache: "true"
|
||||
- name: Build docs
|
||||
run: pnpm turbo build --filter=@homarr/docs
|
||||
run: bun run turbo run build --filter=@homarr/docs
|
||||
env:
|
||||
CI: "true"
|
||||
- name: Validate exported links and search
|
||||
run: |
|
||||
pnpm --filter @homarr/docs validate:links
|
||||
pnpm --filter @homarr/docs verify:search
|
||||
pnpm --filter @homarr/docs verify:seo
|
||||
bun run --filter @homarr/docs validate:links
|
||||
bun run --filter @homarr/docs verify:search
|
||||
bun run --filter @homarr/docs verify:seo
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v6
|
||||
- name: Upload pages artifact
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
|
||||
- name: Dump contributors
|
||||
working-directory: apps/docs
|
||||
run: pnpm api-dump-contributors
|
||||
run: bun run api-dump-contributors
|
||||
|
||||
- name: Commit updated contributors
|
||||
uses: EndBug/add-and-commit@v10
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Generate and validate OpenAPI spec
|
||||
run: pnpm scripts:update-openapi
|
||||
run: bun run scripts:update-openapi
|
||||
|
||||
- name: Commit updated spec
|
||||
uses: EndBug/add-and-commit@v10
|
||||
|
||||
@@ -12,8 +12,15 @@ jobs:
|
||||
renovate-validate:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
env:
|
||||
# The optional RE2 addon uses V8 APIs; validation can use JavaScript regexes.
|
||||
RENOVATE_X_IGNORE_RE2: "true"
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.4.2
|
||||
- run: |
|
||||
npx --yes --package renovate -- \
|
||||
bun x --package renovate \
|
||||
renovate-config-validator --strict .github/renovate.json5
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
ref: dev
|
||||
- name: Setup
|
||||
uses: ./tooling/github/setup
|
||||
- run: pnpm run scripts:update-bug-report-template
|
||||
- run: bun run scripts:update-bug-report-template
|
||||
env:
|
||||
NEXT_VERSION: ${{ github.event.release.tag_name }}
|
||||
|
||||
|
||||
@@ -29,16 +29,12 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
node-version: 24.18.0
|
||||
cache: "pnpm"
|
||||
bun-version: 1.4.2
|
||||
|
||||
- name: Run update script
|
||||
run: node ./scripts/update-contributors.mjs
|
||||
run: bun ./scripts/update-contributors.mjs
|
||||
|
||||
- name: Commit changes
|
||||
env:
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
||||
- name: Setup
|
||||
uses: ./tooling/github/setup
|
||||
- run: pnpm run scripts:update-readme-integrations
|
||||
- run: bun run scripts:update-readme-integrations
|
||||
- name: Commit changes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
||||
|
||||
@@ -13,10 +13,11 @@ on:
|
||||
- "packages/api/src/**"
|
||||
- "packages/auth/src/**"
|
||||
- "packages/integrations/src/**"
|
||||
- "tools/motion-reel/three/**"
|
||||
- "tooling/**"
|
||||
- "pnpm-lock.yaml"
|
||||
- "bun.lock"
|
||||
- "package.json"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "bunfig.toml"
|
||||
- "turbo.json"
|
||||
- ".github/workflows/deployment-docs.yml"
|
||||
- ".github/workflows/validate-docs.yml"
|
||||
@@ -35,20 +36,30 @@ jobs:
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
SKIP_ENV_VALIDATION: true
|
||||
TURBO_API: ${{ secrets.TURBO_API }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup
|
||||
uses: ./tooling/github/setup
|
||||
with:
|
||||
next-cache: "docs"
|
||||
turbo-cache: "true"
|
||||
- name: Validate the published API schema against the application
|
||||
run: pnpm check:openapi
|
||||
run: bun run check:openapi
|
||||
- name: Validate browser access and API schema rendering
|
||||
run: pnpm exec vitest run 'apps/nextjs/src/app/api/[...trpc]/route.spec.ts' apps/docs/src/lib/openapi.spec.ts
|
||||
run: bun x vitest run 'apps/nextjs/src/app/api/[...trpc]/route.spec.ts' apps/docs/src/lib/openapi.spec.ts
|
||||
- name: Build docs
|
||||
run: pnpm --filter @homarr/docs build
|
||||
run: bun run turbo run build --filter=@homarr/docs
|
||||
env:
|
||||
CI: "true"
|
||||
- name: Validate exported links, search, and metadata
|
||||
run: |
|
||||
pnpm --filter @homarr/docs validate:links
|
||||
pnpm --filter @homarr/docs verify:search
|
||||
pnpm --filter @homarr/docs verify:seo
|
||||
bun run --filter @homarr/docs validate:links
|
||||
bun run --filter @homarr/docs verify:search
|
||||
bun run --filter @homarr/docs verify:seo
|
||||
@@ -4,6 +4,7 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/workshop.yml"
|
||||
- "tooling/github/**"
|
||||
- "apps/workshop/**"
|
||||
- "apps/docs/**"
|
||||
- "packages/common/**"
|
||||
@@ -20,14 +21,14 @@ on:
|
||||
- "tooling/typescript/**"
|
||||
- "patches/**"
|
||||
- "package.json"
|
||||
- "pnpm-lock.yaml"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "bun.lock"
|
||||
- "bunfig.toml"
|
||||
- "turbo.json"
|
||||
- ".npmrc"
|
||||
push:
|
||||
branches: [dev, v2, release/v2, main]
|
||||
paths:
|
||||
- ".github/workflows/workshop.yml"
|
||||
- "tooling/github/**"
|
||||
- "apps/workshop/**"
|
||||
- "apps/docs/**"
|
||||
- "packages/common/**"
|
||||
@@ -44,10 +45,9 @@ on:
|
||||
- "tooling/typescript/**"
|
||||
- "patches/**"
|
||||
- "package.json"
|
||||
- "pnpm-lock.yaml"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "bun.lock"
|
||||
- "bunfig.toml"
|
||||
- "turbo.json"
|
||||
- ".npmrc"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -72,10 +72,44 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24.18.0
|
||||
- name: Build and exercise the combined production image
|
||||
- name: Set up Docker Buildx
|
||||
if: "!env.ACT"
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Build the production image
|
||||
if: "!env.ACT"
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: apps/workshop/Dockerfile
|
||||
target: production
|
||||
platforms: linux/${{ matrix.arch }}
|
||||
load: true
|
||||
tags: homarr-workshop:smoke
|
||||
cache-from: type=gha,scope=workshop-linux-${{ matrix.arch }}
|
||||
cache-to: type=gha,scope=workshop-linux-${{ matrix.arch }},mode=max
|
||||
secrets: |
|
||||
TURBO_API=${{ secrets.TURBO_API }}
|
||||
TURBO_TEAM=${{ secrets.TURBO_TEAM }}
|
||||
TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_REMOTE_CACHE_SIGNATURE_KEY=${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
|
||||
- name: Exercise the production image
|
||||
if: "!env.ACT"
|
||||
env:
|
||||
WORKSHOP_IMAGE_TEST_IMAGE: homarr-workshop:smoke
|
||||
run: sh apps/workshop/test-image.sh
|
||||
- name: Build and exercise the production image with Act
|
||||
if: env.ACT
|
||||
env:
|
||||
TURBO_API: ${{ secrets.TURBO_API }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
|
||||
run: sh apps/workshop/test-image.sh
|
||||
|
||||
validate:
|
||||
@@ -83,6 +117,7 @@ jobs:
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
- run: docker compose --env-file apps/workshop/.env.staging.example -f apps/workshop/docker-compose.staging.yml config --quiet
|
||||
|
||||
publish-amd64:
|
||||
@@ -97,6 +132,7 @@ jobs:
|
||||
digest: ${{ steps.build.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
- uses: docker/setup-buildx-action@v4
|
||||
- uses: docker/login-action@v4
|
||||
with:
|
||||
@@ -113,6 +149,11 @@ jobs:
|
||||
outputs: type=image,"name=ghcr.io/homarr-labs/workshop",push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=gha,scope=workshop-linux-amd64
|
||||
cache-to: type=gha,scope=workshop-linux-amd64,mode=max
|
||||
secrets: |
|
||||
TURBO_API=${{ secrets.TURBO_API }}
|
||||
TURBO_TEAM=${{ secrets.TURBO_TEAM }}
|
||||
TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_REMOTE_CACHE_SIGNATURE_KEY=${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
|
||||
|
||||
publish-arm64:
|
||||
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main')
|
||||
@@ -126,6 +167,7 @@ jobs:
|
||||
digest: ${{ steps.build.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
- uses: docker/setup-buildx-action@v4
|
||||
- uses: docker/login-action@v4
|
||||
with:
|
||||
@@ -142,6 +184,11 @@ jobs:
|
||||
outputs: type=image,"name=ghcr.io/homarr-labs/workshop",push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=gha,scope=workshop-linux-arm64
|
||||
cache-to: type=gha,scope=workshop-linux-arm64,mode=max
|
||||
secrets: |
|
||||
TURBO_API=${{ secrets.TURBO_API }}
|
||||
TURBO_TEAM=${{ secrets.TURBO_TEAM }}
|
||||
TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_REMOTE_CACHE_SIGNATURE_KEY=${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
|
||||
|
||||
publish:
|
||||
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main')
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
.pnpm-store/
|
||||
.bun-cache/
|
||||
.pnp
|
||||
.pnp.js
|
||||
.idea/
|
||||
@@ -36,7 +36,7 @@ tools/homarr-dev/homarr-dev
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
bun-debug.log*
|
||||
|
||||
# local env files
|
||||
.env
|
||||
@@ -81,7 +81,7 @@ skills-lock.json
|
||||
# docusaurus
|
||||
apps/docs/.docusaurus
|
||||
apps/docs/build
|
||||
.pnpm-store
|
||||
.bun-cache
|
||||
benchmark-results
|
||||
|
||||
# local performance investigation (never ship benchmark data or restored backups)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"mantine": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@mantine/mcp-server@9.6.0"]
|
||||
"command": "bun",
|
||||
"args": ["x", "@mantine/mcp-server@9.6.0"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
[tools]
|
||||
node = "24.18.0"
|
||||
@@ -1,2 +0,0 @@
|
||||
# pnpm v11: non-auth settings moved to pnpm-workspace.yaml
|
||||
# Keep this file for any future registry/auth configuration
|
||||
+2
-7
@@ -26,16 +26,11 @@
|
||||
"changelogFile": "CHANGELOG.md"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/npm",
|
||||
{
|
||||
"npmPublish": false
|
||||
}
|
||||
],
|
||||
"./scripts/release-version.mjs",
|
||||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
"assets": ["package.json", "CHANGELOG.md"],
|
||||
"assets": ["package.json", "bun.lock", "CHANGELOG.md"],
|
||||
"message": "chore(release): ${nextRelease.version} [skip ci]"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<script value="db:migration:postgresql:generate" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<script value="db:migration:sqlite:generate" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<script value="db:push" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<script value="db:studio" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<script value="dev" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<script value="docker:dev" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<script value="format" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<script value="format:fix" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<script value="test" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<script value="test:ui" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<script value="typecheck" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="pnpm" />
|
||||
<package-manager value="bun" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
Vendored
+9
-9
@@ -5,7 +5,7 @@
|
||||
"name": "dev",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm dev",
|
||||
"command": "bun run dev",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
},
|
||||
@@ -13,7 +13,7 @@
|
||||
"name": "docker dev",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm docker:dev",
|
||||
"command": "bun run docker:dev",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
},
|
||||
@@ -21,7 +21,7 @@
|
||||
"name": "lint fix",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm lint:fix",
|
||||
"command": "bun run lint:fix",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
},
|
||||
@@ -29,7 +29,7 @@
|
||||
"name": "format fix",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm format:fix",
|
||||
"command": "bun run format:fix",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
},
|
||||
@@ -37,7 +37,7 @@
|
||||
"name": "db push",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm db:push",
|
||||
"command": "bun run db:push",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
},
|
||||
@@ -45,7 +45,7 @@
|
||||
"name": "db studio",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm db:studio",
|
||||
"command": "bun run db:studio",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
},
|
||||
@@ -53,7 +53,7 @@
|
||||
"name": "db migration run",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm db:migration:run",
|
||||
"command": "bun run db:migration:sqlite:run",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
},
|
||||
@@ -61,7 +61,7 @@
|
||||
"name": "test",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm test",
|
||||
"command": "bun run test",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
},
|
||||
@@ -69,7 +69,7 @@
|
||||
"name": "test ui",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm test:ui",
|
||||
"command": "bun run test:ui",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
}
|
||||
|
||||
@@ -73,15 +73,15 @@ Only after this user-need test passes, use these locations:
|
||||
|
||||
## Monorepo Commands
|
||||
|
||||
- `pnpm dev` — Next.js app only
|
||||
- `pnpm dev:cli -- dev` — run the developer CLI without installing a global binary
|
||||
- `pnpm db:seed` — seed default database data explicitly
|
||||
- `pnpm docker:dev:up` — start the Redis development service in the background
|
||||
- `pnpm dev:docs` — Fumadocs site only
|
||||
- `pnpm turbo build` — build all packages
|
||||
- `pnpm turbo build --filter=@homarr/docs` — build docs only
|
||||
- `pnpm turbo typecheck` — typecheck all packages
|
||||
- `pnpm lint` / `pnpm format` — oxlint / oxfmt
|
||||
- `bun run dev` — Next.js app only
|
||||
- `bun run dev:cli -- dev` — run the developer CLI without installing a global binary
|
||||
- `bun run db:seed` — seed default database data explicitly
|
||||
- `bun run docker:dev:up` — start the Redis development service in the background
|
||||
- `bun run dev:docs` — Fumadocs site only
|
||||
- `bun run turbo build` — build all packages
|
||||
- `bun run turbo build --filter=@homarr/docs` — build docs only
|
||||
- `bun run turbo typecheck` — typecheck all packages
|
||||
- `bun run lint` / `bun run format` — oxlint / oxfmt
|
||||
|
||||
## Code Style
|
||||
|
||||
@@ -91,9 +91,9 @@ Only after this user-need test passes, use these locations:
|
||||
- Mantine: use the `mantine` MCP server in `.mcp.json` for current v9 APIs before writing component code. Prefer built-in primitives (`Combobox`/`useCombobox`, the polymorphic `component` prop, `@mantine/hooks`) and check `packages/ui/` for existing conventions first.
|
||||
- Icons: @tabler/icons-react
|
||||
- Docs app can import from `@homarr/definitions` for shared types
|
||||
- Run `pnpm dev:cli -- dev` to browse local `homarr:*` images and remote PR images.
|
||||
- Run `pnpm dev:cli -- build <name>` from a Homarr checkout to build `homarr:<name>` with rebuild provenance.
|
||||
- Run `pnpm dev:cli -- build --pr <number>` to build a PR locally from a temporary checkout.
|
||||
- Run `bun run dev:cli -- dev` to browse local `homarr:*` images and remote PR images.
|
||||
- Run `bun run dev:cli -- build <name>` from a Homarr checkout to build `homarr:<name>` with rebuild provenance.
|
||||
- Run `bun run dev:cli -- build --pr <number>` to build a PR locally from a temporary checkout.
|
||||
|
||||
## Testing
|
||||
|
||||
@@ -111,3 +111,14 @@ Portable skills live in `.agents/skills/`. Read the relevant `SKILL.md` before w
|
||||
- `documentation-sync` — user-critical documentation only
|
||||
- `mcp-integration` — safe tRPC-to-MCP exposure
|
||||
- `homarr-custom-widget` — safe Custom JSX v2 authoring
|
||||
|
||||
<!-- BEGIN:turborepo-agent-rules -->
|
||||
|
||||
# This is NOT the Turborepo you know
|
||||
|
||||
Turborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the `turbo` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run `node -p "require.resolve('turbo/package.json')"` from a workspace that depends on `turbo`.
|
||||
|
||||
Read `docs/README.md` inside that installed package first, then read the relevant pages from its `docs/` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.
|
||||
|
||||
This block is written and re-added by `turbo` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in `crates/turborepo-cli/src/cli/agent_guidance.rs`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set `"agentGuidance": false` in the root `turbo.json` or `turbo.jsonc` to opt out; this does not remove an existing block. When agent guidance is enabled, keep the block committed to avoid an uncommitted change on the next agent invocation. This repository disables agent guidance in `turbo.json`.
|
||||
<!-- END:turborepo-agent-rules -->
|
||||
+24
-62
@@ -1,100 +1,62 @@
|
||||
# syntax=docker/dockerfile:1.25
|
||||
|
||||
FROM oven/bun:1.4.2-alpine AS bun-tool
|
||||
|
||||
FROM node:24.18.0-alpine AS base
|
||||
|
||||
FROM base AS builder
|
||||
ARG TARGETPLATFORM
|
||||
WORKDIR /app
|
||||
# Native dependencies normally use prebuilds, but the build must remain
|
||||
# reproducible when a prebuild download times out and compilation is required.
|
||||
RUN apk add --no-cache libc6-compat curl bash python3 make g++
|
||||
|
||||
RUN corepack enable pnpm
|
||||
COPY .npmrc pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY --from=bun-tool /usr/local/bin/bun /usr/local/bin/bun
|
||||
# Compiler tools stay in the builder for optional native dependency fallbacks.
|
||||
RUN apk add --no-cache libstdc++ python3 make g++
|
||||
COPY bun.lock bunfig.toml package.json ./
|
||||
COPY patches ./patches
|
||||
COPY --parents ./apps/*/package.json ./packages/*/package.json ./tooling/*/package.json ./
|
||||
# @homarr/definitions generates documentation types during install.
|
||||
COPY --parents ./packages/definitions/src ./
|
||||
# Workaround for pnpm/pnpm#5268: pnpm fetch crashes when patchedDependencies
|
||||
# are configured with nodeLinker: hoisted. The applyPatchToDir function tries
|
||||
# to chdir into node_modules/<pkg> which doesn't exist during fetch (only the
|
||||
# content-addressable store is populated). By temporarily switching to the
|
||||
# isolated linker, patches apply inside the virtual store (node_modules/.pnpm/...)
|
||||
# which IS created by pnpm fetch. The original hoisted linker is restored
|
||||
# before the install step so the final node_modules layout stays flat.
|
||||
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store,sharing=locked \
|
||||
pnpm config set store-dir /pnpm/store && \
|
||||
sed -i 's/nodeLinker: hoisted/nodeLinker: isolated/' pnpm-workspace.yaml && \
|
||||
pnpm fetch --ignore-scripts && \
|
||||
sed -i 's/nodeLinker: isolated/nodeLinker: hoisted/' pnpm-workspace.yaml
|
||||
|
||||
# Install only from the fetched, committed lockfile so local and Docker builds
|
||||
# resolve the same dependency graph. Serial lifecycle builds avoid esbuild's
|
||||
# atomic binary replacement racing across the hoisted workspace (pnpm/pnpm#8200).
|
||||
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store,sharing=locked \
|
||||
npm_config_nodedir=/usr/local pnpm install --recursive --offline --frozen-lockfile --child-concurrency=1
|
||||
RUN --mount=type=cache,id=homarr-bun-cache,target=/root/.bun/install/cache,sharing=locked \
|
||||
npm_config_nodedir=/usr/local bun install --frozen-lockfile --concurrent-scripts=1
|
||||
|
||||
COPY . .
|
||||
|
||||
ARG SKIP_ENV_VALIDATION='true'
|
||||
ARG CI='true'
|
||||
ARG DISABLE_REDIS_LOGS='true'
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
RUN --mount=type=secret,id=TURBO_API,env=TURBO_API \
|
||||
--mount=type=secret,id=TURBO_TEAM,env=TURBO_TEAM \
|
||||
--mount=type=secret,id=TURBO_TOKEN,env=TURBO_TOKEN \
|
||||
--mount=type=secret,id=TURBO_REMOTE_CACHE_SIGNATURE_KEY,env=TURBO_REMOTE_CACHE_SIGNATURE_KEY \
|
||||
--mount=type=cache,id=homarr-next-build-${TARGETPLATFORM},target=/app/apps/nextjs/.next/cache,sharing=locked \
|
||||
TURBO_PLATFORM="${TARGETPLATFORM:-linux/amd64}" \
|
||||
pnpm turbo build --filter=@homarr/nextjs... --filter=@homarr/cli
|
||||
--mount=type=cache,id=homarr-turbo-${TARGETPLATFORM},target=/app/.turbo,sharing=locked \
|
||||
TURBO_PLATFORM="${TARGETPLATFORM:-linux/amd64}/musl/node-24.18.0" \
|
||||
bun run turbo run build --filter=@homarr/nextjs... --filter=@homarr/cli
|
||||
|
||||
FROM base AS runner
|
||||
FROM alpine:3.24.1 AS runner
|
||||
WORKDIR /app
|
||||
|
||||
# gettext is required for envsubst, openssl for generating AUTH_SECRET, su-exec for running application as non-root
|
||||
RUN apk add --no-cache redis nginx bash gettext su-exec openssl
|
||||
RUN mkdir /appdata
|
||||
COPY --from=base /usr/local/bin/node /usr/local/bin/node
|
||||
# envsubst, privilege drop and AUTH_SECRET generation are used by the entrypoint.
|
||||
RUN apk add --no-cache libstdc++ ca-certificates redis nginx bash gettext su-exec openssl && \
|
||||
mkdir -p /appdata /var/cache/nginx /var/log/nginx /var/lib/nginx \
|
||||
/run/nginx /etc/nginx/templates /etc/nginx/ssl/certs && \
|
||||
touch /run/nginx/nginx.pid
|
||||
VOLUME /appdata
|
||||
|
||||
# Enable homarr cli
|
||||
COPY --from=builder /app/packages/cli/cli.cjs /app/apps/cli/cli.cjs
|
||||
RUN echo $'#!/bin/bash\ncd /app/apps/cli && node ./cli.cjs "$@"' > /usr/bin/homarr
|
||||
RUN chmod +x /usr/bin/homarr
|
||||
|
||||
# Don't run production as root
|
||||
RUN mkdir -p /var/cache/nginx && \
|
||||
mkdir -p /var/log/nginx && \
|
||||
mkdir -p /var/lib/nginx && \
|
||||
touch /run/nginx/nginx.pid && \
|
||||
mkdir -p /etc/nginx/templates /etc/nginx/ssl/certs
|
||||
|
||||
COPY --from=builder /app/apps/nextjs/next.config.ts .
|
||||
COPY --from=builder /app/apps/nextjs/package.json .
|
||||
COPY --from=builder /app/pnpm-workspace.yaml ./pnpm-workspace.yaml
|
||||
|
||||
# Bundled CLI/migrations resolve the native binding from the application root.
|
||||
COPY --from=builder /app/node_modules/better-sqlite3/build/Release/better_sqlite3.node /app/build/better_sqlite3.node
|
||||
|
||||
RUN printf '#!/bin/sh\ncd /app/apps/cli && exec node ./cli.cjs "$@"\n' > /usr/bin/homarr && \
|
||||
chmod +x /usr/bin/homarr
|
||||
COPY --from=builder /app/packages/db/migrations ./db/migrations
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||
COPY --from=builder /app/apps/nextjs/.next/standalone ./
|
||||
COPY --from=builder /app/apps/nextjs/.next/static ./apps/nextjs/.next/static
|
||||
COPY --from=builder /app/apps/nextjs/public ./apps/nextjs/public
|
||||
# Ship only Next's traced production dependencies and application assets.
|
||||
COPY --from=builder /app/apps/nextjs/.output/standalone ./
|
||||
COPY scripts/run.sh ./run.sh
|
||||
COPY --chmod=755 scripts/entrypoint.sh ./entrypoint.sh
|
||||
COPY packages/redis/redis.conf /app/redis.conf
|
||||
COPY nginx.conf /etc/nginx/templates/nginx.conf
|
||||
|
||||
|
||||
ENV DB_URL='/appdata/db/db.sqlite'
|
||||
ENV DB_DIALECT='sqlite'
|
||||
ENV DB_DRIVER='better-sqlite3'
|
||||
ENV AUTH_PROVIDERS='credentials'
|
||||
ENV REDIS_IS_EXTERNAL='false'
|
||||
ENV NODE_ENV='production'
|
||||
|
||||
EXPOSE 7575
|
||||
ENTRYPOINT [ "/app/entrypoint.sh" ]
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
CMD ["sh", "run.sh"]
|
||||
@@ -5,7 +5,7 @@
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.pnpm-store/
|
||||
.bun-cache/
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
+10
-10
@@ -7,23 +7,23 @@ The source for [homarr.dev](https://homarr.dev), built with Next.js and Fumadocs
|
||||
From the repository root:
|
||||
|
||||
```bash
|
||||
pnpm dev:docs
|
||||
bun run dev:docs
|
||||
```
|
||||
|
||||
The site runs on `http://127.0.0.1:3003`. To use a local Workshop API:
|
||||
|
||||
```bash
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 bun run dev:docs
|
||||
```
|
||||
|
||||
## Validate
|
||||
|
||||
```bash
|
||||
pnpm --filter @homarr/docs typecheck
|
||||
pnpm --filter @homarr/docs build
|
||||
pnpm --filter @homarr/docs validate:links
|
||||
pnpm --filter @homarr/docs verify:search
|
||||
pnpm --filter @homarr/docs verify:seo
|
||||
bun run --filter @homarr/docs typecheck
|
||||
bun run --filter @homarr/docs build
|
||||
bun run --filter @homarr/docs validate:links
|
||||
bun run --filter @homarr/docs verify:search
|
||||
bun run --filter @homarr/docs verify:seo
|
||||
```
|
||||
|
||||
The static export is written to `out/`. The build validates docs pages and typed metadata for integrations and widgets
|
||||
@@ -54,7 +54,7 @@ Verify public item pages without changing the remote database:
|
||||
```bash
|
||||
REMOTE_WORKSHOP_URL=https://workshop.example.com
|
||||
WORKSHOP_TEST_URL=http://127.0.0.1:8093 WORKSHOP_REMOTE_API_URL="$REMOTE_WORKSHOP_URL" \
|
||||
node apps/workshop/tests/remote-workshop.integration.mjs
|
||||
bun apps/workshop/tests/remote-workshop.integration.mjs
|
||||
```
|
||||
|
||||
Stop it with `docker stop homarr-docs-static-preview`. Rebuild and restart the preview after changing source files;
|
||||
@@ -132,7 +132,7 @@ The launcher uses Homarr's existing public Kapa Website ID. The root layout load
|
||||
client-side navigation and follows the docs light or dark theme. **Search** and `Ctrl+K` / `Cmd+K` always use Fumadocs search, including when Kapa is unavailable.
|
||||
|
||||
Set `KAPA_WEBSITE_ID` at build time to use a different Kapa Website Widget integration. An explicitly empty value
|
||||
disables the widget, for example `KAPA_WEBSITE_ID= pnpm --filter @homarr/docs build`. This is a public integration ID
|
||||
disables the widget, for example `KAPA_WEBSITE_ID= bun run --filter @homarr/docs build`. This is a public integration ID
|
||||
included in exported pages, not a Kapa API key. Rebuild the static export after changing it.
|
||||
|
||||
In the Kapa dashboard, confirm the integration is live and enable the production and preview domains. Restoring the
|
||||
@@ -164,5 +164,5 @@ Carbon loads one visible placement after the desktop TOC, below API examples, or
|
||||
The homepage is excluded at every viewport size. Navigation reloads the ad script; resizing changes placement only when crossing its breakpoint. Ad blockers
|
||||
or no-fill responses must not prevent content, navigation, or search from working.
|
||||
|
||||
To preview Carbon locally, start docs with `NEXT_PUBLIC_ENABLE_CARBON_ADS=true pnpm dev:docs`. Production builds
|
||||
To preview Carbon locally, start docs with `NEXT_PUBLIC_ENABLE_CARBON_ADS=true bun run dev:docs`. Production builds
|
||||
enable ads automatically. When the TOC collapses into a toggle, the ad appears below the content before “Edit this page”.
|
||||
@@ -1,8 +1,8 @@
|
||||
# Developer Docker CLI
|
||||
|
||||
The host-side developer CLI builds and runs local Homarr images and pull-request previews with non-blocking background operations. Run it directly from a checkout with `pnpm dev:cli -- ...`; no shell-specific setup script is required. It is separate from the recovery CLI included in production images.
|
||||
The host-side developer CLI builds and runs local Homarr images and pull-request previews with non-blocking background operations. Run it directly from a checkout with `bun run dev:cli -- ...`; no shell-specific setup script is required. It is separate from the recovery CLI included in production images.
|
||||
|
||||
To install an optional `homarr` binary, run `pnpm dev:cli:install` and make your Go bin directory available on `PATH`. GitHub CLI authentication (`gh auth login`) is only needed for pull-request features.
|
||||
To install an optional `homarr` binary, run `bun run dev:cli:install` and make your Go bin directory available on `PATH`. GitHub CLI authentication (`gh auth login`) is only needed for pull-request features.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -14,7 +14,7 @@ To install an optional `homarr` binary, run `pnpm dev:cli:install` and make your
|
||||
## Interactive Development Browser
|
||||
|
||||
```bash
|
||||
pnpm dev:cli --
|
||||
bun run dev:cli --
|
||||
# or with global binary:
|
||||
homarr
|
||||
```
|
||||
@@ -66,7 +66,7 @@ Pulls and builds run asynchronously in the background. Press `t` to open the tas
|
||||
```bash
|
||||
homarr dash
|
||||
# or
|
||||
pnpm dev:cli -- dash
|
||||
bun run dev:cli -- dash
|
||||
```
|
||||
|
||||
The instance dashboard displays all running and stopped Homarr containers, published ports, health states, and resource volumes.
|
||||
@@ -81,7 +81,7 @@ The instance dashboard displays all running and stopped Homarr containers, publi
|
||||
|
||||
## CLI Commands
|
||||
|
||||
The commands below use the installed `homarr` binary. Without global installation, prefix each command with `pnpm dev:cli --` (for example, `pnpm dev:cli -- list`).
|
||||
The commands below use the installed `homarr` binary. Without global installation, prefix each command with `bun run dev:cli --` (for example, `bun run dev:cli -- list`).
|
||||
|
||||
### Launch Instances
|
||||
|
||||
@@ -133,7 +133,7 @@ homarr ci 6441 --watch
|
||||
homarr shell homarr-dev
|
||||
|
||||
# Run specific command in container
|
||||
homarr shell homarr-dev pnpm db:migration:sqlite:run
|
||||
homarr shell homarr-dev bun run db:migration:sqlite:run
|
||||
```
|
||||
|
||||
### Image & Volume Management
|
||||
|
||||
@@ -50,20 +50,20 @@ The repository's host-side CLI can build the current checkout and run it as a na
|
||||

|
||||
|
||||
```sh
|
||||
pnpm cli harness setup feature-board
|
||||
bun run cli harness setup feature-board
|
||||
```
|
||||
|
||||
This creates `homarr:feature-board`, starts it with persistent demo data, and prints the assigned URL. Query the port
|
||||
again with:
|
||||
|
||||
```sh
|
||||
pnpm cli harness port feature-board
|
||||
bun run cli harness port feature-board
|
||||
```
|
||||
|
||||
Pass environment overrides with repeated `--env` options:
|
||||
|
||||
```sh
|
||||
pnpm cli harness setup feature-board \
|
||||
bun run cli harness setup feature-board \
|
||||
--env WORKSHOP_WEB_URL=https://example.test \
|
||||
--env FEATURE_FLAG=true
|
||||
```
|
||||
|
||||
@@ -12,22 +12,22 @@ sidebar_position: 1
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Node.js 24.18 or newer**: You can download it from the [official website](https://nodejs.org/).
|
||||
- **Corepack**: Run `corepack enable` so the repository selects its pinned pnpm 11 version.
|
||||
- **Node.js 24.18.0**: application and tooling runtime, pinned in `mise.toml`.
|
||||
- **Bun 1.4.2**: Install the version pinned in `mise.toml` from the [official website](https://bun.sh/docs/installation).
|
||||
- **Git**: You can download it from the [official website](https://git-scm.com/).
|
||||
- **Docker or Docker Desktop**: Required for the Redis service and the developer Docker CLI.
|
||||
- **Go 1.25+**: Required only for the developer Docker CLI. You can download it from the [official website](https://go.dev/).
|
||||
- **GitHub CLI**: Required only for pull-request image features. Install it from the [official website](https://cli.github.com/), then authenticate with `gh auth login`.
|
||||
|
||||
The versions in the root `package.json` are the supported baseline. Let Corepack select the repository version instead
|
||||
of installing an unrelated global pnpm:
|
||||
Install dependencies once at the repository root. Bun uses a hoisted workspace installation, so compatible dependencies
|
||||
are shared across packages. Catalog versions, patches and overrides live in the root `package.json`.
|
||||
|
||||
```sh
|
||||
corepack enable
|
||||
corepack install
|
||||
pnpm install --frozen-lockfile
|
||||
bun install --frozen-lockfile
|
||||
```
|
||||
|
||||
Next.js, operational commands and SQLite run on Node.js. Bun installs dependencies and dispatches package scripts.
|
||||
|
||||
### Run the Homarr application
|
||||
|
||||
1. Copy `.env.example` to `.env`.
|
||||
@@ -46,30 +46,30 @@ cp .env.example .env
|
||||
Terminal 1:
|
||||
|
||||
```sh
|
||||
pnpm docker:dev
|
||||
bun run docker:dev
|
||||
```
|
||||
|
||||
Terminal 2:
|
||||
|
||||
```sh
|
||||
pnpm db:migration:sqlite:run
|
||||
pnpm dev
|
||||
bun run db:migration:sqlite:run
|
||||
bun run dev
|
||||
```
|
||||
|
||||
The infrastructure command remains in the foreground. Open `http://127.0.0.1:3000`; the first user created through
|
||||
Homarr becomes the instance administrator.
|
||||
Use `pnpm docker:dev:up` instead when you only need Redis in the background. Running the migration also seeds the
|
||||
default data; use `pnpm db:seed` to seed it explicitly later.
|
||||
Use `bun run docker:dev:up` instead when you only need Redis in the background. Running the migration also seeds the
|
||||
default data; use `bun run db:seed` to seed it explicitly later.
|
||||
|
||||
`pnpm dev` starts only Next.js. When your work needs scheduled tasks or live subscriptions, run the relevant service
|
||||
`bun run dev` starts only Next.js. When your work needs scheduled tasks or live subscriptions, run the relevant service
|
||||
in a separate terminal alongside it:
|
||||
|
||||
```sh
|
||||
pnpm --filter @homarr/tasks dev
|
||||
bun run --filter @homarr/tasks dev
|
||||
```
|
||||
|
||||
```sh
|
||||
pnpm --filter @homarr/websocket dev
|
||||
bun run --filter @homarr/websocket dev
|
||||
```
|
||||
|
||||
The WebSocket service listens on port 3001. Production startup embeds both services, so these extra commands are
|
||||
@@ -80,44 +80,44 @@ Workshop contributors can set up PocketBase and Fumadocs with the dedicated
|
||||
|
||||
### Useful commands
|
||||
|
||||
- `pnpm dev` — Next.js on port 3000.
|
||||
- `pnpm dev:benchmark` — authenticated-board benchmark; readiness-only `--smoke` runs are not performance claims.
|
||||
- `pnpm dev:cli -- dev` — browse and run local images and remote pull-request images.
|
||||
- `pnpm dev:cli -- build <name>` — build the current checkout as `homarr:<name>`.
|
||||
- `pnpm dev:cli -- build --pr <number>` — build a pull request from a temporary checkout.
|
||||
- `pnpm dev:cli -- rebuild <name>` — rebuild an image from its recorded checkout or pull request.
|
||||
- `pnpm dev:cli:install` — optionally install the developer CLI as a `homarr` binary.
|
||||
- `pnpm dev:docs` — Fumadocs on port 3003.
|
||||
- `pnpm docker:dev:up` — Redis development service in the background.
|
||||
- `pnpm cli` — Homarr operations CLI.
|
||||
- `pnpm db:migration:sqlite:run` — create or update a SQLite database.
|
||||
- `pnpm db:seed` — seed the default database data explicitly.
|
||||
- `pnpm db:studio` — inspect the database.
|
||||
- `pnpm format` / `pnpm format:fix` — check or apply oxfmt.
|
||||
- `pnpm lint` / `pnpm lint:fix` — check or apply oxlint.
|
||||
- `pnpm typecheck` — typecheck all workspaces.
|
||||
- `pnpm test` — unit and contract suites.
|
||||
- `pnpm build` — production monorepo build.
|
||||
- `bun run dev` — Next.js on port 3000.
|
||||
- `bun run dev:benchmark` — authenticated-board benchmark; readiness-only `--smoke` runs are not performance claims.
|
||||
- `bun run dev:cli -- dev` — browse and run local images and remote pull-request images.
|
||||
- `bun run dev:cli -- build <name>` — build the current checkout as `homarr:<name>`.
|
||||
- `bun run dev:cli -- build --pr <number>` — build a pull request from a temporary checkout.
|
||||
- `bun run dev:cli -- rebuild <name>` — rebuild an image from its recorded checkout or pull request.
|
||||
- `bun run dev:cli:install` — optionally install the developer CLI as a `homarr` binary.
|
||||
- `bun run dev:docs` — Fumadocs on port 3003.
|
||||
- `bun run docker:dev:up` — Redis development service in the background.
|
||||
- `bun run cli` — Homarr operations CLI.
|
||||
- `bun run db:migration:sqlite:run` — create or update a SQLite database.
|
||||
- `bun run db:seed` — seed the default database data explicitly.
|
||||
- `bun run db:studio` — inspect the database.
|
||||
- `bun run format` / `bun run format:fix` — check or apply oxfmt.
|
||||
- `bun run lint` / `bun run lint:fix` — check or apply oxlint.
|
||||
- `bun run typecheck` — typecheck all workspaces.
|
||||
- `bun run test` — unit and contract suites.
|
||||
- `bun run build` — production monorepo build.
|
||||
- `docker build -t homarr:local .` — build a production Docker image.
|
||||
- `docker run -p 7575:7575 -e SECRET_ENCRYPTION_KEY='your_64_character_hex_string' homarr:local` — run it.
|
||||
|
||||
## CI and focused validation
|
||||
|
||||
The main CI workflow runs lint, typechecking, affected workspace builds, workspace configuration checks, OpenAPI validation, and Custom Widget architecture and bundle checks. Unit and E2E suites do not run in CI. Preview images publish after the Fast gate and both architecture builds succeed; browser tests do not block publication. The container job builds an amd64 image when no preview image was built.
|
||||
The main CI workflow validates SQLite and PostgreSQL migrations before lint, typechecking, affected workspace builds, workspace configuration, OpenAPI, and Custom Widget architecture and bundle checks. Manual container validation builds an amd64 image and exercises MySQL conversion, authentication, and restart persistence. Preview publication requires the Fast gate and both architecture builds.
|
||||
|
||||
Workshop CI runs only when its workflow or Docker build inputs change. It validates Compose configuration and publishes production images on its configured release branches. Its Docker integration and image tests are manual; workspace typechecks run in the main Fast gate.
|
||||
Workshop CI runs when its workflow or Docker build inputs change. Pull requests exercise production images on amd64 and arm64; release branches publish them after Compose validation. Workspace typechecks run in the main Fast gate.
|
||||
|
||||
Choose the smallest relevant suite when changing behavior:
|
||||
|
||||
| Command | Scope | Requirements |
|
||||
| ------------------------------------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| `pnpm test <file>` | Focused unit or contract tests | Workspace dependencies |
|
||||
| `pnpm test` | All unit and contract tests, without Docker integration suites | Workspace dependencies |
|
||||
| `pnpm test:coverage` | Unit and contract tests with coverage | Workspace dependencies |
|
||||
| `pnpm test:integration <file>` | Docker-backed service and database compatibility tests | Docker |
|
||||
| `pnpm test:e2e <file>` | Browser and container scenarios | Docker; Playwright Chromium and system dependencies for browser tests |
|
||||
| `pnpm test:docs-screenshots` | Regenerate Assistant documentation screenshots | Same browser prerequisites |
|
||||
| `pnpm test:workshop` / `pnpm test:workshop-image` | Workshop integration / production-image validation | Docker |
|
||||
| Command | Scope | Requirements |
|
||||
| ------------------------------------------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| `bun run test <file>` | Focused unit or contract tests | Workspace dependencies |
|
||||
| `bun run test` | All unit and contract tests, without Docker integration suites | Workspace dependencies |
|
||||
| `bun run test:coverage` | Unit and contract tests with coverage | Workspace dependencies |
|
||||
| `bun run test:integration <file>` | Docker-backed service and database compatibility tests | Docker |
|
||||
| `bun run test:e2e <file>` | Browser and container scenarios | Docker; Playwright Chromium and system dependencies for browser tests |
|
||||
| `bun run test:docs-screenshots` | Regenerate Assistant documentation screenshots | Same browser prerequisites |
|
||||
| `bun run test:workshop` / `bun run test:workshop-image` | Workshop integration / production-image validation | Docker |
|
||||
|
||||
Omit `<file>` to run all tests in that command's scope. E2E and screenshot commands use a local image tagged `homarr-e2e`, or an image selected with `HOMARR_E2E_IMAGE`. The default commands run once without automatic retries; coverage is opt-in. The screenshot generator is excluded from both unit and E2E commands.
|
||||
|
||||
@@ -125,17 +125,17 @@ Keep behavioral coverage for authorization, migrations, persistence, and widget
|
||||
|
||||
## Documentation development
|
||||
|
||||
Run `pnpm dev:docs` to open the Next.js and Fumadocs site at `http://127.0.0.1:3003`. Content lives in
|
||||
Run `bun run dev:docs` to open the Next.js and Fumadocs site at `http://127.0.0.1:3003`. Content lives in
|
||||
`apps/docs/docs/`; the nearest `meta.json` controls navigation. Keep integration and widget setup metadata in their
|
||||
typed `index.ts` files alongside the page.
|
||||
|
||||
After changing content, navigation, or shared MDX components, run:
|
||||
|
||||
```sh
|
||||
pnpm --filter @homarr/docs build
|
||||
pnpm --filter @homarr/docs validate:links
|
||||
pnpm --filter @homarr/docs verify:search
|
||||
pnpm --filter @homarr/docs verify:seo
|
||||
bun run --filter @homarr/docs build
|
||||
bun run --filter @homarr/docs validate:links
|
||||
bun run --filter @homarr/docs verify:search
|
||||
bun run --filter @homarr/docs verify:seo
|
||||
```
|
||||
|
||||
The build writes a static site to `apps/docs/out/`. Check heading links, search results, and the page's Markdown export
|
||||
|
||||
@@ -22,7 +22,7 @@ docker compose --env-file apps/workshop/.env -f apps/workshop/docker-compose.yml
|
||||
Start Fumadocs in another terminal:
|
||||
|
||||
```sh
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 bun run dev:docs
|
||||
```
|
||||
|
||||
PocketBase runs at `http://127.0.0.1:8090`; create the first superuser under `/_/`. Workshop is available at
|
||||
|
||||
@@ -12,7 +12,7 @@ A Helm chart to deploy homarr for Kubernetes
|
||||
|
||||
## Source Code
|
||||
|
||||
* <https://github.com/homarr-labs/homarr>
|
||||
- <https://github.com/homarr-labs/homarr>
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -76,38 +76,38 @@ To avoid including sensitive information in plain text within your version contr
|
||||
|
||||
Below is an exhaustive list of all secrets:
|
||||
|
||||
| FEATURE | SECRET NAME | SECRET KEYS | Required |
|
||||
|-----------|-------------------------|-----------------------------------------|----------|
|
||||
| OIDC | auth-oidc-secret | oidc-client-id<br>oidc-client-secret | No |
|
||||
| LDAP | auth-ldap-secret | bind-password | No |
|
||||
| DATABASE | db-secret | db-url | No |
|
||||
| DATABASE | db-encryption | db-encryption-key | yes |
|
||||
| FEATURE | SECRET NAME | SECRET KEYS | Required |
|
||||
| -------- | ---------------- | ------------------------------------ | -------- |
|
||||
| OIDC | auth-oidc-secret | oidc-client-id<br>oidc-client-secret | No |
|
||||
| LDAP | auth-ldap-secret | bind-password | No |
|
||||
| DATABASE | db-secret | db-url | No |
|
||||
| DATABASE | db-encryption | db-encryption-key | yes |
|
||||
|
||||
### Database
|
||||
|
||||
You have multiple options for configuring the database:
|
||||
|
||||
| DRIVER TYPE | Persistence mode |
|
||||
|----------------|---------------------------------|
|
||||
| better-sqlite3 | Pod disk |
|
||||
| better-sqlite3 | homarr-database PVC |
|
||||
| mysql2 | External MySql database |
|
||||
| node-postgres | External Postgresql database |
|
||||
| DRIVER TYPE | Persistence mode |
|
||||
| -------------- | ---------------------------- |
|
||||
| better-sqlite3 | Pod disk |
|
||||
| better-sqlite3 | homarr-database PVC |
|
||||
| mysql2 | External MySql database |
|
||||
| node-postgres | External Postgresql database |
|
||||
|
||||
#### Pod disk
|
||||
|
||||
No additional configuration is required. However, keep in mind that if the pod restarts, all data will be lost. This setup is not *recommended* for production use.
|
||||
No additional configuration is required. However, keep in mind that if the pod restarts, all data will be lost. This setup is not _recommended_ for production use.
|
||||
|
||||
To create the necessary database secret, execute the following command:
|
||||
|
||||
<details>
|
||||
<summary>Required Secrets</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
kubectl create secret generic db-encryption \
|
||||
--from-literal=db-encryption-key='<SECRET_ENCRYPTION_KEY_SECRET_TO_CHANGE>' \
|
||||
--namespace homarr
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -120,11 +120,11 @@ Associated secret to create :
|
||||
<details>
|
||||
<summary>DB Required Secrets</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
kubectl create secret generic db-encryption \
|
||||
--from-literal=db-encryption-key='<SECRET_ENCRYPTION_KEY_SECRET_TO_CHANGE>' \
|
||||
--namespace homarr
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -133,13 +133,13 @@ Bellow an example of the override value file :
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
persistence:
|
||||
homarrDatabase:
|
||||
enabled: true
|
||||
storageClassName: "default"
|
||||
size: "1Gi"
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -150,17 +150,18 @@ To create the necessary database secrets, execute the following command:
|
||||
<details>
|
||||
<summary>Required Secrets</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
kubectl create secret generic db-encryption \
|
||||
--from-literal=db-encryption-key='<SECRET_ENCRYPTION_KEY_SECRET_TO_CHANGE>' \
|
||||
--namespace homarr
|
||||
````
|
||||
```
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
kubectl create secret generic db-secret \
|
||||
--from-literal=db-url='mysql://user:password@host:port/homarrdb' \
|
||||
--namespace homarr
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Below is an example of the override values file:
|
||||
@@ -168,10 +169,11 @@ Below is an example of the override values file:
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
database:
|
||||
type: mysql
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### External Postgresql database
|
||||
@@ -181,17 +183,17 @@ To create the necessary database secrets, execute the following command:
|
||||
<details>
|
||||
<summary>Required Secrets</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
kubectl create secret generic db-encryption \
|
||||
--from-literal=db-encryption-key='<SECRET_ENCRYPTION_KEY_SECRET_TO_CHANGE>' \
|
||||
--namespace homarr
|
||||
````
|
||||
```
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
kubectl create secret generic db-secret \
|
||||
--from-literal=db-url='postgresql://user:password@host:port/homarrdb' \
|
||||
--namespace homarr
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -200,10 +202,11 @@ Below is an example of the override values file:
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
database:
|
||||
type: postgresql
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Ingress
|
||||
@@ -213,7 +216,7 @@ The ingress section in the values.yaml file allows you to configure how external
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
service:
|
||||
enabled: true # Ensure the service is enabled for Ingress to route traffic
|
||||
ingress:
|
||||
@@ -231,7 +234,8 @@ ingress:
|
||||
- "homarr.homelab.dev"
|
||||
- "www.homarr.homelab.dev"
|
||||
secretName: homelab-tls
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### HTTPRoute (Gateway API)
|
||||
@@ -242,9 +246,9 @@ This provides a more expressive and future-proof alternative to Ingress, with su
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
service:
|
||||
enabled: true # must be enabled for HTTPRoute to forward traffic
|
||||
enabled: true # must be enabled for HTTPRoute to forward traffic
|
||||
httproute:
|
||||
enabled: true
|
||||
parentRefs:
|
||||
@@ -260,14 +264,15 @@ httproute:
|
||||
backendRefs:
|
||||
- name: homarr
|
||||
port: 7575
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Notes:
|
||||
|
||||
- parentRefs: the Gateway(s) this route attaches to. Gateways must already exist.
|
||||
- hostnames: domain names this route applies to.
|
||||
- rules: each rule can define matches (paths, headers, queries), optional filters, and backendRefs to services
|
||||
- rules: each rule can define matches (paths, headers, queries), optional filters, and backendRefs to services
|
||||
- TLS must be configured on the Gateway (not on HTTPRoute). For example, the Gateway can have a TLS listener for homarr.homelab.dev, and this route will automatically apply once matched.
|
||||
|
||||
### Certificates
|
||||
@@ -284,7 +289,7 @@ Use inline certificates to generate a ConfigMap, mounted as individual files.
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
persistence:
|
||||
homarrTrustedCerts:
|
||||
enabled: true
|
||||
@@ -298,7 +303,8 @@ persistence:
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID...EFGH==
|
||||
-----END CERTIFICATE-----
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Behavior:
|
||||
@@ -313,7 +319,7 @@ Use inline certificates to generate a Kubernetes Secret, mounted as files.
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
persistence:
|
||||
homarrTrustedCerts:
|
||||
enabled: true
|
||||
@@ -327,7 +333,8 @@ persistence:
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC...XYZ==
|
||||
-----END CERTIFICATE-----
|
||||
````
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Behavior:
|
||||
@@ -342,16 +349,17 @@ Use an existing Kubernetes Secret, assuming its keys are filenames.
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
persistence:
|
||||
homarrTrustedCerts:
|
||||
enabled: true
|
||||
type: existingSecret
|
||||
existingSecretName: "existingSecretName"
|
||||
existingSecretKeys:
|
||||
- cert3.crt
|
||||
- cert4.crt
|
||||
````
|
||||
- cert3.crt
|
||||
- cert4.crt
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Behavior:
|
||||
@@ -372,114 +380,115 @@ All available values are listed on the [artifacthub](https://artifacthub.io/pack
|
||||
|
||||
## Values
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| additionalObjects | list | `[]` | Additional resources to deploy. These objects are templated. |
|
||||
| affinity | object | `{}` | Node affinity for pod scheduling |
|
||||
| autoscaling.enabled | bool | `false` | Enable autoscaling |
|
||||
| autoscaling.maxReplicas | int | `100` | Maximum replicas |
|
||||
| autoscaling.minReplicas | int | `1` | Minimum replicas |
|
||||
| autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization for autoscaling |
|
||||
| containerPorts | object | `{"http":{"port":7575,"protocol":"TCP"}}` | containerPorts defines the ports to open on the container. It is a map where each entry specifies: - `port` (int) (required): The port number to expose inside the container. - `protocol` (string) (required): The network protocol (TCP or UDP) used for the port. - `disabled` (bool) : Optional flag to disable this port (defaults to false). Can be overridden via Helm values. By default, this configuration exposes TCP port 7575 with the name `http`. |
|
||||
| database.migrationEnabled | bool | `true` | Database migration configuration. DB_MIGRATIONS_DISABLED Set to `true` to disable database migrations. Migrations are enabled by default (`false`). |
|
||||
| database.type | string | `"sqlite"` | Database type: sqlite, mysql or postgresql |
|
||||
| env.AUTH_COOKIE_PREFIX | string | `"homarr"` | Prefix used for all authentication cookies. Change if you run another Auth.js/NextAuth app on the same hostname to avoid cookie name collisions. Only letters, numbers, hyphens and underscores. |
|
||||
| env.AUTH_LDAP_BASE | string | `nil` | Base dn of your LDAP server |
|
||||
| env.AUTH_LDAP_BIND_DN | string | `nil` | User used for finding users and groups |
|
||||
| env.AUTH_LDAP_GROUP_CLASS | string | `"groupOfUniqueNames"` | Class used for querying groups |
|
||||
| env.AUTH_LDAP_GROUP_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user's groups search filter (& based) |
|
||||
| env.AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE | string | `"member"` | Attribute used for querying group member |
|
||||
| env.AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE | string | `"dn"` | User attribute used for comparing with group member |
|
||||
| env.AUTH_LDAP_SEARCH_SCOPE | string | `"base"` | LDAP search scope between base, one or sub |
|
||||
| env.AUTH_LDAP_URI | string | `nil` | URI of your LDAP server |
|
||||
| env.AUTH_LDAP_USERNAME_ATTRIBUTE | string | `"uid"` | Attribute used for username |
|
||||
| env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user search filter (& based) |
|
||||
| env.AUTH_LDAP_USER_MAIL_ATTRIBUTE | string | `"mail"` | Attribute used for mail field |
|
||||
| env.AUTH_LOGOUT_REDIRECT_URL | string | `nil` | URL to redirect to after clicking logging out. |
|
||||
| env.AUTH_OIDC_AUTO_LOGIN | string | `"false"` | Automatically redirect to OIDC login |
|
||||
| env.AUTH_OIDC_CLIENT_NAME | string | `"OIDC"` | Display name of provider (in login screen) |
|
||||
| env.AUTH_OIDC_GROUPS_ATTRIBUTE | string | `"groups"` | Attribute used for groups (roles) claim |
|
||||
| env.AUTH_OIDC_ISSUER | string | `nil` | Issuer URI of OIDC provider without trailing slash (/) |
|
||||
| env.AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE | string | `nil` | Overwrite name attribute. By default, it will use preferred_username if it does not contain a @ and otherwise name. |
|
||||
| env.AUTH_OIDC_SCOPE_OVERWRITE | string | `"openid email profile groups"` | Override the OIDC scopes |
|
||||
| env.AUTH_PROVIDERS | string | `"credentials"` | Enabled authentication methods. Multiple providers can be enabled with by separating them with , (ex. AUTH_PROVIDERS=credentials,oidc, it is highly recommended to just enable one provider). |
|
||||
| env.AUTH_SESSION_EXPIRY_TIME | string | `"30d"` | Time for the session to time out. Can be set as pure number, which will automatically be used in seconds, or followed by s, m, h or d for seconds, minutes, hours or days. (ex: "30m") |
|
||||
| env.ENABLE_DNS_CACHING | string | `"false"` | Enables dns caching. This is not yet working for all users. See #4006 |
|
||||
| env.LOG_LEVEL | string | `"info"` | Log level to use. Possible values: debug/info/warn/error |
|
||||
| env.NO_EXTERNAL_CONNECTION | string | `"false"` | Disables some requests that need internet connection |
|
||||
| env.TZ | string | `"Europe/Paris"` | Your local time zone |
|
||||
| envSecrets.authLdapCredentials.existingSecret | string | `"auth-ldap-secret"` | Name of existing secret containing LDAP credentials |
|
||||
| envSecrets.authLdapCredentials.ldapBindingPassword | string | `"bind-password"` | Password for bind user secret key |
|
||||
| envSecrets.authOidcCredentials.existingSecret | string | `"auth-oidc-secret"` | Name of existing secret containing OIDC credentials |
|
||||
| envSecrets.authOidcCredentials.oidcClientId | string | `"oidc-client-id"` | ID of OIDC client (application) secret key |
|
||||
| envSecrets.authOidcCredentials.oidcClientSecret | string | `"oidc-client-secret"` | Secret of OIDC client (application) secret key |
|
||||
| envSecrets.dbCredentials.dbUrlKey | string | `"db-url"` | Secret key for DB_URL Example for external database: `mysql://username:password@host:port/homarrdb` or `postgresql://username:password@host:port/homarrdb` |
|
||||
| envSecrets.dbCredentials.existingSecret | string | `"db-secret"` | Name of existing secret containing DB credentials |
|
||||
| envSecrets.dbEncryption.existingSecret | string | `"db-encryption"` | Name of existing secret containing DB encryption |
|
||||
| envSecrets.dbEncryption.key | string | `"db-encryption-key"` | Secret key for SECRET_ENCRYPTION_KEY can be generated with `openssl rand -hex 32` |
|
||||
| fullnameOverride | string | `""` | Overrides chart's fullname |
|
||||
| hostAliases | list | `[]` | Add static entries to /etc/hosts in the Pod. This is useful in the following cases: - You are running in a dual-stack cluster (IPv4 + IPv6) and want to force usage of IPv4 for specific hostnames - Your application is having DNS resolution issues or IPv6 preference issues - You need to override or simulate DNS entries without changing global DNS - You are running in an air-gapped or isolated environment without external DNS Example: hostAliases: - ip: "192.168.1.10" hostnames: - "example.com" - "example.internal" |
|
||||
| httproute | object | `{"enabled":false,"hostnames":["chart-example.local"],"parentRefs":[{"name":"my-gateway","namespace":"default"}],"rules":[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]}` | Gateway API HTTPRoute configuration |
|
||||
| httproute.enabled | bool | `false` | Enable HTTPRoute |
|
||||
| httproute.hostnames | list | `["chart-example.local"]` | Hostnames this route matches (similar to ingress.hosts.host) |
|
||||
| httproute.parentRefs | list | `[{"name":"my-gateway","namespace":"default"}]` | References to the parent Gateway(s) this route attaches to. Each item must include at least a `name`, and optionally a `namespace`. |
|
||||
| httproute.rules | list | `[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]` | List of routing rules. Each rule can include: - matches: path/header/query matching - filters: optional transformations (redirects, header modifications, etc.) - backendRefs: one or more Kubernetes Services to forward traffic to |
|
||||
| httproute.rules[0].filters | list | `[]` | Optional filters for this rule (default: empty) |
|
||||
| httproute.rules[0].matches[0].path.type | string | `"PathPrefix"` | Path match type. One of: Exact, PathPrefix, RegularExpression |
|
||||
| httproute.rules[0].matches[0].path.value | string | `"/"` | Path value to match |
|
||||
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
|
||||
| image.repository | string | `"ghcr.io/homarr-labs/homarr"` | Image repository |
|
||||
| image.tag | string | `"v2.2.0"` | Overrides the image tag whose default is the chart appVersion |
|
||||
| imagePullSecrets | list | `[]` | Secrets for Docker registry |
|
||||
| ingress.annotations | object | `{}` | Ingress annotations |
|
||||
| ingress.enabled | bool | `false` | Enable ingress |
|
||||
| ingress.hosts | list | `[{"host":"chart-example.local","paths":[{"path":"/"}]}]` | Ingress hosts configuration |
|
||||
| ingress.ingressClassName | string | `""` | Ingress class name |
|
||||
| ingress.tls | list | `[]` | Ingress TLS configuration |
|
||||
| livenessProbe.failureThreshold | int | `3` | Failure threshold for liveness probe - number of consecutive failures before pod is restarted |
|
||||
| livenessProbe.httpGet.path | string | `"/api/health/live"` | This is the liveness check endpoint used by Kubernetes to determine if the application is still running. |
|
||||
| livenessProbe.httpGet.port | int | `7575` | The port on which the liveness check will be performed. This must be the same as the container port exposed by the application. |
|
||||
| livenessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the liveness probe starts |
|
||||
| livenessProbe.periodSeconds | int | `10` | Period in seconds between liveness probe checks |
|
||||
| livenessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each liveness probe check |
|
||||
| nameOverride | string | `""` | Overrides chart's name |
|
||||
| nodeSelector | object | `{}` | Node selectors for pod scheduling |
|
||||
| persistence.homarrDatabase.accessMode | string | `"ReadWriteOnce"` | homarr-database access mode |
|
||||
| persistence.homarrDatabase.enabled | bool | `false` | Enable homarr-database persistent storage |
|
||||
| persistence.homarrDatabase.mountPath | string | `"/appdata"` | homarr-database mount path inside the pod |
|
||||
| persistence.homarrDatabase.name | string | `"homarr-database"` | homarr-database persistent storage name |
|
||||
| persistence.homarrDatabase.size | string | `"50Mi"` | homarr-database storage size |
|
||||
| persistence.homarrDatabase.storageClassName | string | `"local-path"` | homarr-database storage class name |
|
||||
| persistence.homarrDatabase.volumeClaimName | string | `""` | homarr-database optional volumeClaimName to target specific PV |
|
||||
| persistence.homarrTrustedCerts.certificates | string | `nil` | homarr-trusted-certificates certificates, each entry will become a new trusted certificate as a dedicated file (works only for "configmap" and "secret" mode) |
|
||||
| persistence.homarrTrustedCerts.enabled | bool | `false` | Enable trusted certificates persistence |
|
||||
| persistence.homarrTrustedCerts.existingSecretKeys | string | `nil` | List of keys (filenames) to mount from the existing secret (used only when type is "existingSecret") |
|
||||
| persistence.homarrTrustedCerts.existingSecretName | string | `""` | Name of the existing Kubernetes Secret to mount (required if type is "existingSecret") |
|
||||
| persistence.homarrTrustedCerts.mountPath | string | `"/appdata/trusted-certificates"` | homarr-trusted-certificates mount path inside the pod |
|
||||
| persistence.homarrTrustedCerts.type | string | `"configmap"` | Persistence mode can be : configmap (declarative), secret (declarative) or existingSecret (mount an existing Kubernetes Secret by name and specify which keys to mount as files) |
|
||||
| podAnnotations | object | `{}` | Pod annotations |
|
||||
| podLabels | object | `{}` | Pod labels |
|
||||
| podSecurityContext | object | `{}` | Pod security context |
|
||||
| rbac | object | `{"enabled":false}` | Enable RBAC resources for Kubernetes integration Creates Role, ClusterRole, and associated bindings for Homarr's Kubernetes features |
|
||||
| rbac.enabled | bool | `false` | Enable to create RBAC resources and activate Kubernetes integration |
|
||||
| readinessProbe.failureThreshold | int | `3` | Failure threshold for readiness probe - number of consecutive failures before pod is considered unready |
|
||||
| readinessProbe.httpGet.path | string | `"/api/health/ready"` | This is the readiness check endpoint used by Kubernetes to determine if the application is ready to handle traffic. |
|
||||
| readinessProbe.httpGet.port | int | `7575` | The port on which the readiness check will be performed. This must match the container's exposed port. |
|
||||
| readinessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the readiness probe starts. increase this value if the pod is slow to fully start. |
|
||||
| readinessProbe.periodSeconds | int | `10` | Period in seconds between readiness probe checks |
|
||||
| readinessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each readiness probe check |
|
||||
| replicaCount | int | `1` | Number of replicas |
|
||||
| resources | object | `{}` | Resource configuration |
|
||||
| securityContext | object | `{}` | Security context |
|
||||
| service.enabled | bool | `true` | Enable service |
|
||||
| service.ipFamilies | list | `[]` | List of IP families to use for the service. Examples: - ["IPv4"] - ["IPv6"] - ["IPv4", "IPv6"] for dual-stack Leave empty to use cluster default behavior |
|
||||
| service.ipFamilyPolicy | string | `"SingleStack"` | Defines how the service assigns IP families (IPv4/IPv6) Possible values: - SingleStack (default): Only one IP family, usually IPv4 - PreferDualStack: Use dual-stack if the cluster supports it, fallback to single - RequireDualStack: Fail if dual-stack cannot be assigned |
|
||||
| service.ports.app.port | int | `7575` | Service port |
|
||||
| service.ports.app.protocol | string | `"TCP"` | Service protocol |
|
||||
| service.ports.app.targetPort | string | `"http"` | Service target port |
|
||||
| service.type | string | `"ClusterIP"` | Service type |
|
||||
| strategyType | string | `"RollingUpdate"` | `strategyType` specifies the strategy used to replace old Pods by new ones. `strategyType` can be `"Recreate"` or `"RollingUpdate"`. `"RollingUpdate"` is the default value and updates Pods in a rolling update fashion. `"Recreate"` will kill all existing Pods before new ones are created. The `"Recreate"` strategy is necessary when persistent volume's `accessMode` is set to `"ReadWriteOnce"` when using `helm upgrade`, as pod volume attachments to an existing PersistentVolumeClaim need to be cleared before a new pod can attach to it. |
|
||||
| tolerations | list | `[]` | Node tolerations for pod scheduling |
|
||||
| Key | Type | Default | Description |
|
||||
| -------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| additionalObjects | list | `[]` | Additional resources to deploy. These objects are templated. |
|
||||
| affinity | object | `{}` | Node affinity for pod scheduling |
|
||||
| autoscaling.enabled | bool | `false` | Enable autoscaling |
|
||||
| autoscaling.maxReplicas | int | `100` | Maximum replicas |
|
||||
| autoscaling.minReplicas | int | `1` | Minimum replicas |
|
||||
| autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization for autoscaling |
|
||||
| containerPorts | object | `{"http":{"port":7575,"protocol":"TCP"}}` | containerPorts defines the ports to open on the container. It is a map where each entry specifies: - `port` (int) (required): The port number to expose inside the container. - `protocol` (string) (required): The network protocol (TCP or UDP) used for the port. - `disabled` (bool) : Optional flag to disable this port (defaults to false). You can override these ports via Helm values. By default, this configuration exposes TCP port 7575 with the name `http`. |
|
||||
| database.migrationEnabled | bool | `true` | Database migration configuration. DB_MIGRATIONS_DISABLED Set to `true` to disable database migrations. Migrations are enabled by default (`false`). |
|
||||
| database.type | string | `"sqlite"` | Database type: sqlite, mysql or postgresql |
|
||||
| env.AUTH_COOKIE_PREFIX | string | `"homarr"` | Prefix used for all authentication cookies. Change if you run another Auth.js/NextAuth app on the same hostname to avoid cookie name collisions. Only letters, numbers, hyphens and underscores. |
|
||||
| env.AUTH_LDAP_BASE | string | `nil` | Base dn of your LDAP server |
|
||||
| env.AUTH_LDAP_BIND_DN | string | `nil` | User used for finding users and groups |
|
||||
| env.AUTH_LDAP_GROUP_CLASS | string | `"groupOfUniqueNames"` | Class used for querying groups |
|
||||
| env.AUTH_LDAP_GROUP_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user's groups search filter (& based) |
|
||||
| env.AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE | string | `"member"` | Attribute used for querying group member |
|
||||
| env.AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE | string | `"dn"` | User attribute used for comparing with group member |
|
||||
| env.AUTH_LDAP_SEARCH_SCOPE | string | `"base"` | LDAP search scope between base, one or sub |
|
||||
| env.AUTH_LDAP_URI | string | `nil` | URI of your LDAP server |
|
||||
| env.AUTH_LDAP_USERNAME_ATTRIBUTE | string | `"uid"` | Attribute used for username |
|
||||
| env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user search filter (& based) |
|
||||
| env.AUTH_LDAP_USER_MAIL_ATTRIBUTE | string | `"mail"` | Attribute used for mail field |
|
||||
| env.AUTH_LOGOUT_REDIRECT_URL | string | `nil` | URL to redirect to after clicking logging out. |
|
||||
| env.AUTH_OIDC_AUTO_LOGIN | string | `"false"` | Automatically redirect to OIDC login |
|
||||
| env.AUTH_OIDC_CLIENT_NAME | string | `"OIDC"` | Display name of provider (in login screen) |
|
||||
| env.AUTH_OIDC_GROUPS_ATTRIBUTE | string | `"groups"` | Attribute used for groups (roles) claim |
|
||||
| env.AUTH_OIDC_ISSUER | string | `nil` | Issuer URI of OIDC provider without trailing slash (/) |
|
||||
| env.AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE | string | `nil` | Overwrite name attribute. By default, it will use preferred_username if it does not contain a @ and otherwise name. |
|
||||
| env.AUTH_OIDC_SCOPE_OVERWRITE | string | `"openid email profile groups"` | Override the OIDC scopes |
|
||||
| env.AUTH_PROVIDERS | string | `"credentials"` | Enabled authentication methods. Multiple providers can be enabled with by separating them with , (ex. AUTH_PROVIDERS=credentials,oidc, it is highly recommended to just enable one provider). |
|
||||
| env.AUTH_SESSION_EXPIRY_TIME | string | `"30d"` | This value sets the session timeout. You can specify a number in seconds or append s, m, h or d for seconds, minutes, hours or days (for example, "30m"). |
|
||||
| env.ENABLE_DNS_CACHING | string | `"false"` | Enables dns caching. This is not yet working for all users. See #4006 |
|
||||
| env.LOG_LEVEL | string | `"info"` | Log level to use. Possible values: debug/info/warn/error |
|
||||
| env.NO_EXTERNAL_CONNECTION | string | `"false"` | Disables some requests that need internet connection |
|
||||
| env.TZ | string | `"Europe/Paris"` | Your local time zone |
|
||||
| envSecrets.authLdapCredentials.existingSecret | string | `"auth-ldap-secret"` | Name of existing secret containing LDAP credentials |
|
||||
| envSecrets.authLdapCredentials.ldapBindingPassword | string | `"bind-password"` | Password for bind user secret key |
|
||||
| envSecrets.authOidcCredentials.existingSecret | string | `"auth-oidc-secret"` | Name of existing secret containing OIDC credentials |
|
||||
| envSecrets.authOidcCredentials.oidcClientId | string | `"oidc-client-id"` | ID of OIDC client (application) secret key |
|
||||
| envSecrets.authOidcCredentials.oidcClientSecret | string | `"oidc-client-secret"` | Secret of OIDC client (application) secret key |
|
||||
| envSecrets.dbCredentials.dbUrlKey | string | `"db-url"` | Secret key for DB_URL Example for external database: `mysql://username:password@host:port/homarrdb` or `postgresql://username:password@host:port/homarrdb` |
|
||||
| envSecrets.dbCredentials.existingSecret | string | `"db-secret"` | Name of existing secret containing DB credentials |
|
||||
| envSecrets.dbEncryption.existingSecret | string | `"db-encryption"` | Name of existing secret containing DB encryption |
|
||||
| envSecrets.dbEncryption.key | string | `"db-encryption-key"` | Secret key for SECRET_ENCRYPTION_KEY can be generated with `openssl rand -hex 32` |
|
||||
| fullnameOverride | string | `""` | Overrides chart's fullname |
|
||||
| hostAliases | list | `[]` | Add static entries to /etc/hosts in the Pod. This is useful in the following cases: - You are running in a dual-stack cluster (IPv4 + IPv6) and want to force usage of IPv4 for specific hostnames - Your application is having DNS resolution issues or IPv6 preference issues - You need to override or simulate DNS entries without changing global DNS - You are running in an air-gapped or isolated environment without external DNS Example: hostAliases: - ip: "192.168.1.10" hostnames: - "example.com" - "example.internal" |
|
||||
| httproute | object | `{"enabled":false,"hostnames":["chart-example.local"],"parentRefs":[{"name":"my-gateway","namespace":"default"}],"rules":[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]}` | Gateway API HTTPRoute configuration |
|
||||
| httproute.enabled | bool | `false` | Enable HTTPRoute |
|
||||
| httproute.hostnames | list | `["chart-example.local"]` | Hostnames this route matches (similar to ingress.hosts.host) |
|
||||
| httproute.parentRefs | list | `[{"name":"my-gateway","namespace":"default"}]` | References to the parent Gateway(s) this route attaches to. Each item must include at least a `name`, and optionally a `namespace`. |
|
||||
| httproute.rules | list | `[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]` | List of routing rules. Each rule can include: - matches: path/header/query matching - filters: optional transformations (redirects, header modifications, etc.) - backendRefs: one or more Kubernetes Services to forward traffic to |
|
||||
| httproute.rules[0].filters | list | `[]` | Optional filters for this rule (default: empty) |
|
||||
| httproute.rules[0].matches[0].path.type | string | `"PathPrefix"` | Path match type. One of: Exact, PathPrefix, RegularExpression |
|
||||
| httproute.rules[0].matches[0].path.value | string | `"/"` | Path value to match |
|
||||
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
|
||||
| image.repository | string | `"ghcr.io/homarr-labs/homarr"` | Image repository |
|
||||
| image.tag | string | `"v2.2.0"` | Overrides the image tag whose default is the chart appVersion |
|
||||
| imagePullSecrets | list | `[]` | Secrets for Docker registry |
|
||||
| ingress.annotations | object | `{}` | Ingress annotations |
|
||||
| ingress.enabled | bool | `false` | Enable ingress |
|
||||
| ingress.hosts | list | `[{"host":"chart-example.local","paths":[{"path":"/"}]}]` | Ingress hosts configuration |
|
||||
| ingress.ingressClassName | string | `""` | Ingress class name |
|
||||
| ingress.tls | list | `[]` | Ingress TLS configuration |
|
||||
| livenessProbe.failureThreshold | int | `3` | Failure threshold for liveness probe - number of consecutive failures before pod is restarted |
|
||||
| livenessProbe.httpGet.path | string | `"/api/health/live"` | This is the liveness check endpoint used by Kubernetes to determine if the application is still running. |
|
||||
| livenessProbe.httpGet.port | int | `7575` | The port on which the liveness check will be performed. This must be the same as the container port exposed by the application. |
|
||||
| livenessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the liveness probe starts |
|
||||
| livenessProbe.periodSeconds | int | `10` | Period in seconds between liveness probe checks |
|
||||
| livenessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each liveness probe check |
|
||||
| nameOverride | string | `""` | Overrides chart's name |
|
||||
| nodeSelector | object | `{}` | Node selectors for pod scheduling |
|
||||
| persistence.homarrDatabase.accessMode | string | `"ReadWriteOnce"` | homarr-database access mode |
|
||||
| persistence.homarrDatabase.enabled | bool | `false` | Enable homarr-database persistent storage |
|
||||
| persistence.homarrDatabase.mountPath | string | `"/appdata"` | homarr-database mount path inside the pod |
|
||||
| persistence.homarrDatabase.name | string | `"homarr-database"` | homarr-database persistent storage name |
|
||||
| persistence.homarrDatabase.size | string | `"50Mi"` | homarr-database storage size |
|
||||
| persistence.homarrDatabase.storageClassName | string | `"local-path"` | homarr-database storage class name |
|
||||
| persistence.homarrDatabase.volumeClaimName | string | `""` | homarr-database optional volumeClaimName to target specific PV |
|
||||
| persistence.homarrTrustedCerts.certificates | string | `nil` | homarr-trusted-certificates certificates, each entry will become a new trusted certificate as a dedicated file (works only for "configmap" and "secret" mode) |
|
||||
| persistence.homarrTrustedCerts.enabled | bool | `false` | Enable trusted certificates persistence |
|
||||
| persistence.homarrTrustedCerts.existingSecretKeys | string | `nil` | List of keys (filenames) to mount from the existing secret (used only when type is "existingSecret") |
|
||||
| persistence.homarrTrustedCerts.existingSecretName | string | `""` | Name of the existing Kubernetes Secret to mount (required if type is "existingSecret") |
|
||||
| persistence.homarrTrustedCerts.mountPath | string | `"/appdata/trusted-certificates"` | homarr-trusted-certificates mount path inside the pod |
|
||||
| persistence.homarrTrustedCerts.type | string | `"configmap"` | Persistence mode can be : configmap (declarative), secret (declarative) or existingSecret (mount an existing Kubernetes Secret by name and specify which keys to mount as files) |
|
||||
| podAnnotations | object | `{}` | Pod annotations |
|
||||
| podLabels | object | `{}` | Pod labels |
|
||||
| podSecurityContext | object | `{}` | Pod security context |
|
||||
| rbac | object | `{"enabled":false}` | Enable RBAC resources for Kubernetes integration Creates Role, ClusterRole, and associated bindings for Homarr's Kubernetes features |
|
||||
| rbac.enabled | bool | `false` | Enable to create RBAC resources and activate Kubernetes integration |
|
||||
| readinessProbe.failureThreshold | int | `3` | Failure threshold for readiness probe - number of consecutive failures before pod is considered unready |
|
||||
| readinessProbe.httpGet.path | string | `"/api/health/ready"` | This is the readiness check endpoint used by Kubernetes to determine if the application is ready to handle traffic. |
|
||||
| readinessProbe.httpGet.port | int | `7575` | The port on which the readiness check will be performed. This must match the container's exposed port. |
|
||||
| readinessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the readiness probe starts. increase this value if the pod is slow to fully start. |
|
||||
| readinessProbe.periodSeconds | int | `10` | Period in seconds between readiness probe checks |
|
||||
| readinessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each readiness probe check |
|
||||
| replicaCount | int | `1` | Number of replicas |
|
||||
| resources | object | `{}` | Resource configuration |
|
||||
| securityContext | object | `{}` | Security context |
|
||||
| service.enabled | bool | `true` | Enable service |
|
||||
| service.ipFamilies | list | `[]` | List of IP families to use for the service. Examples: - ["IPv4"] - ["IPv6"] - ["IPv4", "IPv6"] for dual-stack Leave empty to use cluster default behavior |
|
||||
| service.ipFamilyPolicy | string | `"SingleStack"` | Defines how the service assigns IP families (IPv4/IPv6) Possible values: - SingleStack (default): Only one IP family, usually IPv4 - PreferDualStack: Use dual-stack if the cluster supports it, fallback to single - RequireDualStack: Fail if dual-stack cannot be assigned |
|
||||
| service.ports.app.port | int | `7575` | Service port |
|
||||
| service.ports.app.protocol | string | `"TCP"` | Service protocol |
|
||||
| service.ports.app.targetPort | string | `"http"` | Service target port |
|
||||
| service.type | string | `"ClusterIP"` | Service type |
|
||||
| strategyType | string | `"RollingUpdate"` | `strategyType` specifies the strategy used to replace old Pods by new ones. `strategyType` can be `"Recreate"` or `"RollingUpdate"`. `"RollingUpdate"` is the default value and updates Pods in a rolling update fashion. `"Recreate"` will kill all existing Pods before new ones are created. The `"Recreate"` strategy is necessary when persistent volume's `accessMode` is set to `"ReadWriteOnce"` when using `helm upgrade`, as pod volume attachments to an existing PersistentVolumeClaim need to be cleared before a new pod can attach to it. |
|
||||
| tolerations | list | `[]` | Node tolerations for pod scheduling |
|
||||
|
||||
---
|
||||
|
||||
Autogenerated from chart metadata using [helm-docs](https://github.com/norwoodj/helm-docs)
|
||||
@@ -21,8 +21,8 @@ This method is only useful, when you want to develop on Homarr or extend it with
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/en/download/) 24.18 or newer
|
||||
- [pnpm](https://pnpm.io/installation) at the version pinned in the repository's `package.json`
|
||||
- [Node.js](https://nodejs.org/) 24.18.0 for the application runtime
|
||||
- [Bun](https://bun.sh/docs/installation) 1.4.2 (the version pinned in `mise.toml`)
|
||||
- [Redis](https://redis.io/download)
|
||||
|
||||
## Steps
|
||||
@@ -35,7 +35,7 @@ This method is only useful, when you want to develop on Homarr or extend it with
|
||||
Enter the created directory using <code>cd homarr</code>
|
||||
</li>
|
||||
<li>
|
||||
Install all dependencies using <code>pnpm install</code>
|
||||
Install all dependencies using <code>bun install</code>
|
||||
</li>
|
||||
<li>
|
||||
Copy `.env.example` to `.env` and set `DB_URL` to an absolute writable path, such as
|
||||
@@ -46,13 +46,9 @@ This method is only useful, when you want to develop on Homarr or extend it with
|
||||
Run `openssl rand -hex 32` twice and set `AUTH_SECRET` and `SECRET_ENCRYPTION_KEY` to the two different generated
|
||||
values. Keep both values secret and preserve `SECRET_ENCRYPTION_KEY` with your backups.
|
||||
</li>
|
||||
<li>Run `pnpm run db:migration:sqlite:run` and wait that it completes</li>
|
||||
<li>Build the source using `pnpm build`</li>
|
||||
<li>
|
||||
Copy better-sqlite3.node files with `mkdir build` and `cp
|
||||
./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node`
|
||||
</li>
|
||||
<li>Run `bun run db:migration:sqlite:run` and wait that it completes</li>
|
||||
<li>Build the source using `bun run build`</li>
|
||||
<li>Start a redis server using `redis-server`</li>
|
||||
<li>Run the server using `pnpm start`</li>
|
||||
<li>Run the server using `bun run start`</li>
|
||||
<li>Open `http://localhost:3000` in your browser</li>
|
||||
</ol>
|
||||
@@ -8,6 +8,9 @@ const config = {
|
||||
allowedDevOrigins: ["127.0.0.1", "100.71.78.45"],
|
||||
reactStrictMode: true,
|
||||
trailingSlash: true,
|
||||
experimental: {
|
||||
turbopackFileSystemCacheForBuild: true,
|
||||
},
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev --port 3003",
|
||||
"dev": "node ../../node_modules/next/dist/bin/next dev --port 3003",
|
||||
"start": "serve out --listen 3003 --config ../serve.json",
|
||||
"build": "pnpm validate:coverage && next build",
|
||||
"typecheck": "next typegen && tsc --noEmit",
|
||||
"build": "bun run validate:coverage && node ../../node_modules/next/dist/bin/next build",
|
||||
"typecheck": "node ../../node_modules/next/dist/bin/next typegen && tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"format": "oxfmt .",
|
||||
"clean": "git clean -xdf .next out node_modules",
|
||||
@@ -66,7 +66,6 @@
|
||||
"@types/three": "catalog:",
|
||||
"cheerio": "^1.2.0",
|
||||
"next-validate-link": "1.6.7",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"zod": "catalog:"
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ try {
|
||||
<a href="child/#child">Child</a><a href="/asset.svg">Asset</a>
|
||||
<a href="https://example.com/missing">External</a><a href="#top">Top</a></main>`,
|
||||
);
|
||||
const result = spawnSync(process.execPath, ["--import", import.meta.resolve("tsx"), validator], {
|
||||
const result = spawnSync(process.execPath, [validator], {
|
||||
cwd: directory,
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": [
|
||||
"$TURBO_EXTENDS$",
|
||||
"HOMARR_WEBSITE_URL",
|
||||
"WORKSHOP_API_URL",
|
||||
"WORKSHOP_WEB_URL",
|
||||
"KAPA_WEBSITE_ID",
|
||||
"WORKSHOP_URL"
|
||||
],
|
||||
"inputs": [
|
||||
"$TURBO_DEFAULT$",
|
||||
"!**/*.test.ts",
|
||||
"!**/*.test.tsx",
|
||||
"!**/*.spec.ts",
|
||||
"!**/*.spec.tsx",
|
||||
"!**/__tests__/**",
|
||||
"!README.md",
|
||||
"!AGENTS.md",
|
||||
"!PRODUCT.md",
|
||||
"$TURBO_ROOT$/packages/definitions/src/docs/**",
|
||||
"$TURBO_ROOT$/tools/motion-reel/three/**",
|
||||
".env*"
|
||||
],
|
||||
"outputs": [".next/**", "!.next/cache/**", "!.next/dev/**", "out/**", "next-env.d.ts", ".source/**"]
|
||||
},
|
||||
"typecheck": {
|
||||
"inputs": ["$TURBO_EXTENDS$", "$TURBO_ROOT$/tools/motion-reel/three/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,21 +6,21 @@ widget, translation, and UI packages from this monorepo.
|
||||
Run commands from the repository root:
|
||||
|
||||
```sh
|
||||
pnpm install --frozen-lockfile
|
||||
bun install --frozen-lockfile
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Keep infrastructure running in the first terminal:
|
||||
|
||||
```sh
|
||||
pnpm docker:dev
|
||||
bun run docker:dev
|
||||
```
|
||||
|
||||
Apply migrations and start the application in a second terminal:
|
||||
|
||||
```sh
|
||||
pnpm db:migration:sqlite:run
|
||||
pnpm dev
|
||||
bun run db:migration:sqlite:run
|
||||
bun run dev
|
||||
```
|
||||
|
||||
The app listens on `http://127.0.0.1:3000`. Configure an absolute writable `DB_URL`, `AUTH_SECRET`, and
|
||||
@@ -29,8 +29,8 @@ The app listens on `http://127.0.0.1:3000`. Configure an absolute writable `DB_U
|
||||
To test Custom Widgets against a local Community Workshop, start PocketBase and the docs in separate terminals:
|
||||
|
||||
```sh
|
||||
pnpm docker:workshop
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
|
||||
bun run docker:workshop
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 bun run dev:docs
|
||||
```
|
||||
|
||||
Then set:
|
||||
|
||||
@@ -4,14 +4,16 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "HOMARR_VERSION=$(node -p \"require('../../package.json').version\") pnpm with-env next build",
|
||||
"clean": "git clean -xdf .next .turbo node_modules",
|
||||
"dev": "pnpm with-env next dev",
|
||||
"build": "node scripts/assemble-standalone.mjs",
|
||||
"clean": "git clean -xdf .next .output .turbo node_modules",
|
||||
"dev": "bun run with-env node ../../node_modules/next/dist/bin/next dev",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"start": "pnpm with-env next start",
|
||||
"start": "bun run with-env node ../../node_modules/next/dist/bin/next start",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"with-env": "dotenv -e ../../.env --"
|
||||
"with-env": "dotenv -e ../../.env --",
|
||||
"build:compiled": "HOMARR_VERSION=$(node -p \"require('../../package.json').version\") bun run with-env node ../../node_modules/next/dist/bin/next build && node scripts/compile-standalone.mjs",
|
||||
"build:dependencies": "node scripts/cache-standalone.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai-compatible": "catalog:",
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import {
|
||||
appDirectory,
|
||||
collectFiles,
|
||||
compiled,
|
||||
copyFiles,
|
||||
dependencies,
|
||||
isCompiledFile,
|
||||
standalone,
|
||||
} from "./standalone-artifact.mjs";
|
||||
|
||||
// Final output is disposable. Never rewrite either restored cache artifact.
|
||||
if (!existsSync(join(dependencies, appDirectory, "package.json"))) {
|
||||
throw new Error("Standalone dependencies are missing; run the production build through Turbo");
|
||||
}
|
||||
rmSync(standalone, { recursive: true, force: true });
|
||||
cpSync(dependencies, standalone, { recursive: true, verbatimSymlinks: true });
|
||||
copyFiles(collectFiles().filter(isCompiledFile), standalone);
|
||||
const application = join(standalone, appDirectory);
|
||||
mkdirSync(application, { recursive: true });
|
||||
copyFileSync(join(compiled, "standalone-server.js"), join(application, "server.js"));
|
||||
|
||||
// Next also copies these directories for prerendered HTML/RSC and route files.
|
||||
for (const directory of ["app", "pages"]) {
|
||||
const source = join(compiled, "server", directory);
|
||||
if (existsSync(source)) {
|
||||
cpSync(source, join(application, ".next/server", directory), {
|
||||
recursive: true,
|
||||
force: true,
|
||||
verbatimSymlinks: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
cpSync(join(compiled, "static"), join(application, ".next/static"), { recursive: true, verbatimSymlinks: true });
|
||||
cpSync("public", join(application, "public"), { recursive: true, verbatimSymlinks: true });
|
||||
if (readFileSync(join(compiled, "BUILD_ID"), "utf8") !== readFileSync(join(application, ".next/BUILD_ID"), "utf8")) {
|
||||
throw new Error("Standalone build ID differs from the compiled build");
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { rmSync } from "node:fs";
|
||||
import { collectFiles, copyFiles, dependencies, isCompiledFile } from "./standalone-artifact.mjs";
|
||||
|
||||
// Separate uploads stay below the remote cache's per-artifact size limit.
|
||||
// Recover expired dependencies from traces without changing cached compilation.
|
||||
const files = collectFiles().filter((file) => !isCompiledFile(file));
|
||||
rmSync(dependencies, { recursive: true, force: true });
|
||||
copyFiles(files, dependencies);
|
||||
@@ -0,0 +1,6 @@
|
||||
import { copyFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { appDirectory, compiled } from "./standalone-artifact.mjs";
|
||||
|
||||
// Keep Next's generated launcher with its compilation and server configuration.
|
||||
copyFileSync(join(compiled, "standalone", appDirectory, "server.js"), join(compiled, "standalone-server.js"));
|
||||
@@ -0,0 +1,82 @@
|
||||
import { constants, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
|
||||
import { dirname, join, relative, resolve, sep } from "node:path";
|
||||
|
||||
export const compiled = resolve(".next");
|
||||
export const root = resolve("../..");
|
||||
export const appDirectory = relative(root, process.cwd());
|
||||
export const dependencies = resolve(".output/dependencies");
|
||||
export const standalone = resolve(".output/standalone");
|
||||
|
||||
function isOutside(path) {
|
||||
return path === ".." || path.startsWith(`..${sep}`);
|
||||
}
|
||||
|
||||
// The compiled artifact owns every generated file, including Next's launcher.
|
||||
// The dependency artifact contains only traced files outside the build tree.
|
||||
export function collectFiles() {
|
||||
const files = new Set([join(compiled, "BUILD_ID"), join(compiled, "required-server-files.json")]);
|
||||
const manifest = JSON.parse(readFileSync(join(compiled, "required-server-files.json"), "utf8"));
|
||||
for (const file of manifest.files) files.add(resolve(file));
|
||||
files.add(resolve("package.json"));
|
||||
for (const file of [".env", ".env.production"]) {
|
||||
if (existsSync(file)) files.add(resolve(file));
|
||||
}
|
||||
|
||||
function collectTraces(directory) {
|
||||
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
||||
if (directory === compiled && ["cache", "dev", "standalone"].includes(entry.name)) continue;
|
||||
const path = join(directory, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
collectTraces(path);
|
||||
continue;
|
||||
}
|
||||
if (!entry.name.endsWith(".nft.json")) continue;
|
||||
// NFT lists dependencies, excluding the entrypoint itself (including proxy).
|
||||
const entrypoint = path.slice(0, -".nft.json".length);
|
||||
if (existsSync(entrypoint)) files.add(entrypoint);
|
||||
const trace = JSON.parse(readFileSync(path, "utf8"));
|
||||
for (const file of trace.files) {
|
||||
const target = resolve(dirname(path), file);
|
||||
if (isOutside(relative(root, target))) throw new Error(`Trace escapes the repository: ${target}`);
|
||||
const generatedPath = relative(compiled, target);
|
||||
if (generatedPath.startsWith(`standalone${sep}`)) continue;
|
||||
// Keep native alias links, without copying through them into dependencies.
|
||||
if (generatedPath.startsWith(`node_modules${sep}`) && !lstatSync(target).isSymbolicLink()) continue;
|
||||
files.add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
collectTraces(compiled);
|
||||
|
||||
// Edge middleware/functions can have assets which are not covered by NFT.
|
||||
const middleware = JSON.parse(readFileSync(join(compiled, "server/middleware-manifest.json"), "utf8"));
|
||||
for (const entry of [...Object.values(middleware.middleware), ...Object.values(middleware.functions)]) {
|
||||
for (const file of entry.files ?? []) files.add(join(compiled, file));
|
||||
for (const file of [...(entry.wasm ?? []), ...(entry.assets ?? [])]) files.add(join(compiled, file.filePath));
|
||||
}
|
||||
return [...files];
|
||||
}
|
||||
|
||||
export function isCompiledFile(file) {
|
||||
return !isOutside(relative(compiled, file));
|
||||
}
|
||||
|
||||
export function copyFiles(files, destinationRoot) {
|
||||
const ordered = files.map((file) => ({ file, symbolicLink: lstatSync(file).isSymbolicLink() }));
|
||||
ordered.sort((left, right) => {
|
||||
if (left.symbolicLink !== right.symbolicLink) {
|
||||
if (left.symbolicLink) return -1;
|
||||
return 1;
|
||||
}
|
||||
return left.file.localeCompare(right.file);
|
||||
});
|
||||
for (const { file, symbolicLink } of ordered) {
|
||||
const destination = join(destinationRoot, relative(root, file));
|
||||
mkdirSync(dirname(destination), { recursive: true });
|
||||
if (symbolicLink) {
|
||||
cpSync(file, destination, { recursive: true, verbatimSymlinks: true });
|
||||
} else {
|
||||
copyFileSync(file, destination, constants.COPYFILE_FICLONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
import fsPromises from "fs/promises";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { glob } from "glob";
|
||||
import { parse as parseYaml } from "yaml";
|
||||
import z from "zod";
|
||||
|
||||
import packageJson from "../../../../package.json";
|
||||
|
||||
@@ -34,29 +30,9 @@ export const getDependenciesAsync = async (): Promise<PackageJsonDependencies> =
|
||||
|
||||
type DependencyCatalog = Map<string, string>;
|
||||
const parseDependencyCatalogsAsync = async (): Promise<DependencyCatalog> => {
|
||||
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const workspaceConfigPath = path.join(currentDir, "..", "..", "..", "..", "pnpm-workspace.yaml");
|
||||
|
||||
try {
|
||||
const workspaceConfigContent = await fsPromises.readFile(workspaceConfigPath, "utf-8");
|
||||
const workspaceConfig: unknown = parseYaml(workspaceConfigContent);
|
||||
const parseResult = workspaceConfigCatalogSchema.parse(workspaceConfig);
|
||||
return new Map(Object.entries(parseResult.catalog ?? {}));
|
||||
} catch (error) {
|
||||
// The workspace config is absent in Docker (only the runner stage is shipped),
|
||||
// so a missing file is expected. Surface any other failure (e.g. malformed YAML).
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
return new Map(Object.entries(packageJson.workspaces.catalog));
|
||||
};
|
||||
|
||||
const workspaceConfigCatalogSchema = z.object({
|
||||
catalog: z.record(z.string(), z.string()).optional(),
|
||||
});
|
||||
|
||||
export type PackageJsonDependencies = Record<string, string>;
|
||||
interface PackageJson {
|
||||
dependencies: PackageJsonDependencies | undefined;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["build:dependencies"],
|
||||
"cache": false,
|
||||
"outputs": [".output/standalone/**"],
|
||||
"inputs": [
|
||||
"$TURBO_EXTENDS$",
|
||||
"$TURBO_ROOT$/package.json",
|
||||
"$TURBO_ROOT$/static-data/**",
|
||||
".env*",
|
||||
"$TURBO_ROOT$/.env*"
|
||||
]
|
||||
},
|
||||
"typecheck": {
|
||||
"inputs": ["$TURBO_EXTENDS$", "$TURBO_ROOT$/static-data/**"]
|
||||
},
|
||||
"build:compiled": {
|
||||
"inputs": [
|
||||
"$TURBO_DEFAULT$",
|
||||
"!**/*.test.ts",
|
||||
"!**/*.test.tsx",
|
||||
"!**/*.spec.ts",
|
||||
"!**/*.spec.tsx",
|
||||
"!**/__tests__/**",
|
||||
"!**/*.md",
|
||||
"!**/*.mdx",
|
||||
"!**/README*",
|
||||
"!**/CHANGELOG*",
|
||||
"!**/LICENSE*",
|
||||
"!**/*.stories.{ts,tsx}",
|
||||
"$TURBO_ROOT$/package.json",
|
||||
"$TURBO_ROOT$/static-data/**",
|
||||
".env*",
|
||||
"$TURBO_ROOT$/.env*"
|
||||
],
|
||||
"outputs": [".next/**", "!.next/cache/**", "!.next/dev/**", "!.next/standalone/**", "next-env.d.ts"],
|
||||
"dependsOn": ["^build"],
|
||||
"env": ["TURBO_PLATFORM"]
|
||||
},
|
||||
"build:dependencies": {
|
||||
"dependsOn": ["build:compiled"],
|
||||
"env": ["TURBO_PLATFORM"],
|
||||
"inputs": ["package.json", "scripts/**"],
|
||||
"outputs": [".output/dependencies/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"dev": "pnpm with-env tsx ./src/main.ts",
|
||||
"dev": "bun run with-env tsx ./src/main.ts",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit",
|
||||
@@ -46,7 +46,6 @@
|
||||
"@types/node": "catalog:",
|
||||
"dotenv-cli": "catalog:",
|
||||
"esbuild": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"dev": "pnpm with-env tsx ./src/main.ts",
|
||||
"dev": "bun run with-env tsx ./src/main.ts",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit",
|
||||
@@ -27,7 +27,6 @@
|
||||
"@homarr/redis": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"dotenv": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"ws": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# syntax=docker/dockerfile:1.10
|
||||
# syntax=docker/dockerfile:1.25
|
||||
|
||||
ARG POCKETBASE_IMAGE=ghcr.io/muchobien/pocketbase:0.39.9@sha256:c0667fe6c5197ff226ff642120ab1ad870c3f2f27300940a61e5a7fa082b6816
|
||||
|
||||
@@ -13,12 +13,30 @@ RUN --mount=type=cache,id=workshop-go-mod,target=/go/pkg/mod \
|
||||
FROM pocketbase-builder AS pocketbase-test
|
||||
RUN --mount=type=cache,id=workshop-go-mod,target=/go/pkg/mod go test ./...
|
||||
|
||||
FROM oven/bun:1.4.2-alpine AS bun-tool
|
||||
|
||||
FROM node:24.18.0-alpine AS dependencies
|
||||
COPY --from=bun-tool /usr/local/bin/bun /usr/local/bin/bun
|
||||
WORKDIR /app
|
||||
ENV COREPACK_HOME=/corepack
|
||||
RUN mkdir -p /corepack && corepack enable pnpm && corepack install --global pnpm@11.15.1
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json turbo.json ./
|
||||
RUN apk add --no-cache libstdc++ python3 make g++
|
||||
COPY bun.lock bunfig.toml package.json turbo.json ./
|
||||
COPY patches ./patches
|
||||
COPY apps/docs/package.json ./apps/docs/package.json
|
||||
COPY packages/common/package.json ./packages/common/package.json
|
||||
COPY packages/core/package.json ./packages/core/package.json
|
||||
COPY packages/custom-widgets/package.json ./packages/custom-widgets/package.json
|
||||
COPY packages/definitions/package.json ./packages/definitions/package.json
|
||||
COPY packages/db/package.json ./packages/db/package.json
|
||||
COPY packages/server-settings/package.json ./packages/server-settings/package.json
|
||||
COPY packages/settings/package.json ./packages/settings/package.json
|
||||
COPY packages/translation/package.json ./packages/translation/package.json
|
||||
COPY packages/ui/package.json ./packages/ui/package.json
|
||||
COPY packages/validation/package.json ./packages/validation/package.json
|
||||
COPY packages/workshop/package.json ./packages/workshop/package.json
|
||||
COPY tooling/typescript/package.json ./tooling/typescript/package.json
|
||||
RUN --mount=type=cache,id=workshop-bun-cache,target=/root/.bun/install/cache \
|
||||
npm_config_nodedir=/usr/local bun install --filter @homarr/docs --filter homarr --frozen-lockfile --concurrent-scripts=1
|
||||
|
||||
COPY apps/docs ./apps/docs
|
||||
COPY tools/motion-reel/three ./tools/motion-reel/three
|
||||
COPY packages/common ./packages/common
|
||||
@@ -33,9 +51,9 @@ COPY packages/ui ./packages/ui
|
||||
COPY packages/validation ./packages/validation
|
||||
COPY packages/workshop ./packages/workshop
|
||||
COPY tooling/typescript ./tooling/typescript
|
||||
RUN --mount=type=cache,id=workshop-pnpm-store,target=/pnpm/store pnpm config set store-dir /pnpm/store && pnpm install --filter @homarr/docs... --frozen-lockfile
|
||||
|
||||
FROM dependencies AS website
|
||||
ARG TARGETPLATFORM
|
||||
ARG KAPA_WEBSITE_ID=1e4656f4-abeb-4343-bbae-1d8626f52378
|
||||
ENV KAPA_WEBSITE_ID=$KAPA_WEBSITE_ID
|
||||
ARG HOMARR_WEBSITE_URL=https://homarr.dev
|
||||
@@ -44,7 +62,14 @@ ARG WORKSHOP_WEB_URL=https://homarr.dev/workshop
|
||||
ENV HOMARR_WEBSITE_URL=$HOMARR_WEBSITE_URL
|
||||
ENV WORKSHOP_API_URL=$WORKSHOP_API_URL
|
||||
ENV WORKSHOP_WEB_URL=$WORKSHOP_WEB_URL
|
||||
RUN CI=true SKIP_ENV_VALIDATION=true pnpm --filter @homarr/docs build
|
||||
RUN --mount=type=secret,id=TURBO_API,env=TURBO_API \
|
||||
--mount=type=secret,id=TURBO_TEAM,env=TURBO_TEAM \
|
||||
--mount=type=secret,id=TURBO_TOKEN,env=TURBO_TOKEN \
|
||||
--mount=type=secret,id=TURBO_REMOTE_CACHE_SIGNATURE_KEY,env=TURBO_REMOTE_CACHE_SIGNATURE_KEY \
|
||||
--mount=type=cache,id=workshop-next-build-${TARGETPLATFORM},target=/app/apps/docs/.next/cache,sharing=locked \
|
||||
--mount=type=cache,id=workshop-turbo-${TARGETPLATFORM},target=/app/.turbo,sharing=locked \
|
||||
TURBO_PLATFORM="${TARGETPLATFORM:-linux/amd64}/musl/node-24.18.0" \
|
||||
CI=true SKIP_ENV_VALIDATION=true bun run turbo run build --filter=@homarr/docs
|
||||
|
||||
FROM ${POCKETBASE_IMAGE} AS pocketbase-runtime
|
||||
ENV PORT=8090
|
||||
|
||||
@@ -12,14 +12,14 @@ From the repository root:
|
||||
|
||||
```sh
|
||||
cp apps/workshop/.env.example apps/workshop/.env
|
||||
pnpm install --frozen-lockfile
|
||||
bun install --frozen-lockfile
|
||||
docker compose --env-file apps/workshop/.env -f apps/workshop/docker-compose.yml up --build workshop
|
||||
```
|
||||
|
||||
Start the documentation site in another terminal:
|
||||
|
||||
```sh
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 bun run dev:docs
|
||||
```
|
||||
|
||||
Open PocketBase administration at `http://127.0.0.1:8090/_/` and Workshop at
|
||||
@@ -71,18 +71,18 @@ deployments can use an explicit comma-separated allowlist.
|
||||
Run the focused Workshop checks:
|
||||
|
||||
```sh
|
||||
pnpm test:workshop
|
||||
pnpm --filter @homarr/workshop typecheck
|
||||
pnpm --filter @homarr/docs typecheck
|
||||
bun run test:workshop
|
||||
bun run --filter @homarr/workshop typecheck
|
||||
bun run --filter @homarr/docs typecheck
|
||||
```
|
||||
|
||||
The integration suite uses a disposable Compose project. `pnpm test:workshop-image` additionally builds and smoke-tests
|
||||
The integration suite uses a disposable Compose project. `bun run test:workshop-image` additionally builds and smoke-tests
|
||||
the combined production image. The production image smoke also runs in PR CI on native amd64 and arm64 runners, including data persistence and backup restore. The broader integration suite remains available manually. Workshop CI validates Compose configuration and publishes images only on release branches. The main Fast gate owns workspace typechecking.
|
||||
|
||||
To smoke-test an already built candidate without rebuilding:
|
||||
|
||||
```sh
|
||||
WORKSHOP_IMAGE_TEST_IMAGE=homarr-workshop:candidate pnpm test:workshop-image
|
||||
WORKSHOP_IMAGE_TEST_IMAGE=homarr-workshop:candidate bun run test:workshop-image
|
||||
```
|
||||
|
||||
## Data safety
|
||||
|
||||
@@ -17,7 +17,7 @@ remote_api_url=${remote_api_url%/}
|
||||
remote_api_url=${remote_api_url%/api}
|
||||
|
||||
if [ ! -f apps/docs/out/404.html ]; then
|
||||
echo 'Build the docs first: pnpm --filter @homarr/docs build' >&2
|
||||
echo 'Build the docs first: bun run --filter @homarr/docs build' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ docker volume create "$WORKSHOP_IMAGE_TEST_VOLUME" >/dev/null
|
||||
docker volume create "$WORKSHOP_IMAGE_TEST_RESTORE_VOLUME" >/dev/null
|
||||
|
||||
if [ -z "${WORKSHOP_IMAGE_TEST_IMAGE:-}" ]; then
|
||||
docker build --target production -f apps/workshop/Dockerfile -t "$WORKSHOP_IMAGE_TEST_TAG" .
|
||||
bash tooling/github/docker-build.sh --target production -f apps/workshop/Dockerfile -t "$WORKSHOP_IMAGE_TEST_TAG" .
|
||||
fi
|
||||
docker run --detach --name "$WORKSHOP_IMAGE_TEST_NAME" \
|
||||
--publish "127.0.0.1:$WORKSHOP_IMAGE_TEST_PORT:8090" \
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
[install]
|
||||
# Share compatible dependencies across all workspaces at the repository root.
|
||||
linker = "hoisted"
|
||||
|
||||
[run]
|
||||
# Preserve Node runtimes for tools with a Node shebang.
|
||||
bun = false
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM node:24.18.0-trixie AS base
|
||||
FROM oven/bun:1.4.2 AS bun-tool
|
||||
FROM node:24.18.0-trixie
|
||||
WORKDIR /app
|
||||
COPY package.json .
|
||||
COPY pnpm-lock.yaml .
|
||||
COPY pnpm-workspace.yaml .
|
||||
RUN corepack enable pnpm && pnpm install --frozen-lockfile
|
||||
CMD ["sleep", "60s"]
|
||||
COPY --from=bun-tool /usr/local/bin/bun /usr/local/bin/bun
|
||||
COPY package.json bun.lock ./
|
||||
RUN bun install --frozen-lockfile --concurrent-scripts=1
|
||||
CMD ["sleep", "60s"]
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"lockfileVersion": 2,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "homarr-prebuilt-debian",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "12.11.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"better-sqlite3",
|
||||
],
|
||||
"packages": {
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"better-sqlite3": ["better-sqlite3@12.11.1", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-dq9AtApgg5PGFtBzPFSBl3HZQjHok5gaQCM6zh2Yk0aSmDCs1CbnVI8/HgASQkNKsWFpseIO9beg5xxpYhbIfA=="],
|
||||
|
||||
"bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
|
||||
|
||||
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
|
||||
|
||||
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||
|
||||
"chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
|
||||
|
||||
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
|
||||
|
||||
"deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
|
||||
|
||||
"expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
|
||||
|
||||
"file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
|
||||
|
||||
"fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
|
||||
|
||||
"github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
|
||||
|
||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
||||
|
||||
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
|
||||
|
||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
|
||||
"mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
|
||||
|
||||
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
|
||||
|
||||
"node-abi": ["node-abi@3.96.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-rebQ/lz7i0EkoLzUVSrKRzA69zMkwLp95kKMWoMDkkM00Suxz0D7zEQPwRml5fQum24mj7bPvmlgLAmu2JCiYg=="],
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
|
||||
|
||||
"pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
|
||||
|
||||
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
|
||||
|
||||
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
|
||||
|
||||
"simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
|
||||
|
||||
"simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
|
||||
|
||||
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
|
||||
|
||||
"tar-fs": ["tar-fs@2.1.5", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw=="],
|
||||
|
||||
"tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
|
||||
|
||||
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,14 @@
|
||||
"name": "homarr-prebuilt-debian",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^12.11.1"
|
||||
"better-sqlite3": "12.11.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24.18.0",
|
||||
"pnpm": ">=11.15.1"
|
||||
"bun": ">=1.4.2",
|
||||
"node": ">=24.18.0"
|
||||
},
|
||||
"packageManager": "pnpm@11.15.1"
|
||||
"packageManager": "bun@1.4.2",
|
||||
"trustedDependencies": [
|
||||
"better-sqlite3"
|
||||
]
|
||||
}
|
||||
-287
@@ -1,287 +0,0 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
better-sqlite3:
|
||||
specifier: ^12.11.1
|
||||
version: 12.11.1
|
||||
|
||||
packages:
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
better-sqlite3@12.11.1:
|
||||
resolution: {integrity: sha512-dq9AtApgg5PGFtBzPFSBl3HZQjHok5gaQCM6zh2Yk0aSmDCs1CbnVI8/HgASQkNKsWFpseIO9beg5xxpYhbIfA==}
|
||||
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x}
|
||||
|
||||
bindings@1.5.0:
|
||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
||||
|
||||
bl@4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||
|
||||
buffer@5.7.1:
|
||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||
|
||||
chownr@1.1.4:
|
||||
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
|
||||
|
||||
decompress-response@6.0.0:
|
||||
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
deep-extend@0.6.0:
|
||||
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
end-of-stream@1.4.5:
|
||||
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||
|
||||
expand-template@2.0.3:
|
||||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
file-uri-to-path@1.0.0:
|
||||
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
||||
|
||||
fs-constants@1.0.0:
|
||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||
|
||||
github-from-package@0.0.0:
|
||||
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
||||
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
||||
ini@1.3.8:
|
||||
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
||||
|
||||
mimic-response@3.1.0:
|
||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
minimist@1.2.8:
|
||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||
|
||||
mkdirp-classic@0.5.3:
|
||||
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
||||
|
||||
napi-build-utils@2.0.0:
|
||||
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
|
||||
|
||||
node-abi@3.85.0:
|
||||
resolution: {integrity: sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
prebuild-install@7.1.3:
|
||||
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
||||
engines: {node: '>=10'}
|
||||
deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
|
||||
hasBin: true
|
||||
|
||||
pump@3.0.3:
|
||||
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
|
||||
|
||||
rc@1.2.8:
|
||||
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
||||
hasBin: true
|
||||
|
||||
readable-stream@3.6.2:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
|
||||
semver@7.7.3:
|
||||
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
simple-concat@1.0.1:
|
||||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||
|
||||
simple-get@4.0.1:
|
||||
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||
|
||||
string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
|
||||
strip-json-comments@2.0.1:
|
||||
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
tar-fs@2.1.4:
|
||||
resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
|
||||
|
||||
tar-stream@2.2.0:
|
||||
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||
|
||||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
better-sqlite3@12.11.1:
|
||||
dependencies:
|
||||
bindings: 1.5.0
|
||||
prebuild-install: 7.1.3
|
||||
|
||||
bindings@1.5.0:
|
||||
dependencies:
|
||||
file-uri-to-path: 1.0.0
|
||||
|
||||
bl@4.1.0:
|
||||
dependencies:
|
||||
buffer: 5.7.1
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
|
||||
buffer@5.7.1:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
|
||||
chownr@1.1.4: {}
|
||||
|
||||
decompress-response@6.0.0:
|
||||
dependencies:
|
||||
mimic-response: 3.1.0
|
||||
|
||||
deep-extend@0.6.0: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
end-of-stream@1.4.5:
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
|
||||
expand-template@2.0.3: {}
|
||||
|
||||
file-uri-to-path@1.0.0: {}
|
||||
|
||||
fs-constants@1.0.0: {}
|
||||
|
||||
github-from-package@0.0.0: {}
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
inherits@2.0.4: {}
|
||||
|
||||
ini@1.3.8: {}
|
||||
|
||||
mimic-response@3.1.0: {}
|
||||
|
||||
minimist@1.2.8: {}
|
||||
|
||||
mkdirp-classic@0.5.3: {}
|
||||
|
||||
napi-build-utils@2.0.0: {}
|
||||
|
||||
node-abi@3.85.0:
|
||||
dependencies:
|
||||
semver: 7.7.3
|
||||
|
||||
once@1.4.0:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
prebuild-install@7.1.3:
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
expand-template: 2.0.3
|
||||
github-from-package: 0.0.0
|
||||
minimist: 1.2.8
|
||||
mkdirp-classic: 0.5.3
|
||||
napi-build-utils: 2.0.0
|
||||
node-abi: 3.85.0
|
||||
pump: 3.0.3
|
||||
rc: 1.2.8
|
||||
simple-get: 4.0.1
|
||||
tar-fs: 2.1.4
|
||||
tunnel-agent: 0.6.0
|
||||
|
||||
pump@3.0.3:
|
||||
dependencies:
|
||||
end-of-stream: 1.4.5
|
||||
once: 1.4.0
|
||||
|
||||
rc@1.2.8:
|
||||
dependencies:
|
||||
deep-extend: 0.6.0
|
||||
ini: 1.3.8
|
||||
minimist: 1.2.8
|
||||
strip-json-comments: 2.0.1
|
||||
|
||||
readable-stream@3.6.2:
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
safe-buffer@5.2.1: {}
|
||||
|
||||
semver@7.7.3: {}
|
||||
|
||||
simple-concat@1.0.1: {}
|
||||
|
||||
simple-get@4.0.1:
|
||||
dependencies:
|
||||
decompress-response: 6.0.0
|
||||
once: 1.4.0
|
||||
simple-concat: 1.0.1
|
||||
|
||||
string_decoder@1.3.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
strip-json-comments@2.0.1: {}
|
||||
|
||||
tar-fs@2.1.4:
|
||||
dependencies:
|
||||
chownr: 1.1.4
|
||||
mkdirp-classic: 0.5.3
|
||||
pump: 3.0.3
|
||||
tar-stream: 2.2.0
|
||||
|
||||
tar-stream@2.2.0:
|
||||
dependencies:
|
||||
bl: 4.1.0
|
||||
end-of-stream: 1.4.5
|
||||
fs-constants: 1.0.0
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
@@ -1,2 +0,0 @@
|
||||
allowBuilds:
|
||||
better-sqlite3: true
|
||||
+341
-22
@@ -2,8 +2,260 @@
|
||||
"name": "homarr",
|
||||
"version": "2.2.0",
|
||||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"apps/*",
|
||||
"packages/*",
|
||||
"tooling/*"
|
||||
],
|
||||
"catalog": {
|
||||
"@ai-sdk/openai-compatible": "3.0.25",
|
||||
"@ai-sdk/react": "4.0.59",
|
||||
"@assistant-ui/react": "0.15.8",
|
||||
"@assistant-ui/react-ai-sdk": "1.4.4",
|
||||
"@assistant-ui/react-lexical": "0.2.7",
|
||||
"@assistant-ui/react-markdown": "0.14.9",
|
||||
"@auth/core": "0.41.3",
|
||||
"@auth/drizzle-adapter": "1.11.3",
|
||||
"@babel/parser": "7.29.7",
|
||||
"@codemirror/autocomplete": "6.20.3",
|
||||
"@codemirror/commands": "6.10.3",
|
||||
"@codemirror/lang-css": "6.3.1",
|
||||
"@codemirror/lang-javascript": "6.2.5",
|
||||
"@codemirror/lang-json": "6.0.2",
|
||||
"@codemirror/lint": "6.9.6",
|
||||
"@codemirror/state": "6.6.0",
|
||||
"@codemirror/theme-one-dark": "6.1.3",
|
||||
"@codemirror/view": "6.43.0",
|
||||
"@ctrl/deluge": "8.0.0",
|
||||
"@ctrl/qbittorrent": "9.14.0",
|
||||
"@ctrl/transmission": "7.4.0",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
"@dnd-kit/collision": "0.5.0",
|
||||
"@dnd-kit/core": "6.3.1",
|
||||
"@dnd-kit/dom": "0.5.0",
|
||||
"@dnd-kit/modifiers": "9.0.0",
|
||||
"@dnd-kit/react": "0.5.0",
|
||||
"@dnd-kit/sortable": "10.0.0",
|
||||
"@dnd-kit/utilities": "3.2.2",
|
||||
"@drizzle-team/brocli": "0.12.0",
|
||||
"@extractus/feed-extractor": "7.2.1",
|
||||
"@gfazioli/mantine-onboarding-tour": "4.0.8",
|
||||
"@gitbeaker/rest": "^43.8.0",
|
||||
"@homarr/node-unifi": "2.6.0",
|
||||
"@immich/sdk": "3.0.2",
|
||||
"@jellyfin/sdk": "0.13.0",
|
||||
"@kubernetes/client-node": "1.4.0",
|
||||
"@libsql/client": "0.18.0",
|
||||
"@mantine/charts": "9.6.0",
|
||||
"@mantine/colors-generator": "9.6.0",
|
||||
"@mantine/core": "9.6.0",
|
||||
"@mantine/dates": "9.6.0",
|
||||
"@mantine/dropzone": "9.6.0",
|
||||
"@mantine/form": "9.6.0",
|
||||
"@mantine/hooks": "9.6.0",
|
||||
"@mantine/lightbox": "9.6.0",
|
||||
"@mantine/modals": "9.6.0",
|
||||
"@mantine/notifications": "9.6.0",
|
||||
"@mantine/spotlight": "9.6.0",
|
||||
"@mantine/tiptap": "9.6.0",
|
||||
"@modelcontextprotocol/server": "2.0.0",
|
||||
"@octokit/auth-app": "^8.2.0",
|
||||
"@paralleldrive/cuid2": "3.3.0",
|
||||
"@playwright/test": "1.61.1",
|
||||
"@rexa-developer/tiks": "0.3.0",
|
||||
"@scalar/api-client-react": "2.0.51",
|
||||
"@scalar/api-reference-react": "0.9.74",
|
||||
"@semantic-release/changelog": "7.0.0",
|
||||
"@semantic-release/commit-analyzer": "13.0.1",
|
||||
"@semantic-release/git": "11.0.1",
|
||||
"@semantic-release/github": "12.0.9",
|
||||
"@semantic-release/release-notes-generator": "14.1.1",
|
||||
"@shadcn/react": "^0.2.1",
|
||||
"@t3-oss/env-nextjs": "0.13.11",
|
||||
"@tabler/icons-react": "3.44.0",
|
||||
"@tanstack/query-async-storage-persister": "5.101.2",
|
||||
"@tanstack/react-devtools": "0.10.9",
|
||||
"@tanstack/react-query": "5.101.2",
|
||||
"@tanstack/react-query-devtools": "5.101.2",
|
||||
"@tanstack/react-query-next-experimental": "5.101.2",
|
||||
"@tanstack/react-query-persist-client": "5.101.2",
|
||||
"@testcontainers/postgresql": "12.0.4",
|
||||
"@testcontainers/redis": "12.0.4",
|
||||
"@tiptap/extension-color": "3.30.5",
|
||||
"@tiptap/extension-details": "3.30.5",
|
||||
"@tiptap/extension-highlight": "3.30.5",
|
||||
"@tiptap/extension-image": "3.30.5",
|
||||
"@tiptap/extension-link": "3.31.3",
|
||||
"@tiptap/extension-placeholder": "3.31.3",
|
||||
"@tiptap/extension-table": "3.30.5",
|
||||
"@tiptap/extension-table-cell": "3.30.5",
|
||||
"@tiptap/extension-table-header": "3.30.5",
|
||||
"@tiptap/extension-table-row": "3.30.5",
|
||||
"@tiptap/extension-task-item": "3.30.5",
|
||||
"@tiptap/extension-task-list": "3.30.5",
|
||||
"@tiptap/extension-text-align": "3.30.5",
|
||||
"@tiptap/extension-text-style": "3.30.5",
|
||||
"@tiptap/extension-underline": "3.30.5",
|
||||
"@tiptap/react": "3.31.3",
|
||||
"@tiptap/starter-kit": "3.31.3",
|
||||
"@trpc/client": "11.18.0",
|
||||
"@trpc/next": "11.18.0",
|
||||
"@trpc/react-query": "11.18.0",
|
||||
"@trpc/server": "11.18.0",
|
||||
"@trpc/tanstack-react-query": "11.18.0",
|
||||
"@turbo/gen": "2.11.7",
|
||||
"@types/adm-zip": "0.5.8",
|
||||
"@types/bcrypt": "6.0.0",
|
||||
"@types/canvas-confetti": "1.9.0",
|
||||
"@types/chroma-js": "3.1.2",
|
||||
"@types/cookies": "0.9.2",
|
||||
"@types/crypto-js": "4.2.2",
|
||||
"@types/css-modules": "1.0.5",
|
||||
"@types/dockerode": "4.0.1",
|
||||
"@types/mdx": "2.0.14",
|
||||
"@types/node": "24.13.3",
|
||||
"@types/node-unifi": "2.5.1",
|
||||
"@types/pg": "8.20.0",
|
||||
"@types/prismjs": "^1.26.6",
|
||||
"@types/react": "19.2.17",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/three": "0.180.0",
|
||||
"@types/video.js": "7.3.58",
|
||||
"@types/ws": "8.18.1",
|
||||
"@types/xml2js": "0.4.14",
|
||||
"@uiw/react-codemirror": "^4.25.11",
|
||||
"@vitejs/plugin-react": "6.0.3",
|
||||
"@vitest/coverage-istanbul": "4.1.10",
|
||||
"@vitest/ui": "4.1.10",
|
||||
"@xterm/addon-canvas": "0.7.0",
|
||||
"@xterm/addon-fit": "0.11.0",
|
||||
"@xterm/xterm": "6.0.0",
|
||||
"acorn": "8.16.0",
|
||||
"acorn-jsx": "5.3.2",
|
||||
"adm-zip": "0.6.1",
|
||||
"ai": "7.0.56",
|
||||
"ajv": "8.18.0",
|
||||
"assistant-stream": "0.3.34",
|
||||
"babel-plugin-react-compiler": "1.0.0",
|
||||
"bcrypt": "6.0.0",
|
||||
"canvas-confetti": "1.9.4",
|
||||
"chroma-js": "3.2.0",
|
||||
"clsx": "2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"codemirror": "6.0.2",
|
||||
"concurrently": "10.0.3",
|
||||
"conventional-changelog-conventionalcommits": "10.2.0",
|
||||
"cookie": "2.0.1",
|
||||
"cookies": "0.9.1",
|
||||
"croner": "10.0.1",
|
||||
"cross-env": "10.1.0",
|
||||
"crypto-js": "4.2.0",
|
||||
"date-fns": "4.4.0",
|
||||
"dayjs": "1.11.21",
|
||||
"deepmerge": "4.3.1",
|
||||
"dns-caching": "0.2.9",
|
||||
"dockerode": "5.0.1",
|
||||
"dotenv": "17.4.2",
|
||||
"dotenv-cli": "11.0.0",
|
||||
"drizzle-kit": "0.31.10",
|
||||
"drizzle-orm": "0.45.2",
|
||||
"drizzle-zod": "0.8.3",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"esbuild": "0.28.2",
|
||||
"fast-xml-parser": "5.11.1",
|
||||
"fastify": "5.12.5",
|
||||
"feed": "6.0.0",
|
||||
"flag-icons": "7.5.0",
|
||||
"fumadocs-core": "16.15.12",
|
||||
"fumadocs-mdx": "15.4.3",
|
||||
"fumadocs-openapi": "12.0.1",
|
||||
"fumadocs-ui": "16.15.12",
|
||||
"glob": "13.0.6",
|
||||
"ical.js": "2.2.1",
|
||||
"input-otp": "^1.4.2",
|
||||
"ioredis": "5.11.1",
|
||||
"isomorphic-dompurify": "3.18.0",
|
||||
"jotai": "2.20.1",
|
||||
"jsdom": "29.1.1",
|
||||
"json-schema-typed": "8.0.2",
|
||||
"json5": "2.2.3",
|
||||
"jsonpath-plus": "10.4.0",
|
||||
"jszip": "3.10.1",
|
||||
"ldapts": "8.1.8",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"lucide-react": "1.24.0",
|
||||
"mantine-contextmenu": "^9.3.1",
|
||||
"mantine-datatable": "9.3.1",
|
||||
"mantine-react-table": "npm:mantine-react-table-open@9.0.3",
|
||||
"maria2": "0.4.1",
|
||||
"mermaid": "11.17.2",
|
||||
"motion": "^13.0.0",
|
||||
"next": "16.3.6",
|
||||
"next-auth": "5.0.0-beta.32",
|
||||
"next-intl": "4.13.1",
|
||||
"next-themes": "0.4.6",
|
||||
"node-loader": "2.1.0",
|
||||
"octokit": "5.0.5",
|
||||
"oxfmt": "0.58.0",
|
||||
"oxlint": "1.73.0",
|
||||
"pg": "8.22.0",
|
||||
"pocketbase": "0.26.9",
|
||||
"postcss-preset-mantine": "1.18.0",
|
||||
"posthog-js": "1.399.2",
|
||||
"posthog-node": "5.41.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"proxmox-api": "1.1.1",
|
||||
"react": "19.2.7",
|
||||
"react-day-picker": "^10.0.1",
|
||||
"react-dom": "19.2.7",
|
||||
"react-error-boundary": "6.1.2",
|
||||
"react-markdown": "10.1.0",
|
||||
"react-quill-new": "3.8.3",
|
||||
"react-resizable-panels": "^4.12.2",
|
||||
"react-shiki": "0.11.0",
|
||||
"react-simple-code-editor": "^0.14.1",
|
||||
"recharts": "3.9.1",
|
||||
"rehype-raw": "7.0.0",
|
||||
"rehype-sanitize": "6.0.0",
|
||||
"remark-breaks": "4.0.0",
|
||||
"remark-directive": "3.0.1",
|
||||
"remark-gfm": "4.0.1",
|
||||
"sass": "1.101.0",
|
||||
"semantic-release": "25.0.5",
|
||||
"semver-parser": "4.2.1",
|
||||
"serve": "14.2.6",
|
||||
"shiki": "4.4.3",
|
||||
"sonner": "2.0.7",
|
||||
"sql.js": "1.14.1",
|
||||
"superjson": "2.2.6",
|
||||
"svgson": "5.3.1",
|
||||
"testcontainers": "12.0.4",
|
||||
"three": "0.180.0",
|
||||
"timezones-ical-library": "2.2.1",
|
||||
"trpc-to-openapi": "3.3.0",
|
||||
"tsdav": "2.3.0",
|
||||
"turbo": "2.11.7",
|
||||
"typescript": "7.0.2",
|
||||
"undici": "7.29.1",
|
||||
"use-deep-compare-effect": "1.8.1",
|
||||
"video.js": "8.23.9",
|
||||
"vite": "8.1.4",
|
||||
"vite-tsconfig-paths": "6.1.1",
|
||||
"vitest": "4.1.10",
|
||||
"winston": "3.19.0",
|
||||
"ws": "8.21.0",
|
||||
"xml2js": "0.6.2",
|
||||
"yaml": "2.9.0",
|
||||
"zod": "4.4.3",
|
||||
"zod-form-data": "3.0.2",
|
||||
"zod-validation-error": "5.0.0",
|
||||
"better-sqlite3": "12.11.1",
|
||||
"@types/better-sqlite3": "7.6.13"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env CI=true turbo build",
|
||||
"build": "cross-env CI=true turbo run build",
|
||||
"benchmark:build:docker": "node --experimental-strip-types scripts/benchmarks/docker-build.mts",
|
||||
"benchmark:compare": "node --experimental-strip-types scripts/benchmarks/benchmark-compare.mts",
|
||||
"benchmark:board-graph": "node --experimental-strip-types scripts/benchmarks/board-static-graph.mts",
|
||||
@@ -11,27 +263,27 @@
|
||||
"benchmark:request-memory": "node --expose-gc --import tsx scripts/benchmarks/request-handler-memory.mts",
|
||||
"clean": "git clean -xdf node_modules",
|
||||
"clean:workspaces": "turbo clean",
|
||||
"check:custom-widgets": "pnpm --filter @homarr/custom-widgets check:architecture",
|
||||
"check:custom-widgets": "bun run --filter @homarr/custom-widgets check:architecture",
|
||||
"check:custom-widget-bundle": "node ./scripts/check-custom-widget-bundle.mjs",
|
||||
"check:openapi": "tsx ./scripts/check-openapi-schema.mts",
|
||||
"ci:act:all": "bash scripts/ci/act.sh all",
|
||||
"ci:act:cleanup": "bash scripts/ci/act.sh cleanup",
|
||||
"ci:act:docker": "bash scripts/ci/act.sh docker",
|
||||
"ci:act:fast": "bash scripts/ci/act.sh fast",
|
||||
"cli": "pnpm with-env tsx packages/cli/index.ts",
|
||||
"db:migration:postgresql:generate": "pnpm -F db migration:postgresql:generate",
|
||||
"db:migration:postgresql:run": "pnpm -F db migration:postgresql:run",
|
||||
"db:migration:sqlite:generate": "pnpm -F db migration:sqlite:generate",
|
||||
"db:migration:sqlite:run": "pnpm -F db migration:sqlite:run",
|
||||
"db:seed": "pnpm -F db seed",
|
||||
"db:push": "pnpm -F db push:sqlite",
|
||||
"db:studio": "pnpm -F db studio",
|
||||
"dev": "pnpm -F nextjs dev",
|
||||
"cli": "bun run with-env tsx packages/cli/index.ts",
|
||||
"db:migration:postgresql:generate": "bun run --filter @homarr/db migration:postgresql:generate",
|
||||
"db:migration:postgresql:run": "bun run --filter @homarr/db migration:postgresql:run",
|
||||
"db:migration:sqlite:generate": "bun run --filter @homarr/db migration:sqlite:generate",
|
||||
"db:migration:sqlite:run": "bun run --filter @homarr/db migration:sqlite:run",
|
||||
"db:seed": "bun run --filter @homarr/db seed",
|
||||
"db:push": "bun run --filter @homarr/db push:sqlite",
|
||||
"db:studio": "bun run --filter @homarr/db studio",
|
||||
"dev": "bun run --filter @homarr/nextjs dev",
|
||||
"dev:benchmark": "node --experimental-strip-types scripts/benchmarks/dev-board.mts",
|
||||
"dev:cli": "go -C tools/homarr-dev run -buildvcs=false .",
|
||||
"dev:cli:build": "go -C tools/homarr-dev build -buildvcs=false .",
|
||||
"dev:cli:install": "go -C tools/homarr-dev install -buildvcs=false .",
|
||||
"dev:docs": "pnpm -F @homarr/docs dev",
|
||||
"dev:docs": "bun run --filter @homarr/docs dev",
|
||||
"docker:docs": "docker compose -f apps/workshop/docker-compose.yml up --build docs",
|
||||
"docker:workshop": "docker compose -f apps/workshop/docker-compose.yml up --build workshop",
|
||||
"docker:dev": "docker compose -f ./development/development.docker-compose.yml up",
|
||||
@@ -40,15 +292,15 @@
|
||||
"format:fix": "turbo format --continue",
|
||||
"lint": "turbo lint --continue",
|
||||
"lint:fix": "turbo lint --continue -- --fix",
|
||||
"lint:ws": "pnpm dlx sherif@latest",
|
||||
"lint:ws": "bun x sherif@latest",
|
||||
"qa:browser-agent:ux": "bash scripts/browser-agent/ux-coherence-happy-paths.sh",
|
||||
"release": "semantic-release",
|
||||
"scripts:update-bug-report-template": "tsx ./scripts/update-bug-report-template.mts",
|
||||
"scripts:update-openapi": "tsx ./scripts/check-openapi-schema.mts --write",
|
||||
"scripts:update-readme-integrations": "tsx ./scripts/update-integration-list.mts",
|
||||
"start": "pnpm -F nextjs start",
|
||||
"start": "bun run --filter @homarr/nextjs start",
|
||||
"test": "cross-env NODE_ENV=development CI=true vitest run --project \"!e2e\" --project \"!integration\" --project \"!docs-screenshots\"",
|
||||
"test:coverage": "pnpm test --coverage.enabled",
|
||||
"test:coverage": "bun run test --coverage.enabled",
|
||||
"test:integration": "cross-env NODE_ENV=development CI=true vitest run --project integration --maxWorkers 2",
|
||||
"test:docs-screenshots": "cross-env UPDATE_DOCS_SCREENSHOTS=true NODE_ENV=development CI=true vitest run --project docs-screenshots",
|
||||
"test:e2e": "cross-env NODE_ENV=development CI=true vitest run --project e2e --maxWorkers 2",
|
||||
@@ -69,12 +321,11 @@
|
||||
"@semantic-release/commit-analyzer": "catalog:",
|
||||
"@semantic-release/git": "catalog:",
|
||||
"@semantic-release/github": "catalog:",
|
||||
"@semantic-release/npm": "catalog:",
|
||||
"@semantic-release/release-notes-generator": "catalog:",
|
||||
"@testcontainers/redis": "catalog:",
|
||||
"@turbo/gen": "catalog:",
|
||||
"@vitejs/plugin-react": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"@vitest/coverage-istanbul": "catalog:",
|
||||
"@vitest/ui": "catalog:",
|
||||
"conventional-changelog-conventionalcommits": "catalog:",
|
||||
"cross-env": "catalog:",
|
||||
@@ -84,16 +335,84 @@
|
||||
"oxlint": "catalog:",
|
||||
"semantic-release": "catalog:",
|
||||
"testcontainers": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"turbo": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite-tsconfig-paths": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24.18.0",
|
||||
"pnpm": ">=11.15.1"
|
||||
"overrides": {
|
||||
"@ai-sdk/provider-utils>undici": "8.10.2",
|
||||
"@assistant-ui/core": "0.3.7",
|
||||
"@assistant-ui/react-ai-sdk>@ai-sdk/mcp": "2.0.27",
|
||||
"@assistant-ui/react-ai-sdk>@ai-sdk/react": "4.0.59",
|
||||
"@assistant-ui/react-ai-sdk>ai": "7.0.56",
|
||||
"@assistant-ui/react-ai-sdk>assistant-stream": "0.3.34",
|
||||
"@assistant-ui/store": "0.3.5",
|
||||
"@assistant-ui/tap": "0.9.10",
|
||||
"@babel/helpers@<7.26.10": ">=7.29.7",
|
||||
"@babel/runtime@<7.26.10": ">=7.29.7",
|
||||
"@grpc/grpc-js@<1.12.7": "1.13.6",
|
||||
"@rvf/set-get@<7.0.2": "7.0.2",
|
||||
"@xmldom/xmldom@<0.8.15": "0.8.15",
|
||||
"assistant-cloud": "0.1.38",
|
||||
"axios@>=1.0.0 <1.18.1": "1.20.0",
|
||||
"brace-expansion@>=1.0.0 <1.1.21": "1.1.21",
|
||||
"brace-expansion@>=2.0.0 <2.1.7": "2.1.7",
|
||||
"brace-expansion@>=4.0.0 <5.0.9": "5.0.12",
|
||||
"browserslist@<4.28.7": "4.28.7",
|
||||
"esbuild@<=0.24.2": ">=0.28.1",
|
||||
"fast-uri@>=3.0.0 <3.1.6": "3.1.8",
|
||||
"find-my-way@<9.6.1": "9.9.0",
|
||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.6",
|
||||
"hono@<4.6.5": ">=4.13.7",
|
||||
"image-size@<=2.0.2": "2.0.3",
|
||||
"immutable@>=5.0.0-beta.1 <5.1.8": "5.1.8",
|
||||
"ip-address@<10.3.1": "10.7.1",
|
||||
"js-yaml@>=3.0.0 <3.15.2": "3.15.2",
|
||||
"js-yaml@>=4.0.0 <4.3.2": "4.3.2",
|
||||
"jsdom>undici": "8.10.2",
|
||||
"linkifyjs@<4.3.2": ">=4.3.3",
|
||||
"lodash-es@<4.17.24": "4.18.1",
|
||||
"nanoid@>=3.0.0 <3.3.18": "3.3.18",
|
||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.16",
|
||||
"picomatch@>=2.0.0 <2.3.2": "2.3.2",
|
||||
"postcss@<8.5.23": "8.5.23",
|
||||
"prismjs@<1.30.0": ">=1.30.0",
|
||||
"protobufjs@<7.5.5": ">=7.5.5 <8",
|
||||
"proxmox-api>undici": "7.29.1",
|
||||
"react-is": "^19.2.7",
|
||||
"rollup@>=4.0.0 <4.22.4": ">=4.62.2",
|
||||
"serialize-javascript@<7.0.5": "7.0.5",
|
||||
"sha.js@<=2.4.11": ">=2.4.12",
|
||||
"shell-quote@<1.8.5": "1.11.0",
|
||||
"svgo@>=3.0.0 <3.3.5": "3.3.5",
|
||||
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.3",
|
||||
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.3",
|
||||
"tmp@<=0.2.3": ">=0.2.7",
|
||||
"undici@>=7.0.0 <7.29.1": "7.29.1",
|
||||
"undici@>=8.0.0 <8.9.0": "8.9.0",
|
||||
"vite@>=5.0.0 <=5.4.18": ">=8.1.4"
|
||||
},
|
||||
"packageManager": "pnpm@11.15.1"
|
||||
"engines": {
|
||||
"bun": ">=1.4.2",
|
||||
"node": ">=24.18.0"
|
||||
},
|
||||
"packageManager": "bun@1.4.2",
|
||||
"patchedDependencies": {
|
||||
"dns-caching@0.2.9": "patches/dns-caching.patch",
|
||||
"@types/node-unifi@2.5.1": "patches/@types__node-unifi.patch",
|
||||
"mantine-datatable@9.3.1": "patches/mantine-datatable@9.3.1.patch",
|
||||
"trpc-to-openapi@3.3.0": "patches/trpc-to-openapi.patch"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"@tree-sitter-grammars/tree-sitter-yaml",
|
||||
"bcrypt",
|
||||
"better-sqlite3",
|
||||
"esbuild",
|
||||
"sharp",
|
||||
"ssh2",
|
||||
"tree-sitter",
|
||||
"tree-sitter-json"
|
||||
]
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"run": "pnpm with-env node cli.cjs",
|
||||
"run": "bun run with-env node cli.cjs",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"with-env": "dotenv -e .env --"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"outputs": ["cli.cjs"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,8 @@
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"check:architecture": "node ./scripts/check-architecture.mjs",
|
||||
"generate:catalog": "tsx ./scripts/generate-component-metadata.ts",
|
||||
"generate:components": "pnpm generate:catalog",
|
||||
"generate": "pnpm generate:catalog",
|
||||
"generate:components": "bun run generate:catalog",
|
||||
"generate": "bun run generate:catalog",
|
||||
"evaluate:ai": "tsx ./scripts/evaluate-ai-authoring.ts",
|
||||
"evaluate:assistant-ai": "tsx ./scripts/evaluate-ai-authoring.ts --assistant",
|
||||
"test:reference-apis": "tsx ./scripts/verify-reference-apis.ts",
|
||||
|
||||
+14
-14
@@ -18,24 +18,24 @@
|
||||
"./validationSchemas": "./validationSchemas.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm run build:sqlite && pnpm run build:postgresql",
|
||||
"build": "bun run build:sqlite && bun run build:postgresql",
|
||||
"build:postgresql": "esbuild migrations/postgresql/migrate.ts --bundle --platform=node --outfile=migrations/postgresql/migrate.cjs",
|
||||
"build:sqlite": "esbuild migrations/sqlite/migrate.ts --bundle --platform=node --outfile=migrations/sqlite/migrate.cjs",
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"migration:custom": "pnpm with-env tsx ./migrations/custom/run-custom.ts",
|
||||
"migration:postgresql:drop": "pnpm with-env drizzle-kit drop --config ./configs/postgresql.config.ts",
|
||||
"migration:postgresql:generate": "pnpm with-env drizzle-kit generate --config ./configs/postgresql.config.ts",
|
||||
"migration:postgresql:run": "pnpm with-env drizzle-kit migrate --config ./configs/postgresql.config.ts && pnpm run seed && pnpm run migration:custom",
|
||||
"migration:sqlite:drop": "pnpm with-env drizzle-kit drop --config ./configs/sqlite.config.ts",
|
||||
"migration:sqlite:generate": "pnpm with-env drizzle-kit generate --config ./configs/sqlite.config.ts",
|
||||
"migration:sqlite:run": "pnpm with-env drizzle-kit migrate --config ./configs/sqlite.config.ts && pnpm run seed && pnpm run migration:custom",
|
||||
"push:postgresql": "pnpm with-env drizzle-kit push --config ./configs/postgresql.config.ts",
|
||||
"push:sqlite": "pnpm with-env drizzle-kit push --config ./configs/sqlite.config.ts",
|
||||
"seed": "pnpm with-env tsx ./migrations/run-seed.ts",
|
||||
"studio": "pnpm with-env drizzle-kit studio --config ./configs/sqlite.config.ts",
|
||||
"studio:postgresql": "pnpm with-env drizzle-kit studio --config ./configs/postgresql.config.ts",
|
||||
"migration:custom": "bun run with-env tsx ./migrations/custom/run-custom.ts",
|
||||
"migration:postgresql:drop": "bun run with-env drizzle-kit drop --config ./configs/postgresql.config.ts",
|
||||
"migration:postgresql:generate": "bun run with-env drizzle-kit generate --config ./configs/postgresql.config.ts",
|
||||
"migration:postgresql:run": "bun run with-env drizzle-kit migrate --config ./configs/postgresql.config.ts && bun run seed && bun run migration:custom",
|
||||
"migration:sqlite:drop": "bun run with-env drizzle-kit drop --config ./configs/sqlite.config.ts",
|
||||
"migration:sqlite:generate": "bun run with-env drizzle-kit generate --config ./configs/sqlite.config.ts",
|
||||
"migration:sqlite:run": "bun run with-env drizzle-kit migrate --config ./configs/sqlite.config.ts && bun run seed && bun run migration:custom",
|
||||
"push:postgresql": "bun run with-env drizzle-kit push --config ./configs/postgresql.config.ts",
|
||||
"push:sqlite": "bun run with-env drizzle-kit push --config ./configs/sqlite.config.ts",
|
||||
"seed": "bun run with-env tsx ./migrations/run-seed.ts",
|
||||
"studio": "bun run with-env drizzle-kit studio --config ./configs/sqlite.config.ts",
|
||||
"studio:postgresql": "bun run with-env drizzle-kit studio --config ./configs/postgresql.config.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"with-env": "dotenv -e ../../.env --"
|
||||
},
|
||||
@@ -60,11 +60,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@libsql/client": "catalog:",
|
||||
"@types/better-sqlite3": "catalog:",
|
||||
"@types/pg": "catalog:",
|
||||
"dotenv-cli": "catalog:",
|
||||
"esbuild": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"outputs": ["migrations/sqlite/migrate.cjs", "migrations/postgresql/migrate.cjs"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,9 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"docs:generate": "node --import tsx ./src/docs/codegen.ts",
|
||||
"docs:refresh": "node --import tsx ./src/docs/codegen.ts --refresh",
|
||||
"format": "oxfmt .",
|
||||
"postinstall": "tsx ./src/docs/codegen.ts",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
@@ -31,7 +32,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -1,104 +1,48 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path, { dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { XMLParser } from "fast-xml-parser";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { createDocumentationLink } from "./index";
|
||||
import { widgetDocSlugs } from "./widget-doc-slugs";
|
||||
import { getIntegrationDocumentationSlug, integrationKinds } from "../integration";
|
||||
import { createDocumentationMetadata, documentationSitemapUrl, parseDocumentationSitemap } from "./metadata";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const snapshotUrl = new URL("./sitemap-paths.json", import.meta.url);
|
||||
const outputUrl = new URL("./homarr-docs-sitemap.ts", import.meta.url);
|
||||
|
||||
const removeCommonUrl = (url: string) => {
|
||||
return url.replace("https://homarr.dev", "");
|
||||
};
|
||||
|
||||
const sitemapSchema = z.object({
|
||||
urlset: z.object({
|
||||
url: z.array(
|
||||
z.object({
|
||||
loc: z.string(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
const fetchSitemapAsync = async () => {
|
||||
const response = await fetch(createDocumentationLink("/sitemap.xml"));
|
||||
return await response.text();
|
||||
};
|
||||
|
||||
const parseXml = (sitemapXml: string) => {
|
||||
const parser = new XMLParser();
|
||||
const data: unknown = parser.parse(sitemapXml);
|
||||
const result = sitemapSchema.safeParse(data);
|
||||
if (!result.success) {
|
||||
throw new Error("Invalid sitemap schema");
|
||||
const writeIfChangedAsync = async (url: URL, content: string) => {
|
||||
let existing: string | undefined;
|
||||
try {
|
||||
existing = await fs.readFile(url, "utf8");
|
||||
} catch (error) {
|
||||
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return result.data;
|
||||
if (existing !== content) {
|
||||
await fs.writeFile(url, content);
|
||||
}
|
||||
};
|
||||
|
||||
const mapSitemapXmlToPaths = (sitemapData: z.infer<typeof sitemapSchema>) => {
|
||||
return sitemapData.urlset.url.map((url) => removeCommonUrl(url.loc));
|
||||
};
|
||||
|
||||
const createSitemapPathType = (paths: string[]) => {
|
||||
return "export type HomarrDocumentationPath =\n" + paths.map((path) => ` | "${path.replace(/\/$/, "")}"`).join("\n");
|
||||
};
|
||||
|
||||
const updateSitemapTypeFileAsync = async (sitemapPathType: string) => {
|
||||
const content =
|
||||
"// This file is auto-generated by the codegen script\n" +
|
||||
"// it uses the sitemap.xml to generate the HomarrDocumentationPath type\n" +
|
||||
sitemapPathType +
|
||||
";\n";
|
||||
|
||||
await fs.writeFile(outputPath, content);
|
||||
};
|
||||
|
||||
const slugMapPaths = [
|
||||
...integrationKinds
|
||||
.map(getIntegrationDocumentationSlug)
|
||||
.filter(Boolean)
|
||||
.map((slug) => `/docs/integrations/${slug}`),
|
||||
...Object.values(widgetDocSlugs)
|
||||
.filter(Boolean)
|
||||
.map((slug) => `/docs/widgets/${slug}`),
|
||||
];
|
||||
|
||||
const outputPath = path.join(__dirname, "homarr-docs-sitemap.ts");
|
||||
|
||||
const main = async () => {
|
||||
let paths: string[];
|
||||
try {
|
||||
const sitemapXml = await fetchSitemapAsync();
|
||||
const sitemapData = parseXml(sitemapXml);
|
||||
paths = mapSitemapXmlToPaths(sitemapData);
|
||||
} catch {
|
||||
const exists = await fs.access(outputPath).then(
|
||||
() => true,
|
||||
() => false,
|
||||
);
|
||||
if (exists) {
|
||||
console.warn("Could not fetch sitemap from homarr.dev, keeping existing generated file");
|
||||
return;
|
||||
}
|
||||
console.warn("Could not fetch sitemap from homarr.dev and no existing file, generating from slug maps only");
|
||||
paths = [];
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length > 1 || (args.length === 1 && args[0] !== "--refresh")) {
|
||||
throw new Error("Usage: codegen.ts [--refresh]");
|
||||
}
|
||||
|
||||
paths.push("/sitemap.xml", "/blog", "/search");
|
||||
for (const p of slugMapPaths) {
|
||||
if (!paths.includes(p)) {
|
||||
paths.push(p);
|
||||
let sitemapPaths: string[];
|
||||
if (args.includes("--refresh")) {
|
||||
const response = await fetch(documentationSitemapUrl, { signal: AbortSignal.timeout(30_000) });
|
||||
if (!response.ok) {
|
||||
throw new Error(`Could not refresh documentation sitemap: HTTP ${response.status}`);
|
||||
}
|
||||
sitemapPaths = parseDocumentationSitemap(await response.text());
|
||||
} else {
|
||||
sitemapPaths = z.array(z.string()).parse(JSON.parse(await fs.readFile(snapshotUrl, "utf8")));
|
||||
}
|
||||
|
||||
const sitemapPathType = createSitemapPathType(paths);
|
||||
await updateSitemapTypeFileAsync(sitemapPathType);
|
||||
const metadata = createDocumentationMetadata(sitemapPaths);
|
||||
if (args.includes("--refresh")) {
|
||||
await writeIfChangedAsync(snapshotUrl, metadata.snapshot);
|
||||
}
|
||||
await writeIfChangedAsync(outputUrl, metadata.source);
|
||||
};
|
||||
|
||||
void main();
|
||||
await main();
|
||||
@@ -1,47 +1,100 @@
|
||||
// This file is auto-generated by the codegen script
|
||||
// it uses the sitemap.xml to generate the HomarrDocumentationPath type
|
||||
// Generated from sitemap-paths.json and local documentation slugs.
|
||||
// Update offline with docs:generate; refresh the pinned sitemap with docs:refresh.
|
||||
export type HomarrDocumentationPath =
|
||||
| "/search"
|
||||
| ""
|
||||
| "/about-us"
|
||||
| "/api-reference"
|
||||
| "/api-reference/apps/appRouter-all"
|
||||
| "/api-reference/apps/appRouter-byId"
|
||||
| "/api-reference/apps/appRouter-create"
|
||||
| "/api-reference/apps/appRouter-delete"
|
||||
| "/api-reference/apps/appRouter-getPaginated"
|
||||
| "/api-reference/apps/appRouter-search"
|
||||
| "/api-reference/apps/appRouter-selectable"
|
||||
| "/api-reference/apps/appRouter-update"
|
||||
| "/api-reference/boards/boardRouter-addItem"
|
||||
| "/api-reference/boards/boardRouter-changeBoardVisibility"
|
||||
| "/api-reference/boards/boardRouter-createBoard"
|
||||
| "/api-reference/boards/boardRouter-deleteBoard"
|
||||
| "/api-reference/boards/boardRouter-duplicateBoard"
|
||||
| "/api-reference/boards/boardRouter-getAllBoards"
|
||||
| "/api-reference/boards/boardRouter-renameBoard"
|
||||
| "/api-reference/boards/boardRouter-savePartialBoardSettings"
|
||||
| "/api-reference/boards/boardRouter-setHomeBoard"
|
||||
| "/api-reference/boards/boardRouter-setMobileHomeBoard"
|
||||
| "/api-reference/boards/boardRouter-updateItemLayout"
|
||||
| "/api-reference/info/infoRouter-getInfo"
|
||||
| "/api-reference/integrations/integration-request"
|
||||
| "/api-reference/invites/inviteRouter-createInvite"
|
||||
| "/api-reference/invites/inviteRouter-deleteInvite"
|
||||
| "/api-reference/invites/inviteRouter-getAll"
|
||||
| "/api-reference/settings/serverSettingsRouter-getBoardSettings"
|
||||
| "/api-reference/settings/serverSettingsRouter-updateBoardSettings"
|
||||
| "/api-reference/users/userRouter-changeColorScheme"
|
||||
| "/api-reference/users/userRouter-changeDdgBangs"
|
||||
| "/api-reference/users/userRouter-changeDefaultSearchEngine"
|
||||
| "/api-reference/users/userRouter-changeEnableRightClickOnWidgets"
|
||||
| "/api-reference/users/userRouter-changeFirstDayOfWeek"
|
||||
| "/api-reference/users/userRouter-changeHeaderPreferences"
|
||||
| "/api-reference/users/userRouter-changeHomeBoards"
|
||||
| "/api-reference/users/userRouter-changePassword"
|
||||
| "/api-reference/users/userRouter-changeSearchPreferences"
|
||||
| "/api-reference/users/userRouter-create"
|
||||
| "/api-reference/users/userRouter-delete"
|
||||
| "/api-reference/users/userRouter-editProfile"
|
||||
| "/api-reference/users/userRouter-getAll"
|
||||
| "/api-reference/users/userRouter-getById"
|
||||
| "/api-reference/users/userRouter-search"
|
||||
| "/api-reference/users/userRouter-selectable"
|
||||
| "/api-reference/users/userRouter-setProfileImage"
|
||||
| "/blog"
|
||||
| "/blog/2023/01/11/version0.11"
|
||||
| "/blog/2023/04/16/version0.12-more-widgets"
|
||||
| "/blog/2023/11/10/authentication"
|
||||
| "/blog/2023/12/22/updated-documentation"
|
||||
| "/blog/2024/09/23/version-1.0"
|
||||
| "/blog/2024/12/17/open-beta-1.0"
|
||||
| "/blog/2025/01/19/migration-guide-1.0"
|
||||
| "/blog/2025/08/02/using-argus"
|
||||
| "/blog/2026/09/03/homarr-2.0"
|
||||
| "/blog/documentation-migration"
|
||||
| "/blog/translations"
|
||||
| "/docs"
|
||||
| "/docs/advanced"
|
||||
| "/docs/advanced/mysql-to-sqlite"
|
||||
| "/docs/getting-started/after-the-installation"
|
||||
| "/docs/getting-started/glossary"
|
||||
| "/docs/getting-started"
|
||||
| "/docs/community/donate"
|
||||
| "/docs/community/faq"
|
||||
| "/docs/community/get-in-touch"
|
||||
| "/docs/community"
|
||||
| "/docs/community/license"
|
||||
| "/docs/community/translations"
|
||||
| "/docs/management/assistant"
|
||||
| "/docs/management/backup"
|
||||
| "/docs/management"
|
||||
| "/docs/management/mcp"
|
||||
| "/docs/integrations"
|
||||
| "/docs/workshop/homarr-provider"
|
||||
| "/docs/workshop"
|
||||
| "/docs/widgets"
|
||||
| "/docs/advanced/command-line"
|
||||
| "/docs/advanced/environment-variables"
|
||||
| "/docs/advanced/command-line/development"
|
||||
| "/docs/advanced/command-line/fix-usernames"
|
||||
| "/docs/advanced/command-line/integrations"
|
||||
| "/docs/advanced/command-line/password-recovery"
|
||||
| "/docs/advanced/command-line/users"
|
||||
| "/docs/advanced/development/getting-started"
|
||||
| "/docs/advanced/development/kubernetes"
|
||||
| "/docs/advanced/development/widgets-and-integrations"
|
||||
| "/docs/advanced/development/workshop-operator"
|
||||
| "/docs/advanced/development/workshop"
|
||||
| "/docs/advanced/development/workshop-operator"
|
||||
| "/docs/advanced/environment-variables"
|
||||
| "/docs/advanced/icons"
|
||||
| "/docs/advanced/keyboard-shortcuts"
|
||||
| "/docs/advanced/running-as-different-user"
|
||||
| "/docs/advanced/mysql-to-sqlite"
|
||||
| "/docs/advanced/proxy"
|
||||
| "/docs/advanced/running-as-different-user"
|
||||
| "/docs/advanced/single-sign-on"
|
||||
| "/docs/advanced/styling"
|
||||
| "/docs/community"
|
||||
| "/docs/community/donate"
|
||||
| "/docs/community/faq"
|
||||
| "/docs/community/get-in-touch"
|
||||
| "/docs/community/license"
|
||||
| "/docs/community/translations"
|
||||
| "/docs/getting-started"
|
||||
| "/docs/getting-started/after-the-installation"
|
||||
| "/docs/getting-started/glossary"
|
||||
| "/docs/getting-started/installation"
|
||||
| "/docs/getting-started/installation/docker"
|
||||
| "/docs/getting-started/installation/easy-panel"
|
||||
| "/docs/getting-started/installation/helm"
|
||||
| "/docs/getting-started/installation/home-assistant"
|
||||
| "/docs/getting-started/installation/hostinger"
|
||||
| "/docs/getting-started/installation"
|
||||
| "/docs/getting-started/installation/pika-pods"
|
||||
| "/docs/getting-started/installation/portainer"
|
||||
| "/docs/getting-started/installation/proxmox"
|
||||
@@ -52,24 +105,7 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/getting-started/installation/source"
|
||||
| "/docs/getting-started/installation/synology"
|
||||
| "/docs/getting-started/installation/unraid"
|
||||
| "/docs/management/api"
|
||||
| "/docs/management/apps"
|
||||
| "/docs/management/boards"
|
||||
| "/docs/management/certificates"
|
||||
| "/docs/management/custom-widgets/agent-authoring"
|
||||
| "/docs/management/custom-widgets/authoring"
|
||||
| "/docs/management/custom-widgets/component-reference"
|
||||
| "/docs/management/custom-widgets/custom-jsx"
|
||||
| "/docs/management/custom-widgets"
|
||||
| "/docs/management/custom-widgets/requests-and-security"
|
||||
| "/docs/management/custom-widgets/troubleshooting"
|
||||
| "/docs/management/logs"
|
||||
| "/docs/management/media"
|
||||
| "/docs/management/integrations"
|
||||
| "/docs/management/search-engines"
|
||||
| "/docs/management/settings"
|
||||
| "/docs/management/tasks"
|
||||
| "/docs/management/users"
|
||||
| "/docs/integrations"
|
||||
| "/docs/integrations/adguard-home"
|
||||
| "/docs/integrations/anchor"
|
||||
| "/docs/integrations/archiveteam-warrior"
|
||||
@@ -161,6 +197,29 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/integrations/whats-up-docker"
|
||||
| "/docs/integrations/xteve"
|
||||
| "/docs/integrations/your-spotify"
|
||||
| "/docs/management"
|
||||
| "/docs/management/api"
|
||||
| "/docs/management/apps"
|
||||
| "/docs/management/assistant"
|
||||
| "/docs/management/backup"
|
||||
| "/docs/management/boards"
|
||||
| "/docs/management/certificates"
|
||||
| "/docs/management/custom-widgets"
|
||||
| "/docs/management/custom-widgets/agent-authoring"
|
||||
| "/docs/management/custom-widgets/authoring"
|
||||
| "/docs/management/custom-widgets/component-reference"
|
||||
| "/docs/management/custom-widgets/custom-jsx"
|
||||
| "/docs/management/custom-widgets/requests-and-security"
|
||||
| "/docs/management/custom-widgets/troubleshooting"
|
||||
| "/docs/management/integrations"
|
||||
| "/docs/management/logs"
|
||||
| "/docs/management/mcp"
|
||||
| "/docs/management/media"
|
||||
| "/docs/management/search-engines"
|
||||
| "/docs/management/settings"
|
||||
| "/docs/management/tasks"
|
||||
| "/docs/management/users"
|
||||
| "/docs/widgets"
|
||||
| "/docs/widgets/air-quality"
|
||||
| "/docs/widgets/anchor-note"
|
||||
| "/docs/widgets/app"
|
||||
@@ -185,6 +244,9 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/widgets/firewall"
|
||||
| "/docs/widgets/health-monitoring"
|
||||
| "/docs/widgets/iframe"
|
||||
| "/docs/widgets/immich-album-carousel"
|
||||
| "/docs/widgets/immich-server-stats"
|
||||
| "/docs/widgets/indexer-manager"
|
||||
| "/docs/widgets/llama-cpp"
|
||||
| "/docs/widgets/media-missing"
|
||||
| "/docs/widgets/media-releases"
|
||||
@@ -196,9 +258,6 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/widgets/network-controller-status"
|
||||
| "/docs/widgets/network-controller-summary"
|
||||
| "/docs/widgets/notebook"
|
||||
| "/docs/widgets/immich-server-stats"
|
||||
| "/docs/widgets/immich-album-carousel"
|
||||
| "/docs/widgets/indexer-manager"
|
||||
| "/docs/widgets/notifications"
|
||||
| "/docs/widgets/paperless-ngx"
|
||||
| "/docs/widgets/patchmon"
|
||||
@@ -210,6 +269,7 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/widgets/stats"
|
||||
| "/docs/widgets/stock-price"
|
||||
| "/docs/widgets/system-disks"
|
||||
| "/docs/widgets/system-resources"
|
||||
| "/docs/widgets/timer"
|
||||
| "/docs/widgets/timetable"
|
||||
| "/docs/widgets/tracearr"
|
||||
@@ -221,215 +281,8 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/widgets/vpn"
|
||||
| "/docs/widgets/weather"
|
||||
| "/docs/widgets/whats-up-docker"
|
||||
| "/docs/advanced/command-line/development"
|
||||
| "/docs/advanced/command-line/fix-usernames"
|
||||
| "/docs/advanced/command-line/integrations"
|
||||
| "/docs/advanced/command-line/password-recovery"
|
||||
| "/docs/advanced/command-line/users"
|
||||
| "/docs/widgets/system-resources"
|
||||
| "/api-reference/apps/appRouter-getPaginated"
|
||||
| "/api-reference/apps/appRouter-all"
|
||||
| "/api-reference/apps/appRouter-create"
|
||||
| "/api-reference/apps/appRouter-search"
|
||||
| "/api-reference/apps/appRouter-selectable"
|
||||
| "/api-reference/apps/appRouter-byId"
|
||||
| "/api-reference/apps/appRouter-update"
|
||||
| "/api-reference/apps/appRouter-delete"
|
||||
| "/api-reference/boards/boardRouter-getAllBoards"
|
||||
| "/api-reference/boards/boardRouter-createBoard"
|
||||
| "/api-reference/boards/boardRouter-duplicateBoard"
|
||||
| "/api-reference/boards/boardRouter-renameBoard"
|
||||
| "/api-reference/boards/boardRouter-changeBoardVisibility"
|
||||
| "/api-reference/boards/boardRouter-deleteBoard"
|
||||
| "/api-reference/boards/boardRouter-setHomeBoard"
|
||||
| "/api-reference/boards/boardRouter-setMobileHomeBoard"
|
||||
| "/api-reference/boards/boardRouter-savePartialBoardSettings"
|
||||
| "/api-reference/boards/boardRouter-addItem"
|
||||
| "/api-reference/boards/boardRouter-updateItemLayout"
|
||||
| "/api-reference/info/infoRouter-getInfo"
|
||||
| "/api-reference/invites/inviteRouter-getAll"
|
||||
| "/api-reference/invites/inviteRouter-createInvite"
|
||||
| "/api-reference/invites/inviteRouter-deleteInvite"
|
||||
| "/api-reference/settings/serverSettingsRouter-getBoardSettings"
|
||||
| "/api-reference/settings/serverSettingsRouter-updateBoardSettings"
|
||||
| "/api-reference/users/userRouter-getAll"
|
||||
| "/api-reference/users/userRouter-create"
|
||||
| "/api-reference/users/userRouter-setProfileImage"
|
||||
| "/api-reference/users/userRouter-selectable"
|
||||
| "/api-reference/users/userRouter-search"
|
||||
| "/api-reference/users/userRouter-getById"
|
||||
| "/api-reference/users/userRouter-delete"
|
||||
| "/api-reference/users/userRouter-editProfile"
|
||||
| "/api-reference/users/userRouter-changePassword"
|
||||
| "/api-reference/users/userRouter-changeHomeBoards"
|
||||
| "/api-reference/users/userRouter-changeDefaultSearchEngine"
|
||||
| "/api-reference/users/userRouter-changeSearchPreferences"
|
||||
| "/api-reference/users/userRouter-changeColorScheme"
|
||||
| "/api-reference/users/userRouter-changeEnableRightClickOnWidgets"
|
||||
| "/api-reference/users/userRouter-changeHeaderPreferences"
|
||||
| "/api-reference/users/userRouter-changeDdgBangs"
|
||||
| "/api-reference/users/userRouter-changeFirstDayOfWeek"
|
||||
| "/api-reference/integrations/integration-request"
|
||||
| "/blog/translations"
|
||||
| "/blog/2024/09/23/version-1.0"
|
||||
| "/blog/2024/12/17/open-beta-1.0"
|
||||
| "/blog/documentation-migration"
|
||||
| "/blog/2023/01/11/version0.11"
|
||||
| "/blog/2023/04/16/version0.12-more-widgets"
|
||||
| "/blog/2023/12/22/updated-documentation"
|
||||
| "/blog/2023/11/10/authentication"
|
||||
| "/blog/2025/01/19/migration-guide-1.0"
|
||||
| "/blog/2026/09/03/homarr-2.0"
|
||||
| "/blog/2025/08/02/using-argus"
|
||||
| "/workshop"
|
||||
| "/api-reference"
|
||||
| "/about-us"
|
||||
| "/docs/workshop"
|
||||
| "/docs/workshop/homarr-provider"
|
||||
| "/search"
|
||||
| "/sitemap.xml"
|
||||
| "/docs/integrations/autobrr"
|
||||
| "/docs/integrations/jellystat"
|
||||
| "/docs/integrations/scrutiny"
|
||||
| "/docs/integrations/tubearchivist"
|
||||
| "/docs/integrations/frigate"
|
||||
| "/docs/integrations/komga"
|
||||
| "/docs/integrations/netalertx"
|
||||
| "/docs/integrations/jackett"
|
||||
| "/docs/integrations/your-spotify"
|
||||
| "/docs/integrations/romm"
|
||||
| "/docs/integrations/homebox"
|
||||
| "/docs/integrations/mealie"
|
||||
| "/docs/integrations/xteve"
|
||||
| "/docs/integrations/unmanic"
|
||||
| "/docs/integrations/syncthing-relay"
|
||||
| "/docs/integrations/stash"
|
||||
| "/docs/integrations/prometheus"
|
||||
| "/docs/integrations/plantit"
|
||||
| "/docs/integrations/netdata"
|
||||
| "/docs/integrations/fileflows"
|
||||
| "/docs/integrations/trilium"
|
||||
| "/docs/integrations/tandoor"
|
||||
| "/docs/integrations/spoolman"
|
||||
| "/docs/integrations/miniflux"
|
||||
| "/docs/integrations/maintainerr"
|
||||
| "/docs/integrations/linkwarden"
|
||||
| "/docs/integrations/karakeep"
|
||||
| "/docs/integrations/healthchecks"
|
||||
| "/docs/integrations/gatus"
|
||||
| "/docs/integrations/changedetection"
|
||||
| "/docs/integrations/caddy"
|
||||
| "/docs/integrations/sabnzbd"
|
||||
| "/docs/integrations/nzbget"
|
||||
| "/docs/integrations/deluge"
|
||||
| "/docs/integrations/transmission"
|
||||
| "/docs/integrations/q-bittorent"
|
||||
| "/docs/integrations/aria2"
|
||||
| "/docs/integrations/slskd"
|
||||
| "/docs/integrations/sonarr"
|
||||
| "/docs/integrations/radarr"
|
||||
| "/docs/integrations/lidarr"
|
||||
| "/docs/integrations/readarr"
|
||||
| "/docs/integrations/bindery"
|
||||
| "/docs/integrations/prowlarr"
|
||||
| "/docs/integrations/bazarr"
|
||||
| "/docs/integrations/jellyfin"
|
||||
| "/docs/integrations/emby"
|
||||
| "/docs/integrations/plex"
|
||||
| "/docs/integrations/jellyseerr"
|
||||
| "/docs/integrations/seerr"
|
||||
| "/docs/integrations/overseerr"
|
||||
| "/docs/integrations/pi-hole"
|
||||
| "/docs/integrations/adguard-home"
|
||||
| "/docs/integrations/technitium-dns"
|
||||
| "/docs/integrations/home-assistant"
|
||||
| "/docs/integrations/open-media-vault"
|
||||
| "/docs/integrations/dash-dot"
|
||||
| "/docs/integrations/glances"
|
||||
| "/docs/integrations/tdarr"
|
||||
| "/docs/integrations/proxmox"
|
||||
| "/docs/integrations/nextcloud"
|
||||
| "/docs/integrations/unifi-controller"
|
||||
| "/docs/integrations/opnsense"
|
||||
| "/docs/integrations/ntfy"
|
||||
| "/docs/integrations/gotify"
|
||||
| "/docs/integrations/ical"
|
||||
| "/docs/integrations/anchor"
|
||||
| "/docs/integrations/truenas"
|
||||
| "/docs/integrations/synology"
|
||||
| "/docs/integrations/unraid"
|
||||
| "/docs/integrations/coolify"
|
||||
| "/docs/integrations/immich"
|
||||
| "/docs/integrations/paperless-ngx"
|
||||
| "/docs/integrations/patchmon"
|
||||
| "/docs/integrations/tracearr"
|
||||
| "/docs/integrations/speedtest-tracker"
|
||||
| "/docs/integrations/uptime-kuma"
|
||||
| "/docs/integrations/audiobookshelf"
|
||||
| "/docs/integrations/navidrome"
|
||||
| "/docs/integrations/umami"
|
||||
| "/docs/integrations/peanut"
|
||||
| "/docs/integrations/beszel"
|
||||
| "/docs/integrations/gluetun"
|
||||
| "/docs/integrations/traefik"
|
||||
| "/docs/integrations/archiveteam-warrior"
|
||||
| "/docs/integrations/wazuh"
|
||||
| "/docs/integrations/whats-up-docker"
|
||||
| "/docs/integrations/llama-cpp"
|
||||
| "/docs/widgets/stats"
|
||||
| "/docs/widgets/clock"
|
||||
| "/docs/widgets/weather"
|
||||
| "/docs/widgets/air-quality"
|
||||
| "/docs/widgets/countdown"
|
||||
| "/docs/widgets/timer"
|
||||
| "/docs/widgets/app"
|
||||
| "/docs/widgets/iframe"
|
||||
| "/docs/widgets/video"
|
||||
| "/docs/widgets/notebook"
|
||||
| "/docs/widgets/anchor-note"
|
||||
| "/docs/widgets/dns-hole-summary"
|
||||
| "/docs/widgets/dns-hole-controls"
|
||||
| "/docs/widgets/smart-home-entity-state"
|
||||
| "/docs/widgets/smart-home-execute-automation"
|
||||
| "/docs/widgets/stock-price"
|
||||
| "/docs/widgets/media-server"
|
||||
| "/docs/widgets/calendar"
|
||||
| "/docs/widgets/downloads"
|
||||
| "/docs/widgets/media-request-list"
|
||||
| "/docs/widgets/media-request-stats"
|
||||
| "/docs/widgets/media-transcoding"
|
||||
| "/docs/widgets/media-missing"
|
||||
| "/docs/widgets/minecraft-server-status"
|
||||
| "/docs/widgets/network-controller-summary"
|
||||
| "/docs/widgets/network-controller-status"
|
||||
| "/docs/widgets/rss-feed"
|
||||
| "/docs/widgets/bookmarks"
|
||||
| "/docs/widgets/indexer-manager"
|
||||
| "/docs/widgets/health-monitoring"
|
||||
| "/docs/widgets/releases"
|
||||
| "/docs/widgets/media-releases"
|
||||
| "/docs/widgets/docker-containers"
|
||||
| "/docs/widgets/firewall"
|
||||
| "/docs/widgets/notifications"
|
||||
| "/docs/widgets/system-resources"
|
||||
| "/docs/widgets/coolify"
|
||||
| "/docs/widgets/system-disks"
|
||||
| "/docs/widgets/timetable"
|
||||
| "/docs/widgets/immich-server-stats"
|
||||
| "/docs/widgets/immich-album-carousel"
|
||||
| "/docs/widgets/paperless-ngx"
|
||||
| "/docs/widgets/patchmon"
|
||||
| "/docs/widgets/bazarr"
|
||||
| "/docs/widgets/tracearr"
|
||||
| "/docs/widgets/speedtest-tracker"
|
||||
| "/docs/widgets/uptime-kuma"
|
||||
| "/docs/widgets/audio-stats"
|
||||
| "/docs/widgets/umami"
|
||||
| "/docs/widgets/vpn"
|
||||
| "/docs/widgets/ups"
|
||||
| "/docs/widgets/beszel-system-table"
|
||||
| "/docs/widgets/beszel-system-grid"
|
||||
| "/docs/widgets/beszel-alerts"
|
||||
| "/docs/widgets/beszel-system-stats"
|
||||
| "/docs/widgets/traefik"
|
||||
| "/docs/widgets/custom-api"
|
||||
| "/docs/widgets/assistant"
|
||||
| "/docs/widgets/whats-up-docker"
|
||||
| "/docs/widgets/llama-cpp";
|
||||
| "/workshop";
|
||||
@@ -0,0 +1,55 @@
|
||||
import { XMLParser } from "fast-xml-parser";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { widgetDocSlugs } from "./widget-doc-slugs";
|
||||
import { getIntegrationDocumentationSlug, integrationKinds } from "../integration";
|
||||
|
||||
const documentationOrigin = "https://homarr.dev";
|
||||
export const documentationSitemapUrl = `${documentationOrigin}/sitemap.xml`;
|
||||
|
||||
const normalizePaths = (paths: readonly string[]) => {
|
||||
const normalized = paths.map((path) => {
|
||||
const url = new URL(path, documentationOrigin);
|
||||
if (url.origin !== documentationOrigin || url.search || url.hash) {
|
||||
throw new Error(`Invalid documentation path: ${path}`);
|
||||
}
|
||||
return url.pathname.replace(/\/+$/u, "");
|
||||
});
|
||||
return [...new Set(normalized)].toSorted();
|
||||
};
|
||||
|
||||
export const parseDocumentationSitemap = (xml: string) => {
|
||||
const data: unknown = new XMLParser({ isArray: (name) => name === "url" }).parse(xml);
|
||||
const sitemap = z
|
||||
.object({ urlset: z.object({ url: z.array(z.object({ loc: z.string().url() })).min(1) }) })
|
||||
.parse(data);
|
||||
return normalizePaths(sitemap.urlset.url.map((url) => url.loc));
|
||||
};
|
||||
|
||||
// Both remote refresh and offline generation share normalization and slug reconciliation.
|
||||
export const createDocumentationMetadata = (sitemapPaths: readonly string[]) => {
|
||||
const snapshotPaths = normalizePaths(sitemapPaths);
|
||||
const paths = normalizePaths([
|
||||
...snapshotPaths,
|
||||
"/sitemap.xml",
|
||||
"/blog",
|
||||
"/search",
|
||||
...integrationKinds
|
||||
.map(getIntegrationDocumentationSlug)
|
||||
.filter((slug) => slug !== null)
|
||||
.map((slug) => `/docs/integrations/${slug}`),
|
||||
...Object.values(widgetDocSlugs)
|
||||
.filter((slug) => slug !== null)
|
||||
.map((slug) => `/docs/widgets/${slug}`),
|
||||
]);
|
||||
|
||||
return {
|
||||
snapshot: `${JSON.stringify(snapshotPaths, null, 2)}\n`,
|
||||
source:
|
||||
"// Generated from sitemap-paths.json and local documentation slugs.\n" +
|
||||
"// Update offline with docs:generate; refresh the pinned sitemap with docs:refresh.\n" +
|
||||
"export type HomarrDocumentationPath =\n" +
|
||||
paths.map((path) => ` | ${JSON.stringify(path)}`).join("\n") +
|
||||
";\n",
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,284 @@
|
||||
[
|
||||
"",
|
||||
"/about-us",
|
||||
"/api-reference",
|
||||
"/api-reference/apps/appRouter-all",
|
||||
"/api-reference/apps/appRouter-byId",
|
||||
"/api-reference/apps/appRouter-create",
|
||||
"/api-reference/apps/appRouter-delete",
|
||||
"/api-reference/apps/appRouter-getPaginated",
|
||||
"/api-reference/apps/appRouter-search",
|
||||
"/api-reference/apps/appRouter-selectable",
|
||||
"/api-reference/apps/appRouter-update",
|
||||
"/api-reference/boards/boardRouter-addItem",
|
||||
"/api-reference/boards/boardRouter-changeBoardVisibility",
|
||||
"/api-reference/boards/boardRouter-createBoard",
|
||||
"/api-reference/boards/boardRouter-deleteBoard",
|
||||
"/api-reference/boards/boardRouter-duplicateBoard",
|
||||
"/api-reference/boards/boardRouter-getAllBoards",
|
||||
"/api-reference/boards/boardRouter-renameBoard",
|
||||
"/api-reference/boards/boardRouter-savePartialBoardSettings",
|
||||
"/api-reference/boards/boardRouter-setHomeBoard",
|
||||
"/api-reference/boards/boardRouter-setMobileHomeBoard",
|
||||
"/api-reference/boards/boardRouter-updateItemLayout",
|
||||
"/api-reference/info/infoRouter-getInfo",
|
||||
"/api-reference/integrations/integration-request",
|
||||
"/api-reference/invites/inviteRouter-createInvite",
|
||||
"/api-reference/invites/inviteRouter-deleteInvite",
|
||||
"/api-reference/invites/inviteRouter-getAll",
|
||||
"/api-reference/settings/serverSettingsRouter-getBoardSettings",
|
||||
"/api-reference/settings/serverSettingsRouter-updateBoardSettings",
|
||||
"/api-reference/users/userRouter-changeColorScheme",
|
||||
"/api-reference/users/userRouter-changeDdgBangs",
|
||||
"/api-reference/users/userRouter-changeDefaultSearchEngine",
|
||||
"/api-reference/users/userRouter-changeEnableRightClickOnWidgets",
|
||||
"/api-reference/users/userRouter-changeFirstDayOfWeek",
|
||||
"/api-reference/users/userRouter-changeHeaderPreferences",
|
||||
"/api-reference/users/userRouter-changeHomeBoards",
|
||||
"/api-reference/users/userRouter-changePassword",
|
||||
"/api-reference/users/userRouter-changeSearchPreferences",
|
||||
"/api-reference/users/userRouter-create",
|
||||
"/api-reference/users/userRouter-delete",
|
||||
"/api-reference/users/userRouter-editProfile",
|
||||
"/api-reference/users/userRouter-getAll",
|
||||
"/api-reference/users/userRouter-getById",
|
||||
"/api-reference/users/userRouter-search",
|
||||
"/api-reference/users/userRouter-selectable",
|
||||
"/api-reference/users/userRouter-setProfileImage",
|
||||
"/blog/2023/01/11/version0.11",
|
||||
"/blog/2023/04/16/version0.12-more-widgets",
|
||||
"/blog/2023/11/10/authentication",
|
||||
"/blog/2023/12/22/updated-documentation",
|
||||
"/blog/2024/09/23/version-1.0",
|
||||
"/blog/2024/12/17/open-beta-1.0",
|
||||
"/blog/2025/01/19/migration-guide-1.0",
|
||||
"/blog/2025/08/02/using-argus",
|
||||
"/blog/2026/09/03/homarr-2.0",
|
||||
"/blog/documentation-migration",
|
||||
"/blog/translations",
|
||||
"/docs",
|
||||
"/docs/advanced",
|
||||
"/docs/advanced/command-line",
|
||||
"/docs/advanced/command-line/development",
|
||||
"/docs/advanced/command-line/fix-usernames",
|
||||
"/docs/advanced/command-line/integrations",
|
||||
"/docs/advanced/command-line/password-recovery",
|
||||
"/docs/advanced/command-line/users",
|
||||
"/docs/advanced/development/getting-started",
|
||||
"/docs/advanced/development/kubernetes",
|
||||
"/docs/advanced/development/widgets-and-integrations",
|
||||
"/docs/advanced/development/workshop",
|
||||
"/docs/advanced/development/workshop-operator",
|
||||
"/docs/advanced/environment-variables",
|
||||
"/docs/advanced/icons",
|
||||
"/docs/advanced/keyboard-shortcuts",
|
||||
"/docs/advanced/mysql-to-sqlite",
|
||||
"/docs/advanced/proxy",
|
||||
"/docs/advanced/running-as-different-user",
|
||||
"/docs/advanced/single-sign-on",
|
||||
"/docs/advanced/styling",
|
||||
"/docs/community",
|
||||
"/docs/community/donate",
|
||||
"/docs/community/faq",
|
||||
"/docs/community/get-in-touch",
|
||||
"/docs/community/license",
|
||||
"/docs/community/translations",
|
||||
"/docs/getting-started",
|
||||
"/docs/getting-started/after-the-installation",
|
||||
"/docs/getting-started/glossary",
|
||||
"/docs/getting-started/installation",
|
||||
"/docs/getting-started/installation/docker",
|
||||
"/docs/getting-started/installation/easy-panel",
|
||||
"/docs/getting-started/installation/helm",
|
||||
"/docs/getting-started/installation/home-assistant",
|
||||
"/docs/getting-started/installation/hostinger",
|
||||
"/docs/getting-started/installation/pika-pods",
|
||||
"/docs/getting-started/installation/portainer",
|
||||
"/docs/getting-started/installation/proxmox",
|
||||
"/docs/getting-started/installation/qnap",
|
||||
"/docs/getting-started/installation/railway",
|
||||
"/docs/getting-started/installation/runtipi",
|
||||
"/docs/getting-started/installation/saltbox",
|
||||
"/docs/getting-started/installation/source",
|
||||
"/docs/getting-started/installation/synology",
|
||||
"/docs/getting-started/installation/unraid",
|
||||
"/docs/integrations",
|
||||
"/docs/integrations/adguard-home",
|
||||
"/docs/integrations/anchor",
|
||||
"/docs/integrations/archiveteam-warrior",
|
||||
"/docs/integrations/aria2",
|
||||
"/docs/integrations/audiobookshelf",
|
||||
"/docs/integrations/autobrr",
|
||||
"/docs/integrations/bazarr",
|
||||
"/docs/integrations/beszel",
|
||||
"/docs/integrations/bindery",
|
||||
"/docs/integrations/caddy",
|
||||
"/docs/integrations/changedetection",
|
||||
"/docs/integrations/coolify",
|
||||
"/docs/integrations/dash-dot",
|
||||
"/docs/integrations/deluge",
|
||||
"/docs/integrations/docker",
|
||||
"/docs/integrations/docker-labels",
|
||||
"/docs/integrations/emby",
|
||||
"/docs/integrations/fileflows",
|
||||
"/docs/integrations/frigate",
|
||||
"/docs/integrations/gatus",
|
||||
"/docs/integrations/glances",
|
||||
"/docs/integrations/gluetun",
|
||||
"/docs/integrations/gotify",
|
||||
"/docs/integrations/healthchecks",
|
||||
"/docs/integrations/home-assistant",
|
||||
"/docs/integrations/homebox",
|
||||
"/docs/integrations/ical",
|
||||
"/docs/integrations/immich",
|
||||
"/docs/integrations/jackett",
|
||||
"/docs/integrations/jellyfin",
|
||||
"/docs/integrations/jellyseerr",
|
||||
"/docs/integrations/jellystat",
|
||||
"/docs/integrations/karakeep",
|
||||
"/docs/integrations/komga",
|
||||
"/docs/integrations/kubernetes",
|
||||
"/docs/integrations/lidarr",
|
||||
"/docs/integrations/linkwarden",
|
||||
"/docs/integrations/llama-cpp",
|
||||
"/docs/integrations/maintainerr",
|
||||
"/docs/integrations/mealie",
|
||||
"/docs/integrations/miniflux",
|
||||
"/docs/integrations/navidrome",
|
||||
"/docs/integrations/netalertx",
|
||||
"/docs/integrations/netdata",
|
||||
"/docs/integrations/nextcloud",
|
||||
"/docs/integrations/ntfy",
|
||||
"/docs/integrations/nzbget",
|
||||
"/docs/integrations/open-media-vault",
|
||||
"/docs/integrations/opnsense",
|
||||
"/docs/integrations/overseerr",
|
||||
"/docs/integrations/paperless-ngx",
|
||||
"/docs/integrations/patchmon",
|
||||
"/docs/integrations/peanut",
|
||||
"/docs/integrations/pi-hole",
|
||||
"/docs/integrations/plantit",
|
||||
"/docs/integrations/plex",
|
||||
"/docs/integrations/prometheus",
|
||||
"/docs/integrations/prowlarr",
|
||||
"/docs/integrations/proxmox",
|
||||
"/docs/integrations/q-bittorent",
|
||||
"/docs/integrations/radarr",
|
||||
"/docs/integrations/readarr",
|
||||
"/docs/integrations/romm",
|
||||
"/docs/integrations/sabnzbd",
|
||||
"/docs/integrations/scrutiny",
|
||||
"/docs/integrations/seerr",
|
||||
"/docs/integrations/slskd",
|
||||
"/docs/integrations/sonarr",
|
||||
"/docs/integrations/speedtest-tracker",
|
||||
"/docs/integrations/spoolman",
|
||||
"/docs/integrations/stash",
|
||||
"/docs/integrations/syncthing-relay",
|
||||
"/docs/integrations/synology",
|
||||
"/docs/integrations/tandoor",
|
||||
"/docs/integrations/tdarr",
|
||||
"/docs/integrations/technitium-dns",
|
||||
"/docs/integrations/tracearr",
|
||||
"/docs/integrations/traefik",
|
||||
"/docs/integrations/transmission",
|
||||
"/docs/integrations/trilium",
|
||||
"/docs/integrations/truenas",
|
||||
"/docs/integrations/tubearchivist",
|
||||
"/docs/integrations/umami",
|
||||
"/docs/integrations/unifi-controller",
|
||||
"/docs/integrations/unmanic",
|
||||
"/docs/integrations/unraid",
|
||||
"/docs/integrations/uptime-kuma",
|
||||
"/docs/integrations/wazuh",
|
||||
"/docs/integrations/whats-up-docker",
|
||||
"/docs/integrations/xteve",
|
||||
"/docs/integrations/your-spotify",
|
||||
"/docs/management",
|
||||
"/docs/management/api",
|
||||
"/docs/management/apps",
|
||||
"/docs/management/assistant",
|
||||
"/docs/management/backup",
|
||||
"/docs/management/boards",
|
||||
"/docs/management/certificates",
|
||||
"/docs/management/custom-widgets",
|
||||
"/docs/management/custom-widgets/agent-authoring",
|
||||
"/docs/management/custom-widgets/authoring",
|
||||
"/docs/management/custom-widgets/component-reference",
|
||||
"/docs/management/custom-widgets/custom-jsx",
|
||||
"/docs/management/custom-widgets/requests-and-security",
|
||||
"/docs/management/custom-widgets/troubleshooting",
|
||||
"/docs/management/integrations",
|
||||
"/docs/management/logs",
|
||||
"/docs/management/mcp",
|
||||
"/docs/management/media",
|
||||
"/docs/management/search-engines",
|
||||
"/docs/management/settings",
|
||||
"/docs/management/tasks",
|
||||
"/docs/management/users",
|
||||
"/docs/widgets",
|
||||
"/docs/widgets/air-quality",
|
||||
"/docs/widgets/anchor-note",
|
||||
"/docs/widgets/app",
|
||||
"/docs/widgets/archive-team-warrior",
|
||||
"/docs/widgets/assistant",
|
||||
"/docs/widgets/audio-stats",
|
||||
"/docs/widgets/bazarr",
|
||||
"/docs/widgets/beszel-alerts",
|
||||
"/docs/widgets/beszel-system-grid",
|
||||
"/docs/widgets/beszel-system-stats",
|
||||
"/docs/widgets/beszel-system-table",
|
||||
"/docs/widgets/bookmarks",
|
||||
"/docs/widgets/calendar",
|
||||
"/docs/widgets/clock",
|
||||
"/docs/widgets/coolify",
|
||||
"/docs/widgets/countdown",
|
||||
"/docs/widgets/custom-api",
|
||||
"/docs/widgets/dns-hole-controls",
|
||||
"/docs/widgets/dns-hole-summary",
|
||||
"/docs/widgets/docker-containers",
|
||||
"/docs/widgets/downloads",
|
||||
"/docs/widgets/firewall",
|
||||
"/docs/widgets/health-monitoring",
|
||||
"/docs/widgets/iframe",
|
||||
"/docs/widgets/immich-album-carousel",
|
||||
"/docs/widgets/immich-server-stats",
|
||||
"/docs/widgets/indexer-manager",
|
||||
"/docs/widgets/llama-cpp",
|
||||
"/docs/widgets/media-missing",
|
||||
"/docs/widgets/media-releases",
|
||||
"/docs/widgets/media-request-list",
|
||||
"/docs/widgets/media-request-stats",
|
||||
"/docs/widgets/media-server",
|
||||
"/docs/widgets/media-transcoding",
|
||||
"/docs/widgets/minecraft-server-status",
|
||||
"/docs/widgets/network-controller-status",
|
||||
"/docs/widgets/network-controller-summary",
|
||||
"/docs/widgets/notebook",
|
||||
"/docs/widgets/notifications",
|
||||
"/docs/widgets/paperless-ngx",
|
||||
"/docs/widgets/patchmon",
|
||||
"/docs/widgets/releases",
|
||||
"/docs/widgets/rss-feed",
|
||||
"/docs/widgets/smart-home-entity-state",
|
||||
"/docs/widgets/smart-home-execute-automation",
|
||||
"/docs/widgets/speedtest-tracker",
|
||||
"/docs/widgets/stats",
|
||||
"/docs/widgets/stock-price",
|
||||
"/docs/widgets/system-disks",
|
||||
"/docs/widgets/system-resources",
|
||||
"/docs/widgets/timer",
|
||||
"/docs/widgets/timetable",
|
||||
"/docs/widgets/tracearr",
|
||||
"/docs/widgets/traefik",
|
||||
"/docs/widgets/umami",
|
||||
"/docs/widgets/ups",
|
||||
"/docs/widgets/uptime-kuma",
|
||||
"/docs/widgets/video",
|
||||
"/docs/widgets/vpn",
|
||||
"/docs/widgets/weather",
|
||||
"/docs/widgets/whats-up-docker",
|
||||
"/docs/workshop",
|
||||
"/docs/workshop/homarr-provider",
|
||||
"/workshop"
|
||||
]
|
||||
+10
-10
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"worktree": {
|
||||
"setup": "mise trust\nmise install\npnpm install\n\nif [ -n \"${PASEO_SOURCE_CHECKOUT_PATH:-}\" ] && [ -f \"$PASEO_SOURCE_CHECKOUT_PATH/.env\" ]; then\n cp \"$PASEO_SOURCE_CHECKOUT_PATH/.env\" .env\nelse\n cp .env.example .env\nfi\n\nDB_PATH=\"$PWD/homarr.sqlite\"\nZERO_KEY=\"0000000000000000000000000000000000000000000000000000000000000000\"\nsed -i.bak \\\n -e \"s|^DB_DRIVER=.*|DB_DRIVER='better-sqlite3'|\" \\\n -e \"s|^DB_URL=.*|DB_URL='$DB_PATH'|\" \\\n -e \"s|^SECRET_ENCRYPTION_KEY=.*|SECRET_ENCRYPTION_KEY=$ZERO_KEY|\" \\\n .env\nrm -f .env.bak\n\nrm -f homarr.sqlite\npnpm db:migration:sqlite:run",
|
||||
"setup": "mise trust\nmise install\nbun install\n\nif [ -n \"${PASEO_SOURCE_CHECKOUT_PATH:-}\" ] && [ -f \"$PASEO_SOURCE_CHECKOUT_PATH/.env\" ]; then\n cp \"$PASEO_SOURCE_CHECKOUT_PATH/.env\" .env\nelse\n cp .env.example .env\nfi\n\nDB_PATH=\"$PWD/homarr.sqlite\"\nZERO_KEY=\"0000000000000000000000000000000000000000000000000000000000000000\"\nsed -i.bak \\\n -e \"s|^DB_DRIVER=.*|DB_DRIVER='better-sqlite3'|\" \\\n -e \"s|^DB_URL=.*|DB_URL='$DB_PATH'|\" \\\n -e \"s|^SECRET_ENCRYPTION_KEY=.*|SECRET_ENCRYPTION_KEY=$ZERO_KEY|\" \\\n .env\nrm -f .env.bak\n\nrm -f homarr.sqlite\nbun run db:migration:sqlite:run",
|
||||
"teardown": "rm -f homarr.sqlite",
|
||||
"servicePorts": {
|
||||
"range": "3000-4000"
|
||||
@@ -9,18 +9,18 @@
|
||||
"scripts": {
|
||||
"dev": {
|
||||
"type": "service",
|
||||
"command": "PORT=$PASEO_PORT pnpm -F nextjs dev"
|
||||
"command": "PORT=$PASEO_PORT bun run --filter @homarr/nextjs dev"
|
||||
},
|
||||
"docs": {
|
||||
"type": "service",
|
||||
"command": "pnpm -F @homarr/docs dev --port $PASEO_PORT"
|
||||
"command": "bun run --filter @homarr/docs dev --port $PASEO_PORT"
|
||||
},
|
||||
"build": { "command": "pnpm build" },
|
||||
"test": { "command": "pnpm test" },
|
||||
"test:e2e": { "command": "pnpm test:e2e" },
|
||||
"lint": { "command": "pnpm lint" },
|
||||
"typecheck": { "command": "pnpm typecheck" },
|
||||
"db:seed": { "command": "pnpm db:seed" },
|
||||
"db:studio": { "command": "pnpm db:studio" }
|
||||
"build": { "command": "bun run build" },
|
||||
"test": { "command": "bun run test" },
|
||||
"test:e2e": { "command": "bun run test:e2e" },
|
||||
"lint": { "command": "bun run lint" },
|
||||
"typecheck": { "command": "bun run typecheck" },
|
||||
"db:seed": { "command": "bun run db:seed" },
|
||||
"db:studio": { "command": "bun run db:studio" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
diff --git a/dist/esm/package.json b/dist/esm/package.json
|
||||
new file mode 100644
|
||||
index 0000000..3dbc1ca
|
||||
--- /dev/null
|
||||
+++ b/dist/esm/package.json
|
||||
@@ -0,0 +1,3 @@
|
||||
+{
|
||||
+ "type": "module"
|
||||
+}
|
||||
@@ -1,13 +1,11 @@
|
||||
diff --git a/dist/esm/utils/zod.mjs b/dist/esm/utils/zod.mjs
|
||||
index a3fda1a5b5403a659bfc70653a94102e607f8b80..070d9728f9b41add50e5d30d3aa559be91018ccd 100644
|
||||
--- a/dist/esm/utils/zod.mjs
|
||||
+++ b/dist/esm/utils/zod.mjs
|
||||
@@ -62,7 +62,7 @@ export const instanceofZodTypeLikeString = (_type) => {
|
||||
@@ -70,7 +70,7 @@
|
||||
}
|
||||
return instanceofZodTypeKind(type, 'string');
|
||||
};
|
||||
-export const zodSupportsCoerce = 'coerce' in z;
|
||||
+export const zodSupportsCoerce = true //'coerce' in z;
|
||||
+export const zodSupportsCoerce = true;
|
||||
export const instanceofZodTypeCoercible = (_type) => {
|
||||
const type = unwrapZodType(_type, false);
|
||||
return (instanceofZodTypeKind(type, 'number') ||
|
||||
Generated
-26484
File diff suppressed because it is too large.
Load diff
@@ -1,331 +0,0 @@
|
||||
packages:
|
||||
- apps/*
|
||||
- packages/*
|
||||
- tooling/*
|
||||
catalog:
|
||||
"@ai-sdk/react": ^4.0.59
|
||||
"@babel/parser": ^7.29.7
|
||||
"@codemirror/autocomplete": ^6.20.3
|
||||
"@codemirror/commands": ^6.10.3
|
||||
"@codemirror/lang-css": ^6.3.1
|
||||
"@codemirror/lang-javascript": ^6.2.5
|
||||
"@codemirror/lang-json": ^6.0.2
|
||||
"@codemirror/lint": ^6.9.6
|
||||
"@codemirror/state": ^6.6.0
|
||||
"@codemirror/theme-one-dark": ^6.1.3
|
||||
"@codemirror/view": ^6.43.0
|
||||
"@auth/core": ^0.41.3
|
||||
"@auth/drizzle-adapter": ^1.11.3
|
||||
"@ctrl/deluge": ^8.0.0
|
||||
"@ctrl/qbittorrent": ^9.14.0
|
||||
"@ctrl/transmission": ^7.4.0
|
||||
"@dnd-kit/abstract": 0.5.0
|
||||
"@dnd-kit/collision": 0.5.0
|
||||
"@dnd-kit/core": ^6.3.1
|
||||
"@dnd-kit/dom": 0.5.0
|
||||
"@dnd-kit/modifiers": ^9.0.0
|
||||
"@dnd-kit/react": 0.5.0
|
||||
"@dnd-kit/sortable": ^10.0.0
|
||||
"@dnd-kit/utilities": ^3.2.2
|
||||
"@drizzle-team/brocli": ^0.12.0
|
||||
"@extractus/feed-extractor": 7.2.1
|
||||
"@gfazioli/mantine-onboarding-tour": ^4.0.7
|
||||
"@gitbeaker/rest": ^43.8.0
|
||||
"@homarr/node-unifi": ^2.6.0
|
||||
"@immich/sdk": ^3.0.2
|
||||
"@jellyfin/sdk": ^0.13.0
|
||||
"@kubernetes/client-node": ^1.4.0
|
||||
"@modelcontextprotocol/server": 2.0.0
|
||||
"@mantine/charts": ^9.6.0
|
||||
"@mantine/colors-generator": ^9.6.0
|
||||
"@mantine/core": ^9.6.0
|
||||
"@mantine/dates": ^9.6.0
|
||||
"@mantine/dropzone": ^9.6.0
|
||||
"@mantine/form": ^9.6.0
|
||||
"@mantine/hooks": ^9.6.0
|
||||
"@mantine/lightbox": ^9.6.0
|
||||
"@mantine/modals": ^9.6.0
|
||||
"@mantine/notifications": ^9.6.0
|
||||
"@mantine/spotlight": ^9.6.0
|
||||
"@mantine/tiptap": ^9.6.0
|
||||
"@octokit/auth-app": ^8.2.0
|
||||
"@paralleldrive/cuid2": ^3.3.0
|
||||
"@playwright/test": ^1.61.1
|
||||
pocketbase: ^0.26.2
|
||||
"@semantic-release/changelog": ^7.0.0
|
||||
"@semantic-release/commit-analyzer": ^13.0.1
|
||||
"@semantic-release/git": ^11.0.1
|
||||
"@semantic-release/github": ^12.0.9
|
||||
"@semantic-release/npm": ^13.1.5
|
||||
"@semantic-release/release-notes-generator": ^14.1.1
|
||||
"@t3-oss/env-nextjs": ^0.13.11
|
||||
"@tabler/icons-react": ^3.44.0
|
||||
"@tanstack/react-devtools": ^0.10.8
|
||||
"@tanstack/react-query": ^5.101.2
|
||||
"@tanstack/react-query-devtools": ^5.101.2
|
||||
"@tanstack/react-query-next-experimental": ^5.101.2
|
||||
"@testcontainers/postgresql": ^12.0.4
|
||||
"@testcontainers/redis": ^12.0.4
|
||||
"@tiptap/extension-color": 3.30.5
|
||||
"@tiptap/extension-details": 3.30.5
|
||||
"@tiptap/extension-highlight": 3.30.5
|
||||
"@tiptap/extension-image": 3.30.5
|
||||
"@tiptap/extension-link": ^3.30.5
|
||||
"@tiptap/extension-placeholder": ^3.30.5
|
||||
"@tiptap/extension-table": 3.30.5
|
||||
"@tiptap/extension-table-cell": 3.30.5
|
||||
"@tiptap/extension-table-header": 3.30.5
|
||||
"@tiptap/extension-table-row": 3.30.5
|
||||
"@tiptap/extension-task-item": 3.30.5
|
||||
"@tiptap/extension-task-list": 3.30.5
|
||||
"@tiptap/extension-text-align": 3.30.5
|
||||
"@tiptap/extension-text-style": 3.30.5
|
||||
"@tiptap/extension-underline": 3.30.5
|
||||
"@tiptap/react": ^3.30.5
|
||||
"@tiptap/starter-kit": ^3.30.5
|
||||
"@trpc/client": ^11.18.0
|
||||
"@trpc/next": ^11.18.0
|
||||
"@trpc/react-query": ^11.18.0
|
||||
"@trpc/server": ^11.18.0
|
||||
"@trpc/tanstack-react-query": ^11.18.0
|
||||
"@turbo/gen": ^2.10.4
|
||||
"@types/adm-zip": 0.5.8
|
||||
"@types/bcrypt": 6.0.0
|
||||
"@types/better-sqlite3": 7.6.13
|
||||
"@types/chroma-js": 3.1.2
|
||||
"@types/cookies": 0.9.2
|
||||
"@types/crypto-js": ^4.2.2
|
||||
"@types/css-modules": ^1.0.5
|
||||
"@types/dockerode": ^4.0.1
|
||||
"@types/node": ^24.13.3
|
||||
"@types/node-unifi": ^2.5.1
|
||||
"@types/pg": ^8.20.0
|
||||
"@types/prismjs": ^1.26.6
|
||||
"@types/react": 19.2.17
|
||||
"@types/react-dom": 19.2.3
|
||||
"@scalar/api-reference-react": ^0.9.74
|
||||
"@scalar/api-client-react": 2.0.51
|
||||
"@shadcn/react": ^0.2.1
|
||||
"@types/video.js": ^7.3.58
|
||||
"@types/ws": ^8.18.1
|
||||
"@uiw/react-codemirror": ^4.25.11
|
||||
"@types/xml2js": ^0.4.14
|
||||
posthog-js: ^1.399.1
|
||||
posthog-node: ^5.40.0
|
||||
"@vitejs/plugin-react": ^6.0.3
|
||||
"@vitest/coverage-v8": ^4.1.10
|
||||
"@vitest/ui": ^4.1.10
|
||||
"@xterm/addon-canvas": ^0.7.0
|
||||
"@xterm/addon-fit": 0.11.0
|
||||
"@xterm/xterm": ^6.0.0
|
||||
adm-zip: 0.6.1
|
||||
acorn: ^8.16.0
|
||||
acorn-jsx: ^5.3.2
|
||||
ajv: 8.18.0
|
||||
jszip: ^3.10.1
|
||||
sql.js: ^1.14.1
|
||||
babel-plugin-react-compiler: ^1.0.0
|
||||
bcrypt: ^6.0.0
|
||||
better-sqlite3: ^12.11.1
|
||||
chroma-js: ^3.2.0
|
||||
clsx: ^2.1.1
|
||||
codemirror: ^6.0.2
|
||||
concurrently: ^10.0.3
|
||||
conventional-changelog-conventionalcommits: ^10.2.0
|
||||
cookie: 2.0.1
|
||||
cookies: ^0.9.1
|
||||
croner: 10.0.1
|
||||
cross-env: ^10.1.0
|
||||
crypto-js: ^4.2.0
|
||||
cmdk: ^1.1.1
|
||||
dayjs: ^1.11.21
|
||||
date-fns: ^4.4.0
|
||||
deepmerge: 4.3.1
|
||||
dns-caching: ^0.2.9
|
||||
dockerode: ^5.0.1
|
||||
dotenv: ^17.4.2
|
||||
dotenv-cli: ^11.0.0
|
||||
drizzle-kit: ^0.31.10
|
||||
drizzle-orm: ^0.45.2
|
||||
drizzle-zod: ^0.8.3
|
||||
embla-carousel-react: ^8.6.0
|
||||
esbuild: ^0.28.1
|
||||
fast-xml-parser: ^5.10.1
|
||||
fastify: ^5.12.1
|
||||
feed: ^6.0.0
|
||||
flag-icons: ^7.5.0
|
||||
fumadocs-core: 16.15.12
|
||||
fumadocs-mdx: 15.4.3
|
||||
fumadocs-openapi: 12.0.1
|
||||
fumadocs-ui: 16.15.12
|
||||
glob: ^13.0.6
|
||||
ical.js: ^2.2.1
|
||||
input-otp: ^1.4.2
|
||||
ioredis: 5.11.1
|
||||
isomorphic-dompurify: ^3.18.0
|
||||
jotai: ^2.20.1
|
||||
jsdom: ^29.1.1
|
||||
json5: ^2.2.3
|
||||
json-schema-typed: 8.0.2
|
||||
jsonpath-plus: ^10.4.0
|
||||
ldapts: 8.1.8
|
||||
lodash.clonedeep: ^4.5.0
|
||||
lucide-react: ^1.24.0
|
||||
"mantine-contextmenu": ^9.3.1
|
||||
mantine-datatable: ^9.3.1
|
||||
mantine-react-table: npm:mantine-react-table-open@9.0.3
|
||||
mermaid: ^11.16.1
|
||||
motion: ^13.0.0
|
||||
maria2: ^0.4.1
|
||||
next: 16.3.6
|
||||
next-auth: 5.0.0-beta.32
|
||||
next-intl: 4.13.1
|
||||
next-themes: ^0.4.6
|
||||
node-loader: ^2.1.0
|
||||
octokit: ^5.0.5
|
||||
oxfmt: ^0.58.0
|
||||
oxlint: ^1.73.0
|
||||
pg: ^8.22.0
|
||||
postcss-preset-mantine: ^1.18.0
|
||||
prismjs: ^1.30.0
|
||||
proxmox-api: 1.1.1
|
||||
react: 19.2.7
|
||||
react-day-picker: ^10.0.1
|
||||
react-dom: 19.2.7
|
||||
react-error-boundary: ^6.1.2
|
||||
rehype-raw: ^7.0.0
|
||||
rehype-sanitize: ^6.0.0
|
||||
react-markdown: ^10.1.0
|
||||
react-quill-new: ^3.8.3
|
||||
react-simple-code-editor: ^0.14.1
|
||||
react-resizable-panels: ^4.12.2
|
||||
recharts: ^3.8.0
|
||||
sass: ^1.101.0
|
||||
semantic-release: ^25.0.5
|
||||
semver-parser: ^4.2.1
|
||||
serve: ^14.2.6
|
||||
shiki: ^4.4.3
|
||||
sonner: ^2.0.7
|
||||
superjson: 2.2.6
|
||||
svgson: ^5.3.1
|
||||
testcontainers: ^12.0.4
|
||||
timezones-ical-library: ^2.2.1
|
||||
trpc-to-openapi: ^3.3.0
|
||||
tsdav: ^2.3.0
|
||||
tsx: 4.20.4
|
||||
turbo: ^2.10.4
|
||||
typescript: 7.0.2
|
||||
vite: ^8.1.4
|
||||
undici: 7.29.1
|
||||
use-deep-compare-effect: ^1.8.1
|
||||
video.js: ^8.23.9
|
||||
vite-tsconfig-paths: ^6.1.1
|
||||
vitest: ^4.1.10
|
||||
winston: 3.19.0
|
||||
ws: ^8.21.0
|
||||
xml2js: ^0.6.2
|
||||
yaml: 2.9.0
|
||||
zod: ^4.4.3
|
||||
zod-form-data: ^3.0.2
|
||||
zod-validation-error: ^5.0.0
|
||||
"@assistant-ui/react": 0.15.8
|
||||
"@assistant-ui/react-ai-sdk": 1.4.4
|
||||
"@assistant-ui/react-lexical": 0.2.7
|
||||
"@assistant-ui/react-markdown": 0.14.9
|
||||
ai: ^7.0.56
|
||||
assistant-stream: 0.3.34
|
||||
"@ai-sdk/openai-compatible": 3.0.25
|
||||
remark-breaks: ^4.0.0
|
||||
remark-directive: ^3.0.1
|
||||
remark-gfm: ^4.0.1
|
||||
react-shiki: ^0.11.0
|
||||
canvas-confetti: ^1.9.4
|
||||
"@types/canvas-confetti": ^1.9.0
|
||||
"@types/mdx": ^2.0.14
|
||||
"@rexa-developer/tiks": ^0.3.0
|
||||
"@tanstack/query-async-storage-persister": 5.101.2
|
||||
"@tanstack/react-query-persist-client": 5.101.2
|
||||
three: 0.180.0
|
||||
"@types/three": 0.180.0
|
||||
|
||||
catalogMode: strict
|
||||
nodeLinker: hoisted
|
||||
strictPeerDependencies: false
|
||||
engineStrict: true
|
||||
verifyDepsBeforeRun: warn
|
||||
overrides:
|
||||
"@assistant-ui/react-ai-sdk>@ai-sdk/mcp": 2.0.27
|
||||
"@assistant-ui/react-ai-sdk>@ai-sdk/react": 4.0.59
|
||||
"@assistant-ui/react-ai-sdk>ai": 7.0.56
|
||||
"@assistant-ui/react-ai-sdk>assistant-stream": 0.3.34
|
||||
"@assistant-ui/core": 0.3.7
|
||||
"@assistant-ui/store": 0.3.5
|
||||
"@assistant-ui/tap": 0.9.10
|
||||
"@grpc/grpc-js@<1.12.7": "1.13.6"
|
||||
"@rvf/set-get@<7.0.2": "7.0.2"
|
||||
"@xmldom/xmldom@<0.8.15": "0.8.15"
|
||||
"assistant-cloud": 0.1.38
|
||||
"@babel/helpers@<7.26.10": ">=7.29.7"
|
||||
"@babel/runtime@<7.26.10": ">=7.29.7"
|
||||
"axios@>=1.0.0 <1.18.1": "1.20.0"
|
||||
"brace-expansion@>=1.0.0 <1.1.18": "1.1.21"
|
||||
"brace-expansion@>=2.0.0 <2.1.4": "2.1.7"
|
||||
"brace-expansion@>=4.0.0 <5.0.9": "5.0.12"
|
||||
"browserslist@<4.28.7": "4.28.7"
|
||||
"esbuild@<=0.24.2": ">=0.28.1"
|
||||
"fast-uri@>=3.0.0 <3.1.6": "3.1.8"
|
||||
"@ai-sdk/provider-utils>undici": "8.10.2"
|
||||
"find-my-way@<9.6.1": "9.9.0"
|
||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.6"
|
||||
"hono@<4.6.5": ">=4.13.7"
|
||||
"immutable@>=5.0.0-beta.1 <5.1.8": "5.1.8"
|
||||
"ip-address@<10.3.1": "10.7.1"
|
||||
"image-size@<=2.0.2": "2.0.3"
|
||||
"js-yaml@>=3.0.0 <3.15.2": "3.15.2"
|
||||
"js-yaml@>=4.0.0 <4.3.2": "4.3.2"
|
||||
"jsdom>undici": "8.10.2"
|
||||
"linkifyjs@<4.3.2": ">=4.3.3"
|
||||
"lodash-es@<4.17.24": "4.18.1"
|
||||
"nanoid@>=3.0.0 <3.3.18": "3.3.18"
|
||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.16"
|
||||
"picomatch@>=2.0.0 <2.3.2": "2.3.2"
|
||||
"postcss@<8.5.23": "8.5.23"
|
||||
"prismjs@<1.30.0": ">=1.30.0"
|
||||
"protobufjs@<7.5.5": ">=7.5.5 <8"
|
||||
"proxmox-api>undici": "7.29.1"
|
||||
"react-is": "^19.2.7"
|
||||
"rollup@>=4.0.0 <4.22.4": ">=4.62.2"
|
||||
"serialize-javascript@<7.0.5": "7.0.5"
|
||||
"sha.js@<=2.4.11": ">=2.4.12"
|
||||
"shell-quote@<1.8.5": "1.11.0"
|
||||
"svgo@>=3.0.0 <3.3.5": "3.3.5"
|
||||
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.3"
|
||||
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.3"
|
||||
"tmp@<=0.2.3": ">=0.2.7"
|
||||
"undici@>=7.0.0 <7.29.1": "7.29.1"
|
||||
"undici@>=8.0.0 <8.9.0": "8.9.0"
|
||||
"vite@>=5.0.0 <=5.4.18": ">=8.1.4"
|
||||
patchedDependencies:
|
||||
"@types/node-unifi": "patches/@types__node-unifi.patch"
|
||||
mantine-datatable@9.3.1: patches/mantine-datatable@9.3.1.patch
|
||||
"trpc-to-openapi": "patches/trpc-to-openapi.patch"
|
||||
|
||||
allowUnusedPatches: true
|
||||
allowBuilds:
|
||||
"@tree-sitter-grammars/tree-sitter-yaml": true
|
||||
bcrypt: true
|
||||
better-sqlite3: true
|
||||
cpu-features: false
|
||||
esbuild: true
|
||||
sharp: true
|
||||
ssh2: true
|
||||
tree-sitter: true
|
||||
tree-sitter-json: true
|
||||
"@scarf/scarf": false
|
||||
"@swc/core": false
|
||||
core-js: false
|
||||
core-js-pure: false
|
||||
msw: false
|
||||
protobufjs: false
|
||||
vue: 3.5.42
|
||||
vue-demi: false
|
||||
@@ -48,7 +48,8 @@ try {
|
||||
collect(join(nextRoot, "server/chunks/ssr"), (name) =>
|
||||
/^packages_widgets_src_custom-api_component.*\.js$/u.test(name),
|
||||
),
|
||||
collect(join(nextRoot, "standalone"), (name) => name === "react-loadable-manifest.json"),
|
||||
// Canonical route manifests survive compiler-cache restoration; standalone is assembled separately.
|
||||
collect(join(nextRoot, "server"), (name) => name === "react-loadable-manifest.json"),
|
||||
]);
|
||||
} catch {
|
||||
throw new Error("Next.js build output is missing. Run the Next.js production build before the bundle check.");
|
||||
|
||||
@@ -25,10 +25,7 @@ describe("Custom Widget bundle budget", () => {
|
||||
test("finds the dynamic module from its runtime chunk when server metadata is absent", async () => {
|
||||
const nextRoot = await mkdtemp(join(tmpdir(), "homarr-custom-widget-bundle-"));
|
||||
const serverChunk = join(nextRoot, "server/chunks/ssr/packages_widgets_src_custom-api_component_test.js");
|
||||
const manifestPath = join(
|
||||
nextRoot,
|
||||
"standalone/apps/nextjs/.next/server/app/[locale]/(home)/(board)/page/react-loadable-manifest.json",
|
||||
);
|
||||
const manifestPath = join(nextRoot, "server/app/[locale]/(home)/(board)/page/react-loadable-manifest.json");
|
||||
const runtimeChunk = join(nextRoot, "static/chunks/custom-widget-runtime.js");
|
||||
try {
|
||||
await Promise.all([
|
||||
|
||||
+20
-5
@@ -30,11 +30,11 @@ if [[ -z "${DOCKER_HOST:-}" ]]; then
|
||||
fi
|
||||
|
||||
head_sha=$(git rev-parse HEAD)
|
||||
base_sha=$(git rev-parse HEAD~1 2>/dev/null || echo "$head_sha")
|
||||
head_ref=$(git branch --show-current)
|
||||
head_ref=${head_ref:-act-local}
|
||||
event_file=$(mktemp "${TMPDIR:-/tmp}/homarr-act-event.XXXXXX")
|
||||
trap 'rm -f "$event_file"' EXIT
|
||||
environment_file=$(mktemp "${TMPDIR:-/tmp}/homarr-act-env.XXXXXX")
|
||||
trap 'rm -f "$event_file" "$environment_file"' EXIT
|
||||
|
||||
json_escape() { printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g'; }
|
||||
|
||||
@@ -46,7 +46,7 @@ if [[ "$mode" == "cleanup" ]]; then
|
||||
"repository": { "full_name": "homarr-labs/homarr" }
|
||||
}
|
||||
EOF
|
||||
elif [[ "$mode" == "docker" ]]; then
|
||||
elif [[ "$mode" == "docker" || "$mode" == "all" ]]; then
|
||||
event_name="workflow_dispatch"
|
||||
cat >"$event_file" <<EOF
|
||||
{
|
||||
@@ -55,6 +55,7 @@ elif [[ "$mode" == "docker" ]]; then
|
||||
}
|
||||
EOF
|
||||
else
|
||||
base_sha=$(git merge-base origin/dev HEAD)
|
||||
event_name="pull_request"
|
||||
safe_ref=$(json_escape "$head_ref")
|
||||
cat >"$event_file" <<EOF
|
||||
@@ -83,15 +84,29 @@ args=(
|
||||
--eventpath "$event_file"
|
||||
--platform "ubuntu-latest=$runner_image"
|
||||
--container-architecture linux/amd64
|
||||
--env-file /dev/null
|
||||
--bind
|
||||
--env "DB_DRIVER=better-sqlite3"
|
||||
--env "TURBO_CACHE_DIR=$PWD/.turbo/cache"
|
||||
# Jobs share the dependency volume; installs must not race.
|
||||
--concurrent-jobs 1
|
||||
)
|
||||
|
||||
# Ryuk mounts the host socket; rootless runners also need a reachable host address.
|
||||
if [[ "$DOCKER_HOST" == unix://* ]]; then
|
||||
args+=(--env "TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=${DOCKER_HOST#unix://}")
|
||||
fi
|
||||
if [[ -n "${ACT_TESTCONTAINERS_HOST:-}" ]]; then
|
||||
args+=(--env "TESTCONTAINERS_HOST_OVERRIDE=$ACT_TESTCONTAINERS_HOST")
|
||||
fi
|
||||
|
||||
if [[ -n "$job" ]]; then
|
||||
args+=(--job "$job")
|
||||
fi
|
||||
|
||||
git_common_dir=$(cd "$(git rev-parse --git-common-dir)" && pwd)
|
||||
container_options="--volume homarr-act-node-modules:$PWD/node_modules --volume homarr-act-pnpm-store:$PWD/.pnpm-store"
|
||||
# Bun loads .env automatically; reproduce GitHub's checkout without local settings.
|
||||
container_options="--volume $environment_file:$PWD/.env --volume homarr-act-bun-1-4-2-node-24-18-0-modules:$PWD/node_modules --volume homarr-act-bun-cache:/root/.bun/install/cache"
|
||||
case "$git_common_dir" in
|
||||
"$PWD" | "$PWD"/*) ;;
|
||||
*) container_options="$container_options --volume $git_common_dir:$git_common_dir:ro" ;;
|
||||
@@ -102,4 +117,4 @@ if [[ -n "${ACT_SECRET_FILE:-}" ]]; then
|
||||
args+=(--secret-file "$ACT_SECRET_FILE")
|
||||
fi
|
||||
|
||||
exec act "${args[@]}"
|
||||
act "${args[@]}"
|
||||
@@ -0,0 +1,14 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
// Homarr publishes images and archives; only the repository version needs updating.
|
||||
export async function prepare(_pluginConfig, { cwd, env, nextRelease, logger }) {
|
||||
const packagePath = join(cwd, "package.json");
|
||||
const manifest = JSON.parse(await readFile(packagePath, "utf8"));
|
||||
manifest.version = nextRelease.version;
|
||||
await writeFile(packagePath, `${JSON.stringify(manifest, null, 2)}\n`);
|
||||
await promisify(execFile)(process.execPath, ["install", "--lockfile-only", "--ignore-scripts"], { cwd, env });
|
||||
logger.log("Updated package.json and bun.lock to %s", nextRelease.version);
|
||||
}
|
||||
+4
-10
@@ -1,19 +1,13 @@
|
||||
import fs from "fs/promises";
|
||||
import { join } from "path";
|
||||
import json5 from "json5";
|
||||
import { parse } from "yaml";
|
||||
import { describe, test } from "vitest";
|
||||
|
||||
describe("Renovate configuration tests", () => {
|
||||
test("automerge should be disabled for allowBuilds dependencies", async () => {
|
||||
const workspaceYaml = await fs.readFile(join(__dirname, "../pnpm-workspace.yaml"), "utf-8").then(parse);
|
||||
test("automerge should be disabled for trustedDependencies dependencies", async () => {
|
||||
const manifest = await fs.readFile(join(__dirname, "../package.json"), "utf-8").then(JSON.parse);
|
||||
const renovateConfig = await fs.readFile(join(__dirname, "../.github/renovate.json5"), "utf-8").then(json5.parse);
|
||||
if (!workspaceYaml.allowBuilds || typeof workspaceYaml.allowBuilds !== "object") {
|
||||
throw new Error("pnpm-workspace.yaml must define allowBuilds as an object");
|
||||
}
|
||||
const allowedBuilds = Object.entries(workspaceYaml.allowBuilds)
|
||||
.filter(([, allowed]) => allowed === true)
|
||||
.map(([name]) => name);
|
||||
const allowedBuilds: string[] = manifest.trustedDependencies;
|
||||
const automergeDisabledDeps = renovateConfig.packageRules
|
||||
.filter((rule: any) => rule.automerge === false)
|
||||
.flatMap((rule: any) => rule.matchPackageNames || []);
|
||||
@@ -22,7 +16,7 @@ describe("Renovate configuration tests", () => {
|
||||
|
||||
if (missingDeps.length > 0) {
|
||||
throw new Error(
|
||||
`The following allowBuilds dependencies are missing automerge disable rules in renovate.json5: ${missingDeps.join(", ")}`,
|
||||
`The following trustedDependencies dependencies are missing automerge disable rules in renovate.json5: ${missingDeps.join(", ")}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
secret_args=()
|
||||
for secret in TURBO_API TURBO_TEAM TURBO_TOKEN TURBO_REMOTE_CACHE_SIGNATURE_KEY; do
|
||||
if [ -n "${!secret:-}" ]; then
|
||||
secret_args+=(--secret "id=${secret},env=${secret}")
|
||||
fi
|
||||
done
|
||||
|
||||
DOCKER_BUILDKIT=1 exec docker build ${secret_args[@]+"${secret_args[@]}"} "$@"
|
||||
@@ -1,14 +1,71 @@
|
||||
name: "Setup and install"
|
||||
description: "Common setup steps for Actions"
|
||||
|
||||
inputs:
|
||||
next-cache:
|
||||
description: Next.js compiler caches to restore (none, nextjs, docs, all)
|
||||
default: "none"
|
||||
turbo-cache:
|
||||
description: Restore local Turbo artifacts when signed remote caching is not configured
|
||||
default: "false"
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24.18.0
|
||||
cache: "pnpm"
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.4.2
|
||||
|
||||
- name: Identify the build platform
|
||||
shell: bash
|
||||
run: |
|
||||
node --input-type=module -e '
|
||||
import { appendFileSync } from "node:fs";
|
||||
const arch = { x64: "amd64", arm64: "arm64" }[process.arch] ?? process.arch;
|
||||
let libc = "musl";
|
||||
if (process.report.getReport().header.glibcVersionRuntime) libc = "glibc";
|
||||
appendFileSync(process.env.GITHUB_ENV, `TURBO_PLATFORM=${process.platform}/${arch}/${libc}/node-${process.versions.node}\n`);
|
||||
'
|
||||
|
||||
- name: Restore Bun dependency cache
|
||||
if: "!env.ACT"
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-1.4.2-${{ hashFiles('bun.lock', 'bunfig.toml', 'package.json', 'patches/**') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-1.4.2-
|
||||
|
||||
- shell: bash
|
||||
run: pnpm install --frozen-lockfile
|
||||
run: bun install --frozen-lockfile --concurrent-scripts=1
|
||||
|
||||
- name: Restore local Turbo artifacts
|
||||
if: inputs.turbo-cache == 'true' && !env.ACT && !(env.TURBO_API != '' && env.TURBO_TEAM != '' && env.TURBO_TOKEN != '' && env.TURBO_REMOTE_CACHE_SIGNATURE_KEY != '')
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: .turbo/cache
|
||||
key: ${{ env.TURBO_PLATFORM }}-turbo-${{ github.job }}-${{ hashFiles('bun.lock') }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ env.TURBO_PLATFORM }}-turbo-${{ github.job }}-${{ hashFiles('bun.lock') }}-
|
||||
|
||||
- name: Restore application compiler cache
|
||||
if: (inputs.next-cache == 'nextjs' || inputs.next-cache == 'all') && !env.ACT
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: apps/nextjs/.next/cache
|
||||
key: ${{ env.TURBO_PLATFORM }}-nextjs-${{ hashFiles('bun.lock') }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ env.TURBO_PLATFORM }}-nextjs-${{ hashFiles('bun.lock') }}-
|
||||
|
||||
- name: Restore documentation compiler cache
|
||||
if: (inputs.next-cache == 'docs' || inputs.next-cache == 'all') && !env.ACT
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: apps/docs/.next/cache
|
||||
key: ${{ env.TURBO_PLATFORM }}-docs-${{ hashFiles('bun.lock') }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ env.TURBO_PLATFORM }}-docs-${{ hashFiles('bun.lock') }}-
|
||||
@@ -3,7 +3,7 @@
|
||||
Start manually with the QA app's local date:
|
||||
|
||||
```sh
|
||||
BENCHMARK_DATE=2026-09-24 node tools/assistant-benchmark/server.mjs
|
||||
BENCHMARK_DATE=2026-09-24 bun tools/assistant-benchmark/server.mjs
|
||||
```
|
||||
|
||||
The server listens only on `127.0.0.1:18094`, makes no outbound requests, and
|
||||
|
||||
+12
-12
@@ -11,19 +11,19 @@ Build and launch local Homarr images and GHCR pull-request builds, then manage t
|
||||
## Setup
|
||||
|
||||
```sh
|
||||
pnpm install
|
||||
bun install
|
||||
```
|
||||
|
||||
Create `.env` from `.env.example` and set `DB_URL` to an absolute path for the SQLite file before running the migration. In a POSIX shell, use `cp .env.example .env`; in PowerShell, use `Copy-Item .env.example .env`.
|
||||
Then run `pnpm db:migration:sqlite:run` to create or update the database. The migration command also seeds a new database. Use `pnpm db:seed` explicitly when you need to seed an existing database. Start Redis separately with `pnpm docker:dev:up` when it is needed.
|
||||
The examples below use `pnpm dev:cli -- ...`, which works from POSIX shells and PowerShell. If you prefer a global command, install it with `pnpm dev:cli:install` and add your Go bin directory to `PATH`.
|
||||
Then run `bun run db:migration:sqlite:run` to create or update the database. The migration command also seeds a new database. Use `bun run db:seed` explicitly when you need to seed an existing database. Start Redis separately with `bun run docker:dev:up` when it is needed.
|
||||
The examples below use `bun run dev:cli -- ...`, which works from POSIX shells and PowerShell. If you prefer a global command, install it with `bun run dev:cli:install` and add your Go bin directory to `PATH`.
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
pnpm dev:cli -- build feature
|
||||
pnpm dev:cli -- build --pr 6441
|
||||
pnpm dev:cli -- rebuild feature
|
||||
bun run dev:cli -- build feature
|
||||
bun run dev:cli -- build --pr 6441
|
||||
bun run dev:cli -- rebuild feature
|
||||
```
|
||||
|
||||
Checkout builds are tagged as `homarr:<name>`. The source checkout and revision are recorded on the image so `homarr rebuild` and the development dashboard can rebuild it later. PR builds use a temporary checkout and default to `homarr:pr-<number>`.
|
||||
@@ -32,11 +32,11 @@ Checkout builds are tagged as `homarr:<name>`. The source checkout and revision
|
||||
|
||||
```sh
|
||||
homarr --pr 6441 --env WORKSHOP_WEB_URL=https://app-v2.preview.homarr.dev/
|
||||
pnpm dev:cli -- run dev
|
||||
pnpm dev:cli -- run --detach dev
|
||||
pnpm dev:cli -- run --pr 6441
|
||||
pnpm dev:cli -- run --pr 6441 --demo
|
||||
pnpm dev:cli -- run --env FOO=bar --env FEATURE=true dev
|
||||
bun run dev:cli -- run dev
|
||||
bun run dev:cli -- run --detach dev
|
||||
bun run dev:cli -- run --pr 6441
|
||||
bun run dev:cli -- run --pr 6441 --demo
|
||||
bun run dev:cli -- run --env FOO=bar --env FEATURE=true dev
|
||||
```
|
||||
|
||||
PR launches always pull the latest GHCR tag before starting. Local tags remain local.
|
||||
@@ -44,7 +44,7 @@ The installed binary accepts `--pr`, `--demo`, `--detach`, and repeatable `--env
|
||||
`homarr --pr <number> --env KEY=VALUE` is shorthand for `homarr run --pr <number> --env KEY=VALUE`.
|
||||
The first launch generates a per-user encryption key in the OS config directory and reuses it for later launches. Set `HOMARR_DEV_SECRET_ENCRYPTION_KEY` to a 64-character hexadecimal key to override it.
|
||||
|
||||
The command reference below assumes the optional `homarr` installation. Without it, prefix each command with `pnpm dev:cli --` (for example, `pnpm dev:cli -- dev`).
|
||||
The command reference below assumes the optional `homarr` installation. Without it, prefix each command with `bun run dev:cli --` (for example, `bun run dev:cli -- dev`).
|
||||
|
||||
## Commands
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ func (a *App) doctorCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath("homarr"); err != nil {
|
||||
optional("homarr is not on PATH", "use `pnpm dev:cli -- ...` or run `pnpm dev:cli:install`")
|
||||
optional("homarr is not on PATH", "use `bun run dev:cli -- ...` or run `bun run dev:cli:install`")
|
||||
} else {
|
||||
pass("homarr is on PATH", "")
|
||||
}
|
||||
|
||||
Loaded 100 of 109 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user