Hi Jarek,

second attempt, better results! Thanks for fixing!

+1 (binding) - Checked SVN, Checksums, Reproducible package build, Licenses, Signatures

On 23.04.26 21:32, Jarek Potiuk wrote:
Oh... I am glad it worked this time :) !!! :D Thanks Wei Lee,  Buğra... I
was afraid something more was broken :)

J.


On Thu, Apr 23, 2026 at 9:29 PM Buğra Öztürk <[email protected]>
wrote:

+1 (binding), I have checked the following.
- SVN
- Reproducible package build
- Licenses
- Signatures
- Checksums

I ran a couple of airflowctl management commands with 3.2.1, looks good.

Bugra Ozturk
Kind regards,

On Thu, Apr 23, 2026 at 12:18 PM Wei Lee <[email protected]> wrote:

+1 (binding), checked:

- Reproducible build
- SVN
- Licenses
- Signatures
- Checksums

Best regards,
Wei Lee

Jarek Potiuk <[email protected]> 於 2026年4月23日週四 上午8:23寫道:

Ok. I think those were accidentally uncommitted binaries - and instead
of
rc3, I still had the old rc2 :(
I think I missed an error when copying files.

It's fantastic that we have reproducibility checks - otherwise we might
not
have found it. Thanks for flagging it Jens!

It should all be fixed now. The PyPI packages were correct.

Let me restart the voting. The new date for the Vote passing is Monday,
2026-04-27 23:59 UTC.

Consider this my (binding) +1.







On Wed, Apr 22, 2026 at 8:13 PM Jarek Potiuk <[email protected]> wrote:

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]



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to