repotour 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.
Files changed (171) hide show
  1. repotour-1.0.0/.gitattributes +4 -0
  2. repotour-1.0.0/.github/ISSUE_TEMPLATE/bug_report.yml +45 -0
  3. repotour-1.0.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. repotour-1.0.0/.github/ISSUE_TEMPLATE/feature_request.yml +22 -0
  5. repotour-1.0.0/.github/dependabot.yml +21 -0
  6. repotour-1.0.0/.github/pull_request_template.md +15 -0
  7. repotour-1.0.0/.github/workflows/action.yml +68 -0
  8. repotour-1.0.0/.github/workflows/benchmarks.yml +30 -0
  9. repotour-1.0.0/.github/workflows/ci.yml +58 -0
  10. repotour-1.0.0/.github/workflows/corpus.yml +35 -0
  11. repotour-1.0.0/.github/workflows/release.yml +102 -0
  12. repotour-1.0.0/.github/workflows/samples.yml +34 -0
  13. repotour-1.0.0/.gitignore +9 -0
  14. repotour-1.0.0/.pre-commit-config.yaml +20 -0
  15. repotour-1.0.0/CHANGELOG.md +108 -0
  16. repotour-1.0.0/CODE_OF_CONDUCT.md +9 -0
  17. repotour-1.0.0/CONTRIBUTING.md +56 -0
  18. repotour-1.0.0/GLOSSARY.md +83 -0
  19. repotour-1.0.0/LICENSE +21 -0
  20. repotour-1.0.0/PKG-INFO +193 -0
  21. repotour-1.0.0/README.md +158 -0
  22. repotour-1.0.0/SECURITY.md +18 -0
  23. repotour-1.0.0/action.yml +111 -0
  24. repotour-1.0.0/benchmarks/__init__.py +0 -0
  25. repotour-1.0.0/benchmarks/corpus.py +127 -0
  26. repotour-1.0.0/benchmarks/run.py +213 -0
  27. repotour-1.0.0/benchmarks/samples.py +81 -0
  28. repotour-1.0.0/docs/action.md +54 -0
  29. repotour-1.0.0/docs/adr/0001-hotspot-window-anchored-to-head.md +3 -0
  30. repotour-1.0.0/docs/adr/0002-ownership-from-log-not-blame.md +8 -0
  31. repotour-1.0.0/docs/adr/0003-one-tour-per-repository.md +3 -0
  32. repotour-1.0.0/docs/adr/0004-cache-in-git-common-dir.md +8 -0
  33. repotour-1.0.0/docs/adr/0005-go-imports-link-to-a-representative-file.md +8 -0
  34. repotour-1.0.0/docs/configuration.md +75 -0
  35. repotour-1.0.0/docs/design/architecture.md +224 -0
  36. repotour-1.0.0/docs/design/flows.md +264 -0
  37. repotour-1.0.0/docs/design/vision.md +84 -0
  38. repotour-1.0.0/docs/json.md +36 -0
  39. repotour-1.0.0/docs/rubric.md +22 -0
  40. repotour-1.0.0/docs/samples/REVIEW.md +51 -0
  41. repotour-1.0.0/docs/samples/express.md +205 -0
  42. repotour-1.0.0/docs/samples/flask.md +260 -0
  43. repotour-1.0.0/docs/samples/fzf.md +193 -0
  44. repotour-1.0.0/pyproject.toml +73 -0
  45. repotour-1.0.0/repotour/__init__.py +5 -0
  46. repotour-1.0.0/repotour/__main__.py +5 -0
  47. repotour-1.0.0/repotour/analyzers/__init__.py +0 -0
  48. repotour-1.0.0/repotour/analyzers/base.py +29 -0
  49. repotour-1.0.0/repotour/analyzers/basics.py +73 -0
  50. repotour-1.0.0/repotour/analyzers/bulk.py +50 -0
  51. repotour-1.0.0/repotour/analyzers/commands.py +301 -0
  52. repotour-1.0.0/repotour/analyzers/dependencies.py +226 -0
  53. repotour-1.0.0/repotour/analyzers/directories.py +224 -0
  54. repotour-1.0.0/repotour/analyzers/entry_points.py +127 -0
  55. repotour-1.0.0/repotour/analyzers/history.py +104 -0
  56. repotour-1.0.0/repotour/analyzers/manifest_entries.py +145 -0
  57. repotour-1.0.0/repotour/analyzers/overview.py +127 -0
  58. repotour-1.0.0/repotour/analyzers/ownership.py +243 -0
  59. repotour-1.0.0/repotour/analyzers/project.py +368 -0
  60. repotour-1.0.0/repotour/analyzers/reading_order.py +213 -0
  61. repotour-1.0.0/repotour/cache/__init__.py +0 -0
  62. repotour-1.0.0/repotour/cache/history.py +66 -0
  63. repotour-1.0.0/repotour/cache/parse.py +46 -0
  64. repotour-1.0.0/repotour/cache/store.py +190 -0
  65. repotour-1.0.0/repotour/cli.py +134 -0
  66. repotour-1.0.0/repotour/config.py +232 -0
  67. repotour-1.0.0/repotour/errors.py +25 -0
  68. repotour-1.0.0/repotour/git/__init__.py +0 -0
  69. repotour-1.0.0/repotour/git/reader.py +300 -0
  70. repotour-1.0.0/repotour/graph/__init__.py +1 -0
  71. repotour-1.0.0/repotour/graph/collapse.py +76 -0
  72. repotour-1.0.0/repotour/graph/pagerank.py +39 -0
  73. repotour-1.0.0/repotour/languages/__init__.py +13 -0
  74. repotour-1.0.0/repotour/languages/base.py +84 -0
  75. repotour-1.0.0/repotour/languages/go.py +162 -0
  76. repotour-1.0.0/repotour/languages/javascript.py +377 -0
  77. repotour-1.0.0/repotour/languages/python.py +300 -0
  78. repotour-1.0.0/repotour/model/__init__.py +0 -0
  79. repotour-1.0.0/repotour/model/history.py +51 -0
  80. repotour-1.0.0/repotour/model/serde.py +66 -0
  81. repotour-1.0.0/repotour/model/snapshot.py +76 -0
  82. repotour-1.0.0/repotour/model/tour.py +338 -0
  83. repotour-1.0.0/repotour/output.py +44 -0
  84. repotour-1.0.0/repotour/pipeline.py +226 -0
  85. repotour-1.0.0/repotour/py.typed +0 -0
  86. repotour-1.0.0/repotour/render/__init__.py +0 -0
  87. repotour-1.0.0/repotour/render/html.py +73 -0
  88. repotour-1.0.0/repotour/render/json.py +29 -0
  89. repotour-1.0.0/repotour/render/markdown.py +23 -0
  90. repotour-1.0.0/repotour/render/mermaid.py +21 -0
  91. repotour-1.0.0/repotour/render/templates/tour.html.j2 +65 -0
  92. repotour-1.0.0/repotour/render/templates/tour.md.j2 +236 -0
  93. repotour-1.0.0/repotour/render/vendor/mermaid-LICENSE +21 -0
  94. repotour-1.0.0/repotour/render/vendor/mermaid.tiny.js +3581 -0
  95. repotour-1.0.0/repotour/repopath.py +38 -0
  96. repotour-1.0.0/repotour/scan/__init__.py +0 -0
  97. repotour-1.0.0/repotour/scan/classifier.py +153 -0
  98. repotour-1.0.0/repotour/scan/filetypes.py +94 -0
  99. repotour-1.0.0/repotour/scan/globs.py +57 -0
  100. repotour-1.0.0/repotour/scan/scanner.py +65 -0
  101. repotour-1.0.0/repotour/summarize/__init__.py +2 -0
  102. repotour-1.0.0/repotour/summarize/provider.py +151 -0
  103. repotour-1.0.0/repotour/summarize/summarizer.py +178 -0
  104. repotour-1.0.0/tests/__init__.py +0 -0
  105. repotour-1.0.0/tests/conftest.py +39 -0
  106. repotour-1.0.0/tests/corpus.yml +28 -0
  107. repotour-1.0.0/tests/fixtures/__init__.py +0 -0
  108. repotour-1.0.0/tests/fixtures/builder.py +124 -0
  109. repotour-1.0.0/tests/fixtures/repos.py +392 -0
  110. repotour-1.0.0/tests/golden/bulk.md +55 -0
  111. repotour-1.0.0/tests/golden/collapsed.md +85 -0
  112. repotour-1.0.0/tests/golden/commands.md +54 -0
  113. repotour-1.0.0/tests/golden/directories.md +79 -0
  114. repotour-1.0.0/tests/golden/entry-points.md +67 -0
  115. repotour-1.0.0/tests/golden/graph-showcase.html +260 -0
  116. repotour-1.0.0/tests/golden/graph-showcase.json +672 -0
  117. repotour-1.0.0/tests/golden/graph-showcase.md +103 -0
  118. repotour-1.0.0/tests/golden/graph.md +79 -0
  119. repotour-1.0.0/tests/golden/history-showcase.md +78 -0
  120. repotour-1.0.0/tests/golden/history.md +63 -0
  121. repotour-1.0.0/tests/golden/messy.md +86 -0
  122. repotour-1.0.0/tests/golden/minimal.md +58 -0
  123. repotour-1.0.0/tests/golden/monorepo.md +78 -0
  124. repotour-1.0.0/tests/golden/polyglot.md +69 -0
  125. repotour-1.0.0/tests/golden/reading-order.md +88 -0
  126. repotour-1.0.0/tests/golden/shallow.md +62 -0
  127. repotour-1.0.0/tests/golden/showcase.md +98 -0
  128. repotour-1.0.0/tests/golden/team.md +74 -0
  129. repotour-1.0.0/tests/test_bulk_commits.py +139 -0
  130. repotour-1.0.0/tests/test_bus_factor.py +127 -0
  131. repotour-1.0.0/tests/test_cache.py +170 -0
  132. repotour-1.0.0/tests/test_classification.py +150 -0
  133. repotour-1.0.0/tests/test_cli.py +92 -0
  134. repotour-1.0.0/tests/test_commands.py +199 -0
  135. repotour-1.0.0/tests/test_config.py +187 -0
  136. repotour-1.0.0/tests/test_corpus_file.py +14 -0
  137. repotour-1.0.0/tests/test_dependency_graph.py +204 -0
  138. repotour-1.0.0/tests/test_directories.py +134 -0
  139. repotour-1.0.0/tests/test_entry_points.py +212 -0
  140. repotour-1.0.0/tests/test_globs.py +40 -0
  141. repotour-1.0.0/tests/test_go_adapter.py +145 -0
  142. repotour-1.0.0/tests/test_graph_algorithms.py +80 -0
  143. repotour-1.0.0/tests/test_graph_shape.py +125 -0
  144. repotour-1.0.0/tests/test_graph_showcase.py +63 -0
  145. repotour-1.0.0/tests/test_history.py +164 -0
  146. repotour-1.0.0/tests/test_history_showcase.py +74 -0
  147. repotour-1.0.0/tests/test_html_output.py +149 -0
  148. repotour-1.0.0/tests/test_incremental_history.py +117 -0
  149. repotour-1.0.0/tests/test_javascript_adapter.py +185 -0
  150. repotour-1.0.0/tests/test_json_output.py +117 -0
  151. repotour-1.0.0/tests/test_missing_basics.py +106 -0
  152. repotour-1.0.0/tests/test_output_safety.py +122 -0
  153. repotour-1.0.0/tests/test_overview.py +54 -0
  154. repotour-1.0.0/tests/test_ownership.py +129 -0
  155. repotour-1.0.0/tests/test_parse_cache.py +104 -0
  156. repotour-1.0.0/tests/test_pipeline.py +105 -0
  157. repotour-1.0.0/tests/test_preconditions.py +121 -0
  158. repotour-1.0.0/tests/test_project_types.py +242 -0
  159. repotour-1.0.0/tests/test_python_adapter.py +52 -0
  160. repotour-1.0.0/tests/test_rate_limits.py +172 -0
  161. repotour-1.0.0/tests/test_reading_order.py +178 -0
  162. repotour-1.0.0/tests/test_real_repo_lessons.py +161 -0
  163. repotour-1.0.0/tests/test_renames.py +133 -0
  164. repotour-1.0.0/tests/test_render_markdown.py +41 -0
  165. repotour-1.0.0/tests/test_repopath.py +24 -0
  166. repotour-1.0.0/tests/test_scanner.py +83 -0
  167. repotour-1.0.0/tests/test_serde.py +65 -0
  168. repotour-1.0.0/tests/test_showcase.py +47 -0
  169. repotour-1.0.0/tests/test_summaries.py +416 -0
  170. repotour-1.0.0/tests/test_tour_model.py +79 -0
  171. repotour-1.0.0/uv.lock +1052 -0
