Compare commits

...

8 Commits

Author SHA1 Message Date
jordanthejet
7b981b154f feat(heartbeat): change default interval to 5min and minimum to 1min
Lower the heartbeat default from 30 minutes to 5 minutes and reduce
the minimum floor from 5 minutes to 1 minute for faster feedback loops.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 20:41:14 -05:00
jordanthejet
5dfd0a5e2b docs: update actions-source-policy for softprops removal and release changes
- Remove softprops/action-gh-release from allowlist (replaced by gh CLI)
- Update Beta Release trigger description (daily schedule + dispatch)
- Add changelog entry for release pipeline overhaul

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:38:15 -05:00
jordanthejet
2896875331 fix(ci): pin release tag to triggering commit via --target GITHUB_SHA
Without --target, gh release create tags the latest commit on the
default branch, which may differ from the commit that built the
artifacts. Pin to $GITHUB_SHA so the tag always matches the built ref.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:12:57 -05:00
jordanthejet
a2d5672e72 fix(ci): correct artifact glob paths for release upload
SHA256SUMS is generated at artifacts/SHA256SUMS (not nested in a
subdirectory), so use artifacts/SHA256SUMS instead of artifacts/**/SHA256SUMS.
Also use artifacts/*/ single-level glob to match the download-artifact
directory structure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 17:53:05 -05:00
jordanthejet
bb7314bc37 fix(ci): add default-branch guard to beta release workflow_dispatch
Prevent manual beta releases from non-default branches by gating
the version job on github.ref matching the default branch. Off-branch
dispatches now fail closed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 17:52:40 -05:00
JordanTheJet
b59e3ae6d1
Merge branch 'master' into fix/release-pipeline 2026-03-07 17:44:43 -05:00
jordanthejet
1f8934c4b4 feat(ci): add armv7 Linux build target to release pipelines
Add armv7-unknown-linux-gnueabihf to the build matrix for both beta
and stable release workflows, cross-compiled from ubuntu-latest using
gcc-arm-linux-gnueabihf.

Full release target matrix:
- Linux: x86_64, aarch64, armv7
- macOS: x86_64, aarch64
- Windows: x86_64

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 17:42:40 -05:00
jordanthejet
ace9f19b11 fix(ci): overhaul release pipeline — daily betas, gh CLI publish, x86_64 macOS cross-compile
- Switch beta releases from per-merge to daily schedule (08:00 UTC)
  plus manual workflow_dispatch to reduce release spam
- Replace softprops/action-gh-release with gh CLI to fix the
  "Finalizing release" retry failure that left releases as drafts
- Beta releases use --prerelease; stable releases use --latest so
  the stable version stays prominently badged on the releases page
- Add x86_64-apple-darwin target back via cross-compile from macos-14
  (ARM runner), replacing the deprecated macos-13 Intel runner

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:46:37 -05:00
8 changed files with 61 additions and 34 deletions

View File

@ -75,10 +75,21 @@ jobs:
cross_compiler: gcc-aarch64-linux-gnu
linker_env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER
linker: aarch64-linux-gnu-gcc
- os: ubuntu-latest
target: armv7-unknown-linux-gnueabihf
artifact: zeroclaw
ext: tar.gz
cross_compiler: gcc-arm-linux-gnueabihf
linker_env: CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER
linker: arm-linux-gnueabihf-gcc
- os: macos-14
target: aarch64-apple-darwin
artifact: zeroclaw
ext: tar.gz
- os: macos-14
target: x86_64-apple-darwin
artifact: zeroclaw
ext: tar.gz
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact: zeroclaw.exe
@ -142,16 +153,16 @@ jobs:
cat SHA256SUMS
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.validate.outputs.tag }}
name: ${{ needs.validate.outputs.tag }}
prerelease: false
generate_release_notes: true
files: |
artifacts/**/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ needs.validate.outputs.tag }}" \
--repo "${{ github.repository }}" \
--target "$GITHUB_SHA" \
--title "${{ needs.validate.outputs.tag }}" \
--latest \
--generate-notes \
artifacts/*/*.tar.gz artifacts/*/*.zip artifacts/SHA256SUMS
docker:
name: Push Docker Image

