8 Commits

Author SHA1 Message Date
jeanGaston d8d152e249 chore: bump version to 1.0.2
Publish Docker image / build-and-push (push) Successful in 4m46s
2026-07-30 16:40:56 +02:00
jeanGaston 75ea9b7416 fix(docker): fix data volume ownership on fresh bind mounts
/app/data is a bind mount, so the chown baked into the image is
overwritten by the host directory's ownership at runtime. On a fresh
prod deploy Docker creates ./data as root-owned, but the container ran
as the non-root nextjs user, so Prisma couldn't open lovelope.db
("unable to open database file").

Add an entrypoint that starts as root, chowns /app/data, then drops to
nextjs via su-exec before running migrations and starting the server.
2026-07-30 16:40:45 +02:00
jeanGaston cd36fc5723 docs: document the dev/tag release process in GIT_WORKFLOW.md
Spells out the two-channel CI trigger (dev branch for test builds, v*
tags for releases) as an actual step-by-step process: keeping dev in
sync with main/master, bumping package.json, tagging, and the
required Gitea repo secrets/variables.
2026-07-30 16:35:45 +02:00
jeanGaston 3987a4e43e chore: bump version to 1.0.1
Publish Docker image / build-and-push (push) Successful in 3m22s
2026-07-30 15:47:12 +02:00
jeanGaston 5ca66f3ed7 chore: rename database file from askedout.db to lovelope.db
Publish Docker image / build-and-push (push) Successful in 3m34s
Leftover naming from before the app was renamed to lovelope. Renamed
the local data/askedout.db file to data/lovelope.db to match (data/
is gitignored, so this doesn't touch version control).
2026-07-30 15:46:21 +02:00
jeanGaston c36d146fef chore: bump version to 1.0.0 for first release
Publish Docker image / build-and-push (push) Successful in 3m39s
2026-07-30 15:28:37 +02:00
jeanGaston 3ac68a88d0 docs: lead Quick start with the published GHCR image
Publish Docker image / build-and-push (push) Successful in 3m31s
Running the app no longer requires cloning the repo: pull the
published ghcr.io image directly via docker-compose. Move the
build-from-source flow (existing docker-compose.yml + Dockerfile) to
a secondary section for contributors.
2026-07-30 12:03:29 +02:00
jeanGaston 94b02198dd ci: build images from a dev branch and release tags, not every commit
Every push to master was triggering a build+push, since that's where
day-to-day commits land. Switch to two deliberate channels instead:
pushing to dev produces a rolling :dev tag for testing, and pushing a
v* tag produces :latest + the version tag for real releases. Plain
commits/merges to master/main no longer build anything on their own.
2026-07-30 12:03:20 +02:00
8 changed files with 134 additions and 21 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# SQLite database file (mounted on a persistent volume in Docker)
DATABASE_URL="file:/app/data/askedout.db"
DATABASE_URL="file:/app/data/lovelope.db"
# Public base URL (used in QR codes and share links)
APP_URL="http://localhost:3000"
+24 -6
View File
@@ -1,8 +1,12 @@
name: Publish Docker image
# Two channels, not one-image-per-commit:
# - push to `dev` -> rolling `:dev` tag, for testing in-progress work
# - push tag `v*` -> `:latest` + the version tag, for actual releases
# Plain commits/merges to master/main build nothing on their own.
on:
push:
branches: [main, master]
branches: [dev]
tags: ['v*']
permissions:
@@ -20,6 +24,24 @@ jobs:
- name: Compute lowercase image name
run: echo "IMAGE_NAME=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"
- name: Compute image tags
run: |
if [ "${{ github.ref_type }}" = "tag" ]; then
VERSION="${{ github.ref_name }}"
TAGS="${{ vars.REGISTRY_HOST }}/${IMAGE_NAME}:latest
${{ vars.REGISTRY_HOST }}/${IMAGE_NAME}:${VERSION}
ghcr.io/${IMAGE_NAME}:latest
ghcr.io/${IMAGE_NAME}:${VERSION}"
else
TAGS="${{ vars.REGISTRY_HOST }}/${IMAGE_NAME}:dev
ghcr.io/${IMAGE_NAME}:dev"
fi
{
echo "TAGS<<TAGS_EOF"
echo "$TAGS"
echo "TAGS_EOF"
} >> "$GITHUB_ENV"
- name: Log in to Gitea container registry
uses: docker/login-action@v3
with:
@@ -39,8 +61,4 @@ jobs:
with:
context: .
push: true
tags: |
${{ vars.REGISTRY_HOST }}/${{ env.IMAGE_NAME }}:latest
${{ vars.REGISTRY_HOST }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
ghcr.io/${{ env.IMAGE_NAME }}:latest
ghcr.io/${{ env.IMAGE_NAME }}:${{ github.sha }}
tags: ${{ env.TAGS }}
+4 -4
View File
@@ -28,7 +28,7 @@ RUN npm run build
# ── Stage 3: runner ────────────────────────────────────────────────────────────
FROM node:20-alpine AS runner
RUN apk add --no-cache libc6-compat openssl
RUN apk add --no-cache libc6-compat openssl su-exec
WORKDIR /app
ENV NODE_ENV=production
@@ -48,11 +48,11 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
RUN mkdir -p /app/data && chown nextjs:nodejs /app/data
USER nextjs
COPY --chmod=755 docker-entrypoint.sh ./docker-entrypoint.sh
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Run migrations then start the server
CMD ["sh", "-c", "npx prisma migrate deploy && node server.js"]
# Fix ownership of the bind-mounted /app/data volume, then drop to nextjs
ENTRYPOINT ["./docker-entrypoint.sh"]
+61 -3
View File
@@ -46,10 +46,68 @@ Types: `feat`, `fix`, `refactor`, `style`, `docs`, `chore`, `test`.
- Never force-push `main`. Force-pushing a feature branch you own (after a
rebase) is fine; never force-push a branch someone else might be using.
## Tags / releases
## Releases & Docker images
Tag production releases as `v<major>.<minor>.<patch>` (semver) once this
project has a deployment cadence worth marking. Not required for every merge.
CI (`.gitea/workflows/docker-publish.yml`) never builds an image from a
plain commit or merge to `main`/`master` — only from two deliberate
triggers, each producing tags on both the Gitea registry and GHCR:
| Trigger | Produces | Purpose |
|------------------|--------------------------|-------------------------------------|
| push to `dev` | `:dev` | test a build before releasing |
| push a `v*` tag | `:latest` + `:vX.Y.Z` | ship an actual release |
### Testing a build (`dev`)
`dev` is a disposable pointer, not a branch with history of its own — it
should always match `main`/`master` exactly. Bring it up to date and push
whenever you want a fresh test image:
```
git checkout dev
git merge main --ff-only
git push origin dev
```
If `dev` has drifted and won't fast-forward (e.g. after a rebase, or a
commit landed only on `dev` by mistake), reset it instead of merging —
`dev` has no unique history worth preserving:
```
git checkout dev
git reset --hard main
git push origin dev --force-with-lease
```
### Shipping a release (tag)
1. Bump the version in `package.json` on `main`/`master` (a plain commit —
builds nothing on its own):
```
# edit package.json: "version": "X.Y.Z"
git add package.json
git commit -m "chore: bump version to X.Y.Z"
git push origin main
```
2. Tag that commit and push the tag — this is what actually triggers the
release build:
```
git tag vX.Y.Z
git push origin vX.Y.Z
```
3. Never move or re-push an existing tag once it's out (it may already be
pulled/deployed). If you need to fix something after tagging, bump to
the next patch version and tag again instead.
Use `v<major>.<minor>.<patch>` (semver) for tags.
### Required repo secrets/variables (Gitea → Settings → Actions)
- `vars.REGISTRY_HOST` — the Gitea instance hostname (no protocol), used to
tag/push to the built-in Gitea container registry.
- `secrets.REGISTRY_TOKEN` — a Gitea PAT with `write:package` scope.
- `secrets.GHCR_TOKEN` — a GitHub PAT with `write:packages` scope, used to
also push to `ghcr.io`.
## What not to commit
+36 -5
View File
@@ -10,7 +10,40 @@ No accounts, no friction, just a sweet little link. Everything personal is encry
## Quick start
You need [Docker](https://www.docker.com/) installed. That's it.
You need [Docker](https://www.docker.com/) installed. That's it — no clone required, the image is published on GHCR.
Create a `docker-compose.yml`:
```yaml
services:
app:
image: ghcr.io/jeangaston/lovelope:latest
ports:
- "3000:3000"
environment:
DATABASE_URL: "file:/app/data/lovelope.db"
APP_URL: "http://localhost:3000"
GIPHY_API_KEY: "${GIPHY_API_KEY:-}"
volumes:
- ./data:/app/data
restart: unless-stopped
```
Then:
```bash
docker compose up -d
```
Open **http://localhost:3000**. Your data lives in `./data/lovelope.db` on your machine.
Want GIF search to work? Grab a free key at [developers.giphy.com](https://developers.giphy.com/), export it as `GIPHY_API_KEY` before running `docker compose up` (or drop it in a `.env` file next to `docker-compose.yml`), then restart. Without it, the app still runs fine, GIF search is just disabled.
Pin a specific version instead of always tracking `latest` by using a release tag, e.g. `ghcr.io/jeangaston/lovelope:v1.0.0`.
### Build from source
If you'd rather build the image yourself instead of pulling from GHCR:
```bash
git clone https://github.com/jeangaston/lovelope.git && cd lovelope
@@ -18,9 +51,7 @@ cp .env.example .env
docker compose up --build
```
Open **http://localhost:3000**. Everything (the app and its database) runs in one container; your data lives in `./data/askedout.db` on your machine.
Want GIF search to work? Grab a free key at [developers.giphy.com](https://developers.giphy.com/) and put it in `.env` as `GIPHY_API_KEY`, then restart. Without it, the app still runs fine, GIF search is just disabled.
This uses the [docker-compose.yml](docker-compose.yml) in the repo, which builds from the local [Dockerfile](Dockerfile) instead of pulling a published image.
## Features
@@ -52,7 +83,7 @@ Browsers never send the part after `#` to any server, so that key never reaches
| Variable | Required | Description |
|---|---|---|
| `DATABASE_URL` | ✅ | SQLite file path, default `file:/app/data/askedout.db` works out of the box with Docker |
| `DATABASE_URL` | ✅ | SQLite file path, default `file:/app/data/lovelope.db` works out of the box with Docker |
| `APP_URL` | ✅ | Base URL used to build share links, e.g. `http://localhost:3000` |
| `GIPHY_API_KEY` | optional | Enables GIF search. Free key at [developers.giphy.com](https://developers.giphy.com/), leave blank to disable |
+1 -1
View File
@@ -6,7 +6,7 @@ services:
ports:
- "3000:3000"
environment:
DATABASE_URL: "file:/app/data/askedout.db"
DATABASE_URL: "file:/app/data/lovelope.db"
APP_URL: "http://localhost:3000"
NODE_ENV: "development"
GIPHY_API_KEY: "${GIPHY_API_KEY:-}"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
set -e
chown -R nextjs:nodejs /app/data
exec su-exec nextjs sh -c "npx prisma migrate deploy && node server.js"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "lovelope",
"version": "0.1.0",
"version": "1.0.2",
"private": true,
"license": "MIT",
"scripts": {