@@ -0,0 +1,4 @@
1
+ * text=auto eol=lf
2
+ *.png binary
3
+ *.jpg binary
4
+ repotour/render/vendor/** -text -diff linguist-vendored
@@ -0,0 +1,45 @@
1
+ name: Bug report
2
+ description: Something in Repotour doesn't work as expected
3
+ labels: ["bug"]
4
+ body:
5
+ - type: textarea
6
+ id: what-happened
7
+ attributes:
8
+ label: What happened?
9
+ description: What you ran, what you expected, and what you got instead.
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: reproduce
14
+ attributes:
15
+ label: How to reproduce
16
+ description: Ideally a public repository and commit where it happens, plus the exact command.
17
+ placeholder: |
18
+ 1. git clone https://github.com/example/project && git -C project checkout <sha>
19
+ 2. repotour project
20
+ validations:
21
+ required: true
22
+ - type: textarea
23
+ id: wrong-claim
24
+ attributes:
25
+ label: Wrong claim (if any)
26
+ description: If the tour said something wrong, paste the claim and its evidence.
27
+ - type: input
28
+ id: version
29
+ attributes:
30
+ label: Repotour version
31
+ placeholder: "0.1.0"
32
+ validations:
33
+ required: true
34
+ - type: dropdown
35
+ id: os
36
+ attributes:
37
+ label: Operating system
38
+ options: [Linux, macOS, Windows]
39
+ validations:
40
+ required: true
41
+ - type: input
42
+ id: git-version
43
+ attributes:
44
+ label: git version
45
+ placeholder: "2.45.0"
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Report a security vulnerability
4
+ url: https://github.com/babarshahzad2005/repotour/security/advisories/new
5
+ about: Please report security issues privately, not as public issues.
@@ -0,0 +1,22 @@
1
+ name: Feature request
2
+ description: Suggest an improvement to the tour or the tool
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: What problem would this solve?
9
+ description: Describe the newcomer question or maintainer need that isn't served today.
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: What would you like to happen?
16
+ validations:
17
+ required: true
18
+ - type: textarea
19
+ id: evidence
20
+ attributes:
21
+ label: What evidence would back it?
22
+ description: Every claim in a tour points to evidence (a file, a commit range, an import). What would this feature cite?
@@ -0,0 +1,21 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: uv
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ commit-message:
8
+ prefix: "chore(deps)"
9
+ groups:
10
+ python-dependencies:
11
+ patterns: ["*"]
12
+
13
+ - package-ecosystem: github-actions
14
+ directory: /
15
+ schedule:
16
+ interval: weekly
17
+ commit-message:
18
+ prefix: "ci(deps)"
19
+ groups:
20
+ github-actions:
21
+ patterns: ["*"]
@@ -0,0 +1,15 @@
1
+ ## Summary
2
+
3
+ <!-- What does this change do, and why? Link the issue if there is one. -->
4
+
5
+ ## How it was tested
6
+
7
+ <!-- Tests added or updated, fixture repositories used, manual checks. -->
8
+
9
+ ## Checklist
10
+
11
+ - [ ] Tests added or updated (fixture-repository tests for new behavior)
12
+ - [ ] Golden tours updated with `uv run pytest --update-goldens` and the diff reviewed, if output changed
13
+ - [ ] Every new claim in the tour carries evidence
14
+ - [ ] Docs updated if behavior or design changed (`docs/design/`, `GLOSSARY.md`, ADRs)
15
+ - [ ] `CHANGELOG.md` updated for user-visible changes
@@ -0,0 +1,68 @@
1
+ name: Action
2
+
3
+ # Runs this repository's own GitHub Action against itself, without opening a
4
+ # pull request, whenever the action or the code it installs changes.
5
+ on:
6
+ pull_request:
7
+ paths:
8
+ - "action.yml"
9
+ - "repotour/**"
10
+ - "pyproject.toml"
11
+ - ".github/workflows/action.yml"
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ dry-run:
19
+ name: dry run (${{ matrix.os }})
20
+ runs-on: ${{ matrix.os }}
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ os: [ubuntu-latest, windows-latest]
25
+ steps:
26
+ - uses: actions/checkout@v7
27
+ with:
28
+ fetch-depth: 0
29
+ - id: markdown
30
+ uses: ./
31
+ with:
32
+ dry-run: "true"
33
+ - id: html
34
+ uses: ./
35
+ with:
36
+ format: html
37
+ path: site/TOUR.html
38
+ dry-run: "true"
39
+ - name: Check the outputs
40
+ shell: bash
41
+ env:
42
+ MD_CHANGED: ${{ steps.markdown.outputs.changed }}
43
+ HTML_CHANGED: ${{ steps.html.outputs.changed }}
44
+ PR_URL: ${{ steps.markdown.outputs.pull-request }}
45
+ run: |
46
+ test "$MD_CHANGED" = "true"
47
+ test "$HTML_CHANGED" = "true"
48
+ test -z "$PR_URL"
49
+ head -1 TOUR.md | grep -qx "<!-- generated by repotour -->"
50
+ grep -q "<!doctype html>" site/TOUR.html
51
+
52
+ shallow-clone:
53
+ name: shallow clone is refused
54
+ runs-on: ubuntu-latest
55
+ steps:
56
+ - uses: actions/checkout@v7
57
+ with:
58
+ fetch-depth: 1
59
+ - id: tour
60
+ uses: ./
61
+ continue-on-error: true
62
+ with:
63
+ dry-run: "true"
64
+ - name: Expect a failure
65
+ shell: bash
66
+ env:
67
+ OUTCOME: ${{ steps.tour.outcome }}
68
+ run: test "$OUTCOME" = "failure"
@@ -0,0 +1,30 @@
1
+ name: Benchmarks
2
+
3
+ # Performance targets are measured on real repositories pinned to exact
4
+ # commits. Not run on every push: weekly, on demand, and when the benchmark
5
+ # itself changes.
6
+ on:
7
+ workflow_dispatch:
8
+ schedule:
9
+ - cron: "0 6 * * 1"
10
+ pull_request:
11
+ paths:
12
+ - "benchmarks/**"
13
+ - ".github/workflows/benchmarks.yml"
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ benchmark:
20
+ name: benchmark (ubuntu-latest)
21
+ runs-on: ubuntu-latest
22
+ timeout-minutes: 90
23
+ steps:
24
+ - uses: actions/checkout@v7
25
+ - uses: astral-sh/setup-uv@v7
26
+ with:
27
+ python-version: "3.13"
28
+ - run: uv sync --locked
29
+ - name: Run benchmarks
30
+ run: uv run python benchmarks/run.py --dir "$RUNNER_TEMP/bench"
@@ -0,0 +1,58 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: ci-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ lint:
17
+ name: lint
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v7
21
+ - uses: astral-sh/setup-uv@v7
22
+ with:
23
+ python-version: "3.11"
24
+ - run: uv sync --locked
25
+ - run: uv run ruff check
26
+ - run: uv run ruff format --check
27
+ - run: uv run mypy
28
+
29
+ test:
30
+ name: test (${{ matrix.os }}, py${{ matrix.python }})
31
+ runs-on: ${{ matrix.os }}
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ os: [ubuntu-latest, macos-latest, windows-latest]
36
+ python: ["3.11", "3.13"]
37
+ steps:
38
+ - uses: actions/checkout@v7
39
+ - uses: astral-sh/setup-uv@v7
40
+ with:
41
+ python-version: ${{ matrix.python }}
42
+ - run: uv sync --locked
43
+ - run: uv run pytest
44
+ - name: Verify goldens are unchanged
45
+ shell: bash
46
+ run: git diff --exit-code -- tests/golden
47
+
48
+ # Single required check for branch protection.
49
+ ci-ok:
50
+ name: ci-ok
51
+ if: always()
52
+ needs: [lint, test]
53
+ runs-on: ubuntu-latest
54
+ steps:
55
+ - name: All jobs passed
56
+ run: |
57
+ test "${{ needs.lint.result }}" = "success"
58
+ test "${{ needs.test.result }}" = "success"
@@ -0,0 +1,35 @@
1
+ name: Corpus
2
+
3
+ # Tours 20 real repositories pinned in tests/corpus.yml and fails on any
4
+ # crash. Weekly, on demand, and when the corpus or its runner changes.
5
+ on:
6
+ workflow_dispatch:
7
+ schedule:
8
+ - cron: "0 7 * * 1"
9
+ pull_request:
10
+ paths:
11
+ - "tests/corpus.yml"
12
+ - "benchmarks/corpus.py"
13
+ - ".github/workflows/corpus.yml"
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ corpus:
20
+ name: corpus (ubuntu-latest)
21
+ runs-on: ubuntu-latest
22
+ timeout-minutes: 90
23
+ steps:
24
+ - uses: actions/checkout@v7
25
+ - uses: astral-sh/setup-uv@v7
26
+ with:
27
+ python-version: "3.13"
28
+ - run: uv sync --locked
29
+ - name: Tour every corpus repository
30
+ run: uv run python benchmarks/corpus.py --dir "$RUNNER_TEMP/corpus"
31
+ - uses: actions/upload-artifact@v7
32
+ if: always()
33
+ with:
34
+ name: corpus-tours
35
+ path: ${{ runner.temp }}/corpus/tours/*.md
@@ -0,0 +1,102 @@
1
+ name: Release
2
+
3
+ # Builds the package once, checks that the built wheel installs and runs on
4
+ # every supported platform, then publishes it with PyPI trusted publishing
5
+ # (no stored token). Run it by hand to publish to TestPyPI first; publishing
6
+ # a GitHub release publishes to PyPI.
7
+ on:
8
+ workflow_dispatch:
9
+ release:
10
+ types: [published]
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ build:
17
+ name: build
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v7
21
+ - uses: astral-sh/setup-uv@v7
22
+ with:
23
+ python-version: "3.13"
24
+ - name: Check the tag matches the version
25
+ if: github.event_name == 'release'
26
+ env:
27
+ TAG: ${{ github.event.release.tag_name }}
28
+ run: |
29
+ version=$(uv run --no-project python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
30
+ test "$TAG" = "v$version" || { echo "::error::Tag $TAG does not match version $version"; exit 1; }
31
+ - run: uv build
32
+ - uses: actions/upload-artifact@v7
33
+ with:
34
+ name: dist
35
+ path: dist/
36
+
37
+ smoke-test:
38
+ name: smoke test (${{ matrix.os }}, py${{ matrix.python }})
39
+ needs: build
40
+ runs-on: ${{ matrix.os }}
41
+ strategy:
42
+ fail-fast: false
43
+ matrix:
44
+ os: [ubuntu-latest, macos-latest, windows-latest]
45
+ python: ["3.11", "3.13"]
46
+ steps:
47
+ - uses: actions/checkout@v7
48
+ with:
49
+ fetch-depth: 0
50
+ - uses: actions/setup-python@v6
51
+ with:
52
+ python-version: ${{ matrix.python }}
53
+ - uses: actions/download-artifact@v8
54
+ with:
55
+ name: dist
56
+ path: dist
57
+ - name: Install the wheel and tour this repository
58
+ shell: bash
59
+ run: |
60
+ python -m pip install --disable-pip-version-check dist/*.whl
61
+ cd "$RUNNER_TEMP"
62
+ repotour --help
63
+ repotour "$GITHUB_WORKSPACE" --out tour.md --format md
64
+ repotour "$GITHUB_WORKSPACE" --out tour.html --format html --json tour.json
65
+ head -1 tour.md | grep -qx "<!-- generated by repotour -->"
66
+
67
+ testpypi:
68
+ name: publish to TestPyPI
69
+ if: github.event_name == 'workflow_dispatch'
70
+ needs: smoke-test
71
+ runs-on: ubuntu-latest
72
+ environment:
73
+ name: testpypi
74
+ url: https://test.pypi.org/project/repotour/
75
+ permissions:
76
+ id-token: write
77
+ steps:
78
+ - uses: actions/download-artifact@v8
79
+ with:
80
+ name: dist
81
+ path: dist
82
+ - uses: pypa/gh-action-pypi-publish@release/v1
83
+ with:
84
+ repository-url: https://test.pypi.org/legacy/
85
+ skip-existing: true
86
+
87
+ pypi:
88
+ name: publish to PyPI
89
+ if: github.event_name == 'release'
90
+ needs: smoke-test
91
+ runs-on: ubuntu-latest
92
+ environment:
93
+ name: pypi
94
+ url: https://pypi.org/project/repotour/
95
+ permissions:
96
+ id-token: write
97
+ steps:
98
+ - uses: actions/download-artifact@v8
99
+ with:
100
+ name: dist
101
+ path: dist
102
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,34 @@
1
+ name: Sample tours
2
+
3
+ # Generates the published sample tours (Flask, Express, fzf at pinned commits)
4
+ # and uploads them as an artifact for review. Runs on demand and whenever
5
+ # product code or the generator changes, so every code change is also checked
6
+ # against three real repositories.
7
+ on:
8
+ workflow_dispatch:
9
+ pull_request:
10
+ paths:
11
+ - "repotour/**"
12
+ - "benchmarks/samples.py"
13
+ - ".github/workflows/samples.yml"
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ samples:
20
+ name: sample tours
21
+ runs-on: ubuntu-latest
22
+ timeout-minutes: 20
23
+ steps:
24
+ - uses: actions/checkout@v7
25
+ - uses: astral-sh/setup-uv@v7
26
+ with:
27
+ python-version: "3.13"
28
+ - run: uv sync --locked
29
+ - name: Generate sample tours
30
+ run: uv run python benchmarks/samples.py --dir "$RUNNER_TEMP/samples" --out sample-tours
31
+ - uses: actions/upload-artifact@v7
32
+ with:
33
+ name: sample-tours
34
+ path: sample-tours/*.md
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
@@ -0,0 +1,20 @@
1
+ # Hooks run the project's own pinned tools through uv, so local checks match CI.
2
+ repos:
3
+ - repo: local
4
+ hooks:
5
+ - id: ruff-check
6
+ name: ruff check
7
+ entry: uv run --frozen ruff check --fix
8
+ language: system
9
+ types_or: [python, markdown]
10
+ - id: ruff-format
11
+ name: ruff format
12
+ entry: uv run --frozen ruff format
13
+ language: system
14
+ types_or: [python, markdown]
15
+ - id: mypy
16
+ name: mypy
17
+ entry: uv run --frozen mypy
18
+ language: system
19
+ types: [python]
20
+ pass_filenames: false
@@ -0,0 +1,108 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [1.0.0] - 2026-11-15
10
+
11
+ First stable release (M4): HTML and JSON output, AI summaries, the GitHub Action, and PyPI packages. Every quality, performance, and robustness target in the plan is met.
12
+
13
+ ### Added
14
+
15
+ - Published on PyPI: `pip install repotour`. Releases are built once, smoke-tested on Linux, macOS, and Windows, and published with trusted publishing.
16
+ - README with sample tours, benchmarks and their method, Action setup, and how AI summaries handle your code. The Flask, Express, and fzf sample tours are regenerated with 1.0.0 (reading orders unchanged since review).
17
+ - GitHub Action: on each release (or on demand) it regenerates the tour and opens or updates one "Update codebase tour" pull request, or none if nothing changed. It caches the analysis between runs, refuses shallow clones with a clear message, and turns on AI summaries when given an `llm-api-key` secret. See `docs/action.md`.
18
+ - Summaries handle free-tier rate limits: a "too many requests" answer is waited out as the provider asks, up to 5 minutes per run, then the remaining files are skipped with one warning.
19
+ - `--summaries` adds a short AI-written paragraph about each key file (reading order first, then core modules, at most 15), in a section clearly labelled as AI-generated, in Markdown, HTML, and JSON. Providers are presets in `.repotour.yml`: Groq (default), OpenRouter, Ollama, or any OpenAI-compatible endpoint; each user brings their own key from the environment. Repotour prints what it will send and where before sending, caches summaries by file content and model, and turns every failure into a warning. Without the flag it never uses the network. See `docs/configuration.md`.
20
+ - `--format html` writes one self-contained `TOUR.html` that works offline: the dependency graph is drawn by an embedded, pinned Mermaid bundle (`@mermaid-js/tiny` 12.3.0, checked by hash), it adapts to light and dark mode, and text from the repository is always escaped. The page is built from the Markdown tour, so both formats say the same thing.
21
+ - `--json FILE` also writes the tour model as JSON with a `schema_version`, for tools rather than people. The format is experimental; see `docs/json.md`. It is byte-identical across platforms and, like the tour, never overwrites a file Repotour did not write.
22
+ - Incremental benchmark: after touring the previous release, re-touring the next one (Flask 3.1.2 to 3.1.3, fzf v0.74.0 to v0.75.0, Go 1.27.0 to 1.27.3) is timed against a 5-second target, and the result must be byte-identical to a cold tour.
23
+
24
+ ### Changed
25
+
26
+ - Re-running after a release is much faster on large repositories: golang/go went from 29 s to 4.8 s, and its first run from 59 s to 45 s. Cached history uses a compact format and is decoded without generic type resolution, renames are followed once per tour, and hot paths handle repository paths as plain strings. Existing caches are rebuilt once.
27
+ - The project is marked Production/Stable.
28
+
29
+ ## [0.3.0] - 2026-11-08
30
+
31
+ Third milestone (M3): dependency graph, entry points, and the reading order.
32
+
33
+ ### Added
34
+
35
+ - Sample tours of Flask, Express, and fzf at pinned release commits in `docs/samples/`, reviewed against a written reading-order rubric (`docs/rubric.md`): 4/5, 4/5, and 5/5.
36
+ - Robustness corpus: 20 real repositories (Python, JavaScript/TypeScript, Go, a monorepo, heavy vendoring and migrations, Django, a Rust repository, and a shallow clone) toured weekly in CI with zero crashes.
37
+ - Large-repository benchmark: golang/go tours in 59 s against a 2-minute target; Kubernetes is reported as a stretch target.
38
+ - Start here: up to five files to read first, in order. Pinned entry points come first, then execution entry points, then public surfaces; within each group files rank by PageRank and hotspot percentiles. After the first entry point, each next file is the best one imported by a file already chosen. Every reason comes from real signals (entry point, imported by, importer count, repeated changes), and the list is never padded.
39
+ - Go in the dependency graph and entry points: imports resolve through every `go.mod` module path (covering `go.work` workspaces), linking to one representative file of the imported package (ADR-0005); files of an imported package are never called leaf modules. Entry points: `package main` programs, `ListenAndServe` servers, and each module's root package.
40
+ - JavaScript and TypeScript in the dependency graph and entry points: static `import`/`export ... from`/`require('literal')` (and TypeScript `import x = require()`), resolved through relative paths with extension inference and index files, `tsconfig.json` `paths`/`baseUrl`, and workspace packages; entry points from `package.json` `main`/`module`/`exports`/`bin`, server startup (`.listen()`), and `require.main === module`.
41
+ - Core and leaf modules: Repotour's own deterministic PageRank names the most depended-on modules; modules nothing imports (that are not entry points or empty) are listed as safe places for a first change.
42
+ - Large dependency graphs collapse to directories, one level coarser at a time, to at most `thresholds.graph_max_nodes` (60) nodes; if even the top level is too big, the highest-ranked directories are kept and the rest counted. `--full-graph` (or `full_graph: true`) draws every module.
43
+ - Per-file parse cache: parse results are cached by content hash and language adapter version, so a re-run parses only changed files (a moved but unchanged file is never re-parsed). Within one run, each file is parsed once and shared by all analyzers.
44
+ - Entry points section: where execution starts (scripts, CLIs, servers, web apps, `python -m` modules, console scripts) and the public surface (top-level packages), plus commands from `package.json`, `Dockerfile`, and `Cargo.toml` that point at real files, in any language. Pinned entry points from config come first; each kind is capped at 10.
45
+ - Dependency graph section (Python): static imports parsed with tree-sitter and resolved to repository files (relative imports, top-level packages, `src/` layout), rendered as a Mermaid diagram that displays on GitHub. Every edge cites its import; external modules are counted; test, generated, and vendored files are left out; unparseable files become warnings.
46
+
47
+ ### Fixed
48
+
49
+ - Files with syntax the parser does not fully know (for example newer TypeScript) keep the imports and entry points it recovered, with one summary warning instead of being dropped from the graph one warning at a time.
50
+ - Binary and oversized files only produce a warning when they are code in a known language; translation files, archives, wheels, and data files are counted silently (Django went from 1,315 warnings to a handful).
51
+ - Example, documentation, and benchmark code no longer leads the reading order or entry points, and is never a core or leaf module.
52
+ - A `package.json` without `main` or `exports` is treated as Node does (`index.js`), so such packages are libraries with a public surface.
53
+ - `require('../..')` resolves to the directory's index file; unresolved relative imports are no longer counted as external packages.
54
+ - `python -m` names use the package's source root.
55
+ - GitHub Actions expressions are no longer quoted as commands; continued Makefile recipe lines end with an ellipsis; Go library evidence names each package once.
56
+
57
+ ## [0.2.0] - 2026-11-07
58
+
59
+ Second milestone (M2): history and ownership.
60
+
61
+ ### Added
62
+
63
+ - Hotspots section: the files changed most often in the hotspot window, measured back from HEAD's commit date (`--since`, default 6 months), each citing its commit range. Merge commits, generated and vendored files, and files no longer tracked are left out.
64
+ - Ownership section: for directories up to depth 2 with at least 5 source or test files, the top authors by share of changed lines (names only, never emails) and contacts who committed there in the 12 months before HEAD. Authors are merged only through `.mailmap`; bots (`[bot]` names and config `bots`) and bulk commits are left out; renamed files count toward their current directory.
65
+ - Bus-factor warnings in the Ownership section when one author made more than 80% of a directory's changes and it has at least 5 files and 10 commits (all configurable). Each directory is marked active (changed inside the hotspot window) or stable (no change in 12 months).
66
+ - Incremental history: the cache keeps the last toured commit's history, so after pulling only the new commits are read from git; rewritten history is read in full, and changing `--since` never re-reads git. History is kept in one canonical order, so incremental and cold runs give identical tours.
67
+ - Bulk commits (more files than `thresholds.bulk_commit_files`, default 200, or listed in `.git-blame-ignore-revs` or config `ignore_revs`) are left out of the history-based sections, and the tour says how many with examples.
68
+ - Renamed and moved files keep their history: renames are detected in the same `git log` pass and chained to the path at HEAD.
69
+ - `python -m repotour` entry point.
70
+ - Benchmarks on Flask and fzf at pinned commits, run weekly in CI (median of three cold runs: 0.69 s and 1.09 s against a 10 s target).
71
+
72
+ ## [0.1.0] - 2026-11-07
73
+
74
+ First milestone (M1): the CLI skeleton and the overview section.
75
+
76
+ ### Fixed
77
+
78
+ - A top-level directory that holds only submodules is summarized as such instead of "0 files".
79
+ - Generated-file headers only count inside a comment at the start of a line, so prose that mentions the marker is no longer misclassified.
80
+
81
+ ### Added
82
+
83
+ - `repotour` command that writes `TOUR.md` with the tour's metadata (repository name, HEAD commit, tool version), as deterministic UTF-8 with LF line endings on every platform.
84
+ - `--out` option to write the tour to another path.
85
+ - Exit code 1 with a clear message when the path is not inside a git repository, git is missing or older than 2.30, or the repository has no commits.
86
+ - Any directory inside a repository tours the whole repository.
87
+ - A shallow clone produces the tour with a "history truncated" warning.
88
+ - The CLI notes when uncommitted changes are ignored; the tour always describes HEAD.
89
+ - `.repotour.yml` configuration: `version: 1` required, unknown keys and wrong types exit 2 naming the key, optional `name:` override, and the config hash shown in the tour. See `docs/configuration.md`.
90
+ - `--since` option for the hotspot window, overriding the config file.
91
+ - Generated tours start with a `<!-- generated by repotour -->` marker; an existing file without it is never overwritten (exit 4) unless `--force` is given.
92
+ - Warnings render in a closing Warnings section and are counted in the CLI summary; `--strict` exits 3 when any warning was produced.
93
+ - Overview section: repository size and a language breakdown, each language citing its largest files. Content is read from the commit, never the working tree.
94
+ - Analyzer pipeline: analyzers run in dependency order; a failing analyzer becomes a warning and an unavailable section instead of stopping the run.
95
+ - File classification: generated and vendored files (from `.gitattributes`, well-known paths, lockfiles, generated-file headers, and config `exclude`) are left out of languages and size and listed with their reasons. Tests are recognized by directory, file name, and config `test_patterns`.
96
+ - Binary files and files over 1 MB are skipped; unexpected ones are reported as warnings. Git LFS pointers count as other files; submodules are listed but never entered.
97
+ - Analysis cache in the git common directory (shared by worktrees, never tracked, safe to delete): re-running on the same commit and config renders without re-scanning. `--no-cache` neither reads nor writes it.
98
+ - Project types (web app, library, CLI, monorepo) detected from manifests and build files, each with evidence, or "unknown". Monorepo packages from npm/pnpm workspaces, `go.work`, Cargo workspaces, or manifests in several top-level directories.
99
+ - "How to work on it": install, run, and test commands quoted from Makefile/justfile targets, package.json scripts, pyproject task runners, tox, nox, and GitHub Actions steps, each citing its source; the highest-priority source wins per purpose, and the tour says so when none are found.
100
+ - Top-level directories with a one-line purpose, taken from a config note, the directory's README, a table of well-known names, or a summary of its files, and showing where each came from. Notes on other paths render as maintainer notes; a note for a missing path is a warning.
101
+ - Missing basics: the overview flags a missing README, tests, contributing guide, license, or CI configuration, saying what was looked for.
102
+ - Contributor documentation, code of conduct, security policy, and issue and pull request templates.
103
+
104
+ [Unreleased]: https://github.com/babarshahzad2005/repotour/compare/v1.0.0...HEAD
105
+ [1.0.0]: https://github.com/babarshahzad2005/repotour/compare/v0.3.0...v1.0.0
106
+ [0.3.0]: https://github.com/babarshahzad2005/repotour/compare/v0.2.0...v0.3.0
107
+ [0.2.0]: https://github.com/babarshahzad2005/repotour/compare/v0.1.0...v0.2.0
108
+ [0.1.0]: https://github.com/babarshahzad2005/repotour/releases/tag/v0.1.0
@@ -0,0 +1,9 @@
1
+ # Code of Conduct
2
+
3
+ This project adopts the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) as its code of conduct.
4
+
5
+ In short: be respectful, assume good intent, welcome newcomers, and keep discussion focused on the work. Harassment, personal attacks, and discriminatory language are not tolerated in any project space, including issues, pull requests, and discussions.
6
+
7
+ ## Reporting
8
+
9
+ Report unacceptable behavior privately to the maintainer at babarshahzad2005@gmail.com. Reports are handled confidentially, and the maintainer will respond as described in the Contributor Covenant's enforcement guidelines.
@@ -0,0 +1,56 @@
1
+ # Contributing to Repotour
2
+
3
+ Thanks for your interest in improving Repotour. This guide covers setup, the development loop, and how changes get merged.
4
+
5
+ ## Before you start
6
+
7
+ - Read the [vision](docs/design/vision.md) and [architecture](docs/design/architecture.md). The architecture's "Rules every change must follow" section is binding.
8
+ - Use the project's vocabulary from the [glossary](GLOSSARY.md) in code, tests, and docs.
9
+ - Check the [architecture decision records](docs/adr/) before changing anything they cover. To reverse a decision, propose a new ADR.
10
+ - For anything bigger than a small fix, open an issue first so we can agree on the approach.
11
+
12
+ ## Setup
13
+
14
+ Requirements: Python 3.11+, git 2.30+, and [uv](https://docs.astral.sh/uv/).
15
+
16
+ ```sh
17
+ git clone https://github.com/babarshahzad2005/repotour.git
18
+ cd repotour
19
+ uv sync
20
+ uv run pre-commit install
21
+ ```
22
+
23
+ ## Development loop
24
+
25
+ ```sh
26
+ uv run pytest # tests
27
+ uv run ruff check # lint
28
+ uv run ruff format # format
29
+ uv run mypy # type-check (strict)
30
+ uv run repotour # run the CLI on the current repository
31
+ ```
32
+
33
+ The pre-commit hooks run ruff and mypy on every commit.
34
+
35
+ ### Tests
36
+
37
+ - Write the test first. Behavior is tested through fixture repositories with scripted history (see `tests/fixtures/`), never against real-world repositories.
38
+ - New behavior gets a fixture-repository test and, if the tour output changes, an updated golden tour.
39
+ - To update golden tours after an intentional output change, run `uv run pytest --update-goldens` and review the diff in `tests/golden/` carefully. It is part of your change.
40
+ - Output must stay deterministic: sort everything, break ties by path, and never add timestamps.
41
+
42
+ ## Commits and pull requests
43
+
44
+ - Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `chore:`, `ci:`.
45
+ - One topic per pull request, on a short-lived branch from `main`.
46
+ - `main` is protected. Pull requests merge by squash once CI passes on Linux, macOS, and Windows.
47
+ - Add a line to the `Unreleased` section of [CHANGELOG.md](CHANGELOG.md) for user-visible changes.
48
+
49
+ ## Reporting bugs and security issues
50
+
51
+ - Bugs and feature requests: use the [issue templates](https://github.com/babarshahzad2005/repotour/issues/new/choose).
52
+ - Security vulnerabilities: do not open a public issue. See [SECURITY.md](SECURITY.md).
53
+
54
+ ## Code of conduct
55
+
56
+ Participation in this project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md).