View File

@ -1,8 +1,9 @@
name: Beta Release
name: Daily Beta Release
on:
push:
branches: [master]
schedule:
- cron: '0 8 * * *' # Daily at 08:00 UTC
workflow_dispatch: {}
concurrency:
group: release
@ -19,6 +20,7 @@ env:
jobs:
version:
if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
name: Resolve Version
runs-on: ubuntu-latest
outputs:
@ -57,10 +59,21 @@ jobs:
cross_compiler: gcc-aarch64-linux-gnu
linker_env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER
linker: aarch64-linux-gnu-gcc
- os: ubuntu-latest
target: armv7-unknown-linux-gnueabihf
artifact: zeroclaw
ext: tar.gz
cross_compiler: gcc-arm-linux-gnueabihf
linker_env: CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER
linker: arm-linux-gnueabihf-gcc
- os: macos-14
target: aarch64-apple-darwin
artifact: zeroclaw
ext: tar.gz
- os: macos-14
target: x86_64-apple-darwin
artifact: zeroclaw
ext: tar.gz
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact: zeroclaw.exe
@ -124,16 +137,16 @@ jobs:
cat SHA256SUMS
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.version.outputs.tag }}
name: ${{ needs.version.outputs.tag }}
prerelease: true
generate_release_notes: true
files: |
artifacts/**/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ needs.version.outputs.tag }}" \
--repo "${{ github.repository }}" \
--target "$GITHUB_SHA" \
--title "${{ needs.version.outputs.tag }}" \
--prerelease \
--generate-notes \
artifacts/*/*.tar.gz artifacts/*/*.zip artifacts/SHA256SUMS
docker:
name: Push Docker Image

View File

@ -688,7 +688,7 @@ allowed_workspace_roots = [] # optional allowlist for workspace mount validati
[heartbeat]
enabled = false
interval_minutes = 30
interval_minutes = 5
message = "Check London time" # optional fallback task when HEARTBEAT.md has no `- ` entries
target = "telegram" # optional announce channel: telegram, discord, slack, mattermost
to = "123456789" # optional target recipient/chat/channel id

View File

@ -663,7 +663,7 @@ allowed_workspace_roots = [] # allowlist tùy chọn để xác thực workspa
[heartbeat]
enabled = false
interval_minutes = 30
interval_minutes = 5
[tunnel]
provider = "none" # "none", "cloudflare", "tailscale", "ngrok", "custom"

View File

