From c08a78ae84734b2d0c3339b9a7f4e7b445c8f155 Mon Sep 17 00:00:00 2001 From: ColonistOne Date: Thu, 9 Apr 2026 14:05:51 +0100 Subject: [PATCH] Add OIDC release automation, grouped Dependabot, and CI coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports the same release infra we set up on colony-sdk-python and crewai-colony, plus adds Codecov coverage reporting which langchain- colony's previous CI lacked entirely. git tag vX.Y.Z && git push origin vX.Y.Z triggers .github/workflows/release.yml, which runs the test suite (via pip install -e .[dev,async]), builds wheel + sdist, publishes to PyPI via short-lived OIDC tokens (no API token stored anywhere), and creates a GitHub Release with the matching CHANGELOG section as release notes. Differences from the colony-sdk-python and crewai-colony versions: - Test job uses `pip install -e ".[dev,async]"` so it picks up the langchain ecosystem deps, dev tooling, AND the async extra (which brings httpx so test_async_native.py can exercise the full stack). - Version-tag check verifies ONLY pyproject.toml (langchain-colony uses `importlib.metadata.version("langchain-colony")` to derive __version__ at import time, so pyproject.toml is the single source of truth — no second file to keep in sync, unlike crewai-colony which had to verify both pyproject.toml AND src/.../__init__.py). - The CHANGELOG-section awk extractor matches the langchain-colony heading format `## X.Y.Z (YYYY-MM-DD)` (with parens), not the `## X.Y.Z — YYYY-MM-DD` format used by the other two repos. CI changes: - Test job now installs with `[dev,async]` extras + `pytest-cov`. - 3.12 job runs `pytest --cov=langchain_colony --cov-report=xml` and uploads to Codecov via `codecov-action@v6`. - Other Python versions still run plain `pytest -v`. - `test_async_native.py` now runs on every CI build (it requires httpx, which the [async] extra brings). dependabot.yml: pip + github-actions, weekly Monday, grouped into single PRs per ecosystem (verbatim from colony-sdk-python). Configuring the PyPI Trusted Publisher for langchain-colony is a separate one-time external step that happens before the first tag push. The PR description tracks the steps. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/dependabot.yml | 28 ++++++++ .github/workflows/ci.yml | 18 ++++- .github/workflows/release.yml | 122 ++++++++++++++++++++++++++++++++++ CHANGELOG.md | 6 ++ 4 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0f69316 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,28 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + labels: + - "dependencies" + groups: + python-deps: + patterns: + - "*" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "ci" + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75f403d..e6b654b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,14 +21,26 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies - run: pip install -e ".[dev]" || pip install -e . + # Install with the [async] extra so test_async_native.py can exercise + # the full AsyncColonyClient stack via httpx.MockTransport. + run: pip install -e ".[dev,async]" pytest-cov - - name: Install test deps - run: pip install pytest + - name: Run tests with coverage + if: matrix.python-version == '3.12' + run: pytest tests/ --ignore=tests/test_integration.py --cov=langchain_colony --cov-report=xml --cov-report=term-missing -v - name: Run tests + if: matrix.python-version != '3.12' run: pytest tests/ --ignore=tests/test_integration.py -v + - name: Upload coverage to Codecov + if: matrix.python-version == '3.12' + uses: codecov/codecov-action@v6 + with: + files: coverage.xml + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} + lint: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0a4d6aa --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,122 @@ +name: Release + +# Publishes to PyPI via OIDC trusted publishing whenever a `v*` tag is pushed. +# No API tokens are stored anywhere — PyPI mints a short-lived token from +# the GitHub Actions OIDC identity at publish time. +# +# To cut a release: +# 1. Bump the version in pyproject.toml +# (langchain_colony.__version__ is derived at import time from +# importlib.metadata.version("langchain-colony"), so pyproject.toml +# is the single source of truth) +# 2. Move the "## Unreleased" section in CHANGELOG.md under a new +# "## X.Y.Z (YYYY-MM-DD)" heading +# 3. Merge to main +# 4. git tag vX.Y.Z && git push origin vX.Y.Z +# +# This workflow will then: run the test suite, build wheel + sdist, +# publish to PyPI via OIDC, and create a GitHub Release with the +# CHANGELOG section as the release notes. + +on: + push: + tags: + - "v*" + +jobs: + test: + name: Test before release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + - run: pip install -e ".[dev,async]" + - run: ruff check src/ tests/ + - run: ruff format --check src/ tests/ + - run: pytest tests/ --ignore=tests/test_integration.py + + build: + name: Build distributions + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + - run: pip install build + - run: python -m build + - name: Verify version matches tag + # langchain-colony's version lives in pyproject.toml only — + # __version__ is auto-derived via importlib.metadata at import time. + run: | + TAG_VERSION="${GITHUB_REF#refs/tags/v}" + PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") + if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then + echo "::error::Tag v$TAG_VERSION does not match pyproject.toml version $PKG_VERSION" + exit 1 + fi + echo "Tag and pyproject.toml agree on version $PKG_VERSION" + - uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ + + publish: + name: Publish to PyPI + needs: build + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/langchain-colony + permissions: + id-token: write # required for OIDC trusted publishing + steps: + - uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + - name: Publish to PyPI via OIDC + uses: pypa/gh-action-pypi-publish@release/v1 + + github-release: + name: Create GitHub Release + needs: publish + runs-on: ubuntu-latest + permissions: + contents: write # required for gh release create + steps: + - uses: actions/checkout@v6 + - uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + - name: Extract changelog section for this version + run: | + VERSION="${GITHUB_REF#refs/tags/v}" + # Print everything under "## VERSION (...)" up to (but not + # including) the next "## " heading. Strips the heading line itself. + # langchain-colony uses "## X.Y.Z (YYYY-MM-DD)" format with parens. + awk -v ver="$VERSION" ' + /^## / { + if (in_section) exit + if ($0 ~ "^## " ver "( |$|\\()") { in_section = 1; next } + next + } + in_section { print } + ' CHANGELOG.md > release_notes.md + if [ ! -s release_notes.md ]; then + echo "::warning::No CHANGELOG entry found for $VERSION — release notes will be empty" + fi + echo "--- release_notes.md ---" + cat release_notes.md + - name: Create GitHub Release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create "${GITHUB_REF_NAME}" \ + --title "${GITHUB_REF_NAME}" \ + --notes-file release_notes.md \ + dist/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 35112b9..da27da6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +### Infrastructure + +- **OIDC release automation** — releases now ship via PyPI Trusted Publishing on tag push. `git tag vX.Y.Z && git push origin vX.Y.Z` triggers `.github/workflows/release.yml`, which runs the test suite, builds wheel + sdist, publishes to PyPI via short-lived OIDC tokens (no API token stored anywhere), and creates a GitHub Release with the changelog entry as release notes. The workflow refuses to publish if the tag version doesn't match `pyproject.toml` (the single source of truth — `langchain_colony.__version__` is auto-derived from package metadata at import time). +- **Dependabot** — `.github/dependabot.yml` watches `pip` and `github-actions` weekly, **grouped** into single PRs per ecosystem to minimise noise. +- **Coverage on CI** — `pytest-cov` now runs on the 3.12 job with Codecov upload via `codecov-action@v6`. Previously CI only ran tests with no coverage signal. CI also now installs the `[async]` extra so `test_async_native.py` exercises the full `AsyncColonyClient` stack on every run. + ### Added — new tools (SDK 1.4.0 + 1.5.0 surface) - **`ColonyFollowUser`**, **`ColonyUnfollowUser`** — manage your social graph on The Colony.