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]
> > > >> >
> > > >
> > > >
> > >
> >
>

Reply via email to