Templates and Package Overrides
Relevant source files
The following files were used as context for generating this wiki page:
Templates and package overrides provide the mechanism for customizing OPSI package generation within the auto-opsi-repo system. The builder uses Jinja2 templates to generate standard OPSI scripts (setup.opsiscript, uninstall.opsiscript) and metadata files (control.toml). While the project provides default templates, administrators can supply product-specific overrides to handle complex installation scenarios or custom detection logic.
Sources: README.md:27-31, builder/builder/opsi.py:22-30
Template Architecture and Generation
The generate function in builder/builder/opsi.py manages the creation of the OPSI source directory. It populates the CLIENT_DATA and OPSI directories by rendering templates with a specific context. This context includes the package definition, version strings, and revision numbers.
Core Template Files
The builder relies on several base templates located in the builder/templates directory. These files define the standard behavior for all packages unless an override is present.
| File | Type | Destination | Description |
|---|---|---|---|
control.toml.j2 | Jinja2 | OPSI/control.toml | Defines OPSI product and package metadata. |
setup.opsiscript.j2 | Jinja2 | CLIENT_DATA/setup.opsiscript | Primary OPSI installation script. |
uninstall.opsiscript.j2 | Jinja2 | CLIENT_DATA/uninstall.opsiscript | Primary OPSI uninstallation script. |
runtime.ps1 | PowerShell | CLIENT_DATA/runtime.ps1 | Shared helper functions for detection and execution. |
install.ps1 | PowerShell | CLIENT_DATA/install.ps1 | Entry point for PowerShell-based installation. |
uninstall.ps1 | PowerShell | CLIENT_DATA/uninstall.ps1 | Entry point for PowerShell-based uninstallation. |
Sources: builder/builder/opsi.py:53-70, README.md:214-220
Generation Logic Flow
The following diagram illustrates how the builder combines templates and overrides to create the final OPSI source files.
The generation process ensures that product-specific requirements take precedence over global defaults while maintaining a consistent structure for the OPSI tooling.
Sources: builder/builder/opsi.py:22-77
Package Overrides
Package overrides allow for granular control over individual OPSI products. You place override files in the catalog/overrides/<opsi_product_id>/ directory. The builder automatically detects these files during the build cycle.
Script Injections
The catalog allows injecting custom OPSI script snippets into specific lifecycle phases. These are referenced in catalog/packages.yaml and must exist within the product's override directory.
pre_install_opsi_scriptpost_install_opsi_scriptpre_uninstall_opsi_scriptpost_uninstall_opsi_script
Sources: README.md:214-222, builder/builder/opsi.py:38-49
Custom Detection Scripts
When detection.method is set to custom, you must provide a detection.script in the override directory. This script replaces the standard detection block in the OPSI script and owns the logic for identifying if the application is already installed.
if package["detection"]["method"] == "custom":
path = (override / package["detection"]["script"]).resolve()
if not path.is_relative_to(override.resolve()):
raise ValueError("Override traversal")
context["custom_detection"] = env.from_string(path.read_text()).render(**context)
Sources: builder/builder/opsi.py:50-55, README.md:223-225
PowerShell Runtime and Configuration
The runtime.ps1 script is a critical component copied into every package's CLIENT_DATA. It reads config.json at runtime on the Windows client to perform version detection and execute installers.
Version Detection Logic
The PowerShell helper supports multiple detection methods, which are configured via the catalog/packages.yaml file and passed through config.json.
| Method | Source Field | Description |
|---|---|---|
registry | display_name_regex | Scans HKLM Uninstall keys (32 and 64-bit) for matches. |
msi | product_code | Matches specific MSI UpgradeCodes or ProductCodes. |
file_version | path | Checks the ProductVersion of a specific file on disk. |
custom | script | Executes an OPSI snippet override for detection. |
Sources: builder/templates/runtime.ps1:7-46, README.md:195-205
Config Generation Snippet
The builder prepares a config.json for the client, including any version normalization or regex extraction required for the specific vendor.
detection_version = (release.detection_version or release.version).lstrip("v")
for old, new in package["detection"].get("version_replacements", {}).items():
detection_version = detection_version.replace(old, new)
config = {**package, "detection_version": detection_version}
(client / "config.json").write_text(json.dumps(config, indent=2))
Sources: builder/builder/opsi.py:72-83
Security Constraints
The template and override system implements basic security guards to prevent directory traversal and unauthorized execution.
- Traversal Protection: All override file paths are resolved and checked to ensure they remain within the product's specific override directory.
- No Linux Shell Execution: The builder never executes the downloaded vendor scripts or OPSI snippets on the Linux host; it only renders them for later execution on Windows clients.
- Strict Context: Jinja2 is configured with
StrictUndefinedto ensure build failures occur if required variables are missing from the context.
Sources: builder/builder/opsi.py:44-46, README.md:243-247
Summary: The template and override system enables the project to handle diverse Windows installers by combining standardized OPSI script structures with highly specific product customizations and a robust PowerShell runtime.