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
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ jobs:
--health-timeout=5s
--health-retries=5

# Test against the durable in this checkout, not the one on Hex.
env:
DURABLE_PATH: ../durable

defaults:
run:
working-directory: durable_dashboard
Expand Down
162 changes: 162 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
name: Release

# Actions → Release → Run workflow, pick a package. It:
#
# 1. picks the version (the input, or the next one git-cliff derives from
# the package's conventional commits since its last tag),
# 2. bumps @version in mix.exs, regenerates the package's CHANGELOG.md,
# commits both to main and tags `<package>-v<version>`,
# 3. publishes to Hex (needs the HEX_API_KEY secret) and creates the GitHub
# release with that version's changelog as its notes.
#
# Publish is safe to retry with "Re-run failed jobs": it reuses the tag and
# skips what already went out. Release durable before a durable_dashboard
# that needs its changes, since the dashboard resolves durable from Hex.

on:
workflow_dispatch:
inputs:
package:
description: Package
type: choice
options: [durable, durable_dashboard]
version:
description: "Version, e.g. 1.2.0 (blank: next from conventional commits)"
required: false

concurrency: release

env:
ELIXIR_VERSION: "1.20"
OTP_VERSION: "29"
PACKAGE: ${{ inputs.package }}

jobs:
tag:
name: Tag
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
tag: ${{ steps.release.outputs.tag }}
version: ${{ steps.release.outputs.version }}
steps:
- uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0

- uses: taiki-e/install-action@v2
with:
tool: git-cliff

- name: Bump, changelog, commit and tag
id: release
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
cliff() { git cliff --include-path "$PACKAGE/**" --tag-pattern "^$PACKAGE-v" "$@"; }

version=${INPUT_VERSION:-$(cliff --bumped-version)}
version=${version#"$PACKAGE-v"}
tag="$PACKAGE-v$version"

if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::'$version' is not a semantic version"; exit 1
fi
if git rev-parse -q --verify "refs/tags/$tag" > /dev/null; then
echo "::error::$tag already exists"; exit 1
fi

sed -i -E "s/^([[:space:]]*@version )\"[^\"]+\"/\1\"$version\"/" "$PACKAGE/mix.exs"
grep -q "@version \"$version\"" "$PACKAGE/mix.exs"
cliff --tag "$tag" --output "$PACKAGE/CHANGELOG.md"

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add "$PACKAGE/mix.exs" "$PACKAGE/CHANGELOG.md"
git commit -m "chore($PACKAGE): release $version"
git tag -a "$tag" -m "$PACKAGE $version"
git push --atomic origin HEAD:main "$tag"

echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"

publish:
name: Publish
needs: tag
runs-on: ubuntu-latest
permissions:
contents: write
env:
TAG: ${{ needs.tag.outputs.tag }}
VERSION: ${{ needs.tag.outputs.version }}
defaults:
run:
working-directory: ${{ inputs.package }}
steps:
- uses: actions/checkout@v6
with:
ref: ${{ needs.tag.outputs.tag }}
fetch-depth: 0

- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ env.ELIXIR_VERSION }}
otp-version: ${{ env.OTP_VERSION }}

- name: Set up pnpm
if: inputs.package == 'durable_dashboard'
uses: pnpm/action-setup@v6
with:
version: 9

- name: Set up Node
if: inputs.package == 'durable_dashboard'
uses: actions/setup-node@v6
with:
node-version: "20"
cache: pnpm
cache-dependency-path: durable_dashboard/assets/pnpm-lock.yaml

- name: Install JS dependencies
if: inputs.package == 'durable_dashboard'
working-directory: durable_dashboard/assets
run: pnpm install --frozen-lockfile

# Against Hex deps, as users get it: catches a dashboard that needs an
# unreleased durable.
- name: Compile
run: |
mix deps.get
mix compile --warnings-as-errors

- name: Publish to Hex
env:
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
run: |
if curl -sfo /dev/null "https://hex.pm/api/packages/$PACKAGE/releases/$VERSION"; then
echo "$PACKAGE $VERSION is already on Hex"
else
mix hex.publish --yes
fi

