v1.1.2 #63
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release to PyPI | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version to release (e.g., 0.1.0)' | |
| required: true | |
| type: string | |
| extension_release_tag: | |
| description: 'Optional extension release tag (e.g., v1.2.3). Defaults to latest if omitted.' | |
| required: false | |
| type: string | |
| jobs: | |
| build-and-publish: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install build dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install build twine | |
| - name: Extract version from tag or input | |
| id: version | |
| run: | | |
| if [ "${{ github.event_name }}" == "release" ]; then | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| VERSION=${VERSION#refs/tags/} | |
| else | |
| VERSION="${{ github.event.inputs.version }}" | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Version: $VERSION" | |
| - name: Update version in pyproject.toml | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| sed -i "s/^version = \".*\"/version = \"$VERSION\"/" pyproject.toml | |
| - name: Update version in __init__.py | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| sed -i "s/^__version__ = \".*\"/__version__ = \"$VERSION\"/" predicate/__init__.py | |
| - name: Sync extension files if missing | |
| run: | | |
| set -euo pipefail | |
| REQUIRED_FILES=( | |
| "predicate/extension/manifest.json" | |
| "predicate/extension/content.js" | |
| "predicate/extension/background.js" | |
| "predicate/extension/injected_api.js" | |
| "predicate/extension/pkg/sentience_core.js" | |
| "predicate/extension/pkg/sentience_core_bg.wasm" | |
| ) | |
| has_all_required_files() { | |
| for file in "${REQUIRED_FILES[@]}"; do | |
| if [ ! -f "$file" ]; then | |
| return 1 | |
| fi | |
| done | |
| return 0 | |
| } | |
| if has_all_required_files; then | |
| echo "✅ Extension files already present in repository" | |
| exit 0 | |
| fi | |
| echo "⚠️ Extension files missing locally. Attempting auto-sync from extension release..." | |
| REPO="${{ secrets.SENTIENCE_CHROME_REPO }}" | |
| TOKEN="${{ secrets.SENTIENCE_CHROME_TOKEN }}" | |
| if [ -z "$REPO" ] || [ -z "$TOKEN" ]; then | |
| echo "❌ Cannot auto-sync extension files: SENTIENCE_CHROME_REPO or SENTIENCE_CHROME_TOKEN is not configured." | |
| echo "Please run sync-extension workflow (or sync manually) before release." | |
| exit 1 | |
| fi | |
| TAG_INPUT="${{ github.event.inputs.extension_release_tag }}" | |
| if [ -n "$TAG_INPUT" ]; then | |
| TAG="$TAG_INPUT" | |
| echo "Using extension release tag from input: $TAG" | |
| else | |
| echo "No extension_release_tag provided. Resolving latest release tag from $REPO..." | |
| HTTP_CODE=$(curl -L -s -o latest_release.json -w "%{http_code}" \ | |
| -H "Authorization: token $TOKEN" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| "https://api.github.com/repos/$REPO/releases/latest") | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "❌ Failed to fetch latest extension release (HTTP $HTTP_CODE)" | |
| cat latest_release.json | |
| exit 1 | |
| fi | |
| TAG=$(jq -r '.tag_name // empty' latest_release.json) | |
| if [ -z "$TAG" ]; then | |
| echo "❌ Could not determine latest extension release tag" | |
| exit 1 | |
| fi | |
| echo "Resolved latest extension tag: $TAG" | |
| fi | |
| mkdir -p extension-temp | |
| cd extension-temp | |
| HTTP_CODE=$(curl -L -s -w "%{http_code}" -o release.json \ | |
| -H "Authorization: token $TOKEN" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| "https://api.github.com/repos/$REPO/releases/tags/$TAG") | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "❌ Failed to fetch extension release info for tag $TAG (HTTP $HTTP_CODE)" | |
| cat release.json | |
| exit 1 | |
| fi | |
| ASSET_URL=$(jq -r '.assets[]? | select(.name == "extension-files.tar.gz") | .url' release.json) | |
| if [ -z "$ASSET_URL" ] || [ "$ASSET_URL" = "null" ]; then | |
| echo "❌ extension-files.tar.gz not found on extension release $TAG" | |
| echo "Available assets:" | |
| jq -r '.assets[]?.name' release.json | |
| exit 1 | |
| fi | |
| HTTP_CODE=$(curl -L -s -w "%{http_code}" -o extension.tar.gz \ | |
| -H "Authorization: token $TOKEN" \ | |
| -H "Accept: application/octet-stream" \ | |
| "$ASSET_URL") | |
| if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "302" ]; then | |
| echo "❌ Failed to download extension tarball (HTTP $HTTP_CODE)" | |
| exit 1 | |
| fi | |
| tar -xzf extension.tar.gz | |
| cd .. | |
| TARGET_DIR="predicate/extension" | |
| rm -rf "$TARGET_DIR" | |
| mkdir -p "$TARGET_DIR" | |
| cp -r extension-temp/* "$TARGET_DIR/" | |
| rm -rf extension-temp latest_release.json | |
| if has_all_required_files; then | |
| echo "✅ Extension files synced successfully from $REPO@$TAG" | |
| else | |
| echo "❌ Extension sync completed but required files are still missing" | |
| exit 1 | |
| fi | |
| - name: Verify extension files are present | |
| run: | | |
| echo "🔍 Verifying extension files are included..." | |
| # Check required extension files exist | |
| REQUIRED_FILES=( | |
| "predicate/extension/manifest.json" | |
| "predicate/extension/content.js" | |
| "predicate/extension/background.js" | |
| "predicate/extension/injected_api.js" | |
| "predicate/extension/pkg/sentience_core.js" | |
| "predicate/extension/pkg/sentience_core_bg.wasm" | |
| ) | |
| MISSING_FILES=() | |
| for file in "${REQUIRED_FILES[@]}"; do | |
| if [ ! -f "$file" ]; then | |
| MISSING_FILES+=("$file") | |
| fi | |
| done | |
| if [ ${#MISSING_FILES[@]} -ne 0 ]; then | |
| echo "❌ Error: Missing required extension files:" | |
| printf ' - %s\n' "${MISSING_FILES[@]}" | |
| echo "" | |
| echo "Please ensure the extension is synced before releasing." | |
| echo "Run the sync-extension workflow or manually sync extension files." | |
| exit 1 | |
| fi | |
| # Verify findTextRect function exists in injected_api.js | |
| if ! grep -q "findTextRect:" predicate/extension/injected_api.js; then | |
| echo "❌ Error: findTextRect function not found in injected_api.js" | |
| echo "The extension may be out of date. Please sync the extension before releasing." | |
| exit 1 | |
| fi | |
| echo "✅ All extension files verified" | |
| echo "📦 Extension files that will be included:" | |
| find predicate/extension -type f | sort | |
| - name: Build package | |
| run: | | |
| python -m build | |
| - name: Check package | |
| run: | | |
| twine check dist/* | |
| - name: Verify extension files in built package | |
| run: | | |
| echo "🔍 Verifying extension files are included in the built package..." | |
| # Extract wheel to check contents | |
| WHEEL_FILE=$(ls dist/*.whl | head -1) | |
| WHEEL_PATH=$(realpath "$WHEEL_FILE") | |
| echo "Checking wheel: $WHEEL_PATH" | |
| # Create temp directory for extraction | |
| TEMP_DIR=$(mktemp -d) | |
| cd "$TEMP_DIR" | |
| # Extract wheel (it's a zip file) | |
| unzip -q "$WHEEL_PATH" | |
| # Check for required extension files in the wheel | |
| REQUIRED_IN_WHEEL=( | |
| "predicate/extension/manifest.json" | |
| "predicate/extension/injected_api.js" | |
| "predicate/extension/pkg/sentience_core.js" | |
| "predicate/extension/pkg/sentience_core_bg.wasm" | |
| ) | |
| MISSING_IN_WHEEL=() | |
| for file in "${REQUIRED_IN_WHEEL[@]}"; do | |
| if [ ! -f "$file" ]; then | |
| MISSING_IN_WHEEL+=("$file") | |
| fi | |
| done | |
| if [ ${#MISSING_IN_WHEEL[@]} -ne 0 ]; then | |
| echo "❌ Error: Extension files missing from built wheel:" | |
| printf ' - %s\n' "${MISSING_IN_WHEEL[@]}" | |
| echo "" | |
| echo "This indicates a packaging configuration issue." | |
| echo "Check MANIFEST.in and pyproject.toml package-data settings." | |
| exit 1 | |
| fi | |
| # Verify findTextRect is in the packaged injected_api.js | |
| if ! grep -q "findTextRect:" predicate/extension/injected_api.js; then | |
| echo "❌ Error: findTextRect not found in packaged injected_api.js" | |
| exit 1 | |
| fi | |
| echo "✅ All extension files verified in built package" | |
| echo "📦 Extension files found in wheel:" | |
| find predicate/extension -type f | sort | |
| # Cleanup | |
| rm -rf "$TEMP_DIR" | |
| - name: Publish to PyPI | |
| env: | |
| TWINE_USERNAME: __token__ | |
| TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} | |
| run: | | |
| twine upload dist/* | |
| - name: Create GitHub Release | |
| if: github.event_name == 'workflow_dispatch' | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: v${{ steps.version.outputs.version }} | |
| name: Release v${{ steps.version.outputs.version }} | |
| body: | | |
| Release v${{ steps.version.outputs.version }} of predicate-runtime | |
| ## Installation | |
| ```bash | |
| pip install predicate-runtime==${{ steps.version.outputs.version }} | |
| ``` | |
| draft: false | |
| prerelease: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| publish-compat-shim: | |
| runs-on: ubuntu-latest | |
| needs: build-and-publish | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install build dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install build twine | |
| - name: Extract version from tag or input | |
| id: version | |
| run: | | |
| if [ "${{ github.event_name }}" == "release" ]; then | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| VERSION=${VERSION#refs/tags/} | |
| else | |
| VERSION="${{ github.event.inputs.version }}" | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Version: $VERSION" | |
| - name: Sync shim version and runtime dependency | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| SHIM_PYPROJECT="compat/predicate-sdk-shim/pyproject.toml" | |
| sed -i "s/^version = \".*\"/version = \"$VERSION\"/" "$SHIM_PYPROJECT" | |
| sed -i "s/^ \"predicate-runtime==.*\",/ \"predicate-runtime==$VERSION\",/" "$SHIM_PYPROJECT" | |
| - name: Build compatibility shim package | |
| run: | | |
| python -m build compat/predicate-sdk-shim -o dist-compat | |
| - name: Check compatibility shim package | |
| run: | | |
| twine check dist-compat/* | |
| - name: Publish compatibility shim to PyPI | |
| env: | |
| TWINE_USERNAME: __token__ | |
| TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} | |
| run: | | |
| twine upload dist-compat/* |