article-cli 2.0.1__tar.gz → 2.1.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 (81) hide show
  1. {article_cli-2.0.1 → article_cli-2.1.0}/PKG-INFO +101 -9
  2. {article_cli-2.0.1 → article_cli-2.1.0}/README.md +97 -2
  3. {article_cli-2.0.1 → article_cli-2.1.0}/pyproject.toml +6 -9
  4. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/__init__.py +1 -1
  5. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/cli.py +1 -0
  6. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/__init__.py +2 -0
  7. article_cli-2.1.0/src/article_cli/commands/bundle.py +93 -0
  8. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/version.py +1 -1
  9. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/config.py +20 -14
  10. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/git_hooks.py +2 -1
  11. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/git_manager.py +2 -1
  12. article_cli-2.1.0/src/article_cli/paper_version.py +108 -0
  13. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/repository_setup.py +6 -1
  14. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/services/__init__.py +8 -0
  15. article_cli-2.1.0/src/article_cli/services/bundle.py +335 -0
  16. article_cli-2.1.0/src/article_cli/services/bundle_backends.py +337 -0
  17. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/services/compiler.py +11 -4
  18. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/services/gitinfo.py +45 -5
  19. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/services/release.py +33 -12
  20. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/article/ieee.tex.j2 +2 -2
  21. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/article/lncs.tex.j2 +2 -2
  22. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/article/lncs.typ.j2 +3 -0
  23. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/article/main.tex.j2 +2 -2
  24. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/article/main.typ.j2 +3 -0
  25. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/poster/poster.tex.j2 +2 -2
  26. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/poster/poster.typ.j2 +3 -0
  27. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/presentation/beamer.tex.j2 +2 -2
  28. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/presentation/typst.typ.j2 +3 -0
  29. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/project/README.md.j2 +2 -2
  30. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/project/pyproject.toml.j2 +1 -1
  31. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/typst_compiler.py +9 -0
  32. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/zotero.py +81 -51
  33. {article_cli-2.0.1 → article_cli-2.1.0}/tests/golden/article_main.tex +2 -2
  34. article_cli-2.1.0/tests/test_bundle.py +311 -0
  35. article_cli-2.1.0/tests/test_bundle_typst.py +332 -0
  36. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_cli.py +6 -5
  37. article_cli-2.1.0/tests/test_paper_version.py +231 -0
  38. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_release_service.py +24 -0
  39. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_repository_setup.py +1 -4
  40. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_services.py +3 -3
  41. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_version_consistency.py +1 -4
  42. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_workflow_ci.py +1 -4
  43. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_zotero_bibliography.py +80 -0
  44. {article_cli-2.0.1 → article_cli-2.1.0}/.gitignore +0 -0
  45. {article_cli-2.0.1 → article_cli-2.1.0}/LICENSE +0 -0
  46. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/__main__.py +0 -0
  47. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/command_runner.py +0 -0
  48. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/bibtex.py +0 -0
  49. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/clean.py +0 -0
  50. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/compile.py +0 -0
  51. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/config.py +0 -0
  52. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/doctor.py +0 -0
  53. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/fonts.py +0 -0
  54. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/init.py +0 -0
  55. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/release.py +0 -0
  56. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/setup.py +0 -0
  57. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/commands/themes.py +0 -0
  58. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/doctor.py +0 -0
  59. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/fonts.py +0 -0
  60. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/latex_compiler.py +0 -0
  61. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/project_context.py +0 -0
  62. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/reporting.py +0 -0
  63. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/services/bibliography.py +0 -0
  64. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/services/git.py +0 -0
  65. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/services/workflow.py +0 -0
  66. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/template_renderer.py +0 -0
  67. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/__init__.py +0 -0
  68. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/github/latex.yml.j2 +0 -0
  69. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/project/gitignore.j2 +0 -0
  70. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/templates/project/vscode-settings.json.j2 +0 -0
  71. {article_cli-2.0.1 → article_cli-2.1.0}/src/article_cli/themes.py +0 -0
  72. {article_cli-2.0.1 → article_cli-2.1.0}/tests/__init__.py +0 -0
  73. {article_cli-2.0.1 → article_cli-2.1.0}/tests/golden/__init__.py +0 -0
  74. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_config.py +0 -0
  75. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_doctor.py +0 -0
  76. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_fonts.py +0 -0
  77. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_latex_compiler.py +0 -0
  78. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_project_context.py +0 -0
  79. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_template_renderer.py +0 -0
  80. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_themes.py +0 -0
  81. {article_cli-2.0.1 → article_cli-2.1.0}/tests/test_typst_compiler.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: article-cli
