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