Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/basic-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Basic CI

on:
push:
branches: [ mainline ]
branches: [ main ]
pull_request:
branches: [ mainline ]
branches: [ main ]

permissions:
contents: read
Expand All @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
python-version: ["3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: "CodeQL"

on:
push:
branches: [ mainline ]
branches: [ main ]
pull_request:
branches: [ mainline ]
branches: [ main ]

jobs:
analyze:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/coverage-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:

- name: Get base branch coverage
run: |
git checkout origin/mainline
git checkout origin/main
pip install -e ".[dev]"
python -m pytest tests/ -m "not integration" --cov=src --cov-report=json:coverage-base.json --tb=no -q || true
BASE_COVERAGE=$(python -c "import json; print(json.load(open('coverage-base.json'))['totals']['percent_covered'])")
Expand Down
4 changes: 4 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
version="1.0.0",
description="Kernel-ci-cloud-labs",
long_description=long_description,
long_description_content_type="text/markdown",
package_dir={"": "src"},
packages=find_packages(where="src"),
python_requires=">=3.11",
Expand All @@ -45,6 +46,9 @@
"isort>=5.0",
"pre-commit>=2.0",
"pytest-cov>=2.0",
"pandas>=1.3.0",
"matplotlib>=3.4.0",
"seaborn>=0.11.0",
],
"analysis": [
"pandas>=1.3.0",
Expand Down
7 changes: 5 additions & 2 deletions src/kernel_ci_cloud_labs/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,11 @@ def main():
val_parser.add_argument("--bucket", help="S3 bucket to verify (and create with --fix)")
val_parser.add_argument("--role", help="IAM role name used by VM instance profiles")
val_parser.add_argument("--region", default="us-west-2", help="AWS region (default: us-west-2)")
val_parser.add_argument("--api-url", help=f"KernelCI API base URI (overrides $KERNELCI_API_BASE_URI)")
val_parser.add_argument("--fix", action="store_true", help="Create missing resources (S3 bucket) instead of just reporting them")
val_parser.add_argument("--api-url", help="KernelCI API base URI (overrides $KERNELCI_API_BASE_URI)")
val_parser.add_argument(
"--fix", action="store_true",
help="Create missing resources (S3 bucket) instead of just reporting them",
)
val_parser.set_defaults(func=cmd_setup_validate)

args = parser.parse_args()
Expand Down
2 changes: 0 additions & 2 deletions src/kernel_ci_cloud_labs/core/artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,5 +262,3 @@ def collect_run_artifacts(
manifest_path,
)
return manifest


3 changes: 1 addition & 2 deletions src/kernel_ci_cloud_labs/pull_labs_poller.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@

from kernel_ci_cloud_labs.kcidb_submit import (
build_test_row,
submit_tests,
to_kcidb_status,
)
from kernel_ci_cloud_labs.pull_labs_translate import translate_job
Expand Down Expand Up @@ -314,7 +313,7 @@ def _validate_default_executor_deps() -> None:

problems: List[str] = []
try:
import boto3 # noqa: F401,PLC0415
import boto3 # noqa: F401,PLC0415 # pylint: disable=unused-import
except ImportError as e:
problems.append(
f"boto3 import failed ({e}) — run: python3.11 -m pip install -e ."
Expand Down
4 changes: 2 additions & 2 deletions src/kernel_ci_cloud_labs/setup_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def _check_bucket_policy_statement(s3, bucket_name: str, fix: bool) -> bool:
print(f"✗ Could not read bucket policy ({code}): {e}")
return False
existing = None
except (ValueError, json.JSONDecodeError) as e:
except ValueError as e:
print(f"✗ Bucket policy is not valid JSON: {e}")
return False

Expand Down Expand Up @@ -365,7 +365,7 @@ def check_kcidb_jwt() -> bool:
# JWT payload is base64url; pad to a multiple of 4 before decoding.
payload_b64 = parts[1] + "=" * (-len(parts[1]) % 4)
payload = json.loads(base64.urlsafe_b64decode(payload_b64))
except (ValueError, json.JSONDecodeError) as e:
except ValueError as e:
print(f"✗ JWT payload not decodable: {e}")
return False

Expand Down
6 changes: 4 additions & 2 deletions tests/test_kcidb_submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,10 @@ def test_submit_revision_posts_with_bearer_auth(self):
class FakeResp:
def __enter__(self):
return self

def __exit__(self, *a):
return False

def read(self):
return b'{"status":"ok","id":"sub-1"}'

Expand Down Expand Up @@ -237,7 +239,7 @@ def __enter__(self): return self
def __exit__(self, *a): return False
def read(self): return b'{}'

def fake_urlopen(req, timeout=None):
def fake_urlopen(req, timeout=None): # pylint: disable=unused-argument
captured["body"] = json.loads(req.data.decode("utf-8"))
return FakeResp()

Expand All @@ -264,7 +266,7 @@ def __enter__(self): return self
def __exit__(self, *a): return False
def read(self): return b'{}'

def fake_urlopen(req, timeout=None):
def fake_urlopen(req, timeout=None): # pylint: disable=unused-argument
captured["body"] = json.loads(req.data.decode("utf-8"))
return FakeResp()

Expand Down
57 changes: 22 additions & 35 deletions tests/test_pull_labs_poller.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2026 Collabora Limited
# pylint: disable=protected-access
# Author: Denys Fedoryshchenko <denys.f@collabora.com>

"""Unit tests for pull_labs_poller (no network, no AWS)."""

import json
import logging
import os
import tempfile
import urllib.error
from unittest.mock import patch

Expand Down Expand Up @@ -626,14 +626,16 @@ class TestProcessEventNodeResult:
def _run(self, poller, event, translate=None):
translate = translate or {"return_value": {}}
captured = {}
with patch.object(poller, "_claim_node", return_value=True), \
patch.object(
poller, "_finish_node",
side_effect=lambda nid, outcome: captured.update(outcome=outcome),
), \
patch(_GET, return_value={"artifacts": {}}), \
patch("kernel_ci_cloud_labs.pull_labs_poller.translate_job", **translate), \
patch("kernel_ci_cloud_labs.pull_labs_poller.submit_tests", return_value={}):
with (
patch.object(poller, "_claim_node", return_value=True),
patch.object(
poller, "_finish_node",
side_effect=lambda nid, outcome: captured.update(outcome=outcome),
),
patch(_GET, return_value={"artifacts": {}}),
patch("kernel_ci_cloud_labs.pull_labs_poller.translate_job",
**translate),
):
poller.process_event(event)
return captured["outcome"]

Expand Down Expand Up @@ -682,9 +684,10 @@ def test_translate_failure_finishes_invalid_job_params(self):
assert "missing artifacts.kernel" in outcome.error_msg

def test_per_instance_rows_carry_log_url_and_stable_test_id(self):
"""When executor returns per-instance rows with log_url, the submitted
KCIDB rows must each carry that URL and a test_id derived from the
instance_id (not the positional index)."""
"""When executor returns per-instance rows with log_url, the node
outcome must carry those URLs in artifacts.test_log (first URL) and
test_log_N (subsequent URLs), and the node result must reflect the
aggregated per-instance statuses."""
per_test = [
{"name": "boot", "status": "PASS", "instance_id": "i-aaaa1111",
"log_url": "https://b.s3.eu-west-1.amazonaws.com/a.log"},
Expand All @@ -695,31 +698,15 @@ def test_per_instance_rows_carry_log_url_and_stable_test_id(self):
_minimal_kc(),
job_executor=lambda cfg: (per_test, None),
)
seen = {}
with patch.object(p, "_claim_node", return_value=True), \
patch.object(p, "_finish_node"), \
patch(_GET, return_value={"artifacts": {}}), \
patch("kernel_ci_cloud_labs.pull_labs_poller.translate_job",
return_value={}), \
patch(
"kernel_ci_cloud_labs.pull_labs_poller.submit_tests",
side_effect=lambda url, jwt, origin, build_id, rows: seen.update(rows=rows),
):
p.process_event(_job_event(node_id="ndX"))

rows = seen["rows"]
assert len(rows) == 2
by_id = {r["id"]: r for r in rows}
# test_id derived from instance_id => stable across retries.
assert set(by_id) == {"pullab_cloud_aws:ndX.i-aaaa1111", "pullab_cloud_aws:ndX.i-bbbb2222"}
# Per-row log_url survives the build_test_row pass-through.
assert by_id["pullab_cloud_aws:ndX.i-aaaa1111"]["log_url"] == \
outcome = self._run(p, _job_event(node_id="ndX"))

# One FAIL among results -> overall node result is "fail".
assert outcome.result == "fail"
# Log URLs are attached to the outcome artifacts for send_kcidb.
assert outcome.artifacts["test_log"] == \
"https://b.s3.eu-west-1.amazonaws.com/a.log"
assert by_id["pullab_cloud_aws:ndX.i-bbbb2222"]["log_url"] == \
assert outcome.artifacts["test_log_1"] == \
"https://b.s3.eu-west-1.amazonaws.com/b.log"
# instance_id surfaces in misc for traceability.
assert by_id["pullab_cloud_aws:ndX.i-aaaa1111"]["misc"]["instance_id"] == "i-aaaa1111"
# Aggregated node outcome from per-instance statuses.
# (one fail among two -> fail; verified indirectly via existing tests).


Expand Down
1 change: 0 additions & 1 deletion tests/test_pull_labs_translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from kernel_ci_cloud_labs.pull_labs_translate import (
DEFAULT_PLATFORM_MAP,
DEFAULT_TEST_TYPE_MAP,
translate_job,
)

Expand Down
1 change: 1 addition & 0 deletions tests/test_role_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Unit tests for AWS Role Manager"""
# pylint: disable=protected-access

__authors__ = ["Max Hubmann <mxhbm@amazon.de>", "Norbert Manthey <nmanthey@amazon.de>"]
__copyright__ = "Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved."
Expand Down
Loading