Skip to content

Release Integration Packages #54

Release Integration Packages

Release Integration Packages #54

name: Release Integration Packages
# Tag scheme: zep-<framework>-<language>-v<version>
# e.g. zep-adk-python-v0.2.0, zep-mastra-typescript-v0.1.0
# Go modules are tested, tagged, and released from workflow_dispatch. Their tags match
# the module subpath, integrations/<framework>/go/vX.Y.Z, consumed via
# go get github.com/getzep/zep/integrations/<framework>/go@vX.Y.Z
on:
release:
types: [published]
workflow_dispatch:
inputs:
framework:
description: 'Framework key (directory under integrations/, e.g. adk, crewai)'
required: true
language:
description: 'Language'
required: true
type: choice
options:
- python
- typescript
- go
version:
description: 'Go version without the v prefix (required for Go, e.g. 0.1.0)'
required: false
permissions:
contents: read
id-token: write
jobs:
detect:
# zep-ingest is released by release-ingestion.yml. A published ingestion
# release also emits this repository-wide event, so skip it cleanly.
if: github.event_name == 'workflow_dispatch' || !startsWith(github.event.release.tag_name, 'zep-ingest-v')
runs-on: ubuntu-latest
outputs:
framework: ${{ steps.detect.outputs.framework }}
language: ${{ steps.detect.outputs.language }}
version: ${{ steps.detect.outputs.version }}
steps:
- name: Detect framework + language
id: detect
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_FRAMEWORK: ${{ github.event.inputs.framework }}
INPUT_LANGUAGE: ${{ github.event.inputs.language }}
INPUT_VERSION: ${{ github.event.inputs.version }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
FRAMEWORK="$INPUT_FRAMEWORK"
LANGUAGE="$INPUT_LANGUAGE"
VERSION="$INPUT_VERSION"
else
TAG="$RELEASE_TAG"
# zep-<framework>-<language>-v<version>
if [[ $TAG =~ ^zep-([a-z0-9][a-z0-9-]*)-(python|typescript)-v(.+)$ ]]; then
FRAMEWORK="${BASH_REMATCH[1]}"
LANGUAGE="${BASH_REMATCH[2]}"
VERSION="${BASH_REMATCH[3]}"
elif [[ $TAG =~ ^integrations/([a-z0-9][a-z0-9-]*)/go/v(.+)$ ]]; then
# Go releases created with GITHUB_TOKEN do not recursively trigger this
# workflow. Recognize externally-created Go releases so they no-op cleanly.
FRAMEWORK="${BASH_REMATCH[1]}"
LANGUAGE="go"
VERSION="${BASH_REMATCH[2]}"
else
echo "Tag '$TAG' does not match a supported integration release tag."
exit 1
fi
fi
if [[ ! "$FRAMEWORK" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
echo "Invalid framework key '$FRAMEWORK'."
exit 1
fi
if [[ ! "$LANGUAGE" =~ ^(python|typescript|go)$ ]]; then
echo "Unsupported integration language '$LANGUAGE'."
exit 1
fi
{
echo "framework=$FRAMEWORK"
echo "language=$LANGUAGE"
echo "version=$VERSION"
} >> "$GITHUB_OUTPUT"
# ---------------------------------------------- Go → scoped tag + GitHub Release
validate-go:
if: github.event_name == 'workflow_dispatch' && needs.detect.outputs.language == 'go'
needs: detect
runs-on: ubuntu-latest
outputs:
framework: ${{ steps.metadata.outputs.framework }}
module: ${{ steps.metadata.outputs.module }}
package_dir: ${{ steps.metadata.outputs.package_dir }}
tag: ${{ steps.metadata.outputs.tag }}
version: ${{ steps.metadata.outputs.version }}
steps:
- name: Validate release metadata
id: metadata
env:
FRAMEWORK: ${{ needs.detect.outputs.framework }}
VERSION: ${{ needs.detect.outputs.version }}
run: |
if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then
echo "Go releases must be dispatched from main (got '$GITHUB_REF')."
exit 1
fi
if [[ ! "$FRAMEWORK" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
echo "Invalid framework key '$FRAMEWORK'."
exit 1
fi
if [[ ! "$VERSION" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
echo "Invalid Go semantic version '$VERSION' (expected e.g. 0.1.0 or 1.2.0-rc.1)."
exit 1
fi
if [[ "$VERSION" == *-* ]]; then
IFS='.' read -ra PRERELEASE_IDENTIFIERS <<< "${VERSION#*-}"
for identifier in "${PRERELEASE_IDENTIFIERS[@]}"; do
if [[ "$identifier" =~ ^[0-9]+$ && "$identifier" == 0* && "$identifier" != "0" ]]; then
echo "Invalid numeric prerelease identifier '$identifier': leading zeroes are not allowed."
exit 1
fi
done
fi
PACKAGE_DIR="integrations/$FRAMEWORK/go"
MODULE="github.com/$GITHUB_REPOSITORY/$PACKAGE_DIR"
MAJOR="${VERSION%%.*}"
if (( MAJOR >= 2 )); then
MODULE="$MODULE/v$MAJOR"
fi
{
echo "framework=$FRAMEWORK"
echo "module=$MODULE"
echo "package_dir=$PACKAGE_DIR"
echo "tag=$PACKAGE_DIR/v$VERSION"
echo "version=v$VERSION"
} >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
- name: Validate Go module path
env:
EXPECTED_MODULE: ${{ steps.metadata.outputs.module }}
PACKAGE_DIR: ${{ steps.metadata.outputs.package_dir }}
run: |
if [[ ! -f "$PACKAGE_DIR/go.mod" ]]; then
echo "Go module $PACKAGE_DIR/go.mod does not exist."
exit 1
fi
ACTUAL_MODULE="$(awk '$1 == "module" { print $2; exit }' "$PACKAGE_DIR/go.mod")"
if [[ "$ACTUAL_MODULE" != "$EXPECTED_MODULE" ]]; then
echo "go.mod declares '$ACTUAL_MODULE'; expected '$EXPECTED_MODULE'."
exit 1
fi
- name: Test Go integration
uses: ./.github/actions/test-go
with:
package: ${{ steps.metadata.outputs.framework }}
go-version: '1.26'
publish-go:
if: github.event_name == 'workflow_dispatch' && needs.detect.outputs.language == 'go'
needs: [detect, validate-go]
runs-on: ubuntu-latest
environment:
name: release
url: https://github.com/${{ github.repository }}/releases
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.sha }}
- name: Create scoped tag and GitHub Release
env:
FRAMEWORK: ${{ needs.validate-go.outputs.framework }}
GH_TOKEN: ${{ github.token }}
MODULE: ${{ needs.validate-go.outputs.module }}
PACKAGE_DIR: ${{ needs.validate-go.outputs.package_dir }}
TAG: ${{ needs.validate-go.outputs.tag }}
VERSION: ${{ needs.validate-go.outputs.version }}
run: |
TAG_EXISTS=false
TAG_REF="$(git ls-remote --tags origin "refs/tags/$TAG")"
if [[ -n "$TAG_REF" ]]; then
TAG_EXISTS=true
git fetch origin "refs/tags/$TAG:refs/tags/$TAG"
TAG_SHA="$(git rev-list -n 1 "$TAG")"
if [[ "$TAG_SHA" != "$GITHUB_SHA" ]]; then
echo "Tag '$TAG' already points to $TAG_SHA, not $GITHUB_SHA."
exit 1
fi
fi
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release '$TAG' already exists at the validated commit; continuing verification."
exit 0
fi
NOTES_FILE="$(mktemp)"
printf '%s\n\n' "Go module: \`$MODULE\`" > "$NOTES_FILE"
# shellcheck disable=SC2016 # Markdown fences are intentionally literal.
printf 'Install with:\n\n```bash\ngo get %s@%s\n```\n\n' "$MODULE" "$VERSION" >> "$NOTES_FILE"
printf '[Changelog](https://github.com/%s/blob/%s/%s/CHANGELOG.md)\n' \
"$GITHUB_REPOSITORY" "$TAG" "$PACKAGE_DIR" >> "$NOTES_FILE"
RELEASE_ARGS=(
"$TAG"
--title "Zep $FRAMEWORK Go integration $VERSION"
--notes-file "$NOTES_FILE"
--latest=false
)
if [[ "$TAG_EXISTS" == "true" ]]; then
RELEASE_ARGS+=(--verify-tag)
else
RELEASE_ARGS+=(--target "$GITHUB_SHA")
fi
gh release create "${RELEASE_ARGS[@]}"
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.26'
- name: Register and verify module with the Go proxy
env:
MODULE: ${{ needs.validate-go.outputs.module }}
VERSION: ${{ needs.validate-go.outputs.version }}
run: |
for attempt in {1..6}; do
if GOPROXY=https://proxy.golang.org GO111MODULE=on go list -m "$MODULE@$VERSION"; then
exit 0
fi
if (( attempt < 6 )); then
echo "Go proxy has not resolved $MODULE@$VERSION yet; retrying in 10 seconds."
sleep 10
fi
done
echo "Go proxy did not resolve $MODULE@$VERSION after 6 attempts."
exit 1
# ---------------------------------------------------------------- Python → PyPI
test-python:
if: needs.detect.outputs.language == 'python'
needs: detect
runs-on: ubuntu-latest
strategy:
matrix:
# All integrations require Python >=3.11.
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Check package exists
run: |
DIR="integrations/${{ needs.detect.outputs.framework }}/python"
if [ ! -d "$DIR" ]; then
echo "Package directory $DIR does not exist"
exit 1
fi
- name: Test Python integration
uses: ./.github/actions/test-python
with:
package: ${{ needs.detect.outputs.framework }}
python-version: ${{ matrix.python-version }}
build-python:
if: needs.detect.outputs.language == 'python'
needs: [detect, test-python]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
with:
version: "latest"
- name: Set up Python
run: uv python install 3.11
- name: Build package
working-directory: integrations/${{ needs.detect.outputs.framework }}/python
run: uv build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ needs.detect.outputs.framework }}-python-dist
path: integrations/${{ needs.detect.outputs.framework }}/python/dist/
publish-pypi:
if: needs.detect.outputs.language == 'python'
needs: [detect, test-python, build-python]
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/p/zep-${{ needs.detect.outputs.framework }}
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: ${{ needs.detect.outputs.framework }}-python-dist
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
with:
packages-dir: dist/
# ------------------------------------------------------------ TypeScript → npm
publish-npm:
if: needs.detect.outputs.language == 'typescript'
needs: detect
runs-on: ubuntu-latest
environment:
name: release
url: https://www.npmjs.com/package/@getzep/zep-${{ needs.detect.outputs.framework }}
defaults:
run:
working-directory: integrations/${{ needs.detect.outputs.framework }}/typescript
steps:
- uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Install npm with OIDC support
run: npm install -g npm@latest
- name: Check package exists
run: |
if [ ! -f package.json ]; then
echo "package.json not found for ${{ needs.detect.outputs.framework }}/typescript"
exit 1
fi
- name: Install, build, test
run: |
npm ci
npm run build --if-present
npm test
- name: Publish to npm
run: npm publish --access public