name: Release
on:
push:
tags: ["v*"]
workflow_dispatch:
permissions:
contents: write
jobs:
preflight:
runs-on: ubuntu-latest
steps:
- name: Check release ref
run: |
case "$GITHUB_REF" in
refs/tags/v*) ;;
*)
echo "::error::Release workflow must run on a v* tag ref, got ${GITHUB_REF}"
exit 1
;;
esac
- name: Check release secrets
env:
CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
missing=0
for name in CRATES_IO_TOKEN HOMEBREW_TAP_TOKEN NPM_TOKEN; do
if [ -z "${!name}" ]; then
echo "::error::Missing required release secret: ${name}"
missing=1
fi
done
exit "$missing"
build:
needs: preflight
strategy:
matrix:
include:
- target: x86_64-apple-darwin
os: macos-latest
name: remem-darwin-x64
cargo_flags: --no-default-features
- target: aarch64-apple-darwin
os: macos-latest
name: remem-darwin-arm64
cargo_flags: ""
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
name: remem-linux-x64
cargo_flags: ""
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
name: remem-linux-arm64
cargo_flags: ""
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6.0.2
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation tools (Linux ARM64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
# g++-aarch64-linux-gnu provides the aarch64 libstdc++ that the
# statically linked ort/ONNX Runtime (C++) needs at link time.
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}
- run: cargo build --release --target ${{ matrix.target }} ${{ matrix.cargo_flags }}
- name: Package binary
run: |
cd target/${{ matrix.target }}/release
tar czf ../../../${{ matrix.name }}.tar.gz remem
- uses: actions/upload-artifact@v7.0.1
with:
name: ${{ matrix.name }}
path: ${{ matrix.name }}.tar.gz
release:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
with:
fetch-depth: 2
- uses: dtolnay/rust-toolchain@1.97.0
- name: Check version sync
run: python3 scripts/ci/check_plugin_version_sync.py --expected-version "${GITHUB_REF_NAME#v}"
- name: Validate surface lifecycle manifest
run: python3 scripts/ci/check_public_surface.py
- name: Authenticate surface lifecycle baseline
env:
GH_TOKEN: ${{ github.token }}
run: python3 scripts/ci/check_surface_baseline.py "${GITHUB_SHA}^"
- uses: actions/download-artifact@v8.0.1
with:
path: artifacts
- name: Generate checksums
run: |
cd artifacts
find . -name '*.tar.gz' -type f -print0 | sort -z | xargs -0 sha256sum > SHA256SUMS
- name: Generate plugin runtime manifest
run: python3 scripts/ci/generate_plugin_release_manifest.py "${GITHUB_REF_NAME#v}" artifacts artifacts/remem-releases.json
- name: Include surface lifecycle manifest
run: cp docs/specs/GH969/surface-manifest.json artifacts/surface-manifest.json
- name: Create Release
uses: softprops/action-gh-release@v3.0.0
with:
generate_release_notes: true
files: |
artifacts/**/*.tar.gz
artifacts/SHA256SUMS
artifacts/remem-releases.json
artifacts/surface-manifest.json
update-homebrew-tap:
needs: release
runs-on: ubuntu-latest
steps:
- name: Checkout Homebrew tap
uses: actions/checkout@v6.0.2
with:
repository: majiayu000/homebrew-tap
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
path: homebrew-tap
- name: Update remem formula
working-directory: homebrew-tap
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
version="${RELEASE_TAG#v}"
base_url="https://github.com/majiayu000/remem/releases/download/v${version}"
tmp_dir="$(mktemp -d)"
sums="${tmp_dir}/SHA256SUMS"
curl -fsSL "${base_url}/SHA256SUMS" -o "$sums"
sha_for() {
local stem="$1"
awk -v path="./${stem}/${stem}.tar.gz" '$2 == path { print $1 }' "$sums"
}
darwin_arm64="$(sha_for remem-darwin-arm64)"
darwin_x64="$(sha_for remem-darwin-x64)"
linux_arm64="$(sha_for remem-linux-arm64)"
linux_x64="$(sha_for remem-linux-x64)"
for name in darwin_arm64 darwin_x64 linux_arm64 linux_x64; do
if [ -z "${!name}" ]; then
echo "::error::Missing checksum for ${name} in ${base_url}/SHA256SUMS"
exit 1
fi
done
mkdir -p Formula
cat > Formula/remem.rb <<RUBY
# typed: false
# frozen_string_literal: true
class Remem < Formula
desc "Persistent memory for Claude Code and Codex"
homepage "https://github.com/majiayu000/remem"
version "${version}"
license "MIT"
on_macos do
on_intel do
url "${base_url}/remem-darwin-x64.tar.gz"
sha256 "${darwin_x64}"
end
on_arm do
url "${base_url}/remem-darwin-arm64.tar.gz"
sha256 "${darwin_arm64}"
end
end
on_linux do
on_intel do
url "${base_url}/remem-linux-x64.tar.gz"
sha256 "${linux_x64}"
end
on_arm do
url "${base_url}/remem-linux-arm64.tar.gz"
sha256 "${linux_arm64}"
end
end
def install
bin.install "remem" => "remem"
if OS.mac? && Hardware::CPU.arm?
system "codesign", "--force", "--sign", "-", bin/"remem"
end
end
def caveats
<<~EOS
Finish agent integration after installing the binary by choosing
the agent configuration to create:
REMEM_INSTALL_BINARY=#{opt_bin}/remem remem install --target codex
REMEM_INSTALL_BINARY=#{opt_bin}/remem remem install --target claude
REMEM_INSTALL_BINARY=#{opt_bin}/remem remem install --target all
If Claude Code or Codex CLI config directories already exist,
auto-detection is also available:
REMEM_INSTALL_BINARY=#{opt_bin}/remem remem install
Run remem doctor to verify or troubleshoot the integration.
EOS
end
test do
assert_match "remem ${version}", shell_output("#{bin}/remem --version")
end
end
RUBY
ruby -c Formula/remem.rb
git add Formula/remem.rb
if git diff --cached --quiet -- Formula/remem.rb; then
echo "Formula/remem.rb already up to date for v${version}"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -m "remem: update formula to v${version}"
git push
publish-crate:
needs: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: dtolnay/rust-toolchain@stable
- name: Publish crate
run: cargo publish --locked --token "${{ secrets.CRATES_IO_TOKEN }}"
publish-npm:
needs: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-node@v6.0.0
with:
node-version: 22
registry-url: https://registry.npmjs.org
- name: Check version sync
run: python3 scripts/ci/check_plugin_version_sync.py --expected-version "${GITHUB_REF_NAME#v}"
- name: Test npm wrapper
run: node --test npm/remem/scripts/install.test.js
- name: Smoke packed npm wrapper
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
version="${RELEASE_TAG#v}"
pack_dir="$(mktemp -d)"
install_prefix="$(mktemp -d)"
unset REMEM_NPM_BINARY REMEM_NPM_SKIP_DOWNLOAD
npm pack ./npm/remem --pack-destination "$pack_dir"
tarball="$(find "$pack_dir" -maxdepth 1 -type f -name '*.tgz' -print -quit)"
if [ -z "$tarball" ]; then
echo "::error::npm pack did not produce a tarball"
exit 1
fi
npm install --global --prefix "$install_prefix" "$tarball"
actual="$("$install_prefix/bin/remem" --version)"
echo "installed remem --version: $actual"
installed_version="$(printf '%s\n' "$actual" | awk 'NR == 1 { print $2 }')"
if [ "$installed_version" != "$version" ]; then
echo "::error::installed remem version $installed_version does not match release tag $RELEASE_TAG"
exit 1
fi
- name: Publish npm wrapper
working-directory: npm/remem
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --access public
publish-mcp-registry:
needs: publish-npm
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v6.0.2
- name: Check version sync
run: python3 scripts/ci/check_plugin_version_sync.py --expected-version "${GITHUB_REF_NAME#v}"
- name: Install mcp-publisher
run: |
set -euo pipefail
arch="$(uname -m)"
case "$arch" in
x86_64) arch=amd64 ;;
aarch64) arch=arm64 ;;
esac
curl -fsSL "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_${arch}.tar.gz" | tar xz mcp-publisher
- name: Publish to MCP registry
run: |
set -euo pipefail
./mcp-publisher login github-oidc
./mcp-publisher publish