Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ Go module cache is persisted in a Docker volume (`aethel-gomod`) for fast repeat

## Release Process

Two-workflow split in GitHub Actions:
Single workflow (`release.yml`) with two jobs:

1. **`release.yml`** — triggers on push to master. Analyzes conventional commits since last tag, computes version bump (major/minor/patch), updates `VERSION` + `CHANGELOG.md`, commits `chore(release): vX.Y.Z`, creates git tag, pushes. Does NOT build binaries or create GitHub Release.
2. **`goreleaser.yml`** — triggers on tag push (`v*`). Runs GoReleaser to cross-compile 5 platforms (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64), creates `.tar.gz` (Unix) / `.zip` (Windows) archives with both `aethel` + `aetheld`, publishes GitHub Release with SHA256 checksums.
1. **`release` job** — triggers on push to master. Analyzes conventional commits since last tag, computes version bump (major/minor/patch), updates `VERSION` + `CHANGELOG.md`, commits `chore(release): vX.Y.Z`, creates git tag, pushes. Outputs version to the next job.
2. **`goreleaser` job** — runs after `release` job. Checks out the tagged commit, runs GoReleaser to cross-compile 5 platforms (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64), creates `.tar.gz` (Unix) / `.zip` (Windows) archives with both `aethel` + `aetheld`, publishes GitHub Release with SHA256 checksums.

GoReleaser config: `.goreleaser.yml` (version 2). Version injected via `-ldflags "-s -w -X main.version={{.Version}}"` on both binaries.
GoReleaser config: `.goreleaser.yml` (version 2). Version injected via `-ldflags "-s -w -X main.version={{.Version}}"` on both binaries. Note: both jobs are in one workflow because tags pushed with `GITHUB_TOKEN` don't trigger other workflows.

Install script: `scripts/install.sh` — POSIX shell, detects OS/arch, downloads from GitHub Releases, verifies checksum, installs to `~/.local/bin/`.

Expand Down
3 changes: 3 additions & 0 deletions .claude/agent-memory/code-reviewer/MEMORY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Code Reviewer Memory Index

- [feedback_env_interpolation.md](feedback_env_interpolation.md) — security feedback: pass version strings via `env:` blocks, not raw `${{ }}` interpolation in run scripts
11 changes: 11 additions & 0 deletions .claude/agent-memory/code-reviewer/feedback_env_interpolation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
name: env_interpolation_security
description: Pass untrusted values via env: blocks in GitHub Actions run steps, not raw ${{ }} interpolation
type: feedback
---

Version strings and other potentially untrusted values must be passed through `env:` blocks in GitHub Actions `run:` steps rather than being interpolated directly with `${{ }}` inside shell script bodies.

**Why:** Script injection risk — if an attacker controls the value (e.g., via a crafted tag), raw interpolation can break out of the shell expression. Env vars are safe because the shell treats them as data, not code.

**How to apply:** Any time a `${{ }}` expression would appear inside the body of a `run:` block (not in `with:` or `if:` expressions), lift it to an `env:` block and reference it as `$ENV_VAR_NAME` in the script.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ jobs:
if: inputs.dry_run == true
run: echo "::notice::DRY RUN — this is a manual test of the CI workflow"

- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- uses: actions/setup-go@v5
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version: '1.25'

Expand Down
40 changes: 0 additions & 40 deletions .github/workflows/goreleaser.yml

This file was deleted.

57 changes: 51 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,26 @@ on:
type: boolean
default: true

permissions:
contents: write

env:
DRY_RUN: ${{ inputs.dry_run || false }}

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write # commit + tag version bump
# Skip release commits to prevent infinite loop
if: "!startsWith(github.event.head_commit.message, 'chore(release):')"
outputs:
version: ${{ steps.bump.outputs.version }}
skip: ${{ steps.bump.outputs.skip }}
dry_run: ${{ env.DRY_RUN }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

- uses: actions/setup-go@v5
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version: '1.25'

Expand Down Expand Up @@ -121,4 +124,46 @@ jobs:
git commit -m "chore(release): v${VERSION}"
git tag "v${VERSION}"
git push origin master --tags
echo "::notice::Tagged v${VERSION} — GoReleaser workflow will build and publish the release"
echo "::notice::Tagged v${VERSION}"

goreleaser:
needs: release
if: needs.release.outputs.skip != 'true' && needs.release.outputs.dry_run != 'true'
runs-on: ubuntu-latest
permissions:
contents: write # publish GitHub release assets
steps:
- name: Validate version
env:
VERSION: ${{ needs.release.outputs.version }}
run: |
echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$' || {
echo "::error::Invalid or empty version: '$VERSION'"
exit 1
}

- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: v${{ needs.release.outputs.version }}
fetch-depth: 1

- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version: '1.25'

- name: Extract release notes from CHANGELOG.md
env:
VERSION: ${{ needs.release.outputs.version }}
run: |
sed -n "/^## \[${VERSION}\]/,/^## \[/{ /^## \[${VERSION}\]/d; /^## \[/d; p; }" CHANGELOG.md \
| tr -d '\r' > /tmp/release-notes.md
echo "::notice::Release notes for v${VERSION}:"
cat /tmp/release-notes.md

- uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6
with:
distribution: goreleaser
version: "~> v2"
args: release --clean --release-notes /tmp/release-notes.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- **GoReleaser workflow not triggering** — tags pushed with `GITHUB_TOKEN` don't trigger other workflows; merged goreleaser into `release.yml` as a second job with `needs: release`
- **Dry run executing goreleaser** — boolean vs string comparison bug in job `if:` condition; `DRY_RUN` now forwarded through job outputs as string
- **Actions pinned to commit SHAs** — `actions/checkout`, `actions/setup-go`, `goreleaser/goreleaser-action` pinned to immutable SHAs for supply-chain security
- **Per-job permissions** — `contents: write` moved from workflow-level to per-job blocks for least-privilege

## [0.10.0] - 2026-03-24

### Added

- **Roadmap PRDs** — 11 detailed Product Requirements Documents in `docs/roadmap/`: workspace files, MCP server, command palette, notification center, pre-built binaries, demo GIF, community plugins, process health, tmux migration, cross-pane events, session sharing
- **Restructured ROADMAP.md** — organized into Core/Growth/Advanced categories with priority matrix, strategic pain-layer analysis, and feature synergy notes
- **Notification center concept (M12)** — centralized event sidebar with pane navigation and history stack; PRD covers process exit detection, plugin notification handlers, and incremental integration path
- **Pre-built binaries & release infrastructure** — GoReleaser config for 5 platforms (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64); two-workflow CI split: `release.yml` handles version bump + tag, `goreleaser.yml` builds + publishes GitHub Release with `.tar.gz`/`.zip` archives and SHA256 checksums
- **Pre-built binaries & release infrastructure** — GoReleaser config for 5 platforms (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64); `release.yml` handles version bump + tag + GoReleaser build, publishes GitHub Release with `.tar.gz`/`.zip` archives and SHA256 checksums
- **One-line install script** — `scripts/install.sh` detects OS/arch, fetches latest release from GitHub API, verifies SHA256 checksum, installs to `~/.local/bin/`; supports `AETHEL_VERSION` for pinned installs and `GITHUB_TOKEN` for API auth
- **Daemon version reporting** — `aetheld version` subcommand, version logged at startup; consistent `-ldflags` injection across all build paths (GoReleaser, dev.sh, dev.ps1, rebuild.ps1, Makefile)

Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Processes emit events when they finish or need attention. A non-modal sidebar sh

> `curl -sSfL .../install.sh | sh` — zero friction install.

GoReleaser cross-compiles 5 platform pairs (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64) with SHA256 checksums. Two-workflow split: `release.yml` handles version bump + tag, `goreleaser.yml` builds + publishes GitHub Release. Install script for Linux/macOS. **Homebrew tap, Scoop, Winget deferred** (need external repos).
GoReleaser cross-compiles 5 platform pairs (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64) with SHA256 checksums. Single `release.yml` workflow: version bump + tag job, then GoReleaser build + publish job. Install script for Linux/macOS. **Homebrew tap, Scoop, Winget deferred** (need external repos).

### The "Holy Shit" Demo — [PRD](docs/roadmap/demo-gif.md)

Expand Down
Loading