From aea09098e6c5d7c7aa7669a1579c54bea8d8a3b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 15 Apr 2026 06:31:50 +0000 Subject: [PATCH 1/2] feat(ci): add Gitea Actions release workflow Builds App + Worker + Installer for win-x64 self-contained on v* tag push, bundles into ClaudeDo--win-x64.zip (app + worker), renames installer to ClaudeDo.Installer-.exe, writes sha256 checksums.txt, then creates a Gitea Release on releases/ClaudeDo and attaches all three assets. Uses the workflow-scoped GITEA_TOKEN; no PAT required. Host-mode runner (ubuntu-latest:host) with installed .NET 8 at /home/mika/.dotnet. Uses python3 -m zipfile because the host runner has no zip CLI, and git clone instead of actions/checkout because DEFAULT_ACTIONS_URL=self has no local checkout mirror. --- .gitea/workflows/release.yml | 172 +++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 .gitea/workflows/release.yml diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..3cc9b7b --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,172 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: ubuntu-latest + env: + DOTNET_ROOT: /home/mika/.dotnet + GITEA_API: https://git.kuns.dev/api/v1 + REPO: releases/ClaudeDo + steps: + - name: Resolve version + id: ver + run: | + set -euo pipefail + TAG="${{ github.ref_name }}" + VERSION="${TAG#v}" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Building version: $VERSION (tag: $TAG)" + + - name: Prepare workspace + id: ws + run: | + set -euo pipefail + WORK="$(mktemp -d -t claudedo-release-XXXXXX)" + echo "dir=$WORK" >> "$GITHUB_OUTPUT" + echo "Workspace: $WORK" + + - name: Checkout tag + env: + TOKEN: ${{ secrets.GITEA_TOKEN }} + WORK: ${{ steps.ws.outputs.dir }} + TAG: ${{ steps.ver.outputs.tag }} + run: | + set -euo pipefail + git clone --depth 1 --branch "$TAG" \ + "https://oauth2:${TOKEN}@git.kuns.dev/${REPO}.git" \ + "$WORK/src" + git -C "$WORK/src" log -1 --oneline + + - name: Publish ClaudeDo.App (win-x64, self-contained) + env: + WORK: ${{ steps.ws.outputs.dir }} + VERSION: ${{ steps.ver.outputs.version }} + run: | + set -euo pipefail + export PATH="$DOTNET_ROOT:$PATH" + cd "$WORK/src" + dotnet publish src/ClaudeDo.App/ClaudeDo.App.csproj \ + -c Release -r win-x64 --self-contained true \ + /p:Version=$VERSION -o out/app + + - name: Publish ClaudeDo.Worker (win-x64, self-contained) + env: + WORK: ${{ steps.ws.outputs.dir }} + VERSION: ${{ steps.ver.outputs.version }} + run: | + set -euo pipefail + export PATH="$DOTNET_ROOT:$PATH" + cd "$WORK/src" + dotnet publish src/ClaudeDo.Worker/ClaudeDo.Worker.csproj \ + -c Release -r win-x64 --self-contained true \ + /p:Version=$VERSION -o out/worker + + - name: Publish ClaudeDo.Installer (win-x64, single-file) + env: + WORK: ${{ steps.ws.outputs.dir }} + VERSION: ${{ steps.ver.outputs.version }} + run: | + set -euo pipefail + export PATH="$DOTNET_ROOT:$PATH" + cd "$WORK/src" + dotnet publish src/ClaudeDo.Installer/ClaudeDo.Installer.csproj \ + -c Release -r win-x64 --self-contained true \ + /p:Version=$VERSION /p:PublishSingleFile=true \ + -o out/installer + + - name: Package assets + env: + WORK: ${{ steps.ws.outputs.dir }} + VERSION: ${{ steps.ver.outputs.version }} + run: | + set -euo pipefail + cd "$WORK/src" + mkdir -p assets + + # 1) App + Worker bundle (top-level dirs /app and /worker) + rm -rf bundle + mkdir -p bundle + cp -r out/app bundle/app + cp -r out/worker bundle/worker + ZIP_NAME="ClaudeDo-${VERSION}-win-x64.zip" + # python's zipfile module is used because `zip` is not installed on the host runner + ( cd bundle && python3 -m zipfile -c "../assets/${ZIP_NAME}" app worker ) + + # 2) Installer single-file exe (renamed) + INSTALLER_EXE=$(ls out/installer/*.exe | head -n 1) + if [ -z "$INSTALLER_EXE" ]; then + echo "::error::No .exe produced by installer publish" >&2 + exit 1 + fi + cp "$INSTALLER_EXE" "assets/ClaudeDo.Installer-${VERSION}.exe" + + # 3) Checksums (sha256, relative filenames) + ( cd assets && sha256sum \ + "ClaudeDo-${VERSION}-win-x64.zip" \ + "ClaudeDo.Installer-${VERSION}.exe" \ + > checksums.txt ) + + echo "--- assets ---" + ls -la assets + + - name: Create Gitea Release + id: release + env: + WORK: ${{ steps.ws.outputs.dir }} + TAG: ${{ steps.ver.outputs.tag }} + TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + set -euo pipefail + BODY=$(jq -n \ + --arg tag "$TAG" \ + --arg name "$TAG" \ + '{tag_name:$tag, name:$name, body:"", draft:false, prerelease:false, target_commitish:"main"}') + RESP=$(curl -sS -X POST \ + -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + -d "$BODY" \ + "${GITEA_API}/repos/${REPO}/releases") + RELEASE_ID=$(echo "$RESP" | jq -r '.id // empty') + if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then + echo "::error::Release creation failed" >&2 + echo "$RESP" >&2 + exit 1 + fi + echo "release_id=$RELEASE_ID" >> "$GITHUB_OUTPUT" + echo "Created release id=$RELEASE_ID for tag=$TAG" + + - name: Upload release assets + env: + WORK: ${{ steps.ws.outputs.dir }} + VERSION: ${{ steps.ver.outputs.version }} + RELEASE_ID: ${{ steps.release.outputs.release_id }} + TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + set -euo pipefail + cd "$WORK/src/assets" + for f in \ + "ClaudeDo-${VERSION}-win-x64.zip" \ + "ClaudeDo.Installer-${VERSION}.exe" \ + "checksums.txt" + do + echo "Uploading: $f" + curl -sS --fail-with-body -X POST \ + -H "Authorization: token ${TOKEN}" \ + -F "attachment=@${f}" \ + "${GITEA_API}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${f}" \ + > /dev/null + done + echo "All assets uploaded." + + - name: Cleanup workspace + if: always() + env: + WORK: ${{ steps.ws.outputs.dir }} + run: | + rm -rf "$WORK" || true -- 2.49.1 From 7119c8474e960b9c93fc9f0b8199495719b22c67 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 15 Apr 2026 06:33:50 +0000 Subject: [PATCH 2/2] ci(release): use system zip now that it's installed on the runner --- .gitea/workflows/release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 3cc9b7b..0c75e0d 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -95,8 +95,7 @@ jobs: cp -r out/app bundle/app cp -r out/worker bundle/worker ZIP_NAME="ClaudeDo-${VERSION}-win-x64.zip" - # python's zipfile module is used because `zip` is not installed on the host runner - ( cd bundle && python3 -m zipfile -c "../assets/${ZIP_NAME}" app worker ) + ( cd bundle && zip -r -q "../assets/${ZIP_NAME}" app worker ) # 2) Installer single-file exe (renamed) INSTALLER_EXE=$(ls out/installer/*.exe | head -n 1) -- 2.49.1