ci: tag and release from GitHub, drop Gitea CI
Since quire took over mirroring, tags pushed by Gitea CI never reached GitHub, so nothing has released since 2026-06-26.

Assisted-by: Claude Opus 5.5 via Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
change vrouqyqstxyznqwoomyolqtmwsvxvury
commit 85beda284b06a6ab644566ec3e581d4220685767
author Alpha Chen <alpha@kejadlen.dev>
date
parent lkvpvypu
diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
deleted file mode 100644
index 91c066b..0000000
--- a/.gitea/workflows/ci.yml
+++ /dev/null
@@ -1,86 +0,0 @@
-name: CI
-
-on:
-  push:
-    branches: [main]
-  pull_request:
-
-permissions: {}
-
-jobs:
-  ci:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          persist-credentials: false
-
-      # Can't use facebook/install-dotslash action because it queries the
-      # GitHub API using $GITHUB_TOKEN, which is a Gitea token on this runner.
-      - run: |
-          curl -sL https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.x86_64.tar.gz |
-            tar xz -C /usr/local/bin
-      - run: |
-          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
-          echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
-          echo "$PWD/bin" >> "$GITHUB_PATH"
-
-      - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4
-        with:
-          path: ~/.rustup/
-          # Bump suffix to force cache bust after upgrading rustup.
-          key: rustup-${{ runner.os }}-v1
-
-      - run: rustup component add clippy rustfmt llvm-tools
-      - run: cargo fmt --check
-      - run: just clippy coverage
-
-  zizmor:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          persist-credentials: false
-
-      - run: |
-          curl -sL https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.x86_64.tar.gz |
-            tar xz -C /usr/local/bin
-      - run: echo "$PWD/bin" >> "$GITHUB_PATH"
-
-      # Can't use zizmorcore/zizmor-action because it doesn't support
-      # auditing .gitea/workflows/ — it rejects them as invalid inputs.
-      - run: zizmor .gitea/workflows/* .github
-
-  tag:
-    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
-    needs: [ci]
-    runs-on: ubuntu-latest
-    permissions:
-      contents: write
-    steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 # zizmor: ignore[artipacked] -- needs credentials to push tag
-        with:
-          fetch-depth: 0
-
-      - name: Calculate version
-        id: version
-        run: |
-          CALVER=$(date -u +"%Y-%m-%d")
-          SHORT_SHA=$(git rev-parse --short HEAD)
-          VERSION="${CALVER}+${SHORT_SHA}"
-          echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
-
-      - name: Push tag
-        env:
-          VERSION: ${{ steps.version.outputs.version }}
-        run: |
-          TAG="v${VERSION}"
-
-          # Skip if this tag already exists
-          if git tag -l "$TAG" | grep -q .; then
-            echo "Tag ${TAG} already exists, skipping"
-            exit 0
-          fi
-
-          git tag "$TAG"
-          git push origin "$TAG"
diff --git a/.github/workflows/release.yml b/.github/workflows/ci.yml
similarity index 50%
rename from .github/workflows/release.yml
rename to .github/workflows/ci.yml
index ff2ddcc..090f16a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/ci.yml
@@ -1,14 +1,68 @@
-name: Release
+name: CI
 
-on: # zizmor: ignore[dangerous-triggers] -- only triggers on tag pushes from Gitea mirror; no fork PR risk.
+on: # zizmor: ignore[dangerous-triggers] -- only triggers on main pushes from the quire mirror; no fork PR risk.
   push:
-    tags:
-      - 'v*'
+    branches: [main]
 
 permissions: {}
 
 jobs:
+  ci:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false
+
+      - run: |
+          curl -sL https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.x86_64.tar.gz |
+            tar xz -C /usr/local/bin
+      - run: echo "$PWD/bin" >> "$GITHUB_PATH"
+
+      - run: rustup component add clippy rustfmt llvm-tools
+      - run: cargo fmt --check
+      - run: just clippy coverage
+
+  zizmor:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false
+
+      - run: |
+          curl -sL https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.x86_64.tar.gz |
+            tar xz -C /usr/local/bin
+      - run: echo "$PWD/bin" >> "$GITHUB_PATH"
+
+      - run: zizmor .github
+
+  version:
+    needs: [ci, zizmor]
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read # look up existing releases.
+    outputs:
+      tag: ${{ steps.version.outputs.tag }}
+      exists: ${{ steps.version.outputs.exists }}
+    steps:
+      - name: Calculate version
+        id: version
+        env:
+          GH_TOKEN: ${{ github.token }}
+        run: |
+          TAG="v$(date -u +%Y-%m-%d)+${GITHUB_SHA::7}"
+          echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
+          if gh release view "$TAG" -R "$GITHUB_REPOSITORY" > /dev/null 2>&1; then
+            echo "Release ${TAG} already exists, skipping"
+            echo "exists=true" >> "$GITHUB_OUTPUT"
+          else
+            echo "exists=false" >> "$GITHUB_OUTPUT"
+          fi
+
   build-macos:
+    needs: version
+    if: needs.version.outputs.exists == 'false'
     runs-on: macos-latest
     steps:
       - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@@ -26,6 +80,8 @@ jobs:
           path: ranger-aarch64-apple-darwin.tar.gz
 
   build-linux:
+    needs: version
+    if: needs.version.outputs.exists == 'false'
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@@ -44,7 +100,7 @@ jobs:
           path: ranger-aarch64-unknown-linux-gnu.tar.gz
 
   publish:
-    needs: [build-macos, build-linux]
+    needs: [version, build-macos, build-linux]
     runs-on: ubuntu-latest
     permissions:
       contents: write
@@ -59,17 +115,18 @@ jobs:
 
       - name: Publish
         run: |
-          VERSION="${GITHUB_REF_NAME#v}"
-          gh release create "${GITHUB_REF_NAME}" \
-            --title "${GITHUB_REF_NAME}" \
+          gh release create "$TAG" \
+            --target "$GITHUB_SHA" \
+            --title "$TAG" \
             --generate-notes \
             ranger-aarch64-apple-darwin.tar.gz \
             ranger-aarch64-unknown-linux-gnu.tar.gz
         env:
           GH_TOKEN: ${{ github.token }}
+          TAG: ${{ needs.version.outputs.tag }}
 
   dotslash:
-    needs: publish
+    needs: [version, publish]
     runs-on: ubuntu-latest
     permissions:
       contents: write
@@ -80,4 +137,4 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           config: .github/workflows/dotslash-config.json
-          tag: ${{ github.ref_name }}
+          tag: ${{ needs.version.outputs.tag }}
diff --git a/AGENTS.md b/AGENTS.md
index 5ad8534..e13b671 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -77,17 +77,14 @@ When installing system packages (`apt-get`), adding Rust components (`rustup com
 
 ## CI and Releases
 
-Two-forge setup:
-
-- **Gitea** (`origin`) is the source of truth. CI runs on Forgejo Actions (`.gitea/workflows/`). On push to `main` and PRs.
-- **GitHub** (`github` remote) is a mirror. Receives tags from Gitea and builds release artifacts.
+- **quire** (`origin`) is the source of truth. It mirrors to Gitea and GitHub (`.quire/config.fnl`).
+- **Gitea** (`gitea` remote) is a plain mirror.
+- **GitHub** (`github` remote) is a mirror. Runs CI and builds release artifacts.
 
 Release flow:
 
-1. Gitea CI passes on `main`.
-2. Gitea release workflow creates a `vYYYY-MM-DD+<short-sha>` tag and pushes it.
-3. Tag mirrors to GitHub.
-4. GitHub release workflow (`.github/workflows/release.yml`) triggers on tag push, builds macOS and Linux binaries, creates a GitHub release, and publishes Dotslash configuration.
+1. quire mirrors `main` to GitHub.
+2. GitHub CI workflow (`.github/workflows/ci.yml`) triggers on the push, runs CI, and once it passes creates a `vYYYY-MM-DD+<short-sha>` tag and GitHub release, builds macOS and Linux binaries, and publishes Dotslash configuration. It skips a SHA that already has a release that day.
 
 ## VCS
 
diff --git a/README.md b/README.md
index 1c20cc5..b27f7d3 100644
--- a/README.md
+++ b/README.md
@@ -88,10 +88,7 @@ The CLI exists primarily so AI agents can manage tasks programmatically. The web
 
 ## Releases
 
-Ranger uses a two-forge release pipeline:
-
-1. **Gitea** (`git.kejadlen.dev`) is the source of truth. CI runs on every push to `main` (and on PRs). When CI passes, a `vYYYY-MM-DD+<short-sha>` tag is created automatically.
-2. Tags mirror to **GitHub** (`github.com/kejadlen/ranger`). A tag push triggers the release workflow, which builds macOS and Linux binaries, creates a GitHub release, and publishes Dotslash configuration.
+Ranger's source of truth is a quire repository, which mirrors to Gitea (`git.kejadlen.dev`) and **GitHub** (`github.com/kejadlen/ranger`). Each push to `main` on GitHub triggers the CI workflow, which runs checks and, once it passes, tags the commit `vYYYY-MM-DD+<short-sha>`, builds macOS and Linux binaries, creates a GitHub release, and publishes Dotslash configuration.
 
 The version format is calver: the date of the commit plus its short SHA (e.g., `v2026-04-21+abc1234`).