orbix 2.0.0__tar.gz → 3.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 (85) hide show
  1. orbix-3.0.0/.codespellrc +4 -0
  2. orbix-3.0.0/.editorconfig +15 -0
  3. orbix-3.0.0/.gitattributes +10 -0
  4. orbix-3.0.0/.github/dependabot.yml +30 -0
  5. orbix-3.0.0/.github/scripts/ci_summary.py +77 -0
  6. orbix-3.0.0/.github/sibling-pins.txt +5 -0
  7. orbix-3.0.0/.github/workflows/ci.yml +208 -0
  8. {orbix-2.0.0 → orbix-3.0.0}/.github/workflows/docs.yml +11 -4
  9. orbix-3.0.0/.github/workflows/hygiene.yml +40 -0
  10. {orbix-2.0.0 → orbix-3.0.0}/.github/workflows/publish-to-pypi.yml +14 -7
  11. {orbix-2.0.0 → orbix-3.0.0}/.github/workflows/release-please.yml +5 -3
  12. orbix-3.0.0/.github/workflows/upstream.yml +146 -0
  13. orbix-3.0.0/.github/zizmor.yml +6 -0
  14. {orbix-2.0.0 → orbix-3.0.0}/.gitignore +3 -0
  15. {orbix-2.0.0 → orbix-3.0.0}/.pre-commit-config.yaml +5 -1
  16. {orbix-2.0.0 → orbix-3.0.0}/CHANGELOG.md +52 -1
  17. {orbix-2.0.0 → orbix-3.0.0}/PKG-INFO +39 -12
  18. {orbix-2.0.0 → orbix-3.0.0}/README.md +11 -0
  19. orbix-3.0.0/SECURITY.md +9 -0
  20. {orbix-2.0.0 → orbix-3.0.0}/docs/conf.py +3 -0
  21. {orbix-2.0.0 → orbix-3.0.0}/docs/examples/orbits.md +9 -1
  22. {orbix-2.0.0 → orbix-3.0.0}/docs/examples/prepared.md +3 -1
  23. {orbix-2.0.0 → orbix-3.0.0}/docs/gallery.md +6 -0
  24. orbix-3.0.0/docs/index.md +70 -0
  25. orbix-3.0.0/pyproject.toml +119 -0
  26. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/_version.py +2 -2
  27. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/equations/__init__.py +1 -1
  28. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/equations/lambert.py +7 -5
  29. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/equations/orbit.py +55 -13
  30. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/equations/propagation.py +37 -19
  31. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/kepler/core.py +33 -22
  32. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/kepler/shortcuts/fixed_e.py +4 -4
  33. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/kepler/shortcuts/grid.py +26 -30
  34. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/observatory/orbit.py +2 -2
  35. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/orbit.py +42 -23
  36. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/viz/_prepare.py +3 -2
  37. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/viz/anim.py +5 -0
  38. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/viz/orbit.py +19 -3
  39. orbix-3.0.0/tests/conftest.py +18 -0
  40. orbix-3.0.0/tests/test_dependency_floors.py +34 -0
  41. {orbix-2.0.0 → orbix-3.0.0}/tests/test_equations_grads.py +9 -4
  42. orbix-3.0.0/tests/test_fixed_e_solvers.py +85 -0
  43. orbix-3.0.0/tests/test_grid_solvers.py +160 -0
  44. orbix-3.0.0/tests/test_kepler_core.py +159 -0
  45. {orbix-2.0.0 → orbix-3.0.0}/tests/test_kepler_markley.py +62 -20
  46. {orbix-2.0.0 → orbix-3.0.0}/tests/test_lambert.py +1 -1
  47. {orbix-2.0.0 → orbix-3.0.0}/tests/test_observatory.py +68 -9
  48. {orbix-2.0.0 → orbix-3.0.0}/tests/test_orbit.py +162 -85
  49. orbix-3.0.0/tests/test_sky_convention.py +485 -0
  50. {orbix-2.0.0 → orbix-3.0.0}/tests/test_viz_anim.py +12 -3
  51. {orbix-2.0.0 → orbix-3.0.0}/tests/test_viz_orbit.py +43 -1
  52. {orbix-2.0.0 → orbix-3.0.0}/tests/test_viz_package.py +1 -1
  53. {orbix-2.0.0 → orbix-3.0.0}/tests/test_viz_prepared.py +20 -17
  54. orbix-2.0.0/.github/dependabot.yml +0 -6
  55. orbix-2.0.0/.github/workflows/tests.yml +0 -32
  56. orbix-2.0.0/docs/index.md +0 -44
  57. orbix-2.0.0/pyproject.toml +0 -66
  58. orbix-2.0.0/tests/conftest.py +0 -5
  59. orbix-2.0.0/tests/test_fixed_e_solvers.py +0 -32
  60. orbix-2.0.0/tests/test_grid_solvers.py +0 -55
  61. orbix-2.0.0/tests/test_kepler_core.py +0 -103
  62. {orbix-2.0.0 → orbix-3.0.0}/.python-version +0 -0
  63. {orbix-2.0.0 → orbix-3.0.0}/.readthedocs.yaml +0 -0
  64. {orbix-2.0.0 → orbix-3.0.0}/.zenodo.json +0 -0
  65. {orbix-2.0.0 → orbix-3.0.0}/CITATION.cff +0 -0
  66. {orbix-2.0.0 → orbix-3.0.0}/LICENSE +0 -0
  67. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/__init__.py +0 -0
  68. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/equations/phase.py +0 -0
  69. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/kepler/__init__.py +0 -0
  70. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/kepler/markley.py +0 -0
  71. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/kepler/shortcuts/__init__.py +0 -0
  72. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/kepler/shortcuts/guess.py +0 -0
  73. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/observatory/__init__.py +0 -0
  74. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/observatory/data/L2_halo_orbit_six_month.npz +0 -0
  75. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/observatory/keepout.py +0 -0
  76. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/observatory/observatory.py +0 -0
  77. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/observatory/solar_system.py +0 -0
  78. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/py.typed +0 -0
  79. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/viz/__init__.py +0 -0
  80. {orbix-2.0.0 → orbix-3.0.0}/src/orbix/viz/_require.py +0 -0
  81. {orbix-2.0.0 → orbix-3.0.0}/tests/test_internal_refs_guard.py +0 -0
  82. {orbix-2.0.0 → orbix-3.0.0}/tests/test_kepler_grads.py +0 -0
  83. {orbix-2.0.0 → orbix-3.0.0}/tests/test_observatory_wrapper.py +0 -0
  84. {orbix-2.0.0 → orbix-3.0.0}/tests/test_vs_exosims.py +0 -0
  85. {orbix-2.0.0 → orbix-3.0.0}/tools/check_internal_refs.py +0 -0
