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
125 changes: 125 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
contents: read

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
verify:
name: verify release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: dtolnay/rust-toolchain@2c7215f132e9ebf062739d9130488b56d53c060c # master
with:
toolchain: 1.97.1
components: clippy,rustfmt
- name: Check tag and version
shell: bash
run: |
set -euo pipefail
tag="${GITHUB_REF_NAME}"
if [[ ! "${tag}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta|rc)\.[0-9]+)?$ ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify release tags are on main

The release policy added in docs/releases.md requires release tags to point at main after CI, but this workflow accepts any matching v* tag and the publish step only uses gh release create --verify-tag, which the gh manual documents as checking that the tag already exists. If a maintainer accidentally pushes a semver tag from an unmerged release-prep or feature branch, these jobs can still publish archives and a GitHub release for code that never landed on main; add a git branch --remotes --contains "$GITHUB_SHA"/origin/main containment check before publishing.

Useful? React with 👍 / 👎.

echo "invalid release tag: ${tag}" >&2
exit 1
fi
version="$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -n 1)"
if [[ "v${version}" != "${tag}" ]]; then
echo "Cargo version ${version} does not match tag ${tag}" >&2
exit 1
fi
if ! grep -Fq "## [${version}]" CHANGELOG.md; then
echo "CHANGELOG.md has no ${version} release heading" >&2
exit 1
fi
- run: cargo fetch --locked
- run: cargo fetch --manifest-path bench/sd-card-emulator/Cargo.toml --locked
- run: python3 -m unittest -v bench.adapters.test_questdb
- run: cargo fmt --all -- --check
- run: cargo clippy --all-targets --all-features --locked --offline -- -D warnings
- run: cargo test --all-targets --all-features --locked --offline
- run: cargo test --doc --all-features --locked --offline
- run: cargo fmt --manifest-path bench/sd-card-emulator/Cargo.toml -- --check
- run: cargo clippy --manifest-path bench/sd-card-emulator/Cargo.toml --all-targets --locked --offline -- -D warnings
- run: cargo test --manifest-path bench/sd-card-emulator/Cargo.toml --all-targets --locked --offline
- run: cargo package --locked --offline

build:
name: build (${{ matrix.os }})
needs: verify
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: dtolnay/rust-toolchain@2c7215f132e9ebf062739d9130488b56d53c060c # master
with:
toolchain: 1.97.1
- run: cargo build --release --locked
- name: Build archive
id: archive
shell: bash
run: |
set -euo pipefail
host="$(rustc -vV | sed -n 's/^host: //p')"
name="ftw-${GITHUB_REF_NAME}-${host}"
mkdir -p "dist/${name}"
cp target/release/ftw README.md CHANGELOG.md LICENSE "dist/${name}/"
tar -C dist -czf "dist/${name}.tar.gz" "${name}"
(
cd dist
shasum -a 256 "${name}.tar.gz" > "${name}.tar.gz.sha256"
)
echo "name=${name}" >> "${GITHUB_OUTPUT}"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: release-${{ steps.archive.outputs.name }}
path: |
dist/${{ steps.archive.outputs.name }}.tar.gz
dist/${{ steps.archive.outputs.name }}.tar.gz.sha256
if-no-files-found: error
retention-days: 14

publish:
name: publish GitHub release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
fetch-depth: 0
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: release-*
path: dist
merge-multiple: true
- name: Publish release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
sort -k2 dist/*.sha256 > dist/SHA256SUMS
prerelease=()
if [[ "${GITHUB_REF_NAME}" == *-* ]]; then
prerelease+=(--prerelease)
fi
gh release create "${GITHUB_REF_NAME}" \
dist/*.tar.gz dist/SHA256SUMS \
--repo "${GITHUB_REPOSITORY}" \
--verify-tag \
--title "FTWDB ${GITHUB_REF_NAME}" \
--notes-from-tag \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require annotated tags before using tag notes

The new process relies on annotated tags to carry the release notes, but --notes-from-tag does not enforce that: the gh manual says a non-annotated tag falls back to the commit message. If someone pushes a lightweight v... tag, this workflow still publishes a release, but the notes can omit the known limits/platform support required by the release policy; add an explicit git cat-file -t "$GITHUB_REF_NAME" check for tag in the verify job.

Useful? React with 👍 / 👎.

"${prerelease[@]}"
46 changes: 46 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Changelog

This file records user-visible FTWDB changes. The project follows
[Semantic Versioning](https://semver.org/spec/v2.0.0.html) and keeps release
steps in [docs/releases.md](docs/releases.md).

## [Unreleased]

## [0.1.0-alpha.1] - 2026-07-21

First public evaluation release.

### Added

- Checksummed atomic point and catalog transactions with explicit durability.
- Valid, knowledge, and change time plus run and plan records.
- Immutable raw segments, persistent fixed and calendar rollups, late-data
rebuilds, and retention safety checks.
- Read-only integrity checks, verified snapshot backup, strict no-clobber
restore, and conservative raw-log salvage.
- File locking, commit identifiers for safe retry, bounded decoders, and
structured command errors.
- Deterministic energy and TSBS workloads, a sanitized real-data fixture, an
SD-card emulator, and result-verified VictoriaMetrics and QuestDB subsets.

### Verified

- CI passes on Ubuntu Linux and macOS with Rust 1.97.1.
- Process-kill, full-disk, sync-failure, corruption, recovery, CLI, backup,
restore, and salvage checks pass.
- The crate packages and verifies from its release source.

### Known limits

- FTWDB supports Unix targets only.
- The active log still rebuilds an in-memory read index, and physical raw-log
reclamation remains disabled.
- Physical target-board and SD-card power cuts during commits, long soak runs,
and remote backup policy remain open M4 work.
- Competitor adapters cover stated subsets and do not claim equal durability
or full model support.
- This release does not carry a production support or file-format stability
promise.

[Unreleased]: https://github.com/srcfl/ftwdb/compare/v0.1.0-alpha.1...HEAD
[0.1.0-alpha.1]: https://github.com/srcfl/ftwdb/releases/tag/v0.1.0-alpha.1
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
[package]
name = "ftwdb"
version = "0.1.0"
version = "0.1.0-alpha.1"
edition = "2024"
rust-version = "1.97"
description = "Forecasts, Telemetry & Watts: an SD-card-conscious embedded energy database"
license = "Apache-2.0"
repository = "https://github.com/srcfl/ftwdb"
readme = "README.md"
keywords = ["database", "time-series", "embedded", "energy"]
categories = ["database-implementations"]

[package.metadata.ftwdb]
support = "Unix targets only; Linux and macOS are tested in CI"
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ small Rust engine that is fast on time-window aggregates, survives abrupt power
loss, and minimizes write amplification on SD cards and other constrained edge
storage.

The current release is **v0.1.0-alpha.1**. It is an evaluation build for Unix
systems, not a production release. Download release archives from
[GitHub Releases](https://github.com/srcfl/ftwdb/releases), or install the CLI
from its immutable tag:

```sh
cargo install --git https://github.com/srcfl/ftwdb --tag v0.1.0-alpha.1 --locked --bin ftw
```

This repository currently contains the first executable storage slice:

- append-only, checksummed atomic batches;
Expand Down Expand Up @@ -78,6 +87,7 @@ cargo run --release -- salvage ./damaged-energy.ftwdb ./salvaged-energy.ftwdb
- [Deterministic energy workload](docs/workload.md)
- [Bootstrap benchmark result](docs/results/2026-07-21-macos-arm64.md)
- [TSBS and robustness result](docs/results/2026-07-21-tsbs-robustness.md)
- [Release policy and process](docs/releases.md)
- [Roadmap](docs/roadmap.md)

## License
Expand Down
72 changes: 72 additions & 0 deletions docs/releases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Release policy and process

FTWDB uses Semantic Versioning for the crate, tags, and GitHub releases. A tag
has the form `vMAJOR.MINOR.PATCH` or
`vMAJOR.MINOR.PATCH-(alpha|beta|rc).NUMBER`. The version in `Cargo.toml`, the
changelog heading, and the tag without its leading `v` must match exactly.

## Release channels

- **Alpha** is for evaluation. Core safety tests and packaging must pass. Known
hardware, scale, and support gaps must appear in the changelog.
- **Beta** requires target-board and physical SD-card tests, power cuts during
commits, a long soak run, and a tested operator backup policy.
- **Release candidate** freezes the public API and on-disk format for the
planned stable release. Only release-blocking fixes may follow.
- **Stable** requires the relevant roadmap exit, a stated compatibility and
support window, and no open release-blocking defect.

Pre-release versions may break APIs and formats between releases. Stable
versions follow the compatibility promise published with that release.

## Tag rules

Release tags point to a commit on `main` after a release-prep pull request has
passed CI. Create annotated tags so the tag records its release notes. Sign a
tag only when the maintainer has a configured key whose public identity users
can check; never claim an unsigned tag is signed.

Never move, reuse, or replace a published tag. If a release has a defect, mark
it as withdrawn in its notes and publish the next version. Delete a tag only to
remove exposed secrets or meet a legal requirement, and record that event.

## Required alpha checks

1. The working tree starts from current `origin/main`.
2. GitHub has no open release-blocking issue or pull request.
3. `Cargo.toml`, `Cargo.lock`, and `CHANGELOG.md` name the same version.
4. Format, Clippy, all targets and features, documentation tests, Python
adapter tests, and SD-emulator tests pass with locked dependencies.
5. `cargo package --locked` builds and verifies the crate.
6. Release notes list platform support and all known safety gaps.
7. The release-prep pull request passes Linux and macOS CI and merges to
`main` before the tag is created.

## Automated publication

Pushing a matching tag starts `.github/workflows/release.yml`. The workflow:

1. checks the tag syntax and exact Cargo/changelog version;
2. repeats the release test and package checks;
3. builds native `ftw` archives on GitHub's Linux and macOS runners;
4. creates `SHA256SUMS` for the archives; and
5. creates a GitHub prerelease when the tag contains a pre-release suffix.

The archive name includes the tag and Rust host target. Each archive contains
the `ftw` binary, README, changelog, and license. GitHub Actions records the
source commit and workflow run. Alpha releases stay on GitHub; publishing the
crate to crates.io needs a separate decision and an explicit publish step.

## Maintainer steps

1. Create a release-prep branch from `origin/main`.
2. Update the Cargo version, changelog, docs, and any compatibility statement.
3. Run all required checks, open a pull request, and wait for CI.
4. Merge the pull request and update local remote references.
5. Create an annotated tag on the exact merge commit with concise release
notes and known limits.
6. Push only that tag and watch the Release workflow to completion.
7. Check the GitHub release, both archives, `SHA256SUMS`, and the source commit.

Do not create a release from a feature branch, a dirty tree, or a commit whose
CI result is unknown.
7 changes: 5 additions & 2 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Roadmap

## M0: durability and semantics — in progress
## M0: durability and semantics — initial exit complete

- checksummed append frames and tail recovery;
- explicit durability modes;
Expand All @@ -9,7 +9,10 @@
- mergeable gauge/counter aggregate states;
- property, corruption, and microbenchmark coverage.

Exit: recovery invariants pass under randomized truncation and bit flips.
Initial exit met: property tests cover every truncation point in the final
frame, complete checksum-corrupt frames stay intact and return corruption, and
earlier frame corruption never becomes tail recovery. Broader media-fault and
physical power-cut work continues in M4.

## M1: catalog, plans, and transactional records — complete

Expand Down
Loading