Skip to content

ci: run tests and tag-only publish through the shared workflows - #139

Open
singlerider wants to merge 9 commits into
devfrom
ci/publish-on-tag
Open

singlerider wants to merge 9 commits into
devfrom
ci/publish-on-tag

Conversation

@singlerider

@singlerider singlerider commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Moves blastdns CI onto the org's shared workflows and publishes only on a human-pushed version tag.

Shared workflows

Calls the reusable workflows in blacklanternsecurity/CLA, pinned to CLA main 40c6e18c0a6e32116eea4f89fc826f53c70889dc, including cla.yml and the README release.sh link. No repo-local copies of test, wheel, crates, release, or SBOM steps remain.

  • tests.yml (pull_request, push to stable, workflow_call) calls rust-tests.yml (fmt, clippy, cargo test with --include-ignored on the rust-toolchain channel) and python-tests.yml (ruff, then pytest on every CPython minor satisfying requires-python, maturin develop detected from the build backend).
  • publish.yml (v* tags only) calls release-check.yml (refuses anything but vMAJOR.MINOR.PATCH / vMAJOR.MINOR.PATCH-rc.N and any tag differing from the Cargo.toml version), tests.yml, maturin-wheels.yml (interpreters from requires-python), crates.yml, then publish.yml (GitHub release, SPDX SBOM, wheels attached, --prerelease for rc tags, same run). PyPI stays a top-level job because trusted publishing does not match reusable workflows.
  • Nothing publishes or tags on a branch push. No workflow runs git tag.
  • README ## Releasing documents the procedure and links the shared release.sh.
  • dnsmasq and AXFR fixtures live in scripts/ci-setup.sh / scripts/ci-teardown.sh, shared by tests and the benchmark. CLI tests run in the python job. MassDNS is cloned at tag v1.1.0.
  • ResolverHealth::decay_errors uses a compare-exchange loop instead of the deprecated fetch_update.
  • pyproject.toml reads its version from Cargo.toml (dynamic = ["version"]).
  • CLA calls the shared reusable workflow.

Required checks for governance: rust / passed, python / passed.

Admin steps

  • Register a PyPI trusted publisher for workflow publish.yml, environment pypi, then delete PYPI_API_TOKEN.

Validation

