Skip to content

Commit beafe66

Browse files
authored
ci: standardize release process (#8)
* ci: add standardized release script and automation Add scripts/release.sh for version bumps, changelog updates, tagging, and GitHub Release creation via CI. Enforce changelog checks on version PRs. * ci: add pytest workflow on pull requests * fix: rewrite changelog updater for Keep a Changelog format Avoid duplicate [Unreleased] headings and keep the preamble intact when preparing releases. Add unit tests and harden release workflow output. * test: fix changelog unit test imports in CI * feat: switch managed databases to /databases API (hotdata>=0.2.3) - Use DatabasesApi instead of ConnectionsApi for all managed database ops - Rename create_managed_database first param from name to description (kw-only) - Add expires_at param to create_managed_database - ManagedDatabase: replace name/source_type with description/default_connection_id - resolve_managed_database: ID lookup first, description scan as fallback - list_managed_databases: fetch all databases, no source_type filter - list_managed_tables, load_managed_table, delete_managed_table: use default_connection_id - Remove MANAGED_SOURCE_TYPE, build_managed_config, create_connection_request from public API * chore: re-lock after hotdata>=0.2.3 dep bump --------- Co-authored-by: Eddie A Tejeda <669988+eddietejeda@users.noreply.github.com>
1 parent a427d1b commit beafe66

20 files changed

Lines changed: 875 additions & 182 deletions
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Check release metadata
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'pyproject.toml'
7+
- 'CHANGELOG.md'
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
check:
14+
name: Verify changelog matches version bump
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
18+
with:
19+
fetch-depth: 0
20+
21+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
22+
with:
23+
python-version: '3.12'
24+
25+
- name: Check release metadata
26+
run: python scripts/check-release.py

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: ["main", "master"]
6+
pull_request:
7+
8+
concurrency:
9+
group: ci-${{ github.ref }}
10+
cancel-in-progress: true
11+
12+
permissions:
13+
contents: read
14+
15+
jobs:
16+
test:
17+
name: Test (Python 3.12)
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
21+
22+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v6
23+
with:
24+
enable-cache: true
25+
26+
- name: Set up Python
27+
run: uv python install 3.12
28+
29+
- name: Install dependencies
30+
run: uv sync --locked
31+
32+
- name: Test
33+
run: uv run pytest -v

.github/workflows/publish.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ jobs:
2828

2929
- name: Verify tag matches pyproject version
3030
run: |
31-
# Release tags must start with `v` followed by a PEP 440 version (e.g. v1.2.3, v1.2.3a1).
3231
if [[ ! "$GITHUB_REF_NAME" =~ ^v[0-9] ]]; then
3332
echo "Release tag '$GITHUB_REF_NAME' must start with 'v' followed by a digit (e.g. v1.0.0)" >&2
3433
exit 1

.github/workflows/release.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: GitHub Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v[0-9]*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
release:
13+
name: Create GitHub Release
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
17+
18+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
19+
with:
20+
python-version: '3.12'
21+
22+
- name: Read package metadata
23+
id: meta
24+
run: |
25+
pkg_name=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['name'])")
26+
pkg_version="${GITHUB_REF_NAME#v}"
27+
echo "name=${pkg_name}" >> "$GITHUB_OUTPUT"
28+
echo "version=${pkg_version}" >> "$GITHUB_OUTPUT"
29+
30+
- name: Extract changelog notes
31+
id: notes
32+
run: |
33+
set -euo pipefail
34+
version="${GITHUB_REF_NAME#v}"
35+
if [[ -f CHANGELOG.md ]]; then
36+
body="$(python scripts/extract-changelog.py "$version")"
37+
else
38+
body="Release ${version}."
39+
fi
40+
delimiter="EOF_${RANDOM}_${RANDOM}"
41+
{
42+
echo "body<<${delimiter}"
43+
echo "$body"
44+
echo "${delimiter}"
45+
} >> "$GITHUB_OUTPUT"
46+
47+
- name: Create GitHub Release
48+
uses: softprops/action-gh-release@1e812e8210a4a8a0b23075e5795f2a4e2b2a0b7 # v2.2.2
49+
with:
50+
tag_name: ${{ github.ref_name }}
51+
name: ${{ steps.meta.outputs.name }} ${{ steps.meta.outputs.version }}
52+
body: ${{ steps.notes.outputs.body }}
53+
generate_release_notes: false
54+
make_latest: true

CHANGELOG.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
## [0.2.0] - 2026-05-24
11+
12+
### Changed
13+
14+
- Switch managed database operations from the connections API to the dedicated `/databases` API (`hotdata>=0.2.3` required).
15+
- `create_managed_database` first parameter renamed from `name` to `description` (keyword-only).
16+
- `ManagedDatabase` dataclass: replace `name`/`source_type` fields with `description`/`default_connection_id`.
17+
- `resolve_managed_database` tries direct ID lookup first, then falls back to a description scan.
18+
- `list_managed_databases` now fetches all databases regardless of source type.
19+
- `list_managed_tables`, `load_managed_table`, and `delete_managed_table` use `default_connection_id` instead of database `id` for connection-scoped operations.
20+
21+
### Added
22+
23+
- `create_managed_database` accepts an optional `expires_at` parameter.
24+
25+
### Removed
26+
27+
- `MANAGED_SOURCE_TYPE`, `build_managed_config`, and `create_connection_request` removed from the public API.
28+
29+
## [0.1.1] - 2026-05-19
30+
31+
### Added
32+
33+
- Managed database helpers on `HotdataClient`.
34+
35+
## [0.1.0] - 2026-05-06
36+
37+
### Added
38+
39+
- Initial release.

