tool-compass 2.5.0__tar.gz → 2.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.env.example +11 -1
- tool_compass-2.5.2/.gitattributes +17 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.github/dependabot.yml +1 -44
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.github/workflows/ci.yml +140 -28
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.github/workflows/publish.yml +50 -23
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.github/workflows/release-binaries.yml +51 -44
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.github/workflows/release.yml +128 -43
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.gitignore +1 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/CHANGELOG.md +66 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/Dockerfile +16 -5
- {tool_compass-2.5.0 → tool_compass-2.5.2}/Makefile +12 -2
- {tool_compass-2.5.0 → tool_compass-2.5.2}/PKG-INFO +21 -8
- {tool_compass-2.5.0 → tool_compass-2.5.2}/README.md +18 -3
- {tool_compass-2.5.0 → tool_compass-2.5.2}/SCORECARD.md +2 -2
- {tool_compass-2.5.0 → tool_compass-2.5.2}/SHIP_GATE.md +1 -1
- {tool_compass-2.5.0 → tool_compass-2.5.2}/analytics.py +93 -58
- {tool_compass-2.5.0 → tool_compass-2.5.2}/backend_client_mcp.py +240 -25
- {tool_compass-2.5.0 → tool_compass-2.5.2}/backend_client_simple.py +1209 -148
- tool_compass-2.5.2/bootstrap.py +273 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/chain_indexer.py +247 -31
- {tool_compass-2.5.0 → tool_compass-2.5.2}/cli.py +230 -64
- {tool_compass-2.5.0 → tool_compass-2.5.2}/config.py +93 -20
- {tool_compass-2.5.0 → tool_compass-2.5.2}/docker-compose.yml +32 -3
- tool_compass-2.5.2/docker-entrypoint.sh +59 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/embedder.py +722 -76
- {tool_compass-2.5.0 → tool_compass-2.5.2}/gateway.py +1002 -122
- tool_compass-2.5.2/indexer.py +1918 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/pyproject.toml +8 -10
- {tool_compass-2.5.0 → tool_compass-2.5.2}/scripts/check-org-urls.sh +5 -1
- {tool_compass-2.5.0 → tool_compass-2.5.2}/sync_manager.py +368 -98
- {tool_compass-2.5.0 → tool_compass-2.5.2}/ui.py +474 -12
- tool_compass-2.5.0/bootstrap.py +0 -128
- tool_compass-2.5.0/indexer.py +0 -1199
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.dockerignore +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/.pre-commit-config.yaml +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/CODE_OF_CONDUCT.md +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/CONTRIBUTING.md +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/LICENSE +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/SECURITY.md +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/_version.py +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/compass_config.example.json +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/docs/index.md +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/fly.toml +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/llms.txt +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/requirements-dev.txt +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/requirements.txt +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/scripts/regenerate-scorecard.sh +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/scripts/sync-version.mjs +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/scripts/verify-metrics.sh +0 -0
- {tool_compass-2.5.0 → tool_compass-2.5.2}/tool_manifest.py +0 -0
|
@@ -18,9 +18,19 @@
|
|
|
18
18
|
# External Services
|
|
19
19
|
# =============================================================================
|
|
20
20
|
|
|
21
|
-
# Ollama server URL for embeddings
|
|
21
|
+
# Ollama server URL for embeddings (used when embedding_provider=ollama)
|
|
22
22
|
OLLAMA_URL=http://localhost:11434
|
|
23
23
|
|
|
24
|
+
# Embedding provider override (ollama | openai | openai-compatible).
|
|
25
|
+
# Wins over compass_config.json embedding_provider for first-run bootstrap.
|
|
26
|
+
# TOOL_COMPASS_EMBEDDING_PROVIDER=ollama
|
|
27
|
+
|
|
28
|
+
# API key for openai / openai-compatible embeddings. Wins over the
|
|
29
|
+
# embedding_api_key field in compass_config.json — keep the secret out of
|
|
30
|
+
# the on-disk config. Required when embedding_provider is openai or
|
|
31
|
+
# openai-compatible (unless the server accepts unauthenticated calls).
|
|
32
|
+
# TOOL_COMPASS_EMBEDDING_API_KEY=
|
|
33
|
+
|
|
24
34
|
# ComfyUI server URL (for AI image generation backend)
|
|
25
35
|
COMFYUI_URL=http://localhost:8188
|
|
26
36
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Shell scripts and the Dockerfile MUST keep LF, on every platform.
|
|
2
|
+
#
|
|
3
|
+
# Earned on the sibling xrpl-camp repo the same day this landed: an entrypoint
|
|
4
|
+
# script checked out with CRLF on Windows (core.autocrlf) makes the shebang
|
|
5
|
+
# `#!/bin/sh\r`, and Linux then tries to exec an interpreter literally named
|
|
6
|
+
# `/bin/sh\r`. It fails with "no such file or directory" naming the SCRIPT, so
|
|
7
|
+
# it reads like a missing file rather than a line-ending problem.
|
|
8
|
+
#
|
|
9
|
+
# CI never catches it -- a Linux runner checks out LF and builds fine. Only a
|
|
10
|
+
# contributor building the image on Windows hits it, which is the worst place
|
|
11
|
+
# for a defect to live: invisible to the people who could fix it.
|
|
12
|
+
*.sh text eol=lf
|
|
13
|
+
Dockerfile text eol=lf
|
|
14
|
+
docker-entrypoint.sh text eol=lf
|
|
15
|
+
|
|
16
|
+
# Keep the rest normalised in the repo and native in the working tree.
|
|
17
|
+
* text=auto
|
|
@@ -16,25 +16,6 @@ updates:
|
|
|
16
16
|
patterns: ["*"]
|
|
17
17
|
update-types: ["minor", "patch"]
|
|
18
18
|
|
|
19
|
-
# Daily security-only overlay (CT-B-006) — CVE updates from the GitHub
|
|
20
|
-
# Advisory Database don't wait the monthly cadence. open-pull-requests-limit
|
|
21
|
-
# raised to 10 since these are infrequent and load-bearing.
|
|
22
|
-
# NOTE: Dependabot security advisories are auto-emitted; this second entry
|
|
23
|
-
# exists to give them their own schedule/labels/PR cap independent of the
|
|
24
|
-
# monthly version-update entry above. The `allow: dependency-type: all`
|
|
25
|
-
# block scopes this entry to all dependencies; security advisories are
|
|
26
|
-
# always opened regardless of the version-update grouping rules.
|
|
27
|
-
- package-ecosystem: "pip"
|
|
28
|
-
directory: "/"
|
|
29
|
-
schedule:
|
|
30
|
-
interval: "daily"
|
|
31
|
-
open-pull-requests-limit: 10
|
|
32
|
-
allow:
|
|
33
|
-
- dependency-type: "all"
|
|
34
|
-
labels:
|
|
35
|
-
- "security"
|
|
36
|
-
- "dependencies"
|
|
37
|
-
|
|
38
19
|
# =============================================================================
|
|
39
20
|
# github-actions — workflow file action references
|
|
40
21
|
# =============================================================================
|
|
@@ -48,22 +29,10 @@ updates:
|
|
|
48
29
|
patterns: ["*"]
|
|
49
30
|
update-types: ["minor", "patch"]
|
|
50
31
|
|
|
51
|
-
# Daily security-only overlay for actions (CT-B-006).
|
|
52
|
-
- package-ecosystem: "github-actions"
|
|
53
|
-
directory: "/"
|
|
54
|
-
schedule:
|
|
55
|
-
interval: "daily"
|
|
56
|
-
open-pull-requests-limit: 10
|
|
57
|
-
allow:
|
|
58
|
-
- dependency-type: "all"
|
|
59
|
-
labels:
|
|
60
|
-
- "security"
|
|
61
|
-
- "dependencies"
|
|
62
|
-
|
|
63
32
|
# =============================================================================
|
|
64
33
|
# docker — Dockerfile base-image FROM directives (CT-B-005)
|
|
65
34
|
# =============================================================================
|
|
66
|
-
# Pairs with the digest-pinned `FROM python:3.
|
|
35
|
+
# Pairs with the digest-pinned `FROM python:3.12-slim@sha256:...` in
|
|
67
36
|
# Dockerfile (CT-B-003). Without this ecosystem entry the digest pin
|
|
68
37
|
# would become a stale anchor with no machine-readable refresh path.
|
|
69
38
|
- package-ecosystem: "docker"
|
|
@@ -75,15 +44,3 @@ updates:
|
|
|
75
44
|
all-docker:
|
|
76
45
|
patterns: ["*"]
|
|
77
46
|
update-types: ["minor", "patch"]
|
|
78
|
-
|
|
79
|
-
# Daily security-only overlay for docker base images (CT-B-006).
|
|
80
|
-
- package-ecosystem: "docker"
|
|
81
|
-
directory: "/"
|
|
82
|
-
schedule:
|
|
83
|
-
interval: "daily"
|
|
84
|
-
open-pull-requests-limit: 10
|
|
85
|
-
allow:
|
|
86
|
-
- dependency-type: "all"
|
|
87
|
-
labels:
|
|
88
|
-
- "security"
|
|
89
|
-
- "dependencies"
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
# --- Measured CI cost (dogfood-swarm SKILL item 8) --------------------------
|
|
2
|
+
# 10 billable min per push across 8 jobs (6.1 min actual wall time),
|
|
3
|
+
# measured 2026-07-04 from run 28722872805. Most expensive jobs:
|
|
4
|
+
# 2 min Org URL sanity check (1.1 min wall)
|
|
5
|
+
# 2 min Python 3.12 on ubuntu-latest (1.9 min wall)
|
|
6
|
+
# 2 min Integration Tests (1.4 min wall)
|
|
7
|
+
# GitHub bills per JOB, rounded UP to the minute, x2 Windows / x10 macOS, so
|
|
8
|
+
# billable minutes exceed wall time - many short jobs cost more than one long
|
|
9
|
+
# one. This number is measured, never estimated; re-measure after any change:
|
|
10
|
+
# gh api repos/mcp-tool-shop-org/tool-compass/actions/runs/<id>/jobs
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
1
12
|
# Tool Compass CI Pipeline
|
|
2
13
|
# Runs tests on pushes and PRs that touch code or config.
|
|
3
14
|
# Also handles GitHub Pages deploy for site/** changes on main (folded in from
|
|
@@ -15,6 +26,12 @@ on:
|
|
|
15
26
|
- 'requirements*.txt'
|
|
16
27
|
- 'pyproject.toml'
|
|
17
28
|
- 'Dockerfile'
|
|
29
|
+
- 'docker-compose.yml'
|
|
30
|
+
- 'Makefile'
|
|
31
|
+
- 'fly.toml'
|
|
32
|
+
- '.pre-commit-config.yaml'
|
|
33
|
+
- '.dockerignore'
|
|
34
|
+
- 'npm/**'
|
|
18
35
|
- 'site/**'
|
|
19
36
|
- '.github/workflows/**'
|
|
20
37
|
pull_request:
|
|
@@ -26,6 +43,12 @@ on:
|
|
|
26
43
|
- 'requirements*.txt'
|
|
27
44
|
- 'pyproject.toml'
|
|
28
45
|
- 'Dockerfile'
|
|
46
|
+
- 'docker-compose.yml'
|
|
47
|
+
- 'Makefile'
|
|
48
|
+
- 'fly.toml'
|
|
49
|
+
- '.pre-commit-config.yaml'
|
|
50
|
+
- '.dockerignore'
|
|
51
|
+
- 'npm/**'
|
|
29
52
|
- 'site/**'
|
|
30
53
|
- '.github/workflows/**'
|
|
31
54
|
# TST-FT-002: nightly-fuzz job runs Hypothesis in `nightly` profile
|
|
@@ -73,7 +96,7 @@ jobs:
|
|
|
73
96
|
- name: Set up Python (pre-commit)
|
|
74
97
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
75
98
|
with:
|
|
76
|
-
python-version: '3.
|
|
99
|
+
python-version: '3.12'
|
|
77
100
|
|
|
78
101
|
# CT-B-018: run the pre-commit hooks (ruff format + ruff check +
|
|
79
102
|
# standard hygiene + gitleaks). Same hooks the contributor runs
|
|
@@ -122,22 +145,46 @@ jobs:
|
|
|
122
145
|
timeout-minutes: 15
|
|
123
146
|
# PR-scoped concurrency (see workflow-top NOTE).
|
|
124
147
|
concurrency:
|
|
125
|
-
group: test-${{ matrix.python-version }}-${{ github.event.pull_request.number || github.run_id }}
|
|
148
|
+
group: test-${{ matrix.python-version }}-${{ matrix.os }}-${{ github.event.pull_request.number || github.run_id }}
|
|
126
149
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
127
150
|
permissions:
|
|
128
151
|
contents: read
|
|
152
|
+
# Windows runners default to pwsh, which does not accept `\` line
|
|
153
|
+
# continuations used below. Git Bash is on the image.
|
|
154
|
+
defaults:
|
|
155
|
+
run:
|
|
156
|
+
shell: bash
|
|
129
157
|
|
|
130
158
|
strategy:
|
|
131
159
|
fail-fast: false
|
|
132
160
|
matrix:
|
|
133
|
-
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
|
|
161
|
+
# Two-cell OS matrix (ubuntu + windows). macos-latest is x10 billable
|
|
162
|
+
# and is covered by the release-binaries darwin-arm64 freeze, not
|
|
163
|
+
# every-push pytest. Do not revive 3.11/3.13: those were dropped for
|
|
164
|
+
# runner SIGILL on hnswlib/numpy wheels (see comment below).
|
|
165
|
+
os: [ubuntu-latest, windows-latest]
|
|
166
|
+
# One version, because one is all this repo can currently test.
|
|
167
|
+
#
|
|
168
|
+
# 3.13 was dropped earlier: hnswlib 0.8.0's cp313 wheel SIGILLs on
|
|
169
|
+
# GitHub ubuntu-latest runners (AVX instruction gap).
|
|
170
|
+
#
|
|
171
|
+
# 3.11 is dropped here 2026-09-08 for the SAME reason, now reaching
|
|
172
|
+
# numpy: pytest died at collection with "Illegal instruction (core
|
|
173
|
+
# dumped)" importing numpy._core._multiarray_umath. Reproducible across
|
|
174
|
+
# a rerun, and 3.12 passed the same commit both times. Version pinning
|
|
175
|
+
# would not have helped -- the last green run used the SAME numpy 2.1.3
|
|
176
|
+
# on the SAME runner image, so the wheel's CPU dispatch, not its
|
|
177
|
+
# version, is what changed underneath.
|
|
178
|
+
#
|
|
179
|
+
# READ THIS AS A RUNNER LIMITATION, NOT A LANGUAGE ONE. Nothing here
|
|
180
|
+
# needs 3.12 syntax; 3.11 is dropped because prebuilt wheels crash on
|
|
181
|
+
# GitHub's ubuntu-latest CPUs, not because the code stopped supporting
|
|
182
|
+
# it. requires-python was raised to >=3.12 in the same commit so the
|
|
183
|
+
# package stops claiming an interpreter no gate exercises -- a green
|
|
184
|
+
# badge over an untested runtime is worse than a narrow matrix.
|
|
185
|
+
# Reinstating 3.11 is a deliberate decision with its own test strategy
|
|
186
|
+
# (a self-hosted runner, or a source build of hnswlib/numpy).
|
|
187
|
+
python-version: ['3.12']
|
|
141
188
|
|
|
142
189
|
steps:
|
|
143
190
|
- name: Checkout repository
|
|
@@ -159,39 +206,39 @@ jobs:
|
|
|
159
206
|
- name: Run tests
|
|
160
207
|
run: |
|
|
161
208
|
pytest -v --tb=short -m "not integration" \
|
|
162
|
-
--junitxml=junit-${{ matrix.python-version }}.xml
|
|
209
|
+
--junitxml=junit-${{ matrix.python-version }}-${{ matrix.os }}.xml
|
|
163
210
|
|
|
164
211
|
- name: Upload JUnit report
|
|
165
212
|
if: always()
|
|
166
213
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
167
214
|
with:
|
|
168
|
-
name: junit-reports-${{ matrix.python-version }}
|
|
169
|
-
path: junit-${{ matrix.python-version }}.xml
|
|
215
|
+
name: junit-reports-${{ matrix.python-version }}-${{ matrix.os }}
|
|
216
|
+
path: junit-${{ matrix.python-version }}-${{ matrix.os }}.xml
|
|
170
217
|
if-no-files-found: warn
|
|
171
218
|
# CT-B-012: JUnit reports are short-lived diagnostic data — only
|
|
172
219
|
# useful for the most recent few runs. Default 90d is wasted.
|
|
173
220
|
retention-days: 14
|
|
174
221
|
|
|
175
222
|
- name: Run tests with coverage
|
|
176
|
-
if: matrix.python-version == '3.
|
|
223
|
+
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
|
|
177
224
|
run: |
|
|
178
225
|
pytest --cov=. --cov-report=xml --cov-report=term -m "not integration"
|
|
179
226
|
|
|
180
227
|
- name: Upload coverage to Codecov
|
|
181
|
-
if: matrix.python-version == '3.
|
|
228
|
+
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
|
|
182
229
|
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
|
|
183
230
|
with:
|
|
184
231
|
file: coverage.xml
|
|
185
232
|
fail_ci_if_error: false
|
|
186
233
|
|
|
187
234
|
- name: Run pip-audit (report only)
|
|
188
|
-
if: matrix.python-version == '3.
|
|
235
|
+
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
|
|
189
236
|
run: |
|
|
190
237
|
pip-audit -r requirements.txt --format json --output pip-audit-report.json || true
|
|
191
238
|
# Non-blocking: full output captured as artifact for reviewers.
|
|
192
239
|
|
|
193
240
|
- name: Upload pip-audit report
|
|
194
|
-
if: matrix.python-version == '3.
|
|
241
|
+
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
|
|
195
242
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
196
243
|
with:
|
|
197
244
|
name: pip-audit-report
|
|
@@ -205,10 +252,34 @@ jobs:
|
|
|
205
252
|
# `pip-audit -r requirements.txt` reports no known vulnerabilities. The
|
|
206
253
|
# report-only artifact step above still runs for the full JSON snapshot.
|
|
207
254
|
- name: Audit dependencies for vulnerabilities
|
|
208
|
-
if: matrix.python-version == '3.
|
|
255
|
+
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
|
|
209
256
|
run: |
|
|
210
257
|
pip-audit -r requirements.txt
|
|
211
258
|
|
|
259
|
+
npm-wrapper:
|
|
260
|
+
name: npm wrapper tests
|
|
261
|
+
runs-on: ubuntu-latest
|
|
262
|
+
timeout-minutes: 5
|
|
263
|
+
concurrency:
|
|
264
|
+
group: npm-wrapper-${{ github.event.pull_request.number || github.run_id }}
|
|
265
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
266
|
+
permissions:
|
|
267
|
+
contents: read
|
|
268
|
+
steps:
|
|
269
|
+
- name: Checkout repository
|
|
270
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
271
|
+
|
|
272
|
+
- name: Setup Node
|
|
273
|
+
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
|
|
274
|
+
with:
|
|
275
|
+
node-version: 22
|
|
276
|
+
|
|
277
|
+
- name: Install and test npm wrapper
|
|
278
|
+
working-directory: npm
|
|
279
|
+
run: |
|
|
280
|
+
npm install --no-audit --no-fund
|
|
281
|
+
npm test
|
|
282
|
+
|
|
212
283
|
# TST-FT-002: nightly Hypothesis fuzzing. Runs on schedule (Mon/Wed/Fri 09:00
|
|
213
284
|
# UTC) or manually via workflow_dispatch. Never runs on push/PR — nightly-only.
|
|
214
285
|
# On failure, opens a tracking issue via actions/github-script.
|
|
@@ -229,7 +300,7 @@ jobs:
|
|
|
229
300
|
- name: Set up Python
|
|
230
301
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
231
302
|
with:
|
|
232
|
-
python-version: '3.
|
|
303
|
+
python-version: '3.12'
|
|
233
304
|
cache: 'pip'
|
|
234
305
|
cache-dependency-path: 'requirements*.txt'
|
|
235
306
|
|
|
@@ -292,7 +363,7 @@ jobs:
|
|
|
292
363
|
- name: Set up Python
|
|
293
364
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
294
365
|
with:
|
|
295
|
-
python-version: '3.
|
|
366
|
+
python-version: '3.12'
|
|
296
367
|
cache: 'pip'
|
|
297
368
|
cache-dependency-path: 'requirements*.txt'
|
|
298
369
|
|
|
@@ -338,14 +409,26 @@ jobs:
|
|
|
338
409
|
continue-on-error: true # Integration tests may be flaky
|
|
339
410
|
|
|
340
411
|
docker:
|
|
341
|
-
name: Docker
|
|
412
|
+
name: Docker ${{ matrix.target }}
|
|
342
413
|
runs-on: ubuntu-latest
|
|
343
414
|
needs: test
|
|
344
415
|
# CT-B-011: docker buildx + gha cache; 20 min covers a cold-cache build.
|
|
345
416
|
timeout-minutes: 20
|
|
417
|
+
strategy:
|
|
418
|
+
fail-fast: false
|
|
419
|
+
matrix:
|
|
420
|
+
include:
|
|
421
|
+
- target: mcp-gateway
|
|
422
|
+
tag: tool-compass:test-gateway
|
|
423
|
+
port: 8080
|
|
424
|
+
smoke: health
|
|
425
|
+
- target: production
|
|
426
|
+
tag: tool-compass:test-ui
|
|
427
|
+
port: 7860
|
|
428
|
+
smoke: bind
|
|
346
429
|
# PR-scoped concurrency (see workflow-top NOTE).
|
|
347
430
|
concurrency:
|
|
348
|
-
group: docker-${{ github.event.pull_request.number || github.run_id }}
|
|
431
|
+
group: docker-${{ matrix.target }}-${{ github.event.pull_request.number || github.run_id }}
|
|
349
432
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
350
433
|
permissions:
|
|
351
434
|
contents: read
|
|
@@ -355,16 +438,45 @@ jobs:
|
|
|
355
438
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
356
439
|
|
|
357
440
|
- name: Set up Docker Buildx
|
|
358
|
-
uses: docker/setup-buildx-action@
|
|
441
|
+
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
|
359
442
|
|
|
360
443
|
- name: Build Docker image
|
|
361
|
-
uses: docker/build-push-action@
|
|
444
|
+
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
|
362
445
|
with:
|
|
363
446
|
context: .
|
|
447
|
+
target: ${{ matrix.target }}
|
|
364
448
|
push: false
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
cache-
|
|
449
|
+
load: true
|
|
450
|
+
tags: ${{ matrix.tag }}
|
|
451
|
+
cache-from: type=gha,scope=ci-${{ matrix.target }}
|
|
452
|
+
cache-to: type=gha,mode=max,scope=ci-${{ matrix.target }}
|
|
453
|
+
|
|
454
|
+
- name: Smoke run
|
|
455
|
+
run: |
|
|
456
|
+
set -euo pipefail
|
|
457
|
+
docker run -d --name tc-smoke -p ${{ matrix.port }}:${{ matrix.port }} ${{ matrix.tag }}
|
|
458
|
+
cleanup() { docker logs tc-smoke || true; docker rm -f tc-smoke || true; }
|
|
459
|
+
trap cleanup EXIT
|
|
460
|
+
for _ in $(seq 1 45); do
|
|
461
|
+
if [ "${{ matrix.smoke }}" = "health" ]; then
|
|
462
|
+
if curl -sf "http://127.0.0.1:${{ matrix.port }}/health"; then
|
|
463
|
+
echo "gateway /health ok"
|
|
464
|
+
exit 0
|
|
465
|
+
fi
|
|
466
|
+
else
|
|
467
|
+
# Bind smoke: any HTTP response (or even a reset after accept)
|
|
468
|
+
# means the UI process bound 7860. Do not use curl -f — Gradio
|
|
469
|
+
# may 4xx before the app is fully ready.
|
|
470
|
+
if curl -sS --connect-timeout 2 -o /dev/null \
|
|
471
|
+
"http://127.0.0.1:${{ matrix.port }}/"; then
|
|
472
|
+
echo "ui port ${{ matrix.port }} bound"
|
|
473
|
+
exit 0
|
|
474
|
+
fi
|
|
475
|
+
fi
|
|
476
|
+
sleep 2
|
|
477
|
+
done
|
|
478
|
+
echo "::error::docker smoke (${{ matrix.target }}) did not become ready on port ${{ matrix.port }}"
|
|
479
|
+
exit 1
|
|
368
480
|
|
|
369
481
|
# ===========================================================================
|
|
370
482
|
# GitHub Pages build + deploy (folded in from former pages.yml)
|
|
@@ -425,4 +537,4 @@ jobs:
|
|
|
425
537
|
url: ${{ steps.deployment.outputs.page_url }}
|
|
426
538
|
steps:
|
|
427
539
|
- id: deployment
|
|
428
|
-
uses: actions/deploy-pages@
|
|
540
|
+
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
|
|
@@ -60,7 +60,7 @@ jobs:
|
|
|
60
60
|
- name: Set up Python
|
|
61
61
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
62
62
|
with:
|
|
63
|
-
python-version: '3.
|
|
63
|
+
python-version: '3.12'
|
|
64
64
|
|
|
65
65
|
- name: Install build tools
|
|
66
66
|
run: |
|
|
@@ -107,7 +107,7 @@ jobs:
|
|
|
107
107
|
path: dist/
|
|
108
108
|
|
|
109
109
|
- name: Publish to PyPI
|
|
110
|
-
uses: pypa/gh-action-pypi-publish@
|
|
110
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
111
111
|
with:
|
|
112
112
|
# CT-B-009: emit SLSA build provenance attestations for the wheel
|
|
113
113
|
# and sdist. PyPI verifies the attestation against the workflow's
|
|
@@ -116,7 +116,7 @@ jobs:
|
|
|
116
116
|
attestations: true
|
|
117
117
|
|
|
118
118
|
docker:
|
|
119
|
-
name: Publish to GHCR
|
|
119
|
+
name: Publish ${{ matrix.stage }} to GHCR
|
|
120
120
|
runs-on: ubuntu-latest
|
|
121
121
|
# PyPI ships first (CDS-B-005). If PyPI fails, Docker must not ship a
|
|
122
122
|
# half-release; if PyPI succeeds, Docker becomes the recoverable tail.
|
|
@@ -124,6 +124,16 @@ jobs:
|
|
|
124
124
|
# CT-B-011: multi-arch buildx (linux/amd64 + linux/arm64) + push to GHCR
|
|
125
125
|
# is the longest leg; 30 min covers the warm cache path with headroom.
|
|
126
126
|
timeout-minutes: 30
|
|
127
|
+
strategy:
|
|
128
|
+
fail-fast: false
|
|
129
|
+
matrix:
|
|
130
|
+
include:
|
|
131
|
+
# mcp-gateway is the last Dockerfile stage; pin it explicitly so a
|
|
132
|
+
# later debug/sbom stage cannot silently retarget :latest.
|
|
133
|
+
- stage: gateway
|
|
134
|
+
target: mcp-gateway
|
|
135
|
+
- stage: ui
|
|
136
|
+
target: production
|
|
127
137
|
permissions:
|
|
128
138
|
contents: read
|
|
129
139
|
packages: write
|
|
@@ -142,7 +152,7 @@ jobs:
|
|
|
142
152
|
ref: ${{ github.event.workflow_run.head_branch || github.event.release.tag_name || github.ref_name }}
|
|
143
153
|
|
|
144
154
|
- name: Log in to GHCR
|
|
145
|
-
uses: docker/login-action@
|
|
155
|
+
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
146
156
|
with:
|
|
147
157
|
registry: ${{ env.REGISTRY }}
|
|
148
158
|
username: ${{ github.actor }}
|
|
@@ -164,43 +174,52 @@ jobs:
|
|
|
164
174
|
V="${REF#v}"
|
|
165
175
|
echo "version=${V}" >> "$GITHUB_OUTPUT"
|
|
166
176
|
echo "major_minor=$(echo "$V" | cut -d. -f1-2)" >> "$GITHUB_OUTPUT"
|
|
167
|
-
echo "resolved=${V} (event=${{ github.event_name }} ref=${REF})"
|
|
177
|
+
echo "resolved=${V} (event=${{ github.event_name }} ref=${REF} stage=${{ matrix.stage }})"
|
|
168
178
|
|
|
169
179
|
- name: Extract metadata
|
|
170
180
|
id: meta
|
|
171
|
-
uses: docker/metadata-action@
|
|
181
|
+
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
|
172
182
|
with:
|
|
173
183
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
184
|
+
flavor: |
|
|
185
|
+
latest=false
|
|
174
186
|
# type=raw from the explicitly-resolved version (see Resolve step) —
|
|
175
187
|
# NOT type=semver, which silently emits nothing under workflow_run
|
|
176
188
|
# because github.ref is main, not the tag. Each enable=... gates its
|
|
177
189
|
# raw tag so an empty/missing version never pushes a bogus tag.
|
|
190
|
+
# Gateway: :latest :gateway :X.Y.Z :X.Y
|
|
191
|
+
# UI: :ui :ui-X.Y.Z
|
|
178
192
|
tags: |
|
|
179
|
-
type=raw,value=${{ steps.ver.outputs.version }},enable=${{ steps.ver.outputs.version != '' }}
|
|
180
|
-
type=raw,value=${{ steps.ver.outputs.major_minor }},enable=${{ steps.ver.outputs.major_minor != '' }}
|
|
181
|
-
type=
|
|
182
|
-
type=raw,value=
|
|
193
|
+
type=raw,value=${{ steps.ver.outputs.version }},enable=${{ matrix.stage == 'gateway' && steps.ver.outputs.version != '' }}
|
|
194
|
+
type=raw,value=${{ steps.ver.outputs.major_minor }},enable=${{ matrix.stage == 'gateway' && steps.ver.outputs.major_minor != '' }}
|
|
195
|
+
type=raw,value=latest,enable=${{ matrix.stage == 'gateway' }}
|
|
196
|
+
type=raw,value=gateway,enable=${{ matrix.stage == 'gateway' }}
|
|
197
|
+
type=sha,enable=${{ matrix.stage == 'gateway' }}
|
|
198
|
+
type=raw,value=ui-${{ steps.ver.outputs.version }},enable=${{ matrix.stage == 'ui' && steps.ver.outputs.version != '' }}
|
|
199
|
+
type=raw,value=ui,enable=${{ matrix.stage == 'ui' }}
|
|
200
|
+
type=sha,prefix=ui-,enable=${{ matrix.stage == 'ui' }}
|
|
183
201
|
|
|
184
202
|
# QEMU is required to cross-build linux/arm64 from an amd64 runner.
|
|
185
203
|
- name: Set up QEMU
|
|
186
204
|
# SHA-pinned per SLSA L3 supply-chain hygiene (CT-B-002). publish job
|
|
187
205
|
# carries packages:write — a compromised mutable v3 tag would inherit
|
|
188
206
|
# GHCR write capability during a release window. Bump via dependabot.
|
|
189
|
-
uses: docker/setup-qemu-action@
|
|
207
|
+
uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0
|
|
190
208
|
|
|
191
209
|
- name: Set up Docker Buildx
|
|
192
|
-
uses: docker/setup-buildx-action@
|
|
210
|
+
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
|
193
211
|
|
|
194
212
|
- name: Build and push
|
|
195
|
-
uses: docker/build-push-action@
|
|
213
|
+
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
|
196
214
|
with:
|
|
197
215
|
context: .
|
|
216
|
+
target: ${{ matrix.target }}
|
|
198
217
|
push: true
|
|
199
218
|
platforms: linux/amd64,linux/arm64
|
|
200
219
|
tags: ${{ steps.meta.outputs.tags }}
|
|
201
220
|
labels: ${{ steps.meta.outputs.labels }}
|
|
202
|
-
cache-from: type=gha
|
|
203
|
-
cache-to: type=gha,mode=max
|
|
221
|
+
cache-from: type=gha,scope=ghcr-${{ matrix.target }}
|
|
222
|
+
cache-to: type=gha,mode=max,scope=ghcr-${{ matrix.target }}
|
|
204
223
|
# CT-B-009: SLSA provenance mode=max emits buildx provenance
|
|
205
224
|
# attestation (including all input source digests) attached to the
|
|
206
225
|
# image manifest. sbom: true emits an SPDX SBOM attestation in the
|
|
@@ -234,7 +253,7 @@ jobs:
|
|
|
234
253
|
- name: Set up Python
|
|
235
254
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
236
255
|
with:
|
|
237
|
-
python-version: '3.
|
|
256
|
+
python-version: '3.12'
|
|
238
257
|
|
|
239
258
|
- name: PyPI smoke — install + --version matches tag
|
|
240
259
|
run: |
|
|
@@ -264,22 +283,30 @@ jobs:
|
|
|
264
283
|
}
|
|
265
284
|
|
|
266
285
|
- name: Log in to GHCR (pull)
|
|
267
|
-
uses: docker/login-action@
|
|
286
|
+
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
268
287
|
with:
|
|
269
288
|
registry: ${{ env.REGISTRY }}
|
|
270
289
|
username: ${{ github.actor }}
|
|
271
290
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
272
291
|
|
|
273
|
-
- name: Docker smoke — pull + --version matches tag
|
|
292
|
+
- name: Docker smoke — pull gateway + UI, --version matches tag
|
|
274
293
|
run: |
|
|
275
294
|
set -euo pipefail
|
|
276
295
|
TAG="${{ steps.ver.outputs.tag }}"
|
|
277
296
|
VERSION="${{ steps.ver.outputs.version }}"
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
297
|
+
GATEWAY="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION}"
|
|
298
|
+
UI="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:ui-${VERSION}"
|
|
299
|
+
docker pull "$GATEWAY"
|
|
300
|
+
docker pull "$UI"
|
|
301
|
+
OUT="$(docker run --rm --entrypoint tool-compass "$GATEWAY" --version 2>&1 || true)"
|
|
302
|
+
echo "docker gateway --version → $OUT"
|
|
282
303
|
echo "$OUT" | grep -Fq "${VERSION}" || {
|
|
283
|
-
echo "::error::Docker smoke failed: --version output did not contain ${VERSION}"
|
|
304
|
+
echo "::error::Docker gateway smoke failed: --version output did not contain ${VERSION}"
|
|
305
|
+
exit 1
|
|
306
|
+
}
|
|
307
|
+
OUT_UI="$(docker run --rm --entrypoint tool-compass "$UI" --version 2>&1 || true)"
|
|
308
|
+
echo "docker ui --version → $OUT_UI"
|
|
309
|
+
echo "$OUT_UI" | grep -Fq "${VERSION}" || {
|
|
310
|
+
echo "::error::Docker UI smoke failed: --version output did not contain ${VERSION}"
|
|
284
311
|
exit 1
|
|
285
312
|
}
|