foothold 0.1.2__tar.gz → 0.1.4__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 (53) hide show
  1. {foothold-0.1.2 → foothold-0.1.4}/.github/workflows/ci.yml +2 -2
  2. foothold-0.1.4/.github/workflows/reading-path.yml +23 -0
  3. {foothold-0.1.2 → foothold-0.1.4}/.github/workflows/release.yml +4 -4
  4. {foothold-0.1.2 → foothold-0.1.4}/CHANGELOG.md +28 -0
  5. {foothold-0.1.2 → foothold-0.1.4}/PKG-INFO +37 -5
  6. {foothold-0.1.2 → foothold-0.1.4}/README.md +36 -4
  7. foothold-0.1.4/action.yml +98 -0
  8. {foothold-0.1.2 → foothold-0.1.4}/pyproject.toml +1 -1
  9. {foothold-0.1.2 → foothold-0.1.4}/.foothold.toml +0 -0
  10. {foothold-0.1.2 → foothold-0.1.4}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  11. {foothold-0.1.2 → foothold-0.1.4}/.github/ISSUE_TEMPLATE/ranking.yml +0 -0
  12. {foothold-0.1.2 → foothold-0.1.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  13. {foothold-0.1.2 → foothold-0.1.4}/.gitignore +0 -0
  14. {foothold-0.1.2 → foothold-0.1.4}/.pre-commit-config.yaml +0 -0
  15. {foothold-0.1.2 → foothold-0.1.4}/ARCHITECTURE.md +0 -0
  16. {foothold-0.1.2 → foothold-0.1.4}/CODE_OF_CONDUCT.md +0 -0
  17. {foothold-0.1.2 → foothold-0.1.4}/CONTRIBUTING.md +0 -0
  18. {foothold-0.1.2 → foothold-0.1.4}/LICENSE +0 -0
  19. {foothold-0.1.2 → foothold-0.1.4}/SECURITY.md +0 -0
  20. {foothold-0.1.2 → foothold-0.1.4}/docs/cost-model.md +0 -0
  21. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/__init__.py +0 -0
  22. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/analyze.py +0 -0
  23. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/cli.py +0 -0
  24. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/collectors/__init__.py +0 -0
  25. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/collectors/git_history.py +0 -0
  26. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/collectors/markers.py +0 -0
  27. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/collectors/python_ast.py +0 -0
  28. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/config.py +0 -0
  29. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/graph/__init__.py +0 -0
  30. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/graph/build.py +0 -0
  31. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/graph/rank.py +0 -0
  32. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/issues.py +0 -0
  33. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/models.py +0 -0
  34. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/narrator/__init__.py +0 -0
  35. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/narrator/client.py +0 -0
  36. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/render/__init__.py +0 -0
  37. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/render/markdown.py +0 -0
  38. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/render/mermaid.py +0 -0
  39. {foothold-0.1.2 → foothold-0.1.4}/src/foothold/render/terminal.py +0 -0
  40. {foothold-0.1.2 → foothold-0.1.4}/tests/conftest.py +0 -0
  41. {foothold-0.1.2 → foothold-0.1.4}/tests/fixtures/minirepo/main.py +0 -0
  42. {foothold-0.1.2 → foothold-0.1.4}/tests/fixtures/minirepo/pkg/__init__.py +0 -0
  43. {foothold-0.1.2 → foothold-0.1.4}/tests/fixtures/minirepo/pkg/api.py +0 -0
  44. {foothold-0.1.2 → foothold-0.1.4}/tests/fixtures/minirepo/pkg/core.py +0 -0
  45. {foothold-0.1.2 → foothold-0.1.4}/tests/fixtures/minirepo/pkg/util/__init__.py +0 -0
  46. {foothold-0.1.2 → foothold-0.1.4}/tests/fixtures/minirepo/pkg/util/helpers.py +0 -0
  47. {foothold-0.1.2 → foothold-0.1.4}/tests/fixtures/minirepo/tests/test_core.py +0 -0
  48. {foothold-0.1.2 → foothold-0.1.4}/tests/unit/test_cli.py +0 -0
  49. {foothold-0.1.2 → foothold-0.1.4}/tests/unit/test_git_history.py +0 -0
  50. {foothold-0.1.2 → foothold-0.1.4}/tests/unit/test_graph.py +0 -0
  51. {foothold-0.1.2 → foothold-0.1.4}/tests/unit/test_rank.py +0 -0
  52. {foothold-0.1.2 → foothold-0.1.4}/tests/unit/test_render_and_issues.py +0 -0
  53. {foothold-0.1.2 → foothold-0.1.4}/uv.lock +0 -0
@@ -17,9 +17,9 @@ jobs:
17
17
  os: [ubuntu-latest, macos-latest, windows-latest]
18
18
  python: ["3.10", "3.11", "3.12", "3.13"]
19
19
  steps:
20
- - uses: actions/checkout@v4
20
+ - uses: actions/checkout@v7
21
21
  with: { fetch-depth: 0 } # churn signal needs real history
22
- - uses: astral-sh/setup-uv@v3
22
+ - uses: astral-sh/setup-uv@v9.0.0
23
23
  with: { enable-cache: true }
24
24
  - run: uv python install ${{ matrix.python }}
25
25
  - run: uv sync --all-extras
@@ -0,0 +1,23 @@
1
+ name: Reading path
2
+
3
+ # Dogfooding the action this repository publishes. The job summary of every pull
4
+ # request shows the files that hold the project together, so a reviewer sees what
5
+ # a newcomer would see.
6
+
7
+ on:
8
+ pull_request:
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ map:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v7
19
+ with:
20
+ fetch-depth: 0 # the churn signal needs real history
21
+ - uses: ./ # the action defined in this repository
22
+ with:
23
+ top: "15"
@@ -11,11 +11,11 @@ jobs:
11
11
  build:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
- - uses: actions/checkout@v4
15
- - uses: astral-sh/setup-uv@v3
14
+ - uses: actions/checkout@v7
15
+ - uses: astral-sh/setup-uv@v9.0.0
16
16
  with: { enable-cache: true }
17
17
  - run: uv build
18
- - uses: actions/upload-artifact@v4
18
+ - uses: actions/upload-artifact@v7
19
19
  with:
20
20
  name: dist
21
21
  path: dist/
@@ -34,6 +34,6 @@ jobs:
34
34
  permissions:
35
35
  id-token: write # trusted publishing - no API token in secrets
36
36
  steps:
37
- - uses: actions/download-artifact@v4
37
+ - uses: actions/download-artifact@v8
38
38
  with: { name: dist, path: dist }
39
39
  - uses: pypa/gh-action-pypi-publish@release/v1
@@ -6,6 +6,32 @@ All notable changes are documented here. Format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.4]
10
+
11
+ ### Changed
12
+
13
+ - The action's Marketplace name is now "Foothold reading path". GitHub requires that
14
+ name to be unique across every action, user and organisation, and the organisation
15
+ `github.com/FootHold` already holds "Foothold". Nothing else changes: the package is
16
+ still `foothold` on PyPI and the action is still used as `serdairy/foothold@vX.Y.Z`.
17
+
18
+ ## [0.1.3]
19
+
20
+ ### Added
21
+
22
+ - A GitHub Action. `uses: serdairy/foothold@v0.1.3` runs the tool against the calling
23
+ repository and writes the ranked reading path to the job summary. Composite action, so
24
+ there is no container to pull; it needs no token and no write permission.
25
+
26
+ ### Changed
27
+
28
+ - Every GitHub Action dependency moved to its current major: checkout v4 to v7,
29
+ setup-python v5 to v7, setup-uv v3 to v9.0.0, upload-artifact v4 to v7,
30
+ download-artifact v4 to v8. The older majors run on Node 20, which GitHub has
31
+ deprecated, so every run printed a warning - including runs in repositories that
32
+ merely use this action. `setup-uv` stopped publishing moving major tags after v7, so
33
+ it is pinned to an exact version.
34
+
9
35
  ## [0.1.2]
