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]

Reply via email to