repolore-mcp 0.2.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.
Files changed (107) hide show
  1. repolore_mcp-0.2.0/.gitattributes +1 -0
  2. repolore_mcp-0.2.0/.githooks/pre-commit +5 -0
  3. repolore_mcp-0.2.0/.githooks/pre-push +7 -0
  4. repolore_mcp-0.2.0/.github/dependabot.yml +29 -0
  5. repolore_mcp-0.2.0/.github/rulesets/README.md +20 -0
  6. repolore_mcp-0.2.0/.github/rulesets/protect-main.json +61 -0
  7. repolore_mcp-0.2.0/.github/workflows/ci.yml +145 -0
  8. repolore_mcp-0.2.0/.github/workflows/codeql.yml +45 -0
  9. repolore_mcp-0.2.0/.github/workflows/release.yml +135 -0
  10. repolore_mcp-0.2.0/.gitignore +11 -0
  11. repolore_mcp-0.2.0/.python-version +1 -0
  12. repolore_mcp-0.2.0/AGENTS.md +279 -0
  13. repolore_mcp-0.2.0/CLAUDE.md +1 -0
  14. repolore_mcp-0.2.0/EXPERIMENTS.md +302 -0
  15. repolore_mcp-0.2.0/LICENSE +21 -0
  16. repolore_mcp-0.2.0/PKG-INFO +344 -0
  17. repolore_mcp-0.2.0/README.md +300 -0
  18. repolore_mcp-0.2.0/SECURITY.md +40 -0
  19. repolore_mcp-0.2.0/_typos.toml +3 -0
  20. repolore_mcp-0.2.0/bonsai-lint.toml +1 -0
  21. repolore_mcp-0.2.0/docs/images/cover-hero.jpg +0 -0
  22. repolore_mcp-0.2.0/evals/questions.example.yaml +29 -0
  23. repolore_mcp-0.2.0/pyproject.toml +122 -0
  24. repolore_mcp-0.2.0/src/repolore/__init__.py +4 -0
  25. repolore_mcp-0.2.0/src/repolore/__main__.py +3 -0
  26. repolore_mcp-0.2.0/src/repolore/chunking.py +242 -0
  27. repolore_mcp-0.2.0/src/repolore/cli.py +691 -0
  28. repolore_mcp-0.2.0/src/repolore/clients.py +147 -0
  29. repolore_mcp-0.2.0/src/repolore/config.py +29 -0
  30. repolore_mcp-0.2.0/src/repolore/discovery.py +84 -0
  31. repolore_mcp-0.2.0/src/repolore/doctor.py +252 -0
  32. repolore_mcp-0.2.0/src/repolore/documents.py +271 -0
  33. repolore_mcp-0.2.0/src/repolore/embedding/__init__.py +36 -0
  34. repolore_mcp-0.2.0/src/repolore/embedding/engines.py +34 -0
  35. repolore_mcp-0.2.0/src/repolore/embedding/local.py +250 -0
  36. repolore_mcp-0.2.0/src/repolore/embedding/mlx_engine.py +129 -0
  37. repolore_mcp-0.2.0/src/repolore/embedding/model_files.py +143 -0
  38. repolore_mcp-0.2.0/src/repolore/embedding/voyage-4-nano.tensors.json +1037 -0
  39. repolore_mcp-0.2.0/src/repolore/evals/__init__.py +0 -0
  40. repolore_mcp-0.2.0/src/repolore/evals/harness.py +89 -0
  41. repolore_mcp-0.2.0/src/repolore/evals/metrics.py +30 -0
  42. repolore_mcp-0.2.0/src/repolore/evals/questions.py +64 -0
  43. repolore_mcp-0.2.0/src/repolore/evals/runs.py +34 -0
  44. repolore_mcp-0.2.0/src/repolore/hub.py +20 -0
  45. repolore_mcp-0.2.0/src/repolore/indexing.py +104 -0
  46. repolore_mcp-0.2.0/src/repolore/ingest.py +92 -0
  47. repolore_mcp-0.2.0/src/repolore/instructions.py +14 -0
  48. repolore_mcp-0.2.0/src/repolore/locks.py +18 -0
  49. repolore_mcp-0.2.0/src/repolore/mcp_server.py +280 -0
  50. repolore_mcp-0.2.0/src/repolore/paths.py +41 -0
  51. repolore_mcp-0.2.0/src/repolore/processes.py +53 -0
  52. repolore_mcp-0.2.0/src/repolore/progress.py +35 -0
  53. repolore_mcp-0.2.0/src/repolore/refresh.py +155 -0
  54. repolore_mcp-0.2.0/src/repolore/relay.py +207 -0
  55. repolore_mcp-0.2.0/src/repolore/reranking.py +57 -0
  56. repolore_mcp-0.2.0/src/repolore/retrieval.py +72 -0
  57. repolore_mcp-0.2.0/src/repolore/server_state.py +106 -0
  58. repolore_mcp-0.2.0/src/repolore/setup.py +62 -0
  59. repolore_mcp-0.2.0/src/repolore/shared_server.py +118 -0
  60. repolore_mcp-0.2.0/src/repolore/sources/__init__.py +0 -0
  61. repolore_mcp-0.2.0/src/repolore/sources/github.graphql +156 -0
  62. repolore_mcp-0.2.0/src/repolore/sources/github.py +409 -0
  63. repolore_mcp-0.2.0/src/repolore/sources/models.py +127 -0
  64. repolore_mcp-0.2.0/src/repolore/sources/plans.py +76 -0
  65. repolore_mcp-0.2.0/src/repolore/store.py +293 -0
  66. repolore_mcp-0.2.0/tests/conftest.py +172 -0
  67. repolore_mcp-0.2.0/tests/fixtures/crashcause/commits.json +59 -0
  68. repolore_mcp-0.2.0/tests/fixtures/crashcause/docs.json +10 -0
  69. repolore_mcp-0.2.0/tests/fixtures/crashcause/info.json +8 -0
  70. repolore_mcp-0.2.0/tests/fixtures/crashcause/issues.json +28 -0
  71. repolore_mcp-0.2.0/tests/fixtures/crashcause/pulls.json +260 -0
  72. repolore_mcp-0.2.0/tests/fixtures/openwb-ui-settings-854.json +136 -0
  73. repolore_mcp-0.2.0/tests/fixtures/voyage-4-nano-reference.json +1 -0
  74. repolore_mcp-0.2.0/tests/test_chunking.py +391 -0
  75. repolore_mcp-0.2.0/tests/test_cli.py +1276 -0
  76. repolore_mcp-0.2.0/tests/test_clients.py +243 -0
  77. repolore_mcp-0.2.0/tests/test_config.py +57 -0
  78. repolore_mcp-0.2.0/tests/test_discovery.py +133 -0
  79. repolore_mcp-0.2.0/tests/test_doctor.py +501 -0
  80. repolore_mcp-0.2.0/tests/test_documents.py +371 -0
  81. repolore_mcp-0.2.0/tests/test_engines.py +69 -0
  82. repolore_mcp-0.2.0/tests/test_github.py +619 -0
  83. repolore_mcp-0.2.0/tests/test_harness.py +101 -0
  84. repolore_mcp-0.2.0/tests/test_hub.py +45 -0
  85. repolore_mcp-0.2.0/tests/test_indexing.py +208 -0
  86. repolore_mcp-0.2.0/tests/test_ingest.py +177 -0
  87. repolore_mcp-0.2.0/tests/test_local.py +322 -0
  88. repolore_mcp-0.2.0/tests/test_locks.py +16 -0
  89. repolore_mcp-0.2.0/tests/test_main.py +25 -0
  90. repolore_mcp-0.2.0/tests/test_mcp_server.py +616 -0
  91. repolore_mcp-0.2.0/tests/test_metrics.py +64 -0
  92. repolore_mcp-0.2.0/tests/test_mlx_engine.py +305 -0
  93. repolore_mcp-0.2.0/tests/test_model_files.py +284 -0
  94. repolore_mcp-0.2.0/tests/test_paths.py +32 -0
  95. repolore_mcp-0.2.0/tests/test_plans.py +101 -0
  96. repolore_mcp-0.2.0/tests/test_processes.py +168 -0
  97. repolore_mcp-0.2.0/tests/test_questions.py +105 -0
  98. repolore_mcp-0.2.0/tests/test_refresh.py +299 -0
  99. repolore_mcp-0.2.0/tests/test_relay.py +415 -0
  100. repolore_mcp-0.2.0/tests/test_reranking.py +100 -0
  101. repolore_mcp-0.2.0/tests/test_retrieval.py +141 -0
  102. repolore_mcp-0.2.0/tests/test_runs.py +52 -0
  103. repolore_mcp-0.2.0/tests/test_server_state.py +116 -0
  104. repolore_mcp-0.2.0/tests/test_setup.py +105 -0
  105. repolore_mcp-0.2.0/tests/test_shared_server.py +175 -0
  106. repolore_mcp-0.2.0/tests/test_store.py +274 -0
  107. repolore_mcp-0.2.0/uv.lock +2112 -0
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+ if [ "$(git symbolic-ref --short -q HEAD)" = "main" ]; then
3
+ echo "Direct commits to main are blocked: create a branch and open a PR." >&2
4
+ exit 1
5
+ fi
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+ while read -r _ _ remote_ref _; do
3
+ if [ "$remote_ref" = "refs/heads/main" ]; then
4
+ echo "Direct pushes to main are blocked: push a branch and open a PR." >&2
5
+ exit 1
6
+ fi
7
+ done
@@ -0,0 +1,29 @@
1
+ version: 2
2
+
3
+ # Weekly version bumps for both ecosystems. The workflow actions are pinned to a commit with the
4
+ # version in a comment, and Dependabot moves both together.
5
+ #
6
+ # Cooldown holds a release for seven days before proposing it: a malicious or broken publish is
7
+ # usually caught and pulled within the week. Security updates for vulnerable dependencies are
8
+ # switched on in the repository settings and arrive on their own, not on this schedule.
9
+ updates:
10
+ - package-ecosystem: uv
11
+ directory: /
12
+ schedule:
13
+ interval: weekly
14
+ cooldown:
15
+ default-days: 7
16
+ groups:
17
+ # Alone, because CI can't run Metal: an mlx bump waits for `uv run pytest -m live` on a Mac.
18
+ mlx:
19
+ patterns: [mlx, mlx-cpu, mlx-metal]
20
+ uv-minor-and-patch:
21
+ update-types: [minor, patch]
22
+ exclude-patterns: [mlx, mlx-cpu, mlx-metal]
23
+
24
+ - package-ecosystem: github-actions
25
+ directory: /
26
+ schedule:
27
+ interval: weekly
28
+ cooldown:
29
+ default-days: 7
@@ -0,0 +1,20 @@
1
+ # Branch protection ruleset
2
+
3
+ `protect-main.json` is the "Protect main" ruleset, applied to this repository. It blocks deleting
4
+ or force-pushing the default branch, requires every change to go through a pull request (no
5
+ direct pushes, no bypass, admins included), and requires the `lint`, `typecheck`,
6
+ `test (ubuntu-latest)`, `test (windows-latest)`, `test (macos-latest)`, `build`, `typos`, `zizmor` and `actionlint`
7
+ checks from `.github/workflows/ci.yml` to pass before a PR can merge. `audit` runs on every PR
8
+ but isn't required: a newly published advisory would otherwise block every PR, related or not. No
9
+ required review count: solo repo, so that's just CI plus a PR.
10
+
11
+ CodeQL (Python and Actions) runs from `.github/workflows/codeql.yml`. Its `analyze (python)` and
12
+ `analyze (actions)` jobs are required checks, so a failed analysis shows as a failed check rather
13
+ than as results that never arrive. A `code_scanning` rule gates the results themselves: a PR can't
14
+ merge without them, or with an error-level alert or a high or critical security alert. GitHub's default setup is switched off,
15
+ because it skips pull requests from forks, which would leave this rule waiting on them forever.
16
+
17
+ The file is the source of truth, but GitHub doesn't read it. A change here, such as a renamed or
18
+ added CI job, has to be applied by hand: **Settings → Rules → Rulesets → Protect main**, or
19
+ through the API with `gh api -X PUT repos/ryckakas/repolore/rulesets/<id> --input
20
+ .github/rulesets/protect-main.json` (`gh api repos/ryckakas/repolore/rulesets` lists the id).
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "Protect main",
3
+ "target": "branch",
4
+ "enforcement": "active",
5
+ "conditions": {
6
+ "ref_name": {
7
+ "include": ["~DEFAULT_BRANCH"],
8
+ "exclude": []
9
+ }
10
+ },
11
+ "rules": [
12
+ {
13
+ "type": "deletion"
14
+ },
15
+ {
16
+ "type": "non_fast_forward"
17
+ },
18
+ {
19
+ "type": "pull_request",
20
+ "parameters": {
21
+ "required_approving_review_count": 0,
22
+ "dismiss_stale_reviews_on_push": false,
23
+ "require_code_owner_review": false,
24
+ "require_last_push_approval": false,
25
+ "required_review_thread_resolution": false
26
+ }
27
+ },
28
+ {
29
+ "type": "required_status_checks",
30
+ "parameters": {
31
+ "required_status_checks": [
32
+ { "context": "lint" },
33
+ { "context": "typecheck" },
34
+ { "context": "test (ubuntu-latest)" },
35
+ { "context": "test (windows-latest)" },
36
+ { "context": "test (macos-latest)" },
37
+ { "context": "build" },
38
+ { "context": "typos" },
39
+ { "context": "zizmor" },
40
+ { "context": "actionlint" },
41
+ { "context": "analyze (python)" },
42
+ { "context": "analyze (actions)" }
43
+ ],
44
+ "strict_required_status_checks_policy": false
45
+ }
46
+ },
47
+ {
48
+ "type": "code_scanning",
49
+ "parameters": {
50
+ "code_scanning_tools": [
51
+ {
52
+ "tool": "CodeQL",
53
+ "alerts_threshold": "errors",
54
+ "security_alerts_threshold": "high_or_higher"
55
+ }
56
+ ]
57
+ }
58
+ }
59
+ ],
60
+ "bypass_actors": []
61
+ }
@@ -0,0 +1,145 @@
1
+ name: CI
2
+
3
+ # Every job's name below becomes a GitHub status check, and the "Protect main" ruleset
4
+ # (.github/rulesets/protect-main.json) requires each one by that exact name before a PR can merge,
5
+ # so a job renamed here needs the ruleset updated to match.
6
+ #
7
+ # Workflows are audited like code (see the zizmor job): every action is pinned to a full commit
8
+ # with its version in a comment, and Dependabot moves both. A tag can be moved to other code; a
9
+ # commit cannot.
10
+ on:
11
+ pull_request:
12
+ push:
13
+ branches: [main]
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ # A new push to a pull request supersedes the run already going; main keeps every run so each
19
+ # merge has its own result.
20
+ concurrency:
21
+ group: ${{ github.workflow }}-${{ github.ref }}
22
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
23
+
24
+ jobs:
25
+ lint:
26
+ runs-on: ubuntu-latest
27
+ timeout-minutes: 10
28
+ steps:
29
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
30
+ with:
31
+ persist-credentials: false
32
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
33
+ - run: uv sync --locked
34
+ - run: uv run ruff check --output-format=github
35
+ - run: uv run ruff format --check
36
+ - run: uv run bonsai-lint --format github
37
+
38
+ typecheck:
39
+ runs-on: ubuntu-latest
40
+ timeout-minutes: 10
41
+ steps:
42
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
43
+ with:
44
+ persist-credentials: false
45
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
46
+ # The extra brings sentence-transformers, so reranking.py is checked against its real types.
47
+ - run: uv sync --locked --extra rerank
48
+ - run: uv run pyright
49
+
50
+ # Windows is load-bearing rather than ceremony: it is the development machine and where installed
51
+ # users run the CLI, and its default cp1252 codec is a known way to break on GitHub content.
52
+ test:
53
+ name: test (${{ matrix.os }})
54
+ runs-on: ${{ matrix.os }}
55
+ timeout-minutes: 15
56
+ strategy:
57
+ fail-fast: false
58
+ matrix:
59
+ os: [ubuntu-latest, windows-latest, macos-latest]
60
+ steps:
61
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
62
+ with:
63
+ persist-credentials: false
64
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
65
+ - run: uv sync --locked
66
+ - run: uv run pytest --cov
67
+
68
+ # Installs the built wheel in an isolated environment and runs the entry point, which catches a
69
+ # packaging break (missing module, wrong script target) that tests against the source tree can't.
70
+ build:
71
+ runs-on: ubuntu-latest
72
+ timeout-minutes: 15
73
+ steps:
74
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
75
+ with:
76
+ persist-credentials: false
77
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
78
+ - run: uv build
79
+ - run: uv run --isolated --no-project --with dist/*.whl repolore version
80
+
81
+ typos:
82
+ runs-on: ubuntu-latest
83
+ timeout-minutes: 5
84
+ steps:
85
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
86
+ with:
87
+ persist-credentials: false
88
+ # An exact release, since each one adds corrections; Dependabot proposes the next one.
89
+ - uses: crate-ci/typos@7f9d0819ca1c008ade73f4b41fa9d258d48f605b # v1.51.1
90
+
91
+ # Workflow security: pinned actions, least-privilege tokens, no credentials left behind, no
92
+ # template injection.
93
+ zizmor:
94
+ runs-on: ubuntu-latest
95
+ timeout-minutes: 5
96
+ steps:
97
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
98
+ with:
99
+ persist-credentials: false
100
+ # An exact release: new audits arrive with a deliberate bump, not with an unrelated pull
101
+ # request.
102
+ - uses: taiki-e/install-action@68a389c788b2cd04016ce8c80ee23b80d2ab3d68 # v2.88.1
103
+ with:
104
+ tool: zizmor@1.31.1
105
+ # The token lets zizmor check that every pinned commit belongs to its action's repository.
106
+ - run: zizmor --format github .github
107
+ env:
108
+ GH_TOKEN: ${{ github.token }}
109
+
110
+ # Workflow correctness, where zizmor covers security, plus shellcheck over every `run:` block
111
+ # and the git hooks, which have no .sh extension for a glob to find.
112
+ actionlint:
113
+ runs-on: ubuntu-latest
114
+ timeout-minutes: 5
115
+ steps:
116
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
117
+ with:
118
+ persist-credentials: false
119
+ # actionlint skips the `run:` blocks without a word when shellcheck is missing, so it is
120
+ # installed first, at an exact release.
121
+ - uses: taiki-e/install-action@68a389c788b2cd04016ce8c80ee23b80d2ab3d68 # v2.88.1
122
+ with:
123
+ tool: shellcheck@0.11.0
124
+ # The release binary, checked against its published SHA-256. Dependabot doesn't see this
125
+ # pin; bump the version and the hash together.
126
+ - name: Install actionlint
127
+ run: |
128
+ curl -sSfL -o "$RUNNER_TEMP/actionlint.tar.gz" \
129
+ https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz
130
+ echo "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8 $RUNNER_TEMP/actionlint.tar.gz" | sha256sum -c -
131
+ tar -xzf "$RUNNER_TEMP/actionlint.tar.gz" -C "$RUNNER_TEMP" actionlint
132
+ - run: '"$RUNNER_TEMP/actionlint"'
133
+ - run: shellcheck .githooks/*
134
+
135
+ # Known advisories in uv.lock. Not a required check: an advisory published overnight would
136
+ # otherwise block every pull request, related or not. `uv audit` is still a preview feature.
137
+ audit:
138
+ runs-on: ubuntu-latest
139
+ timeout-minutes: 5
140
+ steps:
141
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
142
+ with:
143
+ persist-credentials: false
144
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
145
+ - run: uv audit --locked --preview-features audit-command
@@ -0,0 +1,45 @@
1
+ name: CodeQL
2
+
3
+ # Advanced setup rather than GitHub's default setup, because default setup skips pull requests from
4
+ # forks, and the "Protect main" ruleset's code_scanning rule then waits on them forever. The
5
+ # categories match the ones default setup used, so alerts carry over. The ruleset also requires each
6
+ # analyze job by name, so renaming one needs the ruleset updated to match.
7
+ #
8
+ # No weekly schedule: the pinned action fixes the CodeQL version, so rescanning an unchanged main
9
+ # would run the same queries. New queries arrive with a Dependabot bump, which is scanned on its pull
10
+ # request and again on main.
11
+ on:
12
+ pull_request:
13
+ push:
14
+ branches: [main]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ concurrency:
20
+ group: ${{ github.workflow }}-${{ github.ref }}
21
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
22
+
23
+ jobs:
24
+ analyze:
25
+ name: analyze (${{ matrix.language }})
26
+ runs-on: ubuntu-latest
27
+ timeout-minutes: 15
28
+ permissions:
29
+ contents: read
30
+ security-events: write
31
+ strategy:
32
+ fail-fast: false
33
+ matrix:
34
+ language: [python, actions]
35
+ steps:
36
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
37
+ with:
38
+ persist-credentials: false
39
+ - uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
40
+ with:
41
+ languages: ${{ matrix.language }}
42
+ build-mode: none
43
+ - uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
44
+ with:
45
+ category: /language:${{ matrix.language }}
@@ -0,0 +1,135 @@
1
+ name: Release
2
+
3
+ # PyPI and TestPyPI trust this file. A vX.Y.Z tag publishes that version to PyPI and creates its GitHub Release.
4
+ # Dispatched by hand, it rehearses on TestPyPI with a .dev<run number> version, or reruns a failed PyPI publish when
5
+ # started at a release tag. A version on PyPI is permanent, so nothing is ever replaced there.
6
+ on:
7
+ push:
8
+ tags: ["v*"]
9
+ workflow_dispatch:
10
+ inputs:
11
+ target:
12
+ description: "Index to upload to"
13
+ type: choice
14
+ options: [testpypi, pypi]
15
+ default: testpypi
16
+
17
+ permissions: {}
18
+
19
+ concurrency:
20
+ group: release-${{ github.ref }}
21
+ cancel-in-progress: false
22
+
23
+ jobs:
24
+ build:
25
+ runs-on: ubuntu-latest
26
+ timeout-minutes: 15
27
+ permissions:
28
+ contents: read
29
+ outputs:
30
+ target: ${{ steps.request.outputs.target }}
31
+ version: ${{ steps.request.outputs.version }}
32
+ steps:
33
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
34
+ with:
35
+ # hatch-vcs needs the history and tags to count the commits since the last release.
36
+ fetch-depth: 0
37
+ persist-credentials: false
38
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
39
+ with:
40
+ # A release must not restore what another workflow wrote.
41
+ enable-cache: false
42
+ - name: Check the request
43
+ id: request
44
+ env:
45
+ TARGET: ${{ inputs.target || 'pypi' }}
46
+ run: |
47
+ if [ "$TARGET" = pypi ]; then
48
+ if [[ ! "$GITHUB_REF" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
49
+ echo "::error::PyPI takes only a release tag vX.Y.Z, not $GITHUB_REF; prereleases are not published"
50
+ exit 1
51
+ fi
52
+ if ! git merge-base --is-ancestor HEAD origin/main; then
53
+ echo "::error::$GITHUB_REF_NAME is not on main"
54
+ exit 1
55
+ fi
56
+ version="${GITHUB_REF_NAME#v}"
57
+ else
58
+ # TestPyPI rejects the local +g<sha> part, and a rerun needs a version it hasn't seen.
59
+ scm=$(uv run --no-project --python 3.12 --with 'hatch-vcs>=0.5.0,<0.6' python -c \
60
+ 'from setuptools_scm import get_version; print(get_version(local_scheme="no-local-version"))')
61
+ version="${scm%%.dev*}.dev$GITHUB_RUN_NUMBER"
62
+ fi
63
+ echo "Publishing repolore-mcp $version to $TARGET"
64
+ {
65
+ echo "target=$TARGET"
66
+ echo "version=$version"
67
+ } >> "$GITHUB_OUTPUT"
68
+ - name: Build
69
+ env:
70
+ TARGET: ${{ steps.request.outputs.target }}
71
+ VERSION: ${{ steps.request.outputs.version }}
72
+ run: |
73
+ # A release builds the version hatch-vcs reads from its tag, which the next step checks.
74
+ if [ "$TARGET" = testpypi ]; then
75
+ export SETUPTOOLS_SCM_PRETEND_VERSION="$VERSION"
76
+ fi
77
+ uv build --no-sources
78
+ - name: Check the wheel
79
+ env:
80
+ VERSION: ${{ steps.request.outputs.version }}
81
+ run: |
82
+ wheel="dist/repolore_mcp-$VERSION-py3-none-any.whl"
83
+ if [ ! -f "$wheel" ]; then
84
+ echo "::error::expected $wheel, built: $(ls dist)"
85
+ exit 1
86
+ fi
87
+ uv run --isolated --no-project --python 3.12 --with "$wheel" repolore version | tee version.txt
88
+ grep -qx "repolore $VERSION" version.txt
89
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
90
+ with:
91
+ name: dist
92
+ path: dist/
93
+ if-no-files-found: error
94
+ retention-days: 7
95
+
96
+ publish:
97
+ needs: build
98
+ runs-on: ubuntu-latest
99
+ timeout-minutes: 15
100
+ environment:
101
+ name: ${{ needs.build.outputs.target }}
102
+ url: ${{ needs.build.outputs.target == 'pypi' && 'https://pypi.org/p/repolore-mcp' || 'https://test.pypi.org/p/repolore-mcp' }}
103
+ permissions:
104
+ id-token: write
105
+ steps:
106
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
107
+ with:
108
+ name: dist
109
+ path: dist/
110
+ - uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
111
+ with:
112
+ repository-url: ${{ needs.build.outputs.target == 'pypi' && 'https://upload.pypi.org/legacy/' || 'https://test.pypi.org/legacy/' }}
113
+
114
+ github-release:
115
+ needs: [build, publish]
116
+ if: needs.build.outputs.target == 'pypi'
117
+ runs-on: ubuntu-latest
118
+ timeout-minutes: 10
119
+ permissions:
120
+ contents: write
121
+ steps:
122
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
123
+ with:
124
+ name: dist
125
+ path: dist/
126
+ - name: Create the GitHub Release
127
+ env:
128
+ GH_TOKEN: ${{ github.token }}
129
+ run: |
130
+ # A rerun of this job, or a release made by hand, finds it already there.
131
+ if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
132
+ echo "$GITHUB_REF_NAME already has a GitHub Release"
133
+ exit 0
134
+ fi
135
+ gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --generate-notes --verify-tag
@@ -0,0 +1,11 @@
1
+ .plans/
2
+ tmp/
3
+ .env
4
+ *.db
5
+ .venv/
6
+ __pycache__/
7
+ *.py[cod]
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ dist/
11
+ .coverage
@@ -0,0 +1 @@
1
+ 3.12