Skip to content

Commit 04a991f

Browse files
committed
Add release script and update contributing doc
1 parent 1fc1629 commit 04a991f

2 files changed

Lines changed: 274 additions & 0 deletions

File tree

CONTRIBUTING.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,44 @@ For more details, see `drift/instrumentation/README-e2e-tests.md`.
157157
|----------|-------------|
158158
| `docs/context-propagation.md` | Context propagation behavior, edge cases, and patterns |
159159
| `drift/instrumentation/README-e2e-tests.md` | E2E test architecture and debugging |
160+
161+
## For Maintainers
162+
163+
### Releasing
164+
165+
Releases are automated using GitHub Actions. When a GitHub Release is created, the package is automatically built and published to PyPI using [trusted publishing](https://docs.pypi.org/trusted-publishers/).
166+
167+
Prerequisites:
168+
169+
- [GitHub CLI](https://cli.github.com/) (`gh`) installed and authenticated
170+
- On the `main` branch with no uncommitted changes
171+
- Local branch up to date with remote
172+
173+
#### Creating a release
174+
175+
Use the release script to bump the version, create a tag, and publish a GitHub Release:
176+
177+
```bash
178+
# Patch release (0.1.5 → 0.1.6)
179+
./scripts/release.sh patch
180+
181+
# Minor release (0.1.5 → 0.2.0)
182+
./scripts/release.sh minor
183+
```
184+
185+
The script will:
186+
187+
1. Run preflight checks (lint, format, tests)
188+
2. Calculate the next version from `pyproject.toml`
189+
3. Update the version in `pyproject.toml`
190+
4. Commit and tag the version bump
191+
5. Push to origin and create a GitHub Release
192+
193+
Once the release is created, GitHub Actions will automatically:
194+
195+
- Build the Python package with `uv build`
196+
- Publish to PyPI using trusted publishing (no API tokens needed)
197+
198+
#### Manual workflow dispatch
199+
200+
For testing purposes, you can also trigger the publish workflow manually from the Actions tab with an optional version override (e.g., `0.1.6-test`). This is useful for testing the publishing process without affecting the main version.

scripts/release.sh

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Usage: ./scripts/release.sh [patch|minor]
5+
# Default: patch
6+
7+
BUMP_TYPE="${1:-patch}"
8+
9+
# Colors for output
10+
RED='\033[0;31m'
11+
GREEN='\033[0;32m'
12+
YELLOW='\033[1;33m'
13+
NC='\033[0m' # No Color
14+
15+
info() { echo -e "${GREEN}[INFO]${NC} $1"; }
16+
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
17+
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
18+
19+
# Validate bump type
20+
if [[ "$BUMP_TYPE" != "patch" && "$BUMP_TYPE" != "minor" ]]; then
21+
error "Invalid bump type: $BUMP_TYPE. Use 'patch' or 'minor'."
22+
fi
23+
24+
info "Bump type: $BUMP_TYPE"
25+
26+
# =============================================================================
27+
# Preflight checks
28+
# =============================================================================
29+
30+
info "Running preflight checks..."
31+
32+
# Check we're in a git repository
33+
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
34+
error "Not in a git repository"
35+
fi
36+
37+
# Check we're on the default branch (main)
38+
DEFAULT_BRANCH="main"
39+
CURRENT_BRANCH=$(git branch --show-current)
40+
if [[ "$CURRENT_BRANCH" != "$DEFAULT_BRANCH" ]]; then
41+
error "Not on default branch. Current: $CURRENT_BRANCH, Expected: $DEFAULT_BRANCH"
42+
fi
43+
44+
# Check for uncommitted changes
45+
if ! git diff --quiet || ! git diff --staged --quiet; then
46+
error "Working directory has uncommitted changes. Commit or stash them first."
47+
fi
48+
49+
# Check for untracked files (warning only)
50+
UNTRACKED=$(git ls-files --others --exclude-standard)
51+
if [[ -n "$UNTRACKED" ]]; then
52+
warn "Untracked files present (continuing anyway):"
53+
echo "$UNTRACKED" | head -5
54+
fi
55+
56+
# Check that gh CLI is installed and authenticated (warn only, we have a fallback)
57+
GH_AVAILABLE=true
58+
if ! command -v gh &>/dev/null; then
59+
warn "GitHub CLI (gh) is not installed. You'll need to create the release manually."
60+
GH_AVAILABLE=false
61+
elif ! gh auth status &>/dev/null; then
62+
warn "GitHub CLI is not authenticated. You'll need to create the release manually."
63+
GH_AVAILABLE=false
64+
fi
65+
66+
# Fetch latest from remote
67+
info "Fetching latest from origin..."
68+
git fetch origin "$DEFAULT_BRANCH" --tags
69+
70+
# Check if local branch is up to date with remote
71+
LOCAL_COMMIT=$(git rev-parse HEAD)
72+
REMOTE_COMMIT=$(git rev-parse "origin/$DEFAULT_BRANCH")
73+
if [[ "$LOCAL_COMMIT" != "$REMOTE_COMMIT" ]]; then
74+
error "Local branch is not up to date with origin/$DEFAULT_BRANCH. Run 'git pull' first."
75+
fi
76+
77+
# Check if there are commits since last tag
78+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
79+
if [[ -n "$LAST_TAG" ]]; then
80+
COMMITS_SINCE_TAG=$(git rev-list "$LAST_TAG"..HEAD --count)
81+
if [[ "$COMMITS_SINCE_TAG" -eq 0 ]]; then
82+
error "No commits since last tag ($LAST_TAG). Nothing to release."
83+
fi
84+
info "Commits since $LAST_TAG: $COMMITS_SINCE_TAG"
85+
fi
86+
87+
# Check that lint passes
88+
info "Running linter..."
89+
if ! uv run ruff check drift/ tests/; then
90+
error "Lint failed. Fix issues before releasing."
91+
fi
92+
93+
# Check formatting
94+
info "Checking formatting..."
95+
if ! uv run ruff format --check drift/ tests/; then
96+
error "Format check failed. Run 'uv run ruff format drift/ tests/' first."
97+
fi
98+
99+
# Check that tests pass
100+
info "Running tests..."
101+
if ! uv run pytest tests/unit/ -v; then
102+
error "Tests failed. Fix them before releasing."
103+
fi
104+
105+
info "✓ All preflight checks passed"
106+
107+
# =============================================================================
108+
# Calculate new version
109+
# =============================================================================
110+
111+
# Get current version from pyproject.toml
112+
CURRENT_VERSION=$(grep -E '^version = "' pyproject.toml | sed -E 's/version = "(.*)"/\1/')
113+
if [[ -z "$CURRENT_VERSION" ]]; then
114+
error "Failed to read version from pyproject.toml"
115+
fi
116+
117+
info "Current version: $CURRENT_VERSION"
118+
119+
# Parse version
120+
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
121+
122+
# Validate parsed version
123+
if [[ -z "$MAJOR" || -z "$MINOR" || -z "$PATCH" ]]; then
124+
error "Failed to parse version: $CURRENT_VERSION"
125+
fi
126+
127+
# Increment based on bump type
128+
case "$BUMP_TYPE" in
129+
patch)
130+
PATCH=$((PATCH + 1))
131+
;;
132+
minor)
133+
MINOR=$((MINOR + 1))
134+
PATCH=0
135+
;;
136+
esac
137+
138+
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
139+
NEW_TAG="v${NEW_VERSION}"
140+
141+
info "Version bump: $CURRENT_VERSION$NEW_VERSION"
142+
143+
# Check if tag already exists
144+
if git rev-parse "$NEW_TAG" &>/dev/null; then
145+
error "Tag $NEW_TAG already exists!"
146+
fi
147+
148+
# =============================================================================
149+
# Confirm and create release
150+
# =============================================================================
151+
152+
echo ""
153+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
154+
echo " Ready to release: $NEW_VERSION"
155+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
156+
echo ""
157+
echo "This will:"
158+
echo " 1. Update version in pyproject.toml to $NEW_VERSION"
159+
echo " 2. Commit the version bump"
160+
echo " 3. Create and push tag $NEW_TAG"
161+
echo " 4. Create a GitHub Release (triggers PyPI publish)"
162+
echo ""
163+
164+
read -p "Proceed? [y/N] " -n 1 -r
165+
echo ""
166+
167+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
168+
info "Aborted."
169+
exit 0
170+
fi
171+
172+
# Update version in pyproject.toml
173+
info "Updating pyproject.toml..."
174+
if [[ "$OSTYPE" == "darwin"* ]]; then
175+
# macOS
176+
sed -i '' "s/^version = \".*\"/version = \"$NEW_VERSION\"/" pyproject.toml
177+
else
178+
# Linux
179+
sed -i "s/^version = \".*\"/version = \"$NEW_VERSION\"/" pyproject.toml
180+
fi
181+
182+
# Verify the change
183+
UPDATED_VERSION=$(grep -E '^version = "' pyproject.toml | sed -E 's/version = "(.*)"/\1/')
184+
if [[ "$UPDATED_VERSION" != "$NEW_VERSION" ]]; then
185+
error "Failed to update version in pyproject.toml"
186+
fi
187+
188+
# Commit version bump
189+
info "Committing version bump..."
190+
git add pyproject.toml
191+
git commit -m "chore: bump version to $NEW_VERSION"
192+
193+
# Create annotated tag
194+
info "Creating tag $NEW_TAG..."
195+
git tag -a "$NEW_TAG" -m "Release $NEW_VERSION"
196+
197+
# Push commit and tag to origin
198+
info "Pushing to origin..."
199+
git push origin "$DEFAULT_BRANCH"
200+
git push origin "$NEW_TAG"
201+
202+
# Create GitHub Release (triggers the publish workflow)
203+
if [[ "$GH_AVAILABLE" == "true" ]]; then
204+
info "Creating GitHub Release..."
205+
if gh release create "$NEW_TAG" --generate-notes --title "$NEW_TAG"; then
206+
echo ""
207+
info "✓ Released $NEW_VERSION"
208+
info "GitHub Actions will now build and publish to PyPI."
209+
info "Watch progress at: https://github.com/Use-Tusk/drift-python-sdk/actions"
210+
else
211+
echo ""
212+
warn "Failed to create GitHub Release via CLI."
213+
warn "The tag $NEW_TAG has been pushed. To trigger the PyPI publish:"
214+
echo ""
215+
echo " 1. Go to: https://github.com/Use-Tusk/drift-python-sdk/releases/new"
216+
echo " 2. Select the tag: $NEW_TAG"
217+
echo " 3. Set the title: $NEW_TAG"
218+
echo " 4. Click 'Generate release notes' (optional)"
219+
echo " 5. Click 'Publish release'"
220+
echo ""
221+
fi
222+
else
223+
echo ""
224+
info "✓ Tag $NEW_TAG pushed successfully."
225+
info "To publish to PyPI, create a GitHub Release:"
226+
echo ""
227+
echo " 1. Go to: https://github.com/Use-Tusk/drift-python-sdk/releases/new"
228+
echo " 2. Select the tag: $NEW_TAG"
229+
echo " 3. Set the title: Release $NEW_VERSION"
230+
echo " 4. Click 'Generate release notes' (optional)"
231+
echo " 5. Click 'Publish release'"
232+
echo ""
233+
fi

0 commit comments

Comments
 (0)