diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index cb62ee5..a905bad 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -22,9 +22,9 @@ on: - 'v*' # Manual recovery trigger. Use Gitea's "Run workflow" UI and select the # tag (e.g. v1.4.4) from the Ref dropdown - not main. The Validate tag - # ref step below hard-fails if a non-tag ref is selected, because the - # release-action reads GITHUB_REF directly and rejects anything that - # does not start with refs/tags/. + # ref step below hard-fails if a non-tag ref is selected: the release + # name and body are both derived from the tag, so a branch ref would + # publish a release named after a branch. workflow_dispatch: permissions: @@ -37,11 +37,8 @@ jobs: timeout-minutes: 20 steps: - # release-action@main reads GITHUB_REF directly (its action.yml - # does not declare a tag_name input). Validate up-front so manual - # dispatches from a branch ref fail loud here instead of burning - # a full build before the final step errors out with "ref X is - # not a tag". + # Validate up-front so a manual dispatch from a branch ref fails loud + # here instead of burning a full build before the publish step notices. - name: Validate tag ref run: | if [[ "${GITHUB_REF}" != refs/tags/v* ]]; then @@ -156,28 +153,55 @@ jobs: Write-Host $body Write-Host "----------------------------------------" - # release-action@main only declares files/title/body/pre_release/ - # draft/api_key/insecure as inputs (see its action.yml). It silently - # ignores anything else, including body_path and tag_name. The tag - # itself comes from GITHUB_REF, the body must be passed inline via - # body:, so we re-emit release-body.md as a step output first. - - name: Expose release body for release-action - id: body - shell: bash - run: | - { - echo 'content<> "$GITHUB_OUTPUT" - - # Gitea-native release action. Creates the release if the tag has no - # release yet, or updates the existing one with latest.zip attached - # and the generated body. The auto-injected GITHUB_TOKEN on Gitea - # Actions has Gitea-API scope and is sufficient for release write. + # The tag comes from GITHUB_REF, the body from the step above. Posted with + # curl rather than gitea.com/actions/release-action, which declares + # `using: go` and has to be compiled by the runner -- act cannot do that + # here and the step dies with exec: "go": executable file not found, exit + # 127, after a build that otherwise succeeded. This runs as a plain shell + # step in the job image, which has curl and python3. + # + # Idempotent on purpose: a re-run against an existing release reuses it and + # replaces the asset instead of failing on the duplicate. - name: Attach to Gitea release - uses: https://gitea.com/actions/release-action@main - with: - files: ${{ steps.locate.outputs.path }} - body: ${{ steps.body.outputs.content }} - api_key: ${{ secrets.GITHUB_TOKEN }} + shell: bash + env: + GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ZIP_PATH: ${{ steps.locate.outputs.path }} + TAG_NAME: ${{ github.ref_name }} + run: | + set -euo pipefail + + api="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}" + auth="Authorization: token ${GITEA_TOKEN}" + + # Existing release for this tag, or create one. + rel_id="$(curl -sf -H "$auth" "$api/releases/tags/${TAG_NAME}" \ + | python3 -c 'import sys,json; print(json.load(sys.stdin).get("id",""))' 2>/dev/null || true)" + + if [ -z "$rel_id" ]; then + payload="$(python3 -c ' + import json, os, sys + body = open("release-body.md", encoding="utf-8").read() + json.dump({"tag_name": os.environ["TAG_NAME"], "name": os.environ["TAG_NAME"], + "body": body, "draft": False, "prerelease": False}, sys.stdout) + ')" + rel_id="$(printf '%s' "$payload" \ + | curl -sf -X POST -H "$auth" -H "Content-Type: application/json" -d @- "$api/releases" \ + | python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')" + echo "Created release $rel_id for ${TAG_NAME}" + else + echo "Reusing release $rel_id for ${TAG_NAME}" + fi + + # Drop a same-named asset from an earlier attempt, or the upload 409s. + old_id="$(curl -sf -H "$auth" "$api/releases/${rel_id}/assets" \ + | python3 -c 'import sys,json; print(next((a["id"] for a in json.load(sys.stdin) if a["name"]=="latest.zip"), ""))' 2>/dev/null || true)" + if [ -n "$old_id" ]; then + curl -sf -X DELETE -H "$auth" "$api/releases/${rel_id}/assets/${old_id}" + echo "Replaced existing latest.zip (asset $old_id)" + fi + + curl -sf -X POST -H "$auth" \ + -F "attachment=@${ZIP_PATH};filename=latest.zip" \ + "$api/releases/${rel_id}/assets?name=latest.zip" \ + | python3 -c 'import sys,json; a=json.load(sys.stdin); print("Attached", a["name"], a["size"], "bytes")' diff --git a/HellionChat/HellionChat.csproj b/HellionChat/HellionChat.csproj index d91a870..ae9ba39 100644 --- a/HellionChat/HellionChat.csproj +++ b/HellionChat/HellionChat.csproj @@ -13,7 +13,7 @@ - +