Skip to content
Open
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
86 changes: 62 additions & 24 deletions .github/workflows/release.yml
Copy link
Contributor

Choose a reason for hiding this comment

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

@TumeloKonaite do you understand this workflow and can you exlpain it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Skhendle Yes, I understand it. We will discuss it in the meeting.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: write # push tags/commits & create releases
Expand All @@ -16,14 +17,10 @@ jobs:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0 # fetch all history & tags

- name: Sync with remote main (rebase)
run: |
git fetch origin --tags
git checkout main
git pull --rebase origin main
fetch-depth: 0 # fetch all history & tags
persist-credentials: true

# ---- build & test first ----
- name: Install uv (latest release)
run: |
wget -qO- https://astral.sh/uv/install.sh | sh
Expand Down Expand Up @@ -57,6 +54,7 @@ jobs:
files: coverage_reports/coverage.xml
fail_ci_if_error: true

# ---- versioning & release notes ----
- name: Determine Version Bump
id: version
run: |
Expand All @@ -74,13 +72,17 @@ jobs:
run: |
git fetch --tags --force
latest_tag=$(git tag --sort=-v:refname | head -n 1)
echo "Latest tag: ${latest_tag:-<none>}"
echo "tag=${latest_tag}" >> "$GITHUB_OUTPUT"

- name: Calculate Next Version
id: semver
run: |
current=${{ steps.get_tag.outputs.tag }}
if [ -z "$current" ]; then current="v0.1.0"; fi
# default when no tag exists
if [ -z "$current" ]; then
current="v0.1.0"
fi

IFS='.' read -r major minor patch <<<"${current#v}"
bump=${{ steps.version.outputs.bump }}
Expand All @@ -91,43 +93,79 @@ jobs:
patch) new_version="$major.$minor.$((patch + 1))" ;;
esac

echo "current=$current"
echo "new_version=$new_version" >> "$GITHUB_OUTPUT"

- name: Generate Release Notes (commits since last tag)
id: notes
run: |
prev="${{ steps.get_tag.outputs.tag }}"
echo "# Release notes for v${{ steps.semver.outputs.new_version }}" > release_notes.md
echo "" >> release_notes.md
if [ -z "$prev" ]; then
echo "Initial release (no previous tag)." >> release_notes.md
echo "" >> release_notes.md
git log --no-merges --pretty=format:'- %s (%h) by %an' >> release_notes.md
else
echo "Changes since ${prev}:" >> release_notes.md
echo "" >> release_notes.md
git log "${prev}..HEAD" --no-merges --pretty=format:'- %s (%h) by %an' >> release_notes.md
fi
echo "---"
cat release_notes.md
# escape for multi-line output variable
notes=$(awk '{printf "%s\\n", $0}' release_notes.md)
echo "notes=$notes" >> "$GITHUB_OUTPUT"

- name: Set up Git credentials (for commit & tag push)
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}

- name: Update version in pyproject.toml
run: |
new_version=${{ steps.semver.outputs.new_version }}
sed -i -E '/^\[project\]/,/^\[.*\]/ s/^version = ".*"/version = "'"$new_version"'"/' pyproject.toml
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git commit -am "chore: bump version to v${new_version}" || echo "No changes to commit"
git add pyproject.toml
git commit -m "chore: bump version to v${new_version}" || echo "No changes to commit"

- name: Create Git Tag & Push (atomic)
- name: Create Annotated Tag with Release Notes
run: |
set -e
new_tag="v${{ steps.semver.outputs.new_version }}"
# Create the tag only if it doesn't already exist (idempotent reruns)
if ! git rev-parse -q --verify "refs/tags/$new_tag" >/dev/null; then
git tag -a "$new_tag" -m "Release $new_tag"
else
echo "Tag $new_tag already exists; leaving as-is."
fi
# Push commit (HEAD) to main and any new annotated tags together
git push --force-with-lease origin HEAD:main --follow-tags
{
echo "Release ${new_tag}"
echo
cat release_notes.md
} > TAG_MSG.txt
# Recreate locally in case of rerun
git tag -d "$new_tag" 2>/dev/null || true
git tag -a "$new_tag" -F TAG_MSG.txt