CONTRACT.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ The supported import surface is:
3333
- `ManagedDatabase`
3434
- `ManagedTable`
3535
- `LoadManagedTableResult`
36-
- `MANAGED_SOURCE_TYPE`
3736
- `DEFAULT_SCHEMA`
38-
- `build_managed_config`
39-
- `create_connection_request`
4037
- `is_parquet_path`
4138

4239
Adapters should import from `hotdata_runtime` and treat this surface as the stable API.
@@ -58,10 +55,10 @@ Adapters should import from `hotdata_runtime` and treat this surface as the stab
5855
- `columns_for_qualified(qualified, connection_id=...)` resolves table columns, and
5956
adapters should pass `connection_id` when known.
6057
- `uploads()` returns the uploads API wrapper for parquet staging.
61-
- `list_managed_databases()` returns managed-catalog connections (`source_type: managed`).
62-
- `resolve_managed_database(name_or_id)` resolves a managed database by name or id.
63-
- `create_managed_database(name, schema=..., tables=...)` creates a managed database and optionally declares tables up front.
64-
- `delete_managed_database(name_or_id)` deletes a managed database connection.
58+
- `list_managed_databases()` returns all databases via the `/databases` API.
59+
- `resolve_managed_database(name_or_id)` resolves a database by id (direct lookup) or description (list scan).
60+
- `create_managed_database(description=..., schema=..., tables=..., expires_at=...)` creates a database via the `/databases` API and optionally declares tables up front.
61+
- `delete_managed_database(name_or_id)` deletes a database via the `/databases` API.
6562
- `list_managed_tables(database, schema=...)` lists tables in a managed database.
6663
- `upload_parquet(path)` uploads a local parquet file and returns an upload id.
6764
- `load_managed_table(database, table, schema=..., upload_id=..., file=...)` publishes parquet data into a declared managed table.

RELEASING.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Releasing
2+
3+
Every release uses `./scripts/release.sh`. Do not bump versions, tag, or create GitHub Releases manually.
4+
5+
## One-time setup
6+
7+
- Install [GitHub CLI](https://cli.github.com/) (`gh`) and authenticate.
8+
- Ensure PyPI [trusted publishing](https://docs.pypi.org/trusted-publishers/) is configured for this repo (`publish.yml` uses the `pypi` GitHub environment).
9+
10+
## Release steps
11+
12+
1. Add user-facing notes under `## [Unreleased]` in `CHANGELOG.md`.
13+
2. Prepare the release PR:
14+
15+
```bash
16+
./scripts/release.sh prepare patch # or minor | major | 1.2.3
17+
```
18+
19+
3. Merge the PR after CI passes (including the changelog check).
20+
4. Publish from a clean default branch checkout:
21+
22+
```bash
23+
git checkout main # or master for hotdata-marimo
24+
git pull
25+
./scripts/release.sh publish
26+
```
27+
28+
## What happens automatically
29+
30+
Pushing a `vX.Y.Z` tag triggers two workflows:
31+
32+
| Workflow | Purpose |
33+
|----------|---------|
34+
| `publish.yml` | Build wheel/sdist and publish to PyPI |
35+
| `release.yml` | Create the GitHub Release with notes from `CHANGELOG.md` |
36+
37+
## Enforcement
38+
39+
- **PR check** (`check-release.yml`): if `pyproject.toml` version changes, `CHANGELOG.md` must contain a matching `## [X.Y.Z]` section.
40+
- **Tag check** (`publish.yml`): the tag (without `v`) must match `[project].version` in `pyproject.toml`.
41+
- **Publish guard** (`release.sh publish`): refuses to tag if the changelog section is missing.
42+
43+
Together, these make it hard to ship a version without changelog notes or a GitHub Release.

hotdata_runtime/__init__.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313
LoadManagedTableResult,
1414
ManagedDatabase,
1515
ManagedTable,
16-
MANAGED_SOURCE_TYPE,
17-
build_managed_config,
18-
create_connection_request,
1916
is_parquet_path,
2017
)
2118
from hotdata_runtime.env import (
@@ -42,12 +39,9 @@
4239
"DEFAULT_SCHEMA",
4340
"HotdataClient",
4441
"LoadManagedTableResult",
45-
"MANAGED_SOURCE_TYPE",
4642
"ManagedDatabase",
4743
"ManagedTable",
4844
"QueryResult",
49-
"build_managed_config",
50-
"create_connection_request",
5145
"is_parquet_path",
5246
"workspace_health_lines",
5347
"default_api_key",

0 commit comments

Comments
 (0)