pointclouds 0.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 (77) hide show
  1. pointclouds-0.0.0/.gitattributes +20 -0
  2. pointclouds-0.0.0/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md +18 -0
  3. pointclouds-0.0.0/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  4. pointclouds-0.0.0/.github/ISSUE_TEMPLATE/custom.md +8 -0
  5. pointclouds-0.0.0/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
  6. pointclouds-0.0.0/.github/gitleaks.toml +21 -0
  7. pointclouds-0.0.0/.github/pull_request_template.md +43 -0
  8. pointclouds-0.0.0/.github/workflows/github-pages-mkdocs.yml +91 -0
  9. pointclouds-0.0.0/.github/workflows/github-release.yml +97 -0
  10. pointclouds-0.0.0/.github/workflows/lint.yml +106 -0
  11. pointclouds-0.0.0/.github/workflows/pip-audit.yml +80 -0
  12. pointclouds-0.0.0/.github/workflows/pypi-release.yml +55 -0
  13. pointclouds-0.0.0/.github/workflows/tests.yml +98 -0
  14. pointclouds-0.0.0/.gitignore +42 -0
  15. pointclouds-0.0.0/.pre-commit-config.yaml +47 -0
  16. pointclouds-0.0.0/LICENSE.md +675 -0
  17. pointclouds-0.0.0/PKG-INFO +95 -0
  18. pointclouds-0.0.0/README.md +55 -0
  19. pointclouds-0.0.0/docs/AUTHORS.md +3 -0
  20. pointclouds-0.0.0/docs/CODE_OF_CONDUCT.md +3 -0
  21. pointclouds-0.0.0/docs/adr/0001-separate-distribution.md +24 -0
  22. pointclouds-0.0.0/docs/adr/index.md +9 -0
  23. pointclouds-0.0.0/docs/change-log.md +3 -0
  24. pointclouds-0.0.0/docs/contributing.md +69 -0
  25. pointclouds-0.0.0/docs/index.md +40 -0
  26. pointclouds-0.0.0/docs/installation.md +11 -0
  27. pointclouds-0.0.0/docs/reference/index.md +35 -0
  28. pointclouds-0.0.0/docs/tutorials/index.md +3 -0
  29. pointclouds-0.0.0/examples/data/make_copc_sample.py +287 -0
  30. pointclouds-0.0.0/examples/data/make_ept_sample.py +228 -0
  31. pointclouds-0.0.0/examples/data/make_sample.py +101 -0
  32. pointclouds-0.0.0/examples/data/sample-ept/ept-data/0-0-0-0.laz +0 -0
  33. pointclouds-0.0.0/examples/data/sample-ept/ept-data/1-0-0-0.laz +0 -0
  34. pointclouds-0.0.0/examples/data/sample-ept/ept-data/1-0-1-0.laz +0 -0
  35. pointclouds-0.0.0/examples/data/sample-ept/ept-data/1-1-0-0.laz +0 -0
  36. pointclouds-0.0.0/examples/data/sample-ept/ept-data/1-1-1-0.laz +0 -0
  37. pointclouds-0.0.0/examples/data/sample-ept/ept-hierarchy/0-0-0-0.json +7 -0
  38. pointclouds-0.0.0/examples/data/sample-ept/ept.json +47 -0
  39. pointclouds-0.0.0/examples/data/sample-ept-binary/ept-data/0-0-0-0.bin +0 -0
  40. pointclouds-0.0.0/examples/data/sample-ept-binary/ept-data/1-0-0-0.bin +0 -0
  41. pointclouds-0.0.0/examples/data/sample-ept-binary/ept-data/1-0-1-0.bin +0 -0
  42. pointclouds-0.0.0/examples/data/sample-ept-binary/ept-data/1-1-0-0.bin +0 -0
  43. pointclouds-0.0.0/examples/data/sample-ept-binary/ept-data/1-1-1-0.bin +0 -0
  44. pointclouds-0.0.0/examples/data/sample-ept-binary/ept-hierarchy/0-0-0-0.json +7 -0
  45. pointclouds-0.0.0/examples/data/sample-ept-binary/ept.json +65 -0
  46. pointclouds-0.0.0/examples/data/sample.copc.laz +0 -0
  47. pointclouds-0.0.0/examples/data/sample.las +0 -0
  48. pointclouds-0.0.0/examples/data/sample.laz +0 -0
  49. pointclouds-0.0.0/mkdocs.yml +140 -0
  50. pointclouds-0.0.0/pixi.lock +10774 -0
  51. pointclouds-0.0.0/pyproject.toml +315 -0
  52. pointclouds-0.0.0/src/pointclouds/__init__.py +20 -0
  53. pointclouds-0.0.0/src/pointclouds/classify/__init__.py +1 -0
  54. pointclouds-0.0.0/src/pointclouds/classify/ground.py +1 -0
  55. pointclouds-0.0.0/src/pointclouds/classify/trees.py +1 -0
  56. pointclouds-0.0.0/src/pointclouds/convert.py +35 -0
  57. pointclouds-0.0.0/src/pointclouds/io/__init__.py +1 -0
  58. pointclouds-0.0.0/src/pointclouds/io/_records.py +196 -0
  59. pointclouds-0.0.0/src/pointclouds/io/copc.py +570 -0
  60. pointclouds-0.0.0/src/pointclouds/io/ept.py +724 -0
  61. pointclouds-0.0.0/src/pointclouds/io/las.py +119 -0
  62. pointclouds-0.0.0/src/pointclouds/ops/__init__.py +1 -0
  63. pointclouds-0.0.0/src/pointclouds/ops/clip.py +99 -0
  64. pointclouds-0.0.0/src/pointclouds/ops/decimate.py +107 -0
  65. pointclouds-0.0.0/src/pointclouds/ops/filter.py +109 -0
  66. pointclouds-0.0.0/src/pointclouds/ops/grid.py +241 -0
  67. pointclouds-0.0.0/src/pointclouds/ops/merge.py +107 -0
  68. pointclouds-0.0.0/src/pointclouds/points.py +801 -0
  69. pointclouds-0.0.0/tests/conftest.py +74 -0
  70. pointclouds-0.0.0/tests/test_convert.py +45 -0
  71. pointclouds-0.0.0/tests/test_core_has_no_gdal.py +87 -0
  72. pointclouds-0.0.0/tests/test_io_copc.py +717 -0
  73. pointclouds-0.0.0/tests/test_io_ept.py +513 -0
  74. pointclouds-0.0.0/tests/test_io_las.py +119 -0
  75. pointclouds-0.0.0/tests/test_io_records.py +272 -0
  76. pointclouds-0.0.0/tests/test_ops.py +474 -0
  77. pointclouds-0.0.0/tests/test_points.py +552 -0
