Skip to content

Commit d8a8fdd

Browse files
committed
merge from main
2 parents dbfa3bb + e80c173 commit d8a8fdd

File tree

74 files changed

+6456
-126
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+6456
-126
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: 'Check Docker Tag Exists'
2+
description: 'Check if a Docker tag exists on DockerHub to prevent overwrites'
3+
inputs:
4+
image_name:
5+
description: 'Docker image name (e.g. airbyte/source-declarative-manifest)'
6+
required: true
7+
tag:
8+
description: 'Docker tag to check'
9+
required: true
10+
runs:
11+
using: "composite"
12+
steps:
13+
- name: "Check for existing tag (${{ inputs.image_name }}:${{ inputs.tag }})"
14+
shell: bash
15+
run: |
16+
image="${{ inputs.image_name }}"
17+
tag_input="${{ inputs.tag }}"
18+
if [ -z "$image" ] || [ -z "$tag_input" ]; then
19+
echo "Error: image_name and tag are required."
20+
exit 1
21+
fi
22+
tag="${image}:${tag_input}"
23+
echo "Checking if tag '$tag' exists on DockerHub..."
24+
if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$tag" > /dev/null 2>&1; then
25+
echo "The tag '$tag' already exists on DockerHub. Skipping publish to prevent overwrite."
26+
exit 1
27+
fi
28+
echo "No existing tag '$tag' found. Proceeding with publish."

.github/workflows/docker-build-check.yml

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
name: Docker Build Check
2+
permissions:
3+
contents: read
24

35
on:
46
pull_request:
57
branches:
68
- main
79

810
jobs:
9-
docker-build-check:
10-
name: SDM Docker Image Build # Renamed job to be more descriptive
11+
sdm-docker-build-check:
12+
name: SDM Docker Image Build
1113
runs-on: ubuntu-24.04
1214
steps:
1315
- name: Checkout code
@@ -42,3 +44,29 @@ jobs:
4244
push: false
4345
tags: airbyte/source-declarative-manifest:pr-${{ github.event.pull_request.number }}
4446
outputs: type=image,name=target,annotation-index.org.opencontainers.image.description=SDM Docker image for PR ${{ github.event.pull_request.number }}
47+
48+
manifest-server-docker-build-check:
49+
name: Manifest Server Docker Image Build
50+
runs-on: ubuntu-24.04
51+
steps:
52+
- name: Checkout code
53+
uses: actions/checkout@v4
54+
with:
55+
fetch-depth: 0
56+
57+
- name: Set up QEMU for multi-platform builds
58+
uses: docker/setup-qemu-action@v3
59+
60+
- name: Set up Docker Buildx
61+
uses: docker/setup-buildx-action@v3
62+
63+
- name: Build Manifest Server Docker image for multiple platforms
64+
id: manifest-server-build
65+
uses: docker/build-push-action@v5
66+
with:
67+
context: .
68+
file: airbyte_cdk/manifest_server/Dockerfile
69+
platforms: linux/amd64,linux/arm64
70+
push: false
71+
tags: airbyte/manifest-server:pr-${{ github.event.pull_request.number }}
72+
outputs: type=image,name=target,annotation-index.org.opencontainers.image.description=Manifest Server Docker image for PR ${{ github.event.pull_request.number }}

.github/workflows/pypi_publish.yml renamed to .github/workflows/publish.yml

Lines changed: 95 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
# we have to also update the Trusted Publisher settings on PyPI.
77

88
name: CDK Publish
9+
permissions:
10+
contents: read
911

1012
on:
1113
push:
@@ -31,6 +33,11 @@ on:
3133
type: boolean
3234
required: true
3335
default: true
36+
publish_manifest_server:
37+
description: "Publish Manifest Server to DockerHub. If true, the workflow will publish the Manifest Server to DockerHub."
38+
type: boolean
39+
required: true
40+
default: true
3441
update_connector_builder:
3542
description: "Update Connector Builder. If true, the workflow will create a PR to bump the CDK version used by Connector Builder."
3643
type: boolean
@@ -204,18 +211,10 @@ jobs:
204211

