Admit a provider
Run the executable provider admission command that verifies a Supported Provider end to end against your deployment and its built image.
Provider admission is one command. It verifies a provider module against a deployment and reports a named reason for every check. Use it when you add a Supported Provider, when you change a Dockerfile or the wrapper, and before you trust a deployment with a new Sync Connection. The command replaces the static provider checklist; it is the executable form of that checklist.
The command lives in the Ads Sync Reference Deployment at packages/ads-sync/deploy/scripts/provider-admission.ts. It lives there, not in the patronage CLI, because every touchpoint it reads is a deployment file: Dockerfiles, wrangler.jsonc, and Container classes. You point it at your own deployment tree.
run the static rung
pnpm --filter @patronage/ads-sync-deploy provider-admission -- --provider google_ads
The static rung needs no credentials and no Docker. It runs nine checks in order and prints one line per check. The reference deployment declares its connector containers in wrangler.jsonc and pins the wrapper version in src/container-pins.ts, so the default invocation passes for every Supported Provider. Point --deployment at your own container-runner deployment to admit a provider there.
| check | what passes |
|---|---|
catalog |
The provider id is registered in @patronage/ads-sync/providers; supportedProviderDefinitions, providerDefinitions, and supportedImageVersions agree with the module; sourceImage is digest pinned; the reporting stream requirement covers streamName; secret names do not collide with another provider. |
dockerfile |
Exactly one Dockerfile in the images directory pins FROM to the module sourceImage; AIRBYTE_CONNECTOR_IMAGE equals that pin; the file copies and runs airbyte-source-server.py; .dockerignore allowlists the Dockerfile and every COPY input. |
wrapper |
airbyte-source-server.py declares WRAPPER_VERSION in the form ads-sync-wrapper-v<major>.<minor>, and it equals --expect-wrapper-version when you pass one. |
wrangler-container |
wrangler.jsonc has a containers[] entry for the provider: either image is the admitted Dockerfile path, or image is a registry reference whose last repository segment names the provider (for example registry.example.com/ads-sync/ads-sync-google-ads:0.3.0). A registry entry passes with the detail the running image pin is verified by the live rung through /metadata; the consumer image digest never equals the upstream connector digest, so the static rung does not compare them. Failure text: declares no containers[] entry whose image is <Dockerfile or name>. |
wrangler-binding |
durable_objects.bindings has an entry for that container class. |
wrangler-migration |
A migrations[] entry lists the class in new_sqlite_classes or new_classes. |
container-class |
A .ts file in the deployment declares class <name> extends Container and a module exports it. *.test.ts files are not sources for this check. |
metadata-expectation |
Both halves of the pin. Wrapper version: every wrapper-version literal in the deployment .ts sources (tests excluded) equals the wrapper file version; a deployment with no literal fails. Connector image: the sources either pin the provider’s sourceImage digest as a literal that equals the package pin, or read supportedImageVersions and so derive it from the package. A literal for the same repository with another digest fails with <file> pins connectorImage <image> but the package pins <expected>; no literal and no read fails with no connectorImage pin found: no .ts source under <root> pins <image> or reads supportedImageVersions. A Qualified Run needs an exact metadata pin. |
fixture |
<fixtures>/<provider>/catalog.json contains the provider stream with no reporting drift, and source-config.json yields a source identity. |
The default deployment is the reference deployment itself. Point the command at another deployment with --deployment <dir>; a relative path resolves from packages/ads-sync/deploy when you run the command through pnpm --filter. The command finds wrangler.jsonc at the deployment root or under worker/; pass --wrangler <file> for another location. It reads Dockerfiles from <deployment>/images when that directory exists, otherwise from the reference images; pass --images <dir> to override. Fixtures default to the package’s src/test-fixtures/provider-admission; pass --fixtures <dir> for your own.
run the live rung
pnpm --filter @patronage/ads-sync-deploy provider-admission -- \
--provider google_search_console \
--deployment ../../my-ads-sync-deployment \
--live
--live needs Docker. It builds the admitted Dockerfile for linux/amd64, starts the image on a local port, and adds four checks:
| check | what passes |
|---|---|
image-metadata |
GET /metadata reports connectorImage and wrapperVersion that equal the package image and the wrapper file, verified through the same assertContainerImagePin the Qualified Run seam uses, with the seam’s bounded cold-start retry. |
spec |
GET /spec succeeds and the connector emits a SPEC message. |
check |
POST /check with the fixture source config succeeds and the connector emits a CONNECTION_STATUS message. A FAILED status whose message is a config-shape rejection (for example Config validation error) fails the check, because the source config never reached the provider. |
bounded-read |
POST /read-artifact with the fixture config and catalog returns stdout and stderr inside 8 MiB and 50,000 lines, within a five-minute wall clock. |
With synthetic fixture credentials, check reports CONNECTION_STATUS FAILED with the provider’s authentication error and passes. That proves the pinned connector accepted the config shape and reached the provider. Pass --source-config <file> with a real source config to require SUCCEEDED and a read that exits 0 with at least one RECORD or STATE message. Keep that file out of the repository.
Pass --image <ref> to admit an image you already built with build-images.sh instead of building again. Pass --json for a machine-readable report. The exit code is 0 when every check passes and 1 otherwise.
read a failure
Every failure names the touchpoint and the reason. Three examples from the command’s own test suite:
FAIL catalog no provider module registered for "linkedin_ads" (supported: google_ads, google_search_console, meta_ads)
FAIL dockerfile no Dockerfile in <images> pins FROM airbyte/source-facebook-marketing:5.2.11@sha256:4d6c… (found: Dockerfile.meta FROM airbyte/source-facebook-marketing:5.2.11@sha256:ffff…; …)
FAIL metadata-expectation src/runtime.ts expects ads-sync-wrapper-v0.2 but airbyte-source-server.py declares ads-sync-wrapper-v0.3
A failed dockerfile check blocks the Wrangler and Container checks; they report blocked and pass again once the Dockerfile pin is fixed. A failed image-metadata check blocks spec, check, and bounded-read, and the command still stops the container.
add a provider
Admission is the done bar for a new provider. Work through the checks in order:
- Add the provider module and register it in
@patronage/ads-sync/providers(catalog). - Add a Dockerfile in the images directory with a digest-pinned
FROM, the matchingAIRBYTE_CONNECTOR_IMAGE, and the wrapperCOPY; add the file to.dockerignore(dockerfile,wrapper). - Declare the container, its Durable Object binding, and a new migration in
wrangler.jsonc, and add the exportedContainersubclass (wrangler-*,container-class,metadata-expectation). - Add
catalog.jsonandsource-config.jsonunder the fixtures directory with synthetic values only (fixture). - Run the live rung and keep the output as admission evidence.