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
211 changes: 211 additions & 0 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright The Lance Authors

name: Create Release

on:
workflow_dispatch:
inputs:
release_type:
description: 'Version bump type (patch/minor/major bumps version, current keeps it unchanged)'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
- current
release_channel:
description: 'Release channel (preview creates beta tag, stable creates release tag)'
required: true
default: 'preview'
type: choice
options:
- preview
- stable
dry_run:
description: 'Dry run (simulate the release without pushing)'
required: true
default: true
type: boolean

permissions:
contents: write # commit + tag + push
actions: write # gh workflow run release.yml

jobs:
create-release:
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Output Inputs
run: echo "${{ toJSON(github.event.inputs) }}"

- name: Checkout repository
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
# persist-credentials defaults to true → git push uses GITHUB_TOKEN.

- uses: actions-rust-lang/setup-rust-toolchain@v1

- name: Install cargo-edit
run: cargo install cargo-edit --locked

- name: Get current version
id: current_version
run: |
CURRENT=$(grep '^version = ' Cargo.toml | head -1 | cut -d '"' -f2)
echo "version=$CURRENT" >>"$GITHUB_OUTPUT"
echo "Current version: $CURRENT"

- name: Calculate base version
id: base_version
run: |
CURRENT="${{ steps.current_version.outputs.version }}"
# Strip any pre-release suffix (e.g. "0.1.0-beta.3" -> "0.1.0")
BASE_CURRENT="${CURRENT%%-*}"
IFS='.' read -r MAJOR MINOR PATCH <<<"$BASE_CURRENT"

case "${{ inputs.release_type }}" in
major)
MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0
;;
minor)
MINOR=$((MINOR + 1)); PATCH=0
;;
patch)
PATCH=$((PATCH + 1))
;;
current)
# Keep the base version unchanged; useful for cutting another beta
# against the same base (e.g. v0.2.0-beta.2 after v0.2.0-beta.1).
;;
*)
echo "Unknown release_type: ${{ inputs.release_type }}" >&2
exit 1
;;
esac

BASE="${MAJOR}.${MINOR}.${PATCH}"
echo "version=$BASE" >>"$GITHUB_OUTPUT"
echo "Base version: $BASE"

- name: Determine tag and crate version
id: versions
run: |
BASE_VERSION="${{ steps.base_version.outputs.version }}"
CURRENT_VERSION="${{ steps.current_version.outputs.version }}"
if [ "${{ inputs.release_channel }}" = "stable" ]; then
TAG="v${BASE_VERSION}"
CRATE_VERSION="${BASE_VERSION}"
else
# For preview releases, find the next beta number for this base version
BETA_TAGS=$(git tag -l "v${BASE_VERSION}-beta.*" | sort -V)
if [ -z "$BETA_TAGS" ]; then
BETA_NUM=1
else
LAST_BETA=$(echo "$BETA_TAGS" | tail -n 1)
PREFIX="v${BASE_VERSION}-beta."
LAST_NUM="${LAST_BETA#"$PREFIX"}"
BETA_NUM=$((LAST_NUM + 1))
fi
TAG="v${BASE_VERSION}-beta.${BETA_NUM}"
CRATE_VERSION="${BASE_VERSION}-beta.${BETA_NUM}"
fi

if [ "$CURRENT_VERSION" != "$CRATE_VERSION" ]; then
VERSION_CHANGED="true"
else
VERSION_CHANGED="false"
fi

# Refuse to push if the tag already exists.
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "Tag $TAG already exists; aborting." >&2
exit 1
fi

{
echo "tag=$TAG"
echo "crate_version=$CRATE_VERSION"
echo "version_changed=$VERSION_CHANGED"
} >>"$GITHUB_OUTPUT"
echo "Tag will be: $TAG"
echo "Crate version will be: $CRATE_VERSION"
echo "Version changed: $VERSION_CHANGED"