@ -16,7 +16,6 @@ Selected allowlist (all actions currently used across CI, Beta Release, and Prom
| `actions/download-artifact@v4` | release, promote-release | Download build artifacts for packaging |
| `dtolnay/rust-toolchain@stable` | All workflows | Install Rust toolchain (1.92.0) |
| `Swatinem/rust-cache@v2` | All workflows | Cargo build/dependency caching |
| `softprops/action-gh-release@v2` | release, promote-release | Create GitHub Releases |
| `docker/setup-buildx-action@v3` | release, promote-release | Docker Buildx setup |
| `docker/login-action@v3` | release, promote-release | GHCR authentication |
| `docker/build-push-action@v6` | release, promote-release | Multi-platform Docker image build and push |
@ -26,7 +25,6 @@ Equivalent allowlist patterns:
- `actions/*`
- `dtolnay/rust-toolchain@*`
- `Swatinem/rust-cache@*`
- `softprops/action-gh-release@*`
- `docker/*`
## Workflows
@ -34,7 +32,7 @@ Equivalent allowlist patterns:
| Workflow | File | Trigger |
|----------|------|---------|
| CI | `.github/workflows/ci.yml` | Pull requests to `master` |
| Beta Release | `.github/workflows/release.yml` | Push to `master` |
| Daily Beta Release | `.github/workflows/release.yml` | Daily schedule (08:00 UTC) + manual `workflow_dispatch` |
| Promote Release | `.github/workflows/promote-release.yml` | Manual `workflow_dispatch` |
## Change Control
@ -68,6 +66,11 @@ gh api repos/zeroclaw-labs/zeroclaw/actions/permissions/selected-actions
- Retained: `actions/*`, `dtolnay/rust-toolchain@*`, `softprops/action-gh-release@*`, `docker/*`
- 2026-03-05: CI build optimization — added mold linker, cargo-nextest, CARGO_INCREMENTAL=0
- sccache removed due to fragile GHA cache backend causing build failures
- 2026-03-07: Release pipeline overhaul
- Removed: `softprops/action-gh-release@*` (replaced with built-in `gh` CLI)
- Beta trigger changed from push-on-master to daily schedule + workflow_dispatch
- Added default-branch guard on beta workflow_dispatch
- Added build targets: `armv7-unknown-linux-gnueabihf`, `x86_64-apple-darwin` (cross-compiled from macos-14)
## Rollback

View File

@ -2399,7 +2399,7 @@ pub struct ClassificationRule {
pub struct HeartbeatConfig {
/// Enable periodic heartbeat pings. Default: `false`.
pub enabled: bool,
/// Interval in minutes between heartbeat pings. Default: `30`.
/// Interval in minutes between heartbeat pings. Default: `5`.
pub interval_minutes: u32,
/// Optional fallback task text when `HEARTBEAT.md` has no task entries.
#[serde(default)]
@ -2416,7 +2416,7 @@ impl Default for HeartbeatConfig {
fn default() -> Self {
Self {
enabled: false,
interval_minutes: 30,
interval_minutes: 5,
message: None,
target: None,
to: None,
@ -4974,7 +4974,7 @@ mod tests {
async fn heartbeat_config_default() {
let h = HeartbeatConfig::default();
assert!(!h.enabled);
assert_eq!(h.interval_minutes, 30);
assert_eq!(h.interval_minutes, 5);
assert!(h.message.is_none());
assert!(h.target.is_none());
assert!(h.to.is_none());

View File

@ -183,7 +183,7 @@ async fn run_heartbeat_worker(config: Config) -> Result<()> {
);
let delivery = heartbeat_delivery_target(&config)?;
let interval_mins = config.heartbeat.interval_minutes.max(5);
let interval_mins = config.heartbeat.interval_minutes.max(1);
let mut interval = tokio::time::interval(Duration::from_secs(u64::from(interval_mins) * 60));
loop {

View File

@ -33,7 +33,7 @@ impl HeartbeatEngine {
return Ok(());
}
let interval_mins = self.config.interval_minutes.max(5);
let interval_mins = self.config.interval_minutes.max(1);
info!("💓 Heartbeat started: every {} minutes", interval_mins);
let mut interval = time::interval(Duration::from_secs(u64::from(interval_mins) * 60));
@ -247,7 +247,7 @@ mod tests {
let engine = HeartbeatEngine::new(
HeartbeatConfig {
enabled: true,
interval_minutes: 30,
interval_minutes: 5,
..HeartbeatConfig::default()
},
dir.clone(),
@ -273,7 +273,7 @@ mod tests {
let engine = HeartbeatEngine::new(
HeartbeatConfig {
enabled: true,
interval_minutes: 30,
interval_minutes: 5,
..HeartbeatConfig::default()
},
dir.clone(),
@ -291,7 +291,7 @@ mod tests {
let engine = HeartbeatEngine::new(
HeartbeatConfig {
enabled: false,
interval_minutes: 30,
interval_minutes: 5,
..HeartbeatConfig::default()
},
std::env::temp_dir(),