Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions .authors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@
github: deepeshaburse
- name: pre-commit-ci[bot]
email: 66853113+pre-commit-ci[bot]@users.noreply.github.com
num_commits: 89
num_commits: 92
first_commit: 2023-05-02 12:01:43
github: pre-commit-ci[bot]
- name: Matthias Kuhn
Expand All @@ -382,7 +382,7 @@
github: m-kuhn
- name: dependabot[bot]
email: 49699333+dependabot[bot]@users.noreply.github.com
num_commits: 87
num_commits: 89
github: dependabot[bot]
first_commit: 2024-05-07 10:16:05
- name: Julien Jerphanion
Expand All @@ -405,7 +405,7 @@
github: Jrice1317
alternate_emails:
- 100002667+Jrice1317@users.noreply.github.com
num_commits: 6
num_commits: 7
first_commit: 2025-07-30 14:27:00
- name: dionizijefa
github: dionizijefa
Expand All @@ -420,7 +420,7 @@
email: 34315751+lrandersson@users.noreply.github.com
alternate_emails:
- randersson@anaconda.com
num_commits: 35
num_commits: 39
first_commit: 2025-10-21 08:30:00
- name: David Laehnemann
github: dlaehnemann
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
[//]: # (current developments)

## 2026-07-07 3.16.2:
### Enhancements

* Add a `--installer-type` command line option to build only a single installer type (`sh`, `pkg`, `exe`, or `msi`), overriding `installer_type` from the configuration. (#1266)

### Bug fixes

* Fix `AzureSignTool` signature verification failing for installer paths that contain spaces. (#1273)
* Improve the EXE installer path length check added in #1228 to also account for the package cache extraction path (`pkgs/<name-version-build>/`), preventing installations that were allowed to proceed from failing to extract packages when the install path was long. (#1282)

### Contributors

* @lrandersson



## 2026-06-22 3.16.1:
### Bug fixes

Expand Down
5 changes: 3 additions & 2 deletions CONSTRUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,9 @@ Path to a post-install script. Some notes:
`%INSTALLER_PLAT%` environment variables. `%INSTALLER_TYPE%` is set to `EXE`.
`%INSTALLER_UNATTENDED%` will be `"1"` in silent mode (`/S`), `"0"` otherwise.
- For Windows `.msi` installers, the script must be a `.bat` file.
The same variables as `.exe` installers are available, except
`%INSTALLER_TYPE%` is set to `MSI` and `%INSTALLER_UNATTENDED%` is not available.
The same variables as `.exe` installers are available.
`%INSTALLER_TYPE%` is set to `MSI`.
`%INSTALLER_UNATTENDED%` will be `"1"` in silent mode (`msiexec /qn`), `"0"` otherwise.

If necessary, you can activate the installed `base` environment like this:

Expand Down
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Contributing to Constructor

Thank you for your interest in contributing to Constructor!

Please refer to the [conda contributing guide](https://docs.conda.io/projects/conda/en/latest/dev-guide/contributing.html) for general guidelines on how to contribute to conda projects.

For Constructor-specific questions or issues, please open an issue on the [issue tracker](https://github.com/conda/constructor/issues) or start a discussion.

Please also review our [Code of Conduct](https://github.com/conda/constructor/blob/main/CODE_OF_CONDUCT.md) before contributing.
5 changes: 3 additions & 2 deletions constructor/_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,8 +561,9 @@ class ConstructorConfiguration(BaseModel):
`%INSTALLER_PLAT%` environment variables. `%INSTALLER_TYPE%` is set to `EXE`.
`%INSTALLER_UNATTENDED%` will be `"1"` in silent mode (`/S`), `"0"` otherwise.
- For Windows `.msi` installers, the script must be a `.bat` file.
The same variables as `.exe` installers are available, except
`%INSTALLER_TYPE%` is set to `MSI` and `%INSTALLER_UNATTENDED%` is not available.
The same variables as `.exe` installers are available.
`%INSTALLER_TYPE%` is set to `MSI`.
`%INSTALLER_UNATTENDED%` will be `"1"` in silent mode (`msiexec /qn`), `"0"` otherwise.
If necessary, you can activate the installed `base` environment like this:
Expand Down
3 changes: 0 additions & 3 deletions constructor/briefcase/pre_uninstall.bat
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ set "INSTALLER_NAME={{ installer_name }}"
set "INSTALLER_VER={{ installer_version }}"
set "INSTALLER_PLAT={{ installer_platform }}"
set "INSTALLER_TYPE=MSI"
rem INSTALLER_UNATTENDED is not available for MSI installers.
rem Detecting silent mode requires UILevel from WiX, which would need
rem changes to the briefcase-windows-app-template to pass to this script.

rem Determine install mode from .nonadmin marker file written at install time
if exist "%BASE_PATH%\.nonadmin" (
Expand Down
3 changes: 0 additions & 3 deletions constructor/briefcase/run_installation.bat
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@ set "INSTALLER_NAME={{ installer_name }}"
set "INSTALLER_VER={{ installer_version }}"
set "INSTALLER_PLAT={{ installer_platform }}"
set "INSTALLER_TYPE=MSI"
rem INSTALLER_UNATTENDED is not available for MSI installers.
rem Detecting silent mode requires UILevel from WiX, which would need
rem changes to the briefcase-windows-app-template to pass to this script.

{%- if add_debug %}
>> "%LOG%" echo ==== run_installation start ====
Expand Down
2 changes: 1 addition & 1 deletion constructor/data/construct.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1053,7 +1053,7 @@
}
],
"default": null,
"description": "Path to a post-install script. Some notes:\n- For Unix `.sh` installers, the shebang line is respected if present; otherwise, the script is run by the POSIX shell `sh`. Note that the use of a shebang can reduce the portability of the installer. The installation path is available as `${PREFIX}`. Installer metadata is available in the `${INSTALLER_NAME}`, `${INSTALLER_VER}`, `${INSTALLER_PLAT}` environment variables. `${INSTALLER_TYPE}` is set to `SH`. `${INSTALLER_UNATTENDED}` will be `\"1\"` in batch mode (`-b`), `\"0\"` otherwise.\n- For PKG installers, the shebang line is respected if present; otherwise, `bash` is used. The same variables mentioned for `sh` installers are available here. `${INSTALLER_TYPE}` is set to `PKG`. `${INSTALLER_UNATTENDED}` will be `\"1\"` for command line installs, `\"0\"` otherwise.\n- For Windows `.exe` installers, the script must be a `.bat` file. Installation path is available as `%PREFIX%`. Metadata about the installer can be found in the `%INSTALLER_NAME%`, `%INSTALLER_VER%`, `%INSTALLER_PLAT%` environment variables. `%INSTALLER_TYPE%` is set to `EXE`. `%INSTALLER_UNATTENDED%` will be `\"1\"` in silent mode (`/S`), `\"0\"` otherwise.\n- For Windows `.msi` installers, the script must be a `.bat` file. The same variables as `.exe` installers are available, except `%INSTALLER_TYPE%` is set to `MSI` and `%INSTALLER_UNATTENDED%` is not available.\nIf necessary, you can activate the installed `base` environment like this:\n- Unix: `. \"$PREFIX/etc/profile.d/conda.sh\" && conda activate \"$PREFIX\"`\n- Windows: `call \"%PREFIX%\\Scripts\\activate.bat\"`",
"description": "Path to a post-install script. Some notes:\n- For Unix `.sh` installers, the shebang line is respected if present; otherwise, the script is run by the POSIX shell `sh`. Note that the use of a shebang can reduce the portability of the installer. The installation path is available as `${PREFIX}`. Installer metadata is available in the `${INSTALLER_NAME}`, `${INSTALLER_VER}`, `${INSTALLER_PLAT}` environment variables. `${INSTALLER_TYPE}` is set to `SH`. `${INSTALLER_UNATTENDED}` will be `\"1\"` in batch mode (`-b`), `\"0\"` otherwise.\n- For PKG installers, the shebang line is respected if present; otherwise, `bash` is used. The same variables mentioned for `sh` installers are available here. `${INSTALLER_TYPE}` is set to `PKG`. `${INSTALLER_UNATTENDED}` will be `\"1\"` for command line installs, `\"0\"` otherwise.\n- For Windows `.exe` installers, the script must be a `.bat` file. Installation path is available as `%PREFIX%`. Metadata about the installer can be found in the `%INSTALLER_NAME%`, `%INSTALLER_VER%`, `%INSTALLER_PLAT%` environment variables. `%INSTALLER_TYPE%` is set to `EXE`. `%INSTALLER_UNATTENDED%` will be `\"1\"` in silent mode (`/S`), `\"0\"` otherwise.\n- For Windows `.msi` installers, the script must be a `.bat` file. The same variables as `.exe` installers are available. `%INSTALLER_TYPE%` is set to `MSI`. `%INSTALLER_UNATTENDED%` will be `\"1\"` in silent mode (`msiexec /qn`), `\"0\"` otherwise.\nIf necessary, you can activate the installed `base` environment like this:\n- Unix: `. \"$PREFIX/etc/profile.d/conda.sh\" && conda activate \"$PREFIX\"`\n- Windows: `call \"%PREFIX%\\Scripts\\activate.bat\"`",
"title": "Post Install"
},
"post_install_desc": {
Expand Down
6 changes: 6 additions & 0 deletions constructor/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,9 @@ def error_body(self):
"""),
]
)


class InvalidInstallerTypeError(Exception):
"""Raised when installer_type is invalid for the target platform."""

pass
38 changes: 26 additions & 12 deletions constructor/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from .construct import parse as construct_parse
from .construct import render as construct_render
from .construct import verify as construct_verify
from .exceptions import InvalidInstallerTypeError
from .fcp import main as fcp_main
from .utils import (
StandaloneExe,
Expand All @@ -46,6 +47,13 @@


def get_installer_type(info: dict):
"""Return the installer type(s) to build for the given platform.

Raises
------
InvalidInstallerTypeError
If the installer type is not valid for the target platform.
"""
osname, unused_arch = info["_platform"].split("-")

os_allowed = {"linux": ("sh",), "osx": ("sh", "pkg"), "win": ("exe", "msi")}
Expand All @@ -62,28 +70,31 @@ def get_installer_type(info: dict):
for t in itype:
if t not in all_allowed:
all_allowed_str = ", ".join(sorted(all_allowed))
sys.exit("Error: invalid installer type '%s'; allowed: %s" % (t, all_allowed_str))
raise InvalidInstallerTypeError(
f"invalid installer type '{t}'; allowed: {all_allowed_str}"
)
if t not in os_allowed[osname]:
os_allowed_str = ", ".join(sorted(os_allowed[osname]))
sys.exit(
"Error: invalid installer type '%s' for %s; allowed: %s"
% (t, osname, os_allowed_str)
raise InvalidInstallerTypeError(
f"invalid installer type '{t}' for {osname}; allowed: {os_allowed_str}"
)
return tuple(itype)
elif itype not in all_allowed:
all_allowed = ", ".join(sorted(all_allowed))
sys.exit("Error: invalid installer type '%s'; allowed: %s" % (itype, all_allowed))
all_allowed_str = ", ".join(sorted(all_allowed))
raise InvalidInstallerTypeError(
f"invalid installer type '{itype}'; allowed: {all_allowed_str}"
)
elif itype == "docker":
if osname != "linux":
sys.exit(
"Error: Docker features are only supported for Linux target platforms. "
raise InvalidInstallerTypeError(
"Docker features are only supported for Linux target platforms. "
"Use --platform linux-ARCH to build a Docker artifact."
)
return ("sh", "docker")
elif itype not in os_allowed[osname]:
os_allowed = ", ".join(sorted(os_allowed[osname]))
sys.exit(
"Error: invalid installer type '%s' for %s; allowed: %s" % (itype, osname, os_allowed)
os_allowed_str = ", ".join(sorted(os_allowed[osname]))
raise InvalidInstallerTypeError(
f"invalid installer type '{itype}' for {osname}; allowed: {os_allowed_str}"
)
else:
return (itype,)
Expand Down Expand Up @@ -234,7 +245,10 @@ def main_build(
info["_debug"] = debug
if installer_type:
info["installer_type"] = installer_type
itypes = get_installer_type(info)
try:
itypes = get_installer_type(info)
except InvalidInstallerTypeError as e:
sys.exit(f"Error: {e}")

if "docker" in itypes:
if not info.get("docker_base_image"):
Expand Down
2 changes: 1 addition & 1 deletion dev/extra-requirements-windows.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
conda-forge::briefcase>=0.3.26
conda-forge::briefcase>=0.4.3
conda-forge::nsis>=3.08=*_log_*
conda-forge::tomli-w>=1.2.0
5 changes: 3 additions & 2 deletions docs/source/construct-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,9 @@ Path to a post-install script. Some notes:
`%INSTALLER_PLAT%` environment variables. `%INSTALLER_TYPE%` is set to `EXE`.
`%INSTALLER_UNATTENDED%` will be `"1"` in silent mode (`/S`), `"0"` otherwise.
- For Windows `.msi` installers, the script must be a `.bat` file.
The same variables as `.exe` installers are available, except
`%INSTALLER_TYPE%` is set to `MSI` and `%INSTALLER_UNATTENDED%` is not available.
The same variables as `.exe` installers are available.
`%INSTALLER_TYPE%` is set to `MSI`.
`%INSTALLER_UNATTENDED%` will be `"1"` in silent mode (`msiexec /qn`), `"0"` otherwise.

If necessary, you can activate the installed `base` environment like this:

Expand Down
4 changes: 3 additions & 1 deletion examples/azure_signtool/construct.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

name: Signed_AzureSignTool
version: 1.0.0
installer_type: [exe, msi]
installer_type:
- exe
- msi
channels:
- https://repo.anaconda.com/pkgs/main/
specs:
Expand Down
3 changes: 1 addition & 2 deletions examples/scripts/post_install.bat
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ if not "%INSTALLER_NAME%" == "Scripts" exit 1
if not "%INSTALLER_VER%" == "1.0.0" exit 1
if not "%INSTALLER_PLAT%" == "win-64" exit 1
if not "%INSTALLER_TYPE%" == "EXE" if not "%INSTALLER_TYPE%" == "MSI" exit 1
rem INSTALLER_UNATTENDED is not yet set by MSI installers (see conda/constructor#1276).
if "%INSTALLER_TYPE%" == "EXE" if not "%INSTALLER_UNATTENDED%" == "1" exit 1
if not "%INSTALLER_UNATTENDED%" == "1" exit 1
if "%PREFIX%" == "" exit 1
if not "%CUSTOM_VARIABLE_1%" == "FIR$T-CUSTOM_STRING WITH SPACES AND @*! CHARACTERS" exit 1
if not "%CUSTOM_VARIABLE_2%" == "$ECOND-CUSTOM_STRING WITH SPACES AND @*! CHARACTERS" exit 1
Expand Down
3 changes: 1 addition & 2 deletions examples/scripts/pre_install.bat
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ if not "%INSTALLER_NAME%" == "Scripts" exit 1
if not "%INSTALLER_VER%" == "1.0.0" exit 1
if not "%INSTALLER_PLAT%" == "win-64" exit 1
if not "%INSTALLER_TYPE%" == "EXE" if not "%INSTALLER_TYPE%" == "MSI" exit 1
rem INSTALLER_UNATTENDED is not yet set by MSI installers (see conda/constructor#1276).
if "%INSTALLER_TYPE%" == "EXE" if not "%INSTALLER_UNATTENDED%" == "1" exit 1
if not "%INSTALLER_UNATTENDED%" == "1" exit 1
if "%PREFIX%" == "" exit 1
if not "%CUSTOM_VARIABLE_1%" == "FIR$T-CUSTOM_STRING WITH SPACES AND @*! CHARACTERS" exit 1
if not "%CUSTOM_VARIABLE_2%" == "$ECOND-CUSTOM_STRING WITH SPACES AND @*! CHARACTERS" exit 1
Expand Down
2 changes: 2 additions & 0 deletions news/1231.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
### Docs
* Added `CONTRIBUTING.md` to fix broken link in PR workflow template (#1231).
19 changes: 0 additions & 19 deletions news/1266-add-installer-type-cli

This file was deleted.

19 changes: 0 additions & 19 deletions news/1273-fix-msi-signing-spaces

This file was deleted.

19 changes: 19 additions & 0 deletions news/1276-msi-installer-unattended
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### Enhancements

* <news item>

### Bug fixes

* <news item>

### Deprecations

* <news item>

### Docs

* Updated docs to clarify the use of `INSTALLER_UNATTENDED` within the context of MSI installers, and updated minimum `briefcase` version to `0.4.3`. (#1278)

### Other

* <news item>
19 changes: 0 additions & 19 deletions news/1282-fix-exe-pkgs-cache-path-length

This file was deleted.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ dependencies = [
"pillow >=3.1 ; platform_system=='Windows' or platform_system=='Darwin'",
"jinja2",
"jsonschema >=4",
"briefcase >=0.3.26 ; platform_system=='Windows'",
"briefcase >=0.4.3 ; platform_system=='Windows'",
"tomli-w >=1.2.0 ; platform_system=='Windows'",
]

Expand Down
12 changes: 6 additions & 6 deletions recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ requirements:
- conda-standalone >=24.11.0
- jinja2
- jsonschema >=4
- pillow >=3.1 # [win or osx]
- nsis >=3.08 # [win]
- briefcase >=0.3.26 # [win]
- tomli-w >=1.2.0 # [win]
run_constrained: # [unix]
- nsis >=3.08 # [unix]
- pillow >=3.1 # [win or osx]
- nsis >=3.08 # [win]
- briefcase >=0.4.3 # [win]
- tomli-w >=1.2.0 # [win]
run_constrained: # [unix]
- nsis >=3.08 # [unix]
- conda-libmamba-solver !=24.11.0
- pydantic >=2

Expand Down
1 change: 0 additions & 1 deletion tests/test_briefcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,6 @@ def test_render_templates_installer_metadata(template_name):
assert 'set "INSTALLER_VER=1.0.0"' in text
assert f'set "INSTALLER_PLAT={cc_platform}"' in text
assert 'set "INSTALLER_TYPE=MSI"' in text
assert "INSTALLER_UNATTENDED is not available" in text


@pytest.mark.skipif(sys.platform != "win32", reason="Windows-only")
Expand Down
Loading