10
36
 
11
37
  ### Fixed
@@ -30,6 +56,8 @@ All notable changes are documented here. Format follows
30
56
 
31
57
  ## [0.1.0]
32
58
 
59
+ Tagged before PyPI trusted publishing was configured, so this version exists as a git tag only. The first version on PyPI is 0.1.1.
60
+
33
61
  ### Added
34
62
 
35
63
  - `foothold map` — offline ranking of the files that hold a Python repository together,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foothold
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Turn an unfamiliar repository into a reading path
5
5
  Project-URL: Homepage, https://github.com/serdairy/foothold
6
6
  Project-URL: Issues, https://github.com/serdairy/foothold/issues
@@ -33,8 +33,8 @@ Description-Content-Type: text/markdown
33
33
  # Foothold
34
34
 
35
35
  [![CI](https://github.com/serdairy/foothold/actions/workflows/ci.yml/badge.svg)](https://github.com/serdairy/foothold/actions/workflows/ci.yml)
36
- [![PyPI](https://img.shields.io/pypi/v/foothold.svg)](https://pypi.org/project/foothold/)
37
- [![Python](https://img.shields.io/pypi/pyversions/foothold.svg)](https://pypi.org/project/foothold/)
36
+ [![PyPI](https://img.shields.io/pypi/v/foothold?logo=pypi&logoColor=white)](https://pypi.org/project/foothold/)
37
+ [![Python](https://img.shields.io/pypi/pyversions/foothold?logo=python&logoColor=white)](https://pypi.org/project/foothold/)
38
38
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
39
39
  [![Checked with mypy](https://img.shields.io/badge/mypy-strict-blue.svg)](https://mypy-lang.org/)
40
40
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
@@ -100,6 +100,38 @@ Python specifically to avoid pulling ~100 MB of scipy and numpy into a CLI.
100
100
  The two commands that cost money print an estimate and require confirmation; `--yes` is
101
101
  mandatory for non-interactive use.
102
102
 
103
+ ## GitHub Action
104
+
105
+ Put the reading path in the job summary of every pull request. No token, no write
106
+ permission, nothing to configure:
107
+
108
+ ```yaml
109
+ - uses: actions/checkout@v7
110
+ with:
111
+ fetch-depth: 0 # the churn signal needs real history
112
+ - uses: serdairy/foothold@v0.1.3
113
+ with:
114
+ top: "20"
115
+ ```
116
+
117
+ | Input | Default | What it does |
118
+ |---|---|---|
119
+ | `path` | `.` | Repository root to analyse |
120
+ | `command` | `map` | `map`, `docs` or `issues` |
121
+ | `top` | `20` | How many files to report |
122
+ | `output` | `ARCHITECTURE.md` | File written when `command: docs` |
123
+ | `version` | latest | Pin a foothold version, e.g. `0.1.3` |
124
+ | `summary` | `true` | Write the result to the job summary |
125
+ | `python-version` | `3.12` | Python that runs foothold, independent of the analysed project |
126
+
127
+ The action exposes the output as `steps.<id>.outputs.result`, so you can post it
128
+ wherever you like. It runs `pip install foothold` and nothing else — no container to
129
+ pull, no code from the analysed repository is executed.
130
+
131
+ `fetch-depth: 0` matters: a shallow clone has no history, so the churn term collapses to
132
+ zero and the ranking degrades to pure graph structure. It still works, it is just less
133
+ informative.
134
+
103
135
  ## How the ranking works
104
136
 
105
137
  ```
@@ -183,10 +215,10 @@ Stated plainly, because the alternative wastes your time:
183
215
 
184
216
  | Version | Scope | Status |
185
217
  |---|---|---|
186
- | **v0.1** | `map`, `docs`, `issues`, `explain`; Python; 85% coverage | **shipped** |
218
+ | **v0.1** | `map`, `docs`, `issues`, `explain`; GitHub Action; Python; 86% coverage | **shipped** |
187
219
  | v0.2 | Content-hash cache; incremental re-analysis on diff; `--since` | next |
188
220
  | v0.3 | tree-sitter parsers: TypeScript, JavaScript, Go | planned |
189
- | v0.4 | `tour` with personas; GitHub Action for CI-regenerated docs | planned |
221
+ | v0.4 | `tour` with personas; PR-scoped reading paths | planned |
190
222
  | v0.5 | Monorepo support; call-graph edges, not just imports | planned |
191
223
  | v1.0 | Stable JSON schema; benchmark suite against hand-written docs | planned |
192
224
 
@@ -1,8 +1,8 @@
1
1
  # Foothold
2
2
 
3
3
  [![CI](https://github.com/serdairy/foothold/actions/workflows/ci.yml/badge.svg)](https://github.com/serdairy/foothold/actions/workflows/ci.yml)
4
- [![PyPI](https://img.shields.io/pypi/v/foothold.svg)](https://pypi.org/project/foothold/)
5
- [![Python](https://img.shields.io/pypi/pyversions/foothold.svg)](https://pypi.org/project/foothold/)
4
+ [![PyPI](https://img.shields.io/pypi/v/foothold?logo=pypi&logoColor=white)](https://pypi.org/project/foothold/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/foothold?logo=python&logoColor=white)](https://pypi.org/project/foothold/)
6
6
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
7
7
  [![Checked with mypy](https://img.shields.io/badge/mypy-strict-blue.svg)](https://mypy-lang.org/)
8
8
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
@@ -68,6 +68,38 @@ Python specifically to avoid pulling ~100 MB of scipy and numpy into a CLI.
68
68
  The two commands that cost money print an estimate and require confirmation; `--yes` is
69
69
  mandatory for non-interactive use.
70
70
 
71
+ ## GitHub Action
72
+
73
+ Put the reading path in the job summary of every pull request. No token, no write
74
+ permission, nothing to configure:
75
+
76
+ ```yaml
77
+ - uses: actions/checkout@v7
78
+ with:
79
+ fetch-depth: 0 # the churn signal needs real history
80
+ - uses: serdairy/foothold@v0.1.3
81
+ with:
82
+ top: "20"
83
+ ```
84
+
85
+ | Input | Default | What it does |
86
+ |---|---|---|
87
+ | `path` | `.` | Repository root to analyse |
88
+ | `command` | `map` | `map`, `docs` or `issues` |
89
+ | `top` | `20` | How many files to report |
90
+ | `output` | `ARCHITECTURE.md` | File written when `command: docs` |
91
+ | `version` | latest | Pin a foothold version, e.g. `0.1.3` |
92
+ | `summary` | `true` | Write the result to the job summary |
93
+ | `python-version` | `3.12` | Python that runs foothold, independent of the analysed project |
94
+
95
+ The action exposes the output as `steps.<id>.outputs.result`, so you can post it
96
+ wherever you like. It runs `pip install foothold` and nothing else — no container to
97
+ pull, no code from the analysed repository is executed.
98
+
99
+ `fetch-depth: 0` matters: a shallow clone has no history, so the churn term collapses to
100
+ zero and the ranking degrades to pure graph structure. It still works, it is just less
101
+ informative.
102
+
71
103
  ## How the ranking works
72
104
 
73
105
  ```
@@ -151,10 +183,10 @@ Stated plainly, because the alternative wastes your time:
151
183
 
152
184
  | Version | Scope | Status |
153
185
  |---|---|---|
154
- | **v0.1** | `map`, `docs`, `issues`, `explain`; Python; 85% coverage | **shipped** |
186
+ | **v0.1** | `map`, `docs`, `issues`, `explain`; GitHub Action; Python; 86% coverage | **shipped** |
155
187
  | v0.2 | Content-hash cache; incremental re-analysis on diff; `--since` | next |
156
188
  | v0.3 | tree-sitter parsers: TypeScript, JavaScript, Go | planned |
157
- | v0.4 | `tour` with personas; GitHub Action for CI-regenerated docs | planned |
189
+ | v0.4 | `tour` with personas; PR-scoped reading paths | planned |
158
190
  | v0.5 | Monorepo support; call-graph edges, not just imports | planned |
159
191
  | v1.0 | Stable JSON schema; benchmark suite against hand-written docs | planned |
160
192
 
@@ -0,0 +1,98 @@
1
+ name: Foothold reading path
2
+ description: Rank the files that hold a Python repository together, and put the reading path in the job summary.
3
+ author: Sergei Petrov
4
+
5
+ branding:
6
+ icon: map
7
+ color: green
8
+
9
+ inputs:
10
+ path:
11
+ description: Repository root to analyse.
12
+ required: false
13
+ default: "."
14
+ command:
15
+ description: "One of: map, docs, issues."
16
+ required: false
17
+ default: map
18
+ top:
19
+ description: How many files to report.
20
+ required: false
21
+ default: "20"
22
+ output:
23
+ description: File to write when command is `docs`.
24
+ required: false
25
+ default: ARCHITECTURE.md
26
+ version:
27
+ description: 'Version of foothold to install, e.g. "0.1.2". Empty installs the latest.'
28
+ required: false
29
+ default: ""
30
+ summary:
31
+ description: Write the result to the GitHub job summary.
32
+ required: false
33
+ default: "true"
34
+ python-version:
35
+ description: Python used to run foothold. Independent of the analysed project.
36
+ required: false
37
+ default: "3.12"
38
+
39
+ outputs:
40
+ result:
41
+ description: The command output as plain text.
42
+ value: ${{ steps.run.outputs.result }}
43
+
44
+ runs:
45
+ using: composite
46
+ steps:
47
+ - name: Set up Python for foothold
48
+ uses: actions/setup-python@v7
49
+ with:
50
+ python-version: ${{ inputs.python-version }}
51
+
52
+ - name: Install foothold
53
+ shell: bash
54
+ run: |
55
+ if [ -n "${{ inputs.version }}" ]; then
56
+ python -m pip install --quiet "foothold==${{ inputs.version }}"
57
+ else
58
+ python -m pip install --quiet foothold
59
+ fi
60
+ foothold --version
61
+
62
+ - name: Run foothold
63
+ id: run
64
+ shell: bash
65
+ env:
66
+ # rich wraps to the terminal width; fix it so the summary is stable.
67
+ COLUMNS: "110"
68
+ run: |
69
+ set -euo pipefail
70
+ case "${{ inputs.command }}" in
71
+ map) foothold map "${{ inputs.path }}" --top "${{ inputs.top }}" | tee /tmp/foothold-out.txt ;;
72
+ issues) foothold issues "${{ inputs.path }}" --max "${{ inputs.top }}" --markdown | tee /tmp/foothold-out.txt ;;
73
+ docs) foothold docs "${{ inputs.path }}" --out "${{ inputs.output }}" --top "${{ inputs.top }}" | tee /tmp/foothold-out.txt ;;
74
+ *) echo "::error::Unknown command '${{ inputs.command }}'. Use map, docs or issues."; exit 1 ;;
75
+ esac
76
+ {
77
+ echo "result<<FOOTHOLD_EOF"
78
+ cat /tmp/foothold-out.txt
79
+ echo "FOOTHOLD_EOF"
80
+ } >> "$GITHUB_OUTPUT"
81
+
82
+ - name: Write the job summary
83
+ if: ${{ inputs.summary == 'true' }}
84
+ shell: bash
85
+ run: |
86
+ {
87
+ echo "## Foothold"
88
+ echo
89
+ if [ "${{ inputs.command }}" = "issues" ]; then
90
+ cat /tmp/foothold-out.txt
91
+ else
92
+ echo '```'
93
+ cat /tmp/foothold-out.txt
94
+ echo '```'
95
+ fi
96
+ echo
97
+ echo "_Ranked by import graph and git churn. Checkout needs \`fetch-depth: 0\` for the churn signal._"
98
+ } >> "$GITHUB_STEP_SUMMARY"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "foothold"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "Turn an unfamiliar repository into a reading path"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes