Add initial run log file to track execution details

This commit is contained in:
2026-07-11 16:30:36 +02:00
parent e4d4c4eb9a
commit ff634a328a
6 changed files with 2134 additions and 14 deletions
+12
View File
@@ -49,6 +49,18 @@ Siehe `webstatic.example/webstatic.json` und `docs/getting-started.md`.
./install.sh # -> /opt/GitCover/webstatic, Symlink /usr/local/bin/webstatic ./install.sh # -> /opt/GitCover/webstatic, Symlink /usr/local/bin/webstatic
``` ```
## Version & Update
```bash
webstatic --version # Assembly-Version + Git-Commit des Installations-Repos
webstatic update # Selbstaktualisierung (Clone-Installation): lokale
# Änderungen werden gestasht, git pull, gemerged, neu gebaut
```
`webstatic update` setzt eine Clone-Installation voraus (siehe `install.sh`,
Default `WEBSTATIC_INSTALL_MODE=clone`). Bei einer Copy-Installation erfolgt das
Update via erneutem `./install.sh`.
## Machine Translation (optional) ## Machine Translation (optional)
```bash ```bash
+6
View File
@@ -0,0 +1,6 @@
=== Build Started: 2026-07-11 16:11:56.657 ===
ENV: development
BASE_URL: http://localhost:8000
CDN_ASSETS: /assets
ROOT: /mnt/brx5/work/OSS/build_webstatic
Vendored Executable
+1887
View File
File diff suppressed because it is too large Load Diff
+57 -13
View File
@@ -1,26 +1,59 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# GCBoK.WebStaticBuilder — Installationsskript # GCBoK.WebStaticBuilder — Installationsskript
# Installiert das Tool nach /opt/GitCover/webstatic und legt einen Symlink # Installiert das Tool per-user (Default: ~/.local/share/GitCover/webstatic) und
# `webstatic` nach /usr/local/bin. Idempotent. # legt einen ausfuehrbaren Wrapper `webstatic` nach ~/.local/bin. Idempotent.
# Kein sudo noetig (Spiegelbild des hermes-agent-Update-Konzepts: user-owned
# Installationsziel -> `webstatic update` laeuft als normaler User).
# #
# Modi (WEBSTATIC_INSTALL_MODE): # Modi (WEBSTATIC_INSTALL_MODE):
# clone (Default) — klont das Quell-Repo inkl. .git nach /opt/GitCover/webstatic. # clone (Default) — klont das Quell-Repo inkl. .git nach INSTALL_DIR.
# Ermoeglicht spaeter `git pull` + lokale Anpassungen stashen/mergen # Ermoeglicht spaeter `git pull` + lokale Anpassungen stashen/mergen
# (Hermes-Update-Konzept). Das Ziel ist ein echtes Git-Repo. # (Hermes-Update-Konzept). Das Ziel ist ein echtes Git-Repo.
# copy — kopiert nur das gebaute Binary + Beispiel + Docs (kein .git). # copy — kopiert nur das gebaute Binary + Beispiel + Docs (kein .git).
# #
# Hinweis: /opt ist root-owned. Das Script sinnvollerweise als root ausfuehren, # Umgebungsvariablen (optional):
# z.B. `sudo env PATH="$PATH" ./install.sh`, damit `dotnet` im PATH bleibt. # WEBSTATIC_INSTALL_MODE clone|copy (Default: clone)
# WEBSTATIC_INSTALL_DIR Zielverzeichnis
# (Default: $XDG_DATA_HOME/GitCover/webstatic bzw.
# ~/.local/share/GitCover/webstatic)
# WEBSTATIC_BIN_DIR Verzeichnis fuer den Launcher
# (Default: $XDG_BIN_HOME bzw. ~/.local/bin;
# nur bei System-Install fallback /usr/local/bin via sudo)
#
# System-weite Installation nach /opt/GitCover/webstatic (root-owned):
# sudo env PATH="$PATH" \
# WEBSTATIC_INSTALL_DIR=/opt/GitCover/webstatic \
# WEBSTATIC_BIN_DIR=/usr/local/bin ./install.sh
set -euo pipefail set -euo pipefail
INSTALL_MODE="${WEBSTATIC_INSTALL_MODE:-clone}" INSTALL_MODE="${WEBSTATIC_INSTALL_MODE:-clone}"
INSTALL_DIR="${WEBSTATIC_INSTALL_DIR:-/opt/GitCover/webstatic}" INSTALL_DIR="${WEBSTATIC_INSTALL_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/GitCover/webstatic}"
BIN_LINK="/usr/local/bin/webstatic" BIN_DIR="${WEBSTATIC_BIN_DIR:-${XDG_BIN_HOME:-$HOME/.local/bin}}"
BIN_LINK="$BIN_DIR/webstatic"
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "== GCBoK.WebStaticBuilder Installer ==" echo "== GCBoK.WebStaticBuilder Installer =="
echo "Modus: $INSTALL_MODE" echo "Modus: $INSTALL_MODE"
echo "Installationsziel: $INSTALL_DIR" echo "Installationsziel: $INSTALL_DIR"
echo "Launcher: $BIN_LINK"
# 0) Update-Modus (--update)
if [ "${1:-}" = "--update" ]; then
echo "== Update =="
if [ -d "$INSTALL_DIR/.git" ]; then
cd "$INSTALL_DIR"
[ -n "$(git status --porcelain)" ] && git stash
git pull
{ [ -n "$(git stash list)" ] && git stash pop; } || true
dotnet build "$INSTALL_DIR/GCBoK.WebStaticBuilder.csproj" -c Release -v minimal
else
dotnet build "$REPO_DIR/GCBoK.WebStaticBuilder.csproj" -c Release -v minimal
rm -rf "$INSTALL_DIR/bin"
cp -r "$REPO_DIR/bin/Release/net10.0/." "$INSTALL_DIR/"
fi
echo "Update abgeschlossen."
exit 0
fi
# 1) .NET 10 pruefen # 1) .NET 10 pruefen
if ! command -v dotnet >/dev/null 2>&1; then if ! command -v dotnet >/dev/null 2>&1; then
@@ -33,6 +66,7 @@ echo "Gefunden: dotnet $(dotnet --version 2>/dev/null || echo unbekannt)"
# 2) Quelle beschaffen # 2) Quelle beschaffen
if [ "$INSTALL_MODE" = "clone" ]; then if [ "$INSTALL_MODE" = "clone" ]; then
echo "== Clone-Modus: bereite $INSTALL_DIR vor ==" echo "== Clone-Modus: bereite $INSTALL_DIR vor =="
mkdir -p "$(dirname "$INSTALL_DIR")"
if [ -d "$INSTALL_DIR/.git" ]; then if [ -d "$INSTALL_DIR/.git" ]; then
echo "Bereits ein Git-Repo — ueberspringe Clone (git pull ist beim Update noetig)." echo "Bereits ein Git-Repo — ueberspringe Clone (git pull ist beim Update noetig)."
else else
@@ -70,13 +104,22 @@ fi
echo "== Baue Release ==" echo "== Baue Release =="
dotnet build "$BUILD_PROJECT" -c Release -v minimal dotnet build "$BUILD_PROJECT" -c Release -v minimal
# 4) Symlink # 4) Ausfuehrbare Wrapper-Datei (kein direkter Symlink auf die .dll —
echo "== Verknuepfe $BIN_LINK -> $DLL ==" # eine .dll ist unter Linux nicht ausfuehrbar; der Wrapper ruft dotnet auf)
mkdir -p "$BIN_DIR" 2>/dev/null || sudo mkdir -p "$BIN_DIR"
echo "== Erzeuge Wrapper $BIN_LINK -> $DLL =="
WRAPPER_CONTENT="#!/usr/bin/env bash
exec dotnet \"$DLL\" \"\$@\"
"
if [ -w "$(dirname "$BIN_LINK")" ]; then if [ -w "$(dirname "$BIN_LINK")" ]; then
ln -sf "$DLL" "$BIN_LINK" rm -f "$BIN_LINK"
printf '%s' "$WRAPPER_CONTENT" > "$BIN_LINK"
chmod +x "$BIN_LINK"
else else
echo "Hinweis: $BIN_LINK benoetigt Schreibrechte; erstelle Link via sudo." echo "Hinweis: $BIN_LINK benoetigt Schreibrechte; erstelle via sudo."
sudo ln -sf "$DLL" "$BIN_LINK" rm -f "$BIN_LINK" 2>/dev/null || sudo rm -f "$BIN_LINK"
printf '%s' "$WRAPPER_CONTENT" | sudo tee "$BIN_LINK" >/dev/null
sudo chmod +x "$BIN_LINK"
fi fi
echo "" echo ""
@@ -85,4 +128,5 @@ echo "Beispiel-Seite erzeugen: webstatic init mysite && cd mysite && webstatic b
echo "GCBoK bauen: webstatic build --root www --env development" echo "GCBoK bauen: webstatic build --root www --env development"
echo "Umgebungsvariable (optional): WEBSTATIC_BUILDER=$DLL" echo "Umgebungsvariable (optional): WEBSTATIC_BUILDER=$DLL"
echo "" echo ""
echo "Update (Clone-Modus): cd $INSTALL_DIR && git stash && git pull && git stash pop && dotnet build -c Release" echo "Update (Clone-Modus, ohne sudo): webstatic update"
echo " (alternativ: cd $INSTALL_DIR && git stash && git pull && git stash pop && dotnet build -c Release)"
+2
View File
@@ -0,0 +1,2 @@
=== Run Started: 2026-07-11 16:11:56.696 ===
+170 -1
View File
@@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Net; using System.Net;
using System.Text; using System.Text;
using GCBoK.WebStaticBuilder; using GCBoK.WebStaticBuilder;
@@ -45,6 +46,30 @@ if (Val("--cdn") is { } cdn) { /* applied to chosen env below */ }
Directories.Init(cfg, root); Directories.Init(cfg, root);
// Help
if (argsList.Contains("--help") || argsList.Contains("-h") || argsList.Contains("help"))
{
PrintHelp();
return;
}
// Version
if (argsList.Contains("--version"))
{
var v = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
Console.WriteLine($"GCBoK.WebStaticBuilder {v?.ToString() ?? "0.0.0"}");
var hash = GitShortHash(AppContext.BaseDirectory);
if (hash != null) Console.WriteLine($"commit: {hash}");
return;
}
// Self-update (Hermes-Konzept: lokale Aenderungen stashen, pull, mergen, rebuild)
if (argsList.Contains("update"))
{
UpdateSelf(verbose);
return;
}
// Subcommand: translate // Subcommand: translate
if (argsList.Contains("translate")) if (argsList.Contains("translate"))
{ {
@@ -93,6 +118,150 @@ if (verbose) Console.WriteLine($"GCBoK.WebStaticBuilder — building (env={envNa
Builder.Build(cfg, env, envName, verbose, dryRun); Builder.Build(cfg, env, envName, verbose, dryRun);
Console.WriteLine("Build completed successfully."); Console.WriteLine("Build completed successfully.");
static void PrintHelp()
{
Console.WriteLine(@"GCBoK.WebStaticBuilder — Statischer, mehrsprachiger Website-Generator
Aufruf:
webstatic build [Optionen] Website bauen
webstatic serve [--port N] Lokalen Server starten (Vorschau)
webstatic init <name> Beispiel-Site anlegen
webstatic translate --check Fehlende Übersetzungen melden
webstatic translate --draft Übersetzungs-Entwürfe erzeugen
webstatic --version Version (Assembly + Git-Commit) anzeigen
webstatic --help | -h Diese Hilfe
Build-Optionen:
--env <name> Umgebung: development | staging | deploy
--preview|--work|--review Alias für --env development
--staging Alias für --env staging
--deploy Alias für --env deploy
--root <dir> Site-Stammverzeichnis (Default: cwd)
--config <file> Pfad zu webstatic.json
--content/--templates/--assets/--output <dir> Pfade überschreiben
--default-lang <lang> Standardsprache (Default: de)
--langs de,en Nur diese Sprachen bauen
--base-url <url> / --cdn <url> Environment-Werte überschreiben
--verbose | -v Ausführliche Ausgabe
--dry-run | -n Nichts schreiben, nur anzeigen
Der Build liest <root>/webstatic.json (CLI-Flags haben Vorrang).");
}
static string? GitShortHash(string startDir)
{
var repo = FindRepoRoot(startDir);
if (repo == null) return null;
try
{
var psi = new ProcessStartInfo
{
FileName = "git",
Arguments = "rev-parse --short HEAD",
WorkingDirectory = repo,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
using var p = Process.Start(psi);
if (p == null) return null;
var outp = p.StandardOutput.ReadToEnd().Trim();
p.WaitForExit();
return p.ExitCode == 0 && outp.Length > 0 ? outp : null;
}
catch
{
return null;
}
}
static string? FindRepoRoot(string startDir)
{
var dir = new DirectoryInfo(Path.GetFullPath(startDir));
while (dir != null)
{
if (Directory.Exists(Path.Combine(dir.FullName, ".git"))) return dir.FullName;
dir = dir.Parent;
}
return null;
}
static int RunProcess(string fileName, string args, string? workDir = null)
{
var psi = new ProcessStartInfo
{
FileName = fileName,
Arguments = args,
WorkingDirectory = workDir,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
using var p = Process.Start(psi)
?? throw new InvalidOperationException($"Could not start {fileName}.");
p.OutputDataReceived += (_, e) => { if (e.Data != null) Console.WriteLine(e.Data); };
p.ErrorDataReceived += (_, e) => { if (e.Data != null) Console.WriteLine(e.Data); };
p.BeginOutputReadLine();
p.BeginErrorReadLine();
p.WaitForExit();
return p.ExitCode;
}
static void UpdateSelf(bool verbose)
{
var repo = FindRepoRoot(AppContext.BaseDirectory);
if (repo == null)
{
Console.WriteLine("UPDATE nicht möglich: kein Git-Repo gefunden (Copy-Installation?).");
Console.WriteLine("Bitte neu installieren: ./install.sh");
return;
}
Console.WriteLine($"== Update (Hermes): {repo} ==");
var dirty = !string.IsNullOrWhiteSpace(Capture("git", "status --porcelain", repo));
bool stashed = false;
if (dirty)
{
Console.WriteLine("Lokale Änderungen -> git stash");
RunProcess("git", "stash", repo);
stashed = true;
}
try
{
Console.WriteLine("== git pull ==");
var rc = RunProcess("git", "pull", repo);
if (rc != 0) Console.WriteLine("HINWEIS: git pull lieferte Fehler (evtl. Netzwerk/Upstream). Stash wird trotzdem zurückgespielt.");
}
finally
{
if (stashed)
{
Console.WriteLine("== git stash pop ==");
RunProcess("git", "stash pop", repo);
}
}
Console.WriteLine("== Baue Release ==");
RunProcess("dotnet", $"build \"{Path.Combine(repo, "GCBoK.WebStaticBuilder.csproj")}\" -c Release -v minimal", repo);
Console.WriteLine("Update abgeschlossen.");
}
static string Capture(string fileName, string args, string workDir)
{
var psi = new ProcessStartInfo
{
FileName = fileName,
Arguments = args,
WorkingDirectory = workDir,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
using var p = Process.Start(psi)
?? throw new InvalidOperationException($"Could not start {fileName}.");
var outp = p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd();
p.WaitForExit();
return outp;
}
static string FindExampleDir() static string FindExampleDir()
{ {
var candidates = new[] var candidates = new[]
@@ -122,7 +291,7 @@ static void InitSite(string name, string rootDir)
} }
CopyDir(example, target); CopyDir(example, target);
Console.WriteLine($"Initialized site scaffold at {target}"); Console.WriteLine($"Initialized site scaffold at {target}");
Console.WriteLine($"Next: cd {name} && webstatic build --env work"); Console.WriteLine($"Next: cd {name} && webstatic build --env development");
} }
static void CopyDir(string src, string dst) static void CopyDir(string src, string dst)