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