Skip to content

ci: weekly drift check between /stats and upstream mapping files - #21001

Draft
carsonip wants to merge 11 commits into
elastic:mainfrom
carsonip:drift-cron
Draft

ci: weekly drift check between /stats and upstream mapping files#21001
carsonip wants to merge 11 commits into
elastic:mainfrom
carsonip:drift-cron

Conversation

@carsonip

@carsonip carsonip commented Apr 28, 2026

Copy link
Copy Markdown
Member

Motivation/summary

Implements the scheduled drift-detection job proposed in #21000 — the part of that issue that catches drift between apm-server's /stats endpoint and the five upstream mapping files when nobody has manually run the regen tool. The active-developer side of the same issue is implemented in elastic/apm-tools#245; this PR is the CI side.

How and when it runs

A single new workflow at .github/workflows/stats-to-mapping-drift.yml. Triggers:

  • schedule: cron: '0 6 * * MON' — every Monday at 06:00 UTC.
  • workflow_dispatch — on-demand from the Actions tab or gh workflow run.

Each run, in order:

  1. Builds apm-server from the workflow's checked-out commit (go build ./x-pack/apm-server).
  2. Installs the regen tool: go install github.com/elastic/apm-tools/cmd/stats-to-mapping@latest.
  3. Starts apm-server with TBS enabled and captures /stats from the http.enabled endpoint, then stops apm-server. The captured stats.json is the source of truth for the comparison.
  4. Sparse-clones the three upstream repos (elastic/elasticsearch, elastic/beats, elastic/integrations) with --depth 1 --filter=blob:none and sparse-checkout, so only the relevant subtrees materialise.
  5. Runs the regen tool over the five tracked files, then git diffs each one to detect drift.

UPSTREAM_FILES (job-level env) is the single source of truth for the file list: the sparse-checkout, the regen invocation, the drift loop, and the recipe in the issue body all derive from it. Adding or removing a tracked file means editing one list.

The apm-server config used to capture /stats is written to a file in the CI run and then read back (with path.data rewritten) when generating the recipe, so the issue's repro recipe and the CI run cannot disagree about what config produced the captured /stats.

What the run produces

If no file drifted, the workflow exits green with no other output.

If at least one file drifted:

  • Workflow artifact drift-diff (30-day retention) — full git diff of every drifted file, plus the captured stats.json.
  • Issue with labels bug + stats-to-mapping-drift, body containing:
    • One-line summary stating how many files drifted and linking the workflow run.
    • Per-file change summary (<repo>/<path> — +N / -M).
    • A copy-pasteable local reproduction recipe (apm-server build, /stats capture, three upstream clones, regen invocation).

The issue is opened only if no open issue with the stats-to-mapping-drift label already exists. A sustained drift event therefore produces one issue, not a weekly stream of duplicates.

Permissions

  • Workflow level: contents: read.
  • drift-check job: contents: read plus issues: write (required only to file the drift issue). No write access to the codebase, no PR creation, no other scopes.

Checklist

How to test these changes

After merge, trigger the workflow manually from the Actions tab (workflow_dispatch). With current upstream state and a /stats capture from main, the workflow is expected to detect drift and file one issue.

Locally, run the recipe written into a generated drift issue's body to obtain the same diffs.

Related issues

carsonip and others added 2 commits April 28, 2026 15:00
Adds a Monday 06:00 UTC GitHub Actions cron that:

  1. Builds apm-server from main and starts it with TBS enabled, then
     captures /stats from the http.enabled endpoint.
  2. Sparse-clones the three upstream repos (elastic/elasticsearch,
     elastic/beats, elastic/integrations) and runs the
     elastic/apm-tools stats-to-mapping regen tool over the five
     downstream mapping files.
  3. Computes a per-file git diff. If any file changed, uploads the
     full diff and the stats.json as a workflow artifact.
  4. Opens a single tracking issue (labels: bug,
     monitoring-metrics-drift) with a per-file change summary, the
     workflow-run URL, and a copy-pasteable local reproduction
     recipe. If an open issue with the monitoring-metrics-drift label
     already exists, no new issue is created so a single drift event
     does not produce a weekly stream of duplicates.

Workflow uses minimal permissions: contents: read for checkout, and
issues: write only on the drift-check job. workflow_dispatch is
exposed so the job can be run on demand.

Part of elastic#21000.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Consolidate on a single name across the tool, the workflow, and the
issue label. The tool is "stats-to-mapping"; the workflow and the
label drop the "monitoring-metric" prefix to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🤖 GitHub comments

Just comment with:

  • run docs-build : Re-trigger the docs validation. (use unformatted text in the comment!)

The "Tracked by elastic#21000" line was misleading: elastic#21000 tracks the
broader drift-detection proposal that this workflow implements.
A drift issue this workflow opens is an independent occurrence of
metric drift, not a sub-task of elastic#21000. Remove the cross-reference
from the auto-generated issue body.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mergify

mergify Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

This pull request does not have a backport label. Could you fix it @carsonip? 🙏
To fixup this pull request, you need to add the backport labels for the needed
branches, such as:

  • backport-8.19 is the label to automatically backport to the 8.19 branch.
  • backport-9./d is the label to automatically backport to the 9./d branch. /d is the digit.
  • backport-active-all is the label that automatically backports to all active branches.
  • backport-active-9 is the label that automatically backports to all active minor branches for the 9 major.

