Skip to content

Commit

Permalink
Add optional_distros support
Browse files Browse the repository at this point in the history
This provides a way to suggest blessed `--requirement`'s. This includes
a description and default value.
  • Loading branch information
MHendricks committed Jul 22, 2024
1 parent 48a96d6 commit f2254b1
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 16 deletions.
76 changes: 72 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,44 @@ $ hab env projectDummy/Thug
The cli prompt is updated while inside a hab config is active. It is `[URI] [cwd]`
Where URI is the URI requested and cwd is the current working directory.

### Forced Requirements

In most cases you will associate distros with URI's by specifying them in [config](#config)
files with the [distros](#defining-distros) property.

There are times when you want to quickly load a new distro or force a distro to
use other requirements. Here are a few reason why this might be useful.

- Change the version of a distro that is loaded temporarily.
- A plugin that only specific users would find useful, and others find annoying.
For example department specific shelves in Maya/Houdini.
- Loading a plugin that requires an expensive license and only a few users will need.
- Adding a new distro to an existing URI when under development.
- Quickly testing a custom configuration of an existing URI.
- If you define a special empty URI that doesn't load any distros you can load any
arbitrary set of distros to prototype a new URI.

A forced requirement must contain the name of the distro to use(`the_dcc_plugin_a`).
It can optionally contain requirement specifiers (`the_dcc_plugin_a==1.0`). When
used, the forced requirement replaces any distro requirements defined by the config.

This allows the forced requirement to replace an existing distro even if the
forced requirement normally wouldn't be allowed. For this reason use it with caution.
[Optional distros](#optional-distros) provides you with a way to communicate what forced
requirements are safe for users to use.

To use a forced requirement in the hab cli, use the `-r` or `--requirement` flag
before the COMMAND.
```bash
hab -r the_dcc_plugin_a dump -
```

You can use it multiple times to load multiple distros:
```bash
hab -r the_dcc_plugin_a -r the_dcc==1.0 dump -
```


### Restoring resolved configuration

Under normal operation hab resolves the configuration it loads each time it is run.
Expand Down Expand Up @@ -915,7 +953,7 @@ c: dict_keys(['vb_default', 'vb2', 'vb3'])
### Defining Aliases

Aliases are used to run a program in a specific way. The hab cli creates shell
commands for each alias. Aliases are defined on distros, and per-platform.
commands for each alias. Aliases are defined on [distro](#distro)'s per-platform.

```json
{
Expand Down Expand Up @@ -1014,7 +1052,7 @@ alias to launch usdview that only adds the path to your standalone plugin to

#### Alias Mods

Alias mods provide a way for a [distro](#defining-distros) or [config](#config)
Alias mods provide a way for a [distro](#distro) and [config](#config)
to modify another distro's aliases. This is useful for plugins that need to modify
more than one host application independently.

Expand Down Expand Up @@ -1097,7 +1135,7 @@ aliases: maya mayapy maya20 mayapy20 pip houdini houdini18.5 houdinicore

### Defining Environments

The `environment` key in distro and config definitions is used to configure modifications
The `environment` key in [distro](#distro) and [config](#config) definitions is used to configure modifications
to the resolved environment. This is stored in `HabBase.environment_config`.

```json
Expand Down Expand Up @@ -1170,7 +1208,7 @@ using os specific configurations.

### Defining Distros

The `distos` key in distro and config definitions are used to define the distro version
The `distos` key in [distro](#distro) and [config](#config) definitions are used to define the distro version
requirements. When a config is processed the distro requirements are evaluated recursively
to include the requirements of the latest DistroVersion matching the specifier.
This uses the python [packaging module](https://packaging.pypa.io/en/stable/requirements.html)
Expand All @@ -1197,6 +1235,36 @@ It also supports [markers](https://packaging.pypa.io/en/stable/markers.html). Ha
should support all of the [officially supported markers](https://peps.python.org/pep-0508/),
but the most common marker likely to be used with hab is `platform_system`.

### Optional Distros

The `optional_distros` key in [config](#config) definitions are used to specify additional
distros some users may want to use. It allows you to easily communicate to users
on a per URI basis what additional distros they may want to use.

```json5
"optional_distros": {
"maya2024": ["Adds new aliases to launch Maya"],
"the_dcc==1.0": ["Optionally force the_dcc to use this version."],
"the_dcc_plugin_a": ["Load an optional plugin by default", true],
"the_dcc_plugin_b": ["Only have a few licenses for this plugin, so opt into loading it"]
}
```

The dict key is the distro value that will be used. This can include version specifier
information like you see in `the_dcc==1.0`. See [forced requirements](#forced-requirements).

The value is a list where the first item is a text description shown to the user
to help them decide if they want to use this requirement. Optionally the second
item is a bool that controls if interface plugins should enable this optional
distro by default.

Optional distros are loaded using the [forced requirements](#forced-requirements) system. When using
the hab cli, the enabled by default option is not used, users will need to use
the `-r`/`--requirement` option.

Users can see the optional distros in the dump output with verbosity level of 1
or higher. `hab dump - -v`

### Platform specific code

Hab works on windows, linux and osx(needs tested). To make it easier to handle
Expand Down
16 changes: 16 additions & 0 deletions hab/parsers/hab_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,8 @@ def load(self, filename, data=None):
self.environment_config = data.get("environment", NotSet)
if self.min_verbosity is NotSet:
self.min_verbosity = data.get("min_verbosity", NotSet)
if self.optional_distros is NotSet:
self.optional_distros = data.get("optional_distros", NotSet)

if self.context is NotSet:
# TODO: make these use override methods
Expand Down Expand Up @@ -635,6 +637,20 @@ def name(self):
def name(self, name):
self.frozen_data["name"] = name

@hab_property(verbosity=1)
def optional_distros(self):
"""Information about distros chosen to be optionally enabled for a config.
The key is the distro including version specifier text. The value is a 2
item list containing a description to shown next to the key and a bool
indicating that it should be enabled by default when used.
"""
return self.frozen_data.get("optional_distros", NotSet)

@optional_distros.setter
def optional_distros(self, value):
self.frozen_data["optional_distros"] = value

def reduced(self, resolver=None, uri=None):
"""Returns a new instance with the final settings applied respecting inheritance"""
from . import FlatConfig
Expand Down
16 changes: 16 additions & 0 deletions tests/configs/optional/optional.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "optional",
"context": [],
"inherits": false,
"distros": [
"the_dcc"
],
"optional_distros": {
"maya2024": ["Adds new aliases"],
"the_dcc==1.0": ["Specific dcc version. Only choose one at a time."],
"the_dcc==1.2": ["Specific dcc version. Only choose one at a time."],
"the_dcc_plugin_a": ["Load an optional plugin by default", true],
"the_dcc_plugin_a==0.9": ["Force a specific version of this optinal plugin"],
"the_dcc_plugin_b": ["Only have a few licenses for this plugin, so opt into loading it"]
}
}
11 changes: 11 additions & 0 deletions tests/configs/optional/optional_child.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "child",
"context": ["optional"],
"inherits": false,
"distros": [
"the_dcc"
],
"optional_distros": {
"the_dcc_plugin_e": ["Different optional dependencies for a child URI.", true]
}
}
3 changes: 2 additions & 1 deletion tests/distros/the_dcc/1.0/.hab.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
],
"aliases": {
"windows": [
["dcc", "{relative_root}\\the_dcc.exe"]
["dcc", "{relative_root}\\the_dcc.exe"],
["dcc1.0", "{relative_root}\\the_dcc.exe"]
]
}
}
3 changes: 2 additions & 1 deletion tests/distros/the_dcc/1.1/.hab.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
],
"aliases": {
"windows": [
["dcc", "{relative_root}\\the_dcc.exe"]
["dcc", "{relative_root}\\the_dcc.exe"],
["dcc1.1", "{relative_root}\\the_dcc.exe"]
]
}
}
6 changes: 4 additions & 2 deletions tests/distros/the_dcc/1.2/.hab.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
],
"aliases": {
"linux": [
["dcc", "{relative_root}//the_dcc"]
["dcc", "{relative_root}//the_dcc"],
["dcc1.2", "{relative_root}//the_dcc"]
],
"windows": [
["dcc", "{relative_root}\\the_dcc.exe"]
["dcc", "{relative_root}\\the_dcc.exe"],
["dcc1.2", "{relative_root}\\the_dcc.exe"]
]
}
}
18 changes: 16 additions & 2 deletions tests/frozen.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,24 @@
"name": "distros",
"aliases": {
"windows": {
"dcc": "TEST_DIR_NAME\\the_dcc.exe"
"dcc": {
"cmd": "TEST_DIR_NAME\\the_dcc.exe",
"distro": ["the_dcc", "1.2"]
},
"dcc1.2": {
"cmd": "TEST_DIR_NAME\\the_dcc.exe",
"distro": ["the_dcc", "1.2"]
}
},
"linux": {
"dcc": "TEST_DIR_NAME//the_dcc"
"dcc": {
"cmd": "TEST_DIR_NAME//the_dcc",
"distro": ["the_dcc", "1.2"]
},
"dcc1.2": {
"cmd": "TEST_DIR_NAME//the_dcc",
"distro": ["the_dcc", "1.2"]
}
}
},
"versions": [
Expand Down
61 changes: 61 additions & 0 deletions tests/site_main_check.habcache
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,51 @@
},
"inherits": false
},
"{config-root}/configs/optional/optional.json": {
"name": "optional",
"context": [],
"inherits": false,
"distros": [
"the_dcc"
],
"optional_distros": {
"maya2024": [
"Adds new aliases"
],
"the_dcc==1.0": [
"Specific dcc version. Only choose one at a time."
],
"the_dcc==1.2": [
"Specific dcc version. Only choose one at a time."
],
"the_dcc_plugin_a": [
"Load an optional plugin by default",
true
],
"the_dcc_plugin_a==0.9": [
"Force a specific version of this optinal plugin"
],
"the_dcc_plugin_b": [
"Only have a few licenses for this plugin, so opt into loading it"
]
}
},
"{config-root}/configs/optional/optional_child.json": {
"name": "child",
"context": [
"optional"
],
"inherits": false,
"distros": [
"the_dcc"
],
"optional_distros": {
"the_dcc_plugin_e": [
"Different optional dependencies for a child URI.",
true
]
}
},
"{config-root}/configs/place-holder/place-holder_child.json": {
"name": "child",
"context": [
Expand Down Expand Up @@ -1372,6 +1417,10 @@
[
"dcc",
"{relative_root}\\the_dcc.exe"
],
[
"dcc1.0",
"{relative_root}\\the_dcc.exe"
]
]
},
Expand All @@ -1388,6 +1437,10 @@
[
"dcc",
"{relative_root}\\the_dcc.exe"
],
[
"dcc1.1",
"{relative_root}\\the_dcc.exe"
]
]
},
Expand All @@ -1406,12 +1459,20 @@
[
"dcc",
"{relative_root}//the_dcc"
],
[
"dcc1.2",
"{relative_root}//the_dcc"
]
],
"windows": [
[
"dcc",
"{relative_root}\\the_dcc.exe"
],
[
"dcc1.2",
"{relative_root}\\the_dcc.exe"
]
]
},
Expand Down
18 changes: 14 additions & 4 deletions tests/test_freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,20 @@ def test_freeze(monkeypatch, config_root, platform, pathsep):
# Resolve the URI for frozen testing
cfg = resolver.resolve("not_set/distros")

# Ensure consistent testing across platforms. cfg has the current os's
# file paths instead of what is stored in frozen.json
cfg.frozen_data["aliases"]["linux"]["dcc"] = "TEST_DIR_NAME//the_dcc"
cfg.frozen_data["aliases"]["windows"]["dcc"] = "TEST_DIR_NAME\\the_dcc.exe"
for alias in ("dcc", "dcc1.2"):
# Ensure consistent testing across platforms. cfg has the current os's
# file paths instead of what is stored in frozen.json
cfg.frozen_data["aliases"]["linux"][alias]["cmd"] = "TEST_DIR_NAME//the_dcc"
cfg.frozen_data["aliases"]["windows"][alias][
"cmd"
] = "TEST_DIR_NAME\\the_dcc.exe"

# For ease of testing we also need to convert the distro tuple to a list
# that way it matches the json data stored in frozen.json
as_list = list(cfg.frozen_data["aliases"]["linux"][alias]["distro"])
cfg.frozen_data["aliases"]["linux"][alias]["distro"] = as_list
as_list = list(cfg.frozen_data["aliases"]["windows"][alias]["distro"])
cfg.frozen_data["aliases"]["windows"][alias]["distro"] = as_list

# Ensure the HAB_URI environment variable is defined on the FlatConfig object
# When checking the return from `cfg.freeze()` below HAB_URI is removed to
Expand Down
Loading

0 comments on commit f2254b1

Please sign in to comment.