- name: Update Cargo.toml + Cargo.lock
if: steps.versions.outputs.version_changed == 'true'
run: |
cargo set-version --package lance-c "${{ steps.versions.outputs.crate_version }}"

- name: Configure git identity
run: |
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]@users.noreply.github.com'

- name: Create release commit
if: steps.versions.outputs.version_changed == 'true'
run: |
git add Cargo.toml Cargo.lock
git commit -m "chore: bump version to ${{ steps.versions.outputs.crate_version }}"

- name: Create tag
run: |
git tag -a "${{ steps.versions.outputs.tag }}" \
-m "Release ${{ steps.versions.outputs.tag }}"

- name: Push commit + tag (if not dry run)
if: ${{ !inputs.dry_run }}
run: |
if [ "${{ steps.versions.outputs.version_changed }}" = "true" ]; then
git push origin main
fi
git push origin "${{ steps.versions.outputs.tag }}"

# GITHUB_TOKEN-pushed refs do NOT trigger downstream workflows
# (GitHub's recursion guard). We dispatch the Release workflow at the
# new tag's ref so its publish job (gated on refs/tags/v*) can fire.
- name: Trigger Release workflow on the new tag
if: ${{ !inputs.dry_run }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh workflow run release.yml \
--ref "${{ steps.versions.outputs.tag }}" \
-f version="${{ steps.versions.outputs.crate_version }}"

- name: Summary
run: |
{
echo "## Release Summary"
echo ""
echo "- **Release Type:** ${{ inputs.release_type }}"
echo "- **Release Channel:** ${{ inputs.release_channel }}"
echo "- **Current Version:** ${{ steps.current_version.outputs.version }}"
if [ "${{ steps.versions.outputs.version_changed }}" = "true" ]; then
echo "- **New Version:** ${{ steps.versions.outputs.crate_version }}"
fi
echo "- **Tag:** ${{ steps.versions.outputs.tag }}"
echo "- **Dry Run:** ${{ inputs.dry_run }}"
} >>"$GITHUB_STEP_SUMMARY"

if [ "${{ inputs.dry_run }}" = "true" ]; then
{
echo ""
echo "⚠️ This was a dry run. No commits, tags, or releases were pushed."
} >>"$GITHUB_STEP_SUMMARY"
else
{
echo ""
echo "✅ Tag pushed and Release workflow dispatched."
echo ""
echo "### Next Steps:"
echo "1. Watch the [Release workflow](https://github.com/${{ github.repository }}/actions/workflows/release.yml) finish (~20 min)."
echo "2. Verify the published assets at the [release page](https://github.com/${{ github.repository }}/releases/tag/${{ steps.versions.outputs.tag }})."
echo "3. Copy the SHA512 snippet from the publish job log into vcpkg/conan recipes."
} >>"$GITHUB_STEP_SUMMARY"
fi
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,4 @@ jobs:
dist/SHA512SUMS
generate_release_notes: true
fail_on_unmatched_files: true
prerelease: ${{ contains(github.ref_name, '-beta') || contains(github.ref_name, '-rc') }}
34 changes: 28 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,33 @@ auto ds = lance::Dataset::open("data.lance", {}, /*version=*/42);

## Releasing

Releases are tag-driven via [`release.yml`](.github/workflows/release.yml).
Releases are tag-driven: pushing a `v*.*.*` tag fires [`release.yml`](.github/workflows/release.yml), which builds prebuilt tarballs for `linux-{x86_64,aarch64}` and `macos-{x86_64,aarch64}` and attaches them to a GitHub Release. Beta tags (`v*-beta.*`) are published as pre-releases.

### Recommended: cut a release via Actions UI

[`create-release.yml`](.github/workflows/create-release.yml) is a `workflow_dispatch` entry point that bumps `Cargo.toml`, commits, tags, and pushes — replacing the manual edit/commit/tag steps below.

1. Open Actions → **Create Release** → **Run workflow** on `main`.
2. Choose:
- **release_type**: `patch` / `minor` / `major` (or `current` to cut another beta on the same base, e.g. `v0.2.0-beta.2` after `v0.2.0-beta.1`).
- **release_channel**: `preview` (tags `vX.Y.Z-beta.N`, auto-incremented) or `stable` (tags `vX.Y.Z`).
- **dry_run**: leave on for the first run to preview the computed tag/version without pushing anything.
3. Re-run with **dry_run** off. The workflow:
- Bumps `version = ...` in `Cargo.toml` and refreshes `Cargo.lock` via `cargo set-version` (skipped for `current` if the version is already correct).
- Commits as `github-actions[bot]` with message `chore: bump version to <version>`.
- Pushes the commit to `main` and pushes the tag.
- Dispatches `release.yml` at the new tag's ref. (Direct tag push by `GITHUB_TOKEN` does not trigger workflows — GitHub's recursion guard — so we explicitly `gh workflow run` instead.)
4. `release.yml` builds artifacts. ~20 minutes later the [GitHub Release](https://github.com/lance-format/lance-c/releases) has all four `.tar.xz` artifacts plus a `SHA512SUMS` file.
5. The `publish` job's log emits a paste-ready `set(LANCE_C_SHA512_... "...")` snippet. Copy it into:
- [`ports/lance-c/portfile.cmake`](ports/lance-c/portfile.cmake) (SHA512s)
- [`recipes/lance-c/all/conandata.yml`](recipes/lance-c/all/conandata.yml) (SHA256s, derived from the `.sha256` files in the release assets)
6. Open follow-up PRs to `microsoft/vcpkg` and `conan-io/conan-center-index` mirroring the updated `ports/` and `recipes/` directories.

> **Branch protection:** if `main` is a protected branch, allow `github-actions[bot]` (or the GitHub Actions integration) to bypass push restrictions, or replace the default `GITHUB_TOKEN` in `create-release.yml` with a PAT that has `contents: write`.

### Manual fallback

If you need to cut a release without the workflow (e.g. local tag with extra commits):

1. Decide the new version (semver). Pre-1.0 (`0.x.y`): bump **minor** for breaking changes or new features, **patch** for bug fixes only.
2. On `main`, bump `version = ...` in [`Cargo.toml`](Cargo.toml) and refresh `Cargo.lock`:
Expand All @@ -202,11 +228,7 @@ Releases are tag-driven via [`release.yml`](.github/workflows/release.yml).
git tag v0.2.0
git push origin v0.2.0
```
5. `release.yml` fires on the tag push and builds prebuilt tarballs for `linux-{x86_64,aarch64}` and `macos-{x86_64,aarch64}`. ~20 minutes later, the [GitHub Release](https://github.com/lance-format/lance-c/releases) has all four `.tar.xz` artifacts plus a `SHA512SUMS` file.
6. The `publish` job's log emits a paste-ready `set(LANCE_C_SHA512_... "...")` snippet. Copy it into:
- [`ports/lance-c/portfile.cmake`](ports/lance-c/portfile.cmake) (SHA512s)
- [`recipes/lance-c/all/conandata.yml`](recipes/lance-c/all/conandata.yml) (SHA256s, derived from the `.sha256` files in the release assets)
7. Open follow-up PRs to `microsoft/vcpkg` and `conan-io/conan-center-index` mirroring the updated `ports/` and `recipes/` directories.
5. `release.yml` fires on the tag push and builds the four prebuilt tarballs as above.

A `workflow_dispatch` trigger on `release.yml` lets you do dry-run builds without cutting a tag — Actions tab → "Release" → "Run workflow" → enter a version like `0.0.1-dev`. The `publish` job is skipped (gated on `refs/tags/v`), but the build matrix runs end-to-end so you can validate it before the real tag.

Expand Down
Loading