narratty 0.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 (133) hide show
  1. narratty-0.1.0/.codespellignore +5 -0
  2. narratty-0.1.0/.dockerignore +13 -0
  3. narratty-0.1.0/.github/workflows/docker.yml +96 -0
  4. narratty-0.1.0/.github/workflows/main.yml +79 -0
  5. narratty-0.1.0/.github/workflows/pr.yml +156 -0
  6. narratty-0.1.0/.github/workflows/release.yml +134 -0
  7. narratty-0.1.0/.gitignore +37 -0
  8. narratty-0.1.0/.pre-commit-config.yaml +54 -0
  9. narratty-0.1.0/AGENTS.md +29 -0
  10. narratty-0.1.0/CHANGELOG.md +22 -0
  11. narratty-0.1.0/Dockerfile +74 -0
  12. narratty-0.1.0/LICENSE +21 -0
  13. narratty-0.1.0/PKG-INFO +111 -0
  14. narratty-0.1.0/README.md +44 -0
  15. narratty-0.1.0/docs/changelog.md +1 -0
  16. narratty-0.1.0/docs/contributor-guide/design.md +825 -0
  17. narratty-0.1.0/docs/contributor-guide/dev-setup.md +27 -0
  18. narratty-0.1.0/docs/contributor-guide/release-process.md +29 -0
  19. narratty-0.1.0/docs/index.md +39 -0
  20. narratty-0.1.0/docs/user-guide/building.md +60 -0
  21. narratty-0.1.0/docs/user-guide/commands.md +39 -0
  22. narratty-0.1.0/docs/user-guide/configuration.md +42 -0
  23. narratty-0.1.0/docs/user-guide/container.md +110 -0
  24. narratty-0.1.0/docs/user-guide/installation.md +29 -0
  25. narratty-0.1.0/docs/user-guide/shell-completion.md +12 -0
  26. narratty-0.1.0/docs/user-guide/spec.md +158 -0
  27. narratty-0.1.0/docs/user-guide/voices.md +63 -0
  28. narratty-0.1.0/mkdocs.yml +91 -0
  29. narratty-0.1.0/mypy.ini +16 -0
  30. narratty-0.1.0/noxfile.py +173 -0
  31. narratty-0.1.0/pyproject.toml +113 -0
  32. narratty-0.1.0/ruff.toml +17 -0
  33. narratty-0.1.0/schema/v1.json +649 -0
  34. narratty-0.1.0/src/narratty/__init__.py +15 -0
  35. narratty-0.1.0/src/narratty/__main__.py +35 -0
  36. narratty-0.1.0/src/narratty/_version.py +24 -0
  37. narratty-0.1.0/src/narratty/build.py +208 -0
  38. narratty-0.1.0/src/narratty/cache.py +85 -0
  39. narratty-0.1.0/src/narratty/cli/__init__.py +1 -0
  40. narratty-0.1.0/src/narratty/cli/app.py +74 -0
  41. narratty-0.1.0/src/narratty/cli/arguments.py +14 -0
  42. narratty-0.1.0/src/narratty/cli/commands/__init__.py +1 -0
  43. narratty-0.1.0/src/narratty/cli/commands/build.py +97 -0
  44. narratty-0.1.0/src/narratty/cli/commands/cache.py +57 -0
  45. narratty-0.1.0/src/narratty/cli/commands/doctor.py +51 -0
  46. narratty-0.1.0/src/narratty/cli/commands/init.py +25 -0
  47. narratty-0.1.0/src/narratty/cli/commands/plan.py +56 -0
  48. narratty-0.1.0/src/narratty/cli/commands/render.py +79 -0
  49. narratty-0.1.0/src/narratty/cli/commands/schema.py +12 -0
  50. narratty-0.1.0/src/narratty/cli/commands/tape.py +37 -0
  51. narratty-0.1.0/src/narratty/cli/commands/tts.py +48 -0
  52. narratty-0.1.0/src/narratty/cli/commands/validate.py +34 -0
  53. narratty-0.1.0/src/narratty/cli/commands/voices.py +72 -0
  54. narratty-0.1.0/src/narratty/cli/completion.py +51 -0
  55. narratty-0.1.0/src/narratty/cli/options.py +82 -0
  56. narratty-0.1.0/src/narratty/config.py +40 -0
  57. narratty-0.1.0/src/narratty/container.py +303 -0
  58. narratty-0.1.0/src/narratty/data/voices.toml +106 -0
  59. narratty-0.1.0/src/narratty/doctor.py +84 -0
  60. narratty-0.1.0/src/narratty/end_card.py +149 -0
  61. narratty-0.1.0/src/narratty/errors.py +72 -0
  62. narratty-0.1.0/src/narratty/forward.py +83 -0
  63. narratty-0.1.0/src/narratty/paths.py +33 -0
  64. narratty-0.1.0/src/narratty/py.typed +0 -0
  65. narratty-0.1.0/src/narratty/render/__init__.py +1 -0
  66. narratty-0.1.0/src/narratty/render/media.py +103 -0
  67. narratty-0.1.0/src/narratty/render/narration.py +58 -0
  68. narratty-0.1.0/src/narratty/render/tape.py +142 -0
  69. narratty-0.1.0/src/narratty/runtime.py +63 -0
  70. narratty-0.1.0/src/narratty/sandbox.py +294 -0
  71. narratty-0.1.0/src/narratty/spec/__init__.py +6 -0
  72. narratty-0.1.0/src/narratty/spec/loader.py +179 -0
  73. narratty-0.1.0/src/narratty/spec/model.py +315 -0
  74. narratty-0.1.0/src/narratty/spec/schema.py +41 -0
  75. narratty-0.1.0/src/narratty/spec/template.py +46 -0
  76. narratty-0.1.0/src/narratty/timeline.py +101 -0
  77. narratty-0.1.0/src/narratty/tts/__init__.py +1 -0
  78. narratty-0.1.0/src/narratty/tts/audio.py +29 -0
  79. narratty-0.1.0/src/narratty/tts/base.py +73 -0
  80. narratty-0.1.0/src/narratty/tts/catalog.py +97 -0
  81. narratty-0.1.0/src/narratty/tts/download.py +78 -0
  82. narratty-0.1.0/src/narratty/tts/kokoro.py +102 -0
  83. narratty-0.1.0/src/narratty/tts/normalize.py +15 -0
  84. narratty-0.1.0/src/narratty/tts/piper.py +129 -0
  85. narratty-0.1.0/src/narratty/tts/registry.py +49 -0
  86. narratty-0.1.0/src/narratty/tts/synth.py +72 -0
  87. narratty-0.1.0/src/narratty/ui/__init__.py +1 -0
  88. narratty-0.1.0/src/narratty/ui/console.py +21 -0
  89. narratty-0.1.0/src/narratty/workspace.py +172 -0
  90. narratty-0.1.0/tests/__init__.py +0 -0
  91. narratty-0.1.0/tests/helpers.py +28 -0
  92. narratty-0.1.0/tests/integration/__init__.py +0 -0
  93. narratty-0.1.0/tests/integration/conftest.py +20 -0
  94. narratty-0.1.0/tests/integration/test_build.py +101 -0
  95. narratty-0.1.0/tests/integration/test_container.py +46 -0
  96. narratty-0.1.0/tests/integration/test_sandbox_network.py +101 -0
  97. narratty-0.1.0/tests/integration/test_tts_engines.py +67 -0
  98. narratty-0.1.0/tests/performance/__init__.py +0 -0
  99. narratty-0.1.0/tests/performance/test_cold_start_budget.py +44 -0
  100. narratty-0.1.0/tests/unit/__init__.py +0 -0
  101. narratty-0.1.0/tests/unit/cli/__init__.py +0 -0
  102. narratty-0.1.0/tests/unit/cli/test_app.py +46 -0
  103. narratty-0.1.0/tests/unit/cli/test_completion.py +57 -0
  104. narratty-0.1.0/tests/unit/cli/test_render_commands.py +111 -0
  105. narratty-0.1.0/tests/unit/cli/test_spec_commands.py +46 -0
  106. narratty-0.1.0/tests/unit/cli/test_tts_commands.py +118 -0
  107. narratty-0.1.0/tests/unit/conftest.py +17 -0
  108. narratty-0.1.0/tests/unit/render/__init__.py +0 -0
  109. narratty-0.1.0/tests/unit/render/test_media.py +81 -0
  110. narratty-0.1.0/tests/unit/render/test_narration.py +70 -0
  111. narratty-0.1.0/tests/unit/render/test_tape.py +122 -0
  112. narratty-0.1.0/tests/unit/spec/__init__.py +0 -0
  113. narratty-0.1.0/tests/unit/spec/test_loader.py +83 -0
  114. narratty-0.1.0/tests/unit/spec/test_model.py +109 -0
  115. narratty-0.1.0/tests/unit/spec/test_schema.py +39 -0
  116. narratty-0.1.0/tests/unit/test_build.py +70 -0
  117. narratty-0.1.0/tests/unit/test_cache.py +62 -0
  118. narratty-0.1.0/tests/unit/test_container.py +181 -0
  119. narratty-0.1.0/tests/unit/test_doctor.py +20 -0
  120. narratty-0.1.0/tests/unit/test_end_card.py +158 -0
  121. narratty-0.1.0/tests/unit/test_forward.py +48 -0
  122. narratty-0.1.0/tests/unit/test_main.py +18 -0
  123. narratty-0.1.0/tests/unit/test_runtime.py +40 -0
  124. narratty-0.1.0/tests/unit/test_sandbox.py +167 -0
  125. narratty-0.1.0/tests/unit/test_timeline.py +90 -0
  126. narratty-0.1.0/tests/unit/test_workspace.py +135 -0
  127. narratty-0.1.0/tests/unit/tts/__init__.py +0 -0
  128. narratty-0.1.0/tests/unit/tts/test_catalog.py +47 -0
  129. narratty-0.1.0/tests/unit/tts/test_download.py +40 -0
  130. narratty-0.1.0/tests/unit/tts/test_normalize_audio.py +28 -0
  131. narratty-0.1.0/tests/unit/tts/test_providers.py +178 -0
  132. narratty-0.1.0/tests/unit/tts/test_synth.py +119 -0
  133. narratty-0.1.0/uv.lock +2094 -0