@@ -0,0 +1,4 @@
1
+ [codespell]
2
+ skip = .git,*.ipynb,uv.lock,*/_build,*.fits
3
+ # add domain words only if codespell flags them (clean on the libraries sampled)
4
+ # ignore-words-list =
@@ -0,0 +1,15 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+ indent_style = space
9
+ indent_size = 4
10
+
11
+ [*.{yml,yaml,json,toml}]
12
+ indent_size = 2
13
+
14
+ [Makefile]
15
+ indent_style = tab
@@ -0,0 +1,10 @@
1
+ * text=auto eol=lf
2
+ *.ipynb text eol=lf linguist-documentation
3
+ docs/** linguist-documentation
4
+ *.fits binary
5
+ *.npy binary
6
+ *.npz binary
7
+ *.png binary
8
+ *.pdf binary
9
+ uv.lock linguist-generated -diff
10
+ CHANGELOG.md linguist-generated
@@ -0,0 +1,30 @@
1
+ # Drop-in for every owned library. One config, low noise: one grouped pull request per month,
2
+ # new releases wait a week (cooldown) so a yanked or compromised release is seen before it lands.
3
+ # Security updates ignore the cooldown and are configured separately in repository settings.
4
+ version: 2
5
+ updates:
6
+ - package-ecosystem: "github-actions"
7
+ directory: "/"
8
+ schedule:
9
+ interval: "monthly"
10
+ cooldown:
11
+ default-days: 7
12
+ groups:
13
+ actions:
14
+ patterns: ["*"]
15
+ commit-message:
16
+ prefix: "ci" # conventional commit type that release-please ignores
17
+
18
+ # Only for repos with a committed uv.lock or a pre-commit config worth bumping.
19
+ # The libraries pin no lockfile, so the uv ecosystem is deliberately absent here.
20
+ - package-ecosystem: "pre-commit"
21
+ directory: "/"
22
+ schedule:
23
+ interval: "monthly"
24
+ cooldown:
25
+ default-days: 7
26
+ groups:
27
+ hooks:
28
+ patterns: ["*"]
29
+ commit-message:
30
+ prefix: "ci"
@@ -0,0 +1,77 @@
1
+ """Write a job summary from a pytest junit file and enforce a suite time budget.
2
+
3
+ Usage: python ci_summary.py junit.xml [--title TITLE] [--budget-seconds N]
4
+
5
+ Appends markdown to $GITHUB_STEP_SUMMARY (stdout when unset). Exits non-zero when the
6
+ summed test time exceeds the budget, or when any test was skipped (a skip hides a
7
+ missing dependency).
8
+ """
9
+
10
+ import argparse
11
+ import os
12
+ import sys
13
+ import xml.etree.ElementTree as ET
14
+
15
+
16
+ def main():
17
+ """Summarize collected tests and check the requested budget and skip policy."""
18
+ parser = argparse.ArgumentParser()
19
+ parser.add_argument("junit")
20
+ parser.add_argument("--title", default="Tests")
21
+ parser.add_argument("--budget-seconds", type=float, default=0.0)
22
+ parser.add_argument("--allow-skips", action="store_true")
23
+ args = parser.parse_args()
24
+
25
+ if not os.path.exists(args.junit):
26
+ print(f"{args.junit} not found: the test step did not run")
27
+ return 1
28
+ root = ET.parse(args.junit).getroot()
29
+ cases = []
30
+ skipped = []
31
+ for case in root.iter("testcase"):
32
+ name = f"{case.get('classname', '')}::{case.get('name', '')}"
33
+ seconds = float(case.get("time", 0.0))
34
+ cases.append((seconds, name))
35
+ # Pytest records expected failures as skipped; pytest enforces strict xfails.
36
+ skip = case.find("skipped")
37
+ if skip is not None and skip.get("type") != "pytest.xfail":
38
+ skipped.append(name)
39
+ total = sum(s for s, _ in cases)
40
+ cases.sort(reverse=True)
41
+
42
+ lines = [
43
+ f"### {args.title}",
44
+ "",
45
+ f"- tests: {len(cases)}, skipped: {len(skipped)}",
46
+ f"- summed test time: {total:.1f} s"
47
+ + (f" (budget {args.budget_seconds:.0f} s)" if args.budget_seconds else ""),
48
+ "",
49
+ "| slowest tests | seconds |",
50
+ "|---|---|",
51
+ ]
52
+ lines += [f"| `{name}` | {seconds:.2f} |" for seconds, name in cases[:10]]
53
+ if skipped:
54
+ policy = "allowed" if args.allow_skips else "treated as a failure"
55
+ lines += ["", f"Skipped ({policy}):"]
56
+ lines += [f"- `{name}`" for name in skipped[:20]]
57
+
58
+ text = "\n".join(lines) + "\n"
59
+ target = os.environ.get("GITHUB_STEP_SUMMARY")
60
+ if target:
61
+ with open(target, "a", encoding="utf-8") as handle:
62
+ handle.write(text)
63
+ else:
64
+ print(text)
65
+
66
+ failed = False
67
+ if args.budget_seconds and total > args.budget_seconds:
68
+ print(f"suite time {total:.1f} s exceeds budget {args.budget_seconds:.0f} s")
69
+ failed = True
70
+ if skipped and not args.allow_skips:
71
+ print(f"{len(skipped)} tests were skipped")
72
+ failed = True
73
+ return 1 if failed else 0
74
+
75
+
76
+ if __name__ == "__main__":
77
+ sys.exit(main())
@@ -0,0 +1,5 @@
1
+ # Unreleased sibling libraries, applied as uv overrides so each is installed at a pinned main
2
+ # commit whatever version bounds other packages declare (a breaking change waiting for its
3
+ # release tag reports the old major version). Move a pin deliberately; drop it once PyPI covers it.
4
+ hwoutils @ git+https://github.com/CoreySpohn/hwoutils@20f6973e39954321feba7efcf781fc217016a46f
5
+ eyepiece @ git+https://github.com/CoreySpohn/eyepiece@c7a4bea5a34b8751b4791bf74ea35bcd9da54cb1
@@ -0,0 +1,208 @@
1
+ name: CI
2
+
3
+ # Replaces tests.yml. Quick tier on every push and pull request, docs with executed
4
+ # examples, and the slow tier on a nightly schedule. No secrets are used: the default
5
+ # GITHUB_TOKEN with read access is enough because every sibling library is public.
6
+
7
+ on:
8
+ push:
9
+ branches: [ main ]
10
+ pull_request:
11
+ branches: [ main ]
12
+ schedule:
13
+ - cron: "17 7 * * *" # nightly, 07:17 UTC (default branch only)
14
+ workflow_dispatch:
15
+ inputs:
16
+ tier:
17
+ description: "Test selection for a manual run"
18
+ default: "not slow"
19
+ type: choice
20
+ options: [ "not slow", "slow", "all" ]
21
+
22
+ permissions:
23
+ contents: read
24
+
25
+ concurrency:
26
+ group: ci-${{ github.workflow }}-${{ github.ref }}
27
+ cancel-in-progress: ${{ github.event_name != 'schedule' }}
28
+
29
+ env:
30
+ JAX_PLATFORMS: cpu
31
+ EYEPIECE_REQUIRE_MANIM: "1"
32
+ # Batched programs can stall on 2-vCPU runners with the default Eigen threading.
33
+ XLA_FLAGS: --xla_cpu_multi_thread_eigen=false intra_op_parallelism_threads=1
34
+ JAX_COMPILATION_CACHE_DIR: ${{ github.workspace }}/.jax_cache
35
+ JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS: "1"
36
+ PYTHONHASHSEED: "0"
37
+
38
+ jobs:
39
+ quick:
40
+ name: Quick tier (py${{ matrix.python-version }})
41
+ if: github.event_name != 'schedule' && (github.event_name != 'workflow_dispatch' || inputs.tier != 'slow')
42
+ runs-on: ubuntu-latest
43
+ timeout-minutes: 20
44
+ strategy:
45
+ fail-fast: false
46
+ matrix:
47
+ python-version: ["3.12"]
48
+ steps:
49
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
50
+ with:
51
+ persist-credentials: false
52
+
53
+ - uses: astral-sh/setup-uv@eb1897b8dc4b5d5bfe39a428a8f2304605e0983c # v7.0.0
54
+ with:
55
+ python-version: ${{ matrix.python-version }}
56
+ enable-cache: true
57
+ cache-dependency-glob: "pyproject.toml"
58
+
59
+ - name: Restore the JAX compile cache
60
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
61
+ with:
62
+ path: .jax_cache
63
+ key: jax-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}-${{ github.sha }}
64
+ restore-keys: |
65
+ jax-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}-
66
+ jax-${{ runner.os }}-py${{ matrix.python-version }}-
67
+
68
+ # Manim's Python extensions need Cairo and Pango headers at install time.
69
+ - name: Install renderer build dependencies
70
+ run: |
71
+ sudo apt-get update
72
+ sudo apt-get install --no-install-recommends -y libcairo2-dev libpango1.0-dev pkg-config
73
+
74
+ - name: Install the package and test tools
75
+ run: |
76
+ uv venv .venv --python ${{ matrix.python-version }}
77
+ echo "VIRTUAL_ENV=$PWD/.venv" >> "$GITHUB_ENV"
78
+ echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
79
+ # One resolve, so pinned siblings satisfy the package floors.
80
+ uv pip install --python .venv/bin/python -e ".[test]" --override .github/sibling-pins.txt ruff==0.15.18 deptry ty==0.0.96
81
+
82
+ - name: Lint and formatting
83
+ run: |
84
+ ruff check src/orbix tests
85
+ ruff format --check src/orbix tests
86
+
87
+ - name: Check dependencies
88
+ run: deptry src/orbix
89
+
90
+ - name: Check types (advisory)
91
+ continue-on-error: true
92
+ run: ty check src/orbix
93
+
94
+ - name: Quick tier
95
+ run: |
96
+ pytest tests/ -m "not slow" -ra --tb=short \
97
+ --timeout=300 --fail-slow=30s \
98
+ --durations=15 --durations-min=1.0 \
99
+ --junitxml=junit-quick.xml
100
+
101
+ - name: Job summary
102
+ if: always()
103
+ run: python .github/scripts/ci_summary.py junit-quick.xml --title "Quick tier" --budget-seconds 600
104
+
105
+ docs:
106
+ name: Docs with executed examples
107
+ if: github.event_name != 'schedule' && (github.event_name != 'workflow_dispatch' || inputs.tier != 'slow')
108
+ runs-on: ubuntu-latest
109
+ timeout-minutes: 20
110
+ steps:
111
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
112
+ with:
113
+ persist-credentials: false
114
+ fetch-depth: 0 # hatch-vcs needs tags for the docs version
115
+
116
+ - uses: astral-sh/setup-uv@eb1897b8dc4b5d5bfe39a428a8f2304605e0983c # v7.0.0
117
+ with:
118
+ python-version: "3.12"
119
+ enable-cache: true
120
+ cache-dependency-glob: "pyproject.toml"
121
+
122
+ - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
123
+ with:
124
+ path: .jax_cache
125
+ key: jax-docs-${{ hashFiles('pyproject.toml') }}-${{ github.sha }}
126
+ restore-keys: jax-docs-${{ hashFiles('pyproject.toml') }}-
127
+
128
+ # Manim's Python extensions need Cairo and Pango headers at install time.
129
+ - name: Install renderer build dependencies
130
+ run: |
131
+ sudo apt-get update
132
+ sudo apt-get install --no-install-recommends -y libcairo2-dev libpango1.0-dev pkg-config
133
+
134
+ - name: Install docs dependencies
135
+ run: |
136
+ uv venv .venv --python 3.12
137
+ echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
138
+ # One resolve, so pinned siblings satisfy the package floors.
139
+ uv pip install --python .venv/bin/python -e ".[docs,test]" --override .github/sibling-pins.txt
140
+
141
+ # Every Python fence in README.md and docs runs as a test. A block that cannot run on a
142
+ # runner carries the info string `python notest` and a one-line reason above it.
143
+ - name: Execute fenced code blocks
144
+ run: |
145
+ pytest README.md docs --markdown-docs --markdown-docs-syntax=superfences \
146
+ -p no:cacheprovider --timeout=120 -ra --tb=short \
147
+ --junitxml=junit-docs.xml
148
+
149
+ # Executes {code-cell} blocks and notebooks, and fails on any Sphinx warning.
150
+ - name: Build docs with execution forced
151
+ run: |
152
+ sphinx-build -W --keep-going -b html \
153
+ -D nb_execution_mode=force -D nb_execution_raise_on_error=1 \
154
+ docs docs/_build/html
155
+
156
+ - name: Job summary
157
+ if: always()
158
+ run: python .github/scripts/ci_summary.py junit-docs.xml --title "Docs examples" --budget-seconds 300 --allow-skips
159
+
160
+ slow:
161
+ name: Slow tier
162
+ if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.tier != 'not slow')
163
+ runs-on: ubuntu-latest
164
+ timeout-minutes: 90
165
+ steps:
166
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
167
+ with:
168
+ persist-credentials: false
169
+
170
+ - uses: astral-sh/setup-uv@eb1897b8dc4b5d5bfe39a428a8f2304605e0983c # v7.0.0
171
+ with:
172
+ python-version: "3.12"
173
+ enable-cache: true
174
+ cache-dependency-glob: "pyproject.toml"
175
+
176
+ - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
177
+ with:
178
+ path: .jax_cache
179
+ key: jax-slow-${{ hashFiles('pyproject.toml') }}-${{ github.sha }}
180
+ restore-keys: jax-slow-${{ hashFiles('pyproject.toml') }}-
181
+
182
+ # Manim's Python extensions need Cairo and Pango headers at install time.
183
+ - name: Install renderer build dependencies
184
+ run: |
185
+ sudo apt-get update
186
+ sudo apt-get install --no-install-recommends -y libcairo2-dev libpango1.0-dev pkg-config
187
+
188
+ - name: Install the package and test tools
189
+ run: |
190
+ uv venv .venv --python 3.12
191
+ echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
192
+ # One resolve, so pinned siblings satisfy the package floors.
193
+ uv pip install --python .venv/bin/python -e ".[test]" --override .github/sibling-pins.txt
194
+
195
+ - name: Slow tier (hypothesis nightly profile)
196
+ env:
197
+ HYPOTHESIS_PROFILE: nightly
198
+ run: |
199
+ status=0
200
+ pytest tests/ -m slow -ra --tb=short --timeout=900 \
201
+ --durations=25 --junitxml=junit-slow.xml || status=$?
202
+ if [ "$status" -ne 0 ] && [ "$status" -ne 5 ]; then
203
+ exit "$status"
204
+ fi
205
+
206
+ - name: Job summary
207
+ if: always()
208
+ run: python .github/scripts/ci_summary.py junit-slow.xml --title "Slow tier" --allow-skips
@@ -7,15 +7,22 @@ on:
7
7
  branches: [ main ]
8
8
  workflow_dispatch:
9
9
 
10
+ permissions:
11
+ contents: read
12
+
10
13
  jobs:
11
14
  docs:
12
15
  name: Build docs
13
16
  runs-on: ubuntu-latest
17
+ timeout-minutes: 20
14
18
  steps:
15
- - uses: actions/checkout@v7
19
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
20
+ with:
21
+ persist-credentials: false
22
+ fetch-depth: 0
16
23
 
17
24
  - name: Install uv
18
- uses: astral-sh/setup-uv@v7
25
+ uses: astral-sh/setup-uv@eb1897b8dc4b5d5bfe39a428a8f2304605e0983c # v7.0.0
19
26
  with:
20
27
  enable-cache: true
21
28
 
@@ -23,7 +30,7 @@ jobs:
23
30
  run: uv venv .venv --python 3.12
24
31
 
25
32
  - name: Install package with docs dependencies
26
- run: uv pip install -e ".[docs]"
33
+ run: uv pip install --python .venv/bin/python -e ".[docs]" --override .github/sibling-pins.txt
27
34
  env:
28
35
  VIRTUAL_ENV: ${{ github.workspace }}/.venv
29
36
 
@@ -31,4 +38,4 @@ jobs:
31
38
  # nb_execution_raise_on_error, so a cell that stops working fails here
32
39
  # rather than on the documentation host.
33
40
  - name: Build the documentation
34
- run: .venv/bin/sphinx-build -b html docs docs/_build/html
41
+ run: .venv/bin/sphinx-build -W --keep-going -b html -D nb_execution_mode=force -D nb_execution_raise_on_error=1 docs docs/_build/html
@@ -0,0 +1,40 @@
1
+ name: hygiene
2
+
3
+ # Checks that do not depend on JAX: workflow security, spelling, and hooks.
4
+ on:
5
+ push:
6
+ branches: [main]
7
+ pull_request:
8
+ branches: [main]
9
+ schedule:
10
+ - cron: "41 6 * * 1" # weekly, so the repo has activity and new lint rules are noticed
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ concurrency:
17
+ group: hygiene-${{ github.ref }}
18
+ cancel-in-progress: true
19
+
20
+ jobs:
21
+ hygiene:
22
+ runs-on: ubuntu-latest
23
+ timeout-minutes: 10
24
+ steps:
25
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
26
+ with:
27
+ persist-credentials: false
28
+
29
+ - uses: astral-sh/setup-uv@eb1897b8dc4b5d5bfe39a428a8f2304605e0983c # v7.0.0
30
+
31
+ - name: Lint workflows (zizmor)
32
+ run: uvx zizmor --format=github --min-severity=medium .github
33
+
34
+ - name: Spelling (codespell)
35
+ run: uvx codespell --check-hidden
36
+
37
+ - name: Pre-commit hooks
38
+ run: uvx pre-commit run --all-files --show-diff-on-failure
39
+ env:
40
+ SKIP: conventional-pre-commit # only runs with a commit message
@@ -1,6 +1,11 @@
1
1
  name: Publish to PyPI
2
2
 
3
- on: push
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ contents: read
4
9
 
5
10
  jobs:
6
11
  build:
@@ -8,9 +13,11 @@ jobs:
8
13
  runs-on: ubuntu-latest
9
14
 
10
15
  steps:
11
- - uses: actions/checkout@v7
16
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
17
+ with:
18
+ persist-credentials: false
12
19
  - name: Set up Python
13
- uses: actions/setup-python@v7
20
+ uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
14
21
  with:
15
22
  python-version: "3.x"
16
23
  - name: Install pypa/build
@@ -22,7 +29,7 @@ jobs:
22
29
  - name: Build a binary wheel and a source tarball
23
30
  run: python3 -m build
24
31
  - name: Store the distribution packages
25
- uses: actions/upload-artifact@v7
32
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
26
33
  with:
27
34
  name: python-package-distributions
28
35
  path: dist/
@@ -30,7 +37,7 @@ jobs:
30
37
  publish-to-pypi:
31
38
  name: >-
32
39
  Publish Python distribution to PyPI
33
- if: startsWith(github.ref, 'refs/tags/')
40
+ if: startsWith(github.ref, 'refs/tags/v')
34
41
  needs:
35
42
  - build
36
43
  runs-on: ubuntu-latest
@@ -42,9 +49,9 @@ jobs:
42
49
 
43
50
  steps:
44
51
  - name: Download all the dists
45
- uses: actions/download-artifact@v8
52
+ uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
46
53
  with:
47
54
  name: python-package-distributions
48
55
  path: dist/
49
56
  - name: Publish distribution to PyPI
50
- uses: pypa/gh-action-pypi-publish@release/v1
57
+ uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
@@ -4,16 +4,18 @@ on:
4
4
  - main
5
5
 
6
6
  permissions:
7
- contents: write
8
- pull-requests: write
7
+ contents: read
9
8
 
10
9
  name: release-please
11
10
 
12
11
  jobs:
13
12
  release-please:
14
13
  runs-on: ubuntu-latest
14
+ permissions:
15
+ contents: write
16
+ pull-requests: write
15
17
  steps:
16
- - uses: googleapis/release-please-action@v5
18
+ - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4.4.0
17
19
  with:
18
20
  # this assumes that you have created a personal access token
19
21
  # (PAT) and configured it as a GitHub action secret named
@@ -0,0 +1,146 @@
1
+ name: Upstream canary
2
+
3
+ # Exercise upstream releases before a relock, and the declared dependency floors.
4
+ on:
5
+ schedule:
6
+ - cron: "23 5 * * 1,4" # Monday and Thursday, 05:23 UTC
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ci-${{ github.workflow }}-${{ github.ref }}
14
+ cancel-in-progress: ${{ github.event_name != 'schedule' }}
15
+
16
+ env:
17
+ JAX_PLATFORMS: cpu
18
+ EYEPIECE_REQUIRE_MANIM: "1"
19
+ # Batched programs can stall on 2-vCPU runners with the default Eigen threading.
20
+ XLA_FLAGS: --xla_cpu_multi_thread_eigen=false intra_op_parallelism_threads=1
21
+ JAX_COMPILATION_CACHE_DIR: ${{ github.workspace }}/.jax_cache
22
+ JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS: "1"
23
+ PYTHONHASHSEED: "0"
24
+
25
+ jobs:
26
+ upstream-dev:
27
+ name: Upstream pre-releases (py3.12)
28
+ runs-on: ubuntu-latest
29
+ timeout-minutes: 30
30
+ steps:
31
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
32
+ with:
33
+ persist-credentials: false
34
+
35
+ - uses: astral-sh/setup-uv@eb1897b8dc4b5d5bfe39a428a8f2304605e0983c # v7.0.0
36
+ with:
37
+ python-version: "3.12"
38
+ enable-cache: true
39
+ cache-dependency-glob: "pyproject.toml"
40
+
41
+ # Manim's Python extensions need Cairo and Pango headers at install time.
42
+ - name: Install renderer build dependencies
43
+ run: |
44
+ sudo apt-get update
45
+ sudo apt-get install --no-install-recommends -y libcairo2-dev libpango1.0-dev pkg-config
46
+
47
+ - name: Install the package and upstream pre-releases
48
+ run: |
49
+ uv venv .venv --python 3.12
50
+ echo "VIRTUAL_ENV=$PWD/.venv" >> "$GITHUB_ENV"
51
+ echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
52
+ uv pip install --python .venv/bin/python -e ".[test]" --override .github/sibling-pins.txt
53
+ uv pip install --python .venv/bin/python --upgrade --prerelease=allow \
54
+ jax jaxlib numpy scipy astropy --override .github/sibling-pins.txt
55
+ # Nightly-only index verified against the JAX installation docs:
56
+ # https://docs.jax.dev/en/latest/installation.html#jax-nightly-installation
57
+ uv pip install --python .venv/bin/python --upgrade --prerelease=allow \
58
+ --index https://us-python.pkg.dev/ml-oss-artifacts-published/jax-public-nightly-artifacts-registry/simple/ \
59
+ jax jaxlib --override .github/sibling-pins.txt
60
+ uv pip freeze --python .venv/bin/python
61
+
62
+ - name: Quick tier with warnings as errors
63
+ run: |
64
+ for test_file in tests/test_*.py; do
65
+ pytest "$test_file" -m "not slow" -q -p no:cacheprovider --timeout=300
66
+ done
67
+
68
+ lowest-direct:
69
+ name: Lowest direct dependencies (py3.12)
70
+ runs-on: ubuntu-latest
71
+ timeout-minutes: 30
72
+ steps:
73
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
74
+ with:
75
+ persist-credentials: false
76
+
77
+ - uses: astral-sh/setup-uv@eb1897b8dc4b5d5bfe39a428a8f2304605e0983c # v7.0.0
78
+ with:
79
+ python-version: "3.12"
80
+ enable-cache: true
81
+ cache-dependency-glob: "pyproject.toml"
82
+
83
+ # Manim's Python extensions need Cairo and Pango headers at install time.
84
+ - name: Install renderer build dependencies
85
+ run: |
86
+ sudo apt-get update
87
+ sudo apt-get install --no-install-recommends -y libcairo2-dev libpango1.0-dev pkg-config
88
+
89
+ - name: Install the lowest direct dependencies
90
+ run: |
91
+ uv venv .venv --python 3.12
92
+ echo "VIRTUAL_ENV=$PWD/.venv" >> "$GITHUB_ENV"
93
+ echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
94
+ uv pip install --python .venv/bin/python --resolution lowest-direct -e ".[docs,test]" --override .github/sibling-pins.txt
95
+ uv pip freeze --python .venv/bin/python
96
+
97
+ - name: Quick tier
98
+ run: |
99
+ for test_file in tests/test_*.py; do
100
+ pytest "$test_file" -m "not slow" -q -p no:cacheprovider --timeout=300
101
+ done
102
+
103
+ report:
104
+ name: Update the tracking issue
105
+ needs: [upstream-dev, lowest-direct]
106
+ if: always()
107
+ runs-on: ubuntu-latest
108
+ timeout-minutes: 5
109
+ # Manual runs on other branches share the same tracking issue.
110
+ concurrency:
111
+ group: upstream-canary-report
112
+ cancel-in-progress: false
113
+ permissions:
114
+ issues: write # This job alone maintains the canary tracking issue.
115
+ steps:
116
+ - name: Open, update or close the tracking issue
117
+ env:
118
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
119
+ GH_REPO: ${{ github.repository }}
120
+ UPSTREAM_RESULT: ${{ needs.upstream-dev.result }}
121
+ LOWEST_RESULT: ${{ needs.lowest-direct.result }}
122
+ RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
123
+ run: |
124
+ title="Upstream canary failing"
125
+ # Match the exact title and reuse closed issues on a later regression.
126
+ issue=$(gh issue list --state all --search "\"$title\" in:title" --limit 100 \
127
+ --json number,title,state --jq 'map(select(.title == "Upstream canary failing")) | first // empty')
128
+ number=$(jq -r '.number // empty' <<< "$issue")
129
+ state=$(jq -r '.state // empty' <<< "$issue")
130
+ body=$(printf 'Upstream pre-releases: %s\nLowest direct dependencies: %s\n\nLatest run: %s\n' \
131
+ "$UPSTREAM_RESULT" "$LOWEST_RESULT" "$RUN_URL")
132
+ if [ "$UPSTREAM_RESULT" = "failure" ] || [ "$LOWEST_RESULT" = "failure" ]; then
133
+ if [ -n "$number" ]; then
134
+ gh issue edit "$number" --body "$body"
135
+ if [ "$state" = "CLOSED" ]; then
136
+ gh issue reopen "$number"
137
+ fi
138
+ else
139
+ gh issue create --title "$title" --body "$body"
140
+ fi
141
+ elif [ "$UPSTREAM_RESULT" = "success" ] && [ "$LOWEST_RESULT" = "success" ]; then
142
+ if [ -n "$number" ] && [ "$state" = "OPEN" ]; then
143
+ gh issue edit "$number" --body "$body"
144
+ gh issue close "$number" --comment "Both canary jobs are passing: $RUN_URL"
145
+ fi
146
+ fi
@@ -0,0 +1,6 @@
1
+ # Require immutable action references, including first-party actions.
2
+ rules:
3
+ unpinned-uses:
4
+ config:
5
+ policies:
6
+ "*": hash-pin
@@ -164,3 +164,6 @@ cython_debug/
164
164
  input/
165
165
  output/
166
166
  *.png
167
+ .claude/
168
+ .jax_cache/
169
+ .ruff_cache/