@@ -0,0 +1,20 @@
1
+ # SCM syntax highlighting & preventing 3-way merges
2
+ # pixi.lock is machine-generated and ~10k lines. `merge=binary` stops git from
3
+ # attempting a three-way merge on it: a conflict there is never worth resolving
4
+ # by hand - take one side and re-run `pixi lock` against the merged manifest.
5
+ # The linguist hints keep it out of the repo's language statistics and collapse
6
+ # it in GitHub diffs. `text eol=lf` matters on Windows, where core.autocrlf=true
7
+ # is the norm: without it the lock is checked out with CRLF, pixi rewrites it
8
+ # with LF on the next `pixi lock`, and all 10 805 lines show up as a diff nobody
9
+ # made.
10
+ pixi.lock merge=binary text eol=lf linguist-language=YAML linguist-generated=true
11
+
12
+ # Notebooks are LF-normalised JSON, which is what the nbstripout pre-commit hook
13
+ # produces. Nothing here tracks a notebook yet - the rule is in place so the
14
+ # first one under `docs/examples/` arrives correct rather than as a full-file
15
+ # diff on its first Windows checkout.
16
+ *.ipynb text eol=lf
17
+
18
+ # The committed LAS/LAZ fixtures are binary: never touch their line endings.
19
+ *.las binary
20
+ *.laz binary
@@ -0,0 +1,18 @@
1
+ * pointclouds version:
2
+ * Extras installed (`gis` / `cloud` / none):
3
+ * laspy version:
4
+ * pyramids-gis version (only if the `gis` extra is installed):
5
+ * Python version:
6
+ * Operating System:
7
+
8
+ ### Description
9
+
10
+ Describe what you were trying to get done.
11
+ Tell us what happened, what went wrong, and what you expected to happen.
12
+
13
+ ### What I Did
14
+
15
+ ```
16
+ Paste the command(s) you ran and the output.
17
+ If there was a crash, please include the traceback here.
18
+ ```
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior:
15
+ 1. Go to '...'
16
+ 2. Click on '....'
17
+ 3. Scroll down to '....'
18
+ 4. See error
19
+
20
+ **Expected behavior**
21
+ A clear and concise description of what you expected to happen.
22
+
23
+ **Environment**
24
+ - pointclouds version, extras installed (`gis` / `cloud` / none), Python version, OS.
25
+ - If the file matters: format (LAS / LAZ / COPC / EPT), LAS point format and version, and roughly
26
+ how many points.
27
+
28
+ **Screenshots**
29
+ If applicable, add screenshots to help explain your problem.
30
+
31
+ **Additional context**
32
+ Add any other context about the problem here.
@@ -0,0 +1,8 @@
1
+ ---
2
+ name: Custom issue template
3
+ about: Describe this issue template's purpose here.
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Which layer does it belong to?**
20
+ `pointclouds` owns point clouds; `pyramids` owns raster and vector (ADR-0001). If the request is
21
+ really about rasters or vectors, it belongs upstream. Note too whether it would need a new optional
22
+ dependency - the core is GDAL-free and stays that way.
23
+
24
+ **Additional context**
25
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,21 @@
1
+ # Gitleaks configuration for the CI secret scan (`gitleaks dir` in
2
+ # .github/workflows/lint.yml, which passes it as `-c .github/gitleaks.toml`). gitleaks is the repo's single secret scanner,
3
+ # matching pyramids, pyramids-eo and pyramids-mesh. There is no local gitleaks
4
+ # pre-commit hook here, so this file is read only by that job. It keeps
5
+ # gitleaks' full built-in ruleset and only allowlists paths that are registries
6
+ # or fixtures rather than source secrets.
7
+ title = "pointclouds"
8
+
9
+ [extend]
10
+ useDefault = true
11
+
12
+ [allowlist]
13
+ description = "Non-source paths: working notes and lockfile hashes"
14
+ paths = [
15
+ # Planning / review notes are working documents, not shipped code.
16
+ '''(^|/)planning/''',
17
+ # PR review artifacts written into the repo root are working documents.
18
+ '''pr-diff-review-.*\.md$''',
19
+ # The dependency lockfile is package hashes, not credentials.
20
+ '''pixi\.lock$''',
21
+ ]
@@ -0,0 +1,43 @@
1
+ # Description
2
+
3
+ - Please include a summary of the change and which issue is fixed.
4
+ - Please also include relevant motivation and context.
5
+ - List any dependencies that are required for this change.
6
+
7
+ # Issues
8
+
9
+ - Closes # (issue)
10
+
11
+ ## Type of change
12
+
13
+ Check relevant points.
14
+
15
+ - [ ] Bug fix (non-breaking change which fixes an issue)
16
+ - [ ] New feature (non-breaking change which adds functionality)
17
+ - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
18
+ - [ ] This change requires a documentation update
19
+
20
+ # How Has This Been Tested?
21
+
22
+ - Please describe the tests that you ran to verify your changes.
23
+ - Provide instructions so we can reproduce.
24
+ - Please also list any relevant details for your test configuration
25
+
26
+ - [ ] `pixi run -e dev main` (the GDAL-free core)
27
+ - [ ] `pixi run -e full test-all` (full suite, with the `gis` / `cloud` extras)
28
+ - [ ] `pixi run -e full notebooks` (notebooks)
29
+ - [ ] `pixi run -e dev typecheck` and `pre-commit run --all-files`
30
+
31
+ # Checklist:
32
+
33
+ - [ ] I have added tests that prove my fix is effective or that my feature works.
34
+ - [ ] New and existing unit tests pass locally with my changes.
35
+ - [ ] Public symbols I added or changed carry Google-style docstrings.
36
+ - [ ] The documentation is updated (reference page, guide, or notebook as applicable).
37
+ - [ ] `docs/change-log.md` records the change.
38
+ - [ ] The core stays GDAL-free: only `src/pointclouds/convert.py` imports `pyramids`, behind a
39
+ guarded in-function import naming the `[gis]` extra, and
40
+ `tests/test_core_has_no_gdal.py` is still green.
41
+ - [ ] The change stays on this package's side of the boundary (ADR-0001): `pointclouds` owns point
42
+ clouds, `pyramids` owns raster and vector. Anything belonging upstream is reported there
43
+ instead of reimplemented here.
@@ -0,0 +1,91 @@
1
+ name: Deploy MkDocs
2
+
3
+ permissions:
4
+ contents: write
5
+ pages: write
6
+
7
+ on:
8
+ push:
9
+ branches:
10
+ - main
11
+ pull_request:
12
+ branches:
13
+ - '**'
14
+ release:
15
+ types:
16
+ - published
17
+ workflow_run:
18
+ workflows: ['github-release']
19
+ types: [completed]
20
+
21
+ jobs:
22
+ deploy-pr:
23
+ if: github.event_name == 'pull_request'
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
27
+ with:
28
+ fetch-depth: 0
29
+
30
+ # serapeum-org/github-actions is a monorepo: one commit carries the release tag
31
+ # for every action in it, so 38c11108 is the dereferenced commit for pixi/v1.2.1,
32
+ # mkdocs/v1.3.1, github-release/v1.5.0 and pypi-release/v1.0.3 alike. Several
33
+ # different actions pinned to one SHA reads like a copy-paste error and is not;
34
+ # verify with
35
+ # git ls-remote --tags https://github.com/serapeum-org/github-actions
36
+ - uses: serapeum-org/github-actions/actions/mkdocs-deploy@38c111084f99d5ff5f4a1e63bedce48fe33a10c0 # mkdocs/v1.3.1
37
+ with:
38
+ trigger: 'pull_request'
39
+ package-manager: 'pixi'
40
+ package-manager-version: 'v0.73.0'
41
+ install-groups: 'docs'
42
+ deploy-token: ${{ secrets.ACTIONS_DEPLOY_TOKEN }}
43
+
44
+ deploy-main:
45
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
49
+ with:
50
+ fetch-depth: 0
51
+ - uses: serapeum-org/github-actions/actions/mkdocs-deploy@38c111084f99d5ff5f4a1e63bedce48fe33a10c0 # mkdocs/v1.3.1
52
+ with:
53
+ trigger: 'main'
54
+ package-manager: 'pixi'
55
+ package-manager-version: 'v0.73.0'
56
+ install-groups: 'docs'
57
+ deploy-token: ${{ secrets.ACTIONS_DEPLOY_TOKEN }}
58
+
59
+ deploy-release:
60
+ # Security: when triggered via `workflow_run`, require that the upstream
61
+ # workflow ran from the base repository (not a fork) so we can never run
62
+ # untrusted code in a context that holds `ACTIONS_DEPLOY_TOKEN`.
63
+ if: >-
64
+ github.event_name == 'release' ||
65
+ (github.event_name == 'workflow_run'
66
+ && github.event.workflow_run.conclusion == 'success'
67
+ && github.event.workflow_run.head_repository.fork == false)
68
+ runs-on: ubuntu-latest
69
+ steps:
70
+ - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
71
+ with:
72
+ fetch-depth: 0
73
+ # Pin to the immutable SHA recorded on the originating run, not the
74
+ # mutable `head_branch` label (CWE-829: actions/untrusted-checkout).
75
+ ref: ${{ github.event.workflow_run.head_sha || github.ref }}
76
+
77
+ - name: Extract version from pyproject.toml
78
+ id: version
79
+ run: |
80
+ VERSION=$(grep -m1 '^version' pyproject.toml | sed 's/.*"\(.*\)"/\1/')
81
+ echo "tag=$VERSION" >> $GITHUB_OUTPUT
82
+
83
+ - uses: serapeum-org/github-actions/actions/mkdocs-deploy@38c111084f99d5ff5f4a1e63bedce48fe33a10c0 # mkdocs/v1.3.1
84
+ with:
85
+ trigger: 'release'
86
+ package-manager: 'pixi'
87
+ package-manager-version: 'v0.73.0'
88
+ install-groups: 'docs'
89
+ deploy-token: ${{ secrets.ACTIONS_DEPLOY_TOKEN }}
90
+ release-tag: ${{ github.event.release.tag_name || steps.version.outputs.tag }}
91
+ mike-alias: 'latest'
@@ -0,0 +1,97 @@
1
+ name: github-release
2
+
3
+ permissions:
4
+ contents: read
5
+
6
+ on:
7
+ workflow_dispatch:
8
+ inputs:
9
+ release-branch:
10
+ description: "Branch to create release from"
11
+ required: false
12
+ default: "main"
13
+ increment:
14
+ description: "Version increment type"
15
+ required: false
16
+ type: choice
17
+ options:
18
+ - patch
19
+ - minor
20
+ - major
21
+ default: "patch"
22
+ prerelease_type:
23
+ description: "Prerelease type"
24
+ required: false
25
+ type: choice
26
+ options:
27
+ - none
28
+ - alpha
29
+ - beta
30
+ - rc
31
+ default: "none"
32
+ draft:
33
+ description: "Create as draft release"
34
+ required: false
35
+ type: boolean
36
+ default: false
37
+
38
+ jobs:
39
+ release:
40
+ runs-on: ubuntu-latest
41
+ permissions:
42
+ contents: write
43
+ steps:
44
+ - name: Checkout repository
45
+ uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
46
+ with:
47
+ fetch-depth: 0
48
+ ref: ${{ inputs.release-branch }}
49
+
50
+ - name: Check actor is a repository admin
51
+ run: |
52
+ PERMISSION=$(gh api repos/${{ github.repository }}/collaborators/${{ github.actor }}/permission --jq '.permission')
53
+ echo "Permission level for ${{ github.actor }}: $PERMISSION"
54
+ if [ "$PERMISSION" != "admin" ]; then
55
+ echo "::error::${{ github.actor }} is not a repository admin. Only admins can trigger releases."
56
+ exit 1
57
+ fi
58
+ env:
59
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60
+
61
+ - name: Create GitHub Release with Commitizen
62
+ # serapeum-org/github-actions is a monorepo: one commit carries the release tag
63
+ # for every action in it, so 38c11108 is the dereferenced commit for pixi/v1.2.1,
64
+ # mkdocs/v1.3.1, github-release/v1.5.0 and pypi-release/v1.0.3 alike. Several
65
+ # different actions pinned to one SHA reads like a copy-paste error and is not;
66
+ # verify with
67
+ # git ls-remote --tags https://github.com/serapeum-org/github-actions
68
+ uses: serapeum-org/github-actions/actions/release/github@38c111084f99d5ff5f4a1e63bedce48fe33a10c0 # github-release/v1.5.0
69
+ with:
70
+ github-token: ${{ secrets.GITHUB_TOKEN }}
71
+ increment: ${{ github.event.inputs.increment }}
72
+ prerelease-type: ${{ github.event.inputs.prerelease_type }}
73
+ draft: ${{ github.event.inputs.draft }}
74
+ package-manager: pixi
75
+ # Must match the pixi pinned in tests.yml and lint.yml: this job
76
+ # installs from pixi.lock, which is format v7 and unreadable to older
77
+ # pixi.
78
+ package-manager-version: v0.73.0
79
+ install-groups: "dev"
80
+
81
+ # There is deliberately no `pixi lock` + commit step here, unlike the uv
82
+ # setup this replaced. uv.lock recorded the project's own version, so a
83
+ # `cz bump` genuinely invalidated it; pixi.lock does not record it:
84
+ #
85
+ # $ grep -A2 '^- pypi: \./' pixi.lock
86
+ # - pypi: ./
87
+ # name: pointclouds
88
+ # requires_dist: <- no `version:` field
89
+ # $ sed -i 's/^version = "0.1.0"/version = "0.2.0"/' pyproject.toml
90
+ # $ pixi lock --check
91
+ # Lock-file was already up-to-date
92
+ #
93
+ # So a `pixi lock` after the bump can never be committing the bump. It can
94
+ # only commit an unrelated re-solve caused by upstream channel churn -
95
+ # pushed straight to the release branch, unreviewed, by a bot, moments
96
+ # after the tag. The lock is kept fresh by the normal PR flow instead,
97
+ # where every job runs with `verify-lock` and fails on drift.
@@ -0,0 +1,106 @@
1
+ name: Lint
2
+
3
+ permissions:
4
+ contents: read
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ pull_request:
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
14
+
15
+ jobs:
16
+ pre-commit:
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 20
19
+ steps:
20
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
21
+
22
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
23
+ with:
24
+ python-version: "3.12"
25
+
26
+ - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
27
+ with:
28
+ path: ~/.cache/pre-commit
29
+ key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
30
+ restore-keys: pre-commit-
31
+
32
+ - run: python -m pip install pre-commit==4.5.1
33
+
34
+ # No pixi environment is set up for this job on purpose: the only hook
35
+ # that needs the package importable is mypy, a `language: system` hook,
36
+ # and it is skipped below - the `static` job runs it in the real dev
37
+ # environment instead. ruff, nbstripout and the pre-commit-hooks set all
38
+ # run in pre-commit's own isolated environments and read their config
39
+ # from pyproject.toml.
40
+ - name: Run pre-commit hooks
41
+ run: pre-commit run --all-files --show-diff-on-failure --color=always
42
+ env:
43
+ # no-commit-to-branch is a local commit guard that misfires in CI,
44
+ # which legitimately runs on main: on a push event actions/checkout
45
+ # creates a real local branch (`-B main`), so the hook sees `main` and
46
+ # exits 1. pyramids, pyramids-eo and earthlens skip it for the same
47
+ # reason. mypy runs in the `static` job below.
48
+ SKIP: mypy,no-commit-to-branch
49
+
50
+ # Type check and src doctests. Both need an importable package, so they get a
51
+ # real pixi environment - but neither is platform- or version-sensitive, so
52
+ # one runner covers them. It runs on `py314` rather than the identical `dev`
53
+ # environment so CI never caches that 654 MB set twice under two names.
54
+ static:
55
+ runs-on: ubuntu-latest
56
+ timeout-minutes: 20
57
+ steps:
58
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
59
+ with:
60
+ fetch-depth: 0
61
+
62
+ - name: Set up Python
63
+ # serapeum-org/github-actions is a monorepo: one commit carries the release tag
64
+ # for every action in it, so 38c11108 is the dereferenced commit for pixi/v1.2.1,
65
+ # mkdocs/v1.3.1, github-release/v1.5.0 and pypi-release/v1.0.3 alike. Several
66
+ # different actions pinned to one SHA reads like a copy-paste error and is not;
67
+ # verify with
68
+ # git ls-remote --tags https://github.com/serapeum-org/github-actions
69
+ uses: serapeum-org/github-actions/actions/python-setup/pixi@38c111084f99d5ff5f4a1e63bedce48fe33a10c0 # pixi/v1.2.1
70
+ with:
71
+ environments: py314
72
+ activate-environment: py314
73
+ cache: true
74
+ # Must match the pixi pinned in tests.yml and github-release.yml:
75
+ # pixi.lock is lock-file format v7, which older pixi refuses to read.
76
+ version: v0.73.0
77
+
78
+ - name: Type check (mypy)
79
+ run: pixi run -e py314 typecheck
80
+
81
+ - name: Doctests (src)
82
+ run: pixi run -e py314 doctest
83
+
84
+ # The repo's secret gate. gitleaks is the single secret scanner (matching
85
+ # pyramids, pyramids-eo and pyramids-mesh). There is no local gitleaks
86
+ # pre-commit hook here, so this job is the only thing that runs it: it scans
87
+ # the whole working tree - every tracked file, notebooks included - with the
88
+ # .github/gitleaks.toml allowlist.
89
+ gitleaks:
90
+ runs-on: ubuntu-latest
91
+ timeout-minutes: 10
92
+ steps:
93
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
94
+
95
+ - name: Scan for secrets (gitleaks)
96
+ # Pinned binary + SHA256 verification (avoids the gitleaks-action
97
+ # org-license requirement). `gitleaks dir` exits non-zero on a leak,
98
+ # which fails the job.
99
+ run: |
100
+ VER=8.30.0
101
+ SHA=79a3ab579b53f71efd634f3aaf7e04a0fa0cf206b7ed434638d1547a2470a66e
102
+ curl -sSL --proto "=https" --proto-redir "=https" -o gitleaks.tar.gz \
103
+ "https://github.com/gitleaks/gitleaks/releases/download/v${VER}/gitleaks_${VER}_linux_x64.tar.gz"
104
+ echo "${SHA} gitleaks.tar.gz" | sha256sum -c -
105
+ tar -xzf gitleaks.tar.gz gitleaks
106
+ ./gitleaks dir . -c .github/gitleaks.toml --redact --no-banner
@@ -0,0 +1,80 @@
1
+ name: pip-audit
2
+
3
+ permissions:
4
+ contents: read
5
+
6
+ on:
7
+ pull_request:
8
+ schedule:
9
+ - cron: "0 6 * * 1"
10
+ workflow_dispatch:
11
+
12
+ concurrency:
13
+ group: ${{ github.workflow }}-${{ github.ref }}
14
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
15
+
16
+ jobs:
17
+ pip-audit:
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 15
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ # Four environments, because each pixi environment is one concrete
24
+ # resolution and an unaudited environment is an unaudited dependency:
25
+ # py311 / py314 - the two ends of the interpreter range, which resolve
26
+ # different versions of numpy, scipy and pyproj.
27
+ # full - the only environment carrying the `gis` and `cloud`
28
+ # features, so pyramids-gis, fsspec and s3fs are
29
+ # audited at all.
30
+ # docs - mkdocs-material and the mkdocstrings stack, ~80
31
+ # packages that no other environment installs.
32
+ # Together these cover every runtime dependency, extra and dependency
33
+ # group the project declares.
34
+ environment: [py311, py314, full, docs]
35
+
36
+ steps:
37
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
38
+ with:
39
+ fetch-depth: 0
40
+
41
+ - name: Set up Python
42
+ # serapeum-org/github-actions is a monorepo: one commit carries the release tag
43
+ # for every action in it, so 38c11108 is the dereferenced commit for pixi/v1.2.1,
44
+ # mkdocs/v1.3.1, github-release/v1.5.0 and pypi-release/v1.0.3 alike. Several
45
+ # different actions pinned to one SHA reads like a copy-paste error and is not;
46
+ # verify with
47
+ # git ls-remote --tags https://github.com/serapeum-org/github-actions
48
+ uses: serapeum-org/github-actions/actions/python-setup/pixi@38c111084f99d5ff5f4a1e63bedce48fe33a10c0 # pixi/v1.2.1
49
+ with:
50
+ environments: ${{ matrix.environment }}
51
+ activate-environment: ${{ matrix.environment }}
52
+ cache: true
53
+ # Must match the pixi pinned in tests.yml and github-release.yml:
54
+ # pixi.lock is lock-file format v7, which older pixi refuses to read.
55
+ version: v0.73.0
56
+
57
+ # The inventory comes from `pixi list`, not `pip freeze`, for two reasons:
58
+ # `pip` is only in the `dev` dependency group, so `python -m pip freeze`
59
+ # in the `docs` environment prints nothing and the job would audit an
60
+ # empty file and pass; and pip reads the user site-packages, so it reports
61
+ # packages the environment does not contain. `pixi list` is the manager's
62
+ # own record and needs nothing installed inside the environment.
63
+ #
64
+ # Only `kind == "pypi"` entries with a version survive: pip-audit resolves
65
+ # names against PyPI, where the conda packages (python, openssl, ...) are
66
+ # not projects, and the editable self-install carries no version.
67
+ #
68
+ # `test -s` is the guard that matters - an empty requirements file makes
69
+ # pip-audit report success, so a mis-piped command would look like a clean
70
+ # scan. `--no-deps` keeps the result independent of whichever interpreter
71
+ # pipx picks: the file is fully pinned, so pip-audit looks each package up
72
+ # in the advisory DB instead of resolving the set in a throwaway venv.
73
+ - name: Audit dependencies with pip-audit
74
+ run: |
75
+ pixi list -e ${{ matrix.environment }} --json \
76
+ | python -c "import json,sys; [print(p['name']+'=='+p['version']) for p in sorted(json.load(sys.stdin), key=lambda q: q['name']) if p.get('kind')=='pypi' and p.get('version')]" \
77
+ > "${RUNNER_TEMP}/pip-audit-reqs.txt"
78
+ test -s "${RUNNER_TEMP}/pip-audit-reqs.txt"
79
+ echo "Auditing $(wc -l < "${RUNNER_TEMP}/pip-audit-reqs.txt") pinned packages from the ${{ matrix.environment }} environment"
80
+ pipx run pip-audit --requirement "${RUNNER_TEMP}/pip-audit-reqs.txt" --no-deps --desc
@@ -0,0 +1,55 @@
1
+ # Build and publish the Python package to PyPI using the shared composite action.
2
+
3
+ name: pypi-release
4
+
5
+ on:
6
+ workflow_dispatch:
7
+ inputs:
8
+ ref:
9
+ description: "Tag, branch, or SHA to publish to PyPI (e.g. 0.1.0)"
10
+ required: true
11
+ type: string
12
+ workflow_run:
13
+ workflows: ['github-release']
14
+ types: [completed]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ publish:
21
+ runs-on: ubuntu-latest
22
+ # Security: when triggered via `workflow_run`, require that the upstream
23
+ # workflow ran from the base repository (not a fork) so a fork can never
24
+ # reach a context holding `PYPI_PUBLISH`.
25
+ if: >-
26
+ github.event_name == 'workflow_dispatch' ||
27
+ (github.event.workflow_run.conclusion == 'success'
28
+ && github.event.workflow_run.head_repository.fork == false)
29
+
30
+ steps:
31
+ - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
32
+ with:
33
+ fetch-depth: 0
34
+ # On workflow_run, pin to the immutable SHA recorded on the
35
+ # originating run, not the mutable `head_branch` label
36
+ # (CWE-829: actions/untrusted-checkout).
37
+ ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.event.workflow_run.head_sha }}
38
+
39
+ - name: Build and publish to PyPI
40
+ # serapeum-org/github-actions is a monorepo: one commit carries the release tag
41
+ # for every action in it, so 38c11108 is the dereferenced commit for pixi/v1.2.1,
42
+ # mkdocs/v1.3.1, github-release/v1.5.0 and pypi-release/v1.0.3 alike. Several
43
+ # different actions pinned to one SHA reads like a copy-paste error and is not;
44
+ # verify with
45
+ # git ls-remote --tags https://github.com/serapeum-org/github-actions
46
+ uses: serapeum-org/github-actions/actions/release/pypi@38c111084f99d5ff5f4a1e63bedce48fe33a10c0 # pypi-release/v1.0.3
47
+ with:
48
+ pypi-username: __token__
49
+ pypi-password: ${{ secrets.PYPI_PUBLISH }}
50
+ package-manager: 'pixi'
51
+ package-manager-version: 'v0.73.0'
52
+ install-groups: 'dev'
53
+ # The version bump that precedes this run rewrites pyproject.toml, so
54
+ # the lock is one commit behind by design here.
55
+ verify-lock: 'false'