$ uvx --from actionlint-py==1.7.12.24 actionlint .github/workflows/*.yml
(no findings)
$ grep -rnE 'uses: [^.].*@' .github | grep -vE '@[0-9a-f]{40}'
(none)
$ grep -rn 'ubuntu-latest\|actions-rs' .github
(none)
$ cargo clippy --all --all-targets --all-features --locked -- -D warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 27.28s
$ uv run ruff check && uv run ruff format --check
All checks passed!
14 files already formatted
$ gh pr checks 139 -R blacklanternsecurity/blastdns
benchmark	pass
CLAAssistant	pass
python / lint	pass
python / matrix / read	pass
python / passed	pass
python / test (3.10)	pass
python / test (3.11)	pass
python / test (3.12)	pass
python / test (3.13)	pass
python / test (3.14)	pass
python / test (3.9)	pass
rust / lint	pass
rust / passed	pass
rust / test	pass

Refs blacklanternsecurity/bbot-enterprise#139, blacklanternsecurity/bbot-enterprise#140, blacklanternsecurity/bbot-enterprise#149, blacklanternsecurity/bbot-enterprise#155, blacklanternsecurity/bbot-enterprise#156 (blastdns part)

Move PyPI (maturin) and crates.io publishing out of python-tests.yml
and rust-tests.yml into publish.yml, triggered only by v* tags.

- version job fails unless the tag equals v + the Cargo.toml version
- test workflows run via workflow_call; every build job gates every
  registry upload so a release is never partial
- drop --skip-existing and registry already-published probes
- pyproject reads its version from Cargo via dynamic = ["version"]
- concurrency groups use a fixed per-file prefix since github.workflow
  is the caller's name under workflow_call
- pin every action to a full SHA, replace -latest runners

Part of blacklanternsecurity/bbot-enterprise#139
Part of blacklanternsecurity/bbot-enterprise#149
Part of blacklanternsecurity/bbot-enterprise#156
The publish workflow refuses any tag that is not vMAJOR.MINOR.PATCH or
vMAJOR.MINOR.PATCH-rc.N before comparing it to Cargo.toml.

PyPI upload drops PYPI_API_TOKEN for trusted publishing. Registry jobs
run in the release environment so the publish gate applies.

A final job creates the GitHub Release in the same tag run with an SPDX
JSON SBOM attached, so a tag and its release always exist together.

CLA calls the shared reusable workflow.
Governance declares one gated environment named pypi across published
repos. crates and docker jobs stay in release.
Rust 1.95 deprecates AtomicU64::fetch_update in favor of try_update, and
the repo denies warnings, so every CI job failed to compile. A
compare_exchange_weak loop has the same semantics and builds on both the
MSRV and current stable.
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

DNS Resolver Benchmark

Throughput is reported, never gated: it moves with hardware. The stable
columns are unanswered, attempts/query, sockets, and conntrack.

ceiling/1-resolver

engine in flight qps vs dnspython unanswered atts/query p50 p99 p100 sockets conntrack
blastdns 32 16,217 6.31x 0.000% 1.00 0.662s 1.230s 1.233s 44 17,137
dnspython 100 2,570 1.00x 0.000% n/a 0.414s 0.773s 0.778s 211 1,432
massdns - 12,453 4.85x 0.000% n/a n/a n/a n/a 7 5

pool/32-resolvers

engine in flight qps vs dnspython unanswered atts/query p50 p99 p100 sockets conntrack
blastdns 2/res, 64 total 16,200 - 0.000% 1.00 0.666s 1.231s 1.234s 72 19,725
massdns - 12,538 - 0.000% n/a n/a n/a n/a 7 32

pool/32-persistent

engine in flight qps vs dnspython unanswered atts/query p50 p99 p100 sockets conntrack
blastdns 8/res, 256 total 17,037 - 0.000% 1.00 0.638s 1.171s 1.174s 44 31

impaired/10%-loss

engine in flight qps vs dnspython unanswered atts/query p50 p99 p100 sockets conntrack
blastdns 32 188 - 0.000% 1.11 4.050s 8.999s 10.614s 48 2,202
massdns - 975 - 0.000% n/a n/a n/a n/a 7 1

impaired/20%-REFUSED

engine in flight qps vs dnspython unanswered atts/query p50 p99 p100 sockets conntrack
blastdns 32 2,181 - 0.000% 1.25 0.622s 0.917s 0.917s 52 2,453
massdns - 2,535 - 0.000% n/a n/a n/a n/a 7 1

impaired/50ms-latency

engine in flight qps vs dnspython unanswered atts/query p50 p99 p100 sockets conntrack
blastdns 32 617 - 0.000% 1.00 1.848s 3.239s 3.239s 56 1,953

@singlerider
singlerider requested a review from en0f October 3, 2026 13:59
@singlerider singlerider self-assigned this Oct 3, 2026
singlerider added a commit that referenced this pull request Oct 3, 2026
dev carries no required checks (#139), so gh pr merge --auto would merge
immediately rather than on green. The shared workflow now waits for
every other check on the head commit, refuses on failure or cancel, and
merges with --match-head-commit. The caller grants checks: read so it can
read check state.
rust-tests.yml, python-tests.yml and the inline wheel matrix, crates
upload and release steps were copies of what every PyO3 repo carries.
tests.yml now calls rust-tests.yml and python-tests.yml from
blacklanternsecurity/CLA, so the Python matrix follows requires-python
and the toolchain follows rust-toolchain. publish.yml calls
release-check.yml (tag shape and manifest version), maturin-wheels.yml,
crates.yml and publish.yml; only the PyPI upload stays top level because
trusted publishing does not match reusable workflows.

The dnsmasq and AXFR fixtures move into scripts/ci-setup.sh and
scripts/ci-teardown.sh so tests and the benchmark share one copy.
CLI tests run in the python job. MassDNS is cloned at a tag.
Releases are a pushed tag cut with the shared release.sh, which checks
the tag against the manifest. State that here instead of copying the
script.
@singlerider singlerider changed the title ci: publish only on version tags ci: run tests and tag-only publish through the shared workflows Oct 3, 2026

@en0f en0f left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

required status checks on dev point at checks that will never run. Branch protection on dev still requires python-tests (3.9) through (3.14) and rust-tests. This PR deletes the workflows that report those checks, so all 7 sit at "Expected - Waiting for status to be reported" and the PR can't merge even after you approve it. An admin needs to change the required checks to Tests / rust / passed and Tests / python / passed, which is what the PR description asks for. If stable has the same rule, it needs the same change.

CLA#3 merged. Repin rust-tests, python-tests, release-check,
maturin-wheels, crates and publish from the feat/shared-workflows
commit to CLA main de82726. Workflow blob shas are identical at both refs.
The shared rust-tests workflow runs clippy with --all --locked only.
The old repo workflow linted --all-features, which covers the pyo3
bindings. Tests cannot link with extension-module enabled, so lint the
feature in a repo-local job instead of widening cargo-args.
@singlerider
singlerider requested a review from en0f October 6, 2026 18:32
en0f
en0f previously approved these changes Oct 6, 2026
The shared workflow pins pointed at de82726 (and the CLA caller at
4f85d3c where it still lagged). CLA main is now 40c6e18, which adds the
optional registry login for image SBOMs. Every CLA reference, including
the release.sh link, now resolves on CLA main at one commit.

Refs blacklanternsecurity/bbot-enterprise#139

@liquidsec liquidsec 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.

This will mean anyone with write access can publish. This needs to be discussed.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants