Two-pane session browser for /resume with AI-naming hook #1131
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
| # Publishes your package to PyPI using Astral/uv for builds. | |
| # Set PYPI_API_TOKEN in repo secrets. | |
| permissions: | |
| contents: write # Allows writing to the repository | |
| name: Build and Publish to PyPI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| # Serialize publishes: two back-to-back pushes once raced, both bumped to the | |
| # same patch version, and the loser stranded git a version behind PyPI. | |
| # Queue (never cancel) so every push still gets released, one at a time. | |
| concurrency: | |
| group: pypi-publish | |
| cancel-in-progress: false | |
| jobs: | |
| test: | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [macos-latest] | |
| python-version: ['3.13'] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install uv | |
| run: pip install uv | |
| - name: Setup uv virtual environment | |
| run: uv venv | |
| - name: Install dependencies | |
| # [durable] extras pulls in dbos, required by integration test | |
| # tests/integration/test_dbos_enabled.py | |
| run: uv pip install -e ".[durable]" | |
| - name: Install pexpect for integration tests | |
| run: uv pip install pexpect>=4.9.0 | |
| - name: Provision CI model (lilac synthetic GLM-5.2) | |
| # models.json ships empty. CI gets its model from extra_models.json, | |
| # exactly like a real user would after running /add_model. | |
| run: | | |
| mkdir -p "$HOME/.code_puppy" | |
| cat > "$HOME/.code_puppy/extra_models.json" <<'JSON' | |
| { | |
| "lilac-zai-org-glm-5.2": { | |
| "type": "custom_openai", | |
| "provider": "lilac", | |
| "name": "zai-org/glm-5.2", | |
| "custom_endpoint": { | |
| "url": "https://api.getlilac.com/v1", | |
| "api_key": "$LILAC_API_KEY" | |
| }, | |
| "context_length": 524288, | |
| "supported_settings": ["temperature", "seed", "top_p"] | |
| } | |
| } | |
| JSON | |
| # Tests isolate user config, and several fallback contracts require a | |
| # non-empty bundled catalog. Keep this test-only copy out of releases: | |
| # the publish job starts from a fresh checkout. | |
| cp "$HOME/.code_puppy/extra_models.json" code_puppy/models.json | |
| echo "Wrote CI model catalog and test-only bundled fallback" | |
| - name: Debug environment variables | |
| env: | |
| CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY || 'fake-key-for-ci-testing' }} | |
| CONTEXT7_API_KEY: ${{ secrets.CONTEXT7_API_KEY || 'fake-key-for-ci-testing' }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY || 'fake-key-for-ci-testing' }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY || 'fake-key-for-ci-testing' }} | |
| SYN_API_KEY: ${{ secrets.SYN_API_KEY || 'fake-key-for-ci-testing' }} | |
| LILAC_API_KEY: ${{ secrets.LILAC_API_KEY || 'fake-key-for-ci-testing' }} | |
| run: | | |
| echo "=== DEBUG: Environment Variables ===" | |
| echo "CEREBRAS_API_KEY is set: ${{ secrets.CEREBRAS_API_KEY != '' }}" | |
| echo "CONTEXT7_API_KEY is set: ${{ secrets.CONTEXT7_API_KEY != '' }}" | |
| echo "OPENAI_API_KEY is set: ${{ secrets.OPENAI_API_KEY != '' }}" | |
| echo "ANTHROPIC_API_KEY is set: ${{ secrets.ANTHROPIC_API_KEY != '' }}" | |
| echo "SYN_API_KEY is set: ${{ secrets.SYN_API_KEY != '' }}" | |
| echo "CEREBRAS_API_KEY length: ${#CEREBRAS_API_KEY}" | |
| echo "CONTEXT7_API_KEY length: ${#CONTEXT7_API_KEY}" | |
| echo "OPENAI_API_KEY length: ${#OPENAI_API_KEY}" | |
| echo "ANTHROPIC_API_KEY length: ${#ANTHROPIC_API_KEY}" | |
| echo "SYN_API_KEY length: ${#SYN_API_KEY}" | |
| echo "=== END DEBUG ===" | |
| - name: Run tests | |
| env: | |
| CI: '1' | |
| CODE_PUPPY_TEST_FAST: '1' | |
| CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY || 'fake-key-for-ci-testing' }} | |
| CONTEXT7_API_KEY: ${{ secrets.CONTEXT7_API_KEY || 'fake-key-for-ci-testing' }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY || 'fake-key-for-ci-testing' }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY || 'fake-key-for-ci-testing' }} | |
| SYN_API_KEY: ${{ secrets.SYN_API_KEY || 'fake-key-for-ci-testing' }} | |
| LILAC_API_KEY: ${{ secrets.LILAC_API_KEY || 'fake-key-for-ci-testing' }} | |
| run: | | |
| echo "Running tests (including integration tests) on ${{ runner.os }} with Python ${{ matrix.python-version }}..." | |
| echo "Required environment variables are set (using CI fallbacks if secrets not available)" | |
| uv run pytest tests/ -v --cov=code_puppy --cov-report=term-missing | |
| build-publish: | |
| runs-on: ubuntu-latest | |
| needs: test | |
| permissions: | |
| contents: write # Allows writing to the repository | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Python 3.13 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| - name: Install uv, build, and twine | |
| run: pip install uv build twine | |
| - name: Setup uv virtual environment | |
| run: uv venv | |
| - name: Bump version | |
| run: uv version --bump patch | |
| - name: Capture new version | |
| id: version | |
| run: | | |
| NEW_VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/') | |
| echo "version=$NEW_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "New version: $NEW_VERSION" | |
| - name: Build package | |
| run: | | |
| uv build | |
| - name: Publish to PyPI | |
| env: | |
| TWINE_USERNAME: __token__ | |
| TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} | |
| # --skip-existing: if a version already reached PyPI from a run that | |
| # died before pushing its bump commit, warn instead of failing so the | |
| # bump+tag still land and the next run moves past the collision. | |
| run: uv run twine upload --verbose --skip-existing dist/* | |
| - name: Push version bump and tag to GitHub | |
| if: ${{ success() }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| git config --global user.name 'github-actions[bot]' | |
| git config --global user.email 'github-actions[bot]@users.noreply.github.com' | |
| git add . | |
| git commit -m "chore: bump version [ci skip]" || echo "No changes to commit" | |
| TAG="v${{ steps.version.outputs.version }}" | |
| # main can advance between our checkout and this push (a human or | |
| # another branch landing a commit while we bump). The bare `git | |
| # push` used to fail the whole publish with "fetch first". Rebase | |
| # onto the latest main and retry instead of going red. | |
| for attempt in 1 2 3 4 5; do | |
| if ! git pull --rebase origin main 2>/dev/null; then | |
| echo "rebase conflicted/running; aborting and retrying" | |
| git rebase --abort 2>/dev/null || true | |
| sleep 5 | |
| continue | |
| fi | |
| git tag -f -a "$TAG" -m "Release $TAG" | |
| if git push origin main; then | |
| git push origin "$TAG" || echo "tag $TAG already exists remotely (fine)" | |
| echo "Pushed version bump and tag $TAG" | |
| exit 0 | |
| fi | |
| echo "Push rejected (attempt $attempt/5); retrying..." | |
| sleep 5 | |
| done | |
| echo "Giving up pushing version bump after 5 attempts" | |
| exit 1 |