Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ trim_trailing_whitespace = false
indent_style = tab
indent_size = 4

[*.sh]
indent_style = space
indent_size = 2

[*.yml]
indent_style = space
indent_size = 2
Expand Down
112 changes: 111 additions & 1 deletion .github/workflows/CICD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ jobs:
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'

docker-build:
docker_build:
name: 4️⃣ Build Docker Image
runs-on: ubuntu-latest
if: >
Expand Down Expand Up @@ -254,6 +254,116 @@ jobs:
build-args: |
NODE_ENV=production

docker_legacy_check:
name: 3️⃣ Legacy Dockerfile Lint
runs-on: ubuntu-latest
needs:
- phpstan
- check_js

steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit

- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f #v3.12.0

- name: Docker Lint
uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 # v3.3.0
with:
dockerfile: ./Dockerfile-legacy
failure-threshold: warning

- name: Build Docker image locally
run: docker build -f Dockerfile-legacy -t lychee:local-legacy .

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # v0.33.1
with:
image-ref: lychee:local-legacy
format: 'table'
exit-code: 1
ignore-unfixed: true
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'

docker_legacy_build:
name: 4️⃣ Build Legacy Docker Image
runs-on: ubuntu-latest
if: >
(github.ref == 'refs/heads/master' && github.event_name == 'push') ||
(startsWith(github.ref, 'refs/tags/') && github.event_name == 'push')
needs:
- docker_legacy_check
permissions:
contents: read
packages: write

steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit

- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1

- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f #v3.12.0

- name: Log in to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

-
name: Login to DockerHub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with:
images: |
${{ github.repository }}
ghcr.io/${{ github.repository }}
flavor: |
latest=${{ startsWith(github.ref, 'refs/tags/') }}
suffix=-legacy,onlatest=true
tags: |
# define default branch
type=edge,branch=master
# branch event
type=ref,event=branch
# tag event
type=ref,event=tag

- name: Build and push Docker image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
push: true
file: Dockerfile-legacy
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
NODE_ENV=production

createArtifact:
name: 3️⃣ Build Artifact
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')
Expand Down
171 changes: 171 additions & 0 deletions Dockerfile-legacy
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Lychee - Laravel Backend Dockerfile
# Multi-stage build with Laravel Octane + FrankenPHP
ARG NODE_ENV=production

# ============================================================================
# Stage 1: Composer Dependencies
# ============================================================================
FROM composer:2.8@sha256:5248900ab8b5f7f880c2d62180e40960cd87f60149ec9a1abfd62ac72a02577c AS composer

WORKDIR /app

# Copy composer files first for layer caching
COPY composer.json composer.lock ./

# Install dependencies (no dev packages for production)
# Remove markdown and test directories to slim down the image
RUN composer install \
--no-dev \
--no-interaction \
--no-progress \
--no-scripts \
--prefer-dist \
--optimize-autoloader \
--ignore-platform-reqs \
&& find vendor \
\( -iname "*.md" -o -iname "test" -o -iname "tests" \) \
-exec rm -rf {} +

# ============================================================================
# Stage 2: Node.js Build for Frontend Assets
# ============================================================================
FROM node:20-alpine@sha256:658d0f63e501824d6c23e06d4bb95c71e7d704537c9d9272f488ac03a370d448 AS node

# Build argument to control dev vs production build
ARG NODE_ENV
ENV NODE_ENV=$NODE_ENV

WORKDIR /app

# Copy package files for layer caching
COPY package.json package-lock.json ./

# Install dependencies
RUN npm ci --no-audit

# Copy frontend source
COPY resources/ ./resources/
COPY public/ ./public/
COPY lang/ ./lang/
COPY vite.config.ts vite.embed.config.ts tsconfig.json ./

# Build frontend assets
# When NODE_ENV=development, Vite sets import.meta.env.DEV=true
RUN npm run build


FROM debian:trixie-slim@sha256:4bcb9db66237237d03b55b969271728dd3d955eaaa254b9db8a3db94550b1885 AS base

LABEL maintainer="lycheeorg"
LABEL org.opencontainers.image.title="Lychee"
LABEL org.opencontainers.image.description="Self-hosted photo management system done right."
LABEL org.opencontainers.image.authors="LycheeOrg"
LABEL org.opencontainers.image.vendor="LycheeOrg"
LABEL org.opencontainers.image.source="https://github.com/LycheeOrg/Lychee"
LABEL org.opencontainers.image.url="https://lycheeorg.github.io"
LABEL org.opencontainers.image.documentation="https://lycheeorg.dev/docs"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.base.name="debian:trixie-slim"

# Environment variables
ENV PUID='1000'
ENV PGID='1000'
ENV PHP_TZ=UTC

