This is an automated email from the ASF dual-hosted git repository.

Lee-W pushed a commit to branch airflow-ctl/v0-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/airflow-ctl/v0-1-test by this 
push:
     new 566c59e8095 Run CI on airflow-ctl release-branch pull requests and 
pushes (#70724)
566c59e8095 is described below

commit 566c59e809553f2c41b84e8bf409063839ebedbd
Author: Wei Lee <[email protected]>
AuthorDate: Mon Oct 26 10:39:01 2026 +0800

    Run CI on airflow-ctl release-branch pull requests and pushes (#70724)
    
    Co-authored-by: Shahar Epstein <[email protected]>
---
 .github/workflows/codeql-analysis.yml              | 14 +++-
 .../airflowctl_tests/test_airflowctl_commands.py   | 45 ++++++++----
 .../airflowctl/api/datamodels/asset_state_store.py | 83 ++++++++++++++++++++++
 .../src/airflowctl/api/datamodels/generated.py     | 57 ---------------
 airflow-ctl/src/airflowctl/api/operations.py       |  8 ++-
 .../tests/airflow_ctl/api/test_operations.py       |  6 +-
 6 files changed, 135 insertions(+), 78 deletions(-)

diff --git a/.github/workflows/codeql-analysis.yml 
b/.github/workflows/codeql-analysis.yml
index a6ae94fd3fa..9ed0c7fa890 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -20,9 +20,19 @@ name: "CodeQL"
 
 on:  # yamllint disable-line rule:truthy
   pull_request:
-    branches: ['main', 'v[0-9]+-[0-9]+-test', 'v[0-9]+-[0-9]+-stable']
+    branches:
+      - main
+      - v[0-9]+-[0-9]+-test
+      - v[0-9]+-[0-9]+-stable
+      - chart/v[0-9]+-[0-9]+x-test
+      - chart/v[0-9]+-[0-9]+x-stable
+      - airflow-ctl/v[0-9]+-[0-9]+-test
+      - airflow-ctl/v[0-9]+-[0-9]+-stable
   push:
-    branches: [main]
+    branches:
+      - main
+      - chart/v[0-9]+-[0-9]+x-test
+      - airflow-ctl/v[0-9]+-[0-9]+-test
   schedule:
     - cron: '0 2 * * *'
 
diff --git 
a/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py 
b/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
index 4c475ddd550..7862b163e59 100644
--- a/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
+++ b/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
@@ -56,12 +56,6 @@ TEST_COMMANDS = [
     "assets list",
     "assets get 1",
     "assets create-event --asset-id=1",
-    "assets set-state-store 1 test_key test_value",
-    "assets get-state-store 1 test_key",
-    "assets list-state-store 1",
-    "assets delete-state-store 1 test_key",
-    "assets set-state-store 1 clear_key value",
-    "assets clear-state-store 1",
     # Backfill commands
     "backfill list example_bash_operator",
     # Config commands
@@ -140,6 +134,20 @@ TEST_COMMANDS = [
     "plugins list-import-errors",
 ]
 
+# This release branch targets core 3.2.x, which has no asset state store API, 
so these commands stay
+# skipped here. Integration coverage for them lives on `main`.
+ASSET_STATE_STORE_TEST_COMMANDS = [
+    "assets set-state-store 1 test_key test_value",
+    "assets get-state-store 1 test_key",
+    "assets list-state-store 1",
+    "assets delete-state-store 1 test_key",
+    "assets set-state-store 1 clear_key value",
+    "assets clear-state-store 1",
+]
+_ASSET_STATE_STORE_SKIP = pytest.mark.skip(
+    reason="core 3.2.x has no asset state store API; covered on main, skipped 
for this release branch"
+)
+
 NO_AUTH_TEST_COMMANDS = [
     "version --remote",
 ]
@@ -150,12 +158,21 @@ DATE_PARAM_2 = date_param()
 # Unique xcom key per test run to avoid "already exists" errors from leftover 
state
 _XCOM_KEY_1 = f"test_xcom_key_{DATE_PARAM_1.replace(':', '').replace('+', 
'').replace('-', '')[:16]}"
 _XCOM_KEY_2 = f"test_xcom_key_{DATE_PARAM_2.replace(':', '').replace('+', 
'').replace('-', '')[:16]}"
-TEST_COMMANDS_DEBUG_MODE = [LOGIN_COMMAND] + [
-    test.format(date_param=DATE_PARAM_1, xcom_key=_XCOM_KEY_1) for test in 
TEST_COMMANDS
-]
-TEST_COMMANDS_SKIP_KEYRING = [LOGIN_COMMAND_SKIP_KEYRING] + [
-    test.format(date_param=DATE_PARAM_2, xcom_key=_XCOM_KEY_2) for test in 
TEST_COMMANDS
-]
+TEST_COMMANDS_DEBUG_MODE = (
+    [LOGIN_COMMAND]
+    + [test.format(date_param=DATE_PARAM_1, xcom_key=_XCOM_KEY_1) for test in 
TEST_COMMANDS]
+    + [pytest.param(test, marks=_ASSET_STATE_STORE_SKIP) for test in 
ASSET_STATE_STORE_TEST_COMMANDS]
+)
+TEST_COMMANDS_SKIP_KEYRING = (
+    [LOGIN_COMMAND_SKIP_KEYRING]
+    + [test.format(date_param=DATE_PARAM_2, xcom_key=_XCOM_KEY_2) for test in 
TEST_COMMANDS]
+    + [pytest.param(test, marks=_ASSET_STATE_STORE_SKIP) for test in 
ASSET_STATE_STORE_TEST_COMMANDS]
+)
+
+
+def _command_id(command) -> str:
+    command = command.values[0] if hasattr(command, "values") else command
+    return " ".join(command.split(" ", 2)[:2])
 
 
 def test_hardcoded_xcom_key_would_collide():
@@ -182,7 +199,7 @@ def test_hardcoded_xcom_key_would_collide():
 @pytest.mark.parametrize(
     "command",
     TEST_COMMANDS_DEBUG_MODE,
-    ids=[" ".join(command.split(" ", 2)[:2]) for command in 
TEST_COMMANDS_DEBUG_MODE],
+    ids=[_command_id(command) for command in TEST_COMMANDS_DEBUG_MODE],
 )
 def test_airflowctl_commands(command: str, run_command):
     """Test airflowctl commands using docker-compose environment."""
@@ -192,7 +209,7 @@ def test_airflowctl_commands(command: str, run_command):
 @pytest.mark.parametrize(
     "command",
     TEST_COMMANDS_SKIP_KEYRING,
-    ids=[" ".join(command.split(" ", 2)[:2]) for command in 
TEST_COMMANDS_SKIP_KEYRING],
+    ids=[_command_id(command) for command in TEST_COMMANDS_SKIP_KEYRING],
 )
 def test_airflowctl_commands_skip_keyring(command: str, api_token: str, 
run_command):
     """Test airflowctl commands using docker-compose environment without using 
keyring."""
diff --git a/airflow-ctl/src/airflowctl/api/datamodels/asset_state_store.py 
b/airflow-ctl/src/airflowctl/api/datamodels/asset_state_store.py
new file mode 100644
index 00000000000..d58217ae5c0
--- /dev/null
+++ b/airflow-ctl/src/airflowctl/api/datamodels/asset_state_store.py
@@ -0,0 +1,83 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""
+Hand-written asset state store datamodels.
+
+The 3.2.x core API does not expose the asset state store endpoints, so these 
models cannot be produced
+by ``datamodel-codegen``. They stay hand-written for the life of this release 
branch; the generated
+models live on ``main``.
+"""
+
+from __future__ import annotations
+
+from datetime import datetime
+from enum import Enum
+from typing import Annotated
+
+from pydantic import BaseModel, ConfigDict, Field
+
+from airflowctl.api.datamodels.generated import JsonValue  # noqa: TC001 - 
pydantic needs it at runtime
+
+
+class AssetStateStoreWriterKind(str, Enum):
+    """
+    Identifies what kind of writer last updated an asset state store entry.
+
+    ``TASK`` — written by a task via the execution API.
+    ``WATCHER`` — written by a ``BaseEventTrigger`` (no task instance).
+    ``API`` — written directly through the Core API (e.g. manual admin write).
+    """
+
+    TASK = "task"
+    WATCHER = "watcher"
+    API = "api"
+
+
+class AssetStateStoreBody(BaseModel):
+    """Request body for setting an asset state store value."""
+
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    value: JsonValue
+
+
+class AssetStateStoreLastUpdatedBy(BaseModel):
+    """Writer info for the last write to an asset state store entry."""
+
+    kind: AssetStateStoreWriterKind
+    dag_id: Annotated[str | None, Field(title="Dag Id")] = None
+    run_id: Annotated[str | None, Field(title="Run Id")] = None
+    task_id: Annotated[str | None, Field(title="Task Id")] = None
+    map_index: Annotated[int | None, Field(title="Map Index")] = None
+
+
+class AssetStateStoreResponse(BaseModel):
+    """A single asset state store key/value pair with metadata."""
+
+    key: Annotated[str, Field(title="Key")]
+    value: JsonValue
+    updated_at: Annotated[datetime, Field(title="Updated At")]
+    last_updated_by: AssetStateStoreLastUpdatedBy | None = None
+
+
+class AssetStateStoreCollectionResponse(BaseModel):
+    """All asset state store entries for an asset."""
+
+    asset_state_store: Annotated[list[AssetStateStoreResponse], 
Field(title="Asset State Store")]
+    total_entries: Annotated[int, Field(title="Total Entries")]
diff --git a/airflow-ctl/src/airflowctl/api/datamodels/generated.py 
b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
index df61627c77b..43719cbb096 100644
--- a/airflow-ctl/src/airflowctl/api/datamodels/generated.py
+++ b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
@@ -67,20 +67,6 @@ class BaseInfoResponse(BaseModel):
     status: Annotated[str | None, Field(title="Status")] = None
 
 
-class AssetStateStoreWriterKind(str, Enum):
-    """
-    Identifies what kind of writer last updated an asset state store entry.
-
-    ``TASK`` — written by a task via the execution API.
-    ``WATCHER`` — written by a ``BaseEventTrigger`` (no task instance).
-    ``API`` — written directly through the Core API (e.g. manual admin write).
-    """
-
-    TASK = "task"
-    WATCHER = "watcher"
-    API = "api"
-
-
 class BulkActionNotOnExistence(str, Enum):
     """
     Bulk Action to be taken if the entity does not exist.
@@ -1132,40 +1118,6 @@ class AssetResponse(BaseModel):
     last_asset_event: LastAssetEventResponse | None = None
 
 
-class AssetStateStoreBody(BaseModel):
-    """
-    Request body for setting an asset state store value.
-    """
-
-    model_config = ConfigDict(
-        extra="forbid",
-    )
-    value: JsonValue
-
-
-class AssetStateStoreLastUpdatedBy(BaseModel):
-    """
-    Writer info for the last write to an asset state store entry.
-    """
-
-    kind: AssetStateStoreWriterKind
-    dag_id: Annotated[str | None, Field(title="Dag Id")] = None
-    run_id: Annotated[str | None, Field(title="Run Id")] = None
-    task_id: Annotated[str | None, Field(title="Task Id")] = None
-    map_index: Annotated[int | None, Field(title="Map Index")] = None
-
-
-class AssetStateStoreResponse(BaseModel):
-    """
-    A single asset state store key/value pair with metadata.
-    """
-
-    key: Annotated[str, Field(title="Key")]
-    value: JsonValue
-    updated_at: Annotated[datetime, Field(title="Updated At")]
-    last_updated_by: AssetStateStoreLastUpdatedBy | None = None
-
-
 class BackfillPostBody(BaseModel):
     """
     Object used for create backfill request.
@@ -1912,15 +1864,6 @@ class AssetEventCollectionResponse(BaseModel):
     total_entries: Annotated[int, Field(title="Total Entries")]
 
 
-class AssetStateStoreCollectionResponse(BaseModel):
-    """
-    All asset state store entries for an asset.
-    """
-
-    asset_state_store: Annotated[list[AssetStateStoreResponse], 
Field(title="Asset State Store")]
-    total_entries: Annotated[int, Field(title="Total Entries")]
-
-
 class BackfillCollectionResponse(BaseModel):
     """
     Backfill Collection serializer for responses.
diff --git a/airflow-ctl/src/airflowctl/api/operations.py 
b/airflow-ctl/src/airflowctl/api/operations.py
index 39062ed86a5..fac122ac924 100644
--- a/airflow-ctl/src/airflowctl/api/operations.py
+++ b/airflow-ctl/src/airflowctl/api/operations.py
@@ -25,6 +25,11 @@ import httpx
 import structlog
 from pydantic import BaseModel, ValidationError
 
+from airflowctl.api.datamodels.asset_state_store import (
+    AssetStateStoreBody,
+    AssetStateStoreCollectionResponse,
+    AssetStateStoreResponse,
+)
 from airflowctl.api.datamodels.auth_generated import LoginBody, LoginResponse
 from airflowctl.api.datamodels.generated import (
     AssetAliasCollectionResponse,
@@ -32,9 +37,6 @@ from airflowctl.api.datamodels.generated import (
     AssetCollectionResponse,
     AssetEventResponse,
     AssetResponse,
-    AssetStateStoreBody,
-    AssetStateStoreCollectionResponse,
-    AssetStateStoreResponse,
     BackfillCollectionResponse,
     BackfillPostBody,
     BackfillResponse,
diff --git a/airflow-ctl/tests/airflow_ctl/api/test_operations.py 
b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
index 1cd123ada78..35f59563310 100644
--- a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
+++ b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
@@ -29,6 +29,10 @@ import pytest
 from pydantic import BaseModel
 
 from airflowctl.api.client import Client, ClientKind
+from airflowctl.api.datamodels.asset_state_store import (
+    AssetStateStoreCollectionResponse,
+    AssetStateStoreResponse,
+)
 from airflowctl.api.datamodels.auth_generated import LoginBody, LoginResponse
 from airflowctl.api.datamodels.generated import (
     AssetAliasCollectionResponse,
@@ -36,8 +40,6 @@ from airflowctl.api.datamodels.generated import (
     AssetCollectionResponse,
     AssetEventResponse,
     AssetResponse,
-    AssetStateStoreCollectionResponse,
-    AssetStateStoreResponse,
     BackfillCollectionResponse,
     BackfillPostBody,
     BackfillResponse,

Reply via email to