V3.4
This commit is contained in:
+47
-23
@@ -1,49 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
REGISTRY="${GITEA_REGISTRY:-${1:-}}"
|
||||
IMAGE_PATH="${GITEA_IMAGE_PATH:-${2:-}}"
|
||||
TAG="${IMAGE_TAG:-${3:-latest}}"
|
||||
SECOND_TAG="${4:-}"
|
||||
PLATFORM="${PLATFORM:-linux/amd64}"
|
||||
|
||||
if [[ -z "$REGISTRY" || -z "$IMAGE_PATH" ]]; then
|
||||
cat <<USAGE
|
||||
if [[ -z "${REGISTRY}" || -z "${IMAGE_PATH}" ]]; then
|
||||
cat <<'EOF'
|
||||
Verwendung:
|
||||
$0 <registry> <owner/image> [tag]
|
||||
./build-and-push-gitea.sh <registry> <owner/image> [tag] [zweiter-tag]
|
||||
|
||||
Beispiel:
|
||||
$0 git.example.de meinuser/fixfin latest
|
||||
./build-and-push-gitea.sh git.d-razz.de michael/fixfin V3.3 latest
|
||||
|
||||
Alternativ per Umgebungsvariablen:
|
||||
GITEA_REGISTRY=git.example.de \\
|
||||
GITEA_IMAGE_PATH=meinuser/fixfin \\
|
||||
IMAGE_TAG=latest \\
|
||||
$0
|
||||
|
||||
Vor dem ersten Push anmelden:
|
||||
sudo docker login <registry>
|
||||
USAGE
|
||||
Einmalige Registry-Anmeldung:
|
||||
docker login git.d-razz.de -u michael
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command -v docker >/dev/null 2>&1 || { echo "Docker fehlt."; exit 1; }
|
||||
docker buildx version >/dev/null 2>&1 || { echo "Docker Buildx fehlt."; exit 1; }
|
||||
command -v docker >/dev/null 2>&1 || {
|
||||
echo "FEHLER: Docker fehlt."
|
||||
exit 1
|
||||
}
|
||||
|
||||
docker info >/dev/null 2>&1 || {
|
||||
echo "FEHLER: Docker ist für den aktuellen Benutzer nicht erreichbar."
|
||||
echo "Teste: docker ps"
|
||||
exit 1
|
||||
}
|
||||
|
||||
docker buildx version >/dev/null 2>&1 || {
|
||||
echo "FEHLER: Docker Buildx fehlt."
|
||||
exit 1
|
||||
}
|
||||
|
||||
FULL_IMAGE="${REGISTRY}/${IMAGE_PATH}:${TAG}"
|
||||
|
||||
BUILD_ARGS=(
|
||||
--platform "${PLATFORM}"
|
||||
--tag "${FULL_IMAGE}"
|
||||
)
|
||||
|
||||
if [[ -n "${SECOND_TAG}" ]]; then
|
||||
BUILD_ARGS+=(--tag "${REGISTRY}/${IMAGE_PATH}:${SECOND_TAG}")
|
||||
fi
|
||||
|
||||
BUILD_ARGS+=(--push .)
|
||||
|
||||
echo
|
||||
echo ">>> Build + Push"
|
||||
echo "Image: ${FULL_IMAGE}"
|
||||
if [[ -n "${SECOND_TAG}" ]]; then
|
||||
echo "Zusatz: ${REGISTRY}/${IMAGE_PATH}:${SECOND_TAG}"
|
||||
fi
|
||||
echo "Platform: ${PLATFORM}"
|
||||
echo
|
||||
|
||||
cd "$ROOT"
|
||||
docker buildx build \
|
||||
--platform "$PLATFORM" \
|
||||
--tag "$FULL_IMAGE" \
|
||||
--push \
|
||||
.
|
||||
cd "${ROOT}"
|
||||
|
||||
docker buildx build "${BUILD_ARGS[@]}"
|
||||
|
||||
echo
|
||||
echo "Fertig: ${FULL_IMAGE}"
|
||||
echo "Fertig:"
|
||||
echo " ${FULL_IMAGE}"
|
||||
if [[ -n "${SECOND_TAG}" ]]; then
|
||||
echo " ${REGISTRY}/${IMAGE_PATH}:${SECOND_TAG}"
|
||||
fi
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fixfin",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
+44
-40
@@ -11,22 +11,23 @@ Verwendung:
|
||||
|
||||
Beispiele:
|
||||
./release.sh 3.3
|
||||
./release.sh v3.3
|
||||
./release.sh V3.3
|
||||
./release.sh 3.3.1
|
||||
|
||||
Ablauf:
|
||||
1. git add -A
|
||||
2. git commit -m "V<version>"
|
||||
3. git push über origin/SSH
|
||||
4. build-and-push-gitea.sh
|
||||
5. Docker-Image mit Versionstag pushen
|
||||
6. denselben Stand zusätzlich als :latest markieren
|
||||
3. git push origin <branch>
|
||||
4. Docker-Build + Push nach Gitea
|
||||
5. Versionstag + latest
|
||||
|
||||
Voraussetzungen:
|
||||
- origin zeigt per SSH auf Gitea, z.B.
|
||||
git@git.d-razz.de:mboehmlaender/fixfin.git
|
||||
- Docker-Registry einmalig für root angemeldet:
|
||||
sudo docker login git.d-razz.de
|
||||
- Git-Push funktioniert über deinen konfigurierten origin-Remote.
|
||||
- Docker kann als aktueller Benutzer ausgeführt werden:
|
||||
docker ps
|
||||
- Einmalig bei der Gitea Registry anmelden:
|
||||
docker login git.d-razz.de -u michael
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -37,13 +38,12 @@ if [[ -z "${VERSION_RAW}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# V/v am Anfang entfernen und anschließend einheitlich als Vx.x verwenden.
|
||||
VERSION="${VERSION_RAW#V}"
|
||||
VERSION="${VERSION#v}"
|
||||
|
||||
if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
|
||||
echo "FEHLER: Ungültige Version '${VERSION_RAW}'."
|
||||
echo "Erwartet z.B. 3.3, V3.3 oder 3.3.1"
|
||||
echo "Erwartet z.B. 3.3, v3.3, V3.3 oder 3.3.1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -55,6 +55,23 @@ command -v git >/dev/null 2>&1 || {
|
||||
exit 1
|
||||
}
|
||||
|
||||
command -v docker >/dev/null 2>&1 || {
|
||||
echo "FEHLER: docker wurde nicht gefunden."
|
||||
exit 1
|
||||
}
|
||||
|
||||
docker info >/dev/null 2>&1 || {
|
||||
echo "FEHLER: Docker ist für den aktuellen Benutzer nicht erreichbar."
|
||||
echo
|
||||
echo "Teste:"
|
||||
echo " docker ps"
|
||||
echo
|
||||
echo "Falls Permission denied:"
|
||||
echo " sudo usermod -aG docker $USER"
|
||||
echo " newgrp docker"
|
||||
exit 1
|
||||
}
|
||||
|
||||
[[ -x "${BUILD_SCRIPT}" ]] || {
|
||||
echo "FEHLER: ${BUILD_SCRIPT} fehlt oder ist nicht ausführbar."
|
||||
echo "Fix:"
|
||||
@@ -76,30 +93,30 @@ if [[ -z "${REMOTE}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Registry-Host und owner/repo automatisch aus einem SSH-Remote ableiten.
|
||||
# Unterstützt:
|
||||
# git@host:owner/repo.git
|
||||
# ssh://git@host/owner/repo.git
|
||||
# owner/repo + Registry aus SSH- oder HTTPS-Remote ableiten.
|
||||
# Unterstützt z.B.:
|
||||
# git@git.d-razz.de:michael/fixfin.git
|
||||
# ssh://git@git.d-razz.de/michael/fixfin.git
|
||||
# https://git.d-razz.de/michael/fixfin.git
|
||||
if [[ "${REMOTE}" =~ ^git@([^:]+):(.+)$ ]]; then
|
||||
REGISTRY="${BASH_REMATCH[1]}"
|
||||
IMAGE_PATH="${BASH_REMATCH[2]}"
|
||||
elif [[ "${REMOTE}" =~ ^ssh://git@([^/]+)/(.+)$ ]]; then
|
||||
REGISTRY="${BASH_REMATCH[1]}"
|
||||
IMAGE_PATH="${BASH_REMATCH[2]}"
|
||||
elif [[ "${REMOTE}" =~ ^https?://([^/]+)/(.+)$ ]]; then
|
||||
REGISTRY="${BASH_REMATCH[1]}"
|
||||
IMAGE_PATH="${BASH_REMATCH[2]}"
|
||||
else
|
||||
echo "FEHLER: origin ist kein unterstützter SSH-Remote:"
|
||||
echo "FEHLER: origin konnte nicht ausgewertet werden:"
|
||||
echo " ${REMOTE}"
|
||||
echo
|
||||
echo "Erwartet z.B.:"
|
||||
echo " git@git.d-razz.de:mboehmlaender/fixfin.git"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IMAGE_PATH="${IMAGE_PATH%.git}"
|
||||
|
||||
# Sicherheitsprüfung
|
||||
if [[ ! "${IMAGE_PATH}" =~ ^[^/]+/[^/]+$ ]]; then
|
||||
echo "FEHLER: owner/repo konnte aus dem Remote nicht sicher ermittelt werden:"
|
||||
echo "FEHLER: owner/repo konnte nicht sicher ermittelt werden:"
|
||||
echo " ${IMAGE_PATH}"
|
||||
exit 1
|
||||
fi
|
||||
@@ -120,10 +137,10 @@ echo "Repository : ${REMOTE}"
|
||||
echo "Branch : ${BRANCH}"
|
||||
echo "Registry : ${REGISTRY}"
|
||||
echo "Image : ${REGISTRY}/${IMAGE_PATH}"
|
||||
echo "Tag : ${IMAGE_TAG} + latest"
|
||||
echo "Tags : ${IMAGE_TAG}, latest"
|
||||
echo
|
||||
|
||||
echo ">>> Prüfe SSH-Zugriff auf origin ..."
|
||||
echo ">>> Prüfe Git-Remote ..."
|
||||
git ls-remote origin HEAD >/dev/null
|
||||
|
||||
echo ">>> Stage Änderungen ..."
|
||||
@@ -139,28 +156,16 @@ fi
|
||||
echo ">>> Commit ${COMMIT_MESSAGE} ..."
|
||||
git commit -m "${COMMIT_MESSAGE}"
|
||||
|
||||
echo ">>> Git Push über SSH ..."
|
||||
echo ">>> Git Push ..."
|
||||
git push origin "${BRANCH}"
|
||||
|
||||
echo
|
||||
echo ">>> Docker Registry Authentifizierung prüfen ..."
|
||||
# sudo wird hier einmal initialisiert. Docker-Registry-Credentials liegen
|
||||
# danach dauerhaft in der Docker-Konfiguration des root-Benutzers, sofern
|
||||
# zuvor 'sudo docker login <registry>' ausgeführt wurde.
|
||||
sudo -v
|
||||
|
||||
echo
|
||||
echo ">>> Build + Push ${IMAGE_TAG} ..."
|
||||
sudo "${BUILD_SCRIPT}" \
|
||||
echo ">>> Docker Build + Push ..."
|
||||
"${BUILD_SCRIPT}" \
|
||||
"${REGISTRY}" \
|
||||
"${IMAGE_PATH}" \
|
||||
"${IMAGE_TAG}"
|
||||
|
||||
echo
|
||||
echo ">>> Markiere denselben Image-Stand zusätzlich als latest ..."
|
||||
sudo docker buildx imagetools create \
|
||||
--tag "${REGISTRY}/${IMAGE_PATH}:latest" \
|
||||
"${REGISTRY}/${IMAGE_PATH}:${IMAGE_TAG}"
|
||||
"${IMAGE_TAG}" \
|
||||
"latest"
|
||||
|
||||
echo
|
||||
echo "============================================================"
|
||||
@@ -173,4 +178,3 @@ echo
|
||||
echo "Docker:"
|
||||
echo " ${REGISTRY}/${IMAGE_PATH}:${IMAGE_TAG}"
|
||||
echo " ${REGISTRY}/${IMAGE_PATH}:latest"
|
||||
echo
|
||||
|
||||
+1
-1
@@ -129,7 +129,7 @@ function Dashboard({totals,onToggle}){
|
||||
<section className="toggle-panel"><div><strong>Periodische Kosten in Monats-Saldo einrechnen</strong><span>Jährliche, halbjährliche und vierteljährliche Beträge werden auf einen Monatswert heruntergerechnet.</span></div><button className={`switch ${totals.includePeriodic?'on':''}`} onClick={onToggle} role="switch" aria-checked={totals.includePeriodic}><span/></button></section>
|
||||
<section className="summary-grid"><Metric label="Eingänge / Monat" value={totals.income} positive/><Metric label="Ausgänge / Monat" value={totals.expense} negative/><Metric label="Periodische Rücklage / Monat" value={totals.periodicExpenseReserve}/><Metric label="Monatlicher Gesamtsaldo" value={totals.balance} positive={totals.balance>=0} negative={totals.balance<0} featured/></section>
|
||||
{!totals.includePeriodic&&totals.periodicExpenseReserve>0&&<div className="info-note">Periodische Ausgaben von rechnerisch <strong>{formatMoney(totals.periodicExpenseReserve)} / Monat</strong> sind derzeit nicht im Saldo enthalten.</div>}
|
||||
<section className="panel"><div className="section-head"><div><h2>Saldo je Konto</h2><p>Monatliche Fixwerte plus optional heruntergerechnete periodische Werte.</p></div></div>{totals.accounts.length===0?<Empty text="Lege zuerst ein Konto an."/>:<div className="account-balance-list">{totals.accounts.map(s=><details className="account-balance-card" key={s.account.id}><summary><div className="account-balance-title"><h3>{s.account.name}</h3><span>monatlicher Fix-Saldo</span></div><strong className={`account-balance-value ${s.balance>=0?'good-text':'bad-text'}`}>{s.balance>=0?'+':''}{formatMoney(s.balance)}</strong><span className="accordion-chevron" aria-hidden="true">⌄</span></summary><div className="account-balance-details"><div className="mini-grid"><div><span>Eingänge</span><strong className="good-text">+{formatMoney(s.income)}</strong></div><div><span>Ausgänge</span><strong className="bad-text">−{formatMoney(s.expense)}</strong></div><div><span>Transfers rein</span><strong className="good-text">+{formatMoney(s.transferIn)}</strong></div><div><span>Transfers raus</span><strong className="bad-text">−{formatMoney(s.transferOut)}</strong></div></div></div></details>)}</div>}</section>
|
||||
<section className="panel"><div className="section-head"><div><h2>Saldo je Konto</h2><p>Monatliche Fixwerte plus optional heruntergerechnete periodische Werte.</p></div></div>{totals.accounts.length===0?<Empty text="Lege zuerst ein Konto an."/>:<div className="account-balance-list">{totals.accounts.map(s=><details className="account-balance-card" key={s.account.id}><summary><div className="account-balance-title"><h3>{s.account.name}</h3><span>monatlicher Fix-Saldo</span></div><div className="account-balance-right"><strong className={`account-balance-value ${s.balance>=0?'good-text':'bad-text'}`}>{s.balance>=0?'+':''}{formatMoney(s.balance)}</strong><span className="accordion-chevron" aria-hidden="true">⌄</span></div></summary><div className="account-balance-details"><div className="mini-grid"><div><span>Eingänge</span><strong className="good-text">+{formatMoney(s.income)}</strong></div><div><span>Ausgänge</span><strong className="bad-text">−{formatMoney(s.expense)}</strong></div><div><span>Transfers rein</span><strong className="good-text">+{formatMoney(s.transferIn)}</strong></div><div><span>Transfers raus</span><strong className="bad-text">−{formatMoney(s.transferOut)}</strong></div></div></div></details>)}</div>}</section>
|
||||
<section className="panel total-panel"><div><span className="eyebrow">GESAMT</span><h2>Monatlich nach Fixkosten übrig</h2><p>Interne Transfers verändern den Gesamtsaldo nicht.</p></div><div className={`grand-total ${totals.balance>=0?'good-text':'bad-text'}`}>{formatMoney(totals.balance)}</div></section>
|
||||
</div>;
|
||||
}
|
||||
|
||||
+19
-9
@@ -187,7 +187,7 @@ code { color:#b8c3ff; }
|
||||
.year-month-list { gap:9px; }
|
||||
.year-month-card summary { grid-template-columns:1fr 22px; gap:9px 10px; padding:13px 12px; }
|
||||
.year-month-heading { grid-column:1; }
|
||||
.accordion-chevron { grid-column:2; grid-row:1; align-self:center; }
|
||||
.year-month-card .accordion-chevron { grid-column:2; grid-row:1; align-self:center; }
|
||||
.year-month-basics { grid-column:1 / -1; grid-row:2; gap:6px; }
|
||||
.year-month-basics div { background:#101823; border:1px solid #202c3c; border-radius:9px; padding:8px; }
|
||||
.year-month-basics strong { font-size:12px; }
|
||||
@@ -213,7 +213,7 @@ code { color:#b8c3ff; }
|
||||
.mobile-nav { grid-template-columns:repeat(7,minmax(50px,1fr)); }
|
||||
}
|
||||
|
||||
/* v3.2: kompakte Kontosalden als Akkordeon */
|
||||
/* v3.4: robuste Konto-Saldo-Anzeige auf der Übersicht */
|
||||
.account-balance-list {
|
||||
display:grid;
|
||||
gap:10px;
|
||||
@@ -231,16 +231,16 @@ code { color:#b8c3ff; }
|
||||
.account-balance-card summary {
|
||||
list-style:none;
|
||||
cursor:pointer;
|
||||
display:grid;
|
||||
grid-template-columns:minmax(0,1fr) auto 22px;
|
||||
gap:14px;
|
||||
display:flex;
|
||||
gap:12px;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
min-height:68px;
|
||||
padding:12px 15px;
|
||||
user-select:none;
|
||||
}
|
||||
.account-balance-card summary::-webkit-details-marker { display:none; }
|
||||
.account-balance-title { min-width:0; }
|
||||
.account-balance-title { min-width:0; overflow:hidden; flex:1 1 auto; }
|
||||
.account-balance-title h3 {
|
||||
margin:0;
|
||||
overflow:hidden;
|
||||
@@ -253,11 +253,21 @@ code { color:#b8c3ff; }
|
||||
color:var(--muted);
|
||||
font-size:11px;
|
||||
}
|
||||
.account-balance-right {
|
||||
flex:0 0 auto;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:flex-end;
|
||||
gap:8px;
|
||||
min-width:max-content;
|
||||
}
|
||||
.account-balance-value {
|
||||
display:block;
|
||||
font-size:19px;
|
||||
font-weight:850;
|
||||
letter-spacing:-.035em;
|
||||
white-space:nowrap;
|
||||
flex:0 0 auto;
|
||||
}
|
||||
.account-balance-card[open] .accordion-chevron { transform:rotate(180deg); }
|
||||
.account-balance-details {
|
||||
@@ -272,15 +282,15 @@ code { color:#b8c3ff; }
|
||||
@media (max-width:760px) {
|
||||
.account-balance-list { gap:8px; }
|
||||
.account-balance-card summary {
|
||||
grid-template-columns:minmax(0,1fr) auto 18px;
|
||||
gap:8px;
|
||||
min-height:58px;
|
||||
padding:9px 10px;
|
||||
}
|
||||
.account-balance-title h3 { font-size:14px; }
|
||||
.account-balance-title span { font-size:9px; }
|
||||
.account-balance-value { font-size:16px; }
|
||||
.account-balance-card .accordion-chevron { font-size:17px; }
|
||||
.account-balance-value { font-size:15px; letter-spacing:-.025em; }
|
||||
.account-balance-right { gap:6px; }
|
||||
.account-balance-card .accordion-chevron { font-size:17px; flex:0 0 auto; }
|
||||
.account-balance-details { padding:0 10px 10px; }
|
||||
.account-balance-details .mini-grid {
|
||||
gap:8px;
|
||||
|
||||
Reference in New Issue
Block a user