Skip to content

[proposal] Composite Features #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed

Conversation

joshspicer
Copy link
Member

@joshspicer joshspicer commented Mar 8, 2023

ref: #109

A proposal for a Composite Dev Container Feature.

@joshspicer joshspicer changed the title Proposal: Composite Features [proposal] Composite Features Mar 8, 2023
@joshspicer joshspicer marked this pull request as ready for review March 13, 2023 17:45
@joshspicer joshspicer requested a review from a team as a code owner March 13, 2023 17:45
|----------|------|-------------|
| `id` | `string` | The ID of the Feature. This follows the same semantics of the `id` property in the `devcontainer-feature.json` file. |
| `version` | `string` | The version of the Feature. This follows the same semantics of the `version` property in the `devcontainer-feature.json` file. |
| `features` | `array` | An array of objects (in installation order) that define the Feature(s) that compose this Feature. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the correct installation order depend on each of the install script's requirements?

Copy link
Member Author

@joshspicer joshspicer Mar 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd want to peek at each Feature's installsAfter, i think. That was the primary reason that I didn't want to (for now) enable nesting composite Features. This spec does NOT let you nest one composite feature in another, making this process easier to resolve (we can "flatten" the installation from composite Features into one install order.

This is something to discuss and finalize in the spec, but we could have installation follow installsAfter order with precedence within a composite Feature first, and then some algorithm for ordering composite Features based on that? Perhaps the other way around?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are already thinking about composite features depending on each other: Wouldn't it be simpler to avoid the additional artifact type of a "composite feature" and instead allow features to have hard dependencies in addition to the soft dependencies supported with "installAfter"? We already compute the dependency graph including cycle detection for "installAfter". (Other package management systems also seem to have only one package type that can carry code/binaries and dependencies. Do we have an example that keeps these apart?)

We could add "dependencies" to the feature metadata as an object like "features" in the devcontainer.json, so installation order is decided by the same dependency graph algorithm. A dependency would implicitly go into the "installAfter" list of the feature depending on it.

| `version` | `string` | The version of the Feature. This follows the same semantics of the `version` property in the `devcontainer-feature.json` file. |
| `features` | `array` | An array of objects (in installation order) that define the Feature(s) that compose this Feature. |
| `features.id` | `string` | The ID of the Feature that this Feature depends on. |
| `features.version` | `string` | The version of the Feature that this Feature depends on. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if a different version is configured in the devcontainer.json?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this brings us back to similar discussion over at #44.

Maybe it would be helpful to define behavior for installing a Feature more than once. I think a goal of Features should be that they are idempotent - perhaps this would be a good time to also define some "Feature best practices" to guide authors toward the behavior we want Features to exhibit?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. We already have that when you use a base image with a feature installed and you then install that feature again in a second devcontainer.json.

If the version and options objects are equal, we can optimize by running the feature only once.

Maybe a detail: The install order algorithm currently uses the feature ids to sort features that do not require a specific install order to arrive at a stable install order. With the same feature being allowed multiple times in the same install pass, we will need to include also the feature version and options object in this sorting to ensure a stable install order.

| `features` | `array` | An array of objects (in installation order) that define the Feature(s) that compose this Feature. |
| `features.id` | `string` | The ID of the Feature that this Feature depends on. |
| `features.version` | `string` | The version of the Feature that this Feature depends on. |
| `features.detect` | `string` | A command that will be executed in a shell to determine if the Feature should be installed. If the command returns a non-zero exit code, the Feature will be installed. If the command returns a zero exit code, the remaining install steps will be skipped. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does "remaining install steps will be skipped" mean, the remaining features listed in the array will be skipped?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will clarify this - I meant that each Feature will have a detect that will cause the further installation and merging of dev container config for that Feature. Remaining Features in the array will still be executed (and their detect phase run, too).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the examples we want to cover with this? Would this run after all previous features have detected & installed, immediately before this feature's install script would run?

| `features.id` | `string` | The ID of the Feature that this Feature depends on. |
| `features.version` | `string` | The version of the Feature that this Feature depends on. |
| `features.detect` | `string` | A command that will be executed in a shell to determine if the Feature should be installed. If the command returns a non-zero exit code, the Feature will be installed. If the command returns a zero exit code, the remaining install steps will be skipped. |
| `features.options` | `object` | An object of key/value pairs that will be passed to the Feature's `install.sh` script. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are these merged when the same feature is installed by the devcontainer.json?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to #208 (comment)

}
```

An optional `finalize.sh` script can be included, and will be fired after all Features have been installed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have good examples for that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be omitted in the first pass if we find it to be unnecessary or without justification yet. I'll leave for discussion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that we are discussing various options. We should aim for as much simplicity as we can while still covering the important cases we have. It will be easier to later add to a simple design than a complex one.

Updating that featmake bash script has been deprecated in favor of a python cli program (which still going to be deprecated in the future in favor of this proposal)
Copy link
Member

@bamurtaugh bamurtaugh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very thorough and clear, thanks for putting this together! Left a couple minor comments and questions.

Co-authored-by: Brigit Murtaugh <[email protected]>
@joshspicer
Copy link
Member Author

closing in favor of #234

@joshspicer joshspicer closed this Jun 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants