Skip to content
Draft
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
50 changes: 50 additions & 0 deletions .claude/skills/check-ci/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
name: check-ci
description: Fetch and report CI results for a silk PR. Use when the user asks to investigate, address, or fix CI failures, or refers to a PR without specifying what's broken.
---

Always fetch the CI result JSON before reading code or proposing fixes.

## Determining PR, sha, and workflow name

**From a Praktika report URL** (e.g. `https://silk-artifacts-eu-north-1.s3.amazonaws.com/json.html?PR=12&sha=abc123&name_0=Pull%20Request%20CI`):
- Extract `PR`, `sha`, `name_0` from query params
- Normalize `name_0`: lowercase + spaces → underscores (e.g. `"Pull Request CI"` → `"pull_request_ci"`)

**From a bare PR number:**
- Run `gh pr view {PR} --json headRefOid --jq '.headRefOid'` to get the latest sha
- Use `pull_request_ci` as the normalized workflow name

## Building the JSON URL

```
https://silk-artifacts-eu-north-1.s3.amazonaws.com/PRs/{PR}/{sha}/{normalized}/result_{normalized}.json
```

Fetch this URL with WebFetch.

## Result structure

The JSON is a serialized `praktika.Result`:

```
{
"name": str,
"status": str, # OK | FAIL | ERROR | SKIPPED | UNKNOWN | XFAIL | XPASS | PENDING | RUNNING | DROPPED
"start_time": float?,
"duration": float?,
"info": str,
"results": [...], # nested Result objects, same shape, recursive
"files": [...],
"links": [...],
"ext": {
"labels": [{"name": str, "link": str?, "hint": str?}, ...],
"warnings": [...],
"errors": [...],
"report_url": str?,
...
}
}
```

Walk the `results` tree recursively to find all failing jobs and sub-jobs. Use the `info` field of failing nodes as the primary signal for what went wrong before touching any code.
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

jobs:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
.tools/
build/
compile_commands.json
ci/tmp/
259 changes: 259 additions & 0 deletions ci/infrastructure/projects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
from ci.settings.settings import PROJECT_NAME, PROJECT_SLUG, PRAKTIKA_BASE_VENV
from praktika.infrastructure import Components, Storage, VPC
from praktika.infrastructure.cloud import CloudInfrastructure


# until published in pip
_PRAKTIKA_WHL = "https://praktika-artifacts-eu-north-1.s3.amazonaws.com/packages/praktika-0.1.3-py3-none-any.whl"
_PRAKTIKA_CONTROLLER_WHL = "https://praktika-artifacts-eu-north-1.s3.amazonaws.com/packages/praktika_controller-0.1.1-py3-none-any.whl"


def _runner_user_data():
return "\n".join(
[
"#!/usr/bin/env bash",
"set -xeuo pipefail",
"",
"# Force-reinstall praktika so the runner uses the version pinned here",
"# rather than whatever was baked into the AMI at image-build time.",
(
f"/opt/praktika/base-venvs/{PRAKTIKA_BASE_VENV}/bin/python "
f"-m pip install --force-reinstall {_PRAKTIKA_WHL}"
),
"# Add any host customization you need above this line.",
"/usr/local/bin/praktika-configure-cloudwatch-agent",
"/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/etc/praktika/amazon-cloudwatch-agent.json -s",
"systemctl enable --now praktika-controller",
"",
]
)


def _silk_ci_dependencies_component():
return {
"name": "silk-ci-dependencies",
"platform": "Linux",
"description": "Install Silk CI toolchains and build dependencies",
"commands": [
"export DEBIAN_FRONTEND=noninteractive",
(
"wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key "
"> /etc/apt/trusted.gpg.d/apt.llvm.org.asc"
),
(
". /etc/os-release && echo \"deb http://apt.llvm.org/"
"${VERSION_CODENAME}/ llvm-toolchain-${VERSION_CODENAME}-21 main\" "
"> /etc/apt/sources.list.d/llvm-21.list"
),
(
"wget -qO- https://apt.kitware.com/keys/"
"kitware-archive-latest.asc > /etc/apt/trusted.gpg.d/kitware.asc"
),
(
". /etc/os-release && echo \"deb https://apt.kitware.com/ubuntu/ "
"${VERSION_CODENAME} main\" > /etc/apt/sources.list.d/kitware.list"
),
(
"DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=60 "
"install -y software-properties-common"
),
"add-apt-repository -y ppa:ubuntu-toolchain-r/test",
"apt-get -o DPkg::Lock::Timeout=60 update -q",
(
"DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=60 "
"install -y clang-21 clang-format-21 llvm-21 cmake "
"libstdc++-13-dev ninja-build ccache libboost-dev "
"libdouble-conversion-dev libelf-dev zlib1g-dev"
),
],
}


def _silk_ci_image_test_component():
return Components.create_image_test_component(
name="silk-ci-image-test",
description="Validate Silk CI toolchains and build dependencies",
commands=[
"test -d /opt/praktika/work",
"test -w /opt/praktika/work",
"test -x /usr/bin/clang-21",
"test -x /usr/bin/clang-format-21",
"test -x /usr/bin/cmake",
"test -x /usr/bin/ninja",
"test -x /usr/bin/ccache",
"clang-21 --version",
"clang-format-21 --version",
"cmake --version",
"ninja --version",
"ccache --version",
(
"for package in llvm-21 libstdc++-13-dev libboost-dev "
"libdouble-conversion-dev libelf-dev zlib1g-dev; do "
"dpkg-query -W -f='${Status}\\n' \"$package\" "
"| grep -qx 'install ok installed'; done"
),
],
)


def _praktika_controller_image_test_component():
controller = "praktika-controller"
start_script = f"/usr/local/bin/${{controller}}-start"
service_unit = f"/etc/systemd/system/${{controller}}.service"
return Components.create_image_test_component(
name="silk-praktika-controller-image-test",
description="Validate Praktika controller runtime and boot wiring",
commands=[
f"controller={controller}; command -v \"$controller\"",
(
f"controller={controller}; "
"python3.12 -m pip show \"$controller\""
),
f"controller={controller}; test -x {start_script}",
f"controller={controller}; bash -n {start_script}",
f"controller={controller}; test -f {service_unit}",
(
f"controller={controller}; "
f"grep -qx \"ExecStart={start_script}\" {service_unit}"
),
(
f"controller={controller}; "
f"grep -qx \"StandardOutput=append:/var/log/${{controller}}.log\" {service_unit}"
),
(
f"controller={controller}; "
f"grep -qx \"StandardError=append:/var/log/${{controller}}.log\" {service_unit}"
),
(
"test -x /usr/local/bin/praktika-configure-cloudwatch-agent "
"&& bash -n /usr/local/bin/praktika-configure-cloudwatch-agent"
),
"test -x /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl",
],
)


def _silk_ci_image_components():
return [
_silk_ci_dependencies_component(),
_silk_ci_image_test_component(),
_praktika_controller_image_test_component(),
]


def _image_builders():
image_recipe_version = "1.0.7"
prebuilt_venvs = [
Components.create_praktika_venv_config(
PRAKTIKA_BASE_VENV,
"0.1.3",
),
]
return [
Components.create_ubuntu_image_builder_config(
name="ci-arm64-image",
version=image_recipe_version,
controller_package=_PRAKTIKA_CONTROLLER_WHL,
prebuilt_venvs=prebuilt_venvs,
instance_types=["t4g.small"],
components=_silk_ci_image_components(),
),
Components.create_ubuntu_image_builder_config(
name="ci-x86_64-image",
version=image_recipe_version,
controller_package=_PRAKTIKA_CONTROLLER_WHL,
prebuilt_venvs=prebuilt_venvs,
instance_types=["t3.small"],
components=_silk_ci_image_components(),
),
]


_GH_TOKEN_MINTER = Components.GitHubTokenMinter(
repositories=[PROJECT_NAME],
)
_IMAGE_BUILDERS = _image_builders()
_IMAGE_BUILDERS_BY_NAME = {builder.name: builder for builder in _IMAGE_BUILDERS}

PROJECTS = [
CloudInfrastructure.Config(
name=PROJECT_NAME,
min_praktika_version="0.1.3",
vpcs=[
VPC.Config(
subnets=[
VPC.Subnet(availability_zone="eu-north-1a"),
],
)
],
storages=[
Storage.Config(
name="artifacts-eu-north-1",
retention_days=30,
public=True,
),
],
report_pages=[Components.report_page_config],
image_builders=_IMAGE_BUILDERS,
github_token_minters=[_GH_TOKEN_MINTER],
orchestrator_pool=Components.OrchestratorPool(
instance_type="t4g.small",
scaling=Components.OrchestratorPool.Scaling.Auto,
size=0,
max_size=50,
capacity_reserve=1,
volume_size_gb=100,
image_builder=_IMAGE_BUILDERS_BY_NAME["ci-arm64-image"],
ext={"allowed_push_branches": ["main", "add-praktika-ci-config"]}
),
runner_pools=[
Components.RunnerPool(
name="arm-small",
instance_type="t4g.medium",
scaling=Components.RunnerPool.Scaling.Auto,
size=0,
max_size=50,
volume_size_gb=100,
image_builder=_IMAGE_BUILDERS_BY_NAME["ci-arm64-image"],
user_data=_runner_user_data(),
),
Components.RunnerPool(
name="amd-small",
instance_type="t3.medium",
scaling=Components.RunnerPool.Scaling.Auto,
size=0,
max_size=50,
volume_size_gb=100,
image_builder=_IMAGE_BUILDERS_BY_NAME["ci-x86_64-image"],
user_data=_runner_user_data(),
),
Components.RunnerPool(
name="arm-medium",
instance_type="c7g.4xlarge",
scaling=Components.RunnerPool.Scaling.Auto,
size=0,
max_size=50,
volume_size_gb=100,
image_builder=_IMAGE_BUILDERS_BY_NAME["ci-arm64-image"],
),
Components.RunnerPool(
name="arm-large",
instance_type="c7g.8xlarge",
scaling=Components.RunnerPool.Scaling.Auto,
size=0,
max_size=50,
volume_size_gb=100,
image_builder=_IMAGE_BUILDERS_BY_NAME["ci-arm64-image"],
),
Components.RunnerPool(
name="amd-medium",
instance_type="c7a.4xlarge",
scaling=Components.RunnerPool.Scaling.Auto,
size=0,
max_size=50,
volume_size_gb=100,
image_builder=_IMAGE_BUILDERS_BY_NAME["ci-x86_64-image"],
),
],
)
]
7 changes: 7 additions & 0 deletions ci/jobs/fmt_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from praktika.result import Result

if __name__ == "__main__":
Result.from_commands_run(
name="Check formatting",
command=["./bb fmt --check"],
).complete_job()
52 changes: 52 additions & 0 deletions ci/jobs/init_submodules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import subprocess

from praktika.info import Info


COMMON_SUBMODULES = [
"contrib/benchmark",
"contrib/bpftool",
"contrib/cxxopts",
"contrib/googletest",
"contrib/libbacktrace",
"contrib/libbpf",
"contrib/librseq",
"contrib/liburing",
]

EXTRA_SUBMODULES_BY_BUILD = {
"release": ["contrib/poco", "contrib/jemalloc"],
"tsan": ["contrib/poco"],
"msan": ["contrib/llvm-project"],
}


def run(*args):
print("+", " ".join(args), flush=True)
subprocess.run(args, check=True)


def checkout_submodules(paths):
run(
"git",
"submodule",
"update",
"--init",
"--no-fetch",
"--depth=1",
"--jobs",
"8",
*paths,
)


if __name__ == "__main__":
job_name = Info().job_name

run("git", "submodule", "sync")
checkout_submodules(COMMON_SUBMODULES)

for build, paths in EXTRA_SUBMODULES_BY_BUILD.items():
if f"({build})" in job_name:
checkout_submodules(paths)
break
Loading