3
- Version: 2.0.1
3
+ Version: 2.1.0
4
4
  Summary: CLI tool for managing LaTeX and Typst documents with git integration and Zotero bibliography
5
5
  Project-URL: Homepage, https://github.com/feelpp/article.cli
6
6
  Project-URL: Documentation, https://github.com/feelpp/article.cli#readme
@@ -35,18 +35,15 @@ Classifier: Intended Audience :: Science/Research
35
35
  Classifier: License :: OSI Approved :: MIT License
36
36
  Classifier: Operating System :: OS Independent
37
37
  Classifier: Programming Language :: Python :: 3
38
- Classifier: Programming Language :: Python :: 3.9
39
- Classifier: Programming Language :: Python :: 3.10
40
38
  Classifier: Programming Language :: Python :: 3.11
41
39
  Classifier: Programming Language :: Python :: 3.12
42
40
  Classifier: Programming Language :: Python :: 3.13
43
41
  Classifier: Topic :: Scientific/Engineering
44
42
  Classifier: Topic :: Text Processing :: Markup :: LaTeX
45
- Requires-Python: >=3.9
43
+ Requires-Python: >=3.11
46
44
  Requires-Dist: jinja2>=3.1
47
45
  Requires-Dist: pyyaml>=6.0
48
46
  Requires-Dist: requests>=2.28.0
49
- Requires-Dist: tomli>=2.0.0; python_version < '3.11'
50
47
  Provides-Extra: dev
51
48
  Requires-Dist: black==25.11.0; extra == 'dev'
52
49
  Requires-Dist: build>=1.2.0; extra == 'dev'
@@ -54,7 +51,7 @@ Requires-Dist: flake8>=6.0; extra == 'dev'
54
51
  Requires-Dist: mypy<2.0,>=1.0; extra == 'dev'
55
52
  Requires-Dist: pytest-cov; extra == 'dev'
56
53
  Requires-Dist: pytest>=7.0; extra == 'dev'
57
- Requires-Dist: twine>=5.0.0; extra == 'dev'
54
+ Requires-Dist: twine>=7.0.0; extra == 'dev'
58
55
  Requires-Dist: types-requests; extra == 'dev'
59
56
  Description-Content-Type: text/markdown
60
57
 
@@ -292,7 +289,7 @@ The `init` command sets up:
292
289
  ### Git Release Management
293
290
 
294
291
  ```bash
295
- # Refresh gitinfo2 version metadata
292
+ # Refresh paper version metadata (LaTeX or Typst)
296
293
  article-cli version
297
294
 
298
295
  # Refresh metadata, compile, and check the PDF version text
@@ -367,6 +364,97 @@ article-cli compile --clean-first --clean-after
367
364
  article-cli compile --output-dir build/
368
365
  ```
369
366
 
367
+ ### Paper Version Metadata
368
+
369
+ `article-cli version` writes LaTeX's `gitHeadLocal.gin` for `gitinfo2`, or a
370
+ portable `gitHeadLocal.json` snapshot beside a Typst main document. The Typst
371
+ snapshot contains the commit, short hash, branch, description, nearest tag,
372
+ commit date, and dirty flag. Generated metadata is excluded from dirty detection;
373
+ tracked source changes count as dirty, matching the existing LaTeX behavior.
374
+ `--dry-run` reports planned changes without writing files.
375
+
376
+ ```latex
377
+ \usepackage[local]{gitinfo2}
378
+ % In the document body:
379
+ Version: \gitDescribe{} / \gitAbbrevHash{}
380
+ ```
381
+
382
+ ```typst
383
+ #let git = json("gitHeadLocal.json")
384
+ #set page(footer: align(center)[Version: #git.description])
385
+ ```
386
+
387
+ New built-in templates include a version stamp. Typst compilation refreshes its
388
+ snapshot when Git is available; extracted source archives keep their frozen
389
+ snapshot. Typst reads JSON directly, so no extra Typst package is needed.
390
+
391
+ ```bash
392
+ article-cli version --document main.typ --compile --check-pdf --checksum
393
+ article-cli version --document main.tex --compile --check-pdf
394
+ ```
395
+
396
+ ### Submission Source Bundles (LaTeX and Typst)
397
+
398
+ ```bash
399
+ # Build and package the main document and its recorded dependencies
400
+ article-cli bundle main.tex --verify
401
+ article-cli bundle main.typ --verify
402
+
403
+ # Preview an existing build without changing any files
404
+ article-cli bundle main.tex --dry-run
405
+
406
+ # Extend project policy for one archive
407
+ article-cli bundle main.tex --include 'supplement/**' --exclude 'reviews/**' -o dist/preprint.zip
408
+ ```
409
+
410
+ `bundle` automatically selects a LaTeX or Typst backend. LaTeX uses `latexmk`
411
+ and TeX recorder data; Typst 0.14+ uses native JSON dependency reporting, including
412
+ dynamically loaded data, imports, figures, and bibliography styles. The command
413
+ writes a reproducible ZIP and an adjacent JSON manifest with file hashes.
414
+ `--verify` recompiles only the selected files in a temporary directory before
415
+ publishing the archive. Both formats share the same selection and output policy.
416
+
417
+ ```toml
418
+ [tool.article-cli.bundle]
419
+ output = "dist/preprint.zip"
420
+ include = ["supplement/**"]
421
+ exclude = ["reviews/**", "local-only/**"]
422
+ include_pdf = false
423
+ verify = true
424
+ ```
425
+
426
+ Paths and globs are relative to the main document's directory, which becomes the
427
+ ZIP root. Include patterns add files to detected dependencies; CLI patterns extend
428
+ those in configuration. Exclusions take precedence, and excluding a required
429
+ source fails with an explanation. `**` matches any number of directories.
430
+
431
+ Git repository internals, virtual environments, `local-only`, `.env` files, and build logs are
432
+ excluded by default. The main PDF is excluded unless `--include-pdf` is set; figure
433
+ PDFs remain included. Keep all local dependencies and the build directory under
434
+ the main document's directory. System TeX packages are supplied by the receiving
435
+ service. LaTeX shell escape is disabled.
436
+
437
+ Typst imports from external versioned packages are copied, with their manifests
438
+ and license files, under `.article-cli/typst-packages`. Configured local
439
+ `[tool.article-cli.typst].font_paths` must be under the document directory; font
440
+ files are included and their relative search paths recorded in the manifest.
441
+ System fonts remain environment dependencies. To compile an extracted Typst bundle:
442
+
443
+ ```bash
444
+ typst compile --root . --package-path .article-cli/typst-packages main.typ
445
+ # Add --font-path fonts when the bundle uses a configured local font directory.
446
+ ```
447
+
448
+ Use `--build-dir build --no-build` to reuse existing LaTeX `.fls`/`.aux`/`.bbl`
449
+ data or Typst `<document>.typst-deps.json` metadata, or `--force` to replace an
450
+ existing ZIP and manifest. Typst dependency metadata is generated by `bundle`;
451
+ a previous plain `compile` does not create it. Dry runs require existing metadata. `--no-verify` and `--no-include-pdf` override project policy.
452
+ Verification checks local compilability; HAL/arXiv still apply their own submission
453
+ and supported-format requirements. This command prepares files without uploading them.
454
+
455
+ Run `article-cli version` before bundling when you need an up-to-date provenance
456
+ stamp. Bundling preserves the selected snapshot during compilation and verification.
457
+
370
458
  ### Typst Compilation
