A new push to a PR (or ref) now cancels the in-progress validation for
the previous commit instead of letting obsolete pipelines run to
completion. Drafts deliberately keep the full gate set — multi-platform
feedback matters most during iteration.
Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
PRs now run security gates, lint, and the full test suite (perf
assertions excluded — they stay in dry runs and releases where a
timing-flaky red cannot block a merge). Builds, smoke and soak remain
maintainer-driven.
Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
Pull requests run the security island only — static audit, license
gates, and the CodeQL gate plus the ci-ok summary; the full dry-run
chain (lint/test/build/smoke) stays maintainer-driven via
workflow_dispatch. The CodeQL gate now resolves the PR head SHA instead
of the synthetic merge commit so it can find the analysis run.
Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
Pull requests now execute the dry-run chain (security including the
license gates, lint, full test suite, all build legs, smoke) plus a
single ci-ok summary job that fails unless every stage succeeded.
Branch protection requires dco + ci-ok, so nothing unverified can merge
— including from admins. CodeQL also runs on pull requests so the
security island completes on PR commits.
Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
Every commit must now carry a Signed-off-by trailer matching its
author, certifying the right to submit the change under the project's
MIT license (DCO 1.1, the Linux kernel mechanism). Enforcement is
strict at three layers: a commit-msg hook rejects unsigned commits
locally (scripts/install-git-hooks.sh), and the new DCO workflow
rejects every push and pull request containing one. Merge commits and
bot authors are exempt, matching standard DCO checks.
Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
The license gate now proves itself before running: a --selftest mode
plants an unlicensed vendored file and asserts the structural layer
detects it, so a silently broken gate can no longer pass. The
byte-identity provenance audit gains exit-code semantics (any verdict
outside the accepted set fails) and runs as a blocking step in the
security workflow for both dry runs and releases.
The server.json sync step read its version from pkg/npm/package.json,
which can lag behind the release being published in the dispatched
commit. Use the workflow's version input instead — it is authoritative
for the run.
With soak_level=none the soak job is skipped; release-draft already
handles that with an explicit condition, but verify and the publish
jobs relied on the default success() check, which treats a skipped
ancestor as non-success and silently skips the whole publish chain.
Give each downstream job an explicit condition that only blocks on
real failures or cancellations.
Release archives now carry THIRD_PARTY_NOTICES.md, generated by
scripts/gen-third-party-notices.sh from THIRD_PARTY.md, the grammar
manifest, and the per-component license texts; the Homebrew formula
and AUR PKGBUILD install it alongside the binary. The SBOM gains
per-component license metadata, corrected versions, and the previously
missing vendored libraries. The security workflow gains a
vendored-license scan with an explicit allow-list policy, and the
release workflow exposes a skip_perf input for releases that do not
touch pipeline logic.
The release tag must point at the commit the artifacts were built from (github.sha). The step tagged whatever the job checkout resolved to - the branch head at job START - so a commit pushed to main mid-run moved the tag target; when that head commit happened to touch .github/workflows/, the GitHub App token's tag push was rejected outright (refusing to create refs at workflow-modifying commits), failing the 0.8.0 draft.
The 0.8.0 release failed at publish-registries because pkg/npm/package.json still carried the previous hand-pinned version and npm refuses to publish over an existing release. The job now rewrites pkg/npm/package.json and pkg/pypi/pyproject.toml from the workflow's version input (with grep verification that the injection took) before publishing, so a forgotten manual bump can never fail the pipeline again. server.json needs no injection - publish-mcp-registry already syncs it from the published npm package.
packaging/glama -> pkg/glama so all packaging lives under pkg/. Update the
_smoke.yml job command and the Dockerfile/comment references to the new path.
verify.sh is path-relative (dirname $0), so its logic is unchanged.
The standard linux release binary dynamically links glibc 2.38+ and
GLIBCXX_3.4.32, so it fails to start on Debian 11, Ubuntu 20.04/22.04,
RHEL/Rocky 8/9, Amazon Linux 2, etc. — yet install.sh, the npm and PyPI
wrappers, and the binary's own self-update all fetched it by default,
contradicting the "single static binary" promise.
Point every linux install + self-update path at the fully-static
"-portable" asset (gcc -static), which has no glibc floor. macOS/Windows
are unaffected and unchanged.
- install.sh, pkg/npm/install.js, pkg/pypi _cli.py: select -portable on linux
- src/cli/cli.c: self-update download URL AND checksum archive name both use
-portable on linux (they must match or the update fails checksum verify)
- scripts/smoke-test.sh: assert linux self-update targets the -portable asset
- scripts/ci/check-glibc-compat.sh: new guard — runs the binary inside
debian:bullseye (glibc 2.31) and asserts it starts
- _smoke.yml: run the guard on the portable binary in smoke-linux-portable
Reproduced: standard binary -> "GLIBC_2.38 not found" on glibc 2.31;
portable binary runs cleanly.
- glama.json claims maintainership on glama.ai.
- packaging/glama/Dockerfile wraps the static Linux binary in a minimal
image so Glama can launch the stdio server and run its MCP introspection
checks (which power the directory score badge). The tool needs no Docker
to run; this image is only for the directory integration. Fetches the
latest release binary, arch-aware via TARGETARCH.
- packaging/glama/verify.sh builds the image and asserts the server answers
initialize + tools/list with no project indexed.
- _smoke.yml runs verify.sh as a non-gating job (continue-on-error) so the
integration is guarded against drift without ever blocking a release.
- Add `mcpName` to the npm package and an `mcp-name:` marker to the PyPI
README so the registry can verify package ownership for both.
- Rewrite server.json to reference the npm + PyPI packages instead of
five per-platform mcpb tarballs. The old form was pinned to a stale
0.6.1 with hardcoded SHA-256s that needed manual updates every release;
the package form only needs a version sync. Also trims the description
to the registry's 100-character limit.
- Add a publish-mcp-registry job: downloads mcp-publisher, authenticates
via GitHub Actions OIDC (no token, no device flow), syncs server.json's
version to the just-published npm package, and publishes. It runs after
npm/PyPI but does not gate the release un-draft, so a registry outage
never blocks shipping and the job can be retried on its own.
The legacy "Deploy from a branch" Pages build ran Jekyll on every push to
main and failed on the planning docs under /docs — Jekyll's Liquid parser
hit `Unknown tag 'data'` in EVALUATION_PLAN.md, so the website build went
red after nearly every commit.
/docs is a hand-written static site (index.html + robots/sitemap/llms.txt),
so it never needed Jekyll. Deploy it with a GitHub Actions workflow that
uploads /docs as-is and only triggers on docs/** changes, and add a
.nojekyll guard. Switch the Pages source to "GitHub Actions" for this to
take over from the legacy build.
Co-authored-by: DeusData <joerge@datadice.io>
The full suite runs the perf/bench/stress/scale suites, which push the slower ubuntu-24.04-arm runner right up against the 30-minute limit (flaky timeout cancellations marked as 'cancelled'). Give the slow legs headroom so the suite completes deterministically.
Add scripts/check-no-test-skips.sh (run from lint) which fails the lint phase on any plain SKIP() or direct tf_skip_count manipulation; only SKIP_PLATFORM() (for genuinely platform-specific tests) is tolerated. Add FAIL() and SKIP_PLATFORM() helpers to the test framework and convert the remaining SKIP()/perf-gated skips across the suite into pass-or-fail assertions, so a suite that cannot meet its preconditions reports a red failure instead of a silent skip.
The macos-15-intel leg had continue-on-error, so when that runner was unavailable the darwin-amd64 artifact silently never built and releases shipped with no Intel macOS binary. Remove continue-on-error so the leg is blocking - the binary ships or the build fails loudly. macos-15-intel is GitHub's supported Intel image through Aug 2027.
GitHub is sunsetting Intel-macOS runners, so the macos-15-intel build leg is
repeatedly cancelled before it can be allocated, turning the aggregate build
result non-success and skipping the smoke + soak jobs even though every other
build/test passed.
Mark only the macos-15-intel matrix leg continue-on-error, and relax the
smoke/soak gate to run unless builds were skipped or a leg genuinely failed
(tolerating a cancelled aggregate caused solely by the non-blocking Intel leg).
All other platforms remain required.
#394 group triaged: #266/#274/#331/#347/#348 verified fixed on v0.7.0 in
Windows CI; #227/#367 (SMB) pending reporter retest. Removing the throwaway
workflow.
Indexes a repo at a C:\ drive-letter path (subdirs + .cbmignore +
.mjs/.js) on windows-latest and dumps diagnostics for #347/#227/#367/
#331/#266/#274/#348. Temporary — delete after the run.
The #336 (Icinga2/Linux) and #385 (KiCad/Windows) reproductions are done —
both index cleanly on v0.7.0 on their reported platforms. Removing the
throwaway workflow.
The reused _build.yml builds all platforms; a cancelled macos-15-intel
runner skipped the icinga2/kicad index jobs. Build inline in each job so
the Linux (#336) and Windows (#385) repros don't depend on unrelated
platforms. Still temporary — delete after the run.
Temporary workflow_dispatch harness: builds current-main binaries and
indexes Icinga2 on Linux (fast) and KiCad on Windows (moderate) to check
whether the reported crashes still reproduce on v0.7.0. To be removed
after the run.
Reverts the CodeQL portion of 27e640b ('fix(mcp,ci): harden
get_architecture serialization + CodeQL gate'). Leaves the mcp.c +
test_mcp.c changes (NULL-coercion, clusters/services serialization,
extract_text_content fix, regression test) intact.
Restored to the pre-27e640b state:
- drop 'jq' from build-deps install line
- drop 'queries: +./codeql' from codeql-action/init (no ./codeql
custom-query directory exists in this repo)
- drop 'id: analyze' and 'output: codeql-results' on analyze step
- drop the SARIF-parsing 'Fail on CodeQL error-level findings' step
CI workflow changes need their own design discussion per CONTRIBUTING.md
('Project configuration / CI workflows' is in the explicit-approval list);
the gate hardening landed bundled with an unrelated bug fix. Splitting it
out so the upstream MCP fix stays clean and any future CodeQL gate work
can be its own focused PR.
Follow-up to #281 (handle_get_architecture) plus a CodeQL workflow
upgrade developed in parallel; bundling into one commit because the
test suite had to land alongside both.
mcp.c — handle_get_architecture
- NULL-coerce every const-char* field in the architecture sections
via `x ? x : ""`, matching the rest of mcp.c (search_graph, etc.).
Without this, a NULL field becomes a missing JSON key instead of
an empty string; yyjson_mut_obj_add_str returns false on NULL and
silently no-ops, so an inconsistent omission could surprise callers.
- Serialize two more architecture aspects that #281 left on the
floor: services (cbm_service_link_t: from/to/type/count) and
clusters (cbm_cluster_info_t: id/label/members/cohesion plus the
top_nodes / packages / edge_types string arrays). The store-side
computation populates these for aspects=["all"] / explicit names,
so dropping them in the serializer was data loss.
tests/test_mcp.c
- New tool_get_architecture_emits_populated_sections regression test.
Uses a minimal inline fixture (single Function node tagged with
"is_entry_point": true) since arch_entry_points reads that flag
out of properties_json. Asserts the response contains both an
"entry_points" array and the function name — neither would appear
before #281 because handle_get_architecture never called
cbm_store_get_architecture.
- extract_text_content drilled too shallow: it pulled "content" only
from the JSON root, so it worked for cbm_mcp_handle_tool but
silently fell through to the raw response for cbm_mcp_server_handle
(where content lives under .result.content). Added a fallback that
checks .result.content; both unwrappers tested by existing fixtures.
ci(codeql)
- Run on pull_request to main, not just push; surfaces findings on
the PR instead of after merge.
- Pull custom queries from ./codeql via `queries: +./codeql`.
- Capture SARIF output and fail the job on any error-level finding,
using jq to enumerate rule id, file:line, and message text in the
GitHub Actions error annotation. Warnings are still reported as
before; only errors block.
Full suite: 2842 passed, 0 failed.
v3.0.0 moves the action runtime from Node 20 to Node 24. GitHub-hosted runners (which this repo uses) already support the Node 24 Actions runtime, so the upgrade is transparent.
Replace direct `${{ ... }}` interpolation in `run:` steps of `.github/workflows/_build.yml` with `env:` blocks per GitHub's hardening guide. Every workflow input (`inputs.version`) and matrix value (`matrix.cc`, `matrix.cxx`, `matrix.goos`, `matrix.goarch`, `matrix.arch`) now flows into the shell through a named environment variable rather than being textually spliced into the script.
Pattern applied uniformly across the 6 flagged steps plus the archive steps that used `${{ matrix.* }}` in tar filenames. The values become opaque shell variables on expansion, so attacker input becomes a literal argv entry rather than interpreted shell code. The if/else form on `$VERSION` avoids word-splitting pitfalls of conditional arg construction.
semgrep `run-shell-injection` findings: 6 → 0. Behaviour preserved — same build.sh invocations, same artefact names, same conditional version handling.
Reference: https://securitylab.github.com/research/github-actions-untrusted-input/Closes#247.
Previously the verify job only ran scripts/security-strings.sh on
files matching binaries/codebase-memory-mcp* — install.sh, install.ps1,
LICENSE, and any future companion files in the release archives were
NOT covered by the binary-string audit (only by VirusTotal).
Changes:
- release.yml: loop over binaries/* (every file in the audit set).
- security-strings.sh: detect file type via 'file -b'. For shell
scripts and other text files, skip the URL audit and dangerous-cmd
audit (those rules are tuned for compiled binaries — install.sh
legitimately uses wget as a curl fallback, and 'case https://*)'
globs look like unauthorized URLs to a strings dump). Always run
credential and base64 pattern audits — those are universally
meaningful regardless of file type.
- Verified locally: install.sh and install.ps1 now both pass.
Net effect: every release artifact is now audited, with rule sets
appropriate to its file type.
Previously the un-draft step ran inside the verify job, before
publish-registries. If npm or PyPI publish failed, the GitHub release
was already live but the wrappers were not — half-shipped state.
Move the un-draft into a new publish-final job that needs both
[verify, publish-registries]. If any registry fails, the GH release
stays in draft and the run can be re-tried with replace=true.
Add publish-registries job to release.yml that runs after verify
(release published, VirusTotal table appended). Publishes:
- pkg/npm via 'npm publish --access public --provenance'
- pkg/pypi via 'twine upload' (sdist + wheel from hatchling)
Both wrappers fetch the binary at install time, so a version bump is
the only per-release change needed. Bumped to 0.6.1.
Requires NPM_TOKEN and PYPI_TOKEN repo secrets.
checksums.txt contains hashes of .tar.gz/.zip archives; VirusTotal
indexes the extracted binaries. Updated README table and badge to use
correct binary SHA-256 hashes for v0.6.0. Updated release.yml to hash
extracted binaries from the binaries/ directory for future releases.
- Adds VirusTotal (0/72) and SLSA Level 3 badges to the header
- Adds Security section with per-binary VirusTotal scan links for v0.6.0
- Adds step in release.yml to auto-append a Security Verification table
with per-binary VirusTotal links to every release's notes going forward