hull-core 0.1.0b1__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.
- hull_core-0.1.0b1/.github/CODEOWNERS +2 -0
- hull_core-0.1.0b1/.github/ISSUE_TEMPLATE/bug_report.md +34 -0
- hull_core-0.1.0b1/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- hull_core-0.1.0b1/.github/PULL_REQUEST_TEMPLATE.md +25 -0
- hull_core-0.1.0b1/.github/repo-settings.json +12 -0
- hull_core-0.1.0b1/.github/rulesets/main.json +64 -0
- hull_core-0.1.0b1/.github/workflows/cd.yml +388 -0
- hull_core-0.1.0b1/.github/workflows/ci.yml +294 -0
- hull_core-0.1.0b1/.github/workflows/codeql.yml +30 -0
- hull_core-0.1.0b1/.gitignore +24 -0
- hull_core-0.1.0b1/.mise.toml +53 -0
- hull_core-0.1.0b1/.pre-commit-config.yaml +123 -0
- hull_core-0.1.0b1/.python-version +1 -0
- hull_core-0.1.0b1/.repo-bootstrap.toml +15 -0
- hull_core-0.1.0b1/.skret.yaml +8 -0
- hull_core-0.1.0b1/AGENTS.md +28 -0
- hull_core-0.1.0b1/CHANGELOG.md +3 -0
- hull_core-0.1.0b1/CODE_OF_CONDUCT.md +46 -0
- hull_core-0.1.0b1/CONTRIBUTING.md +21 -0
- hull_core-0.1.0b1/LICENSE +201 -0
- hull_core-0.1.0b1/PKG-INFO +135 -0
- hull_core-0.1.0b1/README.md +86 -0
- hull_core-0.1.0b1/SECURITY.md +15 -0
- hull_core-0.1.0b1/packages/core-py/README.md +23 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/__init__.py +36 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/auth/__init__.py +33 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/auth/asgi.py +75 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/auth/context.py +47 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/auth/middleware.py +105 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/auth/tokens.py +83 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/auth/users.py +131 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/cli.py +107 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/config/__init__.py +39 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/config/models.py +59 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/config/settings.py +132 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/crypto/__init__.py +5 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/crypto/timing.py +16 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/http/__init__.py +23 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/http/ssrf.py +297 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/lifecycle/__init__.py +5 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/lifecycle/lock.py +278 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/limits/__init__.py +5 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/limits/limiter.py +34 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/providers/__init__.py +5 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/providers/openai_spec.py +147 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/server/__init__.py +6 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/server/app.py +78 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/server/run.py +57 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/storage/__init__.py +5 -0
- hull_core-0.1.0b1/packages/core-py/src/hull_core/storage/sqlite.py +69 -0
- hull_core-0.1.0b1/packages/core-py/tests/auth/test_authenticator.py +106 -0
- hull_core-0.1.0b1/packages/core-py/tests/auth/test_middleware.py +81 -0
- hull_core-0.1.0b1/packages/core-py/tests/auth/test_tokens.py +31 -0
- hull_core-0.1.0b1/packages/core-py/tests/auth/test_users.py +150 -0
- hull_core-0.1.0b1/packages/core-py/tests/config/test_models.py +52 -0
- hull_core-0.1.0b1/packages/core-py/tests/config/test_settings.py +89 -0
- hull_core-0.1.0b1/packages/core-py/tests/http/test_ssrf.py +203 -0
- hull_core-0.1.0b1/packages/core-py/tests/lifecycle/test_lock.py +401 -0
- hull_core-0.1.0b1/packages/core-py/tests/limits/test_limiter.py +28 -0
- hull_core-0.1.0b1/packages/core-py/tests/providers/test_openai_spec.py +264 -0
- hull_core-0.1.0b1/packages/core-py/tests/server/test_app.py +159 -0
- hull_core-0.1.0b1/packages/core-py/tests/server/test_run.py +111 -0
- hull_core-0.1.0b1/packages/core-py/tests/storage/test_sqlite.py +47 -0
- hull_core-0.1.0b1/packages/core-py/tests/test_cli.py +69 -0
- hull_core-0.1.0b1/packages/core-py/tests/test_crypto.py +42 -0
- hull_core-0.1.0b1/packages/embedding-daemon/README.md +16 -0
- hull_core-0.1.0b1/packages/embedding-daemon/src/hull_embedding_daemon/__init__.py +6 -0
- hull_core-0.1.0b1/packages/embedding-daemon/src/hull_embedding_daemon/__main__.py +53 -0
- hull_core-0.1.0b1/packages/embedding-daemon/src/hull_embedding_daemon/_extra.py +45 -0
- hull_core-0.1.0b1/packages/embedding-daemon/src/hull_embedding_daemon/api.py +82 -0
- hull_core-0.1.0b1/packages/embedding-daemon/src/hull_embedding_daemon/backends/__init__.py +0 -0
- hull_core-0.1.0b1/packages/embedding-daemon/src/hull_embedding_daemon/backends/gguf.py +17 -0
- hull_core-0.1.0b1/packages/embedding-daemon/src/hull_embedding_daemon/backends/onnx.py +17 -0
- hull_core-0.1.0b1/packages/embedding-daemon/tests/test_api.py +89 -0
- hull_core-0.1.0b1/packages/embedding-daemon/tests/test_extra_guard.py +80 -0
- hull_core-0.1.0b1/packages/embedding-daemon/tests/test_gguf.py +26 -0
- hull_core-0.1.0b1/packages/embedding-daemon/tests/test_main.py +59 -0
- hull_core-0.1.0b1/packages/embedding-daemon/tests/test_onnx.py +26 -0
- hull_core-0.1.0b1/packages/embedding-daemon/tests/test_version.py +26 -0
- hull_core-0.1.0b1/packages/web/README.md +30 -0
- hull_core-0.1.0b1/packages/web/scripts/measure_identity_ab.py +243 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/__init__.py +39 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/_extra.py +51 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/adapters/__init__.py +10 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/adapters/mangadex.py +403 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/browsers/__init__.py +19 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/browsers/browserless.py +66 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/browsers/cf_render.py +83 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/browsers/interact.py +132 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/browsers/invisible.py +76 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/browsers/patchright.py +73 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/browsers/protocol.py +27 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/fingerprint.py +209 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/http/__init__.py +13 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/http/client.py +233 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/http/url.py +137 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/py.typed +0 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/__init__.py +19 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/agent.py +391 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/base.py +31 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/cache.py +117 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/robots.py +110 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/selector_inference.py +212 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/state.py +37 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/strategies/__init__.py +22 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/strategies/api_direct.py +80 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/strategies/basic_http.py +101 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/strategies/captcha.py +295 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/strategies/headless.py +106 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/strategies/invisible_browser.py +32 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/strategies/patchright_browser.py +194 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/strategies/remote_render.py +49 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/strategies/tls_spoof.py +97 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/scraper/utils.py +190 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/search/__init__.py +7 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/search/client.py +294 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/search/models.py +33 -0
- hull_core-0.1.0b1/packages/web/src/hull_web/search/runner.py +1448 -0
- hull_core-0.1.0b1/packages/web/tests/__init__.py +0 -0
- hull_core-0.1.0b1/packages/web/tests/conftest.py +34 -0
- hull_core-0.1.0b1/packages/web/tests/test_adapters/__init__.py +0 -0
- hull_core-0.1.0b1/packages/web/tests/test_adapters/test_mangadex.py +961 -0
- hull_core-0.1.0b1/packages/web/tests/test_browsers/__init__.py +0 -0
- hull_core-0.1.0b1/packages/web/tests/test_browsers/test_browserless.py +77 -0
- hull_core-0.1.0b1/packages/web/tests/test_browsers/test_interact.py +55 -0
- hull_core-0.1.0b1/packages/web/tests/test_browsers/test_invisible.py +130 -0
- hull_core-0.1.0b1/packages/web/tests/test_browsers/test_patchright.py +109 -0
- hull_core-0.1.0b1/packages/web/tests/test_browsers/test_protocol.py +84 -0
- hull_core-0.1.0b1/packages/web/tests/test_extra_guard.py +65 -0
- hull_core-0.1.0b1/packages/web/tests/test_http/__init__.py +0 -0
- hull_core-0.1.0b1/packages/web/tests/test_http/test_client.py +463 -0
- hull_core-0.1.0b1/packages/web/tests/test_http/test_ssrf_bypass.py +23 -0
- hull_core-0.1.0b1/packages/web/tests/test_http/test_ssrf_whitelist.py +74 -0
- hull_core-0.1.0b1/packages/web/tests/test_http/test_url.py +334 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/__init__.py +0 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_agent.py +372 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_base.py +102 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_cache.py +242 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_identity.py +127 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_robots.py +120 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_selector_inference.py +358 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_state.py +101 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/__init__.py +0 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/test_api_direct.py +282 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/test_basic_http.py +185 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/test_browser_ssrf.py +113 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/test_captcha.py +558 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/test_headless.py +328 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/test_identity_wiring.py +143 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/test_patchright_strategy.py +361 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/test_remote_render.py +68 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/test_ssrf_protection.py +37 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_strategies/test_tls_spoof.py +251 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_utils.py +310 -0
- hull_core-0.1.0b1/packages/web/tests/test_scraper/test_validate_under_rendered.py +106 -0
- hull_core-0.1.0b1/packages/web/tests/test_search/__init__.py +0 -0
- hull_core-0.1.0b1/packages/web/tests/test_search/conftest.py +61 -0
- hull_core-0.1.0b1/packages/web/tests/test_search/test_build_filtered_query.py +137 -0
- hull_core-0.1.0b1/packages/web/tests/test_search/test_client.py +777 -0
- hull_core-0.1.0b1/packages/web/tests/test_search/test_models.py +98 -0
- hull_core-0.1.0b1/packages/web/tests/test_search/test_runner.py +1572 -0
- hull_core-0.1.0b1/packages/web/tests/test_search/test_runner_security.py +54 -0
- hull_core-0.1.0b1/packages/web/tests/test_search/test_searxng_lock.py +121 -0
- hull_core-0.1.0b1/packages/web/tests/test_search/test_write_secure_text.py +52 -0
- hull_core-0.1.0b1/pyproject.toml +159 -0
- hull_core-0.1.0b1/renovate.json +44 -0
- hull_core-0.1.0b1/scripts/enforce-commit.sh +8 -0
- hull_core-0.1.0b1/scripts/preserve-diacritics.py +287 -0
- hull_core-0.1.0b1/scripts/repo-bootstrap/_lib.py +542 -0
- hull_core-0.1.0b1/scripts/repo-bootstrap/verify_readme_sync.py +511 -0
- hull_core-0.1.0b1/tests/test_extra_modules_sync.py +44 -0
- hull_core-0.1.0b1/tests/test_packaging.py +159 -0
- hull_core-0.1.0b1/uv.lock +4172 -0
|
@@ -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
|
+
- hull-core version: [output of `hull-core --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,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Main Branch Rules",
|
|
3
|
+
"target": "branch",
|
|
4
|
+
"source_type": "Repository",
|
|
5
|
+
"source": "n24q02m/hull",
|
|
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
|
+
"require_extra_approval_for_unattributed_changes": true,
|
|
46
|
+
"allowed_merge_methods": [
|
|
47
|
+
"squash"
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"type": "code_scanning",
|
|
53
|
+
"parameters": {
|
|
54
|
+
"code_scanning_tools": [
|
|
55
|
+
{
|
|
56
|
+
"tool": "CodeQL",
|
|
57
|
+
"security_alerts_threshold": "high_or_higher",
|
|
58
|
+
"alerts_threshold": "errors"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
@@ -0,0 +1,388 @@
|
|
|
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
|
+
publish_existing_tag:
|
|
22
|
+
description: "Recovery only: publish an already-created tag (e.g. v3.1.0), skipping semantic-release. Use when the auto-computed version's tag name is permanently reserved by a GitHub immutable release."
|
|
23
|
+
required: false
|
|
24
|
+
type: string
|
|
25
|
+
default: ""
|
|
26
|
+
|
|
27
|
+
permissions:
|
|
28
|
+
contents: read
|
|
29
|
+
|
|
30
|
+
env:
|
|
31
|
+
# Release lane: dispatch-only (release_type=beta, then stable); a push never releases.
|
|
32
|
+
RELEASE_TYPE: ${{ inputs.release_type }}
|
|
33
|
+
DOCKERHUB_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/hull-core
|
|
34
|
+
GHCR_IMAGE: ghcr.io/${{ github.repository }}
|
|
35
|
+
|
|
36
|
+
concurrency:
|
|
37
|
+
group: release
|
|
38
|
+
cancel-in-progress: false
|
|
39
|
+
|
|
40
|
+
jobs:
|
|
41
|
+
# ============================================================================
|
|
42
|
+
# README registry metadata: the package carries the repo's README to PyPI, so
|
|
43
|
+
# a manifest that names a file which is not there publishes an empty project
|
|
44
|
+
# page. Runs before the release rather than beside it -- a PyPI upload cannot
|
|
45
|
+
# be undone, only yanked.
|
|
46
|
+
# ============================================================================
|
|
47
|
+
readme-sync:
|
|
48
|
+
name: Verify README registry metadata
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
52
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
|
53
|
+
with:
|
|
54
|
+
python-version: "3.13"
|
|
55
|
+
- name: Verify README registry metadata
|
|
56
|
+
run: python scripts/repo-bootstrap/verify_readme_sync.py --repo-root=.
|
|
57
|
+
|
|
58
|
+
# ============================================================================
|
|
59
|
+
# Release: PSR v10
|
|
60
|
+
# ============================================================================
|
|
61
|
+
release:
|
|
62
|
+
name: Semantic Release
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
outputs:
|
|
65
|
+
released: ${{ steps.release.outputs.released || steps.forced.outputs.released }}
|
|
66
|
+
tag: ${{ steps.release.outputs.tag || steps.forced.outputs.tag }}
|
|
67
|
+
version: ${{ steps.release.outputs.version || steps.forced.outputs.version }}
|
|
68
|
+
is_prerelease: ${{ steps.release.outputs.is_prerelease || steps.forced.outputs.is_prerelease }}
|
|
69
|
+
steps:
|
|
70
|
+
- name: Harden Runner
|
|
71
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
72
|
+
with:
|
|
73
|
+
egress-policy: audit
|
|
74
|
+
|
|
75
|
+
- name: Generate GitHub App Token
|
|
76
|
+
id: app-token
|
|
77
|
+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
78
|
+
with:
|
|
79
|
+
app-id: ${{ vars.CI_APP_ID }}
|
|
80
|
+
private-key: ${{ secrets.CI_APP_KEY }}
|
|
81
|
+
|
|
82
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
83
|
+
with:
|
|
84
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
85
|
+
fetch-depth: 0
|
|
86
|
+
|
|
87
|
+
# Version bump runs on the fork (orphan-tag + registry-collision guards).
|
|
88
|
+
# The publish step below stays upstream — the fork does not replace it.
|
|
89
|
+
- uses: n24q02m/better-semantic-release@087b84e8d2ba75bdec350924d3bf8247088e0b1a # v1.4.0
|
|
90
|
+
id: release
|
|
91
|
+
if: inputs.publish_existing_tag == ''
|
|
92
|
+
with:
|
|
93
|
+
github_token: ${{ steps.app-token.outputs.token }}
|
|
94
|
+
prerelease: ${{ env.RELEASE_TYPE == 'beta' }}
|
|
95
|
+
prerelease_token: beta
|
|
96
|
+
|
|
97
|
+
- uses: python-semantic-release/publish-action@5a5718ce47b892ef699f2972dae122297771d641 # v10.6.1
|
|
98
|
+
if: inputs.publish_existing_tag == '' && steps.release.outputs.released == 'true'
|
|
99
|
+
with:
|
|
100
|
+
github_token: ${{ steps.app-token.outputs.token }}
|
|
101
|
+
tag: ${{ steps.release.outputs.tag }}
|
|
102
|
+
|
|
103
|
+
- name: Use existing tag (immutable-release recovery)
|
|
104
|
+
id: forced
|
|
105
|
+
if: inputs.publish_existing_tag != ''
|
|
106
|
+
run: |
|
|
107
|
+
TAG="${{ inputs.publish_existing_tag }}"
|
|
108
|
+
VERSION="${TAG#v}"
|
|
109
|
+
{
|
|
110
|
+
echo "released=true"
|
|
111
|
+
echo "tag=$TAG"
|
|
112
|
+
echo "version=$VERSION"
|
|
113
|
+
echo "is_prerelease=false"
|
|
114
|
+
} >> "$GITHUB_OUTPUT"
|
|
115
|
+
|
|
116
|
+
# ============================================================================
|
|
117
|
+
# Notify Downstream
|
|
118
|
+
# ============================================================================
|
|
119
|
+
# A core/shared repo cutting a stable release has to open a tracking issue in
|
|
120
|
+
# every repo that pins it, or downstream pins drift silently. The dispatch goes
|
|
121
|
+
# to one hub repo that fans the issues out; `n24q02m/n24q02m` is that hub as a
|
|
122
|
+
# literal, copied from the repo this template was measured against rather than
|
|
123
|
+
# derived from `n24q02m` -- the hub happening to share the owner's name is not
|
|
124
|
+
# a rule this template gets to assume.
|
|
125
|
+
#
|
|
126
|
+
# Stable only: `is_prerelease != 'true'`. A beta cutting downstream issues would
|
|
127
|
+
# file the same issue again at stable.
|
|
128
|
+
#
|
|
129
|
+
# New repositories and repositories already using the gate require
|
|
130
|
+
# NOTIFY_DOWNSTREAM, because this template also lands on non-core Python repos.
|
|
131
|
+
# During retrofit, an existing ungated notify-downstream job stays ungated:
|
|
132
|
+
# silently adding an unset-variable gate would stop a live release behavior.
|
|
133
|
+
# apply.py detects only that existing job block; it does not infer core status
|
|
134
|
+
# from a repository name.
|
|
135
|
+
notify-downstream:
|
|
136
|
+
name: Notify Downstream
|
|
137
|
+
needs: [release]
|
|
138
|
+
if: >-
|
|
139
|
+
needs.release.outputs.released == 'true'
|
|
140
|
+
&& needs.release.outputs.is_prerelease != 'true'
|
|
141
|
+
&& vars.NOTIFY_DOWNSTREAM == 'true'
|
|
142
|
+
runs-on: ubuntu-latest
|
|
143
|
+
steps:
|
|
144
|
+
- name: Harden Runner
|
|
145
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
146
|
+
with:
|
|
147
|
+
egress-policy: audit
|
|
148
|
+
|
|
149
|
+
- name: Generate GitHub App Token
|
|
150
|
+
id: app-token
|
|
151
|
+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
152
|
+
with:
|
|
153
|
+
app-id: ${{ vars.CI_APP_ID }}
|
|
154
|
+
private-key: ${{ secrets.CI_APP_KEY }}
|
|
155
|
+
# Without owner/repositories the token is scoped to THIS repo only
|
|
156
|
+
# and the dispatch 403s ("Resource not accessible by integration").
|
|
157
|
+
# Scope to the target repo, nothing more.
|
|
158
|
+
owner: n24q02m
|
|
159
|
+
repositories: n24q02m
|
|
160
|
+
|
|
161
|
+
- name: Dispatch core-update
|
|
162
|
+
env:
|
|
163
|
+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
164
|
+
run: |
|
|
165
|
+
gh api repos/n24q02m/n24q02m/dispatches \
|
|
166
|
+
-f event_type=core-update \
|
|
167
|
+
-f 'client_payload[core]=hull-core' \
|
|
168
|
+
-f 'client_payload[version]=${{ needs.release.outputs.version }}' \
|
|
169
|
+
-f 'client_payload[tag]=${{ needs.release.outputs.tag }}' \
|
|
170
|
+
-f 'client_payload[release_url]=https://github.com/${{ github.repository }}/releases/tag/${{ needs.release.outputs.tag }}'
|
|
171
|
+
|
|
172
|
+
# ============================================================================
|
|
173
|
+
# Publish to PyPI
|
|
174
|
+
# ============================================================================
|
|
175
|
+
publish-pypi:
|
|
176
|
+
name: Publish to PyPI
|
|
177
|
+
needs: [readme-sync, release]
|
|
178
|
+
if: needs.release.outputs.released == 'true' && vars.PUBLISH_PYPI == 'true'
|
|
179
|
+
runs-on: ubuntu-latest
|
|
180
|
+
environment: pypi
|
|
181
|
+
permissions:
|
|
182
|
+
contents: read
|
|
183
|
+
id-token: write
|
|
184
|
+
steps:
|
|
185
|
+
- name: Harden Runner
|
|
186
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
187
|
+
with:
|
|
188
|
+
egress-policy: audit
|
|
189
|
+
|
|
190
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
191
|
+
with:
|
|
192
|
+
ref: ${{ needs.release.outputs.tag }}
|
|
193
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
194
|
+
- run: uv build
|
|
195
|
+
- name: Publish to PyPI
|
|
196
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
197
|
+
|
|
198
|
+
# ============================================================================
|
|
199
|
+
# Build Docker (multi-arch: amd64 + arm64)
|
|
200
|
+
# ============================================================================
|
|
201
|
+
build-docker:
|
|
202
|
+
name: Build Docker (${{ matrix.platform }})
|
|
203
|
+
# readme-sync, because this job pushes an image to a registry and
|
|
204
|
+
# merge-docker then publishes ./README.md as the Docker Hub description.
|
|
205
|
+
# The gate is only a gate if it runs BEFORE the push.
|
|
206
|
+
needs: [readme-sync, release]
|
|
207
|
+
if: needs.release.outputs.released == 'true' && vars.PUBLISH_DOCKER == 'true'
|
|
208
|
+
permissions:
|
|
209
|
+
contents: read
|
|
210
|
+
packages: write
|
|
211
|
+
strategy:
|
|
212
|
+
fail-fast: false
|
|
213
|
+
matrix:
|
|
214
|
+
include:
|
|
215
|
+
- platform: linux/amd64
|
|
216
|
+
runner: ubuntu-latest
|
|
217
|
+
artifact: linux-amd64
|
|
218
|
+
- platform: linux/arm64
|
|
219
|
+
runner: ubuntu-24.04-arm
|
|
220
|
+
artifact: linux-arm64
|
|
221
|
+
runs-on: ${{ matrix.runner }}
|
|
222
|
+
steps:
|
|
223
|
+
- name: Harden Runner
|
|
224
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
225
|
+
with:
|
|
226
|
+
egress-policy: audit
|
|
227
|
+
|
|
228
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
229
|
+
with:
|
|
230
|
+
ref: ${{ needs.release.outputs.tag }}
|
|
231
|
+
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
|
232
|
+
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
233
|
+
with:
|
|
234
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
235
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
236
|
+
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
237
|
+
with:
|
|
238
|
+
registry: ghcr.io
|
|
239
|
+
username: ${{ github.actor }}
|
|
240
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
241
|
+
- uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
|
242
|
+
id: build
|
|
243
|
+
with:
|
|
244
|
+
context: .
|
|
245
|
+
platforms: ${{ matrix.platform }}
|
|
246
|
+
outputs: type=image,"name=${{ env.DOCKERHUB_IMAGE }},${{ env.GHCR_IMAGE }}",push-by-digest=true,name-canonical=true,push=true
|
|
247
|
+
cache-from: type=gha,scope=${{ github.ref_name }}-${{ matrix.artifact }}
|
|
248
|
+
cache-to: type=gha,mode=max,scope=${{ github.ref_name }}-${{ matrix.artifact }}
|
|
249
|
+
- run: |
|
|
250
|
+
mkdir -p ${{ runner.temp }}/digests
|
|
251
|
+
echo "${{ steps.build.outputs.digest }}" > "${{ runner.temp }}/digests/${{ matrix.artifact }}"
|
|
252
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
253
|
+
with:
|
|
254
|
+
name: digest-${{ matrix.artifact }}
|
|
255
|
+
path: ${{ runner.temp }}/digests/*
|
|
256
|
+
retention-days: 1
|
|
257
|
+
|
|
258
|
+
# ============================================================================
|
|
259
|
+
# Merge Docker Manifests
|
|
260
|
+
# ============================================================================
|
|
261
|
+
merge-docker:
|
|
262
|
+
name: Merge Docker Manifests
|
|
263
|
+
needs: [release, build-docker]
|
|
264
|
+
if: needs.release.outputs.released == 'true' && vars.PUBLISH_DOCKER == 'true'
|
|
265
|
+
runs-on: ubuntu-latest
|
|
266
|
+
permissions:
|
|
267
|
+
contents: read
|
|
268
|
+
packages: write
|
|
269
|
+
steps:
|
|
270
|
+
- name: Harden Runner
|
|
271
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
272
|
+
with:
|
|
273
|
+
egress-policy: audit
|
|
274
|
+
|
|
275
|
+
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
|
276
|
+
with:
|
|
277
|
+
pattern: digest-*
|
|
278
|
+
path: ${{ runner.temp }}/digests
|
|
279
|
+
merge-multiple: true
|
|
280
|
+
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
281
|
+
with:
|
|
282
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
283
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
284
|
+
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
285
|
+
with:
|
|
286
|
+
registry: ghcr.io
|
|
287
|
+
username: ${{ github.actor }}
|
|
288
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
289
|
+
- name: Create and push manifests
|
|
290
|
+
env:
|
|
291
|
+
VERSION: ${{ needs.release.outputs.version }}
|
|
292
|
+
IS_PRERELEASE: ${{ needs.release.outputs.is_prerelease }}
|
|
293
|
+
run: |
|
|
294
|
+
if [ "$IS_PRERELEASE" = "true" ]; then
|
|
295
|
+
TAGS="beta ${VERSION}"
|
|
296
|
+
else
|
|
297
|
+
MAJOR=$(echo "$VERSION" | cut -d. -f1)
|
|
298
|
+
MINOR=$(echo "$VERSION" | cut -d. -f1-2)
|
|
299
|
+
TAGS="latest ${VERSION} ${MINOR} ${MAJOR}"
|
|
300
|
+
fi
|
|
301
|
+
|
|
302
|
+
for IMAGE in "$DOCKERHUB_IMAGE" "$GHCR_IMAGE"; do
|
|
303
|
+
tag_args=()
|
|
304
|
+
for TAG in $TAGS; do
|
|
305
|
+
tag_args+=("-t" "${IMAGE}:${TAG}")
|
|
306
|
+
done
|
|
307
|
+
|
|
308
|
+
digest_args=()
|
|
309
|
+
for digest_file in "${{ runner.temp }}/digests/"*; do
|
|
310
|
+
while IFS= read -r digest; do
|
|
311
|
+
if [ -n "$digest" ]; then
|
|
312
|
+
digest_args+=("${IMAGE}@${digest}")
|
|
313
|
+
fi
|
|
314
|
+
done < "$digest_file"
|
|
315
|
+
done
|
|
316
|
+
|
|
317
|
+
docker buildx imagetools create "${tag_args[@]}" "${digest_args[@]}"
|
|
318
|
+
done
|
|
319
|
+
|
|
320
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
321
|
+
with:
|
|
322
|
+
sparse-checkout: README.md
|
|
323
|
+
- uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0
|
|
324
|
+
with:
|
|
325
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
326
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
327
|
+
# CUSTOMIZE: DockerHub repository name
|
|
328
|
+
repository: ${{ env.DOCKERHUB_IMAGE }}
|
|
329
|
+
short-description: ${{ github.event.repository.description }}
|
|
330
|
+
readme-filepath: ./README.md
|
|
331
|
+
|
|
332
|
+
# ============================================================================
|
|
333
|
+
# Publish to MCP Registry (stable only)
|
|
334
|
+
# ============================================================================
|
|
335
|
+
publish-mcp-registry:
|
|
336
|
+
name: Publish to MCP Registry
|
|
337
|
+
needs: [release, publish-pypi, merge-docker]
|
|
338
|
+
if: >-
|
|
339
|
+
needs.release.outputs.released == 'true'
|
|
340
|
+
&& needs.release.outputs.is_prerelease != 'true'
|
|
341
|
+
&& vars.PUBLISH_PYPI == 'true'
|
|
342
|
+
&& vars.PUBLISH_DOCKER == 'true'
|
|
343
|
+
&& vars.PUBLISH_MCP_REGISTRY == 'true'
|
|
344
|
+
runs-on: ubuntu-latest
|
|
345
|
+
permissions:
|
|
346
|
+
contents: read
|
|
347
|
+
id-token: write
|
|
348
|
+
steps:
|
|
349
|
+
- name: Harden Runner
|
|
350
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
351
|
+
with:
|
|
352
|
+
egress-policy: audit
|
|
353
|
+
|
|
354
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
355
|
+
with:
|
|
356
|
+
ref: ${{ needs.release.outputs.tag }}
|
|
357
|
+
|
|
358
|
+
- name: Update server.json version
|
|
359
|
+
run: |
|
|
360
|
+
VERSION="${{ needs.release.outputs.version }}"
|
|
361
|
+
jq --arg v "$VERSION" '
|
|
362
|
+
.version = $v |
|
|
363
|
+
.packages = [.packages[] |
|
|
364
|
+
if .registryType == "oci" then del(.version)
|
|
365
|
+
else .version = $v
|
|
366
|
+
end
|
|
367
|
+
]
|
|
368
|
+
' server.json > server.json.tmp && mv server.json.tmp server.json
|
|
369
|
+
|
|
370
|
+
- name: Install MCP Publisher
|
|
371
|
+
run: |
|
|
372
|
+
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
|
|
373
|
+
|
|
374
|
+
- name: Login to MCP Registry
|
|
375
|
+
run: ./mcp-publisher login github-oidc
|
|
376
|
+
|
|
377
|
+
- name: Publish to MCP Registry
|
|
378
|
+
run: |
|
|
379
|
+
set +e
|
|
380
|
+
out=$(./mcp-publisher publish 2>&1)
|
|
381
|
+
rc=$?
|
|
382
|
+
echo "$out"
|
|
383
|
+
set -e
|
|
384
|
+
if [ $rc -ne 0 ] && echo "$out" | grep -q "cannot publish duplicate version"; then
|
|
385
|
+
echo "duplicate version already in registry - idempotent recovery, treating as success"
|
|
386
|
+
exit 0
|
|
387
|
+
fi
|
|
388
|
+
exit $rc
|