371
459
 
372
460
  ```bash
@@ -502,7 +590,7 @@ Use `[tool.article-cli.release] tag_policy = "semver"` when a repository must re
502
590
 
503
591
  ## Requirements
504
592
 
505
- - Python 3.9+
593
+ - Python 3.11+
506
594
  - Git repository with gitinfo2 package (for LaTeX integration)
507
595
  - Zotero account with API access (for bibliography features)
508
596
  - Typst CLI (for Typst compilation) - install from https://typst.app/
@@ -522,6 +610,10 @@ MIT License - see LICENSE file for details.
522
610
 
523
611
  ## Changelog
524
612
 
613
+ ### v2.1.0
614
+ - Require Python 3.11+ and use its built-in TOML parser
615
+ - Add LaTeX and Typst submission bundles and portable paper version metadata
616
+
525
617
  ### v2.0.1
526
618
  - Keep Python package, generated-template minimum version, Antora component version, docs package version, and lockfiles synchronized
527
619
  - Add a release-version bump script and CI test to prevent stale documentation version labels
@@ -232,7 +232,7 @@ The `init` command sets up:
232
232
  ### Git Release Management
233
233
 
234
234
  ```bash
235
- # Refresh gitinfo2 version metadata
235
+ # Refresh paper version metadata (LaTeX or Typst)
236
236
  article-cli version
237
237
 
238
238
  # Refresh metadata, compile, and check the PDF version text
@@ -307,6 +307,97 @@ article-cli compile --clean-first --clean-after
307
307
  article-cli compile --output-dir build/