- uses: taiki-e/install-action@v2
with:
tool: git-cliff

- name: GitHub release
working-directory: .
env:
GH_TOKEN: ${{ github.token }}
run: |
if gh release view "$TAG" > /dev/null 2>&1; then
echo "$TAG is already released"; exit 0
fi
git cliff --include-path "$PACKAGE/**" --tag-pattern "^$PACKAGE-v" --current --strip all > "$RUNNER_TEMP/notes.md"
flags=()
if [[ $VERSION == *-* ]]; then flags+=(--prerelease); fi
gh release create "$TAG" --verify-tag --title "$PACKAGE $VERSION" --notes-file "$RUNNER_TEMP/notes.md" "${flags[@]}"
32 changes: 26 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,35 @@ commands work: `mix deps.get`, `mix compile`, `mix test`, `mix format`,

## Cross-package references

Packages link to each other via `path:` deps in dev. To publish, swap the
`path:` line for the Hex version:
`durable_dashboard` depends on `{:durable, "~> 1.0"}` from Hex, which is what
it ships with. To build it against the local checkout, set `DURABLE_PATH`:

```elixir
# durable_dashboard/mix.exs
{:durable, path: "../durable"} # dev
{:durable, "~> 0.1"} # release
```bash
cd durable_dashboard && DURABLE_PATH=../durable mix test
```

The workspace commands and CI set it for you.

## Releasing

Each package is released on its own from GitHub Actions: **Actions → Release →
Run workflow**, then pick the package. Leave the version blank to take the
next one [git-cliff](https://git-cliff.org) derives from the package's
conventional commits since its last tag, or enter one. The workflow:

1. bumps `@version`, regenerates the package's `CHANGELOG.md` (config in
[`cliff.toml`](cliff.toml)), commits both to `main`, and tags
`durable-v<version>` or `durable_dashboard-v<version>`;
2. publishes to Hex with the `HEX_API_KEY` repository secret;
3. creates the GitHub release, with that version's changelog as its notes.

If publishing fails, "Re-run failed jobs" retries it for the same tag. Release
`durable` first when the dashboard needs its changes: the dashboard is
published against durable from Hex.

Preview a changelog locally with
`git cliff --include-path "durable/**" --tag-pattern "^durable-v" --unreleased`.

## Database

A shared `docker-compose.yml` at the workspace root brings up the Postgres
Expand Down
51 changes: 51 additions & 0 deletions cliff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# git-cliff config (https://git-cliff.org/docs/configuration).
#
# Each package has its own CHANGELOG.md and tags (`durable-v1.2.3`,
# `durable_dashboard-v1.2.3`), so every run is scoped to one package:
#
# git cliff --include-path "durable/**" --tag-pattern "^durable-v"
#
# The release workflow (.github/workflows/release.yml) does this for you.

[changelog]
header = """
# Changelog

All notable changes to this package. Versions follow [Semantic
Versioning](https://semver.org); entries come from [Conventional
Commits](https://www.conventionalcommits.org).

"""
body = """
{% set v = version | default(value="") | split(pat="-v") | last %}\
{% if version and previous.version %}\
## [{{ v }}](https://github.com/wavezync/durable/compare/{{ previous.version }}...{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{% elif version %}\
## {{ v }} - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## Unreleased
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim }}
{% for commit in commits %}
- {% if commit.breaking %}**Breaking:** {% endif %}{{ commit.message | split(pat="\n") | first | upper_first | trim }} ([{{ commit.id | truncate(length=7, end="") }}](https://github.com/wavezync/durable/commit/{{ commit.id }}))\
{% endfor %}
{% endfor %}\n
"""
trim = true

[git]
conventional_commits = true
filter_unconventional = true
protect_breaking_commits = true
sort_commits = "oldest"
commit_parsers = [
{ message = "^chore(\\(.+\\))?: (release|bump)", skip = true },
{ message = "^feat", group = "<!-- 0 -->Features" },
{ message = "^fix", group = "<!-- 1 -->Bug fixes" },
{ message = "^perf", group = "<!-- 2 -->Performance" },
{ message = "^refactor", group = "<!-- 3 -->Refactoring" },
{ message = "^docs", group = "<!-- 4 -->Documentation" },
# chore, ci, build, test, style: not user-facing.
{ message = ".*", skip = true },
]
39 changes: 39 additions & 0 deletions durable/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Changelog

All notable changes to this package. Versions follow [Semantic
Versioning](https://semver.org); entries come from [Conventional
Commits](https://www.conventionalcommits.org).

## Unreleased

### Features

- Schema for operator controls, deadlines and history ([fd69e82](https://github.com/wavezync/durable/commit/fd69e82f93ae9e5c38965300f25cd0c6281512d0))
- Telemetry events and metrics ([d3e7983](https://github.com/wavezync/durable/commit/d3e7983586ec8271ea037eb40c82ce6372157966))
- Operator controls, workflow timeouts and run observability ([0164365](https://github.com/wavezync/durable/commit/0164365438d320aaf2a3e5abfcd7be25f4d7d176))

### Bug fixes

- Stable ids for parallel and branch blocks ([333c152](https://github.com/wavezync/durable/commit/333c152301aa68608eee76e6403900fc64687c38))
- Keep a run's first start time; dashboard fixes found by page tests ([7c4f55d](https://github.com/wavezync/durable/commit/7c4f55d963ad4316ab4c2e4f033174bcda13c5ca))

### Documentation

- Workflow options, errors, telemetry and the operator API ([0cd4256](https://github.com/wavezync/durable/commit/0cd42560f0cee57acb6e5783d608abfe57251ca8))

## 0.1.0-rc - 2026-06-24

### Features

- Collapse to LiveView-first with a single React island ([cfac43d](https://github.com/wavezync/durable/commit/cfac43d6ff4c1cf9a0a5d3900143ab792a9fa948))
- Harden durable execution — sleep/wait, fencing, retries, child workflows ([6b56c35](https://github.com/wavezync/durable/commit/6b56c351073bc40c88d964e2ce01eb029e47d9c1))
- Copy buttons, full-width step inspector, faceted Executions filters ([9341826](https://github.com/wavezync/durable/commit/9341826f912b2344dae330d7c8c3288aa0402773))

### Bug fixes

- Resolve Elixir 1.20 gradual-type warnings and harden cold-build tests ([b1fbeb8](https://github.com/wavezync/durable/commit/b1fbeb8adff5d669ffc0eccdf3b2699b0187517d))

### Documentation

- Refresh READMEs for the dashboard IA + new features ([c2be144](https://github.com/wavezync/durable/commit/c2be14465f272ddf367f826aa0fee7bcb14f439e))

4 changes: 2 additions & 2 deletions durable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ A durable, resumable workflow engine for Elixir. Similar to Temporal/Inngest.

```elixir
def deps do
[{:durable, "~> 0.1.0-rc"}]
[{:durable, "~> 1.0"}]
end
```

Expand Down Expand Up @@ -599,7 +599,7 @@ in-place — no `forward`, no extra endpoint. You get:
- Settings view
- ⌘K command palette — jump to any page, workflow, or recent run

See [`durable_dashboard/README.md`](durable_dashboard/) for the full
See the [durable_dashboard docs](https://hexdocs.pm/durable_dashboard) for the full
option list and design notes.

## Guides
Expand Down
2 changes: 1 addition & 1 deletion durable/guides/ai_workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Build reliable AI agent workflows with automatic retries, state persistence, and
# mix.exs
defp deps do
[
{:durable, "~> 0.1.0-rc"},
{:durable, "~> 1.0"},
{:req_llm, "~> 1.1"}
]
end
Expand Down
5 changes: 3 additions & 2 deletions durable/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ defmodule Durable.MixProject do
[
main: "readme",
source_url: @source_url,
source_ref: "v#{@version}",
source_ref: "durable-v#{@version}",
extras: [
"README.md",
"CHANGELOG.md",
"guides/ai_workflows.md",
"guides/branching.md",
"guides/compensations.md",
Expand All @@ -121,7 +122,7 @@ defmodule Durable.MixProject do
maintainers: @maintainers,
licenses: @licenses,
links: %{"GitHub" => @source_url, "Homepage" => @homepage_url},
files: ~w(lib priv .formatter.exs mix.exs README.md LICENSE shared.exs)
files: ~w(lib priv .formatter.exs mix.exs README.md CHANGELOG.md LICENSE shared.exs)
]
end
end
42 changes: 42 additions & 0 deletions durable_dashboard/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Changelog

All notable changes to this package. Versions follow [Semantic
Versioning](https://semver.org); entries come from [Conventional
Commits](https://www.conventionalcommits.org).

## Unreleased

### Features

- V1 redesign — pages, design system, tooltips and query bar ([c05e87c](https://github.com/wavezync/durable/commit/c05e87c3ca40a800838800f7141b713e5d6e9292))
- Workflow graph with joins and branch gateways ([2a65c4d](https://github.com/wavezync/durable/commit/2a65c4dba4f772b0863b60cadb30919ffdb72bf7))

### Bug fixes

- Keep a run's first start time; dashboard fixes found by page tests ([7c4f55d](https://github.com/wavezync/durable/commit/7c4f55d963ad4316ab4c2e4f033174bcda13c5ca))

## 0.1.0-rc - 2026-06-24

### Features

- Add durable_dashboard package ([d170094](https://github.com/wavezync/durable/commit/d170094e28b99f4b1b7ccf4dc66f5a83afef2668))
- Collapse to LiveView-first with a single React island ([cfac43d](https://github.com/wavezync/durable/commit/cfac43d6ff4c1cf9a0a5d3900143ab792a9fa948))
- Add setup/assets aliases and bundle assets on hex publish ([b0c8aa9](https://github.com/wavezync/durable/commit/b0c8aa99f9256bd75d617693050c2b90e5849af5))
- Redesign shell around the instrument-panel direction ([54f6a09](https://github.com/wavezync/durable/commit/54f6a0918bd74a0efc0e07fe0c135f7148a0f7d2))
- Shared UI primitives & display components ([c433ebb](https://github.com/wavezync/durable/commit/c433ebb32d83adcdcf74bc79b945f92a73307424))
- Workflow flow-graph island ([d6f2f9a](https://github.com/wavezync/durable/commit/d6f2f9a16b55a69559a50feb0ff63e23428bfd58))
- Workflow detail tabs ([7ea5c1c](https://github.com/wavezync/durable/commit/7ea5c1c2a58939aa1f277a24b50c6d5fe144318a))
- Overview, executions, workflows & settings pages ([58fba94](https://github.com/wavezync/durable/commit/58fba946e8e109e1ce74be6c899a8472c3a99025))
- Pulse brand mark, live ⌘K palette, topbar toolbar, wider inspector ([a1b2590](https://github.com/wavezync/durable/commit/a1b25909d18711fee16f97e7562dbde4980ef001))
- Copy buttons, full-width step inspector, faceted Executions filters ([9341826](https://github.com/wavezync/durable/commit/9341826f912b2344dae330d7c8c3288aa0402773))
- Workspace and dashboard precommit tooling ([daa10c9](https://github.com/wavezync/durable/commit/daa10c98679b7c04e3aefba36eb0796feaa7d1b0))

### Bug fixes

- Unbreak overview links and reorder Recent workflows ([3ae6c8f](https://github.com/wavezync/durable/commit/3ae6c8f759054c02423df482a57ffcaf88e7ca40))
- Resolve Elixir 1.20 gradual-type warnings and harden cold-build tests ([b1fbeb8](https://github.com/wavezync/durable/commit/b1fbeb8adff5d669ffc0eccdf3b2699b0187517d))

### Documentation

- Refresh READMEs for the dashboard IA + new features ([c2be144](https://github.com/wavezync/durable/commit/c2be14465f272ddf367f826aa0fee7bcb14f439e))

Loading
Loading