Move release tagging to Gitea, trigger GitHub releases off tag push
Gitea is the source of truth. CI runs on Forgejo Actions, and after it
passes on main, a calver tag is created automatically. Tags mirror to
GitHub, where a tag-push-triggered workflow builds binaries and creates
the release.
Assisted-by: GLM-5 via pi
diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
new file mode 100644
index 0000000..5d50e86
--- /dev/null
+++ b/.gitea/workflows/ci.yml
@@ -0,0 +1,21 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+
+permissions: {}
+
+jobs:
+ ci:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ persist-credentials: false
+ - run: rustup component add clippy rustfmt llvm-tools
+ - run: cargo install grcov
+ - run: cargo install just
+ - run: cargo fmt --check
+ - run: just clippy coverage
diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml
new file mode 100644
index 0000000..ac35a11
--- /dev/null
+++ b/.gitea/workflows/release.yml
@@ -0,0 +1,42 @@
+name: Tag release
+
+on:
+ workflow_run:
+ workflows: [CI]
+ types: [completed]
+ branches: [main]
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+jobs:
+ tag:
+ if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ 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
+ run: |
+ VERSION="${{ steps.version.outputs.version }}"
+ 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/release.yml
index 15072c2..577d96d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,35 +1,14 @@
name: Release
-on: # zizmor: ignore[dangerous-triggers] -- workflow_run only triggers on main after CI passes; no fork PR risk.
- workflow_dispatch:
- workflow_run:
- workflows: [CI]
- types: [completed]
- branches: [main]
+on: # zizmor: ignore[dangerous-triggers] -- only triggers on tag pushes from Gitea mirror; no fork PR risk.
+ push:
+ tags:
+ - 'v*'
permissions: {}
jobs:
- version:
- if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
- runs-on: ubuntu-latest
- outputs:
- version: ${{ steps.version.outputs.version }}
- steps:
- - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- with:
- fetch-depth: 0
- persist-credentials: false
-
- - name: Calculate version
- id: version
- run: |
- CALVER=$(date -u +"%Y-%m-%d")
- SHORT_SHA=$(git rev-parse --short HEAD)
- echo "version=${CALVER}+${SHORT_SHA}" >> $GITHUB_OUTPUT
-
build-macos:
- needs: version
runs-on: macos-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@@ -47,7 +26,6 @@ jobs:
path: ranger-aarch64-apple-darwin.tar.gz
build-linux:
- needs: version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@@ -66,34 +44,28 @@ jobs:
path: ranger-aarch64-unknown-linux-gnu.tar.gz
publish:
- needs: [version, build-macos, build-linux]
+ needs: [build-macos, build-linux]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # zizmor: ignore[artipacked] -- git push needs persisted credentials to push the tag.
-
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
merge-multiple: true
- name: Publish
run: |
- VERSION="${RELEASE_VERSION}"
- git tag "v${VERSION}"
- git push origin "v${VERSION}"
-
- gh release create "v${VERSION}" \
- --title "v${VERSION}" \
+ VERSION="${GITHUB_REF_NAME#v}"
+ gh release create "${GITHUB_REF_NAME}" \
+ --title "${GITHUB_REF_NAME}" \
--generate-notes \
ranger-aarch64-apple-darwin.tar.gz \
ranger-aarch64-unknown-linux-gnu.tar.gz
env:
GH_TOKEN: ${{ github.token }}
- RELEASE_VERSION: ${{ needs.version.outputs.version }}
dotslash:
- needs: [version, publish]
+ needs: publish
runs-on: ubuntu-latest
permissions:
contents: write
@@ -104,4 +76,4 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
config: .github/workflows/dotslash-config.json
- tag: v${{ needs.version.outputs.version }}
+ tag: ${{ github.ref_name }}
diff --git a/AGENTS.md b/AGENTS.md
index f96ed9b..5ad8534 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -75,6 +75,20 @@ When installing system packages (`apt-get`), adding Rust components (`rustup com
- **Migrations must not lose data.** When recreating a table, always `INSERT INTO ... SELECT` all rows from the original, including data in join tables (e.g. `task_tags`). Test migrations against a database with real data, not just empty schemas.
- SQLite doesn't support `ALTER TABLE DROP COLUMN` with foreign keys cleanly. When recreating a table, wrap in `PRAGMA foreign_keys = OFF/ON` to prevent `ON DELETE CASCADE` from wiping join tables (e.g. `task_tags`).
+## 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.
+
+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.
+
## VCS
This project uses **jj** (Jujutsu), not git directly. Use `jj` commands for commits, diffs, and history.
diff --git a/README.md b/README.md
index b26e49c..4178380 100644
--- a/README.md
+++ b/README.md
@@ -81,6 +81,15 @@ Ranger is a single Rust crate with a library and binary target:
The CLI exists primarily so AI agents can manage tasks programmatically. The webapp exists for humans who prefer a visual interface.
+## 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.
+
+The version format is calver: the date of the commit plus its short SHA (e.g., `v2026-04-21+abc1234`).
+
## Roadmap
**First milestone:** self-host Ranger so an AI agent can use it for task management while building Ranger itself.