feat(release): Packaging für Zorin OS 18 (.deb & .AppImage), In-App Gitea Updater und CI/CD Workflow
Build & Publish Release / Build Packages & Publish to Gitea (push) Successful in 5m26s
Build & Publish Release / Build Packages & Publish to Gitea (push) Successful in 5m26s
This commit is contained in:
1 parent
8a25b1e2f3
commit
dfb5a64917
17 files changed
+1698
-9
No files matched your search
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# Haupt-Release-Build-Skript für ink-notes (Zorin OS 18 / Linux)
|
||||
# Erstellt .deb, .AppImage und SHA256-Prüfsummen in target/
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
cd "${ROOT_DIR}"
|
||||
|
||||
VERSION=$(grep -m1 '^version = ' Cargo.toml | cut -d '"' -f2)
|
||||
DEB_NAME="ink-notes_${VERSION}_amd64.deb"
|
||||
APPIMAGE_NAME="ink-notes_${VERSION}_x86_64.AppImage"
|
||||
CHECKSUM_FILE="SHA256SUMS.txt"
|
||||
|
||||
echo "======================================================================"
|
||||
echo "Starte Release-Build für ink-notes v${VERSION}"
|
||||
echo "======================================================================"
|
||||
|
||||
# 1. Debian-Paket bauen (.deb)
|
||||
"${SCRIPT_DIR}/package-deb.sh"
|
||||
|
||||
# 2. AppImage bauen (.AppImage)
|
||||
"${SCRIPT_DIR}/package-appimage.sh"
|
||||
|
||||
# 3. Checksums berechnen
|
||||
echo "=== Berechne SHA256-Prüfsummen in target/${CHECKSUM_FILE} ==="
|
||||
cd "${ROOT_DIR}/target"
|
||||
sha256sum "${DEB_NAME}" "${APPIMAGE_NAME}" > "${CHECKSUM_FILE}"
|
||||
|
||||
echo "======================================================================"
|
||||
echo "Release-Artefakte für v${VERSION} erfolgreich erstellt:"
|
||||
echo "======================================================================"
|
||||
ls -lh "${DEB_NAME}" "${APPIMAGE_NAME}" "${CHECKSUM_FILE}"
|
||||
echo ""
|
||||
echo "Inhalt von target/${CHECKSUM_FILE}:"
|
||||
cat "${CHECKSUM_FILE}"
|
||||
echo "======================================================================"
|
||||
@@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Erzeugt ein 256x256 PNG-Icon für ink-notes mit Python Standard-Bibliothek (ohne externe Module).
|
||||
"""
|
||||
|
||||
import math
|
||||
import os
|
||||
import struct
|
||||
import zlib
|
||||
|
||||
WIDTH = 256
|
||||
HEIGHT = 256
|
||||
|
||||
|
||||
def make_png(width, height, rgba_data):
|
||||
def chunk(tag, data):
|
||||
return (
|
||||
struct.pack(">I", len(data))
|
||||
+ tag
|
||||
+ data
|
||||
+ struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF)
|
||||
)
|
||||
|
||||
header = b"\x89PNG\r\n\x1a\n"
|
||||
ihdr = chunk(tag=b"IHDR", data=struct.pack(">IIBBBBB", width, height, 8, 6, 0, 0, 0))
|
||||
|
||||
raw = bytearray()
|
||||
for y in range(height):
|
||||
raw.append(0) # Filter type 0 (None)
|
||||
raw.extend(rgba_data[y * width * 4 : (y + 1) * width * 4])
|
||||
|
||||
idat = chunk(tag=b"IDAT", data=zlib.compress(bytes(raw), 9))
|
||||
iend = chunk(tag=b"IEND", data=b"")
|
||||
return header + ihdr + idat + iend
|
||||
|
||||
|
||||
def render_icon():
|
||||
buffer = bytearray(WIDTH * HEIGHT * 4)
|
||||
|
||||
def set_pixel(x, y, r, g, b, a):
|
||||
if 0 <= x < WIDTH and 0 <= y < HEIGHT:
|
||||
idx = (y * WIDTH + x) * 4
|
||||
src_a = a / 255.0
|
||||
dst_a = buffer[idx + 3] / 255.0
|
||||
out_a = src_a + dst_a * (1.0 - src_a)
|
||||
if out_a > 1e-5:
|
||||
out_r = (r * src_a + buffer[idx] * dst_a * (1.0 - src_a)) / out_a
|
||||
out_g = (
|
||||
g * src_a + buffer[idx + 1] * dst_a * (1.0 - src_a)
|
||||
) / out_a
|
||||
out_b = (
|
||||
b * src_a + buffer[idx + 2] * dst_a * (1.0 - src_a)
|
||||
) / out_a
|
||||
buffer[idx] = int(round(out_r))
|
||||
buffer[idx + 1] = int(round(out_g))
|
||||
buffer[idx + 2] = int(round(out_b))
|
||||
buffer[idx + 3] = int(round(out_a * 255.0))
|
||||
|
||||
# 1. Abgerundete Squircle-Kachel (Zorin OS Stil)
|
||||
cx, cy = WIDTH / 2.0, HEIGHT / 2.0
|
||||
half_size = 114.0
|
||||
radius = 52.0
|
||||
|
||||
for y in range(HEIGHT):
|
||||
for x in range(WIDTH):
|
||||
dx = abs(x - cx)
|
||||
dy = abs(y - cy)
|
||||
# Distance from rounded box
|
||||
qx = max(0.0, dx - (half_size - radius))
|
||||
qy = max(0.0, dy - (half_size - radius))
|
||||
dist = math.sqrt(qx * qx + qy * qy)
|
||||
|
||||
if dx <= half_size and dy <= half_size and dist <= radius:
|
||||
# Gradient von Dunkelblau (#1e2638) nach Nachtblau (#0f1422)
|
||||
t = (x + y) / (WIDTH + HEIGHT)
|
||||
r = int(30 * (1 - t) + 12 * t)
|
||||
g = int(38 * (1 - t) + 16 * t)
|
||||
b = int(58 * (1 - t) + 28 * t)
|
||||
|
||||
# Rand-Antialiasing
|
||||
edge_dist = radius - dist
|
||||
alpha = min(255, max(0, int(edge_dist * 255)))
|
||||
|
||||
# Subtiler Rahmen
|
||||
if edge_dist < 2.5:
|
||||
r, g, b = 60, 75, 110
|
||||
|
||||
set_pixel(x, y, r, g, b, alpha)
|
||||
|
||||
# 2. Subtile Gitterlinien
|
||||
for gy in range(80, 200, 30):
|
||||
for gx in range(40, 216):
|
||||
set_pixel(gx, gy, 140, 160, 190, 25)
|
||||
for gx in range(70, 200, 30):
|
||||
for gy in range(50, 216):
|
||||
set_pixel(gx, gy, 140, 160, 190, 25)
|
||||
|
||||
# 3. Tintenspur (Bézier-Kurve mit Glow)
|
||||
pts = []
|
||||
for t_i in range(150):
|
||||
t = t_i / 150.0
|
||||
# P0=(55, 195), P1=(90, 215), P2=(130, 190), P3=(120, 155), P4=(140, 110), P5=(170, 110)
|
||||
# 2 Teilkurven
|
||||
if t <= 0.5:
|
||||
u = t * 2.0
|
||||
bx = (
|
||||
(1 - u) ** 3 * 55
|
||||
+ 3 * (1 - u) ** 2 * u * 90
|
||||
+ 3 * (1 - u) * u**2 * 130
|
||||
+ u**3 * 120
|
||||
)
|
||||
by = (
|
||||
(1 - u) ** 3 * 195
|
||||
+ 3 * (1 - u) ** 2 * u * 215
|
||||
+ 3 * (1 - u) * u**2 * 190
|
||||
+ u**3 * 155
|
||||
)
|
||||
else:
|
||||
u = (t - 0.5) * 2.0
|
||||
bx = (
|
||||
(1 - u) ** 3 * 120
|
||||
+ 3 * (1 - u) ** 2 * u * 110
|
||||
+ 3 * (1 - u) * u**2 * 140
|
||||
+ u**3 * 170
|
||||
)
|
||||
by = (
|
||||
(1 - u) ** 3 * 155
|
||||
+ 3 * (1 - u) ** 2 * u * 120
|
||||
+ 3 * (1 - u) * u**2 * 95
|
||||
+ u**3 * 110
|
||||
)
|
||||
pts.append((bx, by))
|
||||
|
||||
for px, py in pts:
|
||||
for ox in range(-7, 8):
|
||||
for oy in range(-7, 8):
|
||||
d = math.sqrt(ox * ox + oy * oy)
|
||||
if d <= 7.0:
|
||||
a = int((1.0 - d / 7.0) * 110)
|
||||
set_pixel(int(px + ox), int(py + oy), 59, 130, 246, a)
|
||||
for ox in range(-3, 4):
|
||||
for oy in range(-3, 4):
|
||||
d = math.sqrt(ox * ox + oy * oy)
|
||||
if d <= 3.0:
|
||||
a = int((1.0 - d / 3.0) * 230)
|
||||
set_pixel(int(px + ox), int(py + oy), 147, 197, 253, a)
|
||||
|
||||
# 4. Füllfederhalter (diagonale Geometrie)
|
||||
# Stift-Mittelachse von (120, 55) nach (170, 110)
|
||||
pen_dir = (math.cos(math.radians(45)), math.sin(math.radians(45)))
|
||||
norm = (-pen_dir[1], pen_dir[0])
|
||||
|
||||
# Spitze bei (110, 195)
|
||||
tip_x, tip_y = 55.0, 195.0
|
||||
for l in range(0, 120):
|
||||
pos_x = tip_x + pen_dir[0] * l
|
||||
pos_y = tip_y - pen_dir[1] * l
|
||||
# Radius entlang des Stifts
|
||||
if l < 20:
|
||||
# Goldene Feder
|
||||
w = (l / 20.0) * 10.0
|
||||
color = (245, 158, 11) # Gold
|
||||
elif l < 32:
|
||||
# Griffzone
|
||||
w = 11.0
|
||||
color = (30, 41, 59) # Dunkelgrau
|
||||
else:
|
||||
# Schaft
|
||||
w = 12.0
|
||||
color = (59, 130, 246) # Blau
|
||||
|
||||
for s in range(int(-w), int(w) + 1):
|
||||
px = int(pos_x + norm[0] * s)
|
||||
py = int(pos_y + norm[1] * s)
|
||||
set_pixel(px, py, color[0], color[1], color[2], 255)
|
||||
|
||||
# 5. Glanzpunkt auf der Spitze
|
||||
for ox in range(-4, 5):
|
||||
for oy in range(-4, 5):
|
||||
d = math.sqrt(ox * ox + oy * oy)
|
||||
if d <= 4:
|
||||
set_pixel(int(tip_x + ox), int(tip_y + oy), 255, 255, 255, 255)
|
||||
|
||||
return make_png(WIDTH, HEIGHT, buffer)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.makedirs("assets", exist_ok=True)
|
||||
png_data = render_icon()
|
||||
with open("assets/ink-notes.png", "wb") as f:
|
||||
f.write(png_data)
|
||||
print("PNG-Icon erfolgreich generiert: assets/ink-notes.png (256x256)")
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# Baut ein portables .AppImage-Paket für ink-notes (Zorin OS 18 / Linux x86_64)
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
cd "${ROOT_DIR}"
|
||||
|
||||
# 1. Version aus Cargo.toml ermitteln
|
||||
VERSION=$(grep -m1 '^version = ' Cargo.toml | cut -d '"' -f2)
|
||||
PKG_NAME="ink-notes"
|
||||
ARCH="x86_64"
|
||||
APPIMAGE_NAME="${PKG_NAME}_${VERSION}_${ARCH}.AppImage"
|
||||
APPDIR="${ROOT_DIR}/target/AppDir"
|
||||
CACHE_DIR="${ROOT_DIR}/target/tools"
|
||||
APPIMAGETOOL="${CACHE_DIR}/appimagetool-x86_64.AppImage"
|
||||
|
||||
echo "=== [1/5] Stelle Release-Binary sicher (ink-notes v${VERSION}) ==="
|
||||
if [ ! -f "target/release/ink-notes" ]; then
|
||||
cargo build --release
|
||||
fi
|
||||
|
||||
# Sicherstellen, dass das PNG-Icon existiert
|
||||
if [ ! -f "assets/ink-notes.png" ]; then
|
||||
echo "Generiere PNG-Icon..."
|
||||
python3 scripts/generate_png_icon.py
|
||||
fi
|
||||
|
||||
echo "=== [2/5] Bereite AppDir-Struktur vor in ${APPDIR} ==="
|
||||
rm -rf "${APPDIR}"
|
||||
mkdir -p "${APPDIR}/usr/bin"
|
||||
mkdir -p "${APPDIR}/usr/share/applications"
|
||||
mkdir -p "${APPDIR}/usr/share/icons/hicolor/scalable/apps"
|
||||
mkdir -p "${APPDIR}/usr/share/icons/hicolor/256x256/apps"
|
||||
mkdir -p "${APPDIR}/usr/share/metainfo"
|
||||
|
||||
# Binaries & Assets kopieren
|
||||
cp "target/release/ink-notes" "${APPDIR}/usr/bin/ink-notes"
|
||||
chmod 755 "${APPDIR}/usr/bin/ink-notes"
|
||||
|
||||
cp "packaging/ink-notes.desktop" "${APPDIR}/usr/share/applications/ink-notes.desktop"
|
||||
cp "packaging/ink-notes.desktop" "${APPDIR}/ink-notes.desktop"
|
||||
|
||||
cp "assets/ink-notes.svg" "${APPDIR}/usr/share/icons/hicolor/scalable/apps/ink-notes.svg"
|
||||
cp "assets/ink-notes.png" "${APPDIR}/usr/share/icons/hicolor/256x256/apps/ink-notes.png"
|
||||
cp "assets/ink-notes.png" "${APPDIR}/ink-notes.png"
|
||||
ln -sf "ink-notes.png" "${APPDIR}/.DirIcon"
|
||||
|
||||
cp "packaging/ink-notes.metainfo.xml" "${APPDIR}/usr/share/metainfo/eu.pansi.ink-notes.metainfo.xml"
|
||||
|
||||
# AppRun Skript erstellen
|
||||
cat <<'EOF' > "${APPDIR}/AppRun"
|
||||
#!/usr/bin/env bash
|
||||
SELF_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
export PATH="${SELF_DIR}/usr/bin:${PATH}"
|
||||
export XDG_DATA_DIRS="${SELF_DIR}/usr/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
|
||||
export APPDIR="${SELF_DIR}"
|
||||
exec "${SELF_DIR}/usr/bin/ink-notes" "$@"
|
||||
EOF
|
||||
chmod 755 "${APPDIR}/AppRun"
|
||||
|
||||
echo "=== [3/5] Hole appimagetool (gecacht in ${CACHE_DIR}) ==="
|
||||
mkdir -p "${CACHE_DIR}"
|
||||
if [ ! -f "${APPIMAGETOOL}" ]; then
|
||||
echo "Lade appimagetool herunter..."
|
||||
curl -fsSL -o "${APPIMAGETOOL}" "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||
chmod +x "${APPIMAGETOOL}"
|
||||
fi
|
||||
|
||||
echo "=== [4/5] Erstelle AppImage mit appimagetool ==="
|
||||
# --appimage-extract-and-run erlaubt Ausführung auch ohne root / FUSE
|
||||
# NO_APPSTREAM=1 verhindert Build-Abbruch bei privaten/nicht-erreichbaren URLs im Metainfo-XML
|
||||
export NO_APPSTREAM=1
|
||||
ARCH="${ARCH}" "${APPIMAGETOOL}" --appimage-extract-and-run "${APPDIR}" "${ROOT_DIR}/target/${APPIMAGE_NAME}"
|
||||
|
||||
echo "=== [5/5] AppImage erfolgreich erstellt! ==="
|
||||
ls -lh "${ROOT_DIR}/target/${APPIMAGE_NAME}"
|
||||
echo "Pfad: ${ROOT_DIR}/target/${APPIMAGE_NAME}"
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# Baut ein natives .deb-Paket für Zorin OS 18 / Ubuntu / Debian
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
cd "${ROOT_DIR}"
|
||||
|
||||
# 1. Version aus Cargo.toml ermitteln
|
||||
VERSION=$(grep -m1 '^version = ' Cargo.toml | cut -d '"' -f2)
|
||||
PKG_NAME="ink-notes"
|
||||
ARCH="amd64"
|
||||
DEB_NAME="${PKG_NAME}_${VERSION}_${ARCH}.deb"
|
||||
STAGING_DIR="${ROOT_DIR}/target/deb_staging"
|
||||
|
||||
echo "=== [1/4] Baue Release-Binary mit Cargo (ink-notes v${VERSION}) ==="
|
||||
cargo build --release
|
||||
|
||||
# Sicherstellen, dass das PNG-Icon existiert
|
||||
if [ ! -f "assets/ink-notes.png" ]; then
|
||||
echo "Generiere PNG-Icon..."
|
||||
python3 scripts/generate_png_icon.py
|
||||
fi
|
||||
|
||||
echo "=== [2/4] Erstelle Paketstruktur in ${STAGING_DIR} ==="
|
||||
rm -rf "${STAGING_DIR}"
|
||||
mkdir -p "${STAGING_DIR}/DEBIAN"
|
||||
mkdir -p "${STAGING_DIR}/usr/bin"
|
||||
mkdir -p "${STAGING_DIR}/usr/share/applications"
|
||||
mkdir -p "${STAGING_DIR}/usr/share/icons/hicolor/scalable/apps"
|
||||
mkdir -p "${STAGING_DIR}/usr/share/icons/hicolor/256x256/apps"
|
||||
mkdir -p "${STAGING_DIR}/usr/share/metainfo"
|
||||
|
||||
# 2. Dateien kopieren
|
||||
cp "target/release/ink-notes" "${STAGING_DIR}/usr/bin/ink-notes"
|
||||
chmod 755 "${STAGING_DIR}/usr/bin/ink-notes"
|
||||
|
||||
cp "packaging/ink-notes.desktop" "${STAGING_DIR}/usr/share/applications/ink-notes.desktop"
|
||||
chmod 644 "${STAGING_DIR}/usr/share/applications/ink-notes.desktop"
|
||||
|
||||
cp "assets/ink-notes.svg" "${STAGING_DIR}/usr/share/icons/hicolor/scalable/apps/ink-notes.svg"
|
||||
chmod 644 "${STAGING_DIR}/usr/share/icons/hicolor/scalable/apps/ink-notes.svg"
|
||||
|
||||
cp "assets/ink-notes.png" "${STAGING_DIR}/usr/share/icons/hicolor/256x256/apps/ink-notes.png"
|
||||
chmod 644 "${STAGING_DIR}/usr/share/icons/hicolor/256x256/apps/ink-notes.png"
|
||||
|
||||
cp "packaging/ink-notes.metainfo.xml" "${STAGING_DIR}/usr/share/metainfo/eu.pansi.ink-notes.metainfo.xml"
|
||||
chmod 644 "${STAGING_DIR}/usr/share/metainfo/eu.pansi.ink-notes.metainfo.xml"
|
||||
|
||||
# 3. DEBIAN/control Datei generieren
|
||||
cat <<EOF > "${STAGING_DIR}/DEBIAN/control"
|
||||
Package: ${PKG_NAME}
|
||||
Version: ${VERSION}
|
||||
Architecture: ${ARCH}
|
||||
Maintainer: Harald <harald@pansi.eu>
|
||||
Installed-Size: $(du -ks "${STAGING_DIR}/usr" | cut -f1)
|
||||
Depends: libc6, libx11-6, libxcursor1, libxrandr2, libxi6, libxkbcommon0
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://farmersdog.pansi.eu/harald/ink-notes
|
||||
Description: Latenzoptimierte Vektor-Notizbuch- und Inking-App für Linux
|
||||
ink-notes kombiniert minimale Stylus-Latenz mit GPU-Hardwarebeschleunigung (wgpu),
|
||||
stufenlosem Viewport, Geometrie- und Formerkennung, Objekt-Transformationen,
|
||||
Multi-Dokumenten-Verwaltung und Vektor-PDF-Export.
|
||||
EOF
|
||||
|
||||
echo "=== [3/4] Schnüre .deb-Paket mit dpkg-deb ==="
|
||||
dpkg-deb --build --root-owner-group "${STAGING_DIR}" "${ROOT_DIR}/target/${DEB_NAME}"
|
||||
|
||||
echo "=== [4/4] Paket erfolgreich erstellt: target/${DEB_NAME} ==="
|
||||
ls -lh "${ROOT_DIR}/target/${DEB_NAME}"
|
||||
dpkg-deb -I "${ROOT_DIR}/target/${DEB_NAME}"
|
||||
echo "Fertig!"
|
||||
Executable
+140
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# Veröffentlicht ein Release auf dem Gitea-Server (gitea.pansi.eu)
|
||||
# Lädt .deb, .AppImage und SHA256SUMS.txt als Release-Assets hoch.
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
cd "${ROOT_DIR}"
|
||||
|
||||
GITEA_SERVER="${GITEA_SERVER:-https://gitea.pansi.eu}"
|
||||
REPO_OWNER="${REPO_OWNER:-harald}"
|
||||
REPO_NAME="${REPO_NAME:-ink-notes}"
|
||||
|
||||
# Token aus Argument oder Umgebungsvariablen ermitteln
|
||||
TOKEN="${1:-${INK_NOTES_GITEA_TOKEN:-${GITEA_TOKEN:-${GITHUB_TOKEN:-}}}}"
|
||||
|
||||
if [ -z "${TOKEN}" ]; then
|
||||
echo "FEHLER: Kein Gitea-API-Token angegeben."
|
||||
echo "Verwendung: $0 <GITEA_API_TOKEN>"
|
||||
echo "Oder setze die Umgebungsvariable INK_NOTES_GITEA_TOKEN."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$(grep -m1 '^version = ' Cargo.toml | cut -d '"' -f2)
|
||||
TAG_NAME="v${VERSION}"
|
||||
RELEASE_TITLE="ink-notes ${TAG_NAME}"
|
||||
|
||||
DEB_FILE="${ROOT_DIR}/target/ink-notes_${VERSION}_amd64.deb"
|
||||
APPIMAGE_FILE="${ROOT_DIR}/target/ink-notes_${VERSION}_x86_64.AppImage"
|
||||
CHECKSUMS_FILE="${ROOT_DIR}/target/SHA256SUMS.txt"
|
||||
|
||||
# Sicherstellen, dass die Artefakte existieren
|
||||
if [ ! -f "${DEB_FILE}" ] || [ ! -f "${APPIMAGE_FILE}" ] || [ ! -f "${CHECKSUMS_FILE}" ]; then
|
||||
echo "Artefakte nicht vollständig gefunden. Führe build-release.sh aus..."
|
||||
"${SCRIPT_DIR}/build-release.sh"
|
||||
fi
|
||||
|
||||
echo "======================================================================"
|
||||
echo "Veröffentliche Release ${TAG_NAME} auf ${GITEA_SERVER}/${REPO_OWNER}/${REPO_NAME}"
|
||||
echo "======================================================================"
|
||||
|
||||
RELEASE_BODY=$(cat <<EOF
|
||||
## ink-notes ${TAG_NAME}
|
||||
|
||||
Latenzoptimierte Vektor-Notizbuch- und Inking-Anwendung für Linux (Zorin OS 18 / Ubuntu 24.04).
|
||||
|
||||
### Highlights:
|
||||
- **Zwei-Stufen-Inking-Modell**: Kausale Echtzeit-Glättung via 1€-Filter & akausale Ecken-Erhaltung (Corner Preservation)
|
||||
- **Geometrie- und Formerkennung**: Geraden, Rechtecke, Kreise, Rauten, Polygone & Koordinatensysteme (Draw-and-Hold)
|
||||
- **Viewport**: Stufenloser Zoom (25% - 500%) und Panning
|
||||
- **Multi-Dokumenten-Manager**: Unbegrenzte Notizbücher mit atomarem Autosave
|
||||
- **In-App Updater**: Automatische Versionsprüfung und Aktualisierung via Gitea Releases
|
||||
- **Vektor-PDF-Export**: Verluste freie Ausgabe für Druck und Weitergabe
|
||||
|
||||
### Installation:
|
||||
- **Zorin OS / Ubuntu / Debian (.deb)**:
|
||||
\`\`\`bash
|
||||
sudo dpkg -i ink-notes_${VERSION}_amd64.deb
|
||||
\`\`\`
|
||||
- **Portables AppImage (Alle Linux-Distributionen)**:
|
||||
\`\`\`bash
|
||||
chmod +x ink-notes_${VERSION}_x86_64.AppImage
|
||||
./ink-notes_${VERSION}_x86_64.AppImage
|
||||
\`\`\`
|
||||
EOF
|
||||
)
|
||||
|
||||
# 1. Release via Gitea REST API erstellen (oder ID abfragen, falls bereits existent)
|
||||
CREATE_PAYLOAD=$(python3 -c "
|
||||
import json, sys
|
||||
data = {
|
||||
'tag_name': '${TAG_NAME}',
|
||||
'target_commitish': 'main',
|
||||
'name': '${RELEASE_TITLE}',
|
||||
'body': '''${RELEASE_BODY}''',
|
||||
'draft': False,
|
||||
'prerelease': False
|
||||
}
|
||||
print(json.dumps(data))
|
||||
")
|
||||
|
||||
API_BASE="${GITEA_SERVER}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}"
|
||||
|
||||
echo "Erstelle Release über Gitea API..."
|
||||
HTTP_RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \
|
||||
-X POST "${API_BASE}/releases" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-d "${CREATE_PAYLOAD}")
|
||||
|
||||
HTTP_STATUS=$(echo "${HTTP_RESPONSE}" | grep "HTTP_STATUS:" | cut -d':' -f2)
|
||||
RESPONSE_BODY=$(echo "${HTTP_RESPONSE}" | sed '/HTTP_STATUS:/d')
|
||||
|
||||
RELEASE_ID=""
|
||||
if [ "${HTTP_STATUS}" -eq 201 ]; then
|
||||
RELEASE_ID=$(echo "${RESPONSE_BODY}" | python3 -c "import sys, json; print(json.load(sys.stdin)['id'])")
|
||||
echo "Release erfolgreich angelegt (ID: ${RELEASE_ID})!"
|
||||
else
|
||||
echo "Release existiert womöglich bereits (Status ${HTTP_STATUS}). Suche bestehendes Release..."
|
||||
EXISTING_RELEASE=$(curl -fsSL \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/releases/tags/${TAG_NAME}" || true)
|
||||
if [ -n "${EXISTING_RELEASE}" ]; then
|
||||
RELEASE_ID=$(echo "${EXISTING_RELEASE}" | python3 -c "import sys, json; print(json.load(sys.stdin).get('id', ''))")
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${RELEASE_ID}" ]; then
|
||||
echo "FEHLER: Konnte Release nicht erstellen oder abfragen. Serverantwort:"
|
||||
echo "${RESPONSE_BODY}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Assets hochladen
|
||||
upload_asset() {
|
||||
local file_path="$1"
|
||||
local filename="$(basename "${file_path}")"
|
||||
echo "Lade Asset hoch: ${filename}..."
|
||||
curl -fsSL \
|
||||
-X POST "${API_BASE}/releases/${RELEASE_ID}/assets?name=${filename}" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary "@${file_path}" > /dev/null
|
||||
echo "✓ ${filename} erfolgreich hochgeladen."
|
||||
}
|
||||
|
||||
upload_asset "${DEB_FILE}"
|
||||
upload_asset "${APPIMAGE_FILE}"
|
||||
upload_asset "${CHECKSUMS_FILE}"
|
||||
|
||||
echo "======================================================================"
|
||||
echo "Release ${TAG_NAME} erfolgreich veröffentlicht!"
|
||||
echo "URL: ${GITEA_SERVER}/${REPO_OWNER}/${REPO_NAME}/releases/tag/${TAG_NAME}"
|
||||
echo "======================================================================"
|
||||
Reference in new issue
Block a user