Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
netcat-openbsd \
unzip \
curl \
bash \
&& install-php-extensions \
pdo_mysql \
pdo_pgsql \
Expand Down Expand Up @@ -112,7 +113,7 @@
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 \

Check failure on line 116 in Dockerfile

View workflow job for this annotation

GitHub Actions / 3️⃣ Dockerfile Lint

SC2086 info: Double quote to prevent globbing and word splitting.
storage/framework/sessions \
storage/framework/views \
storage/logs \
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:bookworm-slim@sha256:d5d3f9c23164ea16f31852f95bd5959aad1c5e854332fe00f7b3a20fcc9f635c 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:bookworm-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" ]
5 changes: 4 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 Expand Up @@ -393,6 +394,8 @@ services:
depends_on:
lychee_db:
condition: service_healthy
lychee_api:
condition: service_healthy

# Worker health check
# Verifies queue:work process is running
Expand Down
Loading