308
308
  ```
309
309
 
310
+ ### Paper Version Metadata
311
+
312
+ `article-cli version` writes LaTeX's `gitHeadLocal.gin` for `gitinfo2`, or a
313
+ portable `gitHeadLocal.json` snapshot beside a Typst main document. The Typst
314
+ snapshot contains the commit, short hash, branch, description, nearest tag,
315
+ commit date, and dirty flag. Generated metadata is excluded from dirty detection;
316
+ tracked source changes count as dirty, matching the existing LaTeX behavior.
317
+ `--dry-run` reports planned changes without writing files.
318
+
319
+ ```latex
320
+ \usepackage[local]{gitinfo2}
321
+ % In the document body:
322
+ Version: \gitDescribe{} / \gitAbbrevHash{}
323
+ ```
324
+
325
+ ```typst
326
+ #let git = json("gitHeadLocal.json")
327
+ #set page(footer: align(center)[Version: #git.description])
328
+ ```
329
+
330
+ New built-in templates include a version stamp. Typst compilation refreshes its
331
+ snapshot when Git is available; extracted source archives keep their frozen
332
+ snapshot. Typst reads JSON directly, so no extra Typst package is needed.
333
+
334
+ ```bash
335
+ article-cli version --document main.typ --compile --check-pdf --checksum
336
+ article-cli version --document main.tex --compile --check-pdf
337
+ ```
338
+
339
+ ### Submission Source Bundles (LaTeX and Typst)
340
+
341
+ ```bash
342
+ # Build and package the main document and its recorded dependencies
343
+ article-cli bundle main.tex --verify
344
+ article-cli bundle main.typ --verify
345
+
346
+ # Preview an existing build without changing any files
347
+ article-cli bundle main.tex --dry-run
348
+
349
+ # Extend project policy for one archive
350
+ article-cli bundle main.tex --include 'supplement/**' --exclude 'reviews/**' -o dist/preprint.zip
351
+ ```
352
+
353
+ `bundle` automatically selects a LaTeX or Typst backend. LaTeX uses `latexmk`
354
+ and TeX recorder data; Typst 0.14+ uses native JSON dependency reporting, including
355
+ dynamically loaded data, imports, figures, and bibliography styles. The command
356
+ writes a reproducible ZIP and an adjacent JSON manifest with file hashes.
357
+ `--verify` recompiles only the selected files in a temporary directory before
358
+ publishing the archive. Both formats share the same selection and output policy.
359
+
360
+ ```toml
361
+ [tool.article-cli.bundle]
362
+ output = "dist/preprint.zip"
363
+ include = ["supplement/**"]
364
+ exclude = ["reviews/**", "local-only/**"]
365
+ include_pdf = false
366
+ verify = true
367
+ ```
368
+
369
+ Paths and globs are relative to the main document's directory, which becomes the
370
+ ZIP root. Include patterns add files to detected dependencies; CLI patterns extend
371
+ those in configuration. Exclusions take precedence, and excluding a required
372
+ source fails with an explanation. `**` matches any number of directories.
373
+
374
+ Git repository internals, virtual environments, `local-only`, `.env` files, and build logs are
375
+ excluded by default. The main PDF is excluded unless `--include-pdf` is set; figure
376
+ PDFs remain included. Keep all local dependencies and the build directory under
377
+ the main document's directory. System TeX packages are supplied by the receiving
378
+ service. LaTeX shell escape is disabled.
379
+
380
+ Typst imports from external versioned packages are copied, with their manifests
381
+ and license files, under `.article-cli/typst-packages`. Configured local
382
+ `[tool.article-cli.typst].font_paths` must be under the document directory; font
383
+ files are included and their relative search paths recorded in the manifest.
384
+ System fonts remain environment dependencies. To compile an extracted Typst bundle:
385
+
386
+ ```bash
387
+ typst compile --root . --package-path .article-cli/typst-packages main.typ
388
+ # Add --font-path fonts when the bundle uses a configured local font directory.
389
+ ```
390
+
391
+ Use `--build-dir build --no-build` to reuse existing LaTeX `.fls`/`.aux`/`.bbl`
392
+ data or Typst `<document>.typst-deps.json` metadata, or `--force` to replace an
393
+ existing ZIP and manifest. Typst dependency metadata is generated by `bundle`;
394
+ a previous plain `compile` does not create it. Dry runs require existing metadata. `--no-verify` and `--no-include-pdf` override project policy.
395
+ Verification checks local compilability; HAL/arXiv still apply their own submission
396
+ and supported-format requirements. This command prepares files without uploading them.
397
+
398
+ Run `article-cli version` before bundling when you need an up-to-date provenance
399
+ stamp. Bundling preserves the selected snapshot during compilation and verification.
400
+
310
401
  ### Typst Compilation
311
402
 
312
403
  ```bash
@@ -442,7 +533,7 @@ Use `[tool.article-cli.release] tag_policy = "semver"` when a repository must re
442
533
 
443
534
  ## Requirements
444
535
 
445
- - Python 3.9+
536
+ - Python 3.11+
446
537
  - Git repository with gitinfo2 package (for LaTeX integration)
447
538
  - Zotero account with API access (for bibliography features)
448
539
  - Typst CLI (for Typst compilation) - install from https://typst.app/
@@ -462,6 +553,10 @@ MIT License - see LICENSE file for details.
462
553
 
463
554
  ## Changelog
464
555
 
556
+ ### v2.1.0
557
+ - Require Python 3.11+ and use its built-in TOML parser
558
+ - Add LaTeX and Typst submission bundles and portable paper version metadata
559
+
465
560
  ### v2.0.1
466
561
  - Keep Python package, generated-template minimum version, Antora component version, docs package version, and lockfiles synchronized
467
562
  - Add a release-version bump script and CI test to prevent stale documentation version labels
@@ -4,22 +4,20 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "article-cli"
7
- version = "2.0.1"
7
+ version = "2.1.0"
8
8
  authors = [
9
9
  {name = "Christophe Prud'homme", email = "prudhomm@cemosis.fr"},
10
10
  ]
11
11
  description = "CLI tool for managing LaTeX and Typst documents with git integration and Zotero bibliography"
12
12
  readme = "README.md"
