Files
profilarr/.github/workflows/ci.yml
dependabot[bot] 54003609ec chore(deps): bump actions/setup-node from 6.4.0 to 7.0.0
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.4.0 to 7.0.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](48b55a011b...8207627860)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-27 04:38:26 +00:00

385 lines
12 KiB
YAML

name: CI
on:
push:
branches: ['hotfix/*']
paths-ignore:
['docs/**', '*.md', '!SECURITY.md', '.github/issue_template/**', '.github/assets/**']
pull_request:
branches: [develop]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: read
jobs:
ci-scope:
name: Detect CI Scope
runs-on: ubuntu-latest
outputs:
full_ci: ${{ steps.push.outputs.full_ci || steps.filter.outputs.full_ci }}
steps:
- id: push
if: github.event_name != 'pull_request'
run: echo "full_ci=true" >> "$GITHUB_OUTPUT"
- id: filter
if: github.event_name == 'pull_request'
uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
with:
predicate-quantifier: every
filters: |
full_ci:
- '**'
- '!docs/**'
- '!*.md'
- '!LICENSE'
- '!.github/issue_template/**'
- '!.github/assets/**'
docs-only:
name: Docs-only CI
needs: [ci-scope]
if: ${{ needs.ci-scope.outputs.full_ci != 'true' }}
runs-on: ubuntu-latest
steps:
- name: Report skipped CI
run: |
echo "Docs-only change detected." >> "$GITHUB_STEP_SUMMARY"
echo "Build, lint, type-check, tests, Semgrep, integration, and E2E jobs were intentionally skipped." >> "$GITHUB_STEP_SUMMARY"
# ─── Build ──────────────────────────────────────────────────────────────
build:
name: Build
needs: [ci-scope]
if: ${{ needs.ci-scope.outputs.full_ci == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
with:
deno-version-file: .tool-versions
- name: Cache deps
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cache/deno
node_modules
key: deno-${{ runner.os }}-${{ hashFiles('deno.lock') }}
restore-keys: deno-${{ runner.os }}-
- run: deno ci
- name: Build
run: deno task build
- name: Upload build artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: profilarr-build
path: |
dist/build/profilarr
dist/build/server.js
dist/build/static/
retention-days: 1
# ─── Quality gates (all run in parallel with build) ─────────────────────
lint:
name: Lint
needs: [ci-scope]
if: ${{ needs.ci-scope.outputs.full_ci == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
with:
deno-version-file: .tool-versions
- name: Cache deps
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cache/deno
node_modules
key: deno-${{ runner.os }}-${{ hashFiles('deno.lock') }}
restore-keys: deno-${{ runner.os }}-
- run: deno ci
- name: Lint
run: deno task lint
type-check:
name: Type Check
needs: [ci-scope]
if: ${{ needs.ci-scope.outputs.full_ci == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
with:
deno-version-file: .tool-versions
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .tool-versions
- name: Cache deps
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cache/deno
node_modules
key: deno-${{ runner.os }}-${{ hashFiles('deno.lock') }}
restore-keys: deno-${{ runner.os }}-
- run: deno ci
- name: Sync SvelteKit types
run: deno run -A npm:@sveltejs/kit sync
- name: Type check
run: deno task check
unit:
name: Unit Tests
needs: [ci-scope]
if: ${{ needs.ci-scope.outputs.full_ci == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
with:
deno-version-file: .tool-versions
- name: Cache deps
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cache/deno
node_modules
key: deno-${{ runner.os }}-${{ hashFiles('deno.lock') }}
restore-keys: deno-${{ runner.os }}-
- run: deno ci
- name: Run unit tests
run: deno task test
semgrep:
name: Semgrep
needs: [ci-scope]
if: ${{ needs.ci-scope.outputs.full_ci == 'true' }}
runs-on: ubuntu-latest
container:
image: semgrep/semgrep
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Run Semgrep
run: |
semgrep scan \
--config tests/scan/semgrep/ \
--config p/default \
--config p/typescript \
--config p/javascript \
--config p/owasp-top-ten \
--config p/nodejs \
--config p/security-audit \
--config p/csharp \
--error
# ─── Container scanning ────────────────────────────────────────────────
trivy:
name: Trivy
needs: [ci-scope]
if: ${{ needs.ci-scope.outputs.full_ci == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build images
run: docker compose -f compose.yml -f compose.dev.yml build
- name: Scan profilarr
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: 'profilarr:alpine'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Scan parser
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: 'profilarr-parser:alpine'
severity: 'CRITICAL,HIGH'
exit-code: '1'
# ─── Tests that need the binary ─────────────────────────────────────────
integration:
name: Integration Tests
needs: [ci-scope, build]
if: ${{ needs.ci-scope.outputs.full_ci == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
with:
deno-version-file: .tool-versions
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .tool-versions
- name: Cache deps
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cache/deno
node_modules
key: deno-${{ runner.os }}-${{ hashFiles('deno.lock') }}
restore-keys: deno-${{ runner.os }}-
- run: deno ci
# Dedicated cache for the FFI plug binaries (sqlite, bcrypt). The main
# `Cache deps` covers ~/.cache/deno but is shared across jobs and gets
# overwritten by Build (which never populates plug). This cache uses a
# separate key so it survives independently. Without this, the 30+
# parallel integration spec processes race to download plug binaries
# into the same path on a cold cache and corrupt each other.
- name: Cache FFI plug
id: plug-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/deno/plug
key: deno-plug-${{ runner.os }}-${{ hashFiles('deno.lock') }}-v1
- name: Pre-warm FFI plug
if: steps.plug-cache.outputs.cache-hit != 'true'
run: |
deno eval --ext=ts '
import { hash } from "@felix/bcrypt";
import { Database } from "@db/sqlite";
await hash("warmup");
new Database(":memory:").close();
'
- name: Download build artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: profilarr-build
path: dist/build
- name: Make binary executable
run: chmod +x dist/build/profilarr
- name: Cache Docker images
id: docker-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: /tmp/docker-images.tar
key: docker-images-${{ hashFiles('tests/integration/auth/docker-compose.yml') }}
- name: Load cached Docker images
if: steps.docker-cache.outputs.cache-hit == 'true'
run: docker load -i /tmp/docker-images.tar
- name: Pull and save Docker images
if: steps.docker-cache.outputs.cache-hit != 'true'
run: |
docker pull ghcr.io/navikt/mock-oauth2-server:3.0.1
docker pull caddy:2-alpine
docker pull nginx:alpine
docker save ghcr.io/navikt/mock-oauth2-server:3.0.1 caddy:2-alpine nginx:alpine -o /tmp/docker-images.tar
- name: Run integration tests
run: deno task test integration
e2e-auth:
name: E2E Tests
needs: [ci-scope, build]
if: ${{ needs.ci-scope.outputs.full_ci == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
with:
deno-version-file: .tool-versions
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .tool-versions
- name: Cache deps
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cache/deno
node_modules
key: deno-${{ runner.os }}-${{ hashFiles('deno.lock') }}
restore-keys: deno-${{ runner.os }}-
- run: deno ci
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('deno.lock') }}
- name: Install Playwright browsers + system deps
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: deno run -A npm:playwright install --with-deps chromium
- name: Install Playwright system deps only (browsers cached)
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: deno run -A npm:playwright install-deps chromium
- name: Download build artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: profilarr-build
path: dist/build
- name: Make binary executable
run: chmod +x dist/build/profilarr
- name: Cache Docker images
id: docker-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: /tmp/docker-images.tar
key: docker-images-${{ hashFiles('tests/integration/auth/docker-compose.yml') }}
- name: Load cached Docker images
if: steps.docker-cache.outputs.cache-hit == 'true'
run: docker load -i /tmp/docker-images.tar
- name: Pull and save Docker images
if: steps.docker-cache.outputs.cache-hit != 'true'
run: |
docker pull ghcr.io/navikt/mock-oauth2-server:3.0.1
docker pull caddy:2-alpine
docker pull nginx:alpine
docker save ghcr.io/navikt/mock-oauth2-server:3.0.1 caddy:2-alpine nginx:alpine -o /tmp/docker-images.tar
- name: Run E2E auth tests
run: deno task test e2e auth