fastretrieval 1.0.0__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.
- fastretrieval-1.0.0/.coderabbit.yaml +23 -0
- fastretrieval-1.0.0/.github/CODEOWNERS +2 -0
- fastretrieval-1.0.0/.github/ISSUE_TEMPLATE/bug_report.md +34 -0
- fastretrieval-1.0.0/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- fastretrieval-1.0.0/.github/PULL_REQUEST_TEMPLATE.md +25 -0
- fastretrieval-1.0.0/.github/review-learnings.md +14 -0
- fastretrieval-1.0.0/.github/rulesets/main.json +63 -0
- fastretrieval-1.0.0/.github/workflows/bot-governance.yml +99 -0
- fastretrieval-1.0.0/.github/workflows/cd.yml +355 -0
- fastretrieval-1.0.0/.github/workflows/ci.yml +174 -0
- fastretrieval-1.0.0/.github/workflows/codeql.yml +30 -0
- fastretrieval-1.0.0/.github/workflows/convert.yml +71 -0
- fastretrieval-1.0.0/.github/workflows/integration.yml +184 -0
- fastretrieval-1.0.0/.github/workflows/opencode.yml +71 -0
- fastretrieval-1.0.0/.github/workflows/pr-title.yml +59 -0
- fastretrieval-1.0.0/.gitignore +74 -0
- fastretrieval-1.0.0/.infisical.json +5 -0
- fastretrieval-1.0.0/.mise.toml +41 -0
- fastretrieval-1.0.0/.pre-commit-config.yaml +89 -0
- fastretrieval-1.0.0/.skret.yaml +8 -0
- fastretrieval-1.0.0/AGENTS.md +175 -0
- fastretrieval-1.0.0/CHANGELOG.md +1698 -0
- fastretrieval-1.0.0/CLAUDE.md +124 -0
- fastretrieval-1.0.0/CODE_OF_CONDUCT.md +46 -0
- fastretrieval-1.0.0/CONTRIBUTING.md +21 -0
- fastretrieval-1.0.0/Dockerfile +143 -0
- fastretrieval-1.0.0/LICENSE +201 -0
- fastretrieval-1.0.0/NOTICE +13 -0
- fastretrieval-1.0.0/PKG-INFO +460 -0
- fastretrieval-1.0.0/README.md +423 -0
- fastretrieval-1.0.0/SECURITY.md +15 -0
- fastretrieval-1.0.0/fastretrieval/__init__.py +37 -0
- fastretrieval-1.0.0/fastretrieval/common/__init__.py +0 -0
- fastretrieval-1.0.0/fastretrieval/common/custom_model.py +120 -0
- fastretrieval-1.0.0/fastretrieval/common/model_description.py +60 -0
- fastretrieval-1.0.0/fastretrieval/common/model_management.py +883 -0
- fastretrieval-1.0.0/fastretrieval/common/onnx_model.py +273 -0
- fastretrieval-1.0.0/fastretrieval/common/preprocessor_utils.py +74 -0
- fastretrieval-1.0.0/fastretrieval/common/types.py +25 -0
- fastretrieval-1.0.0/fastretrieval/common/utils.py +180 -0
- fastretrieval-1.0.0/fastretrieval/contract/__init__.py +36 -0
- fastretrieval-1.0.0/fastretrieval/contract/manifest.py +168 -0
- fastretrieval-1.0.0/fastretrieval/contract/model.py +203 -0
- fastretrieval-1.0.0/fastretrieval/contract/preprocessor.py +129 -0
- fastretrieval-1.0.0/fastretrieval/convert/__init__.py +40 -0
- fastretrieval-1.0.0/fastretrieval/convert/__main__.py +7 -0
- fastretrieval-1.0.0/fastretrieval/convert/card.py +100 -0
- fastretrieval-1.0.0/fastretrieval/convert/cli.py +212 -0
- fastretrieval-1.0.0/fastretrieval/convert/gguf.py +174 -0
- fastretrieval-1.0.0/fastretrieval/convert/heads.py +103 -0
- fastretrieval-1.0.0/fastretrieval/convert/manifest.py +42 -0
- fastretrieval-1.0.0/fastretrieval/convert/modal_backend.py +231 -0
- fastretrieval-1.0.0/fastretrieval/convert/onnx.py +230 -0
- fastretrieval-1.0.0/fastretrieval/convert/profiles.py +378 -0
- fastretrieval-1.0.0/fastretrieval/convert/requirements.txt +10 -0
- fastretrieval-1.0.0/fastretrieval/convert/verify.py +360 -0
- fastretrieval-1.0.0/fastretrieval/export.py +68 -0
- fastretrieval-1.0.0/fastretrieval/models/__init__.py +14 -0
- fastretrieval-1.0.0/fastretrieval/models/image/__init__.py +4 -0
- fastretrieval-1.0.0/fastretrieval/models/image/image_embedding.py +83 -0
- fastretrieval-1.0.0/fastretrieval/models/image/image_embedding_base.py +45 -0
- fastretrieval-1.0.0/fastretrieval/models/image/onnx_embedding.py +168 -0
- fastretrieval-1.0.0/fastretrieval/models/image/onnx_image_model.py +186 -0
- fastretrieval-1.0.0/fastretrieval/models/image/transform/__init__.py +5 -0
- fastretrieval-1.0.0/fastretrieval/models/image/transform/functional.py +216 -0
- fastretrieval-1.0.0/fastretrieval/models/image/transform/operators.py +491 -0
- fastretrieval-1.0.0/fastretrieval/models/late_interaction/__init__.py +6 -0
- fastretrieval-1.0.0/fastretrieval/models/late_interaction/colbert.py +315 -0
- fastretrieval-1.0.0/fastretrieval/models/late_interaction/late_interaction_embedding_base.py +82 -0
- fastretrieval-1.0.0/fastretrieval/models/late_interaction/late_interaction_text_embedding.py +180 -0
- fastretrieval-1.0.0/fastretrieval/models/late_interaction/token_embeddings.py +84 -0
- fastretrieval-1.0.0/fastretrieval/models/late_interaction_multimodal/__init__.py +6 -0
- fastretrieval-1.0.0/fastretrieval/models/late_interaction_multimodal/colpali.py +219 -0
- fastretrieval-1.0.0/fastretrieval/models/late_interaction_multimodal/late_interaction_multimodal_embedding.py +115 -0
- fastretrieval-1.0.0/fastretrieval/models/late_interaction_multimodal/late_interaction_multimodal_embedding_base.py +57 -0
- fastretrieval-1.0.0/fastretrieval/models/late_interaction_multimodal/onnx_multimodal_model.py +281 -0
- fastretrieval-1.0.0/fastretrieval/models/sparse/__init__.py +5 -0
- fastretrieval-1.0.0/fastretrieval/models/sparse/sparse_embedding_base.py +86 -0
- fastretrieval-1.0.0/fastretrieval/models/sparse/sparse_text_embedding.py +131 -0
- fastretrieval-1.0.0/fastretrieval/models/sparse/splade_pp.py +187 -0
- fastretrieval-1.0.0/fastretrieval/parallel_processor.py +318 -0
- fastretrieval-1.0.0/fastretrieval/preprocessing/__init__.py +6 -0
- fastretrieval-1.0.0/fastretrieval/preprocessing/graph.py +39 -0
- fastretrieval-1.0.0/fastretrieval/preprocessing/image.py +53 -0
- fastretrieval-1.0.0/fastretrieval/py.typed +1 -0
- fastretrieval-1.0.0/fastretrieval/rerank/__init__.py +3 -0
- fastretrieval-1.0.0/fastretrieval/rerank/cross_encoder/__init__.py +0 -0
- fastretrieval-1.0.0/fastretrieval/rerank/cross_encoder/custom_text_cross_encoder.py +69 -0
- fastretrieval-1.0.0/fastretrieval/rerank/cross_encoder/gguf_cross_encoder.py +247 -0
- fastretrieval-1.0.0/fastretrieval/rerank/cross_encoder/onnx_text_cross_encoder.py +211 -0
- fastretrieval-1.0.0/fastretrieval/rerank/cross_encoder/onnx_text_model.py +217 -0
- fastretrieval-1.0.0/fastretrieval/rerank/cross_encoder/qwen3_cross_encoder.py +324 -0
- fastretrieval-1.0.0/fastretrieval/rerank/cross_encoder/text_cross_encoder.py +210 -0
- fastretrieval-1.0.0/fastretrieval/rerank/cross_encoder/text_cross_encoder_base.py +64 -0
- fastretrieval-1.0.0/fastretrieval/text/__init__.py +3 -0
- fastretrieval-1.0.0/fastretrieval/text/custom_text_embedding.py +124 -0
- fastretrieval-1.0.0/fastretrieval/text/gguf_embedding.py +211 -0
- fastretrieval-1.0.0/fastretrieval/text/onnx_embedding.py +188 -0
- fastretrieval-1.0.0/fastretrieval/text/onnx_text_model.py +188 -0
- fastretrieval-1.0.0/fastretrieval/text/pooled_embedding.py +68 -0
- fastretrieval-1.0.0/fastretrieval/text/pooled_normalized_embedding.py +60 -0
- fastretrieval-1.0.0/fastretrieval/text/qwen3_embedding.py +197 -0
- fastretrieval-1.0.0/fastretrieval/text/text_embedding.py +263 -0
- fastretrieval-1.0.0/fastretrieval/text/text_embedding_base.py +76 -0
- fastretrieval-1.0.0/pyproject.toml +102 -0
- fastretrieval-1.0.0/renovate.json +151 -0
- fastretrieval-1.0.0/scripts/clean-venv.mjs +112 -0
- fastretrieval-1.0.0/scripts/enforce-commit.sh +8 -0
- fastretrieval-1.0.0/scripts/export_optimized_reranker.py +221 -0
- fastretrieval-1.0.0/scripts/preserve-diacritics.py +290 -0
- fastretrieval-1.0.0/scripts/repo-bootstrap/_lib.py +533 -0
- fastretrieval-1.0.0/scripts/repo-bootstrap/verify_readme_sync.py +512 -0
- fastretrieval-1.0.0/scripts/test_preserve_diacritics.py +180 -0
- fastretrieval-1.0.0/tests/__init__.py +0 -0
- fastretrieval-1.0.0/tests/convert/__init__.py +0 -0
- fastretrieval-1.0.0/tests/convert/fixtures/qwen3/config.json +6 -0
- fastretrieval-1.0.0/tests/convert/fixtures/tiny-e5/config.json +7 -0
- fastretrieval-1.0.0/tests/convert/test_card.py +64 -0
- fastretrieval-1.0.0/tests/convert/test_cli.py +73 -0
- fastretrieval-1.0.0/tests/convert/test_gguf.py +46 -0
- fastretrieval-1.0.0/tests/convert/test_heads.py +32 -0
- fastretrieval-1.0.0/tests/convert/test_manifest.py +239 -0
- fastretrieval-1.0.0/tests/convert/test_modal_backend.py +80 -0
- fastretrieval-1.0.0/tests/convert/test_onnx.py +61 -0
- fastretrieval-1.0.0/tests/convert/test_verify.py +109 -0
- fastretrieval-1.0.0/tests/test_colpali.py +97 -0
- fastretrieval-1.0.0/tests/test_common_custom_model.py +95 -0
- fastretrieval-1.0.0/tests/test_contract_preprocessor.py +69 -0
- fastretrieval-1.0.0/tests/test_cross_encoder_onnx.py +857 -0
- fastretrieval-1.0.0/tests/test_custom_cross_encoder.py +113 -0
- fastretrieval-1.0.0/tests/test_custom_model.py +217 -0
- fastretrieval-1.0.0/tests/test_custom_reranker_registry.py +124 -0
- fastretrieval-1.0.0/tests/test_custom_text_cross_encoder.py +95 -0
- fastretrieval-1.0.0/tests/test_custom_text_embedding.py +372 -0
- fastretrieval-1.0.0/tests/test_env_compat.py +20 -0
- fastretrieval-1.0.0/tests/test_export.py +80 -0
- fastretrieval-1.0.0/tests/test_gguf_cross_encoder.py +577 -0
- fastretrieval-1.0.0/tests/test_gguf_embedding.py +484 -0
- fastretrieval-1.0.0/tests/test_image_embedding.py +97 -0
- fastretrieval-1.0.0/tests/test_integration.py +629 -0
- fastretrieval-1.0.0/tests/test_integration_gguf.py +203 -0
- fastretrieval-1.0.0/tests/test_integration_q4f16.py +197 -0
- fastretrieval-1.0.0/tests/test_last_token_pool_robustness.py +61 -0
- fastretrieval-1.0.0/tests/test_late_interaction.py +16 -0
- fastretrieval-1.0.0/tests/test_model_contract.py +114 -0
- fastretrieval-1.0.0/tests/test_model_description.py +69 -0
- fastretrieval-1.0.0/tests/test_model_management.py +2066 -0
- fastretrieval-1.0.0/tests/test_model_management_extra.py +362 -0
- fastretrieval-1.0.0/tests/test_onnx_embedding.py +184 -0
- fastretrieval-1.0.0/tests/test_onnx_embedding_shapes.py +30 -0
- fastretrieval-1.0.0/tests/test_onnx_model.py +447 -0
- fastretrieval-1.0.0/tests/test_package_identity.py +29 -0
- fastretrieval-1.0.0/tests/test_parallel_processor.py +819 -0
- fastretrieval-1.0.0/tests/test_pooled_embedding.py +105 -0
- fastretrieval-1.0.0/tests/test_pooled_normalized_embedding.py +83 -0
- fastretrieval-1.0.0/tests/test_pooling.py +94 -0
- fastretrieval-1.0.0/tests/test_preprocessing_graph.py +37 -0
- fastretrieval-1.0.0/tests/test_preprocessor_utils.py +187 -0
- fastretrieval-1.0.0/tests/test_qwen3_embedding.py +96 -0
- fastretrieval-1.0.0/tests/test_qwen3_reranker.py +336 -0
- fastretrieval-1.0.0/tests/test_security_limits.py +111 -0
- fastretrieval-1.0.0/tests/test_sparse_embedding.py +21 -0
- fastretrieval-1.0.0/tests/test_text_cross_encoder.py +56 -0
- fastretrieval-1.0.0/tests/test_text_cross_encoder_base.py +44 -0
- fastretrieval-1.0.0/tests/test_text_embedding.py +131 -0
- fastretrieval-1.0.0/tests/test_text_embedding_base.py +124 -0
- fastretrieval-1.0.0/tests/test_text_embedding_embed.py +52 -0
- fastretrieval-1.0.0/tests/test_text_onnx_text_model.py +745 -0
- fastretrieval-1.0.0/tests/test_types.py +69 -0
- fastretrieval-1.0.0/tests/test_utils.py +261 -0
- fastretrieval-1.0.0/uv.lock +1106 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
language: "en-US"
|
|
2
|
+
reviews:
|
|
3
|
+
auto_review:
|
|
4
|
+
enabled: true
|
|
5
|
+
drafts: false
|
|
6
|
+
ignore_usernames:
|
|
7
|
+
- "n24q02m"
|
|
8
|
+
- "dependabot[bot]"
|
|
9
|
+
- "renovate[bot]"
|
|
10
|
+
- "github-actions[bot]"
|
|
11
|
+
- "devin-ai-integration[bot]"
|
|
12
|
+
- "google-labs-jules[bot]"
|
|
13
|
+
profile: "chill"
|
|
14
|
+
request_changes_workflow: false
|
|
15
|
+
high_level_summary: true
|
|
16
|
+
auto_incremental_review: true
|
|
17
|
+
issue_enrichment:
|
|
18
|
+
auto_enrich:
|
|
19
|
+
enabled: true
|
|
20
|
+
planning:
|
|
21
|
+
enabled: true
|
|
22
|
+
chat:
|
|
23
|
+
auto_reply: false
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Create a report to help us improve
|
|
4
|
+
title: ''
|
|
5
|
+
labels: ''
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**Describe the bug**
|
|
10
|
+
A clear and concise description of what the bug is.
|
|
11
|
+
|
|
12
|
+
**To Reproduce**
|
|
13
|
+
Steps to reproduce the behavior:
|
|
14
|
+
|
|
15
|
+
1. Go to '...'
|
|
16
|
+
2. Click on '....'
|
|
17
|
+
3. Scroll down to '....'
|
|
18
|
+
4. See error
|
|
19
|
+
|
|
20
|
+
**Expected behavior**
|
|
21
|
+
A clear and concise description of what you expected to happen.
|
|
22
|
+
|
|
23
|
+
**Screenshots**
|
|
24
|
+
If applicable, add screenshots to help explain your problem.
|
|
25
|
+
|
|
26
|
+
**Environment (please complete the following information):**
|
|
27
|
+
|
|
28
|
+
- OS: [e.g. macOS, Linux, Windows]
|
|
29
|
+
- Go version: [output of `go version`]
|
|
30
|
+
- fastretrieval version: [output of `fastretrieval --version`]
|
|
31
|
+
- Installation method: [e.g. go install, Docker, brew, scoop, apt, direct binary]
|
|
32
|
+
|
|
33
|
+
**Additional context**
|
|
34
|
+
Add any other context about the problem here.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an idea for this project
|
|
4
|
+
title: ''
|
|
5
|
+
labels: ''
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**Is your feature request related to a problem? Please describe.**
|
|
10
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
11
|
+
|
|
12
|
+
**Describe the solution you'd like**
|
|
13
|
+
A clear and concise description of what you want to happen.
|
|
14
|
+
|
|
15
|
+
**Describe alternatives you've considered**
|
|
16
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
|
17
|
+
|
|
18
|
+
**Additional context**
|
|
19
|
+
Add any other context or screenshots about the feature request here.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
<!-- Describe what this PR does and why -->
|
|
3
|
+
|
|
4
|
+
## Changes
|
|
5
|
+
<!-- List the key changes -->
|
|
6
|
+
-
|
|
7
|
+
|
|
8
|
+
## Type of Change
|
|
9
|
+
- [ ] Bug fix (non-breaking change which fixes an issue)
|
|
10
|
+
- [ ] New feature (non-breaking change which adds functionality)
|
|
11
|
+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
|
12
|
+
|
|
13
|
+
## Testing
|
|
14
|
+
- [ ] Existing tests pass
|
|
15
|
+
- [ ] Added new tests for the changes
|
|
16
|
+
- [ ] Tested manually
|
|
17
|
+
|
|
18
|
+
## Screenshots
|
|
19
|
+
<!-- If applicable, add screenshots to help explain your changes -->
|
|
20
|
+
|
|
21
|
+
## Checklist
|
|
22
|
+
- [ ] My code follows the project's coding standards
|
|
23
|
+
- [ ] I have commented my code where necessary
|
|
24
|
+
- [ ] I have updated documentation if needed
|
|
25
|
+
- [ ] My changes generate no new warnings
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Review learnings
|
|
2
|
+
|
|
3
|
+
Binding rules for the automated reviewer. It reads this file before every review
|
|
4
|
+
and before answering an issue, and it must obey every rule here.
|
|
5
|
+
|
|
6
|
+
Add a rule whenever the reviewer gives feedback that turns out to be wrong for
|
|
7
|
+
this codebase, so it never repeats that feedback. One rule per bullet, scoped by
|
|
8
|
+
path when it only applies in one place.
|
|
9
|
+
|
|
10
|
+
Format: `` `path/glob` `` — the rule, and the reason it exists.
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
|
|
14
|
+
_None recorded yet._
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Main Branch Rules",
|
|
3
|
+
"target": "branch",
|
|
4
|
+
"source_type": "Repository",
|
|
5
|
+
"source": "n24q02m/fastretrieval",
|
|
6
|
+
"enforcement": "active",
|
|
7
|
+
"bypass_actors": [
|
|
8
|
+
{
|
|
9
|
+
"actor_id": 5,
|
|
10
|
+
"actor_type": "RepositoryRole",
|
|
11
|
+
"bypass_mode": "always"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"actor_id": 3200052,
|
|
15
|
+
"actor_type": "Integration",
|
|
16
|
+
"bypass_mode": "always"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"conditions": {
|
|
20
|
+
"ref_name": {
|
|
21
|
+
"exclude": [],
|
|
22
|
+
"include": [
|
|
23
|
+
"~DEFAULT_BRANCH"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"rules": [
|
|
28
|
+
{
|
|
29
|
+
"type": "deletion"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "non_fast_forward"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"type": "required_linear_history"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"type": "pull_request",
|
|
39
|
+
"parameters": {
|
|
40
|
+
"required_approving_review_count": 0,
|
|
41
|
+
"dismiss_stale_reviews_on_push": true,
|
|
42
|
+
"require_code_owner_review": false,
|
|
43
|
+
"require_last_push_approval": false,
|
|
44
|
+
"required_review_thread_resolution": false,
|
|
45
|
+
"allowed_merge_methods": [
|
|
46
|
+
"squash"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"type": "code_scanning",
|
|
52
|
+
"parameters": {
|
|
53
|
+
"code_scanning_tools": [
|
|
54
|
+
{
|
|
55
|
+
"tool": "CodeQL",
|
|
56
|
+
"security_alerts_threshold": "high_or_higher",
|
|
57
|
+
"alerts_threshold": "errors"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
name: Bot PR Governance
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types:
|
|
6
|
+
- opened
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
pull-requests: write
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
governance:
|
|
14
|
+
name: Bot PR Rate Limit & Duplicate Check
|
|
15
|
+
# Internal PRs only. This repo is public, so `pull_request` gives a fork a
|
|
16
|
+
# read-only GITHUB_TOKEN that the `permissions:` block above cannot raise --
|
|
17
|
+
# `gh pr close` would then fail under `set -e` and abort the job. Closing an
|
|
18
|
+
# outside contributor's PR because someone else spent the rate limit is the
|
|
19
|
+
# wrong outcome anyway; the bots this governs push their branches to this
|
|
20
|
+
# repo, so fork PRs are never in scope. Comparing full_name rather than
|
|
21
|
+
# head.repo.fork keeps this correct if this repository is ever itself a
|
|
22
|
+
# fork, where head.repo.fork would be true for internal branches too.
|
|
23
|
+
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- name: Detect and govern bot PRs
|
|
27
|
+
env:
|
|
28
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
29
|
+
BRANCH: ${{ github.head_ref }}
|
|
30
|
+
ACTOR: ${{ github.actor }}
|
|
31
|
+
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
32
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
33
|
+
REPO: ${{ github.repository }}
|
|
34
|
+
run: |
|
|
35
|
+
set -e
|
|
36
|
+
|
|
37
|
+
# Detect if this is a bot-created PR by branch name pattern.
|
|
38
|
+
# Two signals:
|
|
39
|
+
# 1. Explicit bot-tool prefixes (Bolt, Palette, Jules, fix/web-scraper)
|
|
40
|
+
# 2. Any branch ending with a long numeric task/trace ID (-[0-9]{14,})
|
|
41
|
+
IS_BOT_PR=false
|
|
42
|
+
if echo "$BRANCH" | grep -qE \
|
|
43
|
+
'^(bolt[-/]|palette-ux-|fix/web-scraper-|jules[-/])'; then
|
|
44
|
+
IS_BOT_PR=true
|
|
45
|
+
elif echo "$BRANCH" | grep -qE -- \
|
|
46
|
+
'-[0-9]{14,}$'; then
|
|
47
|
+
IS_BOT_PR=true
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
if [ "$IS_BOT_PR" = "false" ]; then
|
|
51
|
+
echo "Not a bot PR ($BRANCH), skipping governance checks."
|
|
52
|
+
exit 0
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
echo "Bot PR detected on branch: $BRANCH"
|
|
56
|
+
gh pr edit "$PR_NUMBER" --add-label "bot-generated" --repo "$REPO" || true
|
|
57
|
+
|
|
58
|
+
# --- Rate limit: max 5 bot PRs/actor/day ---
|
|
59
|
+
SINCE=$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || \
|
|
60
|
+
date -u -v-24H +%Y-%m-%dT%H:%M:%SZ)
|
|
61
|
+
COUNT=$(gh pr list --repo "$REPO" --state open \
|
|
62
|
+
--json createdAt,headRefName,author \
|
|
63
|
+
--jq "[.[] |
|
|
64
|
+
select(.author.login == \"$ACTOR\") |
|
|
65
|
+
select(.createdAt > \"$SINCE\") |
|
|
66
|
+
select(.headRefName | test(
|
|
67
|
+
\"^(bolt[-/]|palette-ux-|fix/web-scraper-|jules[-/])\" +
|
|
68
|
+
\"|-[0-9]{14,}$\"
|
|
69
|
+
))] | length" 2>/dev/null || echo 0)
|
|
70
|
+
|
|
71
|
+
echo "Bot PRs by $ACTOR in last 24h: $COUNT"
|
|
72
|
+
|
|
73
|
+
if [ "${COUNT:-0}" -gt 5 ]; then
|
|
74
|
+
gh pr close "$PR_NUMBER" --repo "$REPO" \
|
|
75
|
+
--comment "**Bot rate limit exceeded**: $COUNT PRs created in the last 24 hours (limit: 5/day). This PR has been automatically closed to reduce PR backlog and CI resource waste. Please consolidate related fixes."
|
|
76
|
+
echo "PR #$PR_NUMBER closed: rate limit exceeded ($COUNT/5 today)"
|
|
77
|
+
exit 0
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# --- Duplicate detection by title keyword ---
|
|
81
|
+
KEYWORD=$(echo "$PR_TITLE" | sed 's/[^a-zA-Z0-9 _-]//g' | \
|
|
82
|
+
tr '[:upper:]' '[:lower:]' | cut -c1-50 | xargs)
|
|
83
|
+
if [ -z "$KEYWORD" ]; then
|
|
84
|
+
echo "No keyword extracted, skipping duplicate check."
|
|
85
|
+
exit 0
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
DUPES=$(gh pr list --repo "$REPO" --state open --search "$KEYWORD" \
|
|
89
|
+
--json number \
|
|
90
|
+
--jq "[.[] | select(.number != $PR_NUMBER)] | length" \
|
|
91
|
+
2>/dev/null || echo 0)
|
|
92
|
+
|
|
93
|
+
echo "Similar open PRs for '$KEYWORD': $DUPES"
|
|
94
|
+
if [ "${DUPES:-0}" -ge 1 ]; then
|
|
95
|
+
gh pr edit "$PR_NUMBER" --repo "$REPO" \
|
|
96
|
+
--add-label "possible-duplicate" || true
|
|
97
|
+
gh pr comment "$PR_NUMBER" --repo "$REPO" \
|
|
98
|
+
--body "**Possible duplicate**: found ${DUPES} open PR(s) with similar title ('${KEYWORD}'). Check before merging: \`gh pr list --search '${KEYWORD}' --state open\`"
|
|
99
|
+
fi
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# CD Template: Python. Publish steps are opt-in per repo via
|
|
3
|
+
# vars.PUBLISH_PYPI / vars.PUBLISH_DOCKER / vars.PUBLISH_MCP_REGISTRY.
|
|
4
|
+
# Managed by n24q02m repo-bootstrap; local changes may be overwritten.
|
|
5
|
+
#
|
|
6
|
+
# Pipeline: PSR v10 -> PyPI -> Docker multi-arch (amd64+arm64) -> MCP Registry
|
|
7
|
+
# CUSTOMIZE: Search for "# CUSTOMIZE:" comments and adjust for your repo.
|
|
8
|
+
# =============================================================================
|
|
9
|
+
name: CD
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
inputs:
|
|
14
|
+
release_type:
|
|
15
|
+
description: "Release type"
|
|
16
|
+
required: true
|
|
17
|
+
type: choice
|
|
18
|
+
options:
|
|
19
|
+
- beta
|
|
20
|
+
- stable
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
|
|
25
|
+
env:
|
|
26
|
+
DOCKERHUB_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/fastretrieval
|
|
27
|
+
GHCR_IMAGE: ghcr.io/${{ github.repository }}
|
|
28
|
+
|
|
29
|
+
concurrency:
|
|
30
|
+
group: release
|
|
31
|
+
cancel-in-progress: false
|
|
32
|
+
|
|
33
|
+
jobs:
|
|
34
|
+
# ============================================================================
|
|
35
|
+
# README registry metadata: the package carries the repo's README to PyPI, so
|
|
36
|
+
# a manifest that names a file which is not there publishes an empty project
|
|
37
|
+
# page. Runs before the release rather than beside it -- a PyPI upload cannot
|
|
38
|
+
# be undone, only yanked.
|
|
39
|
+
# ============================================================================
|
|
40
|
+
readme-sync:
|
|
41
|
+
name: Verify README registry metadata
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
45
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
|
46
|
+
with:
|
|
47
|
+
python-version: "3.13"
|
|
48
|
+
- name: Verify README registry metadata
|
|
49
|
+
run: python scripts/repo-bootstrap/verify_readme_sync.py --repo-root=.
|
|
50
|
+
|
|
51
|
+
# ============================================================================
|
|
52
|
+
# Release: PSR v10
|
|
53
|
+
# ============================================================================
|
|
54
|
+
release:
|
|
55
|
+
name: Semantic Release
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
outputs:
|
|
58
|
+
released: ${{ steps.release.outputs.released }}
|
|
59
|
+
tag: ${{ steps.release.outputs.tag }}
|
|
60
|
+
version: ${{ steps.release.outputs.version }}
|
|
61
|
+
is_prerelease: ${{ steps.release.outputs.is_prerelease }}
|
|
62
|
+
steps:
|
|
63
|
+
- name: Harden Runner
|
|
64
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
65
|
+
with:
|
|
66
|
+
egress-policy: audit
|
|
67
|
+
|
|
68
|
+
- name: Generate GitHub App Token
|
|
69
|
+
id: app-token
|
|
70
|
+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
71
|
+
with:
|
|
72
|
+
app-id: ${{ vars.CI_APP_ID }}
|
|
73
|
+
private-key: ${{ secrets.CI_APP_KEY }}
|
|
74
|
+
|
|
75
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
76
|
+
with:
|
|
77
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
78
|
+
fetch-depth: 0
|
|
79
|
+
|
|
80
|
+
# Version bump runs on the fork (orphan-tag + registry-collision guards).
|
|
81
|
+
# The publish step below stays upstream — the fork does not replace it.
|
|
82
|
+
- uses: n24q02m/better-semantic-release@9b8ee6275b7dae995bac090282ccb1cabc99ae0f # v1.2.3
|
|
83
|
+
id: release
|
|
84
|
+
with:
|
|
85
|
+
github_token: ${{ steps.app-token.outputs.token }}
|
|
86
|
+
prerelease: ${{ inputs.release_type == 'beta' }}
|
|
87
|
+
prerelease_token: beta
|
|
88
|
+
|
|
89
|
+
- uses: python-semantic-release/publish-action@5a5718ce47b892ef699f2972dae122297771d641 # v10.6.1
|
|
90
|
+
if: steps.release.outputs.released == 'true'
|
|
91
|
+
with:
|
|
92
|
+
github_token: ${{ steps.app-token.outputs.token }}
|
|
93
|
+
tag: ${{ steps.release.outputs.tag }}
|
|
94
|
+
|
|
95
|
+
# ============================================================================
|
|
96
|
+
# Notify Downstream
|
|
97
|
+
# ============================================================================
|
|
98
|
+
# A core/shared repo cutting a stable release has to open a tracking issue in
|
|
99
|
+
# every repo that pins it, or downstream pins drift silently. The dispatch goes
|
|
100
|
+
# to one hub repo that fans the issues out; `n24q02m/n24q02m` is that hub as a
|
|
101
|
+
# literal, copied from the repo this template was measured against rather than
|
|
102
|
+
# derived from `n24q02m` -- the hub happening to share the owner's name is not
|
|
103
|
+
# a rule this template gets to assume.
|
|
104
|
+
#
|
|
105
|
+
# Stable only: `is_prerelease != 'true'`. A beta cutting downstream issues would
|
|
106
|
+
# file the same issue again at stable.
|
|
107
|
+
#
|
|
108
|
+
# NOTIFY_DOWNSTREAM, because that rule binds CORE repos and this template lands
|
|
109
|
+
# on every Python repo. Ungated, a non-core repo in this stack would dispatch
|
|
110
|
+
# `core-update` on a stable release with client_payload[core] naming itself.
|
|
111
|
+
# Two measurements say that is latent rather than visible: the hub repo has
|
|
112
|
+
# exactly one workflow (opencode.yml) and no `repository_dispatch` trigger
|
|
113
|
+
# anywhere, so nothing handles `core-update` today; and the job this was
|
|
114
|
+
# copied from is `skipped` in 10 of its 10 recorded CD runs. A job that has
|
|
115
|
+
# never once executed is not evidence that it is safe to fan out -- it is a
|
|
116
|
+
# wrong fan-out waiting for the day a handler is added.
|
|
117
|
+
notify-downstream:
|
|
118
|
+
name: Notify Downstream
|
|
119
|
+
needs: [release]
|
|
120
|
+
if: >-
|
|
121
|
+
needs.release.outputs.released == 'true'
|
|
122
|
+
&& needs.release.outputs.is_prerelease != 'true'
|
|
123
|
+
&& vars.NOTIFY_DOWNSTREAM == 'true'
|
|
124
|
+
runs-on: ubuntu-latest
|
|
125
|
+
steps:
|
|
126
|
+
- name: Harden Runner
|
|
127
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
128
|
+
with:
|
|
129
|
+
egress-policy: audit
|
|
130
|
+
|
|
131
|
+
- name: Generate GitHub App Token
|
|
132
|
+
id: app-token
|
|
133
|
+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
134
|
+
with:
|
|
135
|
+
app-id: ${{ vars.CI_APP_ID }}
|
|
136
|
+
private-key: ${{ secrets.CI_APP_KEY }}
|
|
137
|
+
|
|
138
|
+
- name: Dispatch core-update
|
|
139
|
+
env:
|
|
140
|
+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
141
|
+
run: |
|
|
142
|
+
gh api repos/n24q02m/n24q02m/dispatches \
|
|
143
|
+
-f event_type=core-update \
|
|
144
|
+
-f 'client_payload[core]=fastretrieval' \
|
|
145
|
+
-f 'client_payload[version]=${{ needs.release.outputs.version }}' \
|
|
146
|
+
-f 'client_payload[tag]=${{ needs.release.outputs.tag }}' \
|
|
147
|
+
-f 'client_payload[release_url]=https://github.com/${{ github.repository }}/releases/tag/${{ needs.release.outputs.tag }}'
|
|
148
|
+
|
|
149
|
+
# ============================================================================
|
|
150
|
+
# Publish to PyPI
|
|
151
|
+
# ============================================================================
|
|
152
|
+
publish-pypi:
|
|
153
|
+
name: Publish to PyPI
|
|
154
|
+
needs: [readme-sync, release]
|
|
155
|
+
if: needs.release.outputs.released == 'true' && vars.PUBLISH_PYPI == 'true'
|
|
156
|
+
runs-on: ubuntu-latest
|
|
157
|
+
environment: pypi
|
|
158
|
+
permissions:
|
|
159
|
+
contents: read
|
|
160
|
+
id-token: write
|
|
161
|
+
steps:
|
|
162
|
+
- name: Harden Runner
|
|
163
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
164
|
+
with:
|
|
165
|
+
egress-policy: audit
|
|
166
|
+
|
|
167
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
168
|
+
with:
|
|
169
|
+
ref: ${{ needs.release.outputs.tag }}
|
|
170
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
171
|
+
- run: uv build
|
|
172
|
+
- name: Publish to PyPI
|
|
173
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
174
|
+
|
|
175
|
+
# ============================================================================
|
|
176
|
+
# Build Docker (multi-arch: amd64 + arm64)
|
|
177
|
+
# ============================================================================
|
|
178
|
+
build-docker:
|
|
179
|
+
name: Build Docker (${{ matrix.platform }})
|
|
180
|
+
# readme-sync, because this job pushes an image to a registry and
|
|
181
|
+
# merge-docker then publishes ./README.md as the Docker Hub description.
|
|
182
|
+
# The gate is only a gate if it runs BEFORE the push.
|
|
183
|
+
needs: [readme-sync, release]
|
|
184
|
+
if: needs.release.outputs.released == 'true' && vars.PUBLISH_DOCKER == 'true'
|
|
185
|
+
permissions:
|
|
186
|
+
contents: read
|
|
187
|
+
packages: write
|
|
188
|
+
strategy:
|
|
189
|
+
fail-fast: false
|
|
190
|
+
matrix:
|
|
191
|
+
include:
|
|
192
|
+
- platform: linux/amd64
|
|
193
|
+
runner: ubuntu-latest
|
|
194
|
+
artifact: linux-amd64
|
|
195
|
+
- platform: linux/arm64
|
|
196
|
+
runner: ubuntu-24.04-arm
|
|
197
|
+
artifact: linux-arm64
|
|
198
|
+
runs-on: ${{ matrix.runner }}
|
|
199
|
+
steps:
|
|
200
|
+
- name: Harden Runner
|
|
201
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
202
|
+
with:
|
|
203
|
+
egress-policy: audit
|
|
204
|
+
|
|
205
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
206
|
+
with:
|
|
207
|
+
ref: ${{ needs.release.outputs.tag }}
|
|
208
|
+
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
|
209
|
+
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
210
|
+
with:
|
|
211
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
212
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
213
|
+
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
214
|
+
with:
|
|
215
|
+
registry: ghcr.io
|
|
216
|
+
username: ${{ github.actor }}
|
|
217
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
218
|
+
- uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
|
219
|
+
id: build
|
|
220
|
+
with:
|
|
221
|
+
context: .
|
|
222
|
+
platforms: ${{ matrix.platform }}
|
|
223
|
+
outputs: type=image,"name=${{ env.DOCKERHUB_IMAGE }},${{ env.GHCR_IMAGE }}",push-by-digest=true,name-canonical=true,push=true
|
|
224
|
+
cache-from: type=gha,scope=${{ github.ref_name }}-${{ matrix.artifact }}
|
|
225
|
+
cache-to: type=gha,mode=max,scope=${{ github.ref_name }}-${{ matrix.artifact }}
|
|
226
|
+
- run: |
|
|
227
|
+
mkdir -p ${{ runner.temp }}/digests
|
|
228
|
+
echo "${{ steps.build.outputs.digest }}" > "${{ runner.temp }}/digests/${{ matrix.artifact }}"
|
|
229
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
230
|
+
with:
|
|
231
|
+
name: digest-${{ matrix.artifact }}
|
|
232
|
+
path: ${{ runner.temp }}/digests/*
|
|
233
|
+
retention-days: 1
|
|
234
|
+
|
|
235
|
+
# ============================================================================
|
|
236
|
+
# Merge Docker Manifests
|
|
237
|
+
# ============================================================================
|
|
238
|
+
merge-docker:
|
|
239
|
+
name: Merge Docker Manifests
|
|
240
|
+
needs: [release, build-docker]
|
|
241
|
+
if: needs.release.outputs.released == 'true' && vars.PUBLISH_DOCKER == 'true'
|
|
242
|
+
runs-on: ubuntu-latest
|
|
243
|
+
permissions:
|
|
244
|
+
contents: read
|
|
245
|
+
packages: write
|
|
246
|
+
steps:
|
|
247
|
+
- name: Harden Runner
|
|
248
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
249
|
+
with:
|
|
250
|
+
egress-policy: audit
|
|
251
|
+
|
|
252
|
+
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
|
253
|
+
with:
|
|
254
|
+
pattern: digest-*
|
|
255
|
+
path: ${{ runner.temp }}/digests
|
|
256
|
+
merge-multiple: true
|
|
257
|
+
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
258
|
+
with:
|
|
259
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
260
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
261
|
+
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
262
|
+
with:
|
|
263
|
+
registry: ghcr.io
|
|
264
|
+
username: ${{ github.actor }}
|
|
265
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
266
|
+
- name: Create and push manifests
|
|
267
|
+
env:
|
|
268
|
+
VERSION: ${{ needs.release.outputs.version }}
|
|
269
|
+
IS_PRERELEASE: ${{ needs.release.outputs.is_prerelease }}
|
|
270
|
+
run: |
|
|
271
|
+
if [ "$IS_PRERELEASE" = "true" ]; then
|
|
272
|
+
TAGS="beta ${VERSION}"
|
|
273
|
+
else
|
|
274
|
+
MAJOR=$(echo "$VERSION" | cut -d. -f1)
|
|
275
|
+
MINOR=$(echo "$VERSION" | cut -d. -f1-2)
|
|
276
|
+
TAGS="latest ${VERSION} ${MINOR} ${MAJOR}"
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
for IMAGE in "$DOCKERHUB_IMAGE" "$GHCR_IMAGE"; do
|
|
280
|
+
tag_args=()
|
|
281
|
+
for TAG in $TAGS; do
|
|
282
|
+
tag_args+=("-t" "${IMAGE}:${TAG}")
|
|
283
|
+
done
|
|
284
|
+
|
|
285
|
+
digest_args=()
|
|
286
|
+
for digest_file in "${{ runner.temp }}/digests/"*; do
|
|
287
|
+
while IFS= read -r digest; do
|
|
288
|
+
if [ -n "$digest" ]; then
|
|
289
|
+
digest_args+=("${IMAGE}@${digest}")
|
|
290
|
+
fi
|
|
291
|
+
done < "$digest_file"
|
|
292
|
+
done
|
|
293
|
+
|
|
294
|
+
docker buildx imagetools create "${tag_args[@]}" "${digest_args[@]}"
|
|
295
|
+
done
|
|
296
|
+
|
|
297
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
298
|
+
with:
|
|
299
|
+
sparse-checkout: README.md
|
|
300
|
+
- uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0
|
|
301
|
+
with:
|
|
302
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
303
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
304
|
+
# CUSTOMIZE: DockerHub repository name
|
|
305
|
+
repository: ${{ env.DOCKERHUB_IMAGE }}
|
|
306
|
+
short-description: ${{ github.event.repository.description }}
|
|
307
|
+
readme-filepath: ./README.md
|
|
308
|
+
|
|
309
|
+
# ============================================================================
|
|
310
|
+
# Publish to MCP Registry (stable only)
|
|
311
|
+
# ============================================================================
|
|
312
|
+
publish-mcp-registry:
|
|
313
|
+
name: Publish to MCP Registry
|
|
314
|
+
needs: [release, publish-pypi, merge-docker]
|
|
315
|
+
if: >-
|
|
316
|
+
needs.release.outputs.released == 'true'
|
|
317
|
+
&& needs.release.outputs.is_prerelease != 'true'
|
|
318
|
+
&& vars.PUBLISH_PYPI == 'true'
|
|
319
|
+
&& vars.PUBLISH_DOCKER == 'true'
|
|
320
|
+
&& vars.PUBLISH_MCP_REGISTRY == 'true'
|
|
321
|
+
runs-on: ubuntu-latest
|
|
322
|
+
permissions:
|
|
323
|
+
contents: read
|
|
324
|
+
id-token: write
|
|
325
|
+
steps:
|
|
326
|
+
- name: Harden Runner
|
|
327
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
328
|
+
with:
|
|
329
|
+
egress-policy: audit
|
|
330
|
+
|
|
331
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
332
|
+
with:
|
|
333
|
+
ref: ${{ needs.release.outputs.tag }}
|
|
334
|
+
|
|
335
|
+
- name: Update server.json version
|
|
336
|
+
run: |
|
|
337
|
+
VERSION="${{ needs.release.outputs.version }}"
|
|
338
|
+
jq --arg v "$VERSION" '
|
|
339
|
+
.version = $v |
|
|
340
|
+
.packages = [.packages[] |
|
|
341
|
+
if .registryType == "oci" then del(.version)
|
|
342
|
+
else .version = $v
|
|
343
|
+
end
|
|
344
|
+
]
|
|
345
|
+
' server.json > server.json.tmp && mv server.json.tmp server.json
|
|
346
|
+
|
|
347
|
+
- name: Install MCP Publisher
|
|
348
|
+
run: |
|
|
349
|
+
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
|
|
350
|
+
|
|
351
|
+
- name: Login to MCP Registry
|
|
352
|
+
run: ./mcp-publisher login github-oidc
|
|
353
|
+
|
|
354
|
+
- name: Publish to MCP Registry
|
|
355
|
+
run: ./mcp-publisher publish
|