@@ -0,0 +1,5 @@
1
+ # codespell ignore list: words that are not typos in this project.
2
+ narratty
3
+ astroid
4
+ hiden
5
+ widht
@@ -0,0 +1,13 @@
1
+ .git
2
+ .venv
3
+ .nox
4
+ .mypy_cache
5
+ .ruff_cache
6
+ .pytest_cache
7
+ **/__pycache__
8
+ site
9
+ out
10
+ dist
11
+ tests
12
+ docs
13
+ *.mp4
@@ -0,0 +1,96 @@
1
+ name: Docker image
2
+ # PRs: build and test the image (amd64, not pushed).
3
+ # main: push :edge / :edge-kokoro. Release tags v*: push :<version>, :latest (and -kokoro).
4
+ on:
5
+ pull_request:
6
+ push:
7
+ branches: [main]
8
+ tags: ["v[0-9]+.[0-9]+.[0-9]*"]
9
+ concurrency:
10
+ group: docker-${{ github.ref }}
11
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
12
+ permissions:
13
+ contents: read
14
+ env:
15
+ IMAGE: ghcr.io/${{ github.repository_owner }}/narratty
16
+ jobs:
17
+ image:
18
+ name: Image (${{ matrix.variant }})
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ contents: read
22
+ packages: write
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ include:
27
+ - variant: base
28
+ target: base
29
+ suffix: ""
30
+ - variant: kokoro
31
+ target: kokoro
32
+ suffix: "-kokoro"
33
+ steps:
34
+ - uses: actions/checkout@v7
35
+ with:
36
+ fetch-depth: 0 # the version comes from git tags
37
+ - uses: astral-sh/setup-uv@v7
38
+ - name: Version
39
+ id: version
40
+ run: echo "version=$(uvx --from setuptools-scm python -m setuptools_scm)" >> "$GITHUB_OUTPUT"
41
+ - uses: docker/setup-qemu-action@v4
42
+ if: github.event_name != 'pull_request'
43
+ - uses: docker/setup-buildx-action@v4
44
+ - uses: docker/metadata-action@v6
45
+ id: meta
46
+ with:
47
+ images: ${{ env.IMAGE }}
48
+ flavor: |
49
+ latest=false
50
+ suffix=${{ matrix.suffix }},onlatest=true
51
+ tags: |
52
+ type=edge,branch=main
53
+ type=semver,pattern={{version}}
54
+ type=semver,pattern={{major}}.{{minor}}
55
+ type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
56
+ type=ref,event=pr
57
+ - name: Log in to GHCR
58
+ if: github.event_name != 'pull_request'
59
+ uses: docker/login-action@v4
60
+ with:
61
+ registry: ghcr.io
62
+ username: ${{ github.actor }}
63
+ password: ${{ secrets.GITHUB_TOKEN }}
64
+ - name: Build (and push outside PRs)
65
+ uses: docker/build-push-action@v7
66
+ with:
67
+ context: .
68
+ target: ${{ matrix.target }}
69
+ build-args: NARRATTY_VERSION=${{ steps.version.outputs.version }}
70
+ platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
71
+ push: ${{ github.event_name != 'pull_request' }}
72
+ load: ${{ github.event_name == 'pull_request' }}
73
+ tags: ${{ github.event_name == 'pull_request' && format('narratty:test{0}', matrix.suffix) || steps.meta.outputs.tags }}
74
+ labels: ${{ steps.meta.outputs.labels }}
75
+ cache-from: type=gha,scope=${{ matrix.variant }}
76
+ cache-to: type=gha,mode=max,scope=${{ matrix.variant }}
77
+ - name: Smoke test
78
+ if: github.event_name == 'pull_request'
79
+ run: |
80
+ docker run --rm narratty:test${{ matrix.suffix }} --version
81
+ docker run --rm narratty:test${{ matrix.suffix }} doctor --runtime native
82
+ docker run --rm narratty:test${{ matrix.suffix }} voices --installed
83
+ - uses: actions/setup-python@v6
84
+ if: github.event_name == 'pull_request' && matrix.variant == 'base'
85
+ with:
86
+ python-version: "3.12"
87
+ - name: Install ffprobe (the test checks the video on the host)
88
+ if: github.event_name == 'pull_request' && matrix.variant == 'base'
89
+ run: sudo apt-get update && sudo apt-get install -y --no-install-recommends ffmpeg
90
+ - name: Build a video through the container runtime and test the sandbox network
91
+ if: github.event_name == 'pull_request' && matrix.variant == 'base'
92
+ env:
93
+ NARRATTY_IMAGE: narratty:test
94
+ run: |
95
+ uv tool install nox
96
+ nox -s integration -- -k "container or sandbox_network"
@@ -0,0 +1,79 @@
1
+ name: Main
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ concurrency:
6
+ group: main-${{ github.ref }}
7
+ cancel-in-progress: true
8
+ permissions:
9
+ contents: read
10
+ jobs:
11
+ # ── 1. Full quality gates ───────────────────────────────────────────────────
12
+ quality:
13
+ name: Quality gates (3.12)
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v7
17
+ with:
18
+ fetch-depth: 0
19
+ - uses: actions/setup-python@v6
20
+ with:
21
+ python-version: "3.12"
22
+ - uses: astral-sh/setup-uv@v7
23
+ - name: Install nox
24
+ run: uv tool install nox
25
+ - name: Run all quality gates
26
+ run: nox --no-error-on-missing-interpreters
27
+ # ── 2. Tests: multi-Python matrix, macOS best effort ────────────────────────
28
+ tests:
29
+ name: Tests (${{ matrix.os }}, Python ${{ matrix.python-version }})
30
+ runs-on: ${{ matrix.os }}
31
+ continue-on-error: ${{ matrix.os == 'macos-latest' }}
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ os: [ubuntu-latest]
36
+ python-version: ["3.12", "3.13", "3.14"]
37
+ include:
38
+ - os: macos-latest
39
+ python-version: "3.12"
40
+ steps:
41
+ - uses: actions/checkout@v7
42
+ with:
43
+ fetch-depth: 0
44
+ - uses: actions/setup-python@v6
45
+ with:
46
+ python-version: ${{ matrix.python-version }}
47
+ allow-prereleases: true
48
+ - uses: astral-sh/setup-uv@v7
49
+ - name: Install nox
50
+ run: uv tool install nox
51
+ - name: Unit tests
52
+ run: nox -s tests-${{ matrix.python-version }}
53
+ # ── 3. Deploy docs `latest` to GitHub Pages ─────────────────────────────────
54
+ docs-deploy:
55
+ name: Deploy docs (latest)
56
+ runs-on: ubuntu-latest
57
+ needs: [quality, tests]
58
+ permissions:
59
+ contents: write
60
+ steps:
61
+ - uses: actions/checkout@v7
62
+ with:
63
+ fetch-depth: 0
64
+ - uses: actions/setup-python@v6
65
+ with:
66
+ python-version: "3.12"
67
+ - uses: astral-sh/setup-uv@v7
68
+ - name: Install docs deps
69
+ run: uv pip install -e ".[docs]" --system
70
+ - name: Configure git for mike
71
+ run: |
72
+ git config user.name "github-actions[bot]"
73
+ git config user.email "github-actions[bot]@users.noreply.github.com"
74
+ - name: Deploy latest docs
75
+ env:
76
+ NO_MKDOCS_2_WARNING: "1"
77
+ run: |
78
+ mike deploy --push --update-aliases latest
79
+ mike set-default --push latest
@@ -0,0 +1,156 @@
1
+ name: PR
2
+ on:
3
+ pull_request:
4
+ types: [opened, synchronize, reopened, edited]
5
+ concurrency:
6
+ group: pr-${{ github.event.pull_request.number }}
7
+ cancel-in-progress: true
8
+ permissions:
9
+ contents: read
10
+ pull-requests: read
11
+ jobs:
12
+ # ── 1. Conventional Commits PR-title lint (primary gate for squash merges) ──
13
+ lint-title:
14
+ name: Lint PR title
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: amannn/action-semantic-pull-request@v6
18
+ env:
19
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20
+ with:
21
+ types: |
22
+ feat
23
+ fix
24
+ docs
25
+ refactor
26
+ perf
27
+ test
28
+ build
29
+ ci
30
+ chore
31
+ revert
32
+ requireScope: false
33
+ subjectPattern: ^[a-z].+[^.]$
34
+ subjectPatternError: |
35
+ Subject must start lowercase and not end with a period.
36
+ # ── 2. Code quality ─────────────────────────────────────────────────────────
37
+ quality:
38
+ name: Quality gates (3.12)
39
+ runs-on: ubuntu-latest
40
+ steps:
41
+ - uses: actions/checkout@v7
42
+ with:
43
+ fetch-depth: 0 # hatch-vcs needs tags for the version
44
+ - uses: actions/setup-python@v6
45
+ with:
46
+ python-version: "3.12"
47
+ - uses: astral-sh/setup-uv@v7
48
+ - name: Install nox
49
+ run: uv tool install nox
50
+ - name: Syntax check
51
+ run: nox -s check_syntax
52
+ - name: Lint
53
+ run: nox -s lint
54
+ - name: Type check
55
+ run: nox -s check_types
56
+ - name: Import analysis
57
+ run: nox -s necessary_imports
58
+ - name: Dead code
59
+ run: nox -s dead_code
60
+ - name: Duplicate code
61
+ run: nox -s duplicates
62
+ - name: Cyclic imports
63
+ run: nox -s cyclic_imports
64
+ # ── 3. Tests: multi-Python matrix, macOS best effort ────────────────────────
65
+ tests:
66
+ name: Tests (${{ matrix.os }}, Python ${{ matrix.python-version }})
67
+ runs-on: ${{ matrix.os }}
68
+ continue-on-error: ${{ matrix.os == 'macos-latest' }}
69
+ strategy:
70
+ fail-fast: false
71
+ matrix:
72
+ os: [ubuntu-latest]
73
+ python-version: ["3.12", "3.13", "3.14"]
74
+ include:
75
+ # macOS is optional: smoke-test the primary Python, never block.
76
+ - os: macos-latest
77
+ python-version: "3.12"
78
+ steps:
79
+ - uses: actions/checkout@v7
80
+ with:
81
+ fetch-depth: 0
82
+ - uses: actions/setup-python@v6
83
+ with:
84
+ python-version: ${{ matrix.python-version }}
85
+ allow-prereleases: true
86
+ - uses: astral-sh/setup-uv@v7
87
+ - name: Install nox
88
+ run: uv tool install nox
89
+ - name: Unit tests
90
+ run: nox -s tests-${{ matrix.python-version }}
91
+ # ── 4. Integration: real voices, VHS and ffmpeg ─────────────────────────────
92
+ integration:
93
+ name: Integration (3.12)
94
+ runs-on: ubuntu-latest
95
+ env:
96
+ NARRATTY_DATA_DIR: ${{ github.workspace }}/.narratty-data
97
+ VHS_VERSION: "0.12.1"
98
+ # Chromium's sandbox is blocked by Ubuntu's user-namespace restrictions on runners.
99
+ VHS_NO_SANDBOX: "true"
100
+ steps:
101
+ - uses: actions/checkout@v7
102
+ with:
103
+ fetch-depth: 0
104
+ - uses: actions/setup-python@v6
105
+ with:
106
+ python-version: "3.12"
107
+ - uses: astral-sh/setup-uv@v7
108
+ - name: Cache voices and models
109
+ uses: actions/cache@v5
110
+ with:
111
+ path: .narratty-data
112
+ key: narratty-voices-${{ hashFiles('src/narratty/data/voices.toml') }}
113
+ - name: Install ffmpeg, ttyd and VHS
114
+ run: |
115
+ sudo apt-get update
116
+ sudo apt-get install -y --no-install-recommends ffmpeg ttyd
117
+ curl -fsSL -o "$RUNNER_TEMP/vhs.deb" \
118
+ "https://github.com/charmbracelet/vhs/releases/download/v${VHS_VERSION}/vhs_${VHS_VERSION}_amd64.deb"
119
+ sudo apt-get install -y "$RUNNER_TEMP/vhs.deb"
120
+ vhs --version
121
+ - name: Install nox
122
+ run: uv tool install nox
123
+ - name: Integration tests
124
+ run: nox -s integration
125
+ # ── 5. CLI cold-start performance budgets ───────────────────────────────────
126
+ performance:
127
+ name: Performance budgets (3.12)
128
+ runs-on: ubuntu-latest
129
+ steps:
130
+ - uses: actions/checkout@v7
131
+ with:
132
+ fetch-depth: 0
133
+ - uses: actions/setup-python@v6
134
+ with:
135
+ python-version: "3.12"
136
+ - uses: astral-sh/setup-uv@v7
137
+ - name: Install nox
138
+ run: uv tool install nox
139
+ - name: Performance budgets
140
+ run: nox -s performance
141
+ # ── 6. Docs build (strict) ──────────────────────────────────────────────────
142
+ docs-build:
143
+ name: Docs build (strict)
144
+ runs-on: ubuntu-latest
145
+ steps:
146
+ - uses: actions/checkout@v7
147
+ with:
148
+ fetch-depth: 0
149
+ - uses: actions/setup-python@v6
150
+ with:
151
+ python-version: "3.12"
152
+ - uses: astral-sh/setup-uv@v7
153
+ - name: Install nox
154
+ run: uv tool install nox
155
+ - name: Build docs
156
+ run: nox -s docs
@@ -0,0 +1,134 @@
1
+ name: Release
2
+ on:
3
+ push:
4
+ tags:
5
+ - "v[0-9]+.[0-9]+.[0-9]*"
6
+ permissions:
7
+ contents: write # create GitHub Release
8
+ id-token: write # OIDC Trusted Publishing (PyPI + TestPyPI)
9
+ jobs:
10
+ # ── 1. Build sdist + wheel ───────────────────────────────────────────────────
11
+ build:
12
+ name: Build distribution
13
+ runs-on: ubuntu-latest
14
+ outputs:
15
+ version: ${{ steps.version.outputs.version }}
16
+ steps:
17
+ - uses: actions/checkout@v7
18
+ with:
19
+ fetch-depth: 0 # hatch-vcs needs full history
20
+ - uses: actions/setup-python@v6
21
+ with:
22
+ python-version: "3.12"
23
+ - uses: astral-sh/setup-uv@v7
24
+ - name: Install hatch
25
+ run: uv tool install hatch
26
+ - name: Build
27
+ run: hatch build
28
+ - name: Extract version
29
+ id: version
30
+ run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
31
+ - uses: actions/upload-artifact@v7
32
+ with:
33
+ name: dist
34
+ path: dist/
35
+ # ── 2. Publish to TestPyPI (dry-run gate) ───────────────────────────────────
36
+ publish-test:
37
+ name: Publish to TestPyPI
38
+ runs-on: ubuntu-latest
39
+ needs: build
40
+ environment:
41
+ name: testpypi
42
+ url: https://test.pypi.org/p/narratty
43
+ steps:
44
+ - uses: actions/download-artifact@v8
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+ - name: Publish to TestPyPI
49
+ uses: pypa/gh-action-pypi-publish@release/v1
50
+ with:
51
+ repository-url: https://test.pypi.org/legacy/
52
+ print-hash: true
53
+ verbose: true
54
+ - name: Verify install from TestPyPI
55
+ run: |
56
+ uv tool install \
57
+ --index-url https://test.pypi.org/simple/ \
58
+ --extra-index-url https://pypi.org/simple/ \
59
+ "narratty==${{ needs.build.outputs.version }}"
60
+ narratty --version
61
+ # ── 3. Publish to PyPI (production) ─────────────────────────────────────────
62
+ publish-pypi:
63
+ name: Publish to PyPI
64
+ runs-on: ubuntu-latest
65
+ needs: [build, publish-test]
66
+ environment:
67
+ name: pypi
68
+ url: https://pypi.org/p/narratty
69
+ steps:
70
+ - uses: actions/download-artifact@v8
71
+ with:
72
+ name: dist
73
+ path: dist/
74
+ - name: Publish to PyPI
75
+ uses: pypa/gh-action-pypi-publish@release/v1
76
+ with:
77
+ print-hash: true
78
+ # ── 4. Create GitHub Release ─────────────────────────────────────────────────
79
+ github-release:
80
+ name: Create GitHub Release
81
+ runs-on: ubuntu-latest
82
+ needs: [build, publish-pypi]
83
+ permissions:
84
+ contents: write
85
+ steps:
86
+ - uses: actions/checkout@v7
87
+ with:
88
+ fetch-depth: 0
89
+ - uses: actions/setup-python@v6
90
+ with:
91
+ python-version: "3.12"
92
+ - uses: astral-sh/setup-uv@v7
93
+ - name: Install commitizen
94
+ run: uv tool install commitizen
95
+ - name: Extract changelog for this version
96
+ run: |
97
+ cz changelog --dry-run "${{ needs.build.outputs.version }}" > /tmp/release-notes.md || \
98
+ echo "Release ${{ needs.build.outputs.version }}" > /tmp/release-notes.md
99
+ - uses: actions/download-artifact@v8
100
+ with:
101
+ name: dist
102
+ path: dist/
103
+ - name: Create GitHub Release
104
+ uses: softprops/action-gh-release@v3
105
+ with:
106
+ body_path: /tmp/release-notes.md
107
+ files: dist/*
108
+ fail_on_unmatched_files: true
109
+ # ── 5. Deploy versioned docs ─────────────────────────────────────────────────
110
+ docs-deploy:
111
+ name: Deploy versioned docs
112
+ runs-on: ubuntu-latest
113
+ needs: [build, github-release]
114
+ permissions:
115
+ contents: write
116
+ steps:
117
+ - uses: actions/checkout@v7
118
+ with:
119
+ fetch-depth: 0
120
+ - uses: actions/setup-python@v6
121
+ with:
122
+ python-version: "3.12"
123
+ - uses: astral-sh/setup-uv@v7
124
+ - name: Install docs deps
125
+ run: uv pip install -e ".[docs]" --system
126
+ - name: Configure git for mike
127
+ run: |
128
+ git config user.name "github-actions[bot]"
129
+ git config user.email "github-actions[bot]@users.noreply.github.com"
130
+ - name: Deploy versioned docs
131
+ run: |
132
+ VERSION="${{ needs.build.outputs.version }}"
133
+ mike deploy --push --update-aliases "$VERSION" stable
134
+ mike set-default --push stable
@@ -0,0 +1,37 @@
1
+ # generated files
2
+ src/narratty/_version.py
3
+
4
+ # Byte-compiled / caches
5
+ __pycache__/
6
+ *.py[cod]
7
+ *.egg-info/
8
+ .eggs/
9
+ build/
10
+ dist/
11
+
12
+ # Environments
13
+ .venv/
14
+ venv/
15
+ .env
16
+
17
+ # Tooling caches
18
+ .nox/
19
+ .mypy_cache/
20
+ .ruff_cache/
21
+ .pytest_cache/
22
+ .coverage
23
+ coverage.xml
24
+ htmlcov/
25
+
26
+ # Editor
27
+ .idea/
28
+ *.swp
29
+
30
+
31
+
32
+
33
+ # MkDocs build output
34
+ site/
35
+
36
+ # narratty render output
37
+ out/
@@ -0,0 +1,54 @@
1
+ default_install_hook_types: [pre-commit, commit-msg]
2
+ default_stages: [pre-commit, manual]
3
+
4
+ repos:
5
+ - repo: https://github.com/pre-commit/pre-commit-hooks.git
6
+ rev: v6.0.0
7
+ hooks:
8
+ - id: check-added-large-files
9
+ args: [--maxkb=500]
10
+ - id: check-case-conflict
11
+ - id: check-merge-conflict
12
+ - id: check-symlinks
13
+ - id: check-toml
14
+ - id: check-yaml
15
+ args: [--unsafe] # mkdocs.yml uses custom tags
16
+ - id: end-of-file-fixer
17
+ - id: mixed-line-ending
18
+ args: [--fix=lf]
19
+ - id: trailing-whitespace
20
+ args: [--markdown-linebreak-ext=md]
21
+ - id: detect-private-key
22
+
23
+ - repo: https://github.com/codespell-project/codespell
24
+ rev: v2.4.2
25
+ hooks:
26
+ - id: codespell
27
+ args: [--ignore-words=.codespellignore, --quiet-level=2]
28
+
29
+ # Quality gates mirror the nox sessions and run in isolated uv envs, so
30
+ # versions come from the pyproject extras rather than host tools.
31
+ - repo: local
32
+ hooks:
33
+ - id: ruff-check
34
+ name: ruff check
35
+ entry: uv run --isolated --extra lint ruff check --fix
36
+ language: system
37
+ types: [python]
38
+ - id: ruff-format
39
+ name: ruff format
40
+ entry: uv run --isolated --extra lint ruff format
41
+ language: system
42
+ types: [python]
43
+ - id: mypy
44
+ name: mypy
45
+ entry: uv run --isolated --extra type-check mypy
46
+ language: system
47
+ pass_filenames: false
48
+ args: [--package, narratty]
49
+
50
+ - repo: https://github.com/commitizen-tools/commitizen
51
+ rev: v4.16.4
52
+ hooks:
53
+ - id: commitizen
54
+ stages: [commit-msg]
@@ -0,0 +1,29 @@
1
+ # AGENTS.md
2
+
3
+ `narratty` is a Python CLI (command and import package `narratty`) that turns a
4
+ `.narratty.yaml` spec into a narrated terminal video. The design and milestones live in
5
+ `docs/contributor-guide/design.md`; read it before adding features.
6
+
7
+ ## Tooling
8
+
9
+ - **uv** manages the environment (`uv sync --extra dev` creates `.venv`), **nox** runs the
10
+ quality gates. `noxfile.py` is the authoritative command list.
11
+ - Lint and format: ruff only (`nox -s lint`, `nox -s format`). Types: mypy (`nox -s check_types`).
12
+ - Unit tests: `nox -s tests` or `.venv/bin/pytest`. Performance budgets are marked
13
+ `performance` and deselected by default; run them with `nox -s performance`.
14
+ - Docs: `nox -s docs` (mkdocs `--strict`, zero warnings).
15
+
16
+ ## Conventions
17
+
18
+ - One module per subcommand under `src/narratty/cli/commands/`. Import heavy dependencies
19
+ inside the command function so `--help` and shell completion stay under their budgets.
20
+ - Errors are `NarrattyError` subclasses carrying an `ExitCode` and an optional `hint`;
21
+ never renumber `ExitCode`.
22
+ - Data goes to stdout, logs and errors to stderr (`narratty.ui.console`).
23
+ - Commits and PR titles follow Conventional Commits.
24
+
25
+ ## Gotchas
26
+
27
+ - The version comes from git tags via hatch-vcs; shallow clones without tags report
28
+ `0.1.devN`. CI checks out with `fetch-depth: 0`.
29
+ - `nox` without `NOX_CI=1` runs `format` first, which edits files.
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Conventional Commits](https://www.conventionalcommits.org/) and is maintained by
5
+ commitizen.
6
+
7
+ ## v0.1.0 (2026-09-30)
8
+
9
+ ### Feat
10
+
11
+ - **end-card**: close videos with a "Created with narratty" card and QR code
12
+ - **sandbox**: add workspace modes, network allowlist and sandbox consent
13
+ - **container**: add the narratty image, GHCR publishing and a container runtime
14
+ - **build**: render narrated videos with VHS and ffmpeg
15
+ - **tts**: add Piper and Kokoro providers with voice catalog and audio cache
16
+ - **init**: point the schema header at the installed version's schema
17
+ - **spec**: add spec format v1 with init, validate and schema commands
18
+ - project scaffold with doctor command and shell completion
19
+
20
+ ### Fix
21
+
22
+ - **container**: install ttyd from its release binary