205212
- name: "Check for existing tag (version: ${{ env.VERSION || 'none' }} )"
206213
if: env.VERSION != ''
207-
run: |
208-
tag="airbyte/source-declarative-manifest:${{ env.VERSION }}"
209-
if [ -z "$tag" ]; then
210-
echo "Error: VERSION is not set. Ensure the tag follows the format 'refs/tags/vX.Y.Z'."
211-
exit 1
212-
fi
213-
echo "Checking if tag '$tag' exists on DockerHub..."
214-
if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$tag" > /dev/null 2>&1; then
215-
echo "The tag '$tag' already exists on DockerHub. Skipping publish to prevent overwrite."
216-
exit 1
217-
fi
218-
echo "No existing tag '$tag' found. Proceeding with publish."
214+
uses: ./.github/actions/check-docker-tag
215+
with:
216+
image_name: airbyte/source-declarative-manifest
217+
tag: ${{ env.VERSION }}
219218

220219
- name: "Build and push (sha tag: '${{ github.sha }}')"
221220
# Only run if the version is not set
@@ -250,6 +249,90 @@ jobs:
250249
tags: |
251250
airbyte/source-declarative-manifest:latest
252251
252+
publish_manifest_server:
253+
name: Publish Manifest Server to DockerHub
254+
if: >
255+
(github.event_name == 'push' &&
256+
startsWith(github.ref, 'refs/tags/v')) ||
257+
(github.event_name == 'workflow_dispatch' &&
258+
github.event.inputs.publish_manifest_server == 'true'
259+
)
260+
runs-on: ubuntu-24.04
261+
needs: [build]
262+
environment:
263+
name: DockerHub
264+
url: https://hub.docker.com/r/airbyte/manifest-server/tags
265+
env:
266+
VERSION: ${{ needs.build.outputs.VERSION }}
267+
IS_PRERELEASE: ${{ needs.build.outputs.IS_PRERELEASE }}
268+
269+
steps:
270+
- uses: actions/checkout@v4
271+
with:
272+
fetch-depth: 0
273+
274+
# We need to download the build artifact again because the previous job was on a different runner
275+
- name: Download Build Artifact
276+
uses: actions/download-artifact@v4
277+
with:
278+
name: Packages-${{ github.run_id }}
279+
path: dist
280+
281+
- name: Set up QEMU for multi-platform builds
282+
uses: docker/setup-qemu-action@v3
283+
284+
- name: Set up Docker Buildx
285+
uses: docker/setup-buildx-action@v3
286+
287+
- name: Login to Docker Hub
288+
uses: docker/login-action@v3
289+
with:
290+
username: ${{ secrets.DOCKER_HUB_USERNAME }}
291+
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
292+
293+
- name: "Check for existing tag (version: ${{ env.VERSION || 'none' }} )"
294+
if: env.VERSION != ''
295+
uses: ./.github/actions/check-docker-tag
296+
with:
297+
image_name: airbyte/manifest-server
298+
tag: ${{ env.VERSION }}
299+
300+
- name: "Build and push (sha tag: '${{ github.sha }}')"
301+
# Only run if the version is not set
302+
if: env.VERSION == ''
303+
uses: docker/build-push-action@v5
304+
with:
305+
context: .
306+
file: airbyte_cdk/manifest_server/Dockerfile
307+
platforms: linux/amd64,linux/arm64
308+
push: true
309+
tags: |
310+
airbyte/manifest-server:${{ github.sha }}
311+
312+
- name: "Build and push (version tag: ${{ env.VERSION || 'none'}})"
313+
# Only run if the version is set
314+
if: env.VERSION != ''
315+
uses: docker/build-push-action@v5
316+
with:
317+
context: .
318+
file: airbyte_cdk/manifest_server/Dockerfile
319+
platforms: linux/amd64,linux/arm64
320+
push: true
321+
tags: |
322+
airbyte/manifest-server:${{ env.VERSION }}
323+
324+
- name: Build and push ('latest' tag)
325+
# Only run if version is set and IS_PRERELEASE is false
326+
if: env.VERSION != '' && env.IS_PRERELEASE == 'false'
327+
uses: docker/build-push-action@v5
328+
with:
329+
context: .
330+
file: airbyte_cdk/manifest_server/Dockerfile
331+
platforms: linux/amd64,linux/arm64
332+
push: true
333+
tags: |
334+
airbyte/manifest-server:latest
335+
253336
update-connector-builder:
254337
# Create a PR against the Builder, to update the CDK version that it uses.
255338
# In the future, Builder may use the SDM docker image instead of the Python CDK package.

