Compare commits

...
Author SHA1 Message Date
Thomas Camlong c54a40dcda fix(ci): remove persist-credentials: false to fix git-auto-commit-action push 2026-07-15 10:32:36 +02:00
homarr-renovate[bot] c473e80243 chore(deps): update pnpm to v11.11.0 (#6342)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-15 02:31:21 +00:00
homarr-renovate[bot] bf55bcce92 fix(deps): update dependency swiper to ^14.0.5 (#6341)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-14 23:55:02 +00:00
homarr-renovate[bot] 49248cedaf chore(deps): update dependency posthog-js to ^1.399.1 (#6340)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-14 22:09:52 +00:00
homarr-renovate[bot] a0034b2cfc chore(deps): update dependency @immich/sdk to ^3.0.2 (#6339)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-14 21:01:58 +00:00
homarr-renovate[bot] 4a297180fd chore(deps): update dependency lucide-react to ^1.24.0 (#6334)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-14 19:23:26 +00:00
Thomas 5fa3a3313a Merge pull request #6337 from homarr-labs/fix/ts7-nextjs-build
fix(docker): pin TypeScript to v6 to fix Next.js 16.2.10 build in CI
2026-07-14 18:11:38 +02:00
Thomas Camlong 29ee87c4aa fix(docker): pin typescript to v6 via alias to fix Next.js 16.2.10 build 2026-07-14 18:11:15 +02:00
Thomas 94a247e24a fix(docker): remove --offline flag from pnpm install step (#6335) 2026-07-14 17:19:51 +02:00
homarr-renovate[bot] 6d37710fec chore(deps): update dependency vite@>=5.0.0 <=5.4.18 to >=8.1.4 (#6333)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-14 11:26:55 +00:00
homarr-renovate[bot] c016beb2e5 chore(deps): update dependency vite to ^8.1.4 (#6332)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-14 09:06:17 +00:00
homarr-renovate[bot] f9e1b85c6a chore(deps): update dependency posthog-node to ^5.40.0 (#6314)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-14 06:14:41 +00:00
homarr-renovate[bot] 38ba19e665 chore(deps): update dependency oxlint to ^1.73.0 (#6306)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-14 03:22:16 +00:00
homarr-renovate[bot] f55c99d2d6 chore(deps): update dependency posthog-js to ^1.399.0 (#6331)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-14 00:00:38 +00:00
homarr-renovate[bot] c5e8b83d8a chore(deps): update vitest monorepo to ^4.1.10 (#6286)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-13 23:01:00 +00:00
homarr-renovate[bot] d52d9cfed2 chore(deps): update dependency posthog-js to ^1.398.7 (#6319)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-13 21:58:26 +00:00
Thomas 93325642aa Merge pull request #6324 from homarr-labs/renovate/major-github-artifact-actions 2026-07-13 22:12:32 +02:00
Thomas 165e94abbc Merge pull request #6328 from homarr-labs/fix-pnpm-fetch-build 2026-07-13 22:12:11 +02:00
homarr-renovate[bot] 2e3127e766 chore(deps): update docker/dockerfile docker tag to v1.25 (#6321) 2026-07-13 22:10:35 +02:00
Thomas d37326d905 Merge pull request #6322 from homarr-labs/renovate/actions-cache-6-x 2026-07-13 22:09:57 +02:00
homarr-renovate[bot] e51e39ff38 chore(deps): update dependency typescript to v7 (#6325) 2026-07-13 22:09:30 +02:00
homarr-renovate[bot] 6c075b6d74 chore(deps): update dependency mysql2 to v3.22.6 (#6310)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-13 18:45:08 +00:00
ajnart b2f63c6e0f fix(docker): drop --frozen-lockfile from install step per pnpm fetch docs
The pnpm fetch docs recommend `pnpm install -r --offline` without
--frozen-lockfile. The --offline flag already enforces no registry
access, making --frozen-lockfile redundant.

https://pnpm.io/cli/fetch
2026-07-13 20:24:59 +02:00
ajnart 8c2d73e8af fix(docker): resolve pnpm fetch crash with patchedDependencies
PR #6296 introduced pnpm fetch in the Dockerfile for Docker cache
optimization. However, pnpm fetch crashes with ENOENT when
patchedDependencies are configured with nodeLinker: hoisted because
applyPatchToDir tries to chdir into node_modules/<pkg> which doesn't
exist during fetch (only the content-addressable store is populated).

Fix: temporarily switch nodeLinker to isolated during pnpm fetch so
patches apply inside the virtual store (node_modules/.pnpm/...) which
IS created by pnpm fetch. Restore hoisted before the install step so
the final node_modules layout stays flat.

Refs: pnpm/pnpm#5268, pnpm/pnpm#8726
2026-07-13 20:06:28 +02:00
homarr-renovate[bot] 1c0ff8930e chore(deps): update actions/upload-artifact action to v7 2026-07-13 16:39:27 +00:00
homarr-renovate[bot] e2b8355639 chore(deps): update actions/cache action to v6 2026-07-13 16:39:10 +00:00
Thomas ba7718bde5 feat: rework the ci (#6296) 2026-07-13 18:32:48 +02:00
Thomas 06f1877ae1 Merge pull request #6282 from homarr-labs/docs/update-helm-docs
docs(helm): update chart installation page
2026-07-13 18:11:10 +02:00
homarr-renovate[bot] 1d9e8aabf3 chore(deps): update turbo monorepo to ^2.10.4 (#6303)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-13 13:36:45 +00:00
homarr-renovate[bot] b111e5285b chore(deps): update dependency @scalar/api-reference-react to ^0.9.54 (#6317)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-13 10:38:14 +00:00
homarr-renovate[bot] f4c491b570 chore(deps): update dependency @types/node to ^24.13.3 (#6318)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-13 10:37:23 +00:00
homarr-renovate[bot] 29bd4fd70b chore(deps): update dependency posthog-js to ^1.398.1 (#6311)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-12 18:09:44 +00:00
homarr-renovate[bot] 269dadee8d fix(deps): update dependency swiper to ^14.0.2 (#6313)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-12 18:09:11 +00:00
github-actions[bot] 34bd574354 docs(helm): Update Helm chart documentation 2026-07-10 20:32:11 +00:00
17 changed files with 1364 additions and 953 deletions

No files matched your search

+7
View File
@@ -6,7 +6,14 @@ npm-debug.log
README.md
.next
.turbo
.pnpm-store
**/.pnpm-store
coverage
**/coverage
**/.docusaurus
**/build
.git
.github
dev
.build
e2e
+2
View File
@@ -0,0 +1,2 @@
.secrets
!.secrets.example
+4
View File
@@ -0,0 +1,4 @@
TURBO_API=
TURBO_TEAM=
TURBO_TOKEN=
TURBO_REMOTE_CACHE_SIGNATURE_KEY=
+36
View File
@@ -0,0 +1,36 @@
# Running CI locally
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
```
`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.
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 never publishes or deletes GHCR images. The secret file is ignored by Git.
## PR preview images
Successful non-draft pull requests from this repository publish an amd64 image:
```bash
docker pull ghcr.io/homarr-labs/homarr-test:pr-<number>
```
The tag is replaced when the pull request is updated and deleted when it closes. Production Homarr images remain the supported multi-architecture releases. The `homarr-test` package must be configured as public in GHCR for unauthenticated testers.
## Dokploy preview cache
Keep `needs-demo` as the Dokploy preview label. Under the application's **Preview Build-time Secrets**, configure `TURBO_API`, `TURBO_TEAM`, `TURBO_TOKEN`, and `TURBO_REMOTE_CACHE_SIGNATURE_KEY`. Dokploy passes these to the Dockerfile as BuildKit secrets. Do not configure the token or signature key as Docker build arguments.
Rotate any credentials that have appeared in chat, logs, or committed configuration before enabling the cache.
+47
View File
@@ -0,0 +1,47 @@
name: Auto-fix lint and format
on:
pull_request:
types: [opened, synchronize, reopened]
branches: ["*"]
permissions:
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
autofix:
name: Auto-fix
if: github.event.pull_request.head.repo.id == github.repository_id
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
ref: ${{ github.head_ref }}
- name: Setup
uses: ./tooling/github/setup
- name: Get changed files
id: changed
env:
BASE_REF: ${{ github.base_ref }}
run: |
git diff --name-only --diff-filter=ACMR "origin/${BASE_REF}...HEAD" -- '*.ts' '*.tsx' '*.js' '*.jsx' > /tmp/changed-files
echo "count=$(wc -l < /tmp/changed-files | tr -d '[:space:]')" >> $GITHUB_OUTPUT
- name: Auto-fix lint + format
if: steps.changed.outputs.count != '0'
run: |
xargs -r -a /tmp/changed-files pnpm exec oxlint --fix || true
xargs -r -a /tmp/changed-files pnpm exec oxfmt --write
- name: Commit fixes
uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: "style: auto-fix lint and format"
+347
View File
@@ -0,0 +1,347 @@
name: CI
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches: ["*"]
pull_request_target:
types: [closed]
branches: ["*"]
push:
branches: [main, dev, beta, next]
merge_group:
workflow_dispatch:
inputs:
operation:
description: Operation to run
required: true
default: validate
type: choice
options:
- validate
- publish
- cleanup
pr_number:
description: PR number for publish or cleanup
required: false
type: number
ref:
description: Git ref to validate or publish
required: false
type: string
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || inputs.pr_number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
env:
FORCE_COLOR: 3
SKIP_ENV_VALIDATION: true
TURBO_TELEMETRY_DISABLED: 1
REGISTRY: ghcr.io
PREVIEW_IMAGE: ghcr.io/homarr-labs/homarr-test
jobs:
fast-gate:
name: Fast gate
if: github.event_name != 'pull_request_target' && (github.event_name != 'workflow_dispatch' || inputs.operation != 'cleanup')
runs-on: ubuntu-latest
timeout-minutes: 15
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 }}
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:
- uses: actions/checkout@v7
if: '!env.ACT'
with:
fetch-depth: 0
persist-credentials: false
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }}
- name: Validate manual inputs
if: github.event_name == 'workflow_dispatch' && inputs.operation == 'publish'
env:
PR_NUMBER: ${{ inputs.pr_number }}
BUILD_REF: ${{ inputs.ref }}
run: |
test -n "$PR_NUMBER" || { echo "pr_number is required for publish"; exit 1; }
test -n "$BUILD_REF" || { echo "ref is required for publish"; exit 1; }
- name: Setup
uses: ./tooling/github/setup
- name: Copy environment defaults
run: cp .env.example .env
- name: Check formatting in affected workspaces
run: pnpm turbo run format --affected --continue -- --check
- name: Lint, typecheck, and build affected workspaces
run: pnpm turbo run lint typecheck build --affected --continue=dependencies-successful
- name: Validate workspace configuration
run: pnpm lint:ws
- name: Run unit tests
run: pnpm test
- name: Report coverage
if: always() && !env.ACT
uses: davelosert/vitest-coverage-report-action@v2
container:
name: Container, E2E, and memory
needs: fast-gate
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
(github.event_name == 'workflow_dispatch' && (inputs.operation == 'validate' || inputs.operation == 'publish'))
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7
if: '!env.ACT'
with:
persist-credentials: false
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }}
- name: Setup
uses: ./tooling/github/setup
- name: Set up Docker Buildx
if: '!env.ACT'
uses: docker/setup-buildx-action@v4
- name: Build container once
if: '!env.ACT'
uses: docker/build-push-action@v7
with:
context: .
network: host
platforms: linux/amd64
load: true
push: false
tags: |
homarr-ci
homarr-e2e
labels: |
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.ref.name=pr-${{ github.event.pull_request.number || inputs.pr_number || 'none' }}
cache-from: type=gha,scope=homarr-linux-amd64
cache-to: type=gha,scope=homarr-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 }}
- name: Build container once 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: |
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[@]}" .
- name: Cache Playwright browsers
if: '!env.ACT'
id: playwright-cache
uses: actions/cache@v6
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install Playwright Chromium
if: steps.playwright-cache.outputs.cache-hit != 'true' || env.ACT
run: pnpm exec 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
- name: Run E2E tests
run: pnpm test:e2e
- name: Upload E2E artifacts
if: failure() && !env.ACT
uses: actions/upload-artifact@v7
with:
name: playwright-report
path: e2e/test-results/
retention-days: 7
- name: Run memory regression test
env:
CONTAINER_NAME: homarr-mem-${{ github.run_id }}-${{ github.run_attempt }}
SECRET_ENCRYPTION_KEY: 0000000000000000000000000000000000000000000000000000000000000000
run: |
docker run -d --name "$CONTAINER_NAME" -p 127.0.0.1::7575 \
-e NO_EXTERNAL_CONNECTION=true \
-e SECRET_ENCRYPTION_KEY="$SECRET_ENCRYPTION_KEY" \
homarr-ci
PORT=$(docker port "$CONTAINER_NAME" 7575/tcp | sed 's/.*://')
[[ "$PORT" =~ ^[0-9]+$ ]] || { echo "ERROR: invalid port mapping: '$PORT'"; exit 1; }
URL="http://127.0.0.1:${PORT}"
healthy=false
for i in $(seq 1 30); do
if curl -sf "$URL/api/health/ready" >/dev/null 2>&1; then
echo "Container healthy after $((i * 2))s"
healthy=true
break
fi
sleep 2
done
if [ "$healthy" != "true" ]; then
echo "ERROR: Container never became healthy"
docker logs "$CONTAINER_NAME" --tail 100
exit 1
fi
warmup_ok=0
for _ in $(seq 1 5); do
curl -sf "$URL/" >/dev/null 2>&1 && warmup_ok=$((warmup_ok + 1))
sleep 2
done
if [ "$warmup_ok" -eq 0 ]; then
echo "ERROR: All warmup requests failed"
docker logs "$CONTAINER_NAME" --tail 50
exit 1
fi
docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" | grep -qx true || {
echo "ERROR: Container died before memory measurement"
docker logs "$CONTAINER_NAME" --tail 100
exit 1
}
RAW_MEMORY=$(docker stats --no-stream --format '{{.MemUsage}}' "$CONTAINER_NAME" | sed 's| /.*||')
MEMORY_MB=$(echo "$RAW_MEMORY" | awk '
/GiB/ { gsub(/GiB/, ""); print $1 * 1024; next }
/MiB/ { gsub(/MiB/, ""); print $1; next }
/KiB/ { gsub(/KiB/, ""); print $1 / 1024; next }
{ print $1 / 1048576 }
')
echo "Memory usage: ${MEMORY_MB} MB (${RAW_MEMORY})"
[[ "$MEMORY_MB" =~ ^[0-9]+(\.[0-9]+)?$ ]] || {
echo "ERROR: non-numeric memory value: '$MEMORY_MB' (raw: '$RAW_MEMORY')"
docker logs "$CONTAINER_NAME" --tail 100
exit 1
}
if awk "BEGIN { exit !($MEMORY_MB < 50) }"; then
echo "ERROR: Memory suspiciously low (${MEMORY_MB} MB) — container may have crashed"
docker logs "$CONTAINER_NAME" --tail 100
exit 1
elif awk "BEGIN { exit !($MEMORY_MB > 700) }"; then
echo "ERROR: Memory usage ${MEMORY_MB} MB exceeds 700 MB"
docker logs "$CONTAINER_NAME" --tail 100
exit 1
fi
- name: Clean up memory test container
if: always()
env:
CONTAINER_NAME: homarr-mem-${{ github.run_id }}-${{ github.run_attempt }}
run: docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
- name: Determine preview tag
id: preview
if: |
!env.ACT &&
(
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event_name == 'workflow_dispatch' && inputs.operation == 'publish')
)
env:
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
run: echo "tag=pr-${PR_NUMBER}" >> "$GITHUB_OUTPUT"
- name: Log in to GHCR
if: steps.preview.outputs.tag != ''
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push preview image
if: steps.preview.outputs.tag != ''
env:
PREVIEW_TAG: ${{ steps.preview.outputs.tag }}
run: |
docker tag homarr-ci "${PREVIEW_IMAGE}:${PREVIEW_TAG}"
docker push "${PREVIEW_IMAGE}:${PREVIEW_TAG}"
- name: Show Act publication result
if: env.ACT
run: echo "Act validated the publication path; external GHCR writes are disabled locally."
cleanup:
name: Clean up preview image
if: github.event_name == 'pull_request_target' || (github.event_name == 'workflow_dispatch' && inputs.operation == 'cleanup')
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
packages: write
env:
IMAGE_TAG: pr-${{ github.event.pull_request.number || inputs.pr_number }}
steps:
- name: Validate cleanup input
if: github.event_name == 'workflow_dispatch'
env:
PR_NUMBER: ${{ inputs.pr_number }}
run: test -n "$PR_NUMBER" || { echo "pr_number is required for cleanup"; exit 1; }
- name: Delete GHCR package version
if: '!env.ACT'
uses: actions/github-script@v8
with:
script: |
const packageName = "homarr-test";
const org = context.repo.owner;
const tag = process.env.IMAGE_TAG;
const versions = await github.paginate(
github.rest.packages.getAllPackageVersionsForPackageOwnedByOrg,
{ package_type: "container", package_name: packageName, org, per_page: 100 },
);
const target = versions.find((version) =>
version.metadata?.container?.tags?.includes(tag),
);
if (!target) {
core.info(`No package version found with tag "${tag}"`);
return;
}
await github.rest.packages.deletePackageVersionForOrg({
package_type: "container",
package_name: packageName,
org,
package_version_id: target.id,
});
core.info(`Deleted package version ${target.id} (${tag})`);
- name: Show Act cleanup result
if: env.ACT
run: echo "Act validated cleanup for ${IMAGE_TAG}; external GHCR writes are disabled locally."
-165
View File
@@ -1,165 +0,0 @@
name: "[Quality] Code Analysis"
permissions:
contents: read
on:
pull_request:
branches: ["*"]
push:
branches: ["main"]
merge_group:
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
env:
FORCE_COLOR: 3
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 }}
TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha || github.event.before || 'HEAD~1' }}
TURBO_SCM_HEAD: ${{ github.sha }}
jobs:
lint-format-autofix:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.id == github.repository_id
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
ref: ${{ github.head_ref }}
- name: Setup
uses: ./tooling/github/setup
- name: Get changed files
id: changed
run: |
git diff --name-only --diff-filter=ACMR origin/${{ github.base_ref }}...HEAD -- '*.ts' '*.tsx' '*.js' '*.jsx' > /tmp/changed-files
echo "count=$(wc -l < /tmp/changed-files | tr -d '[:space:]')" >> $GITHUB_OUTPUT
- name: Auto-fix lint + format
if: steps.changed.outputs.count != '0'
run: |
xargs -r -a /tmp/changed-files pnpm exec oxlint --fix || true
xargs -r -a /tmp/changed-files pnpm exec oxfmt --write
- name: Commit fixes
uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: "style: auto-fix lint and format"
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup
uses: ./tooling/github/setup
- name: Copy env
shell: bash
run: cp .env.example .env
- name: Lint
run: pnpm turbo lint --affected && pnpm lint:ws
format:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup
uses: ./tooling/github/setup
- name: Format
run: pnpm turbo format --affected --continue
typecheck:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup
uses: ./tooling/github/setup
- name: Typecheck
run: pnpm turbo typecheck --affected
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
- name: Setup
uses: ./tooling/github/setup
- name: Test
run: pnpm test
- name: "Report Coverage"
# Set if: always() to also generate the report if tests are failing
# Only works if you set `reportOnFailure: true` in your vite config as specified above
if: always()
uses: davelosert/vitest-coverage-report-action@v2
e2e:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- name: Setup
uses: ./tooling/github/setup
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build docker image
id: build-docker-image
uses: docker/build-push-action@v7
with:
platforms: linux/amd64
context: .
push: false
load: true
tags: homarr-e2e
network: host
cache-from: type=gha
cache-to: type=gha,mode=max
env:
SKIP_ENV_VALIDATION: true
- name: Install playwright browsers
run: pnpm exec playwright install chromium
- name: Run E2E Tests
shell: bash
run: pnpm test:e2e
build:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup
uses: ./tooling/github/setup
- name: Copy env
shell: bash
run: cp .env.example .env
- name: Build
run: pnpm turbo build --affected
@@ -148,6 +148,11 @@ jobs:
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
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 }}
env:
SKIP_ENV_VALIDATION: true
build-arm64:
@@ -187,6 +192,11 @@ jobs:
platforms: linux/arm64
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
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 }}
env:
SKIP_ENV_VALIDATION: true
extract-asset-amd64:
+30 -11
View File
@@ -1,24 +1,43 @@
# syntax=docker/dockerfile:1.25
FROM node:24.18.0-alpine AS base
FROM base AS builder
RUN apk add --no-cache libc6-compat
RUN apk update
# Set working directory
WORKDIR /app
RUN apk add --no-cache libc6-compat curl bash
RUN apk update
RUN apk add --no-cache libc6-compat curl bash && apk update
RUN corepack enable pnpm
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY patches ./patches
# 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 \
pnpm config set store-dir /pnpm/store && \
sed -i 's/nodeLinker: hoisted/nodeLinker: isolated/' pnpm-workspace.yaml && \
pnpm fetch && \
sed -i 's/nodeLinker: isolated/nodeLinker: hoisted/' pnpm-workspace.yaml
COPY . .
# Follow the pnpm fetch pattern from https://pnpm.io/cli/fetch
# --frozen-lockfile is omitted as recommended by the pnpm fetch docs
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
pnpm install --recursive
RUN corepack enable pnpm && pnpm install --recursive --frozen-lockfile
# Copy static data as it is not part of the build
COPY static-data ./static-data
ARG SKIP_ENV_VALIDATION='true'
ARG CI='true'
ARG DISABLE_REDIS_LOGS='true'
ARG TARGETPLATFORM
RUN corepack enable pnpm && pnpm 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 \
TURBO_PLATFORM="${TARGETPLATFORM:-linux/amd64}" pnpm build
FROM base AS runner
WORKDIR /app
@@ -2,9 +2,9 @@
<img src="https://raw.githubusercontent.com/homarr-labs/charts/refs/heads/main/charts/homarr/icon.svg" align="right" width="92" alt="homarr logo">
![Version: 8.21.0](https://img.shields.io/badge/Version-8.21.0-informational?style=flat)
![Version: 8.22.0](https://img.shields.io/badge/Version-8.22.0-informational?style=flat)
![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat)
![AppVersion: v1.69.2](https://img.shields.io/badge/AppVersion-v1.69.2-informational?style=flat)
![AppVersion: v1.70.0](https://img.shields.io/badge/AppVersion-v1.70.0-informational?style=flat)
A Helm chart to deploy homarr for Kubernetes
@@ -429,7 +429,7 @@ All available values are listed on the [artifacthub](https://artifacthub.io/pack
| 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 | `"v1.69.2"` | Overrides the image tag whose default is the chart appVersion |
| image.tag | string | `"v1.70.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 |
+1 -1
View File
@@ -43,7 +43,7 @@
"random-word-slugs": "^0.1.7",
"react": "catalog:",
"react-dom": "catalog:",
"swiper": "^14.0.1",
"swiper": "^14.0.5",
"tailwindcss": "^4.3.2"
},
"devDependencies": {
+2 -2
View File
@@ -6,7 +6,7 @@
},
"engines": {
"node": ">=24.18.0",
"pnpm": ">=11.10.0"
"pnpm": ">=11.11.0"
},
"packageManager": "pnpm@11.10.0"
"packageManager": "pnpm@11.11.0"
}
+9 -5
View File
@@ -6,6 +6,10 @@
"build": "cross-env CI=true turbo build",
"clean": "git clean -xdf node_modules",
"clean:workspaces": "turbo clean",
"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:mysql:generate": "pnpm -F db migration:mysql:generate",
"db:migration:mysql:run": "pnpm -F db migration:mysql:run",
@@ -33,6 +37,9 @@
"typecheck": "turbo typecheck",
"with-env": "dotenv -e .env --"
},
"dependencies": {
"jsonpath-plus": "catalog:"
},
"devDependencies": {
"@playwright/test": "catalog:",
"@semantic-release/changelog": "catalog:",
@@ -62,10 +69,7 @@
},
"engines": {
"node": ">=24.18.0",
"pnpm": ">=11.10.0"
"pnpm": ">=11.11.0"
},
"packageManager": "pnpm@11.10.0",
"dependencies": {
"jsonpath-plus": "catalog:"
}
"packageManager": "pnpm@11.11.0"
}
+751 -749
View File
File diff suppressed because it is too large. Load diff
+16 -16
View File
@@ -18,7 +18,7 @@ catalog:
"@gitbeaker/rest": ^43.8.0
"@homarr/gridstack": ^1.12.0
"@homarr/node-unifi": ^2.6.0
"@immich/sdk": ^3.0.1
"@immich/sdk": ^3.0.2
"@jellyfin/sdk": ^0.13.0
"@kubernetes/client-node": ^1.4.0
"@modelcontextprotocol/sdk": 1.29.0
@@ -74,7 +74,7 @@ catalog:
"@trpc/react-query": ^11.18.0
"@trpc/server": ^11.18.0
"@trpc/tanstack-react-query": ^11.18.0
"@turbo/gen": ^2.10.2
"@turbo/gen": ^2.10.4
"@types/adm-zip": 0.5.8
"@types/bcrypt": 6.0.0
"@types/better-sqlite3": 7.6.13
@@ -83,21 +83,21 @@ catalog:
"@types/crypto-js": ^4.2.2
"@types/css-modules": ^1.0.5
"@types/dockerode": ^4.0.1
"@types/node": ^24.13.2
"@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.51
"@scalar/api-reference-react": ^0.9.54
"@types/video.js": ^7.3.58
"@types/ws": ^8.18.1
"@types/xml2js": ^0.4.14
posthog-js: ^1.398.0
posthog-node: ^5.39.2
posthog-js: ^1.399.1
posthog-node: ^5.40.0
"@vitejs/plugin-react": ^6.0.3
"@vitest/coverage-v8": ^4.1.9
"@vitest/ui": ^4.1.9
"@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
@@ -139,20 +139,20 @@ catalog:
jsonpath-plus: ^10.4.0
ldapts: 8.1.8
lodash.clonedeep: ^4.5.0
lucide-react: ^1.23.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
mcp-handler: ^1.1.0
maria2: ^0.4.1
mysql2: 3.22.5
mysql2: 3.22.6
next: 16.2.10
next-auth: 5.0.0-beta.31
next-intl: 4.13.1
node-loader: ^2.1.0
octokit: ^5.0.5
oxfmt: ^0.58.0
oxlint: ^1.72.0
oxlint: ^1.73.0
pg: ^8.22.0
postcss-preset-mantine: ^1.18.0
prismjs: ^1.30.0
@@ -176,14 +176,14 @@ catalog:
trpc-to-openapi: ^3.3.0
tsdav: ^2.3.0
tsx: 4.20.4
turbo: ^2.10.2
typescript: ^6.0.3
vite: ^8.1.2
turbo: ^2.10.4
typescript: "npm:@typescript/typescript6@^6.0.2"
vite: ^8.1.4
undici: 7.28.0
use-deep-compare-effect: ^1.8.1
video.js: ^8.23.9
vite-tsconfig-paths: ^6.1.1
vitest: ^4.1.9
vitest: ^4.1.10
winston: 3.19.0
ws: ^8.21.0
xml2js: ^0.6.2
@@ -229,7 +229,7 @@ overrides:
"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"
"vite@>=5.0.0 <=5.4.18": ">=8.1.3"
"vite@>=5.0.0 <=5.4.18": ">=8.1.4"
patchedDependencies:
"@types/node-unifi": "patches/@types__node-unifi.patch"
"trpc-to-openapi": "patches/trpc-to-openapi.patch"
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
set -euo pipefail
mode="${1:-all}"
case "$mode" in
fast) job="fast-gate" ;;
docker) job="container" ;;
all) job="" ;;
cleanup) job="cleanup" ;;
*)
echo "Usage: $0 {fast|docker|all|cleanup}" >&2
exit 2
;;
esac
command -v act >/dev/null 2>&1 || {
echo "act is required: https://nektosact.com/installation/index.html" >&2
exit 1
}
docker info >/dev/null 2>&1 || {
echo "Docker is not running" >&2
exit 1
}
if [[ -z "${DOCKER_HOST:-}" ]]; then
DOCKER_HOST=$(docker context inspect --format '{{.Endpoints.docker.Host}}' 2>/dev/null || true)
export DOCKER_HOST
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
json_escape() { printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g'; }
if [[ "$mode" == "cleanup" ]]; then
event_name="workflow_dispatch"
cat >"$event_file" <<EOF
{
"inputs": { "operation": "cleanup", "pr_number": "1", "ref": "" },
"repository": { "full_name": "homarr-labs/homarr" }
}
EOF
else
event_name="pull_request"
safe_ref=$(json_escape "$head_ref")
cat >"$event_file" <<EOF
{
"action": "synchronize",
"number": 1,
"repository": { "full_name": "homarr-labs/homarr" },
"pull_request": {
"number": 1,
"draft": false,
"base": { "sha": "$base_sha", "ref": "dev" },
"head": {
"sha": "$head_sha",
"ref": "$safe_ref",
"repo": { "full_name": "homarr-labs/homarr" }
}
}
}
EOF
fi
runner_image="${ACT_RUNNER_IMAGE:-catthehacker/ubuntu:act-latest}"
args=(
"$event_name"
--workflows .github/workflows/ci.yml
--eventpath "$event_file"
--platform "ubuntu-latest=$runner_image"
--container-architecture linux/amd64
--bind
)
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"
case "$git_common_dir" in
"$PWD" | "$PWD"/*) ;;
*) container_options="$container_options --volume $git_common_dir:$git_common_dir:ro" ;;
esac
args+=(--container-options "$container_options")
if [[ -n "${ACT_SECRET_FILE:-}" ]]; then
args+=(--secret-file "$ACT_SECRET_FILE")
fi
exec act "${args[@]}"
+2 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://turborepo.org/schema.json",
"remoteCache": { "signature": false },
"remoteCache": { "signature": true },
"globalEnv": ["CI", "NODE_ENV", "SKIP_ENV_VALIDATION"],
"ui": "stream",
"futureFlags": {
@@ -13,6 +13,7 @@
},
"build": {
"dependsOn": ["^build"],
"env": ["TURBO_PLATFORM"],
"inputs": [
"$TURBO_DEFAULT$",
"!**/*.test.ts",