Thanks. Will take a look !
On Wed, Apr 22, 2026 at 8:12 PM Jens Scheffler <[email protected]> wrote:
> Hi Jarek,
>
> i attempted to run PMC Checks but I fail in reproducibility - so until I
> know I'd do something wrong I'd need to vote a -1:
>
> SVN: revision 83981.
>
> Branch/Git: tag: airflow-ctl/0.1.4rc3
> ca53e01b3c8bb00a3ca697b508ab52cdd28f5e68
>
> Checking if apache_airflow_ctl-0.1.4-py3-none-any.whl is the same as
>
> /home/jscheffl/Workspace/airflow/dist/apache_airflow_ctl-0.1.4-py3-none-any.whl
> Binary files apache_airflow_ctl-0.1.4-py3-none-any.whl and
> /home/jscheffl/Workspace/airflow/dist/apache_airflow_ctl-0.1.4-py3-none-any.whl
>
> differ
>
> Checking if apache_airflow_ctl-0.1.4-source.tar.gz is the same as
>
> /home/jscheffl/Workspace/airflow/dist/apache_airflow_ctl-0.1.4-source.tar.gz
> Binary files apache_airflow_ctl-0.1.4-source.tar.gz and
> /home/jscheffl/Workspace/airflow/dist/apache_airflow_ctl-0.1.4-source.tar.gz
>
> differ
>
> Checking if apache_airflow_ctl-0.1.4.tar.gz is the same as
> /home/jscheffl/Workspace/airflow/dist/apache_airflow_ctl-0.1.4.tar.gz
> Binary files apache_airflow_ctl-0.1.4.tar.gz and
> /home/jscheffl/Workspace/airflow/dist/apache_airflow_ctl-0.1.4.tar.gz
> differ
>
> Did I pick the wrong package or branch?
>
> For the WHL file diffoscope tells me:
>
> * a lot of file meta data differences
> o -rw-r--r-- 2.0 unx 1705 b- defN 26-Apr-13 12:48
> airflowctl/exceptions.py
> to
> o -rw-r--r-- 2.0 unx 1705 b- defN 26-Apr-20 11:13
> airflowctl/exceptions.py
> * Diff in api/datamodels/generated.py
>
> ├── airflowctl/api/datamodels/generated.py
> │ @@ -169,15 +169,14 @@
> │ bool | None,
> │ Field(
> │ description="(Experimental) Run on the latest bundle
> version of the dag after clearing the task instances.",
> │ title="Run On Latest Version",
> │ ),
> │ ] = False
> │ prevent_running_task: Annotated[bool | None, Field(title="Prevent
> Running Task")] = False
> │ - note: Annotated[Note | None, Field(title="Note")] = None
> │
> │
> │ class Value(RootModel[list]):
> │ root: Annotated[list, Field(max_length=2, min_length=2,
> title="Value")]
> │
> │
> │ class ConfigOption(BaseModel):
> │ @@ -284,21 +283,14 @@
> │ """
> │
> │ model_config = ConfigDict(
> │ extra="forbid",
> │ )
> │ dry_run: Annotated[bool | None, Field(title="Dry Run")] = True
> │ only_failed: Annotated[bool | None, Field(title="Only Failed")] =
> False
> │ - only_new: Annotated[
> │ - bool | None,
> │ - Field(
> │ - description="Only queue newly added tasks in the latest
> DAG version without clearing existing tasks.",
> │ - title="Only New",
> │ - ),
> │ - ] = False
> │ run_on_latest_version: Annotated[
> │ bool | None,
> │ Field(
> │ description="(Experimental) Run on the latest bundle
> version of the Dag after clearing the Dag Run.",
> │ title="Run On Latest Version",
> │ ),
> │ ] = False
> │ @@ -398,15 +390,14 @@
> │ """
> │ Class with DagRun types.
> │ """
> │
> │ BACKFILL = "backfill"
> │ SCHEDULED = "scheduled"
> │ MANUAL = "manual"
> │ - OPERATOR_TRIGGERED = "operator_triggered"
> │ ASSET_TRIGGERED = "asset_triggered"
> │ ASSET_MATERIALIZATION = "asset_materialization"
> │
> │
> │ class DagScheduleAssetReference(BaseModel):
> │ """
> │ DAG schedule reference serializer for assets.
> │ @@ -643,23 +634,14 @@
> │ logical_date: Annotated[datetime | None, Field(title="Logical
> Date")] = None
> │ run_after: Annotated[datetime | None, Field(title="Run After")] =
> None
> │ conf: Annotated[dict[str, Any] | None, Field(title="Conf")] = None
> │ note: Annotated[str | None, Field(title="Note")] = None
> │ partition_key: Annotated[str | None, Field(title="Partition
> Key")] = None
> │
> │
> │ -class NewTaskResponse(BaseModel):
> │ - """
> │ - Lightweight response for new tasks that don't have TaskInstances
> yet.
> │ - """
> │ -
> │ - task_id: Annotated[str, Field(title="Task Id")]
> │ - task_display_name: Annotated[str, Field(title="Task Display Name")]
> │ -
> │ -
> │ class PluginImportErrorResponse(BaseModel):
> │ """
> │ Plugin Import Error serializer for responses.
> │ """
> │
> │ source: Annotated[str, Field(title="Source")]
> │ error: Annotated[str, Field(title="Error")]
> │ @@ -1144,15 +1126,15 @@
> │ model_config = ConfigDict(
> │ extra="forbid",
> │ )
> │ dag_id: Annotated[str, Field(title="Dag Id")]
> │ from_date: Annotated[datetime, Field(title="From Date")]
> │ to_date: Annotated[datetime, Field(title="To Date")]
> │ run_backwards: Annotated[bool | None, Field(title="Run
> Backwards")] = False
> │ - dag_run_conf: Annotated[dict[str, Any] | None, Field(title="Dag
> Run Conf")] = None
> │ + dag_run_conf: Annotated[dict[str, Any] | None, Field(title="Dag
> Run Conf")] = {}
> │ reprocess_behavior: ReprocessBehavior | None = "none"
> │ max_active_runs: Annotated[int | None, Field(title="Max Active
> Runs")] = 10
> │ run_on_latest_version: Annotated[bool | None, Field(title="Run On
> Latest Version")] = True
> │
> │
> │ class BackfillResponse(BaseModel):
> │ """
> │ @@ -1390,15 +1372,14 @@
> │ bundle_version: Annotated[str | None, Field(title="Bundle
> Version")] = None
> │ relative_fileloc: Annotated[str | None, Field(title="Relative
> Fileloc")] = None
> │ fileloc: Annotated[str, Field(title="Fileloc")]
> │ description: Annotated[str | None, Field(title="Description")] =
> None
> │ timetable_summary: Annotated[str | None, Field(title="Timetable
> Summary")] = None
> │ timetable_description: Annotated[str | None,
> Field(title="Timetable Description")] = None
> │ timetable_partitioned: Annotated[bool, Field(title="Timetable
> Partitioned")]
> │ - timetable_periodic: Annotated[bool, Field(title="Timetable
> Periodic")]
> │ tags: Annotated[list[DagTagResponse], Field(title="Tags")]
> │ max_active_tasks: Annotated[int, Field(title="Max Active Tasks")]
> │ max_active_runs: Annotated[int | None, Field(title="Max Active
> Runs")] = None
> │ max_consecutive_failed_dag_runs: Annotated[int, Field(title="Max
> Consecutive Failed Dag Runs")]
> │ has_task_concurrency_limits: Annotated[bool, Field(title="Has
> Task Concurrency Limits")]
> │ has_import_errors: Annotated[bool, Field(title="Has Import Errors")]
> │ next_dagrun_logical_date: Annotated[datetime | None,
> Field(title="Next Dagrun Logical Date")] = None
> │ @@ -1423,17 +1404,14 @@
> │ template_search_path: Annotated[list[str] | None,
> Field(title="Template Search Path")] = None
> │ timezone: Annotated[str | None, Field(title="Timezone")] = None
> │ last_parsed: Annotated[datetime | None, Field(title="Last
> Parsed")] = None
> │ default_args: Annotated[dict[str, Any] | None,
> Field(title="Default Args")] = None
> │ owner_links: Annotated[dict[str, str] | None, Field(title="Owner
> Links")] = None
> │ is_favorite: Annotated[bool | None, Field(title="Is Favorite")] =
> False
> │ active_runs_count: Annotated[int | None, Field(title="Active Runs
> Count")] = 0
> │ - is_backfillable: Annotated[
> │ - bool, Field(description="Whether this DAG's schedule supports
> backfilling.", title="Is Backfillable")
> │ - ]
> │ file_token: Annotated[str, Field(description="Return file
> token.", title="File Token")]
> │ concurrency: Annotated[
> │ int,
> │ Field(
> │ description="Return max_active_tasks as
> concurrency.\n\nDeprecated: Use max_active_tasks instead.",
> │ title="Concurrency",
> │ ),
> │ @@ -1459,15 +1437,14 @@
> │ bundle_version: Annotated[str | None, Field(title="Bundle
> Version")] = None
> │ relative_fileloc: Annotated[str | None, Field(title="Relative
> Fileloc")] = None
> │ fileloc: Annotated[str, Field(title="Fileloc")]
> │ description: Annotated[str | None, Field(title="Description")] =
> None
> │ timetable_summary: Annotated[str | None, Field(title="Timetable
> Summary")] = None
> │ timetable_description: Annotated[str | None,
> Field(title="Timetable Description")] = None
> │ timetable_partitioned: Annotated[bool, Field(title="Timetable
> Partitioned")]
> │ - timetable_periodic: Annotated[bool, Field(title="Timetable
> Periodic")]
> │ tags: Annotated[list[DagTagResponse], Field(title="Tags")]
> │ max_active_tasks: Annotated[int, Field(title="Max Active Tasks")]
> │ max_active_runs: Annotated[int | None, Field(title="Max Active
> Runs")] = None
> │ max_consecutive_failed_dag_runs: Annotated[int, Field(title="Max
> Consecutive Failed Dag Runs")]
> │ has_task_concurrency_limits: Annotated[bool, Field(title="Has
> Task Concurrency Limits")]
> │ has_import_errors: Annotated[bool, Field(title="Has Import Errors")]
> │ next_dagrun_logical_date: Annotated[datetime | None,
> Field(title="Next Dagrun Logical Date")] = None
> │ @@ -1476,17 +1453,14 @@
> │ ] = None
> │ next_dagrun_data_interval_end: Annotated[
> │ datetime | None, Field(title="Next Dagrun Data Interval End")
> │ ] = None
> │ next_dagrun_run_after: Annotated[datetime | None,
> Field(title="Next Dagrun Run After")] = None
> │ allowed_run_types: Annotated[list[DagRunType] | None,
> Field(title="Allowed Run Types")] = None
> │ owners: Annotated[list[str], Field(title="Owners")]
> │ - is_backfillable: Annotated[
> │ - bool, Field(description="Whether this DAG's schedule supports
> backfilling.", title="Is Backfillable")
> │ - ]
> │ file_token: Annotated[str, Field(description="Return file
> token.", title="File Token")]
> │
> │
> │ class DAGRunPatchBody(BaseModel):
> │ """
> │ DAG Run Serializer for PATCH requests.
> │ """
> │ @@ -1954,23 +1928,14 @@
> │ entities: Annotated[
> │ list[str | BulkTaskInstanceBody],
> │ Field(description="A list of entity id/key or entity objects
> to be deleted.", title="Entities"),
> │ ]
> │ action_on_non_existence: BulkActionNotOnExistence | None = "fail"
> │
> │
> │ -class ClearTaskInstanceCollectionResponse(BaseModel):
> │ - """
> │ - Response for clear dag run dry run, which may contain new tasks
> without full TaskInstance data.
> │ - """
> │ -
> │ - task_instances: Annotated[list[TaskInstanceResponse |
> NewTaskResponse], Field(title="Task Instances")]
> │ - total_entries: Annotated[int, Field(title="Total Entries")]
> │ -
> │ -
> │ class DAGCollectionResponse(BaseModel):
> │ """
> │ DAG Collection serializer for responses.
> │ """
> │
> │ dags: Annotated[list[DAGResponse], Field(title="Dags")]
> │ total_entries: Annotated[int, Field(title="Total Entries")]
> │ @@ -2070,46 +2035,19 @@
> │
> │ tasks: Annotated[list[TaskResponse], Field(title="Tasks")]
> │ total_entries: Annotated[int, Field(title="Total Entries")]
> │
> │
> │ class TaskInstanceCollectionResponse(BaseModel):
> │ """
> │ - Task instance collection response supporting both offset and
> cursor pagination.
> │ -
> │ - A single flat model is used instead of a discriminated union
> │ - (``Annotated[Offset | Cursor, Field(discriminator=...)]``) because
> │ - the OpenAPI ``oneOf`` + ``discriminator`` construct is not handled
> │ - correctly by ``@hey-api/openapi-ts`` /
> ``@7nohe/openapi-react-query-codegen``:
> │ - return types degrade to ``unknown`` in JSDoc and can produce
> │ - incorrect TypeScript types (see hey-api/openapi-ts#1613, #3270).
> │ + Task Instance Collection serializer for responses.
> │ """
> │
> │ task_instances: Annotated[list[TaskInstanceResponse],
> Field(title="Task Instances")]
> │ - total_entries: Annotated[
> │ - int | None,
> │ - Field(
> │ - description="Total number of matching items. Populated
> for offset pagination, ``null`` when using cursor pagination.",
> │ - title="Total Entries",
> │ - ),
> │ - ] = None
> │ - next_cursor: Annotated[
> │ - str | None,
> │ - Field(
> │ - description="Token pointing to the next page. Populated
> for cursor pagination, ``null`` when using offset pagination or when
> there is no next page.",
> │ - title="Next Cursor",
> │ - ),
> │ - ] = None
> │ - previous_cursor: Annotated[
> │ - str | None,
> │ - Field(
> │ - description="Token pointing to the previous page.
> Populated for cursor pagination, ``null`` when using offset pagination
> or when on the first page.",
> │ - title="Previous Cursor",
> │ - ),
> │ - ] = None
> │ + total_entries: Annotated[int, Field(title="Total Entries")]
> │
> │
> │ class TaskInstanceHistoryCollectionResponse(BaseModel):
> │ """
> │ TaskInstanceHistory Collection serializer for responses.
> │ """
>
> On 22.04.26 01:55, Jarek Potiuk wrote:
> > The release candidate for **Apache Airflow Ctl**: 0.1.4rc3 is now
> > available for testing!
> >
> > This email is calling for a vote on the release, which will last at
> least until
> > Friday, 2026-04-24 23:59 UTC and until 3 binding +1 votes have been
> received.
> >
> > Consider this my +1 (binding) vote.
> >
> > The apache-airflow-ctl 0.1.4rc3 package is available at:
> > https://dist.apache.org/repos/dist/dev/airflow/airflow-ctl/0.1.4rc3/
> >
> > The "apache-airflow-ctl" packages are:
> >
> > - *apache_airflow_ctl-0.1.4-source.tar.gz* is a source release that
> comes
> > with INSTALL instructions.
> > - *apache_airflow_ctl-0.1.4.tar.gz* is the binary Python "sdist"
> release.
> > - *apache_airflow_ctl-0.1.4-py3-none-any.whl* is the binary Python
> wheel
> > "binary" release.
> >
> > Public keys are available at:
> > https://dist.apache.org/repos/dist/release/airflow/KEYS
> >
> > Please vote accordingly:
> >
> > [ ] +1 approve
> > [ ] +0 no opinion
> > [ ] -1 disapprove with the reason
> >
> > Only votes from PMC members are binding, but all members of the
> community are
> > encouraged to test the release and vote with "(non-binding)".
> >
> > The test procedure for PMC members is described in:
> >
> https://github.com/apache/airflow/blob/main/dev/README_RELEASE_AIRFLOWCTL.md#verify-the-release-candidate-by-pmc-members
> >
> > The test procedure for contributors and members of the community who
> would
> > like to test this RC is described in:
> >
> https://github.com/apache/airflow/blob/main/dev/README_RELEASE_AIRFLOWCTL.md#verify-the-release-candidate-by-contributors
> >
> > Please note that the version number excludes the 'rcX' string, so it's
> now
> > simply 0.1.4 for the apache-airflow-ctl package. This will allow us to
> rename
> > the artifact without modifying the artifact checksums when we actually
> release.
> >
> > Testing status issue:
> > https://github.com/apache/airflow/issues/65643
> >
> > *Docs* (for preview):
> >
> https://airflow.staged.apache.org/docs/apache-airflow-ctl/0.1.4/index.html
> >
> > *Release Notes*:
> >
> https://github.com/apache/airflow/blob/airflow-ctl/0.1.4rc3/airflow-ctl/RELEASE_NOTES.rst
> >
> > *Testing Instructions using PyPI*:
> >
> > The packages are available in PyPI:
> > https://pypi.org/project/apache-airflow-ctl/0.1.4rc3/
> >
> > You can build a virtualenv that installs this and other required packages
> > like this:
> >
> > uv venv
> > uv pip install -U apache-airflow-ctl==0.1.4rc3
> >
> > Regards,
> > Jarek
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail:[email protected]
> > For additional commands, e-mail:[email protected]
> >