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]