…EAM_FILES

UPSTREAM_FILES is now the single source of truth for the five files
the workflow tracks. The sparse-checkout step groups its lines by
repo into a bash associative array; the stats-to-mapping invocation
prefixes each line with $WORK/upstream/ via mapfile; the issue-body
recipe prefixes each line with /tmp/ for the local repro. Adding,
removing, or renaming a tracked file now means editing one list
instead of four.

Drop the per-file cd/cd- dance in the drift loop in favor of git -C.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@carsonip carsonip added the backport-skip Skip notification from the automated backport with mergify label Apr 28, 2026
  - Single source of truth for the apm-server config: write it to a
    file in the CI run, then read it back (with path.data rewritten)
    when generating the recipe in the drift issue. The CI run and the
    recipe can no longer disagree about what config produced the
    captured /stats.

  - Split "Run apm-server, capture /stats" into "Write apm-server
    config" + "Run apm-server, capture /stats". The split makes the
    config the source-of-truth artifact other steps reference.

  - Replace the on-disk PID file with a bash variable plus an EXIT
    trap that cleans up apm-server even if curl fails.

  - Hoist STATS_PORT to a job-level env so the magic 15066 lives in
    one place.

  - Recipe: `export PATH="$PATH:$(go env GOPATH)/bin"` after
    `go install`, since contributors without that already in PATH
    would otherwise see "stats-to-mapping: command not found".

  - Issue title and lead paragraph now state the drifted file count
    (`stats-to-mapping drift in N file(s) (date)`) so a reader
    scanning the issue list grasps the scope without opening it.
    Workflow output renamed `drift` -> `drifted_files` (a count) and
    the conditional steps gate on != '0'.

No behavior change in what's detected or when an issue is opened.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@carsonip
carsonip marked this pull request as ready for review April 28, 2026 14:45
@carsonip
carsonip requested review from a team as code owners April 28, 2026 14:45
@carsonip
carsonip requested a review from ericywl April 28, 2026 14:49

@v1v v1v left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if those steps should be added to a specific Makefile so it can be easy tested locally?

Comment thread .github/workflows/stats-to-mapping-drift.yml Outdated
carsonip and others added 5 commits April 28, 2026 21:18
Co-authored-by: Victor Martinez <victormartinezrubio@gmail.com>
The drift-check job's build / capture / clone / regen / diff steps
were inline shell. Move them into script/stats-to-mapping-drift.sh and
expose a top-level Makefile target stats-to-mapping-drift, so:

  - Local reproduction is a single `make stats-to-mapping-drift`
    invocation. Previously a contributor had to copy the recipe out
    of a generated drift issue.
  - The CI workflow shrinks to GitHub-Actions-specific concerns:
    artifact upload, drift-issue creation. Same source of truth for
    UPSTREAM_FILES, the apm-server config, and the diff format
    whether the run is local or in CI.

The drift issue's reproduction recipe now points at the Makefile
target instead of inlining the apm-server config and clone commands.

Add a "Manual intervention required for new EA fields" section to
the drift issue body. The Elastic Agent integration package data
stream is TSDB; new numeric fields need a metric_type annotation
before the integrations PR can ship. The regen tool emits FIXME for
new fields (see elastic/apm-tools regen change), so the issue
explicitly tells the resolver to grep for FIXME.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The drift script previously did its own go build into $WORK; that
duplicates the apm-server make target's logic (build flags, version
ldflags, FIPS handling). Make the make target depend on apm-server
instead, and have the script consume ./apm-server (the canonical
output of `make apm-server`). One build pipeline, no second copy.

The script's APM_SERVER_BIN env var still works as an override for
anyone driving the script outside make.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
  - Makefile: drop "without waiting for the next Monday cron" line
    and the "depends on apm-server" sentence (the dependency itself
    is documented by the prerequisite, no narration needed).
  - Workflow header: drop "Background: elastic#21000".
    The header should describe what this file does, not which issue
    motivated it.
  - Script: drop "config is reused below" comment; the recipe in the
    drift issue now points at `make stats-to-mapping-drift`, not at
    a literal copy of this config.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous capture loop relied on a bare `test -s "$WORK/stats.json"`
post-loop check; on failure the operator saw `set -e` exit with no
context. Print an explicit failure message and the last 50 lines of
apm-server.log so the run that triggered the failure is debuggable
from the Actions log without re-running locally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mallendem
mallendem previously approved these changes May 6, 2026
marclop
marclop previously approved these changes May 6, 2026

@marclop marclop left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

@carsonip
carsonip dismissed stale reviews from marclop and mallendem via fd1006a May 6, 2026 18:01
Previously the drift script ran go install ...@latest, which writes
the binary into $GOPATH/bin and requires that directory be on $PATH.
On a developer machine that pollutes the global Go bin dir; in CI it
only worked because actions-setup-go places GOPATH/bin on PATH.

Default to a shallow clone of apm-tools into $WORK/apm-tools and a
go build into $WORK/stats-to-mapping. Nothing escapes $WORK. Set
APM_TOOLS_DIR to point at an existing local apm-tools checkout to
skip the clone — useful when iterating on the regen tool.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@carsonip
carsonip marked this pull request as draft May 6, 2026 18:10
@elasticmachine

Copy link
Copy Markdown
Contributor

💚 Build Succeeded

History

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-skip Skip notification from the automated backport with mergify

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants