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
9 changes: 9 additions & 0 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Self-hosted runner labels available to this repository, so actionlint does not
# report them as unknown. These are ARC scale sets in the `public` runner group
# (StackVista/argocd-apps, cluster_definitions/tooling-main/apps/github-runner-*);
# the `-public` tier is the one a PUBLIC repository is allowed to schedule on.
self-hosted-runner:
labels:
- docker-public
- xlarge-public
- arm64-xlarge-public
128 changes: 128 additions & 0 deletions .github/scripts/select-checks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/usr/bin/env bash
#
# Selects which integration check suites the test matrix should run, reproducing
# the `changes:` rules that gated each `test_<check>` job in .gitlab-ci.yml
# (GitLab -> GitHub migration, STAC-25463).
#
# GitLab evaluated a per-job `changes:` list; GitHub has no job-level path filter,
# so the equivalent is computed once here and fanned out as a matrix. This is done
# in plain git rather than a path-filter action: StackVista enforces a strict
# third-party action allowlist, and `git diff` against the merge base is exactly
# what the GitLab rule meant.
#
# Selection rules, ported from .gitlab-ci.yml:
# * A change to a shared library, the setup scripts, or this CI wiring runs
# EVERY suite (GitLab: the `base_changes` anchor).
# * Otherwise only the suites whose own directory changed run.
# * GitLab's `splunk_base_build_rule` -- a change to splunk_base also runs the
# other three splunk suites, which import its test helpers -- is not ported
# here because no splunk suite runs yet. It lands with them in phase 2.
# * push / workflow_dispatch run everything (GitLab: `master_branch`,
# `release_branch`).
#
# Writes `checks=<json array>` to $GITHUB_OUTPUT for `fromJson()` in a matrix.

set -euo pipefail

# Suites currently running on GitHub Actions. Phase 1 is the 15 pure-Python
# suites: they need no Docker daemon, so they are the low-risk half of the port.
#
# Deliberately NOT here yet (phase 2, needs the DinD story exercised first):
# splunk_base, splunk_health, splunk_metric, splunk_topology
# -- each drives a real Splunk container via docker-compose.
# stackstate_checks_dev
# -- its tests exercise the toolkit's own Docker helpers.
# Both public ARC runners provide a DinD sidecar, so this is a matter of proving
# it rather than provisioning anything.
#
# Deliberately dropped, not pending:
# postgres -- .gitlab-ci.yml carried a `test_postgres` job for a check that does
# not exist in this repository. It is dead config, not a gap.
CHECKS=(
agent_integration_sample
agent_v2_integration_sample
agent_v2_integration_stateful_sample
agent_v2_integration_transactional_sample
dynatrace_base
dynatrace_health
dynatrace_topology
kubelet
openmetrics
servicenow
stackstate_checks_base
static_health
static_topology
vsphere
zabbix
)

# A change anywhere here invalidates every suite: the base classes and the test
# helpers are imported by all of them, and the setup scripts build the venv the
# suites run in.
SHARED_PATHS=(
stackstate_checks_base/
stackstate_checks_dev/
stackstate_checks_tests_helper/
.setup-scripts/
.github/workflows/checks-tests.yml
.github/scripts/select-checks.sh
)

emit() {
local -a selected=("$@")
local json
if [ "${#selected[@]}" -eq 0 ]; then
json="[]"
else
json=$(printf '%s\n' "${selected[@]}" | sort -u | jq -R . | jq -c -s .)
fi
echo "checks=${json}" >>"${GITHUB_OUTPUT}"
echo "Selected suites: ${json}"
}

# Anything that is not a pull request is a full run. On the release branch the
# whole matrix is the point (it is what Cerberus reports on), and a manual
# dispatch is an explicit request for everything.
if [ "${EVENT_NAME}" != "pull_request" ]; then
echo "Event '${EVENT_NAME}' is not a pull request: running every suite."
emit "${CHECKS[@]}"
exit 0
fi

# Diffing against the merge base keeps a stale base branch from dragging
# unrelated commits into the change set.
MERGE_BASE=$(git merge-base "origin/${BASE_REF}" HEAD)
mapfile -t CHANGED < <(git diff --name-only "${MERGE_BASE}" HEAD)

echo "Changed files (${#CHANGED[@]}) against ${BASE_REF} @ ${MERGE_BASE}:"
printf ' %s\n' "${CHANGED[@]}"

matches_prefix() {
local file=$1 prefix
shift
for prefix in "$@"; do
case "${file}" in
"${prefix}"*) return 0 ;;
esac
done
return 1
}

for file in "${CHANGED[@]}"; do
if matches_prefix "${file}" "${SHARED_PATHS[@]}"; then
echo "'${file}' is shared CI or library code: running every suite."
emit "${CHECKS[@]}"
exit 0
fi
done

SELECTED=()
for file in "${CHANGED[@]}"; do
for check in "${CHECKS[@]}"; do
if [ "${file#"${check}"/}" != "${file}" ]; then
SELECTED+=("${check}")
fi
done
done

emit "${SELECTED[@]+"${SELECTED[@]}"}"
249 changes: 249 additions & 0 deletions .github/workflows/checks-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
name: Check tests

# Ported from .gitlab-ci.yml as part of the GitLab -> GitHub migration
# (STAC-25142 / STAC-25463), phase 1: the pure-Python check suites.
#
# WHAT MOVED
# linux_deps + the `test_<check>` job family -> the `check-tests` matrix below.
# The per-job `changes:` rules -> .github/scripts/select-checks.sh.
# The validate suite that rode along inside `test_stackstate_checks_base`
# -> its own `validate` job, so a metadata
# failure is legible as its own PR check
# instead of hiding inside a test job.
#
# WHAT IS DELIBERATELY NOT HERE
# splunk_{base,health,metric,topology} and stackstate_checks_dev
# The only five suites that need a Docker daemon (the four splunk suites
# drive a real Splunk container via docker-compose; checks_dev tests the
# toolkit's own Docker helpers). Both public ARC runners ship a DinD
# sidecar, so this is about exercising that path rather than provisioning
# anything -- phase 2. That phase also brings across
# .setup-scripts/setup_artifactory_docker.sh (the registry docker login)
# and COMPOSE_HTTP_TIMEOUT, which only those suites need.
# test_postgres
# Dead config: .gitlab-ci.yml tests a `postgres` check that does not exist
# in this repository. Dropped, not pending.
# print_env
# A bare `printenv`. This repository is PUBLIC, so that job publishes every
# CI credential in scope to a world-readable log. Not ported at any phase;
# `secrets: inherit` is likewise never used here.
# The Windows lane
# There is none to port. `.gitlab-ci.yml` defines a `.windows_env` anchor
# but no job has ever referenced it, and Windows is not a supported target,
# so the orphaned .setup-scripts/conda_env.ps1 + windows_*.cmd helpers can
# be retired with the GitLab pipeline (STAC-25464).
# publish-checks-dev / the runner-image `docker` job
# Publishing needs write credentials this repo does not hold; pulumi-infra
# schedules integrations' publishing role for its section 7.4. Until then
# releases keep running from GitLab, so the GitLab pipeline stays live.
# A Cerberus failure notification
# Unlike stackstate-agent, this pipeline has never had one -- there is no
# notify job in .gitlab-ci.yml and no .cerberus directory -- so adding it
# would be new capability, not a port. It also needs CERBERUS_LAMBDA_URL,
# which is a private-visibility org secret and so unreadable from this
# PUBLIC repo without a pulumi-infra grant. Tracked as a follow-up.
#
# CREDENTIALS
# Pulling the runner image needs vars.REGISTRY_HOST / vars.REGISTRY_USER and
# secrets.REGISTRY_PASSWORD, which are org-level visibility=all (STAC-25350)
# and therefore already reach this PUBLIC repo. Note it composes
# vars.REGISTRY_HOST + /docker rather than using vars.REGISTRY_DOCKER_URL: that
# variable is private-visibility and so is NOT readable here. Org secrets are
# never exposed to fork PRs, hence the same-repo guard on every job that pulls
# the image.
#
# Resolving check requirements additionally needs the private PyPI index, for
# the handful of pins that are not on public PyPI. That is
# vars.GITLAB_PACKAGE_REGISTRY_PYPI_SIMPLE_URL and
# secrets.GITLAB_PACKAGE_REGISTRY_USER, granted to this repo in pulumi-infra
# (StackVista/pulumi-infra#263), alongside the already-org-wide
# secrets.GITLAB_PACKAGE_REGISTRY_READONLY_PASSWORD. Pull-only: this repo's
# *publishing* role is still deferred, per the note above.

on:
pull_request:
# Mirrors GitLab's `master_branch` rule, which hardcoded the release branch the
# same way: the full matrix runs there regardless of what a given commit
# touched, so the branch always has a complete verdict.
push:
branches:
- stackstate-7.78.2
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

defaults:
run:
shell: bash

env:
# Tag of the prebuilt test image. Kept in lockstep with RUNNER_IMAGE_TAG in
# .gitlab-ci.yml until the GitLab pipeline is retired; the image is still built
# and published by that pipeline's `docker` job.
RUNNER_IMAGE_TAG: 20260625-py313

jobs:
select-checks:
name: Select check suites to run
# Not strictly credential-bound, but there is nothing to select for a fork PR
# whose downstream jobs cannot pull the image anyway.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: docker-public
timeout-minutes: 10
outputs:
checks: ${{ steps.select.outputs.checks }}
runner_image: ${{ steps.image.outputs.ref }}
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Full history: the selector diffs against the merge base with the base
# branch, which a shallow clone cannot resolve.
fetch-depth: 0
persist-credentials: false