# https://stackoverflow.com/questions/53377176/change-imagemagick-policy-on-a-dockerfile (for the sed on policy.xml)
# Install base dependencies, add user and group, clone the repo and install php libraries
# hadolint ignore=DL3008
RUN \
set -ev && \
apt-get update && \
apt-get upgrade -qy && \
apt-get install -qy --no-install-recommends\
ca-certificates \
curl \
apt-transport-https && \
curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb && \
dpkg -i /tmp/debsuryorg-archive-keyring.deb && \
sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ bookworm main" > /etc/apt/sources.list.d/php.list' && \
apt-get update && \
apt-get install -qy --no-install-recommends \
adduser \
nginx-light \
php8.5-mysql \
php8.5-pgsql \
php8.5-sqlite3 \
php8.5-imagick \
php8.5-mbstring \
php8.5-gd \
php8.5-xml \
php8.5-zip \
php8.5-fpm \
php8.5-redis \
php8.5-bcmath \
php8.5-intl \
netcat-openbsd \
libimage-exiftool-perl \
ffmpeg \
jpegoptim \
optipng \
pngquant \
gifsicle \
webp \
cron \
ghostscript && \
sed -i 's/<policy domain="coder" rights="none" pattern="PDF" \/>/<policy domain="coder" rights="read|write" pattern="PDF" \/>/g' /etc/ImageMagick-6/policy.xml && \
usermod -o -u "$PUID" "www-data" && \
groupmod -o -g "$PGID" "www-data" && \
echo "* * * * * www-data cd /app && php artisan schedule:run >> /dev/null 2>&1" >> /etc/crontab && \
apt-get clean -qy && \
rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy application code
COPY --chown=www-data:www-data . .

# Copy vendor from composer stage
COPY --from=composer --chown=www-data:www-data /app/vendor ./vendor

# Copy built frontend assets from node stage
COPY --from=node --chown=www-data:www-data /app/public/build ./public/build

# Ensure storage and bootstrap/cache are writable with minimal permissions
RUN mkdir -p storage/framework/cache \
storage/framework/sessions \
storage/framework/views \
storage/logs \
bootstrap/cache \
public/dist \
&& chown -R www-data:www-data storage bootstrap/cache public/dist \
&& chmod -R 750 storage bootstrap/cache \
&& chmod -R 755 public/dist \
&& touch /app/docker_target \
&& touch /app/public/dist/user.css \
&& touch /app/public/dist/custom.js \
&& chown www-data:www-data /app/public/dist/user.css /app/public/dist/custom.js \
&& chmod 644 /app/public/dist/user.css /app/public/dist/custom.js

# Copy entrypoint and validation scripts
COPY docker/scripts/entrypoint.sh /usr/local/bin/entrypoint.sh
COPY docker/scripts/validate-env.sh /usr/local/bin/validate-env.sh
COPY docker/scripts/create-admin-user.sh /usr/local/bin/create-admin-user.sh
COPY docker/scripts/permissions-check.sh /usr/local/bin/permissions-check.sh
COPY docker/scripts/dump-env.sh /usr/local/bin/dump-env.sh
COPY docker/nginx.conf /etc/nginx/nginx.conf
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/validate-env.sh /usr/local/bin/permissions-check.sh /usr/local/bin/create-admin-user.sh /usr/local/bin/dump-env.sh

# Expose port 8000 (Octane)
EXPOSE 8000

# Set entrypoint
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]


# RUN chmod +x /entrypoint.sh && \
# chmod +x /inject.sh && \
# if [ ! -e /run/php ] ; then mkdir /run/php ; fi

HEALTHCHECK CMD curl --fail http://localhost:8000/ || exit 1

CMD [ "nginx" ]
3 changes: 2 additions & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ services:
# Laravel Backend API
lychee_api:
image: lychee-frankenphp:latest
# image: lychee-legacy:latest
# build:
# context: ./app
# dockerfile: Dockerfile
Expand Down Expand Up @@ -131,6 +132,7 @@ services:
# to disable it for your Log Viewer.
# Should redis crash, you will no longer be able to access your logs.
LOG_VIEWER_CACHE_DRIVER: "file"
SKIP_PERMISSIONS_CHECKS: "yes"

# Queue
QUEUE_CONNECTION: "${QUEUE_CONNECTION:-database}"
Expand Down Expand Up @@ -270,7 +272,6 @@ services:
- ./lychee/storage/app:/app/storage/app
- ./lychee/logs:/app/storage/logs
- ./lychee/tmp:/app/storage/tmp
- .env:/app/.env:ro

depends_on:
lychee_db:
Expand Down
Loading
Loading