Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/actions/setup-openscad/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ runs:
python-version: '3.14'

- name: Install system dependencies
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: xvfb libglu1-mesa libfuse2 libegl1 libxcb-cursor0
shell: bash
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends xvfb libglu1-mesa libfuse2 libegl1 libxcb-cursor0

- name: Resolve scadm version
id: resolve-scadm
Expand Down Expand Up @@ -66,7 +68,7 @@ runs:
uses: actions/cache@v5
with:
path: bin/openscad
key: openscad-${{ runner.os }}-${{ steps.resolve-scadm.outputs.version }}-${{ hashFiles('scadm.json', inputs.scadm-source && format('{0}/**/constants.py', inputs.scadm-source) || 'scadm.json') }}
key: openscad-${{ runner.os }}-${{ steps.resolve-scadm.outputs.version }}-${{ hashFiles('scadm.json') }}

- name: Install OpenSCAD and dependencies
shell: bash
Expand Down
2 changes: 2 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ HomeRacker is a modular 3D-printable rack-building system. Core components use p

## Core Principles
- **Test-Driven Development**: NO change without a test. EVERY change MUST be tested before completion. No exceptions for "simple" changes.
- **Unit tests**: run via pre-commit hooks (fast, mocked). Always run before commit.
- **Integration tests**: run via CI workflow on ubuntu + windows (`integration-tests.yml`). Update when adding/modifying CLI commands or config schema. See `TESTING.md`.
- **DRY, KISS, YAGNI**: Keep it simple, don't over-engineer
- **Be Brief**: All outputs (code, docs, issues, PRs) should be minimal and to-the-point
- Code: No unnecessary comments, clear variable names speak for themselves
Expand Down
23 changes: 23 additions & 0 deletions .github/instructions/python.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,26 @@ Example: See `cmd/scadm/scadm/flatten.py` for reference.
- Use `unittest` or `pytest` for unit tests.
- Tests live alongside the package they test (e.g., `cmd/scadm/tests/`).
- Run tests with `python -m pytest` from the package directory.

### Unit vs Integration Tests

- **Unit tests** (`test_*.py` without markers): fast, no network, mocked dependencies. Run via pre-commit hooks.
- **Integration tests** (`test_cli_integration.py`, marked `@pytest.mark.integration`): exercise real CLI commands against temp workspaces. Run via CI workflow (`.github/workflows/integration-tests.yml`) on ubuntu + windows matrix.
- **Slow tests** (`@pytest.mark.slow`): download binaries from the network. Subset of integration tests.
Comment thread
kellervater marked this conversation as resolved.
- Non-`slow` integration tests may still make lightweight network calls (e.g., version resolution) but avoid large downloads.

### Running Integration Tests

```bash
# All integration tests (fast + slow)
python -m pytest tests/ -m integration -v

# Fast only (no downloads)
python -m pytest tests/ -m "integration and not slow" -v
```

### When to Add/Update Integration Tests

- Adding or modifying a CLI subcommand → add/update integration test.
- Changing `scadm.json` config schema → update config-dependent tests.
- Changing installer/resolver behavior → update relevant slow tests.
46 changes: 46 additions & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
name: Integration Tests

on:
pull_request:
paths:
- 'cmd/scadm/**'
- 'scadm.json'
- '.github/workflows/integration-tests.yml'
- '.github/actions/setup-openscad/**'

jobs:
integration:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.14'

- name: Install scadm from source
run: pip install -e cmd/scadm pytest pytest-timeout

- name: Install libfuse2 (Linux AppImage support)
if: runner.os == 'Linux'
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends libfuse2

- name: Run integration tests
shell: bash
run: |
Comment thread
kellervater marked this conversation as resolved.
set -euo pipefail
cd cmd/scadm
python -m pytest tests/test_cli_integration.py -m integration -v --timeout=300
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ repos:
hooks:
- id: scadm-tests
name: scadm Python Tests
entry: bash -c 'cd cmd/scadm && python -m pytest tests/ -v'
entry: bash -c 'cd cmd/scadm && python -m pytest tests/ -v -m "not integration"'
language: system
pass_filenames: false
files: '^cmd/scadm/'
Expand Down
40 changes: 40 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,46 @@ Always test changes before committing:
- **Write simple tests** if none exist (bash scripts, Python tests, or manual verification steps)
- **Document test steps** in commit messages or PR descriptions

## scadm Tests

### Unit Tests

Fast, mocked tests that run via pre-commit hooks on every commit.

```bash
cd cmd/scadm
python -m pytest tests/ -m "not integration" -v
```

### Integration Tests

CLI integration tests exercise real `scadm` commands against temporary workspaces. Run via CI workflow (`.github/workflows/integration-tests.yml`) on **ubuntu + windows** matrix.

