Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 58 additions & 4 deletions .github/workflows/pypi_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,75 @@ jobs:
uses: pypa/gh-action-pypi-publish@v1.13.0

# Prod cloud-mcp is bumped via Dependabot on airbytehq/airbyte-ops-mcp, not here.
# A PyPI publish only refreshes the cloud-mcp preview against the new version.
# A PyPI publish dispatches the just-released version to airbyte-ops-mcp, whose
# deploy-mcp-command.yml opens/refreshes a deterministic `airbyte==<version>`
# bump PR and deploys the cloud-mcp preview off it.
deploy_cloud_mcp_preview:
name: Deploy Cloud MCP (Preview)
needs: publish_to_pypi
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.publish == 'true')
runs-on: ubuntu-latest
permissions: {}
steps:
# Read the raw ref/input via env (not inline `${{ }}` in the script) to
# avoid shell injection, then validate it is a plain PEP 440-ish version
# before it reaches the dispatch payload -- this both fails fast on a
# malformed tag/override and guarantees the value is safe to interpolate
# into the client-payload JSON string below (no quotes/backslashes).
- name: Resolve released version
id: version
env:
EVENT_NAME: ${{ github.event_name }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
VERSION_OVERRIDE: ${{ github.event.inputs.version_override }}
run: |
set -euo pipefail
if [ "$EVENT_NAME" = "release" ]; then
raw="$RELEASE_TAG"
else
raw="$VERSION_OVERRIDE"
fi
version="${raw#v}"
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.a-zA-Z0-9-]*)?$ ]] \
|| { echo "::error::Refusing to dispatch an unexpected version string: '$version'"; exit 1; }
echo "value=$version" >> "$GITHUB_OUTPUT"

# Poll the version-specific PyPI endpoint instead of a fixed sleep so the
# dispatch never races ahead of replication (and fails fast if the just-
# published version never shows up).
- name: Wait for PyPI availability
run: sleep 120
env:
AIRBYTE_VERSION: ${{ steps.version.outputs.value }}
run: |
set -euo pipefail
for _ in $(seq 1 30); do
if curl --fail --silent --show-error \
"https://pypi.org/pypi/airbyte/${AIRBYTE_VERSION}/json" >/dev/null; then
exit 0
fi
sleep 10
done
echo "::error::airbyte==${AIRBYTE_VERSION} was not available on PyPI in time"
exit 1

# Cross-repo dispatch is authenticated with an Octavia GitHub App token,
# scoped to airbyte-ops-mcp and to contents:write only (least privilege;
# repository scoping limits where the token works, not its permission
# set), matching the pattern used by prerelease-command.yml.
- name: Authenticate as GitHub App
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
id: get-app-token
with:
owner: airbytehq
repositories: airbyte-ops-mcp
permission-contents: write
app-id: ${{ secrets.OCTAVIA_BOT_APP_ID }}
private-key: ${{ secrets.OCTAVIA_BOT_PRIVATE_KEY }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Trigger cloud-mcp preview deploy
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
with:
token: ${{ secrets.GITHUB_CI_WORKFLOW_TRIGGER_PAT }}
token: ${{ steps.get-app-token.outputs.token }}
repository: airbytehq/airbyte-ops-mcp
event-type: deploy-cloud-mcp
client-payload: '{"mcp-server": "cloud-mcp", "preview": "true"}'
client-payload: '{"mcp-server": "cloud-mcp", "preview": "true", "airbyte-version": "${{ steps.version.outputs.value }}"}'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

☑️ Resolved in f645196. The version is now validated against a PEP 440-ish regex (^[0-9]+\.[0-9]+\.[0-9]+([.a-zA-Z0-9-]*)?$) in the Resolve released version step and the run fails if it doesn't match, so by the time it's interpolated into the client-payload JSON it can only contain [0-9A-Za-z.-] — no quotes/backslashes to break the JSON. I kept the plain interpolation rather than toJson(...) since the validated value is guaranteed safe.

Loading