13
13
  license = {file = "LICENSE"}
14
- requires-python = ">=3.9"
14
+ requires-python = ">=3.11"
15
15
  classifiers = [
16
16
  "Development Status :: 4 - Beta",
17
17
  "Intended Audience :: Science/Research",
18
18
  "License :: OSI Approved :: MIT License",
19
19
  "Operating System :: OS Independent",
20
20
  "Programming Language :: Python :: 3",
21
- "Programming Language :: Python :: 3.9",
22
- "Programming Language :: Python :: 3.10",
23
21
  "Programming Language :: Python :: 3.11",
24
22
  "Programming Language :: Python :: 3.12",
25
23
  "Programming Language :: Python :: 3.13",
@@ -31,7 +29,6 @@ dependencies = [
31
29
  "requests>=2.28.0",
32
30
  "PyYAML>=6.0",
33
31
  "Jinja2>=3.1",
34
- "tomli>=2.0.0; python_version < '3.11'",
35
32
  ]
36
33
 
37
34
  [project.optional-dependencies]
@@ -42,7 +39,7 @@ dev = [
42
39
  "flake8>=6.0",
43
40
  "mypy>=1.0,<2.0",
44
41
  "build>=1.2.0",
45
- "twine>=5.0.0",
42
+ "twine>=7.0.0",
46
43
  "types-requests",
47
44
  ]
48
45
 
@@ -54,7 +51,7 @@ dev = [
54
51
  "flake8>=6.0",
55
52
  "mypy>=1.0,<2.0",
56
53
  "build>=1.2.0",
57
- "twine>=5.0.0",
54
+ "twine>=7.0.0",
58
55
  "types-requests",
59
56
  ]
60
57
 
@@ -80,7 +77,7 @@ include = [
80
77
 
81
78
  [tool.black]
82
79
  line-length = 88
83
- target-version = ['py39']
80
+ target-version = ['py311']
84
81
  include = '\.pyi?$'
85
82
  extend-exclude = '''
86
83
  /(
@@ -97,7 +94,7 @@ extend-exclude = '''
97
94
  '''
98
95
 
99
96
  [tool.mypy]
100
- python_version = "3.9"
97
+ python_version = "3.11"
101
98
  warn_return_any = true
102
99
  warn_unused_configs = true
103
100
  disallow_untyped_defs = false # Relax for now to avoid breaking changes
@@ -9,7 +9,7 @@ This package provides tools for:
9
9
  - Git hooks setup
10
10
  """
11
11
 
12
- __version__ = "2.0.1"
12
+ __version__ = "2.1.0"
13
13
  __author__ = "Christophe Prud'homme"
14
14
  __email__ = "prudhomm@cemosis.fr"
15
15
 
@@ -44,6 +44,7 @@ Examples:
44
44
  %(prog)s bib update --check # Check checked-in bibliography freshness
45
45
  %(prog)s bib update # Update references from Zotero
46
46
  %(prog)s compile # Compile configured main document
47
+ %(prog)s bundle --verify # Prepare a submission source ZIP
47
48
  %(prog)s version # Refresh and report git metadata
48
49
  %(prog)s version --compile --check-pdf # Refresh, compile, and inspect PDF text
49
50
  %(prog)s release v1 --dry-run # Preview release tag creation
@@ -4,6 +4,7 @@ Command modules for article-cli.
4
4
 
5
5
  from . import (
6
6
  bibtex,
7
+ bundle,
7
8
  clean,
8
9
  compile,
9
10
  config,
@@ -25,6 +26,7 @@ COMMAND_MODULES = [
25
26
  version,
26
27
  release,
27
28
  bibtex,
29
+ bundle,
28
30
  config,
29
31
  fonts,
30
32
  themes,
@@ -0,0 +1,93 @@
1
+ """Source archive command for HAL, arXiv, and other submission systems."""
2
+
3
+ import argparse
4
+ import subprocess
5
+
6
+ from ..config import Config
7
+ from ..reporting import print_error, print_info, print_success
8
+ from ..services.bundle import BundleOptions, BundleService
9
+
10
+
11
+ def add_parser(subparsers):
12
+ parser = subparsers.add_parser("bundle", help="Create a LaTeX or Typst source ZIP")
13
+ parser.add_argument(
14
+ "document", nargs="?", help="Main .tex or .typ file (or configured document)"
15
+ )
16
+ parser.add_argument(
17
+ "-o", "--output", help="ZIP path, relative to document directory"
18
+ )
19
+ parser.add_argument(
20
+ "--build-dir", help="Build directory containing compiler dependency metadata"
21
+ )
22
+ parser.add_argument(
23
+ "--include",
24
+ action="append",
25
+ default=[],
26
+ help="Add document-relative glob (repeatable)",
27
+ )
28
+ parser.add_argument(
29
+ "--exclude",
30
+ action="append",
31
+ default=[],
32
+ help="Exclude document-relative glob (repeatable)",
33
+ )
34
+ for key, help_text in [
35
+ ("verify", "Recompile selected files in a clean directory"),
36
+ ("include-pdf", "Include the generated main PDF in the ZIP"),
37
+ ]:
38
+ group = parser.add_mutually_exclusive_group()
39
+ group.add_argument(
40
+ "--" + key,
41
+ dest=key.replace("-", "_"),
42
+ action="store_true",
43
+ default=None,
44
+ help=help_text,
45
+ )
46
+ group.add_argument(
47
+ "--no-" + key, dest=key.replace("-", "_"), action="store_false"
48
+ )
49
+ parser.add_argument(
50
+ "--no-build",
51
+ action="store_true",
52
+ help="Use existing compiler dependency metadata and PDF",
53
+ )
54
+ parser.add_argument(
55
+ "--dry-run",
56
+ action="store_true",
57
+ help="List files from existing build data; write nothing",
58
+ )
59
+ parser.add_argument(
60
+ "--force", action="store_true", help="Replace an existing ZIP and manifest"
61
+ )
62
+ parser.set_defaults(handler=run)
63
+
64
+
65
+ def run(args: argparse.Namespace, config: Config) -> int:
66
+ try:
67
+ service = BundleService(config)
68
+ plan = service.plan(
69
+ BundleOptions(
70
+ document=args.document,
71
+ output=args.output,
72
+ build_dir=args.build_dir,
73
+ include=tuple(args.include),
74
+ exclude=tuple(args.exclude),
75
+ include_pdf=args.include_pdf,
76
+ verify=args.verify,
77
+ no_build=args.no_build,
78
+ dry_run=args.dry_run,
79
+ )
80
+ )
81
+ print_info(f"Bundle files ({len(plan.files)}):")
82
+ for name in sorted(plan.files):
83
+ print(f" {name}")
84
+ if args.dry_run:
85
+ print_info(f"Would write: {plan.output}")
86
+ else:
87
+ output = service.write(plan, force=args.force)
88
+ print_success(f"Created source bundle: {output}")
89
+ print_info(f'Manifest: {output.with_suffix(".manifest.json")}')
90
+ return 0
91
+ except (ValueError, OSError, subprocess.TimeoutExpired) as error:
92
+ print_error(str(error))
93
+ return 1
@@ -17,7 +17,7 @@ def add_parser(subparsers: Any) -> None:
17
17
  "version",
18
18
  help="Refresh and report git version metadata",
19
19
  description=(
20
- "Refresh gitinfo2 metadata and report the current git version state "
20
+ "Refresh LaTeX gitinfo2 or Typst JSON metadata and report Git state "
21
21
  "without creating a tag."
22
22
  ),
23
23
  )
@@ -9,13 +9,7 @@ from pathlib import Path
9
9
  from typing import Dict, Any, Optional, Union
10
10
  import argparse
11
11
 
12
- try:
13
- import tomllib # Python 3.11+
14
- except ImportError:
15
- try:
16
- import tomli as tomllib # fallback for older Python versions
17
- except ImportError:
18
- tomllib = None
12
+ import tomllib
19
13
 
20
14
 
21
15
  class Config:
@@ -67,7 +61,7 @@ class Config:
67
61
  config_path = self._find_config_file()
68
62
  self.loaded_config_file = config_path.resolve() if config_path else None
69
63
 
70
- if config_path and tomllib:
64
+ if config_path:
71
65
  try:
72
66
  with open(config_path, "rb") as f:
73
67
  full_config = tomllib.load(f)
@@ -104,12 +98,6 @@ class Config:
104
98
  if not self.quiet:
105
99
  print(f"Warning: Could not load config file {config_path}: {e}")
106
100
  self._config_data = {}
107
- elif config_path and not tomllib:
108
- if not self.quiet:
109
- print(
110
- "Warning: TOML support not available. Install dependencies with: uv sync"
111
- )
112
- self._config_data = {}
113
101
  else:
114
102
  self._config_data = {}
115
103
 
@@ -205,6 +193,16 @@ class Config:
205
193
  "timeout": self.get("latex", "timeout", 300),
206
194
  }
207
195
 
196
+ def get_bundle_config(self) -> Dict[str, Any]:
197
+ """Get source archive policy; patterns are document-directory relative."""
198
+ return {
199
+ "output": self.get("bundle", "output", ""),
200
+ "include": self.get("bundle", "include", []),
201
+ "exclude": self.get("bundle", "exclude", []),
202
+ "include_pdf": self.get("bundle", "include_pdf", False),
203
+ "verify": self.get("bundle", "verify", False),
204
+ }
205
+
208
206
  def get_project_config(self) -> Dict[str, Any]:
209
207
  """Get project-level configuration"""
210
208
  return {
@@ -426,6 +424,14 @@ shell_escape = false
426
424
  # Compilation timeout in seconds
427
425
  timeout = 300
428
426
 
427
+ [bundle]
428
+ # Submission ZIP; paths and globs are relative to the main document directory.
429
+ # output = "dist/paper-sources.zip"
430
+ include = []
431
+ exclude = ["local-only/**", "reviews/**"]
432
+ include_pdf = false
433
+ verify = false
434
+
429
435
  [project]
430
436
  # Project type: "article", "typst-article", "presentation", "typst-presentation",
431
437
  # "poster", or "typst-poster"
@@ -10,6 +10,7 @@ import subprocess
10
10
  from pathlib import Path
11
11
  from typing import List, Optional
12
12
 
13
+ from .paper_version import METADATA_EXCLUDES
13
14
  from .reporting import print_info, print_success, print_warning
14
15
 
15
16
  MANAGED_HOOK_START = "# >>> article-cli gitinfo2 hook >>>"
@@ -279,7 +280,7 @@ def _is_dirty_for_gitinfo2(repo_path: Path) -> bool:
279
280
  "--untracked-files=no",
280
281
  "--",
281
282
  ".",
282
- ":(exclude)gitHeadLocal.gin",
283
+ *METADATA_EXCLUDES,
283
284
  ],
284
285
  )
285
286
  return bool(result and result.stdout.strip())
@@ -16,6 +16,7 @@ from .git_hooks import (
16
16
  install_managed_gitinfo2_hook,
17
17
  refresh_gitinfo2_metadata,
18
18
  )
19
+ from .paper_version import METADATA_EXCLUDES
19
20
  from .reporting import Colors, print_error, print_info, print_success, print_warning
20
21
 
21
22
 
@@ -294,7 +295,7 @@ class GitManager:
294
295
  """Return dirty tracked/untracked files, optionally ignoring gitHeadLocal.gin."""
295
296
  pathspec = ["--", "."]
296
297
  if ignore_gitinfo:
297
- pathspec.append(":(exclude)gitHeadLocal.gin")
298
+ pathspec.extend(METADATA_EXCLUDES)
298
299
  result = self.git(["status", "--porcelain", *pathspec])
299
300
  if result.returncode != 0:
300
301
  return ["<could not inspect git status>"]