airbyte_cdk/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@
107107
from .sources.declarative.extractors.record_filter import RecordFilter
108108
from .sources.declarative.incremental import DatetimeBasedCursor
109109
from .sources.declarative.interpolation import InterpolatedBoolean, InterpolatedString
110-
from .sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
111110
from .sources.declarative.migrations.legacy_to_per_partition_state_migration import (
112111
LegacyToPerPartitionStateMigration,
113112
)
@@ -253,7 +252,6 @@
253252
"JsonDecoder",
254253
"JsonFileSchemaLoader",
255254
"LegacyToPerPartitionStateMigration",
256-
"ManifestDeclarativeSource",
257255
"MinMaxDatetime",
258256
"NoAuth",
259257
"OffsetIncrement",

airbyte_cdk/connector_builder/connector_builder_handler.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
ConcurrentDeclarativeSource,
2020
TestLimits,
2121
)
22-
from airbyte_cdk.sources.declarative.declarative_source import DeclarativeSource
23-
from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
2422
from airbyte_cdk.utils.airbyte_secrets_utils import filter_secrets
2523
from airbyte_cdk.utils.datetime_helpers import ab_datetime_now
2624
from airbyte_cdk.utils.traced_exception import AirbyteTracedException
@@ -90,7 +88,7 @@ def create_source(
9088

9189

9290
def read_stream(
93-
source: DeclarativeSource,
91+
source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]],
9492
config: Mapping[str, Any],
9593
configured_catalog: ConfiguredAirbyteCatalog,
9694
state: List[AirbyteStateMessage],
@@ -128,7 +126,9 @@ def read_stream(
128126
return error.as_airbyte_message()
129127

130128

131-
def resolve_manifest(source: ManifestDeclarativeSource) -> AirbyteMessage:
129+
def resolve_manifest(
130+
source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]],
131+
) -> AirbyteMessage:
132132
try:
133133
return AirbyteMessage(
134134
type=Type.RECORD,
@@ -145,7 +145,9 @@ def resolve_manifest(source: ManifestDeclarativeSource) -> AirbyteMessage:
145145
return error.as_airbyte_message()
146146

147147

148-
def full_resolve_manifest(source: ManifestDeclarativeSource, limits: TestLimits) -> AirbyteMessage:
148+
def full_resolve_manifest(
149+
source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]], limits: TestLimits
150+
) -> AirbyteMessage:
149151
try:
150152
manifest = {**source.resolved_manifest}
151153
streams = manifest.get("streams", [])

airbyte_cdk/connector_builder/main.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
ConfiguredAirbyteCatalog,
2626
ConfiguredAirbyteCatalogSerializer,
2727
)
28-
from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
28+
from airbyte_cdk.sources.declarative.concurrent_declarative_source import (
29+
ConcurrentDeclarativeSource,
30+
)
2931
from airbyte_cdk.sources.source import Source
3032
from airbyte_cdk.utils.traced_exception import AirbyteTracedException
3133

@@ -68,7 +70,7 @@ def get_config_and_catalog_from_args(
6870

6971

7072
def handle_connector_builder_request(
71-
source: ManifestDeclarativeSource,
73+
source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]],
7274
command: str,
7375
config: Mapping[str, Any],
7476
catalog: Optional[ConfiguredAirbyteCatalog],

