name: release on: push: branches: - main tags: - 'v*' jobs: # Gateway contract gate: the plugin must bind and wire against the REAL # Hermes gateway API (pinned to the deployed image source) before any # release can be built or published. Without this, adapter/API drift used # to surface only as a gateway crash after deploy (the set_message_handler # AttributeError). HERMES_CONTRACT_REQUIRED=1 turns a missing hermes-agent # into a hard failure so this gate can never silently skip. contract-test: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python 3.13 (matches Hermes image) uses: actions/setup-python@v5 with: python-version: '3.13' - name: Install uv run: pip install uv - name: Clone pinned Hermes gateway source (deployed image tag) run: | git clone --depth 1 --branch v2026.8.31 \ https://github.com/NousResearch/hermes-agent.git plugin/.hermes-src test "$(git -C plugin/.hermes-src rev-parse HEAD)" = \ "29112bef099274229cadff79cdff7bf7b99c4b77" - name: Editable install hermes-agent + pytest run: | uv venv --python 3.13 plugin/.venv-contract uv pip install --python plugin/.venv-contract/bin/python -e plugin/.hermes-src uv pip install --python plugin/.venv-contract/bin/python pytest - name: Run gateway contract tests env: HERMES_CONTRACT_REQUIRED: '1' run: | cd plugin .venv-contract/bin/python -m pytest tests/contract -q test-and-release: runs-on: ubuntu-latest needs: contract-test steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python venv & dependencies run: | python3 -m venv .venv . .venv/bin/activate pip install --upgrade pip pip install uv pytest - name: Run unit tests run: | . .venv/bin/activate PYTHONPATH=plugin pytest plugin/tests - name: Build wheel and sdist run: | . .venv/bin/activate uv build plugin/ - name: Publish Gitea Release if: startsWith(github.ref, 'refs/tags/v') env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN }} run: | tag_name="${GITHUB_REF#refs/tags/}" echo "Publishing release for $tag_name" wheel_file=$(ls plugin/dist/*.whl | head -n 1) tar_file=$(ls plugin/dist/*.tar.gz | head -n 1) api_url="${{ github.server_url }}/api/v1/repos/${{ github.repository }}" # server_url may be http while Gitea redirects (308) to https: # curl needs -L or the POST returns an empty body. Release may also # already exist (retried run) — reuse it instead of failing. fetch_id() { python3 -c "import json,sys;print(json.load(sys.stdin).get('id') or '')" 2>/dev/null; } response=$(curl -s -k -L -H "Authorization: token $GITEA_TOKEN" \ "$api_url/releases/tags/$tag_name") release_id=$(printf '%s' "$response" | fetch_id) if [ -z "$release_id" ]; then response=$(curl -s -k -L -X POST "$api_url/releases" \ -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"tag_name\": \"$tag_name\", \"name\": \"$tag_name\", \"body\": \"Release $tag_name for hermes-meshtastic plugin\"}") release_id=$(printf '%s' "$response" | fetch_id) fi if [ -n "$release_id" ]; then curl -s -k -L -X POST "$api_url/releases/$release_id/assets?name=$(basename "$wheel_file")" \ -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/octet-stream" \ --data-binary "@$wheel_file" curl -s -k -L -X POST "$api_url/releases/$release_id/assets?name=$(basename "$tar_file")" \ -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/octet-stream" \ --data-binary "@$tar_file" else echo "ERROR: could not create or fetch release $tag_name" >&2 exit 1 fi