Skip to content

Commit 0816ceb

Browse files
add release script (#636)
add release script to start automating our releases with `cargo-release` and `git-cliff`. the main release script is `./release.sh` with rather straightforward usage. we use `cargo-release` to handle some of the `Cargo.toml` updates and trigger git-cliff, but otherwise the script just manually uses cargo/git to publish the release and tag it the release flow will be the following: 1. from an up-to-date `main` branch, checkout a new release branch (like `prepare 0.x.y`) 2. run `./release 0.x.y` to do the 'prepare' phase of generating a changelog, updating readme etc. a. the tool will validate that you're on a release branch, generate the changelog, and make a commit and (optionally) open a PR (cargo-release is used to update the versions correctly and git-cliff is used as a hook in cargo-release to generate the changelog) b. then the only main action is just making minor CHANGELOG changes and updating + merging the PR 3. merge the 'prepare' PR 4. from `main` branch with prepare branch merged, run: `./release.sh` a. the tool will publish any unpublished version (e.g. if 0.6.0 is latest on crates.io and the prepare PR bumps to 0.6.1 then the tool will do the release for `delta_kernel` and `delta_kernel_derive`, add a git tag, and push the tag) b. NOTE: this is currently done manually but could leverage cargo-release in the future
1 parent fafc776 commit 0816ceb

File tree

14 files changed

+290
-0
lines changed

14 files changed

+290
-0
lines changed

Diff for: .github/pull_request_template.md

+13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ Thanks for sending a pull request! Here are some tips for you:
77
5. Be sure to keep the PR description updated to reflect all changes.
88
-->
99

10+
<!--
11+
PR title formatting:
12+
This project uses conventional commits: https://www.conventionalcommits.org/
13+
14+
Each PR corresponds to a commit on the `main` branch, with the title of the PR (typically) being
15+
used for the commit message on main. In order to ensure proper formatting in the CHANGELOG please
16+
ensure your PR title adheres to the conventional commit specification.
17+
18+
Examples:
19+
- new feature PR: "feat: new API for snapshot.update()"
20+
- bugfix PR: "fix: correctly apply DV in read-table example"
21+
-->
22+
1023
## What changes are proposed in this pull request?
1124
<!--
1225
Please clarify what changes you are proposing and why the changes are needed.

Diff for: acceptance/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ readme.workspace = true
1010
version.workspace = true
1111
rust-version.workspace = true
1212

13+
[package.metadata.release]
14+
release = false
15+
1316
[dependencies]
1417
arrow-array = { workspace = true }
1518
arrow-cast = { workspace = true }

Diff for: cliff.toml

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# git-cliff configuration file. see https://git-cliff.org/docs/configuration
2+
3+
[changelog]
4+
header = """
5+
# Changelog\n
6+
"""
7+
# Tera template
8+
body = """
9+
## [{{ version }}](https://github.com/delta-io/delta-kernel-rs/tree/{{ version }}/) ({{ timestamp | date(format="%Y-%m-%d") }})
10+
11+
[Full Changelog](https://github.com/delta-io/delta-kernel-rs/compare/{{ previous.version }}...{{ version }})
12+
13+
{% for group, commits in commits | group_by(attribute="group") %}
14+
### {{ group | striptags | trim | upper_first }}
15+
{% for commit in commits %}
16+
{{ loop.index }}. {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
17+
{{ commit.message | split(pat="\n") | first | upper_first | replace(from="(#", to="([#")\
18+
| replace(from="0)", to="0])")\
19+
| replace(from="1)", to="1])")\
20+
| replace(from="2)", to="2])")\
21+
| replace(from="3)", to="3])")\
22+
| replace(from="4)", to="4])")\
23+
| replace(from="5)", to="5])")\
24+
| replace(from="6)", to="6])")\
25+
| replace(from="7)", to="7])")\
26+
| replace(from="8)", to="8])")\
27+
| replace(from="9)", to="9])") }}\
28+
{% endfor %}
29+
{% endfor %}
30+
{% for commit in commits %}
31+
{% set message = commit.message | split(pat="\n") | first %}\
32+
{% set pr = message | split(pat="(#") | last | split(pat=")") | first %}\
33+
[#{{ pr }}]: https://github.com/delta-io/delta-kernel-rs/pull/{{ pr }}\
34+
{% endfor %}\n\n\n
35+
"""
36+
footer = """
37+
"""
38+
# remove the leading and trailing s
39+
trim = true
40+
postprocessors = []
41+
42+
[git]
43+
# parse the commits based on https://www.conventionalcommits.org
44+
conventional_commits = true
45+
# filter out the commits that are not conventional
46+
filter_unconventional = false
47+
# process each line of a commit as an individual commit
48+
split_commits = false
49+
# regex for preprocessing the commit messages
50+
commit_preprocessors = []
51+
# regex for parsing and grouping commits. note that e.g. both doc and docs are matched since we have
52+
# trim = true above.
53+
commit_parsers = [
54+
{ field = "github.pr_labels", pattern = "breaking-change", group = "<!-- 0 --> 🏗️ Breaking changes" },
55+
{ message = "^feat", group = "<!-- 1 -->🚀 Features / new APIs" },
56+
{ message = "^fix", group = "<!-- 2 -->🐛 Bug Fixes" },
57+
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
58+
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
59+
{ message = "^refactor", group = "<!-- 5 -->🚜 Refactor" },
60+
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
61+
{ message = "^chore|^ci", group = "<!-- 7 -->⚙️ Chores/CI" },
62+
{ message = "^revert", group = "<!-- 8 -->◀️ Revert" },
63+
{ message = ".*", group = "<!-- 9 -->Other" },
64+
]
65+
# filter out the commits that are not matched by commit parsers
66+
filter_commits = false
67+
# sort the tags topologically
68+
topo_order = false
69+
# sort the commits inside sections by oldest/newest order
70+
sort_commits = "oldest"

Diff for: feature-tests/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ repository.workspace = true
88
readme.workspace = true
99
version.workspace = true
1010

11+
[package.metadata.release]
12+
release = false
13+
1114
[dependencies]
1215
delta_kernel = { path = "../kernel" }
1316

Diff for: ffi-proc-macros/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ readme.workspace = true
1010
rust-version.workspace = true
1111
version.workspace = true
1212

13+
[package.metadata.release]
14+
release = false
15+
1316
[lib]
1417
proc-macro = true
1518

Diff for: ffi/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ version.workspace = true
1010
rust-version.workspace = true
1111
build = "build.rs"
1212

13+
[package.metadata.release]
14+
release = false
15+
1316
[lib]
1417
crate-type = ["lib", "cdylib", "staticlib"]
1518

Diff for: kernel/Cargo.toml

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ rust-version.workspace = true
1515
[package.metadata.docs.rs]
1616
all-features = true
1717

18+
[package.metadata.release]
19+
pre-release-replacements = [
20+
{file="../README.md", search="delta_kernel = \"[a-z0-9\\.-]+\"", replace="delta_kernel = \"{{version}}\""},
21+
{file="../README.md", search="version = \"[a-z0-9\\.-]+\"", replace="version = \"{{version}}\""},
22+
]
23+
pre-release-hook = ["git", "cliff", "--repository", "../", "--config", "../cliff.toml", "--unreleased", "--prepend", "../CHANGELOG.md", "--tag", "{{version}}" ]
24+
1825
[dependencies]
1926
bytes = "1.7"
2027
chrono = { version = "0.4" }

Diff for: kernel/examples/inspect-table/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ delta_kernel = { path = "../../../kernel", features = [
1515
] }
1616
env_logger = "0.11.3"
1717
url = "2"
18+
19+
[package.metadata.release]
20+
release = false

Diff for: kernel/examples/read-table-changes/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ version = "0.1.0"
44
edition = "2021"
55
publish = false
66

7+
[package.metadata.release]
8+
release = false
9+
710
[dependencies]
811
arrow-array = { workspace = true }
912
arrow-schema = { workspace = true }

Diff for: kernel/examples/read-table-multi-threaded/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@ env_logger = "0.11.5"
1717
itertools = "0.13"
1818
spmc = "0.3.0"
1919
url = "2"
20+
21+
[package.metadata.release]
22+
release = false

Diff for: kernel/examples/read-table-single-threaded/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ delta_kernel = { path = "../../../kernel", features = [
1616
env_logger = "0.11.5"
1717
itertools = "0.13"
1818
url = "2"
19+
20+
[package.metadata.release]
21+
release = false

Diff for: release.sh

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#!/usr/bin/env bash
2+
3+
###################################################################################################
4+
# USAGE:
5+
# 1. on a release branch: ./release.sh <version>
6+
# 2. on main branch (after merging release branch): ./release.sh
7+
###################################################################################################
8+
9+
# This is a script to automate a large portion of the release process for the crates we publish to
10+
# crates.io. Currently only `delta_kernel` (in the kernel/ dir) and `delta_kernel_derive` (in the
11+
# derive-macros/ dir) are released.
12+
13+
# Exit on error, undefined variables, and pipe failures
14+
set -euo pipefail
15+
16+
# print commands before executing them for debugging
17+
# set -x
18+
19+
RED='\033[0;31m'
20+
GREEN='\033[0;32m'
21+
YELLOW='\033[1;33m'
22+
BLUE='\033[0;34m'
23+
NC='\033[0m' # no color
24+
25+
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
26+
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
27+
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
28+
log_error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
29+
30+
check_requirements() {
31+
log_info "Checking required tools..."
32+
33+
command -v cargo >/dev/null 2>&1 || log_error "cargo is required but not installed"
34+
command -v git >/dev/null 2>&1 || log_error "git is required but not installed"
35+
command -v cargo-release >/dev/null 2>&1 || log_error "cargo-release is required but not installed. Install with: cargo install cargo-release"
36+
command -v git-cliff >/dev/null 2>&1 || log_error "git-cliff is required but not installed. Install with: cargo install git-cliff"
37+
command -v jq >/dev/null 2>&1 || log_error "jq is required but not installed."
38+
39+
log_success "All required tools are available"
40+
}
41+
42+
is_main_branch() {
43+
local current_branch
44+
current_branch=$(git rev-parse --abbrev-ref HEAD)
45+
[[ "$current_branch" == "main" ]]
46+
}
47+
48+
is_working_tree_clean() {
49+
git diff --quiet && git diff --cached --quiet
50+
}
51+
52+
# check if the version is already published on crates.io
53+
is_version_published() {
54+
local crate_name="$1"
55+
local version
56+
version=get_current_version "$crate_name"
57+
58+
if [[ -z "$version" ]]; then
59+
log_error "Could not find crate '$crate_name' in workspace"
60+
fi
61+
62+
if cargo search "$crate_name" | grep -q "^$crate_name = \"$version\""; then
63+
return 0
64+
else
65+
return 1
66+
fi
67+
}
68+
69+
# get current version from Cargo.toml
70+
get_current_version() {
71+
local crate_name="$1"
72+
cargo metadata --no-deps --format-version 1 | \
73+
jq -r --arg name "$crate_name" '.packages[] | select(.name == $name) | .version'
74+
}
75+
76+
# Prompt user for confirmation
77+
confirm() {
78+
local prompt="$1"
79+
local response
80+
81+
echo -e -n "${YELLOW}${prompt} [y/N]${NC} "
82+
read -r response
83+
84+
[[ "$response" =~ ^[Yy] ]]
85+
}
86+
87+
# handle release branch workflow (CHANGELOG updates, README updates, PR to main)
88+
handle_release_branch() {
89+
local version="$1"
90+
91+
log_info "Starting release preparation for version $version..."
92+
93+
# Update CHANGELOG and README
94+
log_info "Updating CHANGELOG.md and README.md..."
95+
if ! cargo release --workspace "$version" --no-publish --no-push --no-tag --execute; then
96+
log_error "Failed to update CHANGELOG and README"
97+
fi
98+
99+
if confirm "Print diff of CHANGELOG/README changes?"; then
100+
git diff --stat HEAD^
101+
git diff HEAD^
102+
fi
103+
104+
if confirm "Would you like to push these changes to 'origin' remote?"; then
105+
local current_branch
106+
current_branch=$(git rev-parse --abbrev-ref HEAD)
107+
108+
log_info "Pushing changes to remote..."
109+
git push origin "$current_branch"
110+
111+
if confirm "Would you like to create a PR to merge this release into 'main'?"; then
112+
if command -v gh >/dev/null 2>&1; then
113+
gh pr create --title "release $version" --body "release $version"
114+
log_success "PR created successfully"
115+
else
116+
log_warning "GitHub CLI not found. Please create a PR manually."
117+
fi
118+
fi
119+
fi
120+
}
121+
122+
# Handle main branch workflow (publish and tag)
123+
handle_main_branch() {
124+
# could potentially just use full 'cargo release' command here
125+
publish "delta_kernel_derive" "$current_version"
126+
publish "delta_kernel" "$current_version"
127+
128+
if confirm "Would you like to tag this release?"; then
129+
log_info "Tagging release $current_version..."
130+
git tag -a "v$current_version" -m "Release $current_version"
131+
git push upstream "v$current_version"
132+
log_success "Tagged release $current_version"
133+
fi
134+
}
135+
136+
publish() {
137+
local crate_name="$1"
138+
local current_version
139+
current_version=$(get_current_version "$crate_name")
140+
141+
if is_version_published "delta_kernel_derive"; then
142+
log_error "delta_kernel_derive version $current_version is already published to crates.io"
143+
fi
144+
log_info "[DRY RUN] Publishing $crate_name version $version to crates.io..."
145+
if ! cargo publish --dry-run -p "$crate_name"; then
146+
log_error "Failed to publish $crate_name to crates.io"
147+
fi
148+
149+
if confirm "Dry run complete. Continue with publishing?"; then
150+
log_info "Publishing $crate_name version $version to crates.io..."
151+
if ! cargo publish -p "$crate_name"; then
152+
log_error "Failed to publish $crate_name to crates.io"
153+
fi
154+
log_success "Successfully published $crate_name version $version to crates.io"
155+
fi
156+
}
157+
158+
check_requirements
159+
160+
if is_main_branch; then
161+
if [[ $# -ne 0 ]]; then
162+
log_error "Version argument not expected on main branch\nUsage: $0"
163+
fi
164+
handle_main_branch
165+
else
166+
if [[ $# -ne 1 ]]; then
167+
log_error "Version argument required when on release branch\nUsage: $0 <version>"
168+
fi
169+
handle_release_branch "$1"
170+
fi

Diff for: release.toml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
tag = false
2+
publish = false
3+
pre-release-commit-message = "release {{version}}"

Diff for: test-utils/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ repository.workspace = true
88
readme.workspace = true
99
version.workspace = true
1010

11+
[package.metadata.release]
12+
release = false
13+
1114
[dependencies]
1215
arrow-array = { workspace = true, features = ["chrono-tz"] }
1316
arrow-schema = { workspace = true }

0 commit comments

Comments
 (0)