Repository navigation
Let plugins add their own actions to Dag, run and task instance rows - #75229
Conversation
0ae09a6 to
3dfee57
Compare
|
@bbovenzi , awesome! will review this today. I hope the 3.4 train hasn't left the station |
3dfee57 to
4139c56
Compare
There was a problem hiding this comment.
This is a nice extension point, and it holds up well end to end: I ran it behind a base_url sub-path with link, React, applies_to-scoped and invalid actions. Unsafe hrefs get dropped and templated values are encoded, nice.
A few things before merge, mainly the base-path link resolution and keeping the response schema permissive so we don't lock out future fields. Details inline.
9e4cdea to
e43b85e
Compare
dheerajturaga
left a comment
There was a problem hiding this comment.
This is a useful extension point. I ran it locally with a test plugin that covers link and React actions on all three destinations: in-app and external links, applies_to scoping on tags, state, operator and dag_id, custom and dark-mode icons, a popover and a dialog React action, and a set of deliberately invalid entries. Everything behaved as documented, including the load-time rejections and their log messages.
There are two small things inline, neither blocking:
- the bulk-action bar on the Dags list lost its translation when it was extracted (
DagsList.tsx:120); - the docs should warn that a React action's
namecan collide with a browser global (custom-action-plugin.rst:252), plus a related one-line comment fix (PluginActionBoundary.tsx:33).
This review was drafted by an AI-assisted tool and
confirmed by an Apache Airflow maintainer. The maintainer
approving this PR has read the findings and signed off. If
something feels off, please reply on the PR and a maintainer
will follow up.More on how Apache Airflow handles maintainer review:
Contributing guide.
pierrejeambrun
left a comment
There was a problem hiding this comment.
Thanks Brent, everything from the previous round is addressed, and base-path links now navigate in-app end to end. Could you take a look at the open comments from the latest review before merging? Two small things inline.
e43b85e to
ea6bf2e
Compare
Airflow draws a fixed set of actions beside a Dag, a Dag Run and a Task Instance -- trigger, clear, mark state -- and a plugin had no way to put anything next to them. A plugin could add a whole page as a tab, but not a single button on the row or header card where the user already is, so the work it offers for one entity had to be reached by navigating away from that entity. An action is either a `link`, which opens a URL templated with the entity's context, or `react`, which mounts the plugin's own component behind a trigger button. The React kind is handed `open`, `onClose` and `triggerRef` and portals whatever surface it wants -- a popover, a dialog, a drawer -- rather than being given a modal to fill, because the right surface depends on what the plugin is showing. Actions are scoped by the same `applies_to` field paths as tabs and panels, through the same matchers, so a button can be offered only where it applies: only on failed task instances, only on Dags of a given team. That is the reason the two were designed together. A React action's `name` is the global its bundle registers under, so it cannot share one with a React App or another React action -- the second to load would silently win. Such an action is refused rather than loaded.
ea6bf2e to
74a810b
Compare
…75229) (#75427) Airflow draws a fixed set of actions beside a Dag, a Dag Run and a Task Instance -- trigger, clear, mark state -- and a plugin had no way to put anything next to them. A plugin could add a whole page as a tab, but not a single button on the row or header card where the user already is, so the work it offers for one entity had to be reached by navigating away from that entity. An action is either a `link`, which opens a URL templated with the entity's context, or `react`, which mounts the plugin's own component behind a trigger button. The React kind is handed `open`, `onClose` and `triggerRef` and portals whatever surface it wants -- a popover, a dialog, a drawer -- rather than being given a modal to fill, because the right surface depends on what the plugin is showing. Actions are scoped by the same `applies_to` field paths as tabs and panels, through the same matchers, so a button can be offered only where it applies: only on failed task instances, only on Dags of a given team. That is the reason the two were designed together. A React action's `name` is the global its bundle registers under, so it cannot share one with a React App or another React action -- the second to load would silently win. Such an action is refused rather than loaded. (cherry picked from commit c510e4d)
Let plugins add their own custom buttons to a Dag, Dag Run or Task Instance. These buttons will appear on the Header card of any details page but also the list of dags, runs and TIs.
They can link to an existing page or plugin in Airflow or open an external page. They accept all the new
applie_tofields to target the actions. Think adding a special "open an AI chat" button to explain failed task instance logs.Examples:
Task Instance action for tasks with bash operator and dag tag of "example". External link

Dag action for draining dags. Internal link.

Dag run action for a failure. External Link.

Combing a "base" destination plugin with url params to open a custom modal on a button click:

Was generative AI tooling used to co-author this PR?
Claude Opus 5
{pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.