zabob-houdini 0.1.3__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 (80) hide show
  1. zabob_houdini-0.1.3/.env.example.linux +19 -0
  2. zabob_houdini-0.1.3/.env.example.macos +18 -0
  3. zabob_houdini-0.1.3/.env.example.windows +19 -0
  4. zabob_houdini-0.1.3/.flake8 +3 -0
  5. zabob_houdini-0.1.3/.gitattributes +69 -0
  6. zabob_houdini-0.1.3/.github/copilot-instructions.md +171 -0
  7. zabob_houdini-0.1.3/.github/workflows/publish.yml +197 -0
  8. zabob_houdini-0.1.3/.github/workflows/test.yml +118 -0
  9. zabob_houdini-0.1.3/.gitignore +119 -0
  10. zabob_houdini-0.1.3/.markdownlint.json +10 -0
  11. zabob_houdini-0.1.3/.python-version +1 -0
  12. zabob_houdini-0.1.3/.python-version-houdini +1 -0
  13. zabob_houdini-0.1.3/.vscode/extensions.json +11 -0
  14. zabob_houdini-0.1.3/.vscode/project-dictionary.txt +98 -0
  15. zabob_houdini-0.1.3/.vscode/settings.json.example +51 -0
  16. zabob_houdini-0.1.3/.vscode/setup-vscode.sh +69 -0
  17. zabob_houdini-0.1.3/API.md +955 -0
  18. zabob_houdini-0.1.3/CHANGELOG.md +95 -0
  19. zabob_houdini-0.1.3/COMMAND.md +380 -0
  20. zabob_houdini-0.1.3/DEVELOPMENT.md +459 -0
  21. zabob_houdini-0.1.3/LICENSE +21 -0
  22. zabob_houdini-0.1.3/PKG-INFO +140 -0
  23. zabob_houdini-0.1.3/README.md +109 -0
  24. zabob_houdini-0.1.3/TODO.md +51 -0
  25. zabob_houdini-0.1.3/ZABOB.md +87 -0
  26. zabob_houdini-0.1.3/cspell.json +47 -0
  27. zabob_houdini-0.1.3/docs/PYPI_SETUP.md +132 -0
  28. zabob_houdini-0.1.3/docs/images/psd/zabob-banner.psd +0 -0
  29. zabob_houdini-0.1.3/docs/images/psd/zabob.psd +0 -0
  30. zabob_houdini-0.1.3/docs/images/zabob-banner.jpg +3 -0
  31. zabob_houdini-0.1.3/docs/images/zabob-holodeck.jpg +3 -0
  32. zabob_houdini-0.1.3/docs/images/zabob-neutral-bg.jpg +3 -0
  33. zabob_houdini-0.1.3/docs/images/zabob.jpg +3 -0
  34. zabob_houdini-0.1.3/examples/chain_demo.py +95 -0
  35. zabob_houdini-0.1.3/examples/dependency_tracking_demo.py +89 -0
  36. zabob_houdini-0.1.3/examples/diamond_chain_demo.py +212 -0
  37. zabob_houdini-0.1.3/examples/enhanced_node_context_demo.py +83 -0
  38. zabob_houdini-0.1.3/examples/enhanced_typing_demo.py +46 -0
  39. zabob_houdini-0.1.3/examples/hython_bridge_demo.py +19 -0
  40. zabob_houdini-0.1.3/examples/modern_types_demo.py +34 -0
  41. zabob_houdini-0.1.3/examples/node_context_chain_demo.py +77 -0
  42. zabob_houdini-0.1.3/examples/node_context_demo.py +64 -0
  43. zabob_houdini-0.1.3/examples/parameterized_diamond_demo.py +61 -0
  44. zabob_houdini-0.1.3/examples/simple_diamond_demo.py +56 -0
  45. zabob_houdini-0.1.3/examples/type_narrowing_demo.py +112 -0
  46. zabob_houdini-0.1.3/package-lock.json +1152 -0
  47. zabob_houdini-0.1.3/package.json +13 -0
  48. zabob_houdini-0.1.3/pyproject.toml +80 -0
  49. zabob_houdini-0.1.3/release.sh +238 -0
  50. zabob_houdini-0.1.3/src/zabob_houdini/__init__.py +92 -0
  51. zabob_houdini-0.1.3/src/zabob_houdini/__main__.py +102 -0
  52. zabob_houdini-0.1.3/src/zabob_houdini/__version__.py +15 -0
  53. zabob_houdini-0.1.3/src/zabob_houdini/cli.py +310 -0
  54. zabob_houdini-0.1.3/src/zabob_houdini/core.py +1343 -0
  55. zabob_houdini-0.1.3/src/zabob_houdini/houdini_bridge.py +285 -0
  56. zabob_houdini-0.1.3/src/zabob_houdini/houdini_config.py +86 -0
  57. zabob_houdini-0.1.3/src/zabob_houdini/houdini_functions.py +171 -0
  58. zabob_houdini-0.1.3/src/zabob_houdini/houdini_info.py +433 -0
  59. zabob_houdini-0.1.3/src/zabob_houdini/houdini_test_functions.py +1915 -0
  60. zabob_houdini-0.1.3/src/zabob_houdini/houdini_versions.py +771 -0
  61. zabob_houdini-0.1.3/src/zabob_houdini/package_installer.py +174 -0
  62. zabob_houdini-0.1.3/src/zabob_houdini/utils.py +61 -0
  63. zabob_houdini-0.1.3/stubs/README.md +77 -0
  64. zabob_houdini-0.1.3/stubs/TYPE_SAFETY.md +146 -0
  65. zabob_houdini-0.1.3/stubs/hou.pyi +1107 -0
  66. zabob_houdini-0.1.3/test.sh +70 -0
  67. zabob_houdini-0.1.3/tests/conftest.py +196 -0
  68. zabob_houdini-0.1.3/tests/test_chain_positional_copy.py +56 -0
  69. zabob_houdini-0.1.3/tests/test_core_caching.py +237 -0
  70. zabob_houdini-0.1.3/tests/test_dependency_tracking.py +39 -0
  71. zabob_houdini-0.1.3/tests/test_enhanced_copy.py +81 -0
  72. zabob_houdini-0.1.3/tests/test_houdini_bridge.py +165 -0
  73. zabob_houdini-0.1.3/tests/test_houdini_integration.py +102 -0
  74. zabob_houdini-0.1.3/tests/test_input_connections.py +61 -0
  75. zabob_houdini-0.1.3/tests/test_input_validation.py +87 -0
  76. zabob_houdini-0.1.3/tests/test_node_context.py +174 -0
  77. zabob_houdini-0.1.3/tests/test_node_creation.py +78 -0
  78. zabob_houdini-0.1.3/tests/test_node_duplication.py +64 -0
  79. zabob_houdini-0.1.3/tests/test_simple.py +19 -0
  80. zabob_houdini-0.1.3/uv.lock +657 -0
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # Houdini Python environment setup for Linux
3
+ # Copy this to .env and adjust paths if your Houdini installation differs
4
+
5
+ # Standard Houdini installation paths on Linux
6
+ HOUDINI_PATH="/opt/hfs19.5.640"
7
+
8
+ # Alternative paths for different versions or locations:
9
+ # HOUDINI_PATH="/opt/hfs20.0.547"
10
+ # HOUDINI_PATH="/usr/local/houdini/hfs19.5.640"
11
+ # HOUDINI_PATH="/home/user/houdini/hfs19.5.640"
12
+
13
+ # Python path setup for VS Code IntelliSense
14
+ PYTHONPATH="${HOUDINI_PATH}/houdini/python3.11libs:${PYTHONPATH}"
15
+
16
+ # SideFX credentials (optional) - required for Houdini installer download functionality
17
+ # Create an account at https://www.sidefx.com/login/ to get these credentials
18
+ # SIDEFX_USERNAME=""
19
+ # SIDEFX_PASSWORD=""
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+ # Houdini Python environment setup for macOS
3
+ # Copy this to .env and adjust paths if your Houdini installation differs
4
+
5
+ # Standard Houdini installation path on macOS
6
+ HOUDINI_PATH="/Applications/Houdini/Current/Frameworks/Houdini.framework/Versions/Current/Resources"
7
+
8
+ # Alternative paths for different versions:
9
+ # HOUDINI_PATH="/Applications/Houdini/Houdini19.5.640/Frameworks/Houdini.framework/Versions/19.5.640/Resources"
10
+ # HOUDINI_PATH="/Applications/Houdini/Houdini20.0.547/Frameworks/Houdini.framework/Versions/20.0.547/Resources"
11
+
12
+ # Python path setup for VS Code IntelliSense
13
+ PYTHONPATH="${HOUDINI_PATH}/houdini/python3.11libs:${PYTHONPATH}"
14
+
15
+ # SideFX credentials (optional) - required for Houdini installer download functionality
16
+ # Create an account at https://www.sidefx.com/login/ to get these credentials
17
+ # SIDEFX_USERNAME=""
18
+ # SIDEFX_PASSWORD=""
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # Houdini Python environment setup for Windows
3
+ # Copy this to .env and adjust paths if your Houdini installation differs
4
+
5
+ # Standard Houdini installation paths on Windows
6
+ HOUDINI_PATH="C:/Program Files/Side Effects Software/Houdini 19.5.640"
7
+
8
+ # Alternative paths for different versions or locations:
9
+ # HOUDINI_PATH="C:/Program Files/Side Effects Software/Houdini 20.0.547"
10
+ # HOUDINI_PATH="D:/Software/Houdini/19.5.640"
11
+ # HOUDINI_PATH="C:/Apps/Houdini/hfs19.5.640"
12
+
13
+ # Python path setup for VS Code IntelliSense
14
+ PYTHONPATH="${HOUDINI_PATH}/houdini/python3.11libs;${PYTHONPATH}"
15
+
16
+ # SideFX credentials (optional) - required for Houdini installer download functionality
17
+ # Create an account at https://www.sidefx.com/login/ to get these credentials
18
+ # SIDEFX_USERNAME=""
19
+ # SIDEFX_PASSWORD=""
@@ -0,0 +1,3 @@
1
+ [flake8]
2
+ max-line-length = 120
3
+ exclude = ".git,__pycache__,build,dist,stubs"
@@ -0,0 +1,69 @@
1
+ # Houdini scene files
2
+ *.hip filter=lfs diff=lfs merge=lfs -text
3
+ *.hipnc filter=lfs diff=lfs merge=lfs -text
4
+ *.hiplc filter=lfs diff=lfs merge=lfs -text
5
+ *.hda filter=lfs diff=lfs merge=lfs -text
6
+ *.otl filter=lfs diff=lfs merge=lfs -text
7
+ *.hdanc filter=lfs diff=lfs merge=lfs -text
8
+
9
+ # Houdini geometry files
10
+ *.bgeo filter=lfs diff=lfs merge=lfs -text
11
+ *.bgeo.gz filter=lfs diff=lfs merge=lfs -text
12
+ *.bgeo.bz2 filter=lfs diff=lfs merge=lfs -text
13
+ *.bgeo.sc filter=lfs diff=lfs merge=lfs -text
14
+ *.bgeoc filter=lfs diff=lfs merge=lfs -text
15
+ *.geo filter=lfs diff=lfs merge=lfs -text
16
+ *.obj filter=lfs diff=lfs merge=lfs -text
17
+ *.ply filter=lfs diff=lfs merge=lfs -text
18
+ *.abc filter=lfs diff=lfs merge=lfs -text
19
+ *.fbx filter=lfs diff=lfs merge=lfs -text
20
+ *.dae filter=lfs diff=lfs merge=lfs -text
21
+
22
+ # Houdini image/texture files
23
+ *.pic filter=lfs diff=lfs merge=lfs -text
24
+ *.rat filter=lfs diff=lfs merge=lfs -text
25
+ *.exr filter=lfs diff=lfs merge=lfs -text
26
+ *.hdr filter=lfs diff=lfs merge=lfs -text
27
+ *.tif filter=lfs diff=lfs merge=lfs -text
28
+ *.tiff filter=lfs diff=lfs merge=lfs -text
29
+ *.tga filter=lfs diff=lfs merge=lfs -text
30
+ *.png filter=lfs diff=lfs merge=lfs -text
31
+ *.jpg filter=lfs diff=lfs merge=lfs -text
32
+ *.jpeg filter=lfs diff=lfs merge=lfs -text
33
+
34
+ # Houdini simulation cache files
35
+ *.sim filter=lfs diff=lfs merge=lfs -text
36
+ *.simdata filter=lfs diff=lfs merge=lfs -text
37
+ *.sc filter=lfs diff=lfs merge=lfs -text
38
+
39
+ # Houdini rendering files
40
+ *.ifd filter=lfs diff=lfs merge=lfs -text
41
+ *.ass filter=lfs diff=lfs merge=lfs -text
42
+ *.rib filter=lfs diff=lfs merge=lfs -text
43
+ *.mi filter=lfs diff=lfs merge=lfs -text
44
+
45
+ # Houdini volume files
46
+ *.vdb filter=lfs diff=lfs merge=lfs -text
47
+ *.vol filter=lfs diff=lfs merge=lfs -text
48
+
49
+ # Houdini animation/mocap files
50
+ *.bclip filter=lfs diff=lfs merge=lfs -text
51
+ *.clip filter=lfs diff=lfs merge=lfs -text
52
+ *.chan filter=lfs diff=lfs merge=lfs -text
53
+
54
+ # Video files (often used in Houdini)
55
+ *.mov filter=lfs diff=lfs merge=lfs -text
56
+ *.mp4 filter=lfs diff=lfs merge=lfs -text
57
+ *.avi filter=lfs diff=lfs merge=lfs -text
58
+ *.mkv filter=lfs diff=lfs merge=lfs -text
59
+
60
+ # Audio files
61
+ *.wav filter=lfs diff=lfs merge=lfs -text
62
+ *.aiff filter=lfs diff=lfs merge=lfs -text
63
+ *.mp3 filter=lfs diff=lfs merge=lfs -text
64
+
65
+ # Archive files
66
+ *.zip filter=lfs diff=lfs merge=lfs -text
67
+ *.tar.gz filter=lfs diff=lfs merge=lfs -text
68
+ *.tar.bz2 filter=lfs diff=lfs merge=lfs -text
69
+ *.7z filter=lfs diff=lfs merge=lfs -text
@@ -0,0 +1,171 @@
1
+ # Copilot Instructions for Zabob-Houdini
2
+
3
+ ## Project Overview
4
+ Zabob-Houdini is a Python API for creating Houdini node graphs programmatically. This is an early-stage project that provides a simplified interface for building and connecting Houdini nodes.
5
+
6
+ ## Core Architecture Concepts
7
+
8
+ ### Node Graph API Design
9
+ - **`node()`** function: Core API for creating individual nodes
10
+ - Takes `NodeType`, optional name, and keyword attributes
11
+ - Returns `NodeInstance` objects
12
+ - Special `_input` keyword connects 0+ input nodes
13
+ - **`chain()`** function: Creates linear node sequences
14
+ - Connects nodes in sequence automatically
15
+ - Accepts `_input` for external connections
16
+ - Can be nested/spliced into other chains
17
+ - Returns `Chain` objects
18
+ - **Instantiation Pattern**: Both `NodeInstance` and `Chain` use `.create()` method for actual creation
19
+
20
+ ### Project Structure
21
+ - `src/zabob_houdini/`: Main package directory
22
+ - Currently minimal implementation - most API described in README is not yet implemented
23
+ - Uses modern Python packaging with `pyproject.toml` and hatchling
24
+
25
+ ## Development Conventions
26
+
27
+ ### Response Guidelines
28
+ - **Be concise and focused** in all responses to prevent context overflow
29
+ - When performing code changes:
30
+ - Make minimal, targeted edits that address the specific request
31
+ - Avoid explaining what you're doing unless asked
32
+ - Don't repeat information already established in the conversation
33
+ - Skip verbose descriptions of obvious changes
34
+ - **Inline chat spatial signals**: When user starts with "here," "this line," "on this line," etc., make surgical changes at that exact location only
35
+ - **Context preservation**: Prioritize actionable content over explanatory text to maintain focus on technical work
36
+
37
+ ### Python Standards
38
+ - **Compatibility**: Requires Python 3.11+ (pyproject.toml), for hython compatibility.
39
+ - Entry point: `zabob_houdini:main` console script
40
+ - **CLI framework**: Uses Click instead of argparse for command-line interface
41
+ - **Type hints**: Use modern built-in types (`list`, `dict`, `tuple`) instead of `typing.List`, etc.
42
+ - **Docstrings**: Write comprehensive docstrings for all public functions and classes, global variables.
43
+ - Docstrings for global variables should follow the definition of the variable and provide a clear description of its purpose and usage.
44
+ - **Modern constructs**: Use dataclasses, match statements, and other Python 3.13+ features
45
+ - **Parameter typing**: Declare all parameter types explicitly
46
+
47
+ ### Key Files to Understand
48
+ - `README.md`: Contains the complete API specification and usage patterns
49
+ - `src/zabob_houdini/__init__.py`: Current minimal implementation
50
+ - `pyproject.toml`: Project configuration and dependencies
51
+ - `.gitignore`: Python and Houdini-specific ignore patterns
52
+ - `.gitattributes`: LFS configuration for Houdini binary files
53
+ - `.env.example.*`: Platform-specific environment variable templates (users copy to `.env`)
54
+ - `stubs/hou.pyi`: Type stubs for Houdini's `hou` module for development IntelliSense
55
+ - `TODO.md`: Deferred tasks to avoid branching work - add items here instead of implementing immediately
56
+
57
+ ## Implementation Notes
58
+
59
+ ### Current State
60
+ The project is in early development - the README describes the intended API, but implementation is minimal (just a hello world function). When implementing:
61
+
62
+ 1. **Follow the README specification exactly** - it defines the expected behavior
63
+ 2. **Implement the `node()` and `chain()` functions** as the core API
64
+ 3. **Create `NodeInstance` and `Chain` classes** with `.create()` methods
65
+ 4. **Handle the `_input` keyword parameter** for node connections
66
+ 5. **Start with string-based NodeType** (SOP node names like "box", "merge")
67
+ 6. **Defer `hou` module calls** - only execute during `.create()`, not during node definition
68
+ 7. **Plan for NodeTypeInstance expansion** - namespace resolution for duplicate names across categories
69
+
70
+ ### Integration Considerations
71
+ - **Abstraction Layer**: This is a Python wrapper that calls Houdini's `hou` module during `.create()` execution
72
+ - **Houdini Python compatibility**: Watch for potential issues with `hython` and other Houdini Python tools due to historical version constraints
73
+ - **NodeType Implementation**:
74
+ - Initially: strings representing SOP node type names (e.g., "box", "merge", "transform")
75
+ - Future: `NodeTypeInstance` objects to resolve namespace conflicts across categories
76
+ - Long-term: Context-aware validation (e.g., SOPs under `geo` nodes)
77
+ - **Creation Pattern**: Nodes are defined declaratively, then `.create()` calls `hou` module functions
78
+
79
+ ## Testing & Development
80
+ - **Testing**: Uses pytest framework for testing
81
+ - **Package management**: Uses UV - always run `uv sync` after modifying dependencies in pyproject.toml
82
+ - **Code organization**: Consider dataclasses for structured data (e.g., node configurations)
83
+ - **Modern Python**: Leverage Python 3.13+ features like improved type hints and pattern matching
84
+ - No CI/CD setup yet - runs as console application via entry point
85
+ - Development should focus on implementing the API described in README.md first
86
+
87
+ ### Test Architecture
88
+ - **Unit Tests**: Test object construction, equality, hashing, copying WITHOUT importing hou
89
+ - **NEVER mock modules** - restructure tests to avoid import issues instead
90
+ - Focus on dataclass behavior, caching via @functools.cache, immutability
91
+ - Test the API functions (node(), chain()) rather than classes directly if needed
92
+ - **Integration Tests**: Use `hython_test` fixture to run actual Houdini operations
93
+ - Never mock hou in integration tests - they should run in real Houdini environment
94
+ - Call functions in `houdini_test_functions.py` via the bridge
95
+
96
+ ### Module Import Strategy
97
+ - core.py imports hou at module level - this is correct for Houdini environment
98
+ - Unit tests should avoid importing core directly if it causes hou import issues
99
+ - Integration tests run in Houdini via hython_test fixture
100
+
101
+ ### Context Overflow Prevention
102
+ - **Be extremely concise** - this project has hit context limits multiple times
103
+ - When architectural changes are needed, focus on ONE specific issue at a time
104
+ - Don't explain what you're doing unless asked - just make the minimal change
105
+ - If you find yourself in an edit loop, stop and ask for clarification
106
+ - The core.py architecture is mature - be very cautious about major changes
107
+
108
+ ### Immutable Architecture (ESTABLISHED - Don't Change)
109
+ - Frozen dataclasses with @functools.cache for automatic caching
110
+ - HashableMapping for dict fields to enable hashing
111
+ - Objects constructed without hou imports, hou only imported in .create() methods
112
+ - Identity-based hashing allows object-specific caching
113
+ - .copy() methods create deep copies, .create() returns cached hou.Node instances
114
+
115
+ ## Task Management
116
+ - **TODO.md**: Use for deferred tasks to avoid branching current work
117
+ - When encountering complex tasks that would derail current focus, add to TODO.md instead of implementing
118
+ - Keep TODO.md organized with categories and clear descriptions
119
+ - Mark completed items and remove them periodically
120
+
121
+ ## VS Code Configuration Management
122
+ - **Personal Settings**: `.vscode/settings.json` is personal (not committed) and created from `.vscode/settings.json.example`
123
+ - **Template Sync**: When `.vscode/settings.json` is modified with project-relevant changes, remind the user to update `.vscode/settings.json.example` for other contributors
124
+ - **Project Files**: `.vscode/project-dictionary.txt`, `.vscode/extensions.json`, and `.vscode/setup-vscode.sh` are committed
125
+ - **Setup Script**: New contributors use `./.vscode/setup-vscode.sh` for automated setup
126
+ - **Spell Checking**: Uses cSpell with project dictionary - add technical terms to `.vscode/project-dictionary.txt`
127
+
128
+ ## Communication Guidelines
129
+
130
+ ### Avoid Sycophantic Language
131
+ - **NEVER** use phrases like "You're absolutely right!", "You're absolutely correct!", "Excellent point!", or similar flattery
132
+ - **NEVER** validate statements as "right" when the user didn't make a factual claim that could be evaluated
133
+ - **NEVER** use general praise or validation as conversational filler
134
+
135
+ ### Appropriate Acknowledgments
136
+ Use brief, factual acknowledgments only to confirm understanding of instructions:
137
+ - "Got it."
138
+ - "Ok, that makes sense."
139
+ - "I understand."
140
+ - "I see the issue."
141
+
142
+ These should only be used when:
143
+ 1. You genuinely understand the instruction and its reasoning
144
+ 2. The acknowledgment adds clarity about what you'll do next
145
+ 3. You're confirming understanding of a technical requirement or constraint
146
+
147
+ ### Examples
148
+
149
+ #### ❌ Inappropriate (Sycophantic)
150
+ User: "Yes please."
151
+ Assistant: "You're absolutely right! That's a great decision."
152
+
153
+ User: "Let's remove this unused code."
154
+ Assistant: "Excellent point! You're absolutely correct that we should clean this up."
155
+
156
+ #### ✅ Appropriate (Brief Acknowledgment)
157
+ User: "Yes please."
158
+ Assistant: "Got it." [proceeds with the requested action]
159
+
160
+ User: "Let's remove this unused code."
161
+ Assistant: "I'll remove the unused code path." [proceeds with removal]
162
+
163
+ #### ✅ Also Appropriate (No Acknowledgment)
164
+ User: "Yes please."
165
+ Assistant: [proceeds directly with the requested action]
166
+
167
+ ### Rationale
168
+ - Maintains professional, technical communication
169
+ - Avoids artificial validation of non-factual statements
170
+ - Focuses on understanding and execution rather than praise
171
+ - Prevents misrepresenting user statements as claims that could be "right" or "wrong"
@@ -0,0 +1,197 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ # Trigger on tags that look like version numbers
5
+ push:
6
+ tags:
7
+ - 'v*.*.*'
8
+
9
+ # Allow manual trigger from GitHub UI
10
+ workflow_dispatch:
11
+ inputs:
12
+ pypi_repository:
13
+ description: 'PyPI repository to publish to'
14
+ required: true
15
+ default: 'testpypi'
16
+ type: choice
17
+ options:
18
+ - testpypi
19
+ - pypi
20
+
21
+ permissions:
22
+ contents: read
23
+ id-token: write # Required for trusted publishing
24
+
25
+ jobs:
26
+ build:
27
+ name: Build package
28
+ runs-on: ubuntu-latest
29
+
30
+ steps:
31
+ - name: Checkout repository
32
+ uses: actions/checkout@v4
33
+
34
+ - name: Set up Python
35
+ uses: actions/setup-python@v5
36
+ with:
37
+ python-version: '3.13'
38
+
39
+ - name: Install UV
40
+ uses: astral-sh/setup-uv@v3
41
+ with:
42
+ enable-cache: true
43
+
44
+ - name: Install dependencies
45
+ run: uv sync --all-extras --dev
46
+
47
+ - name: Run unit tests (excluding Houdini integration tests)
48
+ run: uv run pytest -m "unit and not integration" -v
49
+
50
+ - name: Run spell check
51
+ run: |
52
+ npm install
53
+ npm run spell-check
54
+
55
+ - name: Build package
56
+ run: uv build
57
+
58
+ - name: Verify package contents
59
+ run: |
60
+ echo "Built packages:"
61
+ ls -la dist/
62
+ echo "Package info:"
63
+ uv run python -m tarfile -l dist/*.tar.gz
64
+
65
+ - name: Upload build artifacts
66
+ uses: actions/upload-artifact@v4
67
+ with:
68
+ name: python-package-distributions
69
+ path: dist/
70
+ retention-days: 7
71
+
72
+ publish-testpypi:
73
+ name: Publish to TestPyPI
74
+ runs-on: ubuntu-latest
75
+ needs: build
76
+ if: github.event_name == 'workflow_dispatch' && github.event.inputs.pypi_repository == 'testpypi'
77
+
78
+ environment:
79
+ name: testpypi
80
+ url: https://test.pypi.org/p/zabob-houdini
81
+
82
+ steps:
83
+ - name: Download build artifacts
84
+ uses: actions/download-artifact@v4
85
+ with:
86
+ name: python-package-distributions
87
+ path: dist/
88
+
89
+ - name: Publish to TestPyPI
90
+ uses: pypa/gh-action-pypi-publish@release/v1
91
+ with:
92
+ repository-url: https://test.pypi.org/legacy/
93
+ print-hash: true
94
+
95
+ publish-pypi:
96
+ name: Publish to PyPI
97
+ runs-on: ubuntu-latest
98
+ needs: build
99
+ if: |
100
+ (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) ||
101
+ (github.event_name == 'workflow_dispatch' && github.event.inputs.pypi_repository == 'pypi')
102
+
103
+ environment:
104
+ name: pypi
105
+ url: https://pypi.org/p/zabob-houdini
106
+
107
+ steps:
108
+ - name: Download build artifacts
109
+ uses: actions/download-artifact@v4
110
+ with:
111
+ name: python-package-distributions
112
+ path: dist/
113
+
114
+ - name: Publish to PyPI
115
+ uses: pypa/gh-action-pypi-publish@release/v1
116
+ with:
117
+ print-hash: true
118
+
119
+ create-release:
120
+ name: Create GitHub Release
121
+ runs-on: ubuntu-latest
122
+ needs: publish-pypi
123
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
124
+
125
+ permissions:
126
+ contents: write
127
+
128
+ steps:
129
+ - name: Checkout repository
130
+ uses: actions/checkout@v4
131
+
132
+ - name: Download build artifacts
133
+ uses: actions/download-artifact@v4
134
+ with:
135
+ name: python-package-distributions
136
+ path: dist/
137
+
138
+ - name: Extract version from tag
139
+ id: version
140
+ run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
141
+
142
+ - name: Extract changelog for this version
143
+ id: changelog
144
+ run: |
145
+ if [ -f "CHANGELOG.md" ]; then
146
+ # Extract changelog section for this version
147
+ awk "/^## \[?v?${{ steps.version.outputs.version }}\]?/,/^## \[?v?[0-9]/ { if (/^## \[?v?[0-9]/ && !/^## \[?v?${{ steps.version.outputs.version }}\]?/) exit; print }" CHANGELOG.md | head -n -1 > changelog_section.md
148
+ if [ -s changelog_section.md ]; then
149
+ echo "changelog_content<<EOF" >> $GITHUB_OUTPUT
150
+ cat changelog_section.md >> $GITHUB_OUTPUT
151
+ echo "EOF" >> $GITHUB_OUTPUT
152
+ else
153
+ echo "changelog_content=" >> $GITHUB_OUTPUT
154
+ fi
155
+ else
156
+ echo "changelog_content=" >> $GITHUB_OUTPUT
157
+ fi
158
+
159
+ - name: Generate release notes from commits
160
+ id: commits
161
+ run: |
162
+ # Get previous tag
163
+ PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
164
+ if [ -n "$PREV_TAG" ]; then
165
+ echo "commits_content<<EOF" >> $GITHUB_OUTPUT
166
+ echo "### Changes since $PREV_TAG" >> $GITHUB_OUTPUT
167
+ echo "" >> $GITHUB_OUTPUT
168
+ git log --oneline --no-merges $PREV_TAG..HEAD | sed 's/^/- /' >> $GITHUB_OUTPUT
169
+ echo "EOF" >> $GITHUB_OUTPUT
170
+ else
171
+ echo "commits_content=" >> $GITHUB_OUTPUT
172
+ fi
173
+
174
+ - name: Create GitHub Release
175
+ uses: softprops/action-gh-release@v2
176
+ with:
177
+ name: Release v${{ steps.version.outputs.version }}
178
+ body: |
179
+ ## Zabob-Houdini v${{ steps.version.outputs.version }}
180
+
181
+ This release has been automatically published to PyPI.
182
+
183
+ ### Installation
184
+ ```bash
185
+ pip install zabob-houdini==${{ steps.version.outputs.version }}
186
+ ```
187
+
188
+ ### Development Installation
189
+ ```bash
190
+ uv add zabob-houdini==${{ steps.version.outputs.version }}
191
+ ```
192
+
193
+ ${{ steps.changelog.outputs.changelog_content || steps.commits.outputs.commits_content || '### Changes\n\nSee the [CHANGELOG](CHANGELOG.md) for details about this release.' }}
194
+ files: dist/*
195
+ draft: false
196
+ prerelease: false
197
+ make_latest: true
@@ -0,0 +1,118 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, develop ]
6
+ pull_request:
7
+ branches: [ main, develop ]
8
+
9
+ jobs:
10
+ unit-tests:
11
+ name: Unit Tests (No Houdini)
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ python-version: ['3.11', '3.12', '3.13']
16
+
17
+ steps:
18
+ - name: Checkout repository
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Set up Python ${{ matrix.python-version }}
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+
26
+ - name: Install UV
27
+ uses: astral-sh/setup-uv@v3
28
+ with:
29
+ enable-cache: true
30
+
31
+ - name: Install dependencies
32
+ run: uv sync --all-extras --dev
33
+
34
+ - name: Run unit tests (excluding Houdini integration tests)
35
+ run: |
36
+ uv run pytest -m "unit and not integration" -v \
37
+ --tb=short \
38
+ --junit-xml=test-results-${{ matrix.python-version }}.xml
39
+
40
+ - name: Upload test results
41
+ uses: actions/upload-artifact@v4
42
+ if: always()
43
+ with:
44
+ name: test-results-${{ matrix.python-version }}
45
+ path: test-results-${{ matrix.python-version }}.xml
46
+ retention-days: 30
47
+
48
+ lint-and-type-check:
49
+ name: Linting and Type Checking
50
+ runs-on: ubuntu-latest
51
+
52
+ steps:
53
+ - name: Checkout repository
54
+ uses: actions/checkout@v4
55
+
56
+ - name: Set up Python
57
+ uses: actions/setup-python@v5
58
+ with:
59
+ python-version: '3.13'
60
+
61
+ - name: Install UV
62
+ uses: astral-sh/setup-uv@v3
63
+ with:
64
+ enable-cache: true
65
+
66
+ - name: Install dependencies
67
+ run: uv sync --all-extras --dev
68
+
69
+ - name: Run spell check
70
+ run: |
71
+ npm install
72
+ npm run spell-check
73
+
74
+ - name: Run type checking (if mypy is available)
75
+ run: |
76
+ # Test if mypy can run without segfaulting
77
+ if timeout 10s uv run mypy --version >/dev/null 2>&1; then
78
+ echo "Running mypy type checking..."
79
+ uv run mypy src/zabob_houdini --ignore-missing-imports || echo "MyPy found issues but continuing..."
80
+ else
81
+ echo "Mypy not available or incompatible with this Python version, skipping type checking"
82
+ fi
83
+ continue-on-error: true
84
+
85
+ integration-tests:
86
+ name: Integration Tests (Houdini Required)
87
+ runs-on: ubuntu-latest
88
+ # Only run integration tests on main branch or when explicitly requested
89
+ if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'test-integration')
90
+
91
+ steps:
92
+ - name: Checkout repository
93
+ uses: actions/checkout@v4
94
+
95
+ - name: Set up Python
96
+ uses: actions/setup-python@v5
97
+ with:
98
+ python-version: '3.13'
99
+
100
+ - name: Install UV
101
+ uses: astral-sh/setup-uv@v3
102
+ with:
103
+ enable-cache: true
104
+
105
+ - name: Install dependencies
106
+ run: uv sync --all-extras --dev
107
+
108
+ - name: Skip integration tests (Houdini not available in CI)
109
+ run: |
110
+ echo "Integration tests require Houdini installation."
111
+ echo "These tests are skipped in CI but can be run locally with:"
112
+ echo " uv run pytest tests/test_core_caching.py tests/test_houdini_integration.py -v"
113
+ echo ""
114
+ echo "To run with Houdini locally:"
115
+ echo "1. Install Houdini"
116
+ echo "2. Set up your .env file with HOUDINI_PATH"
117
+ echo "3. Run: uv run pytest tests/ -v"
118
+ exit 0