onepaste 1.3.1__tar.gz → 1.3.2__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 (28) hide show
  1. {onepaste-1.3.1 → onepaste-1.3.2}/.github/workflows/publish.yml +7 -0
  2. {onepaste-1.3.1 → onepaste-1.3.2}/.gitignore +22 -20
  3. {onepaste-1.3.1 → onepaste-1.3.2}/PKG-INFO +2 -1
  4. {onepaste-1.3.1 → onepaste-1.3.2}/PUBLISHING.md +1 -0
  5. {onepaste-1.3.1 → onepaste-1.3.2}/README.md +1 -0
  6. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/__init__.py +1 -1
  7. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/collector.py +19 -7
  8. {onepaste-1.3.1 → onepaste-1.3.2}/pyproject.toml +1 -1
  9. {onepaste-1.3.1 → onepaste-1.3.2}/tests/test_collector.py +22 -0
  10. onepaste-1.3.2/website/package-lock.json +1062 -0
  11. {onepaste-1.3.1 → onepaste-1.3.2}/LICENSE +0 -0
  12. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/__main__.py +0 -0
  13. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/cli.py +0 -0
  14. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/config.py +0 -0
  15. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/formatter.py +0 -0
  16. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/gitignore.py +0 -0
  17. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/output.py +0 -0
  18. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/selector.py +0 -0
  19. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/splitter.py +0 -0
  20. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/tokens.py +0 -0
  21. {onepaste-1.3.1 → onepaste-1.3.2}/onepaste/uninstall.py +0 -0
  22. {onepaste-1.3.1 → onepaste-1.3.2}/tests/conftest.py +0 -0
  23. {onepaste-1.3.1 → onepaste-1.3.2}/tests/test_cli.py +0 -0
  24. {onepaste-1.3.1 → onepaste-1.3.2}/tests/test_config.py +0 -0
  25. {onepaste-1.3.1 → onepaste-1.3.2}/tests/test_formatter.py +0 -0
  26. {onepaste-1.3.1 → onepaste-1.3.2}/tests/test_output.py +0 -0
  27. {onepaste-1.3.1 → onepaste-1.3.2}/tests/test_splitter.py +0 -0
  28. {onepaste-1.3.1 → onepaste-1.3.2}/tests/test_tokens.py +0 -0
@@ -3,6 +3,8 @@ name: Publish to PyPI
3
3
  on:
4
4
  release:
5
5
  types: [published]
6
+ push:
7
+ tags: ["v*"]
6
8
  workflow_dispatch:
7
9
 
8
10
  jobs:
@@ -35,3 +37,8 @@ jobs:
35
37
 
36
38
  - name: Publish to PyPI
37
39
  uses: pypa/gh-action-pypi-publish@release/v1
40
+ with:
41
+ # Primary path: OIDC trusted publishing (no secret needed).
42
+ # Optional fallback: add a repo secret named PYPI_API_TOKEN and it
43
+ # takes precedence automatically; leave it unset for pure OIDC.
44
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -13,13 +13,13 @@ __pycache__/
13
13
 
14
14
  # Distribution / packaging
15
15
  .Python
16
- build/
16
+ /build/
17
17
  develop-eggs/
18
- dist/
18
+ /dist/
19
19
  downloads/
20
20
  eggs/
21
21
  .eggs/
22
- lib/
22
+ /lib/
23
23
  lib64/
24
24
  parts/
25
25
  sdist/
@@ -52,7 +52,7 @@ coverage.xml
52
52
  *.py,cover
53
53
  .hypothesis/
54
54
  .pytest_cache/
55
- cover/
55
+ /cover/
56
56
 
57
57
  # Translations
58
58
  *.mo
@@ -76,7 +76,7 @@ docs/_build/
76
76
 
77
77
  # PyBuilder
78
78
  .pybuilder/
79
- target/
79
+ /target/
80
80
 
81
81
  # Jupyter Notebook
82
82
  .ipynb_checkpoints
@@ -111,9 +111,9 @@ celerybeat.pid
111
111
  # Environments
112
112
  .env
113
113
  .venv
114
- env/
115
- venv/
116
- ENV/
114
+ /env/
115
+ /venv/
116
+ /ENV/
117
117
  env.bak/
118
118
  venv.bak/
119
119
 
@@ -225,12 +225,12 @@ code_collection*.md
225
225
  code_collection*.manifest.json
226
226
  *.collection.txt
227
227
  *.collection.md
228
- output/
229
- collected_code/
228
+ /output/
229
+ /collected_code/
230
230
 
231
231
  # 临时文件
232
- tmp/
233
- temp/
232
+ /tmp/
233
+ /temp/
234
234
  *.tmp
235
235
  *.temp
236
236
 
@@ -251,7 +251,7 @@ config.local.json
251
251
 
252
252
  # 日志文件
253
253
  *.log
254
- logs/
254
+ /logs/
255
255
 
256
256
  # --- Environment ---
257
257
  # Conda
@@ -262,10 +262,10 @@ Anaconda*/
262
262
 
263
263
  # NPM (如果项目中有前端部分)
264
264
  node_modules/
265
- package-lock.json
266
- yarn.lock
265
+ /package-lock.json/
266
+ /yarn.lock/
267
267
  .npm
268
- .npmrc
268
+ /.npmrc/
269
269
 
270
270
  # --- Docker ---
271
271
  # (如果将来添加 Docker 支持)
@@ -274,7 +274,7 @@ docker-compose.override.yml
274
274
 
275
275
  # --- Documentation ---
276
276
  # 生成的文档
277
- site/
277
+ /site/
278
278
  docs/_build/
279
279
 
280
280
  # --- Security ---
@@ -302,8 +302,8 @@ collected_*.txt
302
302
  !collected_example.txt
303
303
 
304
304
  # 测试输出
305
- test_output/
306
- test_results/
305
+ /test_output/
306
+ /test_results/
307
307
 
308
308
  # 性能分析
309
309
  *.prof
@@ -312,4 +312,6 @@ test_results/
312
312
  # 数据库
313
313
  *.db
314
314
  *.sqlite
315
- *.sqlite3
315
+ *.sqlite3
316
+ # Website lockfile MUST be tracked (reproducible Vercel builds)
317
+ !website/package-lock.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: onepaste
3
- Version: 1.3.1
3
+ Version: 1.3.2
4
4
  Summary: Collect project code files into a single output for AI assistants
5
5
  Project-URL: Homepage, https://github.com/emVisible/codecollector
6
6
  Project-URL: Repository, https://github.com/emVisible/codecollector.git
