Merge dev into main for v0.2.1
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m18s

This commit is contained in:
sandrodach committed 2026-10-28 16:38:43 +01:00
commit da12b3946a
4 files changed
+39 -16

No files matched your search

+24 -10
View File
@@ -1,17 +1,21 @@
name: MindSift CI & Docker Release name: MindSift CI & Docker Release
# Image tags:
# push to dev -> :dev
# push/merge main -> :latest
# git tag v1.2.0 -> :v1.2.0 and :stable
on: on:
push: push:
branches-ignore: branches:
- dev
- main - main
tags: tags:
- '*' - 'v*'
jobs: jobs:
build-test-push: build-test-push:
name: Build, Test in Container & Push name: Build, Test in Container & Push
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.ref != 'refs/heads/main'
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -33,9 +37,17 @@ jobs:
# Ersetze evtl. Slashes in Branch-Namen (z.B. rc/1.1 -> rc-1.1) für valide Docker-Tags # Ersetze evtl. Slashes in Branch-Namen (z.B. rc/1.1 -> rc-1.1) für valide Docker-Tags
DOCKER_TAG=$(echo "${REF_NAME}" | sed 's#/#-#g' | tr '[:upper:]' '[:lower:]') DOCKER_TAG=$(echo "${REF_NAME}" | sed 's#/#-#g' | tr '[:upper:]' '[:lower:]')
# All tags of this run (space separated)
case "${GITHUB_REF:-}" in
refs/tags/*) TAGS="${DOCKER_TAG} stable" ;;
refs/heads/main) TAGS="latest" ;;
*) TAGS="${DOCKER_TAG}" ;;
esac
echo "registry=${REGISTRY_HOST}" >> "$GITHUB_OUTPUT" echo "registry=${REGISTRY_HOST}" >> "$GITHUB_OUTPUT"
echo "image=${IMAGE_NAME}" >> "$GITHUB_OUTPUT" echo "image=${IMAGE_NAME}" >> "$GITHUB_OUTPUT"
echo "tag=${DOCKER_TAG}" >> "$GITHUB_OUTPUT" echo "tag=${TAGS%% *}" >> "$GITHUB_OUTPUT"
echo "tags=${TAGS}" >> "$GITHUB_OUTPUT"
echo "actor=${ACTOR_NAME}" >> "$GITHUB_OUTPUT" echo "actor=${ACTOR_NAME}" >> "$GITHUB_OUTPUT"
# Releases (tags) show the tag as version in the app header # Releases (tags) show the tag as version in the app header
APP_VERSION="" APP_VERSION=""
@@ -43,7 +55,7 @@ jobs:
refs/tags/*) APP_VERSION="${REF_NAME}" ;; refs/tags/*) APP_VERSION="${REF_NAME}" ;;
esac esac
echo "app_version=${APP_VERSION}" >> "$GITHUB_OUTPUT" echo "app_version=${APP_VERSION}" >> "$GITHUB_OUTPUT"
echo "Target Docker image: ${IMAGE_NAME}:${DOCKER_TAG} (Registry: ${REGISTRY_HOST}, Actor: ${ACTOR_NAME})" echo "Target Docker image: ${IMAGE_NAME} tags: ${TAGS} (Registry: ${REGISTRY_HOST}, Actor: ${ACTOR_NAME})"
- name: Login to Gitea Container Registry - name: Login to Gitea Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -54,8 +66,10 @@ jobs:
- name: Build & Run All Checks in Container, then Push - name: Build & Run All Checks in Container, then Push
run: | run: |
FULL_IMAGE="${{ steps.prep.outputs.image }}:${{ steps.prep.outputs.tag }}" IMAGE="${{ steps.prep.outputs.image }}"
echo "🚀 Building container & executing Frontend/Backend checks inside image: ${FULL_IMAGE}" TAG_ARGS=""
DOCKER_BUILDKIT=1 docker build --progress=plain --build-arg APP_VERSION="${{ steps.prep.outputs.app_version }}" -t "${FULL_IMAGE}" . for t in ${{ steps.prep.outputs.tags }}; do TAG_ARGS="${TAG_ARGS} -t ${IMAGE}:${t}"; done
echo "✅ All checks & tests passed inside container! Pushing ${FULL_IMAGE}..." echo "🚀 Building container & executing Frontend/Backend checks inside image: ${IMAGE} (tags: ${{ steps.prep.outputs.tags }})"
docker push "${FULL_IMAGE}" DOCKER_BUILDKIT=1 docker build --progress=plain --build-arg APP_VERSION="${{ steps.prep.outputs.app_version }}" ${TAG_ARGS} .
echo "✅ All checks & tests passed inside container! Pushing..."
for t in ${{ steps.prep.outputs.tags }}; do docker push "${IMAGE}:${t}"; done
+2
View File
@@ -91,6 +91,8 @@ Stage 2 golang:1.23 go vet, go test, go build → /app/mindsift (
Stage 3 alpine:3.21 nur Binary, ca-certificates, tzdata Stage 3 alpine:3.21 nur Binary, ca-certificates, tzdata
``` ```
**Image-Tags (CI):** Push auf `dev` → `:dev`; Push/Merge auf `main` → `:latest`; Git-Tag `v*` (z. B. `v1.2.0`) → `:v1.2.0` und `:stable`. Andere Branches bauen nicht.
Die angezeigte Version (Kopf der App) setzt der Build-Parameter `APP_VERSION`: Die CI übergibt bei Git-Tags den Tag-Namen (`v0.2`), sonst zeigt die App die Version aus `frontend/package.json` mit `-dev` (`v0.2-dev`). Umgesetzt in `frontend/vite.config.ts` und `frontend/src/version.ts`. Die angezeigte Version (Kopf der App) setzt der Build-Parameter `APP_VERSION`: Die CI übergibt bei Git-Tags den Tag-Namen (`v0.2`), sonst zeigt die App die Version aus `frontend/package.json` mit `-dev` (`v0.2-dev`). Umgesetzt in `frontend/vite.config.ts` und `frontend/src/version.ts`.
Die Tests laufen beim Image-Build (Frontend: `npm run test:run`, Backend: `go test -v ./...`); ein Fehler bricht den Build ab. Frontend-Tests liegen unter `frontend/src/**/__tests__/` (Vitest + jsdom + Testing Library, Setup in `frontend/src/test/setup.ts`). Die Tests laufen beim Image-Build (Frontend: `npm run test:run`, Backend: `go test -v ./...`); ein Fehler bricht den Build ab. Frontend-Tests liegen unter `frontend/src/**/__tests__/` (Vitest + jsdom + Testing Library, Setup in `frontend/src/test/setup.ts`).
+2 -2
View File
@@ -128,7 +128,7 @@ Minimal:
```yaml ```yaml
services: services:
mindsift: mindsift:
image: git.dsit.me/sandrodach/mindsift:v0.2 # feste Version; :dev = neuester Entwicklungsstand image: git.dsit.me/sandrodach/mindsift:stable # oder feste Version (:v0.2); :latest = main, :dev = Entwicklungsstand
restart: unless-stopped restart: unless-stopped
ports: ["8080:8080"] ports: ["8080:8080"]
volumes: ["./data:/data"] volumes: ["./data:/data"]
@@ -161,7 +161,7 @@ Rufe `http://localhost:8080` auf. Beim ersten Start führt ein Assistent durch d
* **Reverse-Proxy:** Am besten WebSocket-Upgrade und ungepufferte SSE-Streams durchreichen. Blockiert ein Proxy beides, funktioniert MindSift trotzdem über Long-Polling bzw. regelmäßige Abfragen, nur etwas träger. Beispiele für Nginx, Traefik, Caddy und Cloudflare: [Doku/reverse-proxy.md](Doku/reverse-proxy.md). * **Reverse-Proxy:** Am besten WebSocket-Upgrade und ungepufferte SSE-Streams durchreichen. Blockiert ein Proxy beides, funktioniert MindSift trotzdem über Long-Polling bzw. regelmäßige Abfragen, nur etwas träger. Beispiele für Nginx, Traefik, Caddy und Cloudflare: [Doku/reverse-proxy.md](Doku/reverse-proxy.md).
* **Datensicherung:** Alle Daten liegen in `data/mindsift.db` (+ `-wal`/`-shm`). Sichere sie mit `sqlite3 mindsift.db ".backup 'backup.db'"` oder bei gestopptem Container. Zusätzlich gibt es einen JSON-Export pro Benutzer. * **Datensicherung:** Alle Daten liegen in `data/mindsift.db` (+ `-wal`/`-shm`). Sichere sie mit `sqlite3 mindsift.db ".backup 'backup.db'"` oder bei gestopptem Container. Zusätzlich gibt es einen JSON-Export pro Benutzer.
* **Updates:** Neues Image ziehen und Container neu starten (`docker compose pull && docker compose up -d`). Das Schema wird beim Start automatisch angepasst. Die laufende Version steht im Kopf der App unter dem Namen. * **Updates:** Neues Image ziehen und Container neu starten (`docker compose pull && docker compose up -d`). Das Schema wird beim Start automatisch angepasst. Die laufende Version steht im Kopf der App unter dem Namen.
* **Image-Tags:** Jeder Git-Tag erzeugt ein gleichnamiges Image (`:v0.2`), jeder Branch eines mit dem Branch-Namen (`:dev`). * **Image-Tags:** `:dev` = Stand des Branches `dev`, `:latest` = Stand von `main`, `:stable` = zuletzt veröffentlichte Version. Ein Git-Tag wie `v1.2.0` erzeugt `:v1.2.0` und verschiebt `:stable` darauf.
--- ---
@@ -816,13 +816,19 @@ export const OutlinerBlock: React.FC<OutlinerBlockProps> = ({
{block.type === 'todo' && ( {block.type === 'todo' && (
<span className="inline-flex items-center align-middle ml-1.5 gap-1 shrink-0 pointer-events-auto z-10 relative select-none"> <span className="inline-flex items-center align-middle ml-1.5 gap-1 shrink-0 pointer-events-auto z-10 relative select-none">
{block.taskId && ( {block.taskId && (
<span <button
className="badge-accent text-[9px] inline-flex items-center gap-0.5 px-1.5 py-0.2 rounded font-medium shrink-0" type="button"
title="Mit erweiterter Aufgabe synchronisiert" onMouseDown={(e) => e.stopPropagation()}
onClick={(e) => {
e.stopPropagation();
onOpenTask?.(block.taskId as string);
}}
className="badge-accent text-[9px] inline-flex items-center gap-0.5 px-1.5 py-0.2 rounded font-medium shrink-0 cursor-pointer hover:opacity-80"
title="Erweiterte Aufgabe öffnen"
> >
<CheckSquare className="w-2.5 h-2.5" /> <CheckSquare className="w-2.5 h-2.5" />
<span>Erweitert</span> <span>Erweitert</span>
</span> </button>
)} )}
{canEdit && ( {canEdit && (
@@ -861,6 +867,7 @@ export const OutlinerBlock: React.FC<OutlinerBlockProps> = ({
type: 'todo', type: 'todo',
}; };
onUpdateBlockRef.current(updatedBlock, true); onUpdateBlockRef.current(updatedBlock, true);
onOpenTask?.(thought.id);
} catch (err: any) { } catch (err: any) {
alert(err.message || 'Fehler beim Erstellen der erweiterten Aufgabe'); alert(err.message || 'Fehler beim Erstellen der erweiterten Aufgabe');
} }