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.
- pointclouds-0.0.0/.gitattributes +20 -0
- pointclouds-0.0.0/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md +18 -0
- pointclouds-0.0.0/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
- pointclouds-0.0.0/.github/ISSUE_TEMPLATE/custom.md +8 -0
- pointclouds-0.0.0/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- pointclouds-0.0.0/.github/gitleaks.toml +21 -0
- pointclouds-0.0.0/.github/pull_request_template.md +43 -0
- pointclouds-0.0.0/.github/workflows/github-pages-mkdocs.yml +91 -0
- pointclouds-0.0.0/.github/workflows/github-release.yml +97 -0
- pointclouds-0.0.0/.github/workflows/lint.yml +106 -0
- pointclouds-0.0.0/.github/workflows/pip-audit.yml +80 -0
- pointclouds-0.0.0/.github/workflows/pypi-release.yml +55 -0
- pointclouds-0.0.0/.github/workflows/tests.yml +98 -0
- pointclouds-0.0.0/.gitignore +42 -0
- pointclouds-0.0.0/.pre-commit-config.yaml +47 -0
- pointclouds-0.0.0/LICENSE.md +675 -0
- pointclouds-0.0.0/PKG-INFO +95 -0
- pointclouds-0.0.0/README.md +55 -0
- pointclouds-0.0.0/docs/AUTHORS.md +3 -0
- pointclouds-0.0.0/docs/CODE_OF_CONDUCT.md +3 -0
- pointclouds-0.0.0/docs/adr/0001-separate-distribution.md +24 -0
- pointclouds-0.0.0/docs/adr/index.md +9 -0
- pointclouds-0.0.0/docs/change-log.md +3 -0
- pointclouds-0.0.0/docs/contributing.md +69 -0
- pointclouds-0.0.0/docs/index.md +40 -0
- pointclouds-0.0.0/docs/installation.md +11 -0
- pointclouds-0.0.0/docs/reference/index.md +35 -0
- pointclouds-0.0.0/docs/tutorials/index.md +3 -0
- pointclouds-0.0.0/examples/data/make_copc_sample.py +287 -0
- pointclouds-0.0.0/examples/data/make_ept_sample.py +228 -0
- pointclouds-0.0.0/examples/data/make_sample.py +101 -0
- pointclouds-0.0.0/examples/data/sample-ept/ept-data/0-0-0-0.laz +0 -0
- pointclouds-0.0.0/examples/data/sample-ept/ept-data/1-0-0-0.laz +0 -0
- pointclouds-0.0.0/examples/data/sample-ept/ept-data/1-0-1-0.laz +0 -0
- pointclouds-0.0.0/examples/data/sample-ept/ept-data/1-1-0-0.laz +0 -0
- pointclouds-0.0.0/examples/data/sample-ept/ept-data/1-1-1-0.laz +0 -0
- pointclouds-0.0.0/examples/data/sample-ept/ept-hierarchy/0-0-0-0.json +7 -0
- pointclouds-0.0.0/examples/data/sample-ept/ept.json +47 -0
- pointclouds-0.0.0/examples/data/sample-ept-binary/ept-data/0-0-0-0.bin +0 -0
- pointclouds-0.0.0/examples/data/sample-ept-binary/ept-data/1-0-0-0.bin +0 -0
- pointclouds-0.0.0/examples/data/sample-ept-binary/ept-data/1-0-1-0.bin +0 -0
- pointclouds-0.0.0/examples/data/sample-ept-binary/ept-data/1-1-0-0.bin +0 -0
- pointclouds-0.0.0/examples/data/sample-ept-binary/ept-data/1-1-1-0.bin +0 -0
- pointclouds-0.0.0/examples/data/sample-ept-binary/ept-hierarchy/0-0-0-0.json +7 -0
- pointclouds-0.0.0/examples/data/sample-ept-binary/ept.json +65 -0
- pointclouds-0.0.0/examples/data/sample.copc.laz +0 -0
- pointclouds-0.0.0/examples/data/sample.las +0 -0
- pointclouds-0.0.0/examples/data/sample.laz +0 -0
- pointclouds-0.0.0/mkdocs.yml +140 -0
- pointclouds-0.0.0/pixi.lock +10774 -0
- pointclouds-0.0.0/pyproject.toml +315 -0
- pointclouds-0.0.0/src/pointclouds/__init__.py +20 -0
- pointclouds-0.0.0/src/pointclouds/classify/__init__.py +1 -0
- pointclouds-0.0.0/src/pointclouds/classify/ground.py +1 -0
- pointclouds-0.0.0/src/pointclouds/classify/trees.py +1 -0
- pointclouds-0.0.0/src/pointclouds/convert.py +35 -0
- pointclouds-0.0.0/src/pointclouds/io/__init__.py +1 -0
- pointclouds-0.0.0/src/pointclouds/io/_records.py +196 -0
- pointclouds-0.0.0/src/pointclouds/io/copc.py +570 -0
- pointclouds-0.0.0/src/pointclouds/io/ept.py +724 -0
- pointclouds-0.0.0/src/pointclouds/io/las.py +119 -0
- pointclouds-0.0.0/src/pointclouds/ops/__init__.py +1 -0
- pointclouds-0.0.0/src/pointclouds/ops/clip.py +99 -0
- pointclouds-0.0.0/src/pointclouds/ops/decimate.py +107 -0
- pointclouds-0.0.0/src/pointclouds/ops/filter.py +109 -0
- pointclouds-0.0.0/src/pointclouds/ops/grid.py +241 -0
- pointclouds-0.0.0/src/pointclouds/ops/merge.py +107 -0
- pointclouds-0.0.0/src/pointclouds/points.py +801 -0
- pointclouds-0.0.0/tests/conftest.py +74 -0
- pointclouds-0.0.0/tests/test_convert.py +45 -0
- pointclouds-0.0.0/tests/test_core_has_no_gdal.py +87 -0
- pointclouds-0.0.0/tests/test_io_copc.py +717 -0
- pointclouds-0.0.0/tests/test_io_ept.py +513 -0
- pointclouds-0.0.0/tests/test_io_las.py +119 -0
- pointclouds-0.0.0/tests/test_io_records.py +272 -0
- pointclouds-0.0.0/tests/test_ops.py +474 -0
- 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,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'
|