airbyte_cdk/connector_builder/test_reader/reader.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
ConfiguredAirbyteCatalog,
2424
TraceType,
2525
)
26-
from airbyte_cdk.sources.declarative.declarative_source import DeclarativeSource
26+
from airbyte_cdk.sources.declarative.concurrent_declarative_source import (
27+
ConcurrentDeclarativeSource,
28+
)
2729
from airbyte_cdk.utils import AirbyteTracedException
2830
from airbyte_cdk.utils.datetime_format_inferrer import DatetimeFormatInferrer
2931
from airbyte_cdk.utils.schema_inferrer import (
@@ -55,7 +57,7 @@ class TestReader:
5557
that contains slices of data, log messages, auxiliary requests, and any inferred schema or datetime formats.
5658
5759
Parameters:
58-
source (DeclarativeSource): The data source to read from.
60+
source (ConcurrentDeclarativeSource): The data source to read from.
5961
config (Mapping[str, Any]): Configuration parameters for the source.
6062
configured_catalog (ConfiguredAirbyteCatalog): Catalog containing stream configuration.
6163
state (List[AirbyteStateMessage]): Current state information for the read.
@@ -83,7 +85,7 @@ def __init__(
8385

8486
def run_test_read(
8587
self,
86-
source: DeclarativeSource,
88+
source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]],
8789
config: Mapping[str, Any],
8890
configured_catalog: ConfiguredAirbyteCatalog,
8991
stream_name: str,
@@ -94,7 +96,7 @@ def run_test_read(
9496
Run a test read for the connector by reading from a single stream and inferring schema and datetime formats.
9597
9698
Parameters:
97-
source (DeclarativeSource): The source instance providing the streams.
99+
source (ConcurrentDeclarativeSource): The source instance providing the streams.
98100
config (Mapping[str, Any]): The configuration settings to use for reading.
99101
configured_catalog (ConfiguredAirbyteCatalog): The catalog specifying the stream configuration.
100102
state (List[AirbyteStateMessage]): A list of state messages to resume the read.
@@ -126,7 +128,7 @@ def run_test_read(
126128
if stream
127129
else None,
128130
self._cursor_field_to_nested_and_composite_field(stream.cursor_field)
129-
if stream
131+
if stream and stream.cursor_field
130132
else None,
131133
)
132134
datetime_format_inferrer = DatetimeFormatInferrer()
@@ -381,13 +383,13 @@ def _get_latest_config_update(
381383

382384
def _read_stream(
383385
self,
384-
source: DeclarativeSource,
386+
source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]],
385387
config: Mapping[str, Any],
386388
configured_catalog: ConfiguredAirbyteCatalog,
387389
state: List[AirbyteStateMessage],
388390
) -> Iterator[AirbyteMessage]:
389391
"""
390-
Reads messages from the given DeclarativeSource using an AirbyteEntrypoint.
392+
Reads messages from the given ConcurrentDeclarativeSource using an AirbyteEntrypoint.
391393
392394
This method attempts to yield messages from the source's read generator. If the generator
393395
raises an AirbyteTracedException, it checks whether the exception message indicates a non-actionable
@@ -396,7 +398,7 @@ def _read_stream(
396398
wrapped into an AirbyteTracedException, and yielded as an AirbyteMessage.
397399
398400
Parameters:
399-
source (DeclarativeSource): The source object that provides data reading logic.
401+
source (ConcurrentDeclarativeSource): The source object that provides data reading logic.
400402
config (Mapping[str, Any]): The configuration dictionary for the source.
401403
configured_catalog (ConfiguredAirbyteCatalog): The catalog defining the streams and their configurations.
402404
state (List[AirbyteStateMessage]): A list representing the current state for incremental sync.

airbyte_cdk/legacy/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.

0 commit comments

Comments
 (0)