- name: Push main and tag
run: |
git push origin main
git push origin "v${{ steps.semver.outputs.new_version }}"

# ---- build & publish ----
- name: Build package
run: |
source .venv/bin/activate
uv build

# This workflow runs on push to main, not on a tag ref. Publish using secrets directly.
# We’re running on push to main, so publish explicitly (not tag-triggered)
- name: Publish to PyPI
run: |
source .venv/bin/activate
uv publish --username __token__ --password ${{ secrets.PYPI_TOKEN }}

- name: Create GitHub Release
- name: Create/Update GitHub Release (with notes)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
new_tag="v${{ steps.semver.outputs.new_version }}"
gh release create "$new_tag" --title "$new_tag" --notes "Automated release for $new_tag"
if gh release view "$new_tag" >/dev/null 2>&1; then
gh release edit "$new_tag" --title "$new_tag" --notes-file release_notes.md
else
gh release create "$new_tag" --title "$new_tag" --notes-file release_notes.md
fi
30 changes: 26 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
[![codecov](https://codecov.io/gh/SoftwareVerse/userverse-python-client/branch/main/graph/badge.svg?token=YOUR_TOKEN)](https://codecov.io/gh/SoftwareVerse/userverse-python-client)




<!-- CI status for your release workflow -->

[![CI - Release Tag](https://github.com/SoftwareVerse/userverse-python-client/actions/workflows/release.yml/badge.svg)](https://github.com/SoftwareVerse/userverse-python-client/actions/workflows/release.yml)

<!-- Latest release (SemVer-aware) badge → opens the latest release page -->

[![Latest Release](https://img.shields.io/github/v/release/SoftwareVerse/userverse-python-client?display_name=tag&sort=semver)](https://github.com/SoftwareVerse/userverse-python-client/releases/latest)

<!-- Optional: latest tag badge (from tags, even if not “GitHub Release”) -->

[![Latest Tag](https://img.shields.io/github/v/tag/SoftwareVerse/userverse-python-client?label=tag&sort=semver)](https://github.com/SoftwareVerse/userverse-python-client/releases/latest)

<!-- Optional: release date & total downloads badges -->

[![Release Date](https://img.shields.io/github/release-date/SoftwareVerse/userverse-python-client)](https://github.com/SoftwareVerse/userverse-python-client/releases/latest)
[![Downloads](https://img.shields.io/github/downloads/SoftwareVerse/userverse-python-client/total)](https://github.com/SoftwareVerse/userverse-python-client/releases)

<!-- You already have Codecov; keep it (replace token if needed) -->

[![codecov](https://codecov.io/gh/SoftwareVerse/userverse-python-client/branch/main/graph/badge.svg?token=YOUR_TOKEN)](https://codecov.io/gh/SoftwareVerse/userverse-python-client)

# userverse-python-client

Python client for the Userverse HTTP server.
Expand All @@ -10,18 +32,21 @@ Python client for the Userverse HTTP server.
Create and activate a virtual environment, then install the project in editable mode:

## linux configuration

```bash
uv venv
source .venv\Scripts\activate
uv pip install -e .
```

## windows configuration

```bash
uv venv
.venv\Scripts\activate
uv pip install -e .
```

## Usage

The package currently exposes a greeting helper and a simple arithmetic function. The example below prints both results:
Expand All @@ -42,7 +67,6 @@ python examples/demo.py
uv run python examples/demo.py
```


## Tests

Run the unit tests with:
Expand All @@ -53,5 +77,3 @@ python -m unittest discover -s tests -v
## uv run tests
uv run python -m unittest discover -s tests -v
```