@@ -40,6 +40,7 @@ Description-Content-Type: text/markdown
40
40
  [![PyPI](https://img.shields.io/pypi/v/onepaste)](https://pypi.org/project/onepaste/)
41
41
  [![Python](https://img.shields.io/pypi/pyversions/onepaste)](https://pypi.org/project/onepaste/)
42
42
  [![License](https://img.shields.io/pypi/l/onepaste)](LICENSE)
43
+ [![Publish](https://github.com/emVisible/onepaste/actions/workflows/publish.yml/badge.svg)](https://github.com/emVisible/onepaste/actions/workflows/publish.yml)
43
44
 
44
45
  Collect project code into LLM-ready Markdown. Select a directory, press Enter, copy and paste.
45
46
 
@@ -31,6 +31,7 @@ Uses PyPI Trusted Publishing (OIDC) — no secrets stored in GitHub.
31
31
  - Workflow filename: `publish.yml`
32
32
  - Environment name: `pypi`
33
33
  2. Release: create a GitHub *Release* (or run the workflow manually via `workflow_dispatch`).
34
+ Alternatively: `git tag vX.Y.Z && git push origin vX.Y.Z` triggers the same workflow.
34
35
 
35
36
  The workflow runs tests, builds, twine-checks, then publishes.
36
37
 
@@ -3,6 +3,7 @@
3
3
  [![PyPI](https://img.shields.io/pypi/v/onepaste)](https://pypi.org/project/onepaste/)
4
4
  [![Python](https://img.shields.io/pypi/pyversions/onepaste)](https://pypi.org/project/onepaste/)
5
5
  [![License](https://img.shields.io/pypi/l/onepaste)](LICENSE)
6
+ [![Publish](https://github.com/emVisible/onepaste/actions/workflows/publish.yml/badge.svg)](https://github.com/emVisible/onepaste/actions/workflows/publish.yml)
6
7
 
7
8
  Collect project code into LLM-ready Markdown. Select a directory, press Enter, copy and paste.
8
9
 
@@ -1,6 +1,6 @@
1
1
  """OnePaste - Collect project code files for AI assistants."""
2
2
 
3
- __version__ = "1.3.1"
3
+ __version__ = "1.3.2"
4
4
  __author__ = "thawn"
5
5
  __license__ = "MIT"
6
6
 
@@ -150,16 +150,28 @@ class FileCollector:
150
150
 
151
151
  def _path_in_excluded_dir(self, file_path: Path) -> bool:
152
152
  try:
153
- parts = file_path.resolve().relative_to(self.config.root_path.resolve()).parts
153
+ rel = file_path.relative_to(self.config.root_path)
154
154
  except ValueError:
155
- parts = file_path.parts
156
- return any(part in self.config.all_exclude_dirs for part in parts[:-1])
155
+ try:
156
+ rel = file_path.resolve().relative_to(self.config.root_path.resolve())
157
+ except ValueError:
158
+ return False
159
+ return any(part in self.config.all_exclude_dirs for part in rel.parts[:-1])
157
160
 
158
161
  def _should_exclude_dir(self, dir_path: Path) -> bool:
159
- """Check if a directory should be excluded."""
160
- for part in dir_path.parts:
161
- if part in self.config.all_exclude_dirs:
162
- return True
162
+ """Check if a directory should be excluded.
163
+
164
+ Exclusion names match path segments *relative to the collection root*
165
+ only — never absolute ancestors (e.g. a /tmp-based temp dir must not
166
+ trip the "tmp" rule).
167
+ """
168
+ try:
169
+ segments = dir_path.relative_to(self.config.root_path).parts
170
+ except ValueError:
171
+ segments = (dir_path.name,)
172
+
173
+ if any(part in self.config.all_exclude_dirs for part in segments):
174
+ return True
163
175
 
164
176
  if self._git_files is None and self._gitignore:
165
177
  if self._gitignore.is_ignored(dir_path, is_dir=True):
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "onepaste"
7
- version = "1.3.1"
7
+ version = "1.3.2"
8
8
  description = "Collect project code files into a single output for AI assistants"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -92,6 +92,28 @@ class TestGitignoreFiltering:
92
92
  assert "secret_key.txt" in names
93
93
 
94
94
 
95
+ class TestExcludesAreRootScoped:
96
+ def test_suspicious_ancestor_names_do_not_prune(self, tmp_path: Path):
97
+ """Regression: exclusion names must match segments relative to the
98
+ collection root, never absolute ancestors (CI runs under /tmp)."""
99
+ root = tmp_path / "tmp" / "build" / "env" / "proj"
100
+ (root / "src").mkdir(parents=True)
101
+ (root / "a.py").write_text("x = 1\n")
102
+ (root / "src" / "b.py").write_text("y = 2\n")
103
+ (root / "build").mkdir() # a REAL excluded dir inside the project
104
+ (root / "build" / "junk.py").write_text("junk\n")
105
+
106
+ names = _rel_names(_collect(root), root)
107
+
108
+ assert "a.py" in names
109
+ assert "src/b.py" in names
110
+ assert not any(n.startswith("build/") for n in names)
111
+
112
+ def test_real_excluded_dirs_inside_root_still_pruned(self, project_dir: Path):
113
+ fc = _collect(project_dir)
114
+ assert all("node_modules" not in fp.parts for fp in fc.collected_files)
115
+
116
+
95
117
  class TestGlobPatterns:
96
118
  def test_include_overrides_extension_whitelist(self, project_dir: Path):
97
119
  names = _rel_names(