- name: Resolve the runner image reference
id: image
env:
REGISTRY_HOST: ${{ vars.REGISTRY_HOST }}
run: |
set -euo pipefail
# Same image the GitLab pipeline runs on, via the same read-only proxy:
# GitLab's ${REGISTRY_DOCKER_URL} is registry.tooling.stackstate.io/docker.
echo "ref=${REGISTRY_HOST}/docker/stackstate/stackstate-agent-integrations-runner:${RUNNER_IMAGE_TAG}" >>"${GITHUB_OUTPUT}"

- name: Select check suites
id: select
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
run: |
set -euo pipefail
# The ARC work volume is owned by the runner uid; mark it safe so the
# selector's git calls are not rejected as "dubious ownership".
git config --global --add safe.directory '*'
bash .github/scripts/select-checks.sh

validate:
name: Check metadata validation (checksdev validate)
# Ported from the `checksdev validate *` commands that opened
# test_stackstate_checks_base. Cheap, repo-wide, and independent of the
# matrix, so it runs on every change rather than per suite.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
needs: select-checks
runs-on: docker-public
timeout-minutes: 30
container:
# Tag-pinned rather than digest-pinned: the tag is produced by the GitLab
# `docker` job and updated in lockstep with .gitlab-ci.yml, so pinning a
# digest here would silently drift from the pipeline that builds it.
image: ${{ needs.select-checks.outputs.runner_image }} # zizmor: ignore[unpinned-images]
credentials:
username: ${{ vars.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Build the toolchain virtualenv
run: |
set -eo pipefail
git config --global --add safe.directory '*'
# Creates venv/ and installs checksdev; the GitLab `linux_deps` job did
# this once and shipped venv/ as an artifact. Here each job builds its
# own: the matrix legs run in parallel, so repeating it costs runner
# time but no wall-clock, and it avoids relocating a venv through the
# artifact store. Worth revisiting with real timings, the way the
# agent's cache image was justified (STAC-25429).
source .setup-scripts/setup_env.sh

- name: checksdev validate
run: |
set -eo pipefail
source venv/bin/activate
checksdev validate config
checksdev validate dep
checksdev validate manifest --include-extras
checksdev validate metadata
checksdev validate service-checks

check-tests:
name: Check tests (${{ matrix.check }})
if: >-
${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
&& needs.select-checks.outputs.checks != '[]' }}
needs: select-checks
runs-on: docker-public
timeout-minutes: 45
strategy:
# One suite's failure should not mask the state of the other fourteen.
fail-fast: false
matrix:
check: ${{ fromJson(needs.select-checks.outputs.checks) }}
container:
image: ${{ needs.select-checks.outputs.runner_image }} # zizmor: ignore[unpinned-images]
credentials:
username: ${{ vars.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Build the toolchain virtualenv
run: |
set -eo pipefail
git config --global --add safe.directory '*'
source .setup-scripts/setup_env.sh

- name: Configure the private PyPI index (GitLab Package Registry)
env:
GITLAB_PACKAGE_REGISTRY_PYPI_SIMPLE_URL: ${{ vars.GITLAB_PACKAGE_REGISTRY_PYPI_SIMPLE_URL }}
GITLAB_PACKAGE_REGISTRY_USER: ${{ secrets.GITLAB_PACKAGE_REGISTRY_USER }}
GITLAB_PACKAGE_REGISTRY_READONLY_PASSWORD: ${{ secrets.GITLAB_PACKAGE_REGISTRY_READONLY_PASSWORD }}
# A few checks pin wheels that are not on public PyPI and are served from the
# central registry instead (currently only vsphere, which pins
# vsphere-automation-sdk). This mirrors what the GitLab `.linux_test` anchor did
# immediately before `checksdev test`. The script writes ~/.pip/pip.conf and a
# 0600 ~/.netrc, keeping credentials out of the index URL.
#
# Guarded rather than unconditional: only one suite actually needs the private
# index, so a missing credential should not fail the fourteen that resolve
# everything from public PyPI. When it is missing we say so loudly, and the
# affected suite still fails visibly on its own unresolvable pin.
run: |
set -eo pipefail
if [ -z "${GITLAB_PACKAGE_REGISTRY_PYPI_SIMPLE_URL}" ] \
|| [ -z "${GITLAB_PACKAGE_REGISTRY_USER}" ] \
|| [ -z "${GITLAB_PACKAGE_REGISTRY_READONLY_PASSWORD}" ]; then
echo "::warning title=Private PyPI index not configured::GitLab Package Registry credentials are not available to this job. Checks that pin wheels absent from public PyPI (currently 'vsphere') will fail to resolve their requirements."
exit 0
fi
.setup-scripts/setup_artifact_registry.sh

- name: checksdev test ${{ matrix.check }}
env:
CHECK: ${{ matrix.check }}
run: |
set -eo pipefail
source venv/bin/activate
checksdev test --cov "${CHECK}"

- name: checksdev benchmarks ${{ matrix.check }}
env:
CHECK: ${{ matrix.check }}
# Non-blocking, matching GitLab's `|| true`: benchmarks are reported for
# information and have never gated a merge.
continue-on-error: true
run: |
set -eo pipefail
source venv/bin/activate
checksdev test "${CHECK}" --bench
Loading