Add CalVer release workflow with DotSlash
Builds on macos-latest, tags with CalVer (YYYY-MM-DD+SHORT_SHA), and
publishes a GitHub release with the aarch64-apple-darwin tarball. A
second job runs facebook/dotslash-publish-release to generate a
DotSlash file from the release artifact. Both CI and release workflows
target the rewrite branch.

build.rs sets RAMEKIN_VERSION: CI provides CalVer, local builds
default to dev+COMMIT. The --version flag reports it.

Assisted-by: Claude Opus 4.6 via pi
change onplsnkwtxksyqwsqromstryprurpqxp
commit 3641aa880ae3a34a0f085f524c680d14ae20cfa4
author Alpha Chen <alpha@kejadlen.dev>
date
parent lwrxrsqv
diff --git a/.github/dotslash-config.json b/.github/dotslash-config.json
new file mode 100644
index 0000000..5c346e7
--- /dev/null
+++ b/.github/dotslash-config.json
@@ -0,0 +1,12 @@
+{
+  "outputs": {
+    "ramekin": {
+      "platforms": {
+        "macos-aarch64": {
+          "regex": "^ramekin-aarch64-apple-darwin\\.tar\\.gz$",
+          "path": "ramekin"
+        }
+      }
+    }
+  }
+}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d602983..47d11d5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,7 +2,7 @@ name: CI
 
 on:
   push:
-    branches: [main]
+    branches: [rewrite]
   pull_request:
 
 jobs:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..fca19c7
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,62 @@
+name: Release
+
+on:
+  workflow_dispatch:
+  workflow_run:
+    workflows: [CI]
+    types: [completed]
+    branches: [rewrite]
+
+permissions:
+  contents: write
+
+jobs:
+  build:
+    if: >-
+      github.event_name == 'workflow_dispatch'
+      || github.event.workflow_run.conclusion == 'success'
+    runs-on: macos-latest
+    outputs:
+      version: ${{ steps.version.outputs.version }}
+    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)
+          echo "version=${CALVER}+${SHORT_SHA}" >> $GITHUB_OUTPUT
+
+      - name: Build
+        run: |
+          RAMEKIN_VERSION="${{ steps.version.outputs.version }}" cargo build --release
+          tar -czf ramekin-aarch64-apple-darwin.tar.gz -C target/release ramekin
+
+      - name: Publish
+        run: |
+          VERSION="${{ steps.version.outputs.version }}"
+          git tag "v${VERSION}"
+          git push origin "v${VERSION}"
+          gh release create "v${VERSION}" \
+            --title "v${VERSION}" \
+            --generate-notes \
+            ramekin-aarch64-apple-darwin.tar.gz
+        env:
+          GH_TOKEN: ${{ github.token }}
+
+  dotslash:
+    needs: build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Generate DotSlash file
+        uses: facebook/dotslash-publish-release@v1
+        with:
+          config: .github/dotslash-config.json
+          tag: v${{ needs.build.outputs.version }}
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..c3e5fa6
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,19 @@
+use std::process::Command;
+
+fn main() {
+    let version = std::env::var("RAMEKIN_VERSION").unwrap_or_else(|_| {
+        let commit = cmd("git", &["rev-parse", "--short", "HEAD"]);
+        format!("dev+{commit}")
+    });
+    println!("cargo:rustc-env=RAMEKIN_VERSION={version}");
+}
+
+fn cmd(program: &str, args: &[&str]) -> String {
+    Command::new(program)
+        .args(args)
+        .output()
+        .ok()
+        .and_then(|o| String::from_utf8(o.stdout).ok())
+        .map(|s| s.trim().to_string())
+        .unwrap_or_else(|| "unknown".into())
+}
diff --git a/src/main.rs b/src/main.rs
index 09e50e0..2b15bbc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,8 +12,10 @@ use tracing_subscriber::{EnvFilter, fmt, prelude::*};
 const DOCKERFILE: &str = include_str!("../assets/Dockerfile");
 const RAMEKIN_EXTENSION: &str = include_str!("../assets/ramekin.ts");
 
+const VERSION: &str = env!("RAMEKIN_VERSION");
+
 #[derive(Parser)]
-#[command(about = "Run a pi coding agent in a containerized environment")]
+#[command(about = "Run a pi coding agent in a containerized environment", version = VERSION)]
 struct Cli {
     #[command(subcommand)]
     command: Option<Cmd>,