Merge pull request 'claude/compassionate-feistel' (#4) from claude/compassionate-feistel into main
Some checks failed
Build and Push Docker Image / build (push) Failing after 57s
Some checks failed
Build and Push Docker Image / build (push) Failing after 57s
Reviewed-on: http://gitea.lan/gpatti/MediaLore/pulls/4
This commit is contained in:
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.git
|
||||
node_modules
|
||||
.next
|
||||
.thumbnails
|
||||
medialore.db
|
||||
medialore.db-shm
|
||||
medialore.db-wal
|
||||
data/
|
||||
.env*
|
||||
docker-compose*.yml
|
||||
42
.gitea/workflows/docker.yml
Normal file
42
.gitea/workflows/docker.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Login to Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: gitea.lan
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- name: Build Docker Image
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.ref_name }}
|
||||
SHORT_HASH: ${{ github.sha }}
|
||||
run: |
|
||||
# Build the image with the commit hash tag
|
||||
docker build --build-arg BUILD_IDENTIFIER=${SHORT_HASH:0:5} -t gitea.lan/gpatti/MediaLore:${BRANCH_NAME}-${SHORT_HASH:0:5} .
|
||||
|
||||
# Tag the same image as "latest"
|
||||
docker tag gitea.lan/gpatti/MediaLore:${BRANCH_NAME}-${SHORT_HASH:0:5} gitea.lan/gpatti/MediaLore:${BRANCH_NAME}-latest
|
||||
|
||||
- name: Push Docker Images
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.ref_name }}
|
||||
SHORT_HASH: ${{ github.sha }}
|
||||
run: |
|
||||
docker push gitea.lan/gpatti/MediaLore:${BRANCH_NAME}-${SHORT_HASH:0:5}
|
||||
docker push gitea.lan/gpatti/MediaLore:${BRANCH_NAME}-latest
|
||||
|
||||
- name: Log out from registry
|
||||
if: always()
|
||||
run: docker logout gitea.lan
|
||||
57
Dockerfile
Normal file
57
Dockerfile
Normal file
@@ -0,0 +1,57 @@
|
||||
# Stage 1: Install dependencies (with build tools for native modules)
|
||||
FROM node:22-bookworm-slim AS deps
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3 make g++ \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Stage 2: Build the Next.js application
|
||||
FROM node:22-bookworm-slim AS builder
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# Stage 3: Production image
|
||||
FROM node:22-bookworm-slim AS runner
|
||||
WORKDIR /app
|
||||
|
||||
# Install ffmpeg for video thumbnail generation
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ffmpeg \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
# Bind to all interfaces so the container port is reachable
|
||||
ENV HOSTNAME=0.0.0.0
|
||||
# Store libraries.json in /config so it can be bind-mounted as a directory.
|
||||
# Mounting a directory (not a single file) ensures the atomic rename in
|
||||
# writeLibraries() works — both .tmp and the target are on the same filesystem.
|
||||
ENV LIBRARIES_CONFIG_PATH=/config/libraries.json
|
||||
RUN mkdir -p /config
|
||||
|
||||
# Copy standalone Next.js output
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# Copy native modules — Next.js standalone's file tracer does not follow
|
||||
# .node binary files, so we copy these manually from the deps stage.
|
||||
# Both are listed in serverExternalPackages and resolved at runtime via require().
|
||||
COPY --from=deps /app/node_modules/better-sqlite3 ./node_modules/better-sqlite3
|
||||
COPY --from=deps /app/node_modules/sharp ./node_modules/sharp
|
||||
COPY --from=deps /app/node_modules/@img ./node_modules/@img
|
||||
|
||||
# Create thumbnail cache directory (mounted as a volume in production)
|
||||
RUN mkdir -p /app/.thumbnails
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
14
config/libraries.json
Normal file
14
config/libraries.json
Normal file
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": "games",
|
||||
"name": "Games",
|
||||
"path": "/data/Games",
|
||||
"type": "games"
|
||||
},
|
||||
{
|
||||
"id": "various",
|
||||
"name": "various",
|
||||
"path": "/data/Various Media",
|
||||
"type": "mixed"
|
||||
}
|
||||
]
|
||||
23
docker-compose.yml
Normal file
23
docker-compose.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- 3000:3000
|
||||
environment:
|
||||
PORT: 3000
|
||||
NODE_ENV: production
|
||||
volumes:
|
||||
# Runtime data — must map to /app/ since process.cwd() = /app in the container
|
||||
- ./medialore.db:/app/medialore.db
|
||||
- ./.thumbnails:/app/.thumbnails
|
||||
# Library config — mounted as a directory so the atomic rename in the API works.
|
||||
# A single-file bind-mount causes EBUSY on rename() because .tmp and the target
|
||||
# end up on different devices. Initialize before first run:
|
||||
# mkdir -p config && echo '[]' > config/libraries.json
|
||||
- ./config:/config
|
||||
|
||||
# Mount your media folders and reference them in libraries.json using
|
||||
# absolute /data/ paths, e.g. { "path": "/data/Games" }
|
||||
- ./data:/data:ro
|
||||
# - /path/to/your/movies:/data/Movies:ro
|
||||
restart: unless-stopped
|
||||
@@ -4,11 +4,5 @@
|
||||
"name": "Games",
|
||||
"path": "./data/Games",
|
||||
"type": "games"
|
||||
},
|
||||
{
|
||||
"id": "various",
|
||||
"name": "Various",
|
||||
"path": "./data/Various Media",
|
||||
"type": "mixed"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { NextConfig } from 'next'
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: 'standalone',
|
||||
serverExternalPackages: ['better-sqlite3', 'sharp'],
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
import { redirect } from 'next/navigation'
|
||||
import { getLibraries } from '@/lib/libraries'
|
||||
import LibraryCard from '@/components/LibraryCard'
|
||||
|
||||
@@ -2,7 +2,7 @@ import path from 'path'
|
||||
import fs from 'fs'
|
||||
import type { Library, LibraryType } from '@/types'
|
||||
|
||||
const CONFIG_PATH = path.resolve(process.cwd(), 'libraries.json')
|
||||
const CONFIG_PATH = process.env.LIBRARIES_CONFIG_PATH ?? path.resolve(process.cwd(), 'libraries.json')
|
||||
const CONFIG_TMP = CONFIG_PATH + '.tmp'
|
||||
|
||||
export function getLibraries(): Library[] {
|
||||
|
||||
Reference in New Issue
Block a user