| Marker | What | Network | Speed |
|--------|------|---------|-------|
| `integration` (no `slow`) | Config parsing, `--info`, `--check`, vscode settings, cache | ⚡ version resolution only | ~5s |
| `integration` + `slow` | Binary download, library install | ✅ | ~60s |
Comment thread
kellervater marked this conversation as resolved.

#### Running Locally

```bash
cd cmd/scadm

# All integration tests
python -m pytest tests/test_cli_integration.py -m integration -v

# Fast only (no downloads)
python -m pytest tests/test_cli_integration.py -m "integration and not slow" -v
```

> **Prerequisite**: Install scadm in editable mode first: `pip install -e cmd/scadm`

#### When to Update

- Adding or modifying a CLI subcommand → add/update integration test
- Changing `scadm.json` config schema → update config-dependent tests
- Changing installer/resolver behavior → update relevant slow tests

## Renovate Configuration Testing

When modifying `renovate.json5`, always test changes before merging to prevent incorrect PRs.
Expand Down
24 changes: 20 additions & 4 deletions cmd/scadm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,17 @@ scadm --version
### Install everything (OpenSCAD + libraries)

```bash
scadm install # Install nightly build (default - RECOMMENDED)
scadm install --stable # Install stable release (2021.01)
scadm install # Install based on scadm.json config (default: nightly latest)
```

> [!NOTE]
> Nightly builds are installed by default since the stable release (2021.01) is outdated and missing modern features. All nightly versions pass rendering tests before being published to ensure quality.
> By default, scadm installs the latest nightly build of OpenSCAD. Configure the build type and version in `scadm.json` (see [Configuration](#configuration)). Nightly builds are recommended since the stable release (2021.01) is outdated and missing modern features.

### Show version info

```bash
scadm install --info # Show configured, resolved, and installed versions
```

### Check installation status

Expand Down Expand Up @@ -190,6 +195,10 @@ scadm vscode --python # Install and configure Python extension

```json
{
"openscad": {
"type": "nightly",
"version": "latest"
},
"dependencies": [
{
"name": "BOSL2",
Expand All @@ -207,7 +216,14 @@ scadm vscode --python # Install and configure Python extension
}
```

**Fields:**
**OpenSCAD fields** (optional — defaults to nightly/latest if omitted):
- `type`: `"nightly"` (default) or `"stable"` — which build channel to use
- `version`: `"latest"` (default) or a pinned version string (e.g., `"2026.03.28"`)
- `"latest"` resolves dynamically by scraping the [OpenSCAD snapshots page](https://files.openscad.org/snapshots/) (nightly) or the [GitHub releases API](https://api.github.com/repos/openscad/openscad/releases/latest) (stable)
- Pinned versions are used as-is without network access
- Resolved versions are cached in `bin/openscad/.resolved-version`; use `--force` to bypass cache

**Dependency fields:**
- `name`: Library name (creates `bin/openscad/libraries/{name}/`)
- `repository`: GitHub repository in `owner/repo` format
- `version`: Git tag, commit SHA, or branch name
Expand Down
6 changes: 6 additions & 0 deletions cmd/scadm/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,9 @@ include = ["scadm*"]

[tool.setuptools.package-data]
scadm = ["py.typed"]

[tool.pytest.ini_options]
markers = [
"integration: CLI integration tests (require network, run with -m integration)",
"slow: slow tests that download binaries (run with -m slow)",
]
14 changes: 4 additions & 10 deletions cmd/scadm/scadm/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ def _handle_install(args):

try:
if not args.libs_only:
if not install_openscad(nightly=args.nightly, force=args.force, check_only=args.check):
if not install_openscad(force=args.force, check_only=args.check, info=args.info):
success = False
if not args.check:
if not args.check and not args.info:
logger.error("OpenSCAD installation failed. Aborting.")
sys.exit(1)

if not args.openscad_only:
if not args.openscad_only and not args.info:
if not install_libraries(force=args.force, check_only=args.check):
success = False
except FileNotFoundError as e:
Expand Down Expand Up @@ -131,13 +131,7 @@ def main():
install_parser = subparsers.add_parser("install", help="Install OpenSCAD and libraries")
install_parser.add_argument("--check", action="store_true", help="Check installation status only")
install_parser.add_argument("--force", action="store_true", help="Force reinstall")
install_parser.add_argument(
"--stable",
action="store_false",
dest="nightly",
default=True,
help="Install stable release (2021.01) instead of nightly",
)
install_parser.add_argument("--info", action="store_true", help="Show OpenSCAD version info")
install_parser.add_argument("--openscad-only", action="store_true", help="Install only OpenSCAD binary")
install_parser.add_argument("--libs-only", action="store_true", help="Install only libraries")

Expand Down
9 changes: 0 additions & 9 deletions cmd/scadm/scadm/constants.py

This file was deleted.

Loading
Loading