diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d1f3036..cdc721e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,7 +55,7 @@ skills// git push origin main /v ``` -7. Release vom eigenen Rechner anlegen (siehe unten). Auf git.gundel.dev läuft kein Actions-Runner. +7. Release anlegen (siehe unten): Pakete bauen und dem Tag als Anhänge hinzufügen. ## Update eines Skills @@ -91,50 +91,26 @@ powershell -ExecutionPolicy Bypass -File scripts\package-mcpb.ps1 -Name powershell -ExecutionPolicy Bypass -File scripts\package-all.ps1 ``` -### Vom eigenen Rechner +### Release veröffentlichen -Auf git.gundel.dev ist kein Actions-Runner registriert. Das Release legt deshalb ein Skript auf dem Rechner an, der das ZIP schon gebaut hat. Es spricht die Gitea-API an ([Release anlegen](https://docs.gitea.com/enterprise/api/21/operations/repo-create-release/), [Anhang hochladen](https://docs.gitea.com/api/operations/repo-create-release-attachment/)): +Der Tag `/v` muss lokal und remote existieren. Danach entweder: -- `POST /api/v1/repos/dg/claude-skills/releases` -- `POST /api/v1/repos/dg/claude-skills/releases/{id}/assets` - -Dafür braucht es ein persönliches Zugangs-Token mit Schreibrecht auf dieses Repository (`write:repository`). Anlegen unter Profil → Einstellungen → Applications. Authentifizierung: [`Authorization: token …`](https://docs.gitea.com/development/api-usage/). Das Token nur in die Umgebung legen, nicht ins Repository. - -Der Tag `/v` muss lokal existieren und schon auf dem Server liegen. Das Skript verschiebt keine Tags und pusht nichts. +**Skript** (baut ZIP/`.mcpb` und hängt sie ans Release; nutzt dieselbe Anmeldung wie `git push`): ```powershell -$env:GITEA_TOKEN = '' powershell -ExecutionPolicy Bypass -File scripts\release.ps1 -Name ``` ```sh -GITEA_TOKEN='' scripts/release.sh +scripts/release.sh ``` -### Manuell, ohne Token +**Oder manuell** in der Release-Oberfläche des Remotes: 1. Paket bauen (Befehle oben). Prüfen, dass `dist/-v.zip` die Einträge `/` und `/SKILL.md` enthält und weder `README.md` noch `CHANGELOG.md`. -2. Unter [Releases](https://git.gundel.dev/dg/claude-skills/releases) ein neues Release anlegen. -3. Tag: `/v` (der Tag muss schon auf dem Server liegen). Titel: ` `. -4. Als Text den Abschnitt dieser Version aus `skills//CHANGELOG.md` einfügen. -5. `dist/-v.zip` als Anhang hochladen. -6. Falls vorhanden: `dist/-v.mcpb` ebenfalls hochladen (oder `scripts/release.ps1` nutzen – hängt beides an). - -Nur die `.mcpb` nachträglich an ein bestehendes Release hängen (Token nur in der Umgebung): - -```powershell -$env:GITEA_TOKEN = '' -$id = -$name = 'wordcraft-mcp-v0.2.0.mcpb' -$bytes = [IO.File]::ReadAllBytes("dist\$name") -$req = [Net.HttpWebRequest]::Create("https://git.gundel.dev/api/v1/repos/dg/claude-skills/releases/$id/assets?name=$name") -$req.Method = 'POST' -$req.Headers.Add('Authorization', "token $env:GITEA_TOKEN") -$req.ContentType = 'application/octet-stream' -$req.ContentLength = $bytes.LongLength -$s = $req.GetRequestStream(); $s.Write($bytes, 0, $bytes.Length); $s.Close() -$resp = $req.GetResponse(); (New-Object IO.StreamReader($resp.GetResponseStream())).ReadToEnd() -``` +2. Neues Release zum Tag `/v` anlegen, Titel ` `. +3. Als Text den Abschnitt dieser Version aus `skills//CHANGELOG.md` einfügen. +4. `dist/-v.zip` und – falls vorhanden – `dist/-v.mcpb` anhängen. ## Hinweise zu den Skripten diff --git a/README.md b/README.md index 02bd24b..b36a735 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # claude-skills -Sammlung von Skills für Claude. Jeder Skill ist ein abgeschlossener Ordner: `SKILL.md` für Claude, `README.md` für Menschen. Die fertigen Pakete liegen als ZIP in den [Releases](https://git.gundel.dev/dg/claude-skills/releases) auf [git.gundel.dev/dg/claude-skills](https://git.gundel.dev/dg/claude-skills). +Sammlung von Skills für Claude. Jeder Skill ist ein abgeschlossener Ordner: `SKILL.md` für Claude, `README.md` für Menschen. Fertige Pakete liegen unter [Releases](https://git.gundel.dev/dg/claude-skills/releases). Schritt-für-Schritt-Installation: [docs/INSTALLATION.md](docs/INSTALLATION.md). diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 24b42c2..cdaf4b2 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -49,7 +49,7 @@ Für jede `.mcpb`-Datei: 5. Installieren. Die Erweiterung erscheint unter Einstellungen → Erweiterungen und muss **eingeschaltet** sein. > **Windows meldet „Der Computer wurde durch Windows geschützt“ oder Defender schlägt an?** -> Die Erweiterungen sind nicht signiert. Wende dich an Dominic, bevor du die Warnung übergehst. +> Die Erweiterungen sind nicht signiert. Nur installieren, wenn du der Quelle vertraust. --- diff --git a/scripts/release.ps1 b/scripts/release.ps1 index 491915a..3877dde 100644 --- a/scripts/release.ps1 +++ b/scripts/release.ps1 @@ -1,17 +1,19 @@ -# Baut das ZIP eines Skills und legt damit ein Gitea-Release an. -# Kein Actions-Runner. Der Tag /v muss lokal und auf dem Server liegen. -# Token: Umgebungsvariable GITEA_TOKEN (persoenliches Zugangs-Token, nicht ins Repo). -# Aufruf: scripts\release.ps1 -Name +# Builds the skill ZIP (and .mcpb if extension/) and creates a remote release with those assets. +# Auth: same as git push (credential helper), or env RELEASE_TOKEN if set. +# The tag /v must exist locally and on the remote. Does not push tags. +# Usage: scripts\release.ps1 -Name param([Parameter(Mandatory = $true)][string]$Name) . (Join-Path $PSScriptRoot 'lib.ps1') -# Prefer GITEA_TOKEN; otherwise reuse git credential helper (same auth as git push). +$RepoApi = 'https://git.gundel.dev/api/v1/repos/dg/claude-skills' +$RepoHost = 'git.gundel.dev' + $AuthHeader = $null -if ($env:GITEA_TOKEN) { - $AuthHeader = "token $($env:GITEA_TOKEN)" +if ($env:RELEASE_TOKEN) { + $AuthHeader = "token $($env:RELEASE_TOKEN)" } else { - $credRaw = "protocol=https`nhost=git.gundel.dev`n`n" | git credential fill 2>$null + $credRaw = "protocol=https`nhost=$RepoHost`n`n" | git credential fill 2>$null $user = $null; $pass = $null foreach ($line in $credRaw) { if ($line -match '^username=(.*)$') { $user = $Matches[1] } @@ -23,7 +25,7 @@ else { } } if (-not $AuthHeader) { - Write-Host 'Keine Auth: GITEA_TOKEN setzen oder git credential fuer git.gundel.dev hinterlegen.' + Write-Host 'Keine Auth fuer das Release (git credential oder RELEASE_TOKEN).' exit 1 } @@ -68,10 +70,9 @@ foreach ($l in ((Read-Text (Join-Path $dir 'CHANGELOG.md')) -split "`n")) { } $notes = (($acc -join "`n").Trim()) -$api = 'https://git.gundel.dev/api/v1/repos/dg/claude-skills' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -function Invoke-Gitea([string]$Method, [string]$Url, [byte[]]$Body, [string]$ContentType) { +function Invoke-ReleaseApi([string]$Method, [string]$Url, [byte[]]$Body, [string]$ContentType) { $req = [Net.HttpWebRequest]::Create($Url) $req.Method = $Method $req.Headers.Add('Authorization', $AuthHeader) @@ -109,7 +110,7 @@ $payload = @{ body = $notes } | ConvertTo-Json -Compress try { - $created = Invoke-Gitea 'POST' "$api/releases" $Utf8NoBom.GetBytes($payload) 'application/json; charset=utf-8' | ConvertFrom-Json + $created = Invoke-ReleaseApi 'POST' "$RepoApi/releases" $Utf8NoBom.GetBytes($payload) 'application/json; charset=utf-8' | ConvertFrom-Json } catch { Write-Host $_ @@ -117,8 +118,8 @@ catch { exit 1 } -$assetUrl = "$api/releases/$($created.id)/assets?name=$([Uri]::EscapeDataString($zipName))" -$asset = Invoke-Gitea 'POST' $assetUrl ([IO.File]::ReadAllBytes($zipPath)) 'application/octet-stream' | ConvertFrom-Json +$assetUrl = "$RepoApi/releases/$($created.id)/assets?name=$([Uri]::EscapeDataString($zipName))" +$asset = Invoke-ReleaseApi 'POST' $assetUrl ([IO.File]::ReadAllBytes($zipPath)) 'application/octet-stream' | ConvertFrom-Json $mcpbName = "$Name-v$ver.mcpb" $mcpbPath = Join-Path $DistDir $mcpbName @@ -129,8 +130,8 @@ if (Test-Path (Join-Path (Join-Path $dir 'extension') 'manifest.json')) { if ($LASTEXITCODE -ne 0) { exit 1 } } if (Test-Path $mcpbPath) { - $mcpbUrl = "$api/releases/$($created.id)/assets?name=$([Uri]::EscapeDataString($mcpbName))" - $mcpbAsset = Invoke-Gitea 'POST' $mcpbUrl ([IO.File]::ReadAllBytes($mcpbPath)) 'application/octet-stream' | ConvertFrom-Json + $mcpbUrl = "$RepoApi/releases/$($created.id)/assets?name=$([Uri]::EscapeDataString($mcpbName))" + $mcpbAsset = Invoke-ReleaseApi 'POST' $mcpbUrl ([IO.File]::ReadAllBytes($mcpbPath)) 'application/octet-stream' | ConvertFrom-Json } } diff --git a/scripts/release.sh b/scripts/release.sh index a54f490..4c22980 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash -# Baut das ZIP eines Skills und legt damit ein Gitea-Release an. -# Kein Actions-Runner. Der Tag /v muss lokal und auf dem Server liegen. -# Token: Umgebungsvariable GITEA_TOKEN (persönliches Zugangs-Token, nicht ins Repo). -# Aufruf: scripts/release.sh +# Builds the skill ZIP (and .mcpb if extension/) and creates a remote release with those assets. +# Auth: git credential helper, or env RELEASE_TOKEN if set. +# The tag /v must exist locally and on the remote. Does not push tags. +# Usage: scripts/release.sh set -euo pipefail source "$(dirname "$0")/lib.sh" @@ -11,15 +11,31 @@ if [[ -z "$name" ]]; then echo "Aufruf: scripts/release.sh " >&2 exit 1 fi -if [[ -z "${GITEA_TOKEN:-}" ]]; then - echo "GITEA_TOKEN fehlt. Persönliches Zugangs-Token mit Schreibrecht (write:repository), nur in der Umgebung." >&2 - exit 1 -fi if ! command -v python3 >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1; then echo "release.sh braucht curl und python3. Unter Windows: scripts\\release.ps1" >&2 exit 1 fi +REPO_HOST=git.gundel.dev +api="https://$REPO_HOST/api/v1/repos/dg/claude-skills" + +auth_header="" +if [[ -n "${RELEASE_TOKEN:-}" ]]; then + auth_header="Authorization: token $RELEASE_TOKEN" +else + cred="$(printf 'protocol=https\nhost=%s\n\n' "$REPO_HOST" | git credential fill 2>/dev/null || true)" + user="$(printf '%s\n' "$cred" | sed -n 's/^username=//p' | head -n1)" + pass="$(printf '%s\n' "$cred" | sed -n 's/^password=//p' | head -n1)" + if [[ -n "$user" && -n "$pass" ]]; then + basic="$(printf '%s:%s' "$user" "$pass" | base64 | tr -d '\n')" + auth_header="Authorization: Basic $basic" + fi +fi +if [[ -z "$auth_header" ]]; then + echo "Keine Auth fuer das Release (git credential oder RELEASE_TOKEN)." >&2 + exit 1 +fi + dir="$SKILLS_DIR/$name" version="$(skill_version "$dir")" if [[ -z "$version" ]]; then @@ -55,10 +71,9 @@ print(json.dumps({ "body": open(os.environ["NOTES"], encoding="utf-8").read().strip(), }))' > "$payload" -api="https://git.gundel.dev/api/v1/repos/dg/claude-skills" resp="$(mktemp)" code="$(curl -sS -o "$resp" -w '%{http_code}' -X POST "$api/releases" \ - -H "Authorization: token $GITEA_TOKEN" \ + -H "$auth_header" \ -H "Content-Type: application/json" \ --data-binary @"$payload")" if [[ "$code" != "201" ]]; then @@ -78,7 +93,7 @@ upload_asset() { local asset_resp code asset download asset_resp="$(mktemp)" code="$(curl -sS -o "$asset_resp" -w '%{http_code}' -X POST "$api/releases/$rel_id/assets?name=$file_name" \ - -H "Authorization: token $GITEA_TOKEN" \ + -H "$auth_header" \ -H "Content-Type: application/octet-stream" \ --data-binary @"$file_path")" if [[ "$code" != "201" ]]; then diff --git a/skills/gridcraft-mcp/README.md b/skills/gridcraft-mcp/README.md index d894980..b95402f 100644 --- a/skills/gridcraft-mcp/README.md +++ b/skills/gridcraft-mcp/README.md @@ -51,7 +51,7 @@ Fehlen die Tools: Einstellungen → Erweiterungen – ist die Erweiterung einges ## Stolpersteine -- Erweiterungen sind nicht signiert; bei Defender-Warnung an Dominic wenden. +- Erweiterungen sind nicht signiert; nur installieren, wenn du der Quelle vertraust. - Formeln mit deutschen Namen ergeben `#NAME?` – englische Funktionsnamen verlangen. ## Deinstallation/Update diff --git a/skills/wordcraft-mcp/README.md b/skills/wordcraft-mcp/README.md index f59992d..9345666 100644 --- a/skills/wordcraft-mcp/README.md +++ b/skills/wordcraft-mcp/README.md @@ -38,7 +38,7 @@ Fehlen die Tools: Einstellungen → Erweiterungen – ist die Erweiterung einges ## Stolpersteine - Speichern mit „path rejected“: Ziel lag außerhalb des Schreib-Ordners. Einstellungen → Erweiterungen → WordCraft für Claude → Einstellungen prüfen. -- Erweiterungen sind nicht signiert; bei Defender-Warnung an Dominic wenden. +- Erweiterungen sind nicht signiert; nur installieren, wenn du der Quelle vertraust. ## Deinstallation/Update