Skip to content

Commit e1100ef

Browse files
authored
ci: create unified cdk, sdm, and builder publish flow (#107)
1 parent cd1bd1c commit e1100ef

File tree

3 files changed

+319
-8
lines changed

3 files changed

+319
-8
lines changed

.github/workflows/publish_sdm_connector.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# This flow publishes the Source-Declarative-Manifest (SDM)
22
# connector to DockerHub as a Docker image.
3+
# TODO: Delete this workflow file once the unified publish flow is implemented and proven stable.
34

45
name: Publish SDM Connector
56

.github/workflows/pypi_publish.yml

Lines changed: 299 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,98 @@
11
# This workflow builds the python package.
2-
# On release tags, it also publishes to PyPI and DockerHub.
3-
# If we rename the workflow file name, we have to also update the
4-
# Trusted Publisher settings on PyPI.
2+
3+
# When from from a release tags or a workflow dispatch, it also publishes to PyPI and DockerHub along
4+
# with bumping the Connector Builder pinned version.
5+
# Note: We may want to rename this file at some point. However, if we rename the workflow file name,
6+
# we have to also update the Trusted Publisher settings on PyPI.
7+
58
name: Packaging and Publishing
69

710
on:
811
push:
12+
tags:
13+
- 'v*'
914
workflow_dispatch:
1015
inputs:
1116
version:
12-
description: "The version to publish, ie 1.0.0 or 1.0.0-dev1"
17+
description: "Version. The version to publish, ie 1.0.0 or 1.0.0-dev1. In most cases, you can leave this blank. If run from a release tag (recommended), the version number will be inferred from the git tag."
18+
required: false
19+
publish_to_pypi:
20+
description: "Publish to PyPI. If true, the workflow will publish to PyPI."
21+
type: boolean
22+
required: true
23+
default: true
24+
publish_to_dockerhub:
25+
description: "Publish to DockerHub. If true, the workflow will publish the SDM connector to DockerHub."
26+
type: boolean
1327
required: true
28+
default: true
29+
update_connector_builder:
30+
description: "Update Connector Builder. If true, the workflow will create a PR to bump the CDK version used by Connector Builder."
31+
type: boolean
32+
required: true
33+
default: true
1434

1535
jobs:
1636
build:
37+
name: Build Python Package
1738
runs-on: ubuntu-latest
1839
steps:
40+
- name: Detect Release Tag Version
41+
if: startsWith(github.ref, 'refs/tags/v')
42+
run: |
43+
DETECTED_VERSION=${{ github.ref_name }}
44+
echo "Version ref set to '${DETECTED_VERSION}'"
45+
# Remove the 'v' prefix if it exists
46+
DETECTED_VERSION="${DETECTED_VERSION#v}"
47+
echo "Setting version to '$DETECTED_VERSION'"
48+
echo "DETECTED_VERSION=${DETECTED_VERSION}" >> $GITHUB_ENV
49+
50+
- name: Validate and set VERSION from git ref ('${{ github.ref_name }}') and input (${{ github.event.inputs.version || 'none' }})
51+
id: set_version
52+
run: |
53+
INPUT_VERSION=${{ github.event.inputs.version }}
54+
echo "Version input set to '${INPUT_VERSION}'"
55+
# Exit with success if both detected and input versions are empty
56+
if [ -z "${DETECTED_VERSION:-}" ] && [ -z "${INPUT_VERSION:-}" ]; then
57+
echo "No version detected or input. Will publish to SHA tag instead."
58+
echo 'VERSION=' >> $GITHUB_ENV
59+
exit 0
60+
fi
61+
# Remove the 'v' prefix if it exists
62+
INPUT_VERSION="${INPUT_VERSION#v}"
63+
# Fail if detected version is non-empty and different from the input version
64+
if [ -n "${DETECTED_VERSION:-}" ] && [ -n "${INPUT_VERSION:-}" ] && [ "${DETECTED_VERSION}" != "${INPUT_VERSION}" ]; then
65+
echo "Error: Version input '${INPUT_VERSION}' does not match detected version '${DETECTED_VERSION}'."
66+
exit 1
67+
fi
68+
# Set the version to the input version if non-empty, otherwise the detected version
69+
VERSION="${INPUT_VERSION:-$DETECTED_VERSION}"
70+
# Fail if the version is still empty
71+
if [ -z "$VERSION" ]; then
72+
echo "Error: VERSION is not set. Ensure the tag follows the format 'refs/tags/vX.Y.Z'."
73+
exit 1
74+
fi
75+
echo "Setting version to '$VERSION'"
76+
echo "VERSION=${VERSION}" >> $GITHUB_ENV
77+
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
78+
# Check if version is a prerelease version (will not tag 'latest')
79+
if [[ "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
80+
echo "IS_PRERELEASE=false" >> $GITHUB_ENV
81+
echo "IS_PRERELEASE=false" >> $GITHUB_OUTPUT
82+
else
83+
echo "IS_PRERELEASE=true" >> $GITHUB_ENV
84+
echo "IS_PRERELEASE=true" >> $GITHUB_OUTPUT
85+
fi
86+
1987
- uses: actions/checkout@v4
2088
with:
2189
fetch-depth: 0
22-
ref: ${{ github.event_name == 'workflow_dispatch' && format('v{0}', github.event.inputs.version) || github.ref }}
2390

2491
- uses: hynek/build-and-inspect-python-package@v2
92+
env:
93+
# Pass in the evaluated version from the previous step
94+
# More info: https://github.com/mtkennerly/poetry-dynamic-versioning#user-content-environment-variables
95+
POETRY_DYNAMIC_VERSIONING_BYPASS: ${{ env.VERSION || '0.0.0dev0'}}
2596

2697
- uses: actions/upload-artifact@v4
2798
with:
@@ -30,7 +101,11 @@ jobs:
30101
/tmp/baipp/dist/*.whl
31102
/tmp/baipp/dist/*.tar.gz
32103
33-
publish:
104+
outputs:
105+
VERSION: ${{ steps.set_version.outputs.VERSION }}
106+
IS_PRERELEASE: ${{ steps.set_version.outputs.IS_PRERELEASE }}
107+
108+
publish_cdk:
34109
name: Publish CDK version to PyPI
35110
runs-on: ubuntu-latest
36111
needs: [build]
@@ -39,8 +114,11 @@ jobs:
39114
contents: write
40115
environment:
41116
name: PyPi
42-
url: https://pypi.org/p/airbyte
43-
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
117+
url: https://pypi.org/p/airbyte-cdk/
118+
if: (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.publish_to_pypi == 'true' || github.event.inputs.update_connector_builder == 'true'))
119+
env:
120+
VERSION: ${{ needs.build.outputs.VERSION }}
121+
IS_PRERELEASE: ${{ needs.build.outputs.IS_PRERELEASE }}
44122
steps:
45123
- uses: actions/download-artifact@v4
46124
with:
@@ -58,4 +136,217 @@ jobs:
58136
file_glob: true
59137

60138
- name: Publish to PyPI
139+
if: (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) || github.event.inputs.publish_to_pypi == 'true'
61140
uses: pypa/[email protected]
141+
142+
publish_sdm:
143+
name: Publish SDM to DockerHub
144+
# TODO: When we're ready to publish after each release, prefix the `if` below with:
145+
# (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) ||
146+
# Until then, this workflow needs to be kicked off manually.
147+
# Last remaining blocker documented here: https://github.com/airbytehq/airbyte-python-cdk/issues/64
148+
if: (github.event_name == 'workflow_dispatch' && github.event.inputs.publish_to_dockerhub == 'true')
149+
runs-on: ubuntu-latest
150+
needs: [build]
151+
environment:
152+
name: DockerHub
153+
url: https://hub.docker.com/r/airbyte/source-declarative-manifest/tags
154+
env:
155+
VERSION: ${{ needs.build.outputs.VERSION }}
156+
IS_PRERELEASE: ${{ needs.build.outputs.IS_PRERELEASE }}
157+
158+
steps:
159+
- uses: actions/checkout@v4
160+
with:
161+
fetch-depth: 0
162+
163+
# We need to download the build artifact again because the previous job was on a different runner
164+
- name: Download Build Artifact
165+
uses: actions/download-artifact@v4
166+
with:
167+
name: Packages-${{ github.run_id }}
168+
path: dist
169+
170+
- name: Set up QEMU for multi-platform builds
171+
uses: docker/setup-qemu-action@v3
172+
173+
- name: Set up Docker Buildx
174+
uses: docker/setup-buildx-action@v3
175+
176+
- name: Login to Docker Hub
177+
uses: docker/login-action@v3
178+
with:
179+
username: ${{ secrets.DOCKER_HUB_USERNAME }}
180+
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
181+
182+
- name: "Check for existing tag (version: ${{ env.VERSION || 'none' }} )"
183+
if: env.VERSION != ''
184+
run: |
185+
tag="airbyte/source-declarative-manifest:${{ env.VERSION }}"
186+
if [ -z "$tag" ]; then
187+
echo "Error: VERSION is not set. Ensure the tag follows the format 'refs/tags/vX.Y.Z'."
188+
exit 1
189+
fi
190+
echo "Checking if tag '$tag' exists on DockerHub..."
191+
if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$tag" > /dev/null 2>&1; then
192+
echo "The tag '$tag' already exists on DockerHub. Skipping publish to prevent overwrite."
193+
exit 1
194+
fi
195+
echo "No existing tag '$tag' found. Proceeding with publish."
196+
197+
- name: "Build and push (sha tag: '${{ github.sha }}')"
198+
# Only run if the version is not set
199+
if: env.VERSION == ''
200+
uses: docker/build-push-action@v5
201+
with:
202+
context: .
203+
platforms: linux/amd64,linux/arm64
204+
push: true
205+
tags: |
206+
airbyte/source-declarative-manifest:${{ github.sha }}
207+
208+
- name: "Build and push (version tag: ${{ env.VERSION || 'none'}})"
209+
# Only run if the version is set
210+
if: env.VERSION != ''
211+
uses: docker/build-push-action@v5
212+
with:
213+
context: .
214+
platforms: linux/amd64,linux/arm64
215+
push: true
216+
tags: |
217+
airbyte/source-declarative-manifest:${{ env.VERSION }}
218+
219+
- name: Build and push ('latest' tag)
220+
# Only run if version is set and IS_PRERELEASE is false
221+
if: env.VERSION != '' && env.IS_PRERELEASE == 'false'
222+
uses: docker/build-push-action@v5
223+
with:
224+
context: .
225+
platforms: linux/amd64,linux/arm64
226+
push: true
227+
tags: |
228+
airbyte/source-declarative-manifest:latest
229+
230+
update-connector-builder:
231+
# Create a PR against the Builder, to update the CDK version that it uses.
232+
# In the future, Builder may use the SDM docker image instead of the Python CDK package.
233+
name: Bump Connector Builder CDK version
234+
needs:
235+
- build
236+
- publish_cdk
237+
if: (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) || (github.event_name == 'workflow_dispatch' && github.event.inputs.update_connector_builder == 'true')
238+
env:
239+
VERSION: ${{ needs.build.outputs.VERSION }}
240+
IS_PRERELEASE: ${{ needs.build.outputs.IS_PRERELEASE }}
241+
runs-on: ubuntu-latest
242+
steps:
243+
- uses: actions/setup-python@v5
244+
with:
245+
python-version: "3.10"
246+
- name: Checkout Airbyte Platform Internal
247+
uses: actions/checkout@v4
248+
with:
249+
repository: airbytehq/airbyte-platform-internal
250+
token: ${{ secrets.GH_PAT_MAINTENANCE_OCTAVIA }}
251+
- name: Update Builder's CDK version to ${{ env.VERSION }}
252+
# PyPI servers aren't immediately updated so we may need to retry a few times.
253+
uses: nick-fields/retry@v3
254+
with:
255+
shell: bash
256+
max_attempts: 5
257+
retry_wait_seconds: 30
258+
timeout_minutes: 7
259+
command: |
260+
set -euo pipefail
261+
PREVIOUS_VERSION=$(cat oss/airbyte-connector-builder-resources/CDK_VERSION)
262+
sed -i "s/${PREVIOUS_VERSION}/${VERSION}/g" "oss/airbyte-connector-builder-server/Dockerfile"
263+
sed -i "s/${PREVIOUS_VERSION}/${VERSION}/g" "cloud/airbyte-connector-builder-server-wrapped/Dockerfile"
264+
sed -i "s/airbyte-cdk==${PREVIOUS_VERSION}/airbyte-cdk==${VERSION}/g" oss/airbyte-connector-builder-server/requirements.in
265+
echo ${VERSION} > oss/airbyte-connector-builder-resources/CDK_VERSION
266+
cd oss/airbyte-connector-builder-server
267+
python -m pip install --no-cache-dir pip-tools
268+
pip-compile --upgrade
269+
- name: Create Pull Request
270+
id: create-pull-request
271+
uses: peter-evans/create-pull-request@v6
272+
with:
273+
token: ${{ secrets.GH_PAT_MAINTENANCE_OCTAVIA }}
274+
commit-message: "chore: update CDK version following release"
275+
title: "chore: update CDK version following release"
276+
body: This is an automatically generated PR triggered by a CDK release
277+
branch: automatic-cdk-release
278+
base: master
279+
delete-branch: true
280+
- name: Post success to Slack channel dev-connectors-extensibility
281+
uses: slackapi/[email protected]
282+
continue-on-error: true
283+
env:
284+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN_AIRBYTE_TEAM }}
285+
with:
286+
channel-id: C04J1M66D8B
287+
# Channel: #dev-connectors-extensibility-releases
288+
# Link (internal): https://airbytehq-team.slack.com/archives/C04J1M66D8B
289+
payload: |
290+
{
291+
"text": "A new version of Python CDK has been released!",
292+
"blocks": [
293+
{
294+
"type": "section",
295+
"text": {
296+
"type": "mrkdwn",
297+
"text": "Python CDK `v${{ env.VERSION }}` has been <https://github.com/airbytehq/airbyte-python-cdk/releases|released>\n\n"
298+
}
299+
},
300+
{
301+
"type": "section",
302+
"text": {
303+
"type": "mrkdwn",
304+
"text": "A PR has also been created for the <${{ steps.create-pull-request.outputs.pull-request-url }}|Connector Builder>\n"
305+
}
306+
},
307+
{
308+
"type": "section",
309+
"text": {
310+
"type": "mrkdwn",
311+
"text": "See details on <https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}|GitHub>\n"
312+
}
313+
}
314+
]
315+
}
316+
- name: Post failure to Slack channel dev-connectors-extensibility
317+
if: failure()
318+
uses: slackapi/[email protected]
319+
continue-on-error: true
320+
env:
321+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN_AIRBYTE_TEAM }}
322+
with:
323+
channel-id: C04J1M66D8B
324+
# Channel: #dev-connectors-extensibility-releases
325+
# Link (internal): https://airbytehq-team.slack.com/archives/C04J1M66D8B
326+
payload: |
327+
{
328+
"text": ":warning: A new version of Python CDK has been released but Connector Builder hasn't been automatically updated",
329+
"blocks": [
330+
{
331+
"type": "section",
332+
"text": {
333+
"type": "mrkdwn",
334+
"text": "Python CDK `v${{ env.VERSION }}` has been <https://github.com/airbytehq/airbyte-python-cdk/releases|released>\n\n"
335+
}
336+
},
337+
{
338+
"type": "section",
339+
"text": {
340+
"type": "mrkdwn",
341+
"text": ":warning: Could not automatically create a PR for Connector Builder>\n"
342+
}
343+
},
344+
{
345+
"type": "section",
346+
"text": {
347+
"type": "mrkdwn",
348+
"text": "See details on <https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}|GitHub>\n"
349+
}
350+
}
351+
]
352+
}

.github/workflows/pytest_fast.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@ on:
77
pull_request:
88

99
jobs:
10+
test-build:
11+
name: Build and Inspect Python Package
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- uses: hynek/build-and-inspect-python-package@v2
18+
env:
19+
# Pass in dummy version '0.0.0dev0' version to appease dynamic versioning
20+
POETRY_DYNAMIC_VERSIONING_BYPASS: 0.0.0dev0
21+
22+
- uses: actions/upload-artifact@v4
23+
with:
24+
name: Packages-${{ github.run_id }}
25+
path: |
26+
/tmp/baipp/dist/*.whl
27+
/tmp/baipp/dist/*.tar.gz
28+
1029
pytest-fast:
1130
name: Pytest (Fast)
1231
runs-on: ubuntu-latest

0 commit comments

Comments
 (0)