[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-astronomer-deploying-java-sdk-bundles":3,"mdc-nvt0ax-key":55,"related-org-astronomer-deploying-java-sdk-bundles":2070,"related-repo-astronomer-deploying-java-sdk-bundles":2235},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":25,"repoUrl":26,"updatedAt":27,"license":28,"forks":29,"topics":30,"repo":50,"sourceUrl":53,"mdContent":54},"deploying-java-sdk-bundles","build and deploy Airflow Java SDK bundles","Builds and deploys compiled Airflow Java SDK bundles so workers can run them. Use when the user wants to package a JVM task bundle into a JAR, asks about the `org.apache.airflow.sdk` Gradle plugin, `.\u002Fgradlew bundle`, the Maven shade\u002FBOM setup, fat vs thin JARs, the logging integration artifacts (JPL, SLF4J, Log4j 2, JUL), preview\u002Fsnapshot builds, or getting the JAR onto an Airflow worker (Docker, Kubernetes, or Astro). For the task code see authoring-java-sdk-tasks; for the Airflow coordinator settings see configuring-airflow-language-sdks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"astronomer","Astronomer","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fastronomer.png",[12,16,19,22],{"name":13,"slug":14,"type":15},"Java","java","tag",{"name":17,"slug":18,"type":15},"Data Engineering","data-engineering",{"name":20,"slug":21,"type":15},"Deployment","deployment",{"name":23,"slug":24,"type":15},"Engineering","engineering",412,"https:\u002F\u002Fgithub.com\u002Fastronomer\u002Fagents","2026-07-18T05:48:12.924272",null,55,[31,32,33,34,35,36,37,38,39,18,40,41,42,43,44,45,46,47,48,49],"agentic-workflow","agents","ai","ai-agents","airflow","apache-airflow","claude","cursor","dag","data-pipelines","dbt","llm","mcp","orchestrator","skills","workflow-automation","workflow-management","workflow-orchestration","workflows",{"repoUrl":26,"stars":25,"forks":29,"topics":51,"description":52},[31,32,33,34,35,36,37,38,39,18,40,41,42,43,44,45,46,47,48,49],"AI agent tooling for data engineering workflows.","https:\u002F\u002Fgithub.com\u002Fastronomer\u002Fagents\u002Ftree\u002FHEAD\u002Fskills\u002Fdeploying-java-sdk-bundles","---\nname: deploying-java-sdk-bundles\ndescription: Builds and deploys compiled Airflow Java SDK bundles so workers can run them. Use when the user wants to package a JVM task bundle into a JAR, asks about the `org.apache.airflow.sdk` Gradle plugin, `.\u002Fgradlew bundle`, the Maven shade\u002FBOM setup, fat vs thin JARs, the logging integration artifacts (JPL, SLF4J, Log4j 2, JUL), preview\u002Fsnapshot builds, or getting the JAR onto an Airflow worker (Docker, Kubernetes, or Astro). For the task code see authoring-java-sdk-tasks; for the Airflow coordinator settings see configuring-airflow-language-sdks.\n---\n\n# Deploying Java SDK Bundles\n\nA Java SDK deployment has one artifact: a **bundle** — your compiled task classes plus the SDK, packaged as a JAR (or a thin JAR alongside its dependency JARs). You build it with Gradle or Maven, then place it in a directory that the `JavaCoordinator` scans (`jars_root`) on every worker. This skill is platform-neutral; it shows the build once, then both an open-source and an Astro deployment path.\n\n> **Experimental.** The Java SDK is in preview. Artifact versions below are shown as `${version}`; while the SDK is pre-release you may need to build the artifacts into your local Maven repository yourself (see the preview builds section).\n\n> **Order of operations:** build the bundle (this skill) → place it where `jars_root` points → configure the coordinator (**configuring-airflow-language-sdks**). The task code itself is **authoring-java-sdk-tasks**.\n\n---\n\n## Build with Gradle (recommended)\n\nApply the SDK's Gradle plugin and declare dependencies in `build.gradle`:\n\n```groovy\nplugins {\n    id(\"org.apache.airflow.sdk\") version \"${version}\"\n}\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    annotationProcessor(\"org.apache.airflow:airflow-sdk-processor:${version}\")  \u002F\u002F annotation API only\n    implementation(\"org.apache.airflow:airflow-sdk:${version}\")\n    \u002F\u002F Optional logging integration, e.g.:\n    \u002F\u002F implementation(\"org.apache.airflow:airflow-sdk-jpl:${version}\")\n}\n\nairflowBundle {\n    mainClass = \"com.example.Main\"   \u002F\u002F your BundleBuilder entry point\n    \u002F\u002F fatJar = false                \u002F\u002F opt out of the single-JAR build (see below)\n}\n```\n\nBuild it:\n\n```bash\n.\u002Fgradlew bundle\n```\n\nThe `build\u002Fbundle\u002F` directory then holds all required JAR(s). Notes:\n\n- The `annotationProcessor` line is needed **only if you use the annotation-based API**. The interface-based API doesn't need it.\n- By default the plugin produces a **fat JAR** (via the Shadow plugin) — one self-contained file, which avoids cross-project dependency clashes. Set `fatJar = false` in `airflowBundle` for thin JARs; you then deploy every dependency JAR too.\n- The Gradle plugin validates that `mainClass` exists at build time (`verifyBundleMainClass`).\n\n---\n\n## Build with Maven\n\nImport the BOM so artifact versions and the supervisor schema version are managed in one place:\n\n```xml\n\u003CdependencyManagement>\n  \u003Cdependencies>\n    \u003Cdependency>\n      \u003CgroupId>org.apache.airflow\u003C\u002FgroupId>\n      \u003CartifactId>airflow-sdk-bom\u003C\u002FartifactId>\n      \u003Cversion>${version}\u003C\u002Fversion>\n      \u003Ctype>pom\u003C\u002Ftype>\n      \u003Cscope>import\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n  \u003C\u002Fdependencies>\n\u003C\u002FdependencyManagement>\n\n\u003Cdependencies>\n  \u003Cdependency>\n    \u003CgroupId>org.apache.airflow\u003C\u002FgroupId>\n    \u003CartifactId>airflow-sdk\u003C\u002FartifactId>   \u003C!-- version from the BOM -->\n  \u003C\u002Fdependency>\n\u003C\u002Fdependencies>\n```\n\nWire the annotation processor through `maven-compiler-plugin` (annotation API only) so it stays off the runtime classpath. Then pick a packaging option:\n\n- **Fat JAR (recommended):** use `maven-shade-plugin`. In its `ManifestResourceTransformer`, set `\u003CmainClass>` to your `BundleBuilder` and add the manifest entry `Airflow-Supervisor-Schema-Version` resolved from the BOM property `${airflow.supervisor.schema.version}` (don't hard-code it). `mvn package` writes the JAR to `target\u002F`.\n- **Thin JAR:** use `maven-jar-plugin` to set `Main-Class` and `maven-dependency-plugin` (`copy-dependencies`) to collect runtime JARs into `target\u002Fbundle\u002F`. Here `Airflow-Supervisor-Schema-Version` is not needed — Airflow reads it from the `airflow-sdk` JAR on the classpath.\n\nUnlike Gradle, Maven does **not** validate `mainClass` at build time; a wrong value only fails at runtime.\n\n---\n\n## Logging integration\n\nFor task log records to reach Airflow's log store (and the task log view in the UI), the bundle must include **exactly one** SDK logging artifact per logging facade you use. Versions are managed by `airflow-sdk-bom`; Maven users apply the same artifact IDs.\n\n**Choosing a facade.** For a greenfield project, prefer JPL (`System.Logger`) — it is built into the JDK, so your tasks need no extra logging API. Pick another facade only when the libraries you integrate with already log through it, so their records reach Airflow too. Preference order: JPL > SLF4J = Log4j 2 > JUL; treat JUL as legacy integration only, not a choice for new code.\n\n| Facade | Artifact | Setup beyond the dependency |\n|--------|----------|-----------------------------|\n| `System.Logger` (JPL) | `airflow-sdk-jpl` | None — the provider is discovered via `ServiceLoader`. |\n| SLF4J 2.x | `airflow-sdk-slf4j` | None — the binding is discovered automatically (pulls in `slf4j-api` for you). |\n| Log4j 2 | `airflow-sdk-log4j2` | `log4j-core` on the runtime classpath + `AirflowAppender` declared in `log4j2.xml` (below). |\n| `java.util.logging` (JUL) | `airflow-sdk-jul` | Call `AirflowJulHandler.setup()` in `main()` (below), or use a `logging.properties` file (see **configuring-airflow-language-sdks**). |\n\n**Log4j 2** — `log4j-core` hosts the plugin loader that discovers the appender (`log4j-api` comes in transitively):\n\n```groovy\nimplementation(\"org.apache.airflow:airflow-sdk-log4j2:${version}\")\nruntimeOnly(\"org.apache.logging.log4j:log4j-core:${log4jVersion}\")\n```\n\n```xml\n\u003CConfiguration>\n  \u003CAppenders>\n    \u003CAirflowAppender name=\"Airflow\"\u002F>\n  \u003C\u002FAppenders>\n  \u003CLoggers>\n    \u003CRoot level=\"info\">\n      \u003CAppenderRef ref=\"Airflow\"\u002F>\n    \u003C\u002FRoot>\n  \u003C\u002FLoggers>\n\u003C\u002FConfiguration>\n```\n\n**JUL** — call `AirflowJulHandler.setup()` before any task runs. It clears the root logger's existing handlers (the default `ConsoleHandler` writes to stderr, which Airflow would otherwise capture as `task.stderr` at ERROR level, duplicating each record):\n\n```java\npublic static void main(String[] args) {\n    AirflowJulHandler.setup();\n    Server.create(args).serve(new MyBundle().build());\n}\n```\n\n**Don't double up providers.** A second `System.LoggerFinder` implementation alongside `airflow-sdk-jpl`, or a second SLF4J binding (`logback-classic`, `slf4j-simple`) alongside `airflow-sdk-slf4j`, makes provider selection unpredictable.\n\n---\n\n## Preview builds (before a stable release)\n\n**Skip this section if you depend on a stable release.** Once you pin a released version (e.g. `1.0.0`) published to Maven Central, the `mavenCentral()` repository in the build snippets above is enough.\n\nWhile the SDK is pre-release, the documented path is to build the artifacts and the Gradle plugin from the Airflow repo into your local Maven repository:\n\n```bash\n# in apache\u002Fairflow's java-sdk\u002F directory\n.\u002Fgradlew publishToMavenLocal -PskipSigning=true\n```\n\nThen add `mavenLocal()` in your project, in **both** `pluginManagement` (in `settings.gradle`) and project `repositories` (in `build.gradle`) — this is how the SDK's own example project resolves it.\n\nOnce `-SNAPSHOT` artifacts are published to Apache's snapshot Nexus, that repository can stand in for the local build (same two places):\n\n```groovy\nmaven {\n    name = \"apacheSnapshots\"\n    url = \"https:\u002F\u002Frepository.apache.org\u002Fcontent\u002Frepositories\u002Fsnapshots\u002F\"\n    mavenContent { snapshotsOnly() }\n}\n```\n\nSnapshots move; force a refresh with `.\u002Fgradlew bundle --refresh-dependencies`. (For Maven, add the same repository to `\u003Crepositories>` and `\u003CpluginRepositories>`.)\n\n---\n\n## Place the bundle where the coordinator scans\n\nThe coordinator scans `jars_root` recursively and builds the classpath automatically, so you copy the whole output directory:\n\n```bash\ncp build\u002Fbundle\u002F* \u002Fopt\u002Fairflow\u002Fjars\u002F    # \u002Fopt\u002Fairflow\u002Fjars == jars_root\n```\n\nThe worker also needs a **JRE 17+**. Wiring the coordinator to this directory is covered in **configuring-airflow-language-sdks**.\n\n---\n\n## Deployment paths\n\nAstronomer tooling is **not required** — the SDK runs on any Airflow with the Task SDK. Choose the path that matches the user's setup.\n\n### Open-source (Docker \u002F Kubernetes)\n\nBake the JRE and the bundle into your Airflow image, or mount them:\n\n```dockerfile\nFROM apache\u002Fairflow:3          # pin a specific 3.x in production\nUSER root\nRUN apt-get update \\\n    && apt-get install -y --no-install-recommends default-jre-headless \\\n    && apt-get clean && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\nRUN mkdir -p \u002Fopt\u002Fairflow\u002Fjars\nCOPY build\u002Fbundle\u002F \u002Fopt\u002Fairflow\u002Fjars\u002F\nUSER airflow\n```\n\nOn Kubernetes (Helm chart), bake the JAR into a custom image as above, or mount it via a shared volume; set the `[sdk]` config through environment variables on the worker\u002Fscheduler. See **deploying-airflow** for the broader Docker Compose and Helm workflow.\n\n### Astro (one option, not required)\n\nIf the user is on Astronomer's Astro CLI, the same idea maps onto an Astro project:\n\n1. Build the bundle, then stage it in the project: `mkdir -p include\u002Fjars && cp ..\u002Fjava-bundle\u002Fbuild\u002Fbundle\u002F*.jar include\u002Fjars\u002F`.\n2. Edit the project `Dockerfile` to install a JRE and copy the JARs to the coordinator's directory:\n\n   ```dockerfile\n   FROM quay.io\u002Fastronomer\u002Fastro-runtime:\u003Cversion>\n   USER root\n   RUN apt-get update \\\n       && apt-get install -y --no-install-recommends default-jre-headless \\\n       && apt-get clean && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n   RUN mkdir -p \u002Fopt\u002Fairflow\u002Fjars\n   COPY include\u002Fjars\u002F \u002Fopt\u002Fairflow\u002Fjars\u002F\n   USER airflow\n   ```\n\n3. Put the coordinator config in the project's `.env` (loaded automatically) — see **configuring-airflow-language-sdks** for the `AIRFLOW__SDK__*` values.\n4. `astro dev start` (or `astro dev restart` after changes) builds the image and starts Airflow locally; deploy with `astro deploy` as usual.\n\n> Don't pin Astro Runtime \u002F Airflow versions from memory — read the generated `Dockerfile` or check current docs. While the SDK and Airflow 3.3 are in preview, a beta\u002Fdev Astro Runtime image may be required.\n\n---\n\n## Deploy checklist\n\n- Bundle built (`.\u002Fgradlew bundle` or `mvn package`) and `mainClass` points at your `BundleBuilder`.\n- `annotationProcessor` present **iff** you use the annotation API.\n- JAR(s) copied into the worker's `jars_root` directory; with thin JARs, dependency JARs too.\n- JRE 17+ available on the worker.\n- Coordinator + `queue_to_coordinator` configured (**configuring-airflow-language-sdks**).\n- If multiple executable JARs exist under `jars_root`, set `main_class` explicitly.\n\n---\n\n## Related Skills\n\n- **authoring-java-sdk-tasks**: Write the Java task code and the matching Python stubs.\n- **configuring-airflow-language-sdks**: Register the coordinator and route the queue.\n- **deploying-airflow**: General Airflow deployment (Astro, Docker Compose, Kubernetes).\n- **setting-up-astro-project**: Initialize and configure an Astro project.\n",{"data":56,"body":57},{"name":4,"description":6},{"type":58,"children":59},"root",[60,68,99,121,155,159,166,179,364,369,393,406,479,482,488,493,645,658,798,817,820,826,846,864,1061,1085,1108,1195,1228,1267,1315,1318,1324,1350,1355,1388,1438,1451,1497,1525,1528,1534,1546,1582,1599,1602,1608,1620,1627,1632,1705,1725,1731,1736,1884,1899,1902,1908,2015,2018,2024,2064],{"type":61,"tag":62,"props":63,"children":64},"element","h1",{"id":4},[65],{"type":66,"value":67},"text","Deploying Java SDK Bundles",{"type":61,"tag":69,"props":70,"children":71},"p",{},[72,74,80,82,89,91,97],{"type":66,"value":73},"A Java SDK deployment has one artifact: a ",{"type":61,"tag":75,"props":76,"children":77},"strong",{},[78],{"type":66,"value":79},"bundle",{"type":66,"value":81}," — your compiled task classes plus the SDK, packaged as a JAR (or a thin JAR alongside its dependency JARs). You build it with Gradle or Maven, then place it in a directory that the ",{"type":61,"tag":83,"props":84,"children":86},"code",{"className":85},[],[87],{"type":66,"value":88},"JavaCoordinator",{"type":66,"value":90}," scans (",{"type":61,"tag":83,"props":92,"children":94},{"className":93},[],[95],{"type":66,"value":96},"jars_root",{"type":66,"value":98},") on every worker. This skill is platform-neutral; it shows the build once, then both an open-source and an Astro deployment path.",{"type":61,"tag":100,"props":101,"children":102},"blockquote",{},[103],{"type":61,"tag":69,"props":104,"children":105},{},[106,111,113,119],{"type":61,"tag":75,"props":107,"children":108},{},[109],{"type":66,"value":110},"Experimental.",{"type":66,"value":112}," The Java SDK is in preview. Artifact versions below are shown as ",{"type":61,"tag":83,"props":114,"children":116},{"className":115},[],[117],{"type":66,"value":118},"${version}",{"type":66,"value":120},"; while the SDK is pre-release you may need to build the artifacts into your local Maven repository yourself (see the preview builds section).",{"type":61,"tag":100,"props":122,"children":123},{},[124],{"type":61,"tag":69,"props":125,"children":126},{},[127,132,134,139,141,146,148,153],{"type":61,"tag":75,"props":128,"children":129},{},[130],{"type":66,"value":131},"Order of operations:",{"type":66,"value":133}," build the bundle (this skill) → place it where ",{"type":61,"tag":83,"props":135,"children":137},{"className":136},[],[138],{"type":66,"value":96},{"type":66,"value":140}," points → configure the coordinator (",{"type":61,"tag":75,"props":142,"children":143},{},[144],{"type":66,"value":145},"configuring-airflow-language-sdks",{"type":66,"value":147},"). The task code itself is ",{"type":61,"tag":75,"props":149,"children":150},{},[151],{"type":66,"value":152},"authoring-java-sdk-tasks",{"type":66,"value":154},".",{"type":61,"tag":156,"props":157,"children":158},"hr",{},[],{"type":61,"tag":160,"props":161,"children":163},"h2",{"id":162},"build-with-gradle-recommended",[164],{"type":66,"value":165},"Build with Gradle (recommended)",{"type":61,"tag":69,"props":167,"children":168},{},[169,171,177],{"type":66,"value":170},"Apply the SDK's Gradle plugin and declare dependencies in ",{"type":61,"tag":83,"props":172,"children":174},{"className":173},[],[175],{"type":66,"value":176},"build.gradle",{"type":66,"value":178},":",{"type":61,"tag":180,"props":181,"children":186},"pre",{"className":182,"code":183,"language":184,"meta":185,"style":185},"language-groovy shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","plugins {\n    id(\"org.apache.airflow.sdk\") version \"${version}\"\n}\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    annotationProcessor(\"org.apache.airflow:airflow-sdk-processor:${version}\")  \u002F\u002F annotation API only\n    implementation(\"org.apache.airflow:airflow-sdk:${version}\")\n    \u002F\u002F Optional logging integration, e.g.:\n    \u002F\u002F implementation(\"org.apache.airflow:airflow-sdk-jpl:${version}\")\n}\n\nairflowBundle {\n    mainClass = \"com.example.Main\"   \u002F\u002F your BundleBuilder entry point\n    \u002F\u002F fatJar = false                \u002F\u002F opt out of the single-JAR build (see below)\n}\n","groovy","",[187],{"type":61,"tag":83,"props":188,"children":189},{"__ignoreMap":185},[190,201,210,219,229,238,247,255,263,272,281,290,299,308,316,324,333,342,356],{"type":61,"tag":191,"props":192,"children":195},"span",{"class":193,"line":194},"line",1,[196],{"type":61,"tag":191,"props":197,"children":198},{},[199],{"type":66,"value":200},"plugins {\n",{"type":61,"tag":191,"props":202,"children":204},{"class":193,"line":203},2,[205],{"type":61,"tag":191,"props":206,"children":207},{},[208],{"type":66,"value":209},"    id(\"org.apache.airflow.sdk\") version \"${version}\"\n",{"type":61,"tag":191,"props":211,"children":213},{"class":193,"line":212},3,[214],{"type":61,"tag":191,"props":215,"children":216},{},[217],{"type":66,"value":218},"}\n",{"type":61,"tag":191,"props":220,"children":222},{"class":193,"line":221},4,[223],{"type":61,"tag":191,"props":224,"children":226},{"emptyLinePlaceholder":225},true,[227],{"type":66,"value":228},"\n",{"type":61,"tag":191,"props":230,"children":232},{"class":193,"line":231},5,[233],{"type":61,"tag":191,"props":234,"children":235},{},[236],{"type":66,"value":237},"repositories {\n",{"type":61,"tag":191,"props":239,"children":241},{"class":193,"line":240},6,[242],{"type":61,"tag":191,"props":243,"children":244},{},[245],{"type":66,"value":246},"    mavenCentral()\n",{"type":61,"tag":191,"props":248,"children":250},{"class":193,"line":249},7,[251],{"type":61,"tag":191,"props":252,"children":253},{},[254],{"type":66,"value":218},{"type":61,"tag":191,"props":256,"children":258},{"class":193,"line":257},8,[259],{"type":61,"tag":191,"props":260,"children":261},{"emptyLinePlaceholder":225},[262],{"type":66,"value":228},{"type":61,"tag":191,"props":264,"children":266},{"class":193,"line":265},9,[267],{"type":61,"tag":191,"props":268,"children":269},{},[270],{"type":66,"value":271},"dependencies {\n",{"type":61,"tag":191,"props":273,"children":275},{"class":193,"line":274},10,[276],{"type":61,"tag":191,"props":277,"children":278},{},[279],{"type":66,"value":280},"    annotationProcessor(\"org.apache.airflow:airflow-sdk-processor:${version}\")  \u002F\u002F annotation API only\n",{"type":61,"tag":191,"props":282,"children":284},{"class":193,"line":283},11,[285],{"type":61,"tag":191,"props":286,"children":287},{},[288],{"type":66,"value":289},"    implementation(\"org.apache.airflow:airflow-sdk:${version}\")\n",{"type":61,"tag":191,"props":291,"children":293},{"class":193,"line":292},12,[294],{"type":61,"tag":191,"props":295,"children":296},{},[297],{"type":66,"value":298},"    \u002F\u002F Optional logging integration, e.g.:\n",{"type":61,"tag":191,"props":300,"children":302},{"class":193,"line":301},13,[303],{"type":61,"tag":191,"props":304,"children":305},{},[306],{"type":66,"value":307},"    \u002F\u002F implementation(\"org.apache.airflow:airflow-sdk-jpl:${version}\")\n",{"type":61,"tag":191,"props":309,"children":311},{"class":193,"line":310},14,[312],{"type":61,"tag":191,"props":313,"children":314},{},[315],{"type":66,"value":218},{"type":61,"tag":191,"props":317,"children":319},{"class":193,"line":318},15,[320],{"type":61,"tag":191,"props":321,"children":322},{"emptyLinePlaceholder":225},[323],{"type":66,"value":228},{"type":61,"tag":191,"props":325,"children":327},{"class":193,"line":326},16,[328],{"type":61,"tag":191,"props":329,"children":330},{},[331],{"type":66,"value":332},"airflowBundle {\n",{"type":61,"tag":191,"props":334,"children":336},{"class":193,"line":335},17,[337],{"type":61,"tag":191,"props":338,"children":339},{},[340],{"type":66,"value":341},"    mainClass = \"com.example.Main\"   \u002F\u002F your BundleBuilder entry point\n",{"type":61,"tag":191,"props":343,"children":345},{"class":193,"line":344},18,[346,351],{"type":61,"tag":191,"props":347,"children":348},{},[349],{"type":66,"value":350},"    \u002F\u002F fatJar = false",{"type":61,"tag":191,"props":352,"children":353},{},[354],{"type":66,"value":355},"                \u002F\u002F opt out of the single-JAR build (see below)\n",{"type":61,"tag":191,"props":357,"children":359},{"class":193,"line":358},19,[360],{"type":61,"tag":191,"props":361,"children":362},{},[363],{"type":66,"value":218},{"type":61,"tag":69,"props":365,"children":366},{},[367],{"type":66,"value":368},"Build it:",{"type":61,"tag":180,"props":370,"children":374},{"className":371,"code":372,"language":373,"meta":185,"style":185},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight",".\u002Fgradlew bundle\n","bash",[375],{"type":61,"tag":83,"props":376,"children":377},{"__ignoreMap":185},[378],{"type":61,"tag":191,"props":379,"children":380},{"class":193,"line":194},[381,387],{"type":61,"tag":191,"props":382,"children":384},{"style":383},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[385],{"type":66,"value":386},".\u002Fgradlew",{"type":61,"tag":191,"props":388,"children":390},{"style":389},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[391],{"type":66,"value":392}," bundle\n",{"type":61,"tag":69,"props":394,"children":395},{},[396,398,404],{"type":66,"value":397},"The ",{"type":61,"tag":83,"props":399,"children":401},{"className":400},[],[402],{"type":66,"value":403},"build\u002Fbundle\u002F",{"type":66,"value":405}," directory then holds all required JAR(s). Notes:",{"type":61,"tag":407,"props":408,"children":409},"ul",{},[410,430,458],{"type":61,"tag":411,"props":412,"children":413},"li",{},[414,415,421,423,428],{"type":66,"value":397},{"type":61,"tag":83,"props":416,"children":418},{"className":417},[],[419],{"type":66,"value":420},"annotationProcessor",{"type":66,"value":422}," line is needed ",{"type":61,"tag":75,"props":424,"children":425},{},[426],{"type":66,"value":427},"only if you use the annotation-based API",{"type":66,"value":429},". The interface-based API doesn't need it.",{"type":61,"tag":411,"props":431,"children":432},{},[433,435,440,442,448,450,456],{"type":66,"value":434},"By default the plugin produces a ",{"type":61,"tag":75,"props":436,"children":437},{},[438],{"type":66,"value":439},"fat JAR",{"type":66,"value":441}," (via the Shadow plugin) — one self-contained file, which avoids cross-project dependency clashes. Set ",{"type":61,"tag":83,"props":443,"children":445},{"className":444},[],[446],{"type":66,"value":447},"fatJar = false",{"type":66,"value":449}," in ",{"type":61,"tag":83,"props":451,"children":453},{"className":452},[],[454],{"type":66,"value":455},"airflowBundle",{"type":66,"value":457}," for thin JARs; you then deploy every dependency JAR too.",{"type":61,"tag":411,"props":459,"children":460},{},[461,463,469,471,477],{"type":66,"value":462},"The Gradle plugin validates that ",{"type":61,"tag":83,"props":464,"children":466},{"className":465},[],[467],{"type":66,"value":468},"mainClass",{"type":66,"value":470}," exists at build time (",{"type":61,"tag":83,"props":472,"children":474},{"className":473},[],[475],{"type":66,"value":476},"verifyBundleMainClass",{"type":66,"value":478},").",{"type":61,"tag":156,"props":480,"children":481},{},[],{"type":61,"tag":160,"props":483,"children":485},{"id":484},"build-with-maven",[486],{"type":66,"value":487},"Build with Maven",{"type":61,"tag":69,"props":489,"children":490},{},[491],{"type":66,"value":492},"Import the BOM so artifact versions and the supervisor schema version are managed in one place:",{"type":61,"tag":180,"props":494,"children":498},{"className":495,"code":496,"language":497,"meta":185,"style":185},"language-xml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003CdependencyManagement>\n  \u003Cdependencies>\n    \u003Cdependency>\n      \u003CgroupId>org.apache.airflow\u003C\u002FgroupId>\n      \u003CartifactId>airflow-sdk-bom\u003C\u002FartifactId>\n      \u003Cversion>${version}\u003C\u002Fversion>\n      \u003Ctype>pom\u003C\u002Ftype>\n      \u003Cscope>import\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n  \u003C\u002Fdependencies>\n\u003C\u002FdependencyManagement>\n\n\u003Cdependencies>\n  \u003Cdependency>\n    \u003CgroupId>org.apache.airflow\u003C\u002FgroupId>\n    \u003CartifactId>airflow-sdk\u003C\u002FartifactId>   \u003C!-- version from the BOM -->\n  \u003C\u002Fdependency>\n\u003C\u002Fdependencies>\n","xml",[499],{"type":61,"tag":83,"props":500,"children":501},{"__ignoreMap":185},[502,510,518,526,534,542,550,558,566,574,582,590,597,605,613,621,629,637],{"type":61,"tag":191,"props":503,"children":504},{"class":193,"line":194},[505],{"type":61,"tag":191,"props":506,"children":507},{},[508],{"type":66,"value":509},"\u003CdependencyManagement>\n",{"type":61,"tag":191,"props":511,"children":512},{"class":193,"line":203},[513],{"type":61,"tag":191,"props":514,"children":515},{},[516],{"type":66,"value":517},"  \u003Cdependencies>\n",{"type":61,"tag":191,"props":519,"children":520},{"class":193,"line":212},[521],{"type":61,"tag":191,"props":522,"children":523},{},[524],{"type":66,"value":525},"    \u003Cdependency>\n",{"type":61,"tag":191,"props":527,"children":528},{"class":193,"line":221},[529],{"type":61,"tag":191,"props":530,"children":531},{},[532],{"type":66,"value":533},"      \u003CgroupId>org.apache.airflow\u003C\u002FgroupId>\n",{"type":61,"tag":191,"props":535,"children":536},{"class":193,"line":231},[537],{"type":61,"tag":191,"props":538,"children":539},{},[540],{"type":66,"value":541},"      \u003CartifactId>airflow-sdk-bom\u003C\u002FartifactId>\n",{"type":61,"tag":191,"props":543,"children":544},{"class":193,"line":240},[545],{"type":61,"tag":191,"props":546,"children":547},{},[548],{"type":66,"value":549},"      \u003Cversion>${version}\u003C\u002Fversion>\n",{"type":61,"tag":191,"props":551,"children":552},{"class":193,"line":249},[553],{"type":61,"tag":191,"props":554,"children":555},{},[556],{"type":66,"value":557},"      \u003Ctype>pom\u003C\u002Ftype>\n",{"type":61,"tag":191,"props":559,"children":560},{"class":193,"line":257},[561],{"type":61,"tag":191,"props":562,"children":563},{},[564],{"type":66,"value":565},"      \u003Cscope>import\u003C\u002Fscope>\n",{"type":61,"tag":191,"props":567,"children":568},{"class":193,"line":265},[569],{"type":61,"tag":191,"props":570,"children":571},{},[572],{"type":66,"value":573},"    \u003C\u002Fdependency>\n",{"type":61,"tag":191,"props":575,"children":576},{"class":193,"line":274},[577],{"type":61,"tag":191,"props":578,"children":579},{},[580],{"type":66,"value":581},"  \u003C\u002Fdependencies>\n",{"type":61,"tag":191,"props":583,"children":584},{"class":193,"line":283},[585],{"type":61,"tag":191,"props":586,"children":587},{},[588],{"type":66,"value":589},"\u003C\u002FdependencyManagement>\n",{"type":61,"tag":191,"props":591,"children":592},{"class":193,"line":292},[593],{"type":61,"tag":191,"props":594,"children":595},{"emptyLinePlaceholder":225},[596],{"type":66,"value":228},{"type":61,"tag":191,"props":598,"children":599},{"class":193,"line":301},[600],{"type":61,"tag":191,"props":601,"children":602},{},[603],{"type":66,"value":604},"\u003Cdependencies>\n",{"type":61,"tag":191,"props":606,"children":607},{"class":193,"line":310},[608],{"type":61,"tag":191,"props":609,"children":610},{},[611],{"type":66,"value":612},"  \u003Cdependency>\n",{"type":61,"tag":191,"props":614,"children":615},{"class":193,"line":318},[616],{"type":61,"tag":191,"props":617,"children":618},{},[619],{"type":66,"value":620},"    \u003CgroupId>org.apache.airflow\u003C\u002FgroupId>\n",{"type":61,"tag":191,"props":622,"children":623},{"class":193,"line":326},[624],{"type":61,"tag":191,"props":625,"children":626},{},[627],{"type":66,"value":628},"    \u003CartifactId>airflow-sdk\u003C\u002FartifactId>   \u003C!-- version from the BOM -->\n",{"type":61,"tag":191,"props":630,"children":631},{"class":193,"line":335},[632],{"type":61,"tag":191,"props":633,"children":634},{},[635],{"type":66,"value":636},"  \u003C\u002Fdependency>\n",{"type":61,"tag":191,"props":638,"children":639},{"class":193,"line":344},[640],{"type":61,"tag":191,"props":641,"children":642},{},[643],{"type":66,"value":644},"\u003C\u002Fdependencies>\n",{"type":61,"tag":69,"props":646,"children":647},{},[648,650,656],{"type":66,"value":649},"Wire the annotation processor through ",{"type":61,"tag":83,"props":651,"children":653},{"className":652},[],[654],{"type":66,"value":655},"maven-compiler-plugin",{"type":66,"value":657}," (annotation API only) so it stays off the runtime classpath. Then pick a packaging option:",{"type":61,"tag":407,"props":659,"children":660},{},[661,734],{"type":61,"tag":411,"props":662,"children":663},{},[664,669,671,677,679,685,687,693,695,701,703,709,711,717,719,725,727,733],{"type":61,"tag":75,"props":665,"children":666},{},[667],{"type":66,"value":668},"Fat JAR (recommended):",{"type":66,"value":670}," use ",{"type":61,"tag":83,"props":672,"children":674},{"className":673},[],[675],{"type":66,"value":676},"maven-shade-plugin",{"type":66,"value":678},". In its ",{"type":61,"tag":83,"props":680,"children":682},{"className":681},[],[683],{"type":66,"value":684},"ManifestResourceTransformer",{"type":66,"value":686},", set ",{"type":61,"tag":83,"props":688,"children":690},{"className":689},[],[691],{"type":66,"value":692},"\u003CmainClass>",{"type":66,"value":694}," to your ",{"type":61,"tag":83,"props":696,"children":698},{"className":697},[],[699],{"type":66,"value":700},"BundleBuilder",{"type":66,"value":702}," and add the manifest entry ",{"type":61,"tag":83,"props":704,"children":706},{"className":705},[],[707],{"type":66,"value":708},"Airflow-Supervisor-Schema-Version",{"type":66,"value":710}," resolved from the BOM property ",{"type":61,"tag":83,"props":712,"children":714},{"className":713},[],[715],{"type":66,"value":716},"${airflow.supervisor.schema.version}",{"type":66,"value":718}," (don't hard-code it). ",{"type":61,"tag":83,"props":720,"children":722},{"className":721},[],[723],{"type":66,"value":724},"mvn package",{"type":66,"value":726}," writes the JAR to ",{"type":61,"tag":83,"props":728,"children":730},{"className":729},[],[731],{"type":66,"value":732},"target\u002F",{"type":66,"value":154},{"type":61,"tag":411,"props":735,"children":736},{},[737,742,743,749,751,757,759,765,767,773,775,781,783,788,790,796],{"type":61,"tag":75,"props":738,"children":739},{},[740],{"type":66,"value":741},"Thin JAR:",{"type":66,"value":670},{"type":61,"tag":83,"props":744,"children":746},{"className":745},[],[747],{"type":66,"value":748},"maven-jar-plugin",{"type":66,"value":750}," to set ",{"type":61,"tag":83,"props":752,"children":754},{"className":753},[],[755],{"type":66,"value":756},"Main-Class",{"type":66,"value":758}," and ",{"type":61,"tag":83,"props":760,"children":762},{"className":761},[],[763],{"type":66,"value":764},"maven-dependency-plugin",{"type":66,"value":766}," (",{"type":61,"tag":83,"props":768,"children":770},{"className":769},[],[771],{"type":66,"value":772},"copy-dependencies",{"type":66,"value":774},") to collect runtime JARs into ",{"type":61,"tag":83,"props":776,"children":778},{"className":777},[],[779],{"type":66,"value":780},"target\u002Fbundle\u002F",{"type":66,"value":782},". Here ",{"type":61,"tag":83,"props":784,"children":786},{"className":785},[],[787],{"type":66,"value":708},{"type":66,"value":789}," is not needed — Airflow reads it from the ",{"type":61,"tag":83,"props":791,"children":793},{"className":792},[],[794],{"type":66,"value":795},"airflow-sdk",{"type":66,"value":797}," JAR on the classpath.",{"type":61,"tag":69,"props":799,"children":800},{},[801,803,808,810,815],{"type":66,"value":802},"Unlike Gradle, Maven does ",{"type":61,"tag":75,"props":804,"children":805},{},[806],{"type":66,"value":807},"not",{"type":66,"value":809}," validate ",{"type":61,"tag":83,"props":811,"children":813},{"className":812},[],[814],{"type":66,"value":468},{"type":66,"value":816}," at build time; a wrong value only fails at runtime.",{"type":61,"tag":156,"props":818,"children":819},{},[],{"type":61,"tag":160,"props":821,"children":823},{"id":822},"logging-integration",[824],{"type":66,"value":825},"Logging integration",{"type":61,"tag":69,"props":827,"children":828},{},[829,831,836,838,844],{"type":66,"value":830},"For task log records to reach Airflow's log store (and the task log view in the UI), the bundle must include ",{"type":61,"tag":75,"props":832,"children":833},{},[834],{"type":66,"value":835},"exactly one",{"type":66,"value":837}," SDK logging artifact per logging facade you use. Versions are managed by ",{"type":61,"tag":83,"props":839,"children":841},{"className":840},[],[842],{"type":66,"value":843},"airflow-sdk-bom",{"type":66,"value":845},"; Maven users apply the same artifact IDs.",{"type":61,"tag":69,"props":847,"children":848},{},[849,854,856,862],{"type":61,"tag":75,"props":850,"children":851},{},[852],{"type":66,"value":853},"Choosing a facade.",{"type":66,"value":855}," For a greenfield project, prefer JPL (",{"type":61,"tag":83,"props":857,"children":859},{"className":858},[],[860],{"type":66,"value":861},"System.Logger",{"type":66,"value":863},") — it is built into the JDK, so your tasks need no extra logging API. Pick another facade only when the libraries you integrate with already log through it, so their records reach Airflow too. Preference order: JPL > SLF4J = Log4j 2 > JUL; treat JUL as legacy integration only, not a choice for new code.",{"type":61,"tag":865,"props":866,"children":867},"table",{},[868,892],{"type":61,"tag":869,"props":870,"children":871},"thead",{},[872],{"type":61,"tag":873,"props":874,"children":875},"tr",{},[876,882,887],{"type":61,"tag":877,"props":878,"children":879},"th",{},[880],{"type":66,"value":881},"Facade",{"type":61,"tag":877,"props":883,"children":884},{},[885],{"type":66,"value":886},"Artifact",{"type":61,"tag":877,"props":888,"children":889},{},[890],{"type":66,"value":891},"Setup beyond the dependency",{"type":61,"tag":893,"props":894,"children":895},"tbody",{},[896,931,961,1005],{"type":61,"tag":873,"props":897,"children":898},{},[899,910,919],{"type":61,"tag":900,"props":901,"children":902},"td",{},[903,908],{"type":61,"tag":83,"props":904,"children":906},{"className":905},[],[907],{"type":66,"value":861},{"type":66,"value":909}," (JPL)",{"type":61,"tag":900,"props":911,"children":912},{},[913],{"type":61,"tag":83,"props":914,"children":916},{"className":915},[],[917],{"type":66,"value":918},"airflow-sdk-jpl",{"type":61,"tag":900,"props":920,"children":921},{},[922,924,930],{"type":66,"value":923},"None — the provider is discovered via ",{"type":61,"tag":83,"props":925,"children":927},{"className":926},[],[928],{"type":66,"value":929},"ServiceLoader",{"type":66,"value":154},{"type":61,"tag":873,"props":932,"children":933},{},[934,939,948],{"type":61,"tag":900,"props":935,"children":936},{},[937],{"type":66,"value":938},"SLF4J 2.x",{"type":61,"tag":900,"props":940,"children":941},{},[942],{"type":61,"tag":83,"props":943,"children":945},{"className":944},[],[946],{"type":66,"value":947},"airflow-sdk-slf4j",{"type":61,"tag":900,"props":949,"children":950},{},[951,953,959],{"type":66,"value":952},"None — the binding is discovered automatically (pulls in ",{"type":61,"tag":83,"props":954,"children":956},{"className":955},[],[957],{"type":66,"value":958},"slf4j-api",{"type":66,"value":960}," for you).",{"type":61,"tag":873,"props":962,"children":963},{},[964,969,978],{"type":61,"tag":900,"props":965,"children":966},{},[967],{"type":66,"value":968},"Log4j 2",{"type":61,"tag":900,"props":970,"children":971},{},[972],{"type":61,"tag":83,"props":973,"children":975},{"className":974},[],[976],{"type":66,"value":977},"airflow-sdk-log4j2",{"type":61,"tag":900,"props":979,"children":980},{},[981,987,989,995,997,1003],{"type":61,"tag":83,"props":982,"children":984},{"className":983},[],[985],{"type":66,"value":986},"log4j-core",{"type":66,"value":988}," on the runtime classpath + ",{"type":61,"tag":83,"props":990,"children":992},{"className":991},[],[993],{"type":66,"value":994},"AirflowAppender",{"type":66,"value":996}," declared in ",{"type":61,"tag":83,"props":998,"children":1000},{"className":999},[],[1001],{"type":66,"value":1002},"log4j2.xml",{"type":66,"value":1004}," (below).",{"type":61,"tag":873,"props":1006,"children":1007},{},[1008,1019,1028],{"type":61,"tag":900,"props":1009,"children":1010},{},[1011,1017],{"type":61,"tag":83,"props":1012,"children":1014},{"className":1013},[],[1015],{"type":66,"value":1016},"java.util.logging",{"type":66,"value":1018}," (JUL)",{"type":61,"tag":900,"props":1020,"children":1021},{},[1022],{"type":61,"tag":83,"props":1023,"children":1025},{"className":1024},[],[1026],{"type":66,"value":1027},"airflow-sdk-jul",{"type":61,"tag":900,"props":1029,"children":1030},{},[1031,1033,1039,1040,1046,1048,1054,1056,1060],{"type":66,"value":1032},"Call ",{"type":61,"tag":83,"props":1034,"children":1036},{"className":1035},[],[1037],{"type":66,"value":1038},"AirflowJulHandler.setup()",{"type":66,"value":449},{"type":61,"tag":83,"props":1041,"children":1043},{"className":1042},[],[1044],{"type":66,"value":1045},"main()",{"type":66,"value":1047}," (below), or use a ",{"type":61,"tag":83,"props":1049,"children":1051},{"className":1050},[],[1052],{"type":66,"value":1053},"logging.properties",{"type":66,"value":1055}," file (see ",{"type":61,"tag":75,"props":1057,"children":1058},{},[1059],{"type":66,"value":145},{"type":66,"value":478},{"type":61,"tag":69,"props":1062,"children":1063},{},[1064,1068,1070,1075,1077,1083],{"type":61,"tag":75,"props":1065,"children":1066},{},[1067],{"type":66,"value":968},{"type":66,"value":1069}," — ",{"type":61,"tag":83,"props":1071,"children":1073},{"className":1072},[],[1074],{"type":66,"value":986},{"type":66,"value":1076}," hosts the plugin loader that discovers the appender (",{"type":61,"tag":83,"props":1078,"children":1080},{"className":1079},[],[1081],{"type":66,"value":1082},"log4j-api",{"type":66,"value":1084}," comes in transitively):",{"type":61,"tag":180,"props":1086,"children":1088},{"className":182,"code":1087,"language":184,"meta":185,"style":185},"implementation(\"org.apache.airflow:airflow-sdk-log4j2:${version}\")\nruntimeOnly(\"org.apache.logging.log4j:log4j-core:${log4jVersion}\")\n",[1089],{"type":61,"tag":83,"props":1090,"children":1091},{"__ignoreMap":185},[1092,1100],{"type":61,"tag":191,"props":1093,"children":1094},{"class":193,"line":194},[1095],{"type":61,"tag":191,"props":1096,"children":1097},{},[1098],{"type":66,"value":1099},"implementation(\"org.apache.airflow:airflow-sdk-log4j2:${version}\")\n",{"type":61,"tag":191,"props":1101,"children":1102},{"class":193,"line":203},[1103],{"type":61,"tag":191,"props":1104,"children":1105},{},[1106],{"type":66,"value":1107},"runtimeOnly(\"org.apache.logging.log4j:log4j-core:${log4jVersion}\")\n",{"type":61,"tag":180,"props":1109,"children":1111},{"className":495,"code":1110,"language":497,"meta":185,"style":185},"\u003CConfiguration>\n  \u003CAppenders>\n    \u003CAirflowAppender name=\"Airflow\"\u002F>\n  \u003C\u002FAppenders>\n  \u003CLoggers>\n    \u003CRoot level=\"info\">\n      \u003CAppenderRef ref=\"Airflow\"\u002F>\n    \u003C\u002FRoot>\n  \u003C\u002FLoggers>\n\u003C\u002FConfiguration>\n",[1112],{"type":61,"tag":83,"props":1113,"children":1114},{"__ignoreMap":185},[1115,1123,1131,1139,1147,1155,1163,1171,1179,1187],{"type":61,"tag":191,"props":1116,"children":1117},{"class":193,"line":194},[1118],{"type":61,"tag":191,"props":1119,"children":1120},{},[1121],{"type":66,"value":1122},"\u003CConfiguration>\n",{"type":61,"tag":191,"props":1124,"children":1125},{"class":193,"line":203},[1126],{"type":61,"tag":191,"props":1127,"children":1128},{},[1129],{"type":66,"value":1130},"  \u003CAppenders>\n",{"type":61,"tag":191,"props":1132,"children":1133},{"class":193,"line":212},[1134],{"type":61,"tag":191,"props":1135,"children":1136},{},[1137],{"type":66,"value":1138},"    \u003CAirflowAppender name=\"Airflow\"\u002F>\n",{"type":61,"tag":191,"props":1140,"children":1141},{"class":193,"line":221},[1142],{"type":61,"tag":191,"props":1143,"children":1144},{},[1145],{"type":66,"value":1146},"  \u003C\u002FAppenders>\n",{"type":61,"tag":191,"props":1148,"children":1149},{"class":193,"line":231},[1150],{"type":61,"tag":191,"props":1151,"children":1152},{},[1153],{"type":66,"value":1154},"  \u003CLoggers>\n",{"type":61,"tag":191,"props":1156,"children":1157},{"class":193,"line":240},[1158],{"type":61,"tag":191,"props":1159,"children":1160},{},[1161],{"type":66,"value":1162},"    \u003CRoot level=\"info\">\n",{"type":61,"tag":191,"props":1164,"children":1165},{"class":193,"line":249},[1166],{"type":61,"tag":191,"props":1167,"children":1168},{},[1169],{"type":66,"value":1170},"      \u003CAppenderRef ref=\"Airflow\"\u002F>\n",{"type":61,"tag":191,"props":1172,"children":1173},{"class":193,"line":257},[1174],{"type":61,"tag":191,"props":1175,"children":1176},{},[1177],{"type":66,"value":1178},"    \u003C\u002FRoot>\n",{"type":61,"tag":191,"props":1180,"children":1181},{"class":193,"line":265},[1182],{"type":61,"tag":191,"props":1183,"children":1184},{},[1185],{"type":66,"value":1186},"  \u003C\u002FLoggers>\n",{"type":61,"tag":191,"props":1188,"children":1189},{"class":193,"line":274},[1190],{"type":61,"tag":191,"props":1191,"children":1192},{},[1193],{"type":66,"value":1194},"\u003C\u002FConfiguration>\n",{"type":61,"tag":69,"props":1196,"children":1197},{},[1198,1203,1205,1210,1212,1218,1220,1226],{"type":61,"tag":75,"props":1199,"children":1200},{},[1201],{"type":66,"value":1202},"JUL",{"type":66,"value":1204}," — call ",{"type":61,"tag":83,"props":1206,"children":1208},{"className":1207},[],[1209],{"type":66,"value":1038},{"type":66,"value":1211}," before any task runs. It clears the root logger's existing handlers (the default ",{"type":61,"tag":83,"props":1213,"children":1215},{"className":1214},[],[1216],{"type":66,"value":1217},"ConsoleHandler",{"type":66,"value":1219}," writes to stderr, which Airflow would otherwise capture as ",{"type":61,"tag":83,"props":1221,"children":1223},{"className":1222},[],[1224],{"type":66,"value":1225},"task.stderr",{"type":66,"value":1227}," at ERROR level, duplicating each record):",{"type":61,"tag":180,"props":1229,"children":1232},{"className":1230,"code":1231,"language":14,"meta":185,"style":185},"language-java shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","public static void main(String[] args) {\n    AirflowJulHandler.setup();\n    Server.create(args).serve(new MyBundle().build());\n}\n",[1233],{"type":61,"tag":83,"props":1234,"children":1235},{"__ignoreMap":185},[1236,1244,1252,1260],{"type":61,"tag":191,"props":1237,"children":1238},{"class":193,"line":194},[1239],{"type":61,"tag":191,"props":1240,"children":1241},{},[1242],{"type":66,"value":1243},"public static void main(String[] args) {\n",{"type":61,"tag":191,"props":1245,"children":1246},{"class":193,"line":203},[1247],{"type":61,"tag":191,"props":1248,"children":1249},{},[1250],{"type":66,"value":1251},"    AirflowJulHandler.setup();\n",{"type":61,"tag":191,"props":1253,"children":1254},{"class":193,"line":212},[1255],{"type":61,"tag":191,"props":1256,"children":1257},{},[1258],{"type":66,"value":1259},"    Server.create(args).serve(new MyBundle().build());\n",{"type":61,"tag":191,"props":1261,"children":1262},{"class":193,"line":221},[1263],{"type":61,"tag":191,"props":1264,"children":1265},{},[1266],{"type":66,"value":218},{"type":61,"tag":69,"props":1268,"children":1269},{},[1270,1275,1277,1283,1285,1290,1292,1298,1300,1306,1308,1313],{"type":61,"tag":75,"props":1271,"children":1272},{},[1273],{"type":66,"value":1274},"Don't double up providers.",{"type":66,"value":1276}," A second ",{"type":61,"tag":83,"props":1278,"children":1280},{"className":1279},[],[1281],{"type":66,"value":1282},"System.LoggerFinder",{"type":66,"value":1284}," implementation alongside ",{"type":61,"tag":83,"props":1286,"children":1288},{"className":1287},[],[1289],{"type":66,"value":918},{"type":66,"value":1291},", or a second SLF4J binding (",{"type":61,"tag":83,"props":1293,"children":1295},{"className":1294},[],[1296],{"type":66,"value":1297},"logback-classic",{"type":66,"value":1299},", ",{"type":61,"tag":83,"props":1301,"children":1303},{"className":1302},[],[1304],{"type":66,"value":1305},"slf4j-simple",{"type":66,"value":1307},") alongside ",{"type":61,"tag":83,"props":1309,"children":1311},{"className":1310},[],[1312],{"type":66,"value":947},{"type":66,"value":1314},", makes provider selection unpredictable.",{"type":61,"tag":156,"props":1316,"children":1317},{},[],{"type":61,"tag":160,"props":1319,"children":1321},{"id":1320},"preview-builds-before-a-stable-release",[1322],{"type":66,"value":1323},"Preview builds (before a stable release)",{"type":61,"tag":69,"props":1325,"children":1326},{},[1327,1332,1334,1340,1342,1348],{"type":61,"tag":75,"props":1328,"children":1329},{},[1330],{"type":66,"value":1331},"Skip this section if you depend on a stable release.",{"type":66,"value":1333}," Once you pin a released version (e.g. ",{"type":61,"tag":83,"props":1335,"children":1337},{"className":1336},[],[1338],{"type":66,"value":1339},"1.0.0",{"type":66,"value":1341},") published to Maven Central, the ",{"type":61,"tag":83,"props":1343,"children":1345},{"className":1344},[],[1346],{"type":66,"value":1347},"mavenCentral()",{"type":66,"value":1349}," repository in the build snippets above is enough.",{"type":61,"tag":69,"props":1351,"children":1352},{},[1353],{"type":66,"value":1354},"While the SDK is pre-release, the documented path is to build the artifacts and the Gradle plugin from the Airflow repo into your local Maven repository:",{"type":61,"tag":180,"props":1356,"children":1358},{"className":371,"code":1357,"language":373,"meta":185,"style":185},"# in apache\u002Fairflow's java-sdk\u002F directory\n.\u002Fgradlew publishToMavenLocal -PskipSigning=true\n",[1359],{"type":61,"tag":83,"props":1360,"children":1361},{"__ignoreMap":185},[1362,1371],{"type":61,"tag":191,"props":1363,"children":1364},{"class":193,"line":194},[1365],{"type":61,"tag":191,"props":1366,"children":1368},{"style":1367},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[1369],{"type":66,"value":1370},"# in apache\u002Fairflow's java-sdk\u002F directory\n",{"type":61,"tag":191,"props":1372,"children":1373},{"class":193,"line":203},[1374,1378,1383],{"type":61,"tag":191,"props":1375,"children":1376},{"style":383},[1377],{"type":66,"value":386},{"type":61,"tag":191,"props":1379,"children":1380},{"style":389},[1381],{"type":66,"value":1382}," publishToMavenLocal",{"type":61,"tag":191,"props":1384,"children":1385},{"style":389},[1386],{"type":66,"value":1387}," -PskipSigning=true\n",{"type":61,"tag":69,"props":1389,"children":1390},{},[1391,1393,1399,1401,1406,1408,1414,1416,1422,1424,1430,1431,1436],{"type":66,"value":1392},"Then add ",{"type":61,"tag":83,"props":1394,"children":1396},{"className":1395},[],[1397],{"type":66,"value":1398},"mavenLocal()",{"type":66,"value":1400}," in your project, in ",{"type":61,"tag":75,"props":1402,"children":1403},{},[1404],{"type":66,"value":1405},"both",{"type":66,"value":1407}," ",{"type":61,"tag":83,"props":1409,"children":1411},{"className":1410},[],[1412],{"type":66,"value":1413},"pluginManagement",{"type":66,"value":1415}," (in ",{"type":61,"tag":83,"props":1417,"children":1419},{"className":1418},[],[1420],{"type":66,"value":1421},"settings.gradle",{"type":66,"value":1423},") and project ",{"type":61,"tag":83,"props":1425,"children":1427},{"className":1426},[],[1428],{"type":66,"value":1429},"repositories",{"type":66,"value":1415},{"type":61,"tag":83,"props":1432,"children":1434},{"className":1433},[],[1435],{"type":66,"value":176},{"type":66,"value":1437},") — this is how the SDK's own example project resolves it.",{"type":61,"tag":69,"props":1439,"children":1440},{},[1441,1443,1449],{"type":66,"value":1442},"Once ",{"type":61,"tag":83,"props":1444,"children":1446},{"className":1445},[],[1447],{"type":66,"value":1448},"-SNAPSHOT",{"type":66,"value":1450}," artifacts are published to Apache's snapshot Nexus, that repository can stand in for the local build (same two places):",{"type":61,"tag":180,"props":1452,"children":1454},{"className":182,"code":1453,"language":184,"meta":185,"style":185},"maven {\n    name = \"apacheSnapshots\"\n    url = \"https:\u002F\u002Frepository.apache.org\u002Fcontent\u002Frepositories\u002Fsnapshots\u002F\"\n    mavenContent { snapshotsOnly() }\n}\n",[1455],{"type":61,"tag":83,"props":1456,"children":1457},{"__ignoreMap":185},[1458,1466,1474,1482,1490],{"type":61,"tag":191,"props":1459,"children":1460},{"class":193,"line":194},[1461],{"type":61,"tag":191,"props":1462,"children":1463},{},[1464],{"type":66,"value":1465},"maven {\n",{"type":61,"tag":191,"props":1467,"children":1468},{"class":193,"line":203},[1469],{"type":61,"tag":191,"props":1470,"children":1471},{},[1472],{"type":66,"value":1473},"    name = \"apacheSnapshots\"\n",{"type":61,"tag":191,"props":1475,"children":1476},{"class":193,"line":212},[1477],{"type":61,"tag":191,"props":1478,"children":1479},{},[1480],{"type":66,"value":1481},"    url = \"https:\u002F\u002Frepository.apache.org\u002Fcontent\u002Frepositories\u002Fsnapshots\u002F\"\n",{"type":61,"tag":191,"props":1483,"children":1484},{"class":193,"line":221},[1485],{"type":61,"tag":191,"props":1486,"children":1487},{},[1488],{"type":66,"value":1489},"    mavenContent { snapshotsOnly() }\n",{"type":61,"tag":191,"props":1491,"children":1492},{"class":193,"line":231},[1493],{"type":61,"tag":191,"props":1494,"children":1495},{},[1496],{"type":66,"value":218},{"type":61,"tag":69,"props":1498,"children":1499},{},[1500,1502,1508,1510,1516,1517,1523],{"type":66,"value":1501},"Snapshots move; force a refresh with ",{"type":61,"tag":83,"props":1503,"children":1505},{"className":1504},[],[1506],{"type":66,"value":1507},".\u002Fgradlew bundle --refresh-dependencies",{"type":66,"value":1509},". (For Maven, add the same repository to ",{"type":61,"tag":83,"props":1511,"children":1513},{"className":1512},[],[1514],{"type":66,"value":1515},"\u003Crepositories>",{"type":66,"value":758},{"type":61,"tag":83,"props":1518,"children":1520},{"className":1519},[],[1521],{"type":66,"value":1522},"\u003CpluginRepositories>",{"type":66,"value":1524},".)",{"type":61,"tag":156,"props":1526,"children":1527},{},[],{"type":61,"tag":160,"props":1529,"children":1531},{"id":1530},"place-the-bundle-where-the-coordinator-scans",[1532],{"type":66,"value":1533},"Place the bundle where the coordinator scans",{"type":61,"tag":69,"props":1535,"children":1536},{},[1537,1539,1544],{"type":66,"value":1538},"The coordinator scans ",{"type":61,"tag":83,"props":1540,"children":1542},{"className":1541},[],[1543],{"type":66,"value":96},{"type":66,"value":1545}," recursively and builds the classpath automatically, so you copy the whole output directory:",{"type":61,"tag":180,"props":1547,"children":1549},{"className":371,"code":1548,"language":373,"meta":185,"style":185},"cp build\u002Fbundle\u002F* \u002Fopt\u002Fairflow\u002Fjars\u002F    # \u002Fopt\u002Fairflow\u002Fjars == jars_root\n",[1550],{"type":61,"tag":83,"props":1551,"children":1552},{"__ignoreMap":185},[1553],{"type":61,"tag":191,"props":1554,"children":1555},{"class":193,"line":194},[1556,1561,1566,1572,1577],{"type":61,"tag":191,"props":1557,"children":1558},{"style":383},[1559],{"type":66,"value":1560},"cp",{"type":61,"tag":191,"props":1562,"children":1563},{"style":389},[1564],{"type":66,"value":1565}," build\u002Fbundle\u002F",{"type":61,"tag":191,"props":1567,"children":1569},{"style":1568},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[1570],{"type":66,"value":1571},"*",{"type":61,"tag":191,"props":1573,"children":1574},{"style":389},[1575],{"type":66,"value":1576}," \u002Fopt\u002Fairflow\u002Fjars\u002F",{"type":61,"tag":191,"props":1578,"children":1579},{"style":1367},[1580],{"type":66,"value":1581},"    # \u002Fopt\u002Fairflow\u002Fjars == jars_root\n",{"type":61,"tag":69,"props":1583,"children":1584},{},[1585,1587,1592,1594,1598],{"type":66,"value":1586},"The worker also needs a ",{"type":61,"tag":75,"props":1588,"children":1589},{},[1590],{"type":66,"value":1591},"JRE 17+",{"type":66,"value":1593},". Wiring the coordinator to this directory is covered in ",{"type":61,"tag":75,"props":1595,"children":1596},{},[1597],{"type":66,"value":145},{"type":66,"value":154},{"type":61,"tag":156,"props":1600,"children":1601},{},[],{"type":61,"tag":160,"props":1603,"children":1605},{"id":1604},"deployment-paths",[1606],{"type":66,"value":1607},"Deployment paths",{"type":61,"tag":69,"props":1609,"children":1610},{},[1611,1613,1618],{"type":66,"value":1612},"Astronomer tooling is ",{"type":61,"tag":75,"props":1614,"children":1615},{},[1616],{"type":66,"value":1617},"not required",{"type":66,"value":1619}," — the SDK runs on any Airflow with the Task SDK. Choose the path that matches the user's setup.",{"type":61,"tag":1621,"props":1622,"children":1624},"h3",{"id":1623},"open-source-docker-kubernetes",[1625],{"type":66,"value":1626},"Open-source (Docker \u002F Kubernetes)",{"type":61,"tag":69,"props":1628,"children":1629},{},[1630],{"type":66,"value":1631},"Bake the JRE and the bundle into your Airflow image, or mount them:",{"type":61,"tag":180,"props":1633,"children":1637},{"className":1634,"code":1635,"language":1636,"meta":185,"style":185},"language-dockerfile shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","FROM apache\u002Fairflow:3          # pin a specific 3.x in production\nUSER root\nRUN apt-get update \\\n    && apt-get install -y --no-install-recommends default-jre-headless \\\n    && apt-get clean && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\nRUN mkdir -p \u002Fopt\u002Fairflow\u002Fjars\nCOPY build\u002Fbundle\u002F \u002Fopt\u002Fairflow\u002Fjars\u002F\nUSER airflow\n","dockerfile",[1638],{"type":61,"tag":83,"props":1639,"children":1640},{"__ignoreMap":185},[1641,1649,1657,1665,1673,1681,1689,1697],{"type":61,"tag":191,"props":1642,"children":1643},{"class":193,"line":194},[1644],{"type":61,"tag":191,"props":1645,"children":1646},{},[1647],{"type":66,"value":1648},"FROM apache\u002Fairflow:3          # pin a specific 3.x in production\n",{"type":61,"tag":191,"props":1650,"children":1651},{"class":193,"line":203},[1652],{"type":61,"tag":191,"props":1653,"children":1654},{},[1655],{"type":66,"value":1656},"USER root\n",{"type":61,"tag":191,"props":1658,"children":1659},{"class":193,"line":212},[1660],{"type":61,"tag":191,"props":1661,"children":1662},{},[1663],{"type":66,"value":1664},"RUN apt-get update \\\n",{"type":61,"tag":191,"props":1666,"children":1667},{"class":193,"line":221},[1668],{"type":61,"tag":191,"props":1669,"children":1670},{},[1671],{"type":66,"value":1672},"    && apt-get install -y --no-install-recommends default-jre-headless \\\n",{"type":61,"tag":191,"props":1674,"children":1675},{"class":193,"line":231},[1676],{"type":61,"tag":191,"props":1677,"children":1678},{},[1679],{"type":66,"value":1680},"    && apt-get clean && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",{"type":61,"tag":191,"props":1682,"children":1683},{"class":193,"line":240},[1684],{"type":61,"tag":191,"props":1685,"children":1686},{},[1687],{"type":66,"value":1688},"RUN mkdir -p \u002Fopt\u002Fairflow\u002Fjars\n",{"type":61,"tag":191,"props":1690,"children":1691},{"class":193,"line":249},[1692],{"type":61,"tag":191,"props":1693,"children":1694},{},[1695],{"type":66,"value":1696},"COPY build\u002Fbundle\u002F \u002Fopt\u002Fairflow\u002Fjars\u002F\n",{"type":61,"tag":191,"props":1698,"children":1699},{"class":193,"line":257},[1700],{"type":61,"tag":191,"props":1701,"children":1702},{},[1703],{"type":66,"value":1704},"USER airflow\n",{"type":61,"tag":69,"props":1706,"children":1707},{},[1708,1710,1716,1718,1723],{"type":66,"value":1709},"On Kubernetes (Helm chart), bake the JAR into a custom image as above, or mount it via a shared volume; set the ",{"type":61,"tag":83,"props":1711,"children":1713},{"className":1712},[],[1714],{"type":66,"value":1715},"[sdk]",{"type":66,"value":1717}," config through environment variables on the worker\u002Fscheduler. See ",{"type":61,"tag":75,"props":1719,"children":1720},{},[1721],{"type":66,"value":1722},"deploying-airflow",{"type":66,"value":1724}," for the broader Docker Compose and Helm workflow.",{"type":61,"tag":1621,"props":1726,"children":1728},{"id":1727},"astro-one-option-not-required",[1729],{"type":66,"value":1730},"Astro (one option, not required)",{"type":61,"tag":69,"props":1732,"children":1733},{},[1734],{"type":66,"value":1735},"If the user is on Astronomer's Astro CLI, the same idea maps onto an Astro project:",{"type":61,"tag":1737,"props":1738,"children":1739},"ol",{},[1740,1752,1830,1857],{"type":61,"tag":411,"props":1741,"children":1742},{},[1743,1745,1751],{"type":66,"value":1744},"Build the bundle, then stage it in the project: ",{"type":61,"tag":83,"props":1746,"children":1748},{"className":1747},[],[1749],{"type":66,"value":1750},"mkdir -p include\u002Fjars && cp ..\u002Fjava-bundle\u002Fbuild\u002Fbundle\u002F*.jar include\u002Fjars\u002F",{"type":66,"value":154},{"type":61,"tag":411,"props":1753,"children":1754},{},[1755,1757,1763,1765],{"type":66,"value":1756},"Edit the project ",{"type":61,"tag":83,"props":1758,"children":1760},{"className":1759},[],[1761],{"type":66,"value":1762},"Dockerfile",{"type":66,"value":1764}," to install a JRE and copy the JARs to the coordinator's directory:",{"type":61,"tag":180,"props":1766,"children":1768},{"className":1634,"code":1767,"language":1636,"meta":185,"style":185},"FROM quay.io\u002Fastronomer\u002Fastro-runtime:\u003Cversion>\nUSER root\nRUN apt-get update \\\n    && apt-get install -y --no-install-recommends default-jre-headless \\\n    && apt-get clean && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\nRUN mkdir -p \u002Fopt\u002Fairflow\u002Fjars\nCOPY include\u002Fjars\u002F \u002Fopt\u002Fairflow\u002Fjars\u002F\nUSER airflow\n",[1769],{"type":61,"tag":83,"props":1770,"children":1771},{"__ignoreMap":185},[1772,1780,1787,1794,1801,1808,1815,1823],{"type":61,"tag":191,"props":1773,"children":1774},{"class":193,"line":194},[1775],{"type":61,"tag":191,"props":1776,"children":1777},{},[1778],{"type":66,"value":1779},"FROM quay.io\u002Fastronomer\u002Fastro-runtime:\u003Cversion>\n",{"type":61,"tag":191,"props":1781,"children":1782},{"class":193,"line":203},[1783],{"type":61,"tag":191,"props":1784,"children":1785},{},[1786],{"type":66,"value":1656},{"type":61,"tag":191,"props":1788,"children":1789},{"class":193,"line":212},[1790],{"type":61,"tag":191,"props":1791,"children":1792},{},[1793],{"type":66,"value":1664},{"type":61,"tag":191,"props":1795,"children":1796},{"class":193,"line":221},[1797],{"type":61,"tag":191,"props":1798,"children":1799},{},[1800],{"type":66,"value":1672},{"type":61,"tag":191,"props":1802,"children":1803},{"class":193,"line":231},[1804],{"type":61,"tag":191,"props":1805,"children":1806},{},[1807],{"type":66,"value":1680},{"type":61,"tag":191,"props":1809,"children":1810},{"class":193,"line":240},[1811],{"type":61,"tag":191,"props":1812,"children":1813},{},[1814],{"type":66,"value":1688},{"type":61,"tag":191,"props":1816,"children":1817},{"class":193,"line":249},[1818],{"type":61,"tag":191,"props":1819,"children":1820},{},[1821],{"type":66,"value":1822},"COPY include\u002Fjars\u002F \u002Fopt\u002Fairflow\u002Fjars\u002F\n",{"type":61,"tag":191,"props":1824,"children":1825},{"class":193,"line":257},[1826],{"type":61,"tag":191,"props":1827,"children":1828},{},[1829],{"type":66,"value":1704},{"type":61,"tag":411,"props":1831,"children":1832},{},[1833,1835,1841,1843,1847,1849,1855],{"type":66,"value":1834},"Put the coordinator config in the project's ",{"type":61,"tag":83,"props":1836,"children":1838},{"className":1837},[],[1839],{"type":66,"value":1840},".env",{"type":66,"value":1842}," (loaded automatically) — see ",{"type":61,"tag":75,"props":1844,"children":1845},{},[1846],{"type":66,"value":145},{"type":66,"value":1848}," for the ",{"type":61,"tag":83,"props":1850,"children":1852},{"className":1851},[],[1853],{"type":66,"value":1854},"AIRFLOW__SDK__*",{"type":66,"value":1856}," values.",{"type":61,"tag":411,"props":1858,"children":1859},{},[1860,1866,1868,1874,1876,1882],{"type":61,"tag":83,"props":1861,"children":1863},{"className":1862},[],[1864],{"type":66,"value":1865},"astro dev start",{"type":66,"value":1867}," (or ",{"type":61,"tag":83,"props":1869,"children":1871},{"className":1870},[],[1872],{"type":66,"value":1873},"astro dev restart",{"type":66,"value":1875}," after changes) builds the image and starts Airflow locally; deploy with ",{"type":61,"tag":83,"props":1877,"children":1879},{"className":1878},[],[1880],{"type":66,"value":1881},"astro deploy",{"type":66,"value":1883}," as usual.",{"type":61,"tag":100,"props":1885,"children":1886},{},[1887],{"type":61,"tag":69,"props":1888,"children":1889},{},[1890,1892,1897],{"type":66,"value":1891},"Don't pin Astro Runtime \u002F Airflow versions from memory — read the generated ",{"type":61,"tag":83,"props":1893,"children":1895},{"className":1894},[],[1896],{"type":66,"value":1762},{"type":66,"value":1898}," or check current docs. While the SDK and Airflow 3.3 are in preview, a beta\u002Fdev Astro Runtime image may be required.",{"type":61,"tag":156,"props":1900,"children":1901},{},[],{"type":61,"tag":160,"props":1903,"children":1905},{"id":1904},"deploy-checklist",[1906],{"type":66,"value":1907},"Deploy checklist",{"type":61,"tag":407,"props":1909,"children":1910},{},[1911,1944,1961,1973,1978,1996],{"type":61,"tag":411,"props":1912,"children":1913},{},[1914,1916,1922,1924,1929,1931,1936,1938,1943],{"type":66,"value":1915},"Bundle built (",{"type":61,"tag":83,"props":1917,"children":1919},{"className":1918},[],[1920],{"type":66,"value":1921},".\u002Fgradlew bundle",{"type":66,"value":1923}," or ",{"type":61,"tag":83,"props":1925,"children":1927},{"className":1926},[],[1928],{"type":66,"value":724},{"type":66,"value":1930},") and ",{"type":61,"tag":83,"props":1932,"children":1934},{"className":1933},[],[1935],{"type":66,"value":468},{"type":66,"value":1937}," points at your ",{"type":61,"tag":83,"props":1939,"children":1941},{"className":1940},[],[1942],{"type":66,"value":700},{"type":66,"value":154},{"type":61,"tag":411,"props":1945,"children":1946},{},[1947,1952,1954,1959],{"type":61,"tag":83,"props":1948,"children":1950},{"className":1949},[],[1951],{"type":66,"value":420},{"type":66,"value":1953}," present ",{"type":61,"tag":75,"props":1955,"children":1956},{},[1957],{"type":66,"value":1958},"iff",{"type":66,"value":1960}," you use the annotation API.",{"type":61,"tag":411,"props":1962,"children":1963},{},[1964,1966,1971],{"type":66,"value":1965},"JAR(s) copied into the worker's ",{"type":61,"tag":83,"props":1967,"children":1969},{"className":1968},[],[1970],{"type":66,"value":96},{"type":66,"value":1972}," directory; with thin JARs, dependency JARs too.",{"type":61,"tag":411,"props":1974,"children":1975},{},[1976],{"type":66,"value":1977},"JRE 17+ available on the worker.",{"type":61,"tag":411,"props":1979,"children":1980},{},[1981,1983,1989,1991,1995],{"type":66,"value":1982},"Coordinator + ",{"type":61,"tag":83,"props":1984,"children":1986},{"className":1985},[],[1987],{"type":66,"value":1988},"queue_to_coordinator",{"type":66,"value":1990}," configured (",{"type":61,"tag":75,"props":1992,"children":1993},{},[1994],{"type":66,"value":145},{"type":66,"value":478},{"type":61,"tag":411,"props":1997,"children":1998},{},[1999,2001,2006,2007,2013],{"type":66,"value":2000},"If multiple executable JARs exist under ",{"type":61,"tag":83,"props":2002,"children":2004},{"className":2003},[],[2005],{"type":66,"value":96},{"type":66,"value":686},{"type":61,"tag":83,"props":2008,"children":2010},{"className":2009},[],[2011],{"type":66,"value":2012},"main_class",{"type":66,"value":2014}," explicitly.",{"type":61,"tag":156,"props":2016,"children":2017},{},[],{"type":61,"tag":160,"props":2019,"children":2021},{"id":2020},"related-skills",[2022],{"type":66,"value":2023},"Related Skills",{"type":61,"tag":407,"props":2025,"children":2026},{},[2027,2036,2045,2054],{"type":61,"tag":411,"props":2028,"children":2029},{},[2030,2034],{"type":61,"tag":75,"props":2031,"children":2032},{},[2033],{"type":66,"value":152},{"type":66,"value":2035},": Write the Java task code and the matching Python stubs.",{"type":61,"tag":411,"props":2037,"children":2038},{},[2039,2043],{"type":61,"tag":75,"props":2040,"children":2041},{},[2042],{"type":66,"value":145},{"type":66,"value":2044},": Register the coordinator and route the queue.",{"type":61,"tag":411,"props":2046,"children":2047},{},[2048,2052],{"type":61,"tag":75,"props":2049,"children":2050},{},[2051],{"type":66,"value":1722},{"type":66,"value":2053},": General Airflow deployment (Astro, Docker Compose, Kubernetes).",{"type":61,"tag":411,"props":2055,"children":2056},{},[2057,2062],{"type":61,"tag":75,"props":2058,"children":2059},{},[2060],{"type":66,"value":2061},"setting-up-astro-project",{"type":66,"value":2063},": Initialize and configure an Astro project.",{"type":61,"tag":2065,"props":2066,"children":2067},"style",{},[2068],{"type":66,"value":2069},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2071,"total":2234},[2072,2089,2101,2118,2131,2148,2161,2174,2189,2198,2208,2221],{"slug":35,"name":35,"fn":2073,"description":2074,"org":2075,"tags":2076,"stars":25,"repoUrl":26,"updatedAt":2088},"manage and troubleshoot Airflow via CLI","Queries, manages, and troubleshoots Apache Airflow using the `af` CLI. Use when working with anything related to Airflow - a DAG, a DAG run, a task log, an import or parse error, a broken DAG, or any Airflow operation. Covers listing and triggering DAGs, retrying runs, reading task logs, diagnosing failures, debugging import and parse errors, checking connections, variables and pools, exploring the REST API, and monitoring health (for example \"trigger a pipeline\", \"retry a run\", \"list connections\", \"check Airflow health\", \"why did my DAG fail\"). This is the entrypoint that routes to sibling skills for authoring, testing, deploying, and migrating Airflow 2 to 3. Not for warehouse\u002FSQL analytics on Airflow metadata tables (use analyzing-data); for deep root-cause reports use debugging-dags or airflow-investigation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2077,2079,2082,2085],{"name":2078,"slug":35,"type":15},"Airflow",{"name":2080,"slug":2081,"type":15},"CLI","cli",{"name":2083,"slug":2084,"type":15},"Data Pipeline","data-pipeline",{"name":2086,"slug":2087,"type":15},"Debugging","debugging","2026-04-06T18:01:43.992997",{"slug":2090,"name":2090,"fn":2091,"description":2092,"org":2093,"tags":2094,"stars":25,"repoUrl":26,"updatedAt":2100},"airflow-hitl","add human-in-the-loop steps to Airflow DAGs","Builds human-in-the-loop (HITL) Airflow workflows - approval gates, form input, and human-driven branching. Use when a DAG needs a human in the loop - an approval or reject step, sign-off before a task runs, a decision or approval UI, branching on a human choice, or collecting form input mid-run; also on mentions of ApprovalOperator, HITLOperator, HITLBranchOperator, HITLEntryOperator, or HITLTrigger. Requires Airflow 3.1+. Not for AI\u002FLLM task calls (see migrating-ai-sdk-to-common-ai).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2095,2096,2099],{"name":2078,"slug":35,"type":15},{"name":2097,"slug":2098,"type":15},"Approvals","approvals",{"name":2083,"slug":2084,"type":15},"2026-04-06T18:01:46.758548",{"slug":2102,"name":2102,"fn":2103,"description":2104,"org":2105,"tags":2106,"stars":25,"repoUrl":26,"updatedAt":2117},"airflow-plugins","build Airflow UI plugins","Builds Airflow 3.1+ plugins that embed FastAPI apps, custom UI pages, React components, middleware, macros, and operator links directly into the Airflow UI. Use when building anything custom inside Airflow 3.1+ that involves Python and a browser-facing interface - creating an Airflow plugin, adding a custom UI page or nav entry, building FastAPI-backed endpoints inside Airflow, serving static assets from a plugin, embedding a React app, adding middleware to the API server, creating custom operator extra links, or calling the Airflow REST API from inside a plugin; also when AirflowPlugin, fastapi_apps, external_views, react_apps, or plugin registration come up.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2107,2108,2111,2114],{"name":2078,"slug":35,"type":15},{"name":2109,"slug":2110,"type":15},"Plugin Development","plugin-development",{"name":2112,"slug":2113,"type":15},"Python","python",{"name":2115,"slug":2116,"type":15},"UI Components","ui-components","2026-04-06T18:01:56.827891",{"slug":2119,"name":2119,"fn":2120,"description":2121,"org":2122,"tags":2123,"stars":25,"repoUrl":26,"updatedAt":2130},"airflow-state-store","persist Airflow task and asset state","Persists task and asset state across retries and DAG runs using Airflow 3.3's AIP-103 key\u002Fvalue stores (`task_state_store`, `asset_state_store`) and the crash-safe `ResumableJobMixin`. Use when the user asks about task state store, checkpointing in tasks, persisting state across retries, job IDs surviving worker crashes, watermarks, asset metadata, resumable tasks, crash-safe operators, or \"what's new in Airflow 3.3\". Also use proactively when reading a DAG that uses Variables or XCom for intra-task coordination state — flag the anti-pattern and recommend task_state_store or asset_state_store instead. Requires Airflow 3.3+.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2124,2125,2126,2127],{"name":2078,"slug":35,"type":15},{"name":17,"slug":18,"type":15},{"name":2083,"slug":2084,"type":15},{"name":2128,"slug":2129,"type":15},"Operations","operations","2026-07-07T06:43:11.160671",{"slug":2132,"name":2132,"fn":2133,"description":2134,"org":2135,"tags":2136,"stars":25,"repoUrl":26,"updatedAt":2147},"analyzing-data","query data warehouses for business questions","Queries the data warehouse with SQL and answers business questions about data. Use when answering anything that needs warehouse data - counts, metrics, trends, aggregations, joins across tables, data lookups, or ad-hoc SQL analysis (for example \"who uses X\", \"how many Y\", \"show me Z\", \"find customers\", \"what is the count\").",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2137,2140,2143,2144],{"name":2138,"slug":2139,"type":15},"Analytics","analytics",{"name":2141,"slug":2142,"type":15},"Data Analysis","data-analysis",{"name":17,"slug":18,"type":15},{"name":2145,"slug":2146,"type":15},"SQL","sql","2026-04-06T18:01:49.599775",{"slug":2149,"name":2149,"fn":2150,"description":2151,"org":2152,"tags":2153,"stars":25,"repoUrl":26,"updatedAt":2160},"annotating-task-lineage","annotate Airflow tasks with data lineage","Annotate Airflow tasks with data lineage using inlets and outlets. Use when the user wants to add lineage metadata to tasks, specify input\u002Foutput datasets, or enable lineage tracking for operators without built-in OpenLineage extraction.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2154,2155,2156,2157],{"name":2078,"slug":35,"type":15},{"name":17,"slug":18,"type":15},{"name":2083,"slug":2084,"type":15},{"name":2158,"slug":2159,"type":15},"Observability","observability","2026-04-06T18:02:03.487365",{"slug":2162,"name":2162,"fn":2163,"description":2164,"org":2165,"tags":2166,"stars":25,"repoUrl":26,"updatedAt":2173},"authoring-dags","author Airflow DAGs","Workflow and best practices for writing Apache Airflow DAGs. Use when creating a new DAG, write pipeline code, handling questions about DAG patterns and conventions or extending an existing DAG with a follow-up\u002Fdownstream task. ANY request shaped like 'add a DAG named X', 'write a pipeline', 'add a task that runs after Y', or 'extend the DAG'. For testing and debugging DAGs, see the testing-dags skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2167,2168,2169,2172],{"name":2078,"slug":35,"type":15},{"name":2083,"slug":2084,"type":15},{"name":2170,"slug":2171,"type":15},"ETL","etl",{"name":2112,"slug":2113,"type":15},"2026-04-06T18:01:52.679888",{"slug":2175,"name":2175,"fn":2176,"description":2177,"org":2178,"tags":2179,"stars":25,"repoUrl":26,"updatedAt":2188},"authoring-go-sdk-tasks","implement Airflow tasks in Go","Writes Airflow task logic in Go using the Airflow Go SDK. Use when the user wants to implement Airflow tasks in Go, asks about `BundleProvider`\u002F`RegisterDags`, the `bundlev1` Registry\u002FDag interfaces, registering Go tasks (`AddTask`\u002F`AddTaskWithName`), dependency injection by parameter type (`context.Context`, `sdk.TIRunContext`, `*slog.Logger`, `sdk.Client`), or reading connections\u002Fvariables\u002FXComs from Go. This skill covers the Go-specific native API; the shared Python-stub pattern and conceptual model live in authoring-language-sdk-tasks. For building\u002Fpacking\u002Fshipping the bundle see deploying-go-sdk-bundles; for coordinator config see configuring-airflow-language-sdks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2180,2181,2184,2185],{"name":2078,"slug":35,"type":15},{"name":2182,"slug":2183,"type":15},"API Development","api-development",{"name":2083,"slug":2084,"type":15},{"name":2186,"slug":2187,"type":15},"Go","go","2026-07-11T05:39:13.552213",{"slug":152,"name":152,"fn":2190,"description":2191,"org":2192,"tags":2193,"stars":25,"repoUrl":26,"updatedAt":2197},"implement Airflow tasks in Java","Writes Airflow task logic in Java, Kotlin, or any JVM language using the Airflow Java SDK. Use when the user wants to implement Airflow tasks in Java\u002FJVM, asks about `@Builder.Dag`\u002F`@Builder.Task`\u002F`@Builder.XCom`, the `Task`\u002F`BundleBuilder` interfaces, reading connections\u002Fvariables\u002FXComs from Java, the JSON-to-Java type mapping, or logging from Java tasks. This skill covers the Java-specific native API; the shared Python-stub pattern and conceptual model live in authoring-language-sdk-tasks. For building\u002Fshipping the bundle see deploying-java-sdk-bundles; for coordinator config see configuring-airflow-language-sdks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2194,2195,2196],{"name":2083,"slug":2084,"type":15},{"name":23,"slug":24,"type":15},{"name":13,"slug":14,"type":15},"2026-07-18T05:48:13.374003",{"slug":2199,"name":2199,"fn":2200,"description":2201,"org":2202,"tags":2203,"stars":25,"repoUrl":26,"updatedAt":2207},"authoring-language-sdk-tasks","implement non-Python Airflow tasks","The language-neutral foundation for Airflow language SDKs — implement task logic in a non-Python language while the DAG stays in Python. Use when the user wants to run an Airflow task in another language (Java, Kotlin, Go, or other JVM\u002Fnative languages), asks how the Python `@task.stub` pairs with native task code, how task\u002FDAG IDs must match across the two sides, how data passes via XCom as JSON, or which language SDKs exist. This skill owns the shared Python-stub pattern and conceptual model; for a specific language's native API, build, and runtime, use that language's skill (e.g. authoring-java-sdk-tasks, authoring-go-sdk-tasks).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2204,2205,2206],{"name":2078,"slug":35,"type":15},{"name":2083,"slug":2084,"type":15},{"name":23,"slug":24,"type":15},"2026-07-18T05:11:54.496539",{"slug":2209,"name":2209,"fn":2210,"description":2211,"org":2212,"tags":2213,"stars":25,"repoUrl":26,"updatedAt":2220},"blueprint","build reusable Airflow task group templates","Define reusable Airflow task group templates with Pydantic validation and compose DAGs from YAML. Use when creating blueprint templates, composing DAGs from YAML, validating configurations, or enabling no-code DAG authoring for non-engineers.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2214,2215,2216,2217],{"name":2078,"slug":35,"type":15},{"name":2083,"slug":2084,"type":15},{"name":2170,"slug":2171,"type":15},{"name":2218,"slug":2219,"type":15},"Templates","templates","2026-04-06T18:01:45.361425",{"slug":2222,"name":2222,"fn":2223,"description":2224,"org":2225,"tags":2226,"stars":25,"repoUrl":26,"updatedAt":2233},"checking-freshness","check data freshness in warehouses","Quick data freshness check. Use when the user asks if data is up to date, when a table was last updated, if data is stale, or needs to verify data currency before using it.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2227,2228,2229,2232],{"name":2078,"slug":35,"type":15},{"name":17,"slug":18,"type":15},{"name":2230,"slug":2231,"type":15},"Data Quality","data-quality",{"name":2170,"slug":2171,"type":15},"2026-04-06T18:02:02.138565",34,{"items":2236,"total":2234},[2237,2244,2250,2257,2264,2271,2278],{"slug":35,"name":35,"fn":2073,"description":2074,"org":2238,"tags":2239,"stars":25,"repoUrl":26,"updatedAt":2088},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2240,2241,2242,2243],{"name":2078,"slug":35,"type":15},{"name":2080,"slug":2081,"type":15},{"name":2083,"slug":2084,"type":15},{"name":2086,"slug":2087,"type":15},{"slug":2090,"name":2090,"fn":2091,"description":2092,"org":2245,"tags":2246,"stars":25,"repoUrl":26,"updatedAt":2100},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2247,2248,2249],{"name":2078,"slug":35,"type":15},{"name":2097,"slug":2098,"type":15},{"name":2083,"slug":2084,"type":15},{"slug":2102,"name":2102,"fn":2103,"description":2104,"org":2251,"tags":2252,"stars":25,"repoUrl":26,"updatedAt":2117},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2253,2254,2255,2256],{"name":2078,"slug":35,"type":15},{"name":2109,"slug":2110,"type":15},{"name":2112,"slug":2113,"type":15},{"name":2115,"slug":2116,"type":15},{"slug":2119,"name":2119,"fn":2120,"description":2121,"org":2258,"tags":2259,"stars":25,"repoUrl":26,"updatedAt":2130},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2260,2261,2262,2263],{"name":2078,"slug":35,"type":15},{"name":17,"slug":18,"type":15},{"name":2083,"slug":2084,"type":15},{"name":2128,"slug":2129,"type":15},{"slug":2132,"name":2132,"fn":2133,"description":2134,"org":2265,"tags":2266,"stars":25,"repoUrl":26,"updatedAt":2147},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2267,2268,2269,2270],{"name":2138,"slug":2139,"type":15},{"name":2141,"slug":2142,"type":15},{"name":17,"slug":18,"type":15},{"name":2145,"slug":2146,"type":15},{"slug":2149,"name":2149,"fn":2150,"description":2151,"org":2272,"tags":2273,"stars":25,"repoUrl":26,"updatedAt":2160},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2274,2275,2276,2277],{"name":2078,"slug":35,"type":15},{"name":17,"slug":18,"type":15},{"name":2083,"slug":2084,"type":15},{"name":2158,"slug":2159,"type":15},{"slug":2162,"name":2162,"fn":2163,"description":2164,"org":2279,"tags":2280,"stars":25,"repoUrl":26,"updatedAt":2173},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2281,2282,2283,2284],{"name":2078,"slug":35,"type":15},{"name":2083,"slug":2084,"type":15},{"name":2170,"slug":2171,"type":15},{"name":2112,"slug":2113,"type":15}]