Skip to content

Expose Command Structs for Plugins - #603

Merged
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins
Sep 17, 2025
Merged

Expose Command Structs for Plugins#603
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins

Conversation

@Mcrich23

@Mcrich23Mcrich23 commented Sep 11, 2025

Copy link
Copy Markdown
Contributor

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

Plugins technically exist, but to add shortcuts or to do existing things with functions in container requires calling a compiled binary. This pull request aims to remove that hurdle and instability by exposing commands as a new ContainerCLI target.

Simply import ContainerCLI and you can access almost any command as if it were a native part of the binary. This makes plugin development significantly easier.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

No new tests or documentation in code needed?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan This also accomplishes some of the work that I found necessary when developing my initial compose effort, because it increases safety and reduces the developer workload to reproduce, funnel, or output commands being run in the background. It also will encourage devs to use the commands internal to container instead of modifying containers and other things directly.

@jglogan

Copy link
Copy Markdown
Contributor

How are you running the commands? Are you using ParseableCommand.parse()?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

You can do that, or just init it and then manually set every property after it.

Because of how Swift parser operates, we can't just init it with all of the properties, so we have to instead init it empty and then set all of the properties.

An example of this is in my Compose proposal.

@jglogan

Copy link
Copy Markdown
Contributor

Having the ParseableCommand types public so we can all import the library and use parse() totally makes sense.

I'd prefer that that be just the API, instead of making it so that any change to the member properties is potentially a breaking change. Could we start with this PR simply allowing clients to use parse(), without changing member visibility?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We could put that in the documentation, but AsyncParsableCommand requires an empty public init to be specified in any public structure that conforms to it, so it would have to be available technically.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

However, we could make all properties and functions private to force use of the parse method.

@jglogan

Copy link
Copy Markdown
Contributor

However, we could make all properties and functions private to force use of the parse method.

Exactly! Could you try that approach - making the CLI library such that you can import it and use parse, but the property visibility isn't public? I'm curious how that'd work for your plugin.

@jglogan

Copy link
Copy Markdown
Contributor

Also, do you have an enhancement issue open for making the CLI library importable? If not, please create one and I'll assign it to you. It'd be best to have an issue backing each PR (especially for larger changes or those affecting the API). Thanks!

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

I didn't have one, so I just made #609. But as I try to update my Compose plugin to use entirely parse, it is becoming increasingly difficult to pass down global flags that I want to respect inherently such as Flags.Global.verbose or Flags.Progress.disableProgressUpdates.

I strongly think that a great DX is the only way that people will make plugins for compose, so I think we kinda only have two ways to move forward. Either, we expose every property on commands, or we can expose flag groups in a setable manner that allows developers to pass down flag groups.

If we do the latter, this would not be required because .parse() would set them by default, but it would also allow them to be easily overriden and treated in a sort of "environment" manner.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We can undo it, but I decided to go ahead and make all of the OptionGroup properties public to make this possible. Again these are not required to be set when using .parse it just makes passing down use flags far easier.

@Mcrich23Mcrich23 mentioned this pull request Sep 17, 2025
4 tasks
@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Actually, before we merge this, should I update the readme with information about developing plugins?

@jglogan

Copy link
Copy Markdown
Contributor

Nah, for that, could you create a git issue for plugin development information? You and I can put together an outline there first, and then we'll do that in a dedicated PR.

It's long overdue. I have some thoughts scratched down in a local branch but have been getting sidetracked a lot.

Comment threadSources/CLI/Image/ImageList.swift Outdated
Comment threadSources/CLI/Network/NetworkList.swift Outdated
Comment threadSources/CLI/System/Kernel/KernelSet.swift Outdated
Comment threadSources/CLI/Volume/VolumeList.swift Outdated
@jglogan

Copy link
Copy Markdown
Contributor

Builds now! I went through everything and left a few comments regarding some of the private methods that went to default visibility. Was that needed to compile the code without errors? For those (especially the table output stuff which we should just solve in a better way), I'm inclined to leave these private until someone actually needs to use these functions.

@jgloganjglogan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Sounds good. I will fix these soon

@katiewasnothere

Copy link
Copy Markdown
Contributor

@Mcrich23 Do you have an example of a plugin you're hoping to use these changes with? Draft code is fine, I'm just curious to see how it looks

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@katiewasnothere yeah. This hasn't been updated entirely for the finalized interface, but it and the conversation here should give you a decent idea of the current API with ContainerCommands. https://github.com/Mcrich23/container/blob/add-compose/Plugins/Compose/ComposeCLI/Commands/ComposeUp.swift

import ContainerCommands

@main
public struct Executable: AsyncParsableCommand {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm sufficiently dumb, but what is this target for?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

This is the new compose executable entry point since I could not otherwise get an executable and normal target for the same sources. So this is just a proxy making it executable.

@jglogan

Copy link
Copy Markdown
Contributor

Looks like we need make fmt once more...

@jglogan
jglogan merged commit dd6bdc2 into apple:mainSep 17, 2025
2 checks passed
Mcrich23 added a commit to Mcrich23/container that referenced this pull request Sep 17, 2025
commit dd6bdc2
Author: Morris Richman <81453549+Mcrich23@users.noreply.github.com>
Date: Wed Sep 17 15:24:26 2025 -0700
Expose Command Structs for Plugins (apple#603)
## Type of Change
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [ ] Documentation update
## Motivation and Context
Plugins technically exist, but to add shortcuts or to do existing things
with functions in `container` requires calling a compiled binary. This
pull request aims to remove that hurdle and instability by exposing
commands as a new `ContainerCommands ` target.
Simply import `ContainerCommands` and you can access almost
any command as if it were a native part of the binary. This makes
plugin development significantly easier.
Closesapple#609.
jglogan pushed a commit that referenced this pull request Sep 19, 2025
## Motivation and Context
This is an extension of #603 to cleanup the folder structure and have it
match with the new library and target names.
mazdak added a commit to mazdak/container that referenced this pull request Sep 21, 2025
@Mcrich23
Mcrich23 deleted the expose-command-structs-for-plugins branch September 23, 2025 00:02
Sign up for freeto 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

@Mcrich23@jglogan@dcantah@katiewasnothere
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Expose Command Structs for Plugins by Mcrich23 · Pull Request #603 · apple/container · GitHub
Skip to content

Expose Command Structs for Plugins - #603

Merged
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins
Sep 17, 2025
Merged

Expose Command Structs for Plugins#603
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins

Conversation

@Mcrich23

@Mcrich23Mcrich23 commented Sep 11, 2025

Copy link
Copy Markdown
Contributor

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

Plugins technically exist, but to add shortcuts or to do existing things with functions in container requires calling a compiled binary. This pull request aims to remove that hurdle and instability by exposing commands as a new ContainerCLI target.

Simply import ContainerCLI and you can access almost any command as if it were a native part of the binary. This makes plugin development significantly easier.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

No new tests or documentation in code needed?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan This also accomplishes some of the work that I found necessary when developing my initial compose effort, because it increases safety and reduces the developer workload to reproduce, funnel, or output commands being run in the background. It also will encourage devs to use the commands internal to container instead of modifying containers and other things directly.

@jglogan

Copy link
Copy Markdown
Contributor

How are you running the commands? Are you using ParseableCommand.parse()?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

You can do that, or just init it and then manually set every property after it.

Because of how Swift parser operates, we can't just init it with all of the properties, so we have to instead init it empty and then set all of the properties.

An example of this is in my Compose proposal.

@jglogan

Copy link
Copy Markdown
Contributor

Having the ParseableCommand types public so we can all import the library and use parse() totally makes sense.

I'd prefer that that be just the API, instead of making it so that any change to the member properties is potentially a breaking change. Could we start with this PR simply allowing clients to use parse(), without changing member visibility?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We could put that in the documentation, but AsyncParsableCommand requires an empty public init to be specified in any public structure that conforms to it, so it would have to be available technically.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

However, we could make all properties and functions private to force use of the parse method.

@jglogan

Copy link
Copy Markdown
Contributor

However, we could make all properties and functions private to force use of the parse method.

Exactly! Could you try that approach - making the CLI library such that you can import it and use parse, but the property visibility isn't public? I'm curious how that'd work for your plugin.

@jglogan

Copy link
Copy Markdown
Contributor

Also, do you have an enhancement issue open for making the CLI library importable? If not, please create one and I'll assign it to you. It'd be best to have an issue backing each PR (especially for larger changes or those affecting the API). Thanks!

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

I didn't have one, so I just made #609. But as I try to update my Compose plugin to use entirely parse, it is becoming increasingly difficult to pass down global flags that I want to respect inherently such as Flags.Global.verbose or Flags.Progress.disableProgressUpdates.

I strongly think that a great DX is the only way that people will make plugins for compose, so I think we kinda only have two ways to move forward. Either, we expose every property on commands, or we can expose flag groups in a setable manner that allows developers to pass down flag groups.

If we do the latter, this would not be required because .parse() would set them by default, but it would also allow them to be easily overriden and treated in a sort of "environment" manner.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We can undo it, but I decided to go ahead and make all of the OptionGroup properties public to make this possible. Again these are not required to be set when using .parse it just makes passing down use flags far easier.

@Mcrich23Mcrich23 mentioned this pull request Sep 17, 2025
4 tasks
@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Actually, before we merge this, should I update the readme with information about developing plugins?

@jglogan

Copy link
Copy Markdown
Contributor

Nah, for that, could you create a git issue for plugin development information? You and I can put together an outline there first, and then we'll do that in a dedicated PR.

It's long overdue. I have some thoughts scratched down in a local branch but have been getting sidetracked a lot.

Comment threadSources/CLI/Image/ImageList.swift Outdated
Comment threadSources/CLI/Network/NetworkList.swift Outdated
Comment threadSources/CLI/System/Kernel/KernelSet.swift Outdated
Comment threadSources/CLI/Volume/VolumeList.swift Outdated
@jglogan

Copy link
Copy Markdown
Contributor

Builds now! I went through everything and left a few comments regarding some of the private methods that went to default visibility. Was that needed to compile the code without errors? For those (especially the table output stuff which we should just solve in a better way), I'm inclined to leave these private until someone actually needs to use these functions.

@jgloganjglogan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Sounds good. I will fix these soon

@katiewasnothere

Copy link
Copy Markdown
Contributor

@Mcrich23 Do you have an example of a plugin you're hoping to use these changes with? Draft code is fine, I'm just curious to see how it looks

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@katiewasnothere yeah. This hasn't been updated entirely for the finalized interface, but it and the conversation here should give you a decent idea of the current API with ContainerCommands. https://github.com/Mcrich23/container/blob/add-compose/Plugins/Compose/ComposeCLI/Commands/ComposeUp.swift

import ContainerCommands

@main
public struct Executable: AsyncParsableCommand {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm sufficiently dumb, but what is this target for?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

This is the new compose executable entry point since I could not otherwise get an executable and normal target for the same sources. So this is just a proxy making it executable.

@jglogan

Copy link
Copy Markdown
Contributor

Looks like we need make fmt once more...

@jglogan
jglogan merged commit dd6bdc2 into apple:mainSep 17, 2025
2 checks passed
Mcrich23 added a commit to Mcrich23/container that referenced this pull request Sep 17, 2025
commit dd6bdc2
Author: Morris Richman <81453549+Mcrich23@users.noreply.github.com>
Date: Wed Sep 17 15:24:26 2025 -0700
Expose Command Structs for Plugins (apple#603)
## Type of Change
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [ ] Documentation update
## Motivation and Context
Plugins technically exist, but to add shortcuts or to do existing things
with functions in `container` requires calling a compiled binary. This
pull request aims to remove that hurdle and instability by exposing
commands as a new `ContainerCommands ` target.
Simply import `ContainerCommands` and you can access almost
any command as if it were a native part of the binary. This makes
plugin development significantly easier.
Closesapple#609.
jglogan pushed a commit that referenced this pull request Sep 19, 2025
## Motivation and Context
This is an extension of #603 to cleanup the folder structure and have it
match with the new library and target names.
mazdak added a commit to mazdak/container that referenced this pull request Sep 21, 2025
@Mcrich23
Mcrich23 deleted the expose-command-structs-for-plugins branch September 23, 2025 00:02
Sign up for freeto 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

@Mcrich23@jglogan@dcantah@katiewasnothere
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Expose Command Structs for Plugins by Mcrich23 · Pull Request #603 · apple/container · GitHub
Skip to content

Expose Command Structs for Plugins - #603

Merged
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins
Sep 17, 2025
Merged

Expose Command Structs for Plugins#603
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins

Conversation

@Mcrich23

@Mcrich23Mcrich23 commented Sep 11, 2025

Copy link
Copy Markdown
Contributor

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

Plugins technically exist, but to add shortcuts or to do existing things with functions in container requires calling a compiled binary. This pull request aims to remove that hurdle and instability by exposing commands as a new ContainerCLI target.

Simply import ContainerCLI and you can access almost any command as if it were a native part of the binary. This makes plugin development significantly easier.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

No new tests or documentation in code needed?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan This also accomplishes some of the work that I found necessary when developing my initial compose effort, because it increases safety and reduces the developer workload to reproduce, funnel, or output commands being run in the background. It also will encourage devs to use the commands internal to container instead of modifying containers and other things directly.

@jglogan

Copy link
Copy Markdown
Contributor

How are you running the commands? Are you using ParseableCommand.parse()?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

You can do that, or just init it and then manually set every property after it.

Because of how Swift parser operates, we can't just init it with all of the properties, so we have to instead init it empty and then set all of the properties.

An example of this is in my Compose proposal.

@jglogan

Copy link
Copy Markdown
Contributor

Having the ParseableCommand types public so we can all import the library and use parse() totally makes sense.

I'd prefer that that be just the API, instead of making it so that any change to the member properties is potentially a breaking change. Could we start with this PR simply allowing clients to use parse(), without changing member visibility?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We could put that in the documentation, but AsyncParsableCommand requires an empty public init to be specified in any public structure that conforms to it, so it would have to be available technically.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

However, we could make all properties and functions private to force use of the parse method.

@jglogan

Copy link
Copy Markdown
Contributor

However, we could make all properties and functions private to force use of the parse method.

Exactly! Could you try that approach - making the CLI library such that you can import it and use parse, but the property visibility isn't public? I'm curious how that'd work for your plugin.

@jglogan

Copy link
Copy Markdown
Contributor

Also, do you have an enhancement issue open for making the CLI library importable? If not, please create one and I'll assign it to you. It'd be best to have an issue backing each PR (especially for larger changes or those affecting the API). Thanks!

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

I didn't have one, so I just made #609. But as I try to update my Compose plugin to use entirely parse, it is becoming increasingly difficult to pass down global flags that I want to respect inherently such as Flags.Global.verbose or Flags.Progress.disableProgressUpdates.

I strongly think that a great DX is the only way that people will make plugins for compose, so I think we kinda only have two ways to move forward. Either, we expose every property on commands, or we can expose flag groups in a setable manner that allows developers to pass down flag groups.

If we do the latter, this would not be required because .parse() would set them by default, but it would also allow them to be easily overriden and treated in a sort of "environment" manner.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We can undo it, but I decided to go ahead and make all of the OptionGroup properties public to make this possible. Again these are not required to be set when using .parse it just makes passing down use flags far easier.

@Mcrich23Mcrich23 mentioned this pull request Sep 17, 2025
4 tasks
@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Actually, before we merge this, should I update the readme with information about developing plugins?

@jglogan

Copy link
Copy Markdown
Contributor

Nah, for that, could you create a git issue for plugin development information? You and I can put together an outline there first, and then we'll do that in a dedicated PR.

It's long overdue. I have some thoughts scratched down in a local branch but have been getting sidetracked a lot.

Comment threadSources/CLI/Image/ImageList.swift Outdated
Comment threadSources/CLI/Network/NetworkList.swift Outdated
Comment threadSources/CLI/System/Kernel/KernelSet.swift Outdated
Comment threadSources/CLI/Volume/VolumeList.swift Outdated
@jglogan

Copy link
Copy Markdown
Contributor

Builds now! I went through everything and left a few comments regarding some of the private methods that went to default visibility. Was that needed to compile the code without errors? For those (especially the table output stuff which we should just solve in a better way), I'm inclined to leave these private until someone actually needs to use these functions.

@jgloganjglogan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Sounds good. I will fix these soon

@katiewasnothere

Copy link
Copy Markdown
Contributor

@Mcrich23 Do you have an example of a plugin you're hoping to use these changes with? Draft code is fine, I'm just curious to see how it looks

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@katiewasnothere yeah. This hasn't been updated entirely for the finalized interface, but it and the conversation here should give you a decent idea of the current API with ContainerCommands. https://github.com/Mcrich23/container/blob/add-compose/Plugins/Compose/ComposeCLI/Commands/ComposeUp.swift

import ContainerCommands

@main
public struct Executable: AsyncParsableCommand {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm sufficiently dumb, but what is this target for?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

This is the new compose executable entry point since I could not otherwise get an executable and normal target for the same sources. So this is just a proxy making it executable.

@jglogan

Copy link
Copy Markdown
Contributor

Looks like we need make fmt once more...

@jglogan
jglogan merged commit dd6bdc2 into apple:mainSep 17, 2025
2 checks passed
Mcrich23 added a commit to Mcrich23/container that referenced this pull request Sep 17, 2025
commit dd6bdc2
Author: Morris Richman <81453549+Mcrich23@users.noreply.github.com>
Date: Wed Sep 17 15:24:26 2025 -0700
Expose Command Structs for Plugins (apple#603)
## Type of Change
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [ ] Documentation update
## Motivation and Context
Plugins technically exist, but to add shortcuts or to do existing things
with functions in `container` requires calling a compiled binary. This
pull request aims to remove that hurdle and instability by exposing
commands as a new `ContainerCommands ` target.
Simply import `ContainerCommands` and you can access almost
any command as if it were a native part of the binary. This makes
plugin development significantly easier.
Closesapple#609.
jglogan pushed a commit that referenced this pull request Sep 19, 2025
## Motivation and Context
This is an extension of #603 to cleanup the folder structure and have it
match with the new library and target names.
mazdak added a commit to mazdak/container that referenced this pull request Sep 21, 2025
@Mcrich23
Mcrich23 deleted the expose-command-structs-for-plugins branch September 23, 2025 00:02
Sign up for freeto 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

@Mcrich23@jglogan@dcantah@katiewasnothere
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Expose Command Structs for Plugins by Mcrich23 · Pull Request #603 · apple/container · GitHub
Skip to content

Expose Command Structs for Plugins - #603

Merged
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins
Sep 17, 2025
Merged

Expose Command Structs for Plugins#603
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins

Conversation

@Mcrich23

@Mcrich23Mcrich23 commented Sep 11, 2025

Copy link
Copy Markdown
Contributor

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

Plugins technically exist, but to add shortcuts or to do existing things with functions in container requires calling a compiled binary. This pull request aims to remove that hurdle and instability by exposing commands as a new ContainerCLI target.

Simply import ContainerCLI and you can access almost any command as if it were a native part of the binary. This makes plugin development significantly easier.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

No new tests or documentation in code needed?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan This also accomplishes some of the work that I found necessary when developing my initial compose effort, because it increases safety and reduces the developer workload to reproduce, funnel, or output commands being run in the background. It also will encourage devs to use the commands internal to container instead of modifying containers and other things directly.

@jglogan

Copy link
Copy Markdown
Contributor

How are you running the commands? Are you using ParseableCommand.parse()?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

You can do that, or just init it and then manually set every property after it.

Because of how Swift parser operates, we can't just init it with all of the properties, so we have to instead init it empty and then set all of the properties.

An example of this is in my Compose proposal.

@jglogan

Copy link
Copy Markdown
Contributor

Having the ParseableCommand types public so we can all import the library and use parse() totally makes sense.

I'd prefer that that be just the API, instead of making it so that any change to the member properties is potentially a breaking change. Could we start with this PR simply allowing clients to use parse(), without changing member visibility?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We could put that in the documentation, but AsyncParsableCommand requires an empty public init to be specified in any public structure that conforms to it, so it would have to be available technically.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

However, we could make all properties and functions private to force use of the parse method.

@jglogan

Copy link
Copy Markdown
Contributor

However, we could make all properties and functions private to force use of the parse method.

Exactly! Could you try that approach - making the CLI library such that you can import it and use parse, but the property visibility isn't public? I'm curious how that'd work for your plugin.

@jglogan

Copy link
Copy Markdown
Contributor

Also, do you have an enhancement issue open for making the CLI library importable? If not, please create one and I'll assign it to you. It'd be best to have an issue backing each PR (especially for larger changes or those affecting the API). Thanks!

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

I didn't have one, so I just made #609. But as I try to update my Compose plugin to use entirely parse, it is becoming increasingly difficult to pass down global flags that I want to respect inherently such as Flags.Global.verbose or Flags.Progress.disableProgressUpdates.

I strongly think that a great DX is the only way that people will make plugins for compose, so I think we kinda only have two ways to move forward. Either, we expose every property on commands, or we can expose flag groups in a setable manner that allows developers to pass down flag groups.

If we do the latter, this would not be required because .parse() would set them by default, but it would also allow them to be easily overriden and treated in a sort of "environment" manner.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We can undo it, but I decided to go ahead and make all of the OptionGroup properties public to make this possible. Again these are not required to be set when using .parse it just makes passing down use flags far easier.

@Mcrich23Mcrich23 mentioned this pull request Sep 17, 2025
4 tasks
@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Actually, before we merge this, should I update the readme with information about developing plugins?

@jglogan

Copy link
Copy Markdown
Contributor

Nah, for that, could you create a git issue for plugin development information? You and I can put together an outline there first, and then we'll do that in a dedicated PR.

It's long overdue. I have some thoughts scratched down in a local branch but have been getting sidetracked a lot.

Comment threadSources/CLI/Image/ImageList.swift Outdated
Comment threadSources/CLI/Network/NetworkList.swift Outdated
Comment threadSources/CLI/System/Kernel/KernelSet.swift Outdated
Comment threadSources/CLI/Volume/VolumeList.swift Outdated
@jglogan

Copy link
Copy Markdown
Contributor

Builds now! I went through everything and left a few comments regarding some of the private methods that went to default visibility. Was that needed to compile the code without errors? For those (especially the table output stuff which we should just solve in a better way), I'm inclined to leave these private until someone actually needs to use these functions.

@jgloganjglogan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Sounds good. I will fix these soon

@katiewasnothere

Copy link
Copy Markdown
Contributor

@Mcrich23 Do you have an example of a plugin you're hoping to use these changes with? Draft code is fine, I'm just curious to see how it looks

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@katiewasnothere yeah. This hasn't been updated entirely for the finalized interface, but it and the conversation here should give you a decent idea of the current API with ContainerCommands. https://github.com/Mcrich23/container/blob/add-compose/Plugins/Compose/ComposeCLI/Commands/ComposeUp.swift

import ContainerCommands

@main
public struct Executable: AsyncParsableCommand {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm sufficiently dumb, but what is this target for?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

This is the new compose executable entry point since I could not otherwise get an executable and normal target for the same sources. So this is just a proxy making it executable.

@jglogan

Copy link
Copy Markdown
Contributor

Looks like we need make fmt once more...

@jglogan
jglogan merged commit dd6bdc2 into apple:mainSep 17, 2025
2 checks passed
Mcrich23 added a commit to Mcrich23/container that referenced this pull request Sep 17, 2025
commit dd6bdc2
Author: Morris Richman <81453549+Mcrich23@users.noreply.github.com>
Date: Wed Sep 17 15:24:26 2025 -0700
Expose Command Structs for Plugins (apple#603)
## Type of Change
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [ ] Documentation update
## Motivation and Context
Plugins technically exist, but to add shortcuts or to do existing things
with functions in `container` requires calling a compiled binary. This
pull request aims to remove that hurdle and instability by exposing
commands as a new `ContainerCommands ` target.
Simply import `ContainerCommands` and you can access almost
any command as if it were a native part of the binary. This makes
plugin development significantly easier.
Closesapple#609.
jglogan pushed a commit that referenced this pull request Sep 19, 2025
## Motivation and Context
This is an extension of #603 to cleanup the folder structure and have it
match with the new library and target names.
mazdak added a commit to mazdak/container that referenced this pull request Sep 21, 2025
@Mcrich23
Mcrich23 deleted the expose-command-structs-for-plugins branch September 23, 2025 00:02
Sign up for freeto 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

@Mcrich23@jglogan@dcantah@katiewasnothere
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Expose Command Structs for Plugins by Mcrich23 · Pull Request #603 · apple/container · GitHub
Skip to content

Expose Command Structs for Plugins - #603

Merged
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins
Sep 17, 2025
Merged

Expose Command Structs for Plugins#603
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins

Conversation

@Mcrich23

@Mcrich23Mcrich23 commented Sep 11, 2025

Copy link
Copy Markdown
Contributor

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

Plugins technically exist, but to add shortcuts or to do existing things with functions in container requires calling a compiled binary. This pull request aims to remove that hurdle and instability by exposing commands as a new ContainerCLI target.

Simply import ContainerCLI and you can access almost any command as if it were a native part of the binary. This makes plugin development significantly easier.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

No new tests or documentation in code needed?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan This also accomplishes some of the work that I found necessary when developing my initial compose effort, because it increases safety and reduces the developer workload to reproduce, funnel, or output commands being run in the background. It also will encourage devs to use the commands internal to container instead of modifying containers and other things directly.

@jglogan

Copy link
Copy Markdown
Contributor

How are you running the commands? Are you using ParseableCommand.parse()?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

You can do that, or just init it and then manually set every property after it.

Because of how Swift parser operates, we can't just init it with all of the properties, so we have to instead init it empty and then set all of the properties.

An example of this is in my Compose proposal.

@jglogan

Copy link
Copy Markdown
Contributor

Having the ParseableCommand types public so we can all import the library and use parse() totally makes sense.

I'd prefer that that be just the API, instead of making it so that any change to the member properties is potentially a breaking change. Could we start with this PR simply allowing clients to use parse(), without changing member visibility?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We could put that in the documentation, but AsyncParsableCommand requires an empty public init to be specified in any public structure that conforms to it, so it would have to be available technically.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

However, we could make all properties and functions private to force use of the parse method.

@jglogan

Copy link
Copy Markdown
Contributor

However, we could make all properties and functions private to force use of the parse method.

Exactly! Could you try that approach - making the CLI library such that you can import it and use parse, but the property visibility isn't public? I'm curious how that'd work for your plugin.

@jglogan

Copy link
Copy Markdown
Contributor

Also, do you have an enhancement issue open for making the CLI library importable? If not, please create one and I'll assign it to you. It'd be best to have an issue backing each PR (especially for larger changes or those affecting the API). Thanks!

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

I didn't have one, so I just made #609. But as I try to update my Compose plugin to use entirely parse, it is becoming increasingly difficult to pass down global flags that I want to respect inherently such as Flags.Global.verbose or Flags.Progress.disableProgressUpdates.

I strongly think that a great DX is the only way that people will make plugins for compose, so I think we kinda only have two ways to move forward. Either, we expose every property on commands, or we can expose flag groups in a setable manner that allows developers to pass down flag groups.

If we do the latter, this would not be required because .parse() would set them by default, but it would also allow them to be easily overriden and treated in a sort of "environment" manner.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We can undo it, but I decided to go ahead and make all of the OptionGroup properties public to make this possible. Again these are not required to be set when using .parse it just makes passing down use flags far easier.

@Mcrich23Mcrich23 mentioned this pull request Sep 17, 2025
4 tasks
@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Actually, before we merge this, should I update the readme with information about developing plugins?

@jglogan

Copy link
Copy Markdown
Contributor

Nah, for that, could you create a git issue for plugin development information? You and I can put together an outline there first, and then we'll do that in a dedicated PR.

It's long overdue. I have some thoughts scratched down in a local branch but have been getting sidetracked a lot.

Comment threadSources/CLI/Image/ImageList.swift Outdated
Comment threadSources/CLI/Network/NetworkList.swift Outdated
Comment threadSources/CLI/System/Kernel/KernelSet.swift Outdated
Comment threadSources/CLI/Volume/VolumeList.swift Outdated
@jglogan

Copy link
Copy Markdown
Contributor

Builds now! I went through everything and left a few comments regarding some of the private methods that went to default visibility. Was that needed to compile the code without errors? For those (especially the table output stuff which we should just solve in a better way), I'm inclined to leave these private until someone actually needs to use these functions.

@jgloganjglogan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Sounds good. I will fix these soon

@katiewasnothere

Copy link
Copy Markdown
Contributor

@Mcrich23 Do you have an example of a plugin you're hoping to use these changes with? Draft code is fine, I'm just curious to see how it looks

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@katiewasnothere yeah. This hasn't been updated entirely for the finalized interface, but it and the conversation here should give you a decent idea of the current API with ContainerCommands. https://github.com/Mcrich23/container/blob/add-compose/Plugins/Compose/ComposeCLI/Commands/ComposeUp.swift

import ContainerCommands

@main
public struct Executable: AsyncParsableCommand {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm sufficiently dumb, but what is this target for?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

This is the new compose executable entry point since I could not otherwise get an executable and normal target for the same sources. So this is just a proxy making it executable.

@jglogan

Copy link
Copy Markdown
Contributor

Looks like we need make fmt once more...

@jglogan
jglogan merged commit dd6bdc2 into apple:mainSep 17, 2025
2 checks passed
Mcrich23 added a commit to Mcrich23/container that referenced this pull request Sep 17, 2025
commit dd6bdc2
Author: Morris Richman <81453549+Mcrich23@users.noreply.github.com>
Date: Wed Sep 17 15:24:26 2025 -0700
Expose Command Structs for Plugins (apple#603)
## Type of Change
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [ ] Documentation update
## Motivation and Context
Plugins technically exist, but to add shortcuts or to do existing things
with functions in `container` requires calling a compiled binary. This
pull request aims to remove that hurdle and instability by exposing
commands as a new `ContainerCommands ` target.
Simply import `ContainerCommands` and you can access almost
any command as if it were a native part of the binary. This makes
plugin development significantly easier.
Closesapple#609.
jglogan pushed a commit that referenced this pull request Sep 19, 2025
## Motivation and Context
This is an extension of #603 to cleanup the folder structure and have it
match with the new library and target names.
mazdak added a commit to mazdak/container that referenced this pull request Sep 21, 2025
@Mcrich23
Mcrich23 deleted the expose-command-structs-for-plugins branch September 23, 2025 00:02
Sign up for freeto 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

@Mcrich23@jglogan@dcantah@katiewasnothere
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Expose Command Structs for Plugins by Mcrich23 · Pull Request #603 · apple/container · GitHub
Skip to content

Expose Command Structs for Plugins - #603

Merged
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins
Sep 17, 2025
Merged

Expose Command Structs for Plugins#603
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins

Conversation

@Mcrich23

@Mcrich23Mcrich23 commented Sep 11, 2025

Copy link
Copy Markdown
Contributor

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

Plugins technically exist, but to add shortcuts or to do existing things with functions in container requires calling a compiled binary. This pull request aims to remove that hurdle and instability by exposing commands as a new ContainerCLI target.

Simply import ContainerCLI and you can access almost any command as if it were a native part of the binary. This makes plugin development significantly easier.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

No new tests or documentation in code needed?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan This also accomplishes some of the work that I found necessary when developing my initial compose effort, because it increases safety and reduces the developer workload to reproduce, funnel, or output commands being run in the background. It also will encourage devs to use the commands internal to container instead of modifying containers and other things directly.

@jglogan

Copy link
Copy Markdown
Contributor

How are you running the commands? Are you using ParseableCommand.parse()?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

You can do that, or just init it and then manually set every property after it.

Because of how Swift parser operates, we can't just init it with all of the properties, so we have to instead init it empty and then set all of the properties.

An example of this is in my Compose proposal.

@jglogan

Copy link
Copy Markdown
Contributor

Having the ParseableCommand types public so we can all import the library and use parse() totally makes sense.

I'd prefer that that be just the API, instead of making it so that any change to the member properties is potentially a breaking change. Could we start with this PR simply allowing clients to use parse(), without changing member visibility?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We could put that in the documentation, but AsyncParsableCommand requires an empty public init to be specified in any public structure that conforms to it, so it would have to be available technically.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

However, we could make all properties and functions private to force use of the parse method.

@jglogan

Copy link
Copy Markdown
Contributor

However, we could make all properties and functions private to force use of the parse method.

Exactly! Could you try that approach - making the CLI library such that you can import it and use parse, but the property visibility isn't public? I'm curious how that'd work for your plugin.

@jglogan

Copy link
Copy Markdown
Contributor

Also, do you have an enhancement issue open for making the CLI library importable? If not, please create one and I'll assign it to you. It'd be best to have an issue backing each PR (especially for larger changes or those affecting the API). Thanks!

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

I didn't have one, so I just made #609. But as I try to update my Compose plugin to use entirely parse, it is becoming increasingly difficult to pass down global flags that I want to respect inherently such as Flags.Global.verbose or Flags.Progress.disableProgressUpdates.

I strongly think that a great DX is the only way that people will make plugins for compose, so I think we kinda only have two ways to move forward. Either, we expose every property on commands, or we can expose flag groups in a setable manner that allows developers to pass down flag groups.

If we do the latter, this would not be required because .parse() would set them by default, but it would also allow them to be easily overriden and treated in a sort of "environment" manner.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We can undo it, but I decided to go ahead and make all of the OptionGroup properties public to make this possible. Again these are not required to be set when using .parse it just makes passing down use flags far easier.

@Mcrich23Mcrich23 mentioned this pull request Sep 17, 2025
4 tasks
@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Actually, before we merge this, should I update the readme with information about developing plugins?

@jglogan

Copy link
Copy Markdown
Contributor

Nah, for that, could you create a git issue for plugin development information? You and I can put together an outline there first, and then we'll do that in a dedicated PR.

It's long overdue. I have some thoughts scratched down in a local branch but have been getting sidetracked a lot.

Comment threadSources/CLI/Image/ImageList.swift Outdated
Comment threadSources/CLI/Network/NetworkList.swift Outdated
Comment threadSources/CLI/System/Kernel/KernelSet.swift Outdated
Comment threadSources/CLI/Volume/VolumeList.swift Outdated
@jglogan

Copy link
Copy Markdown
Contributor

Builds now! I went through everything and left a few comments regarding some of the private methods that went to default visibility. Was that needed to compile the code without errors? For those (especially the table output stuff which we should just solve in a better way), I'm inclined to leave these private until someone actually needs to use these functions.

@jgloganjglogan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Sounds good. I will fix these soon

@katiewasnothere

Copy link
Copy Markdown
Contributor

@Mcrich23 Do you have an example of a plugin you're hoping to use these changes with? Draft code is fine, I'm just curious to see how it looks

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@katiewasnothere yeah. This hasn't been updated entirely for the finalized interface, but it and the conversation here should give you a decent idea of the current API with ContainerCommands. https://github.com/Mcrich23/container/blob/add-compose/Plugins/Compose/ComposeCLI/Commands/ComposeUp.swift

import ContainerCommands

@main
public struct Executable: AsyncParsableCommand {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm sufficiently dumb, but what is this target for?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

This is the new compose executable entry point since I could not otherwise get an executable and normal target for the same sources. So this is just a proxy making it executable.

@jglogan

Copy link
Copy Markdown
Contributor

Looks like we need make fmt once more...

@jglogan
jglogan merged commit dd6bdc2 into apple:mainSep 17, 2025
2 checks passed
Mcrich23 added a commit to Mcrich23/container that referenced this pull request Sep 17, 2025
commit dd6bdc2
Author: Morris Richman <81453549+Mcrich23@users.noreply.github.com>
Date: Wed Sep 17 15:24:26 2025 -0700
Expose Command Structs for Plugins (apple#603)
## Type of Change
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [ ] Documentation update
## Motivation and Context
Plugins technically exist, but to add shortcuts or to do existing things
with functions in `container` requires calling a compiled binary. This
pull request aims to remove that hurdle and instability by exposing
commands as a new `ContainerCommands ` target.
Simply import `ContainerCommands` and you can access almost
any command as if it were a native part of the binary. This makes
plugin development significantly easier.
Closesapple#609.
jglogan pushed a commit that referenced this pull request Sep 19, 2025
## Motivation and Context
This is an extension of #603 to cleanup the folder structure and have it
match with the new library and target names.
mazdak added a commit to mazdak/container that referenced this pull request Sep 21, 2025
@Mcrich23
Mcrich23 deleted the expose-command-structs-for-plugins branch September 23, 2025 00:02
Sign up for freeto 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

@Mcrich23@jglogan@dcantah@katiewasnothere
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Expose Command Structs for Plugins by Mcrich23 · Pull Request #603 · apple/container · GitHub
Skip to content

Expose Command Structs for Plugins - #603

Merged
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins
Sep 17, 2025
Merged

Expose Command Structs for Plugins#603
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins

Conversation

@Mcrich23

@Mcrich23Mcrich23 commented Sep 11, 2025

Copy link
Copy Markdown
Contributor

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

Plugins technically exist, but to add shortcuts or to do existing things with functions in container requires calling a compiled binary. This pull request aims to remove that hurdle and instability by exposing commands as a new ContainerCLI target.

Simply import ContainerCLI and you can access almost any command as if it were a native part of the binary. This makes plugin development significantly easier.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

No new tests or documentation in code needed?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan This also accomplishes some of the work that I found necessary when developing my initial compose effort, because it increases safety and reduces the developer workload to reproduce, funnel, or output commands being run in the background. It also will encourage devs to use the commands internal to container instead of modifying containers and other things directly.

@jglogan

Copy link
Copy Markdown
Contributor

How are you running the commands? Are you using ParseableCommand.parse()?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

You can do that, or just init it and then manually set every property after it.

Because of how Swift parser operates, we can't just init it with all of the properties, so we have to instead init it empty and then set all of the properties.

An example of this is in my Compose proposal.

@jglogan

Copy link
Copy Markdown
Contributor

Having the ParseableCommand types public so we can all import the library and use parse() totally makes sense.

I'd prefer that that be just the API, instead of making it so that any change to the member properties is potentially a breaking change. Could we start with this PR simply allowing clients to use parse(), without changing member visibility?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We could put that in the documentation, but AsyncParsableCommand requires an empty public init to be specified in any public structure that conforms to it, so it would have to be available technically.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

However, we could make all properties and functions private to force use of the parse method.

@jglogan

Copy link
Copy Markdown
Contributor

However, we could make all properties and functions private to force use of the parse method.

Exactly! Could you try that approach - making the CLI library such that you can import it and use parse, but the property visibility isn't public? I'm curious how that'd work for your plugin.

@jglogan

Copy link
Copy Markdown
Contributor

Also, do you have an enhancement issue open for making the CLI library importable? If not, please create one and I'll assign it to you. It'd be best to have an issue backing each PR (especially for larger changes or those affecting the API). Thanks!

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

I didn't have one, so I just made #609. But as I try to update my Compose plugin to use entirely parse, it is becoming increasingly difficult to pass down global flags that I want to respect inherently such as Flags.Global.verbose or Flags.Progress.disableProgressUpdates.

I strongly think that a great DX is the only way that people will make plugins for compose, so I think we kinda only have two ways to move forward. Either, we expose every property on commands, or we can expose flag groups in a setable manner that allows developers to pass down flag groups.

If we do the latter, this would not be required because .parse() would set them by default, but it would also allow them to be easily overriden and treated in a sort of "environment" manner.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We can undo it, but I decided to go ahead and make all of the OptionGroup properties public to make this possible. Again these are not required to be set when using .parse it just makes passing down use flags far easier.

@Mcrich23Mcrich23 mentioned this pull request Sep 17, 2025
4 tasks
@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Actually, before we merge this, should I update the readme with information about developing plugins?

@jglogan

Copy link
Copy Markdown
Contributor

Nah, for that, could you create a git issue for plugin development information? You and I can put together an outline there first, and then we'll do that in a dedicated PR.

It's long overdue. I have some thoughts scratched down in a local branch but have been getting sidetracked a lot.

Comment threadSources/CLI/Image/ImageList.swift Outdated
Comment threadSources/CLI/Network/NetworkList.swift Outdated
Comment threadSources/CLI/System/Kernel/KernelSet.swift Outdated
Comment threadSources/CLI/Volume/VolumeList.swift Outdated
@jglogan

Copy link
Copy Markdown
Contributor

Builds now! I went through everything and left a few comments regarding some of the private methods that went to default visibility. Was that needed to compile the code without errors? For those (especially the table output stuff which we should just solve in a better way), I'm inclined to leave these private until someone actually needs to use these functions.

@jgloganjglogan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Sounds good. I will fix these soon

@katiewasnothere

Copy link
Copy Markdown
Contributor

@Mcrich23 Do you have an example of a plugin you're hoping to use these changes with? Draft code is fine, I'm just curious to see how it looks

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@katiewasnothere yeah. This hasn't been updated entirely for the finalized interface, but it and the conversation here should give you a decent idea of the current API with ContainerCommands. https://github.com/Mcrich23/container/blob/add-compose/Plugins/Compose/ComposeCLI/Commands/ComposeUp.swift

import ContainerCommands

@main
public struct Executable: AsyncParsableCommand {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm sufficiently dumb, but what is this target for?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

This is the new compose executable entry point since I could not otherwise get an executable and normal target for the same sources. So this is just a proxy making it executable.

@jglogan

Copy link
Copy Markdown
Contributor

Looks like we need make fmt once more...

@jglogan
jglogan merged commit dd6bdc2 into apple:mainSep 17, 2025
2 checks passed
Mcrich23 added a commit to Mcrich23/container that referenced this pull request Sep 17, 2025
commit dd6bdc2
Author: Morris Richman <81453549+Mcrich23@users.noreply.github.com>
Date: Wed Sep 17 15:24:26 2025 -0700
Expose Command Structs for Plugins (apple#603)
## Type of Change
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [ ] Documentation update
## Motivation and Context
Plugins technically exist, but to add shortcuts or to do existing things
with functions in `container` requires calling a compiled binary. This
pull request aims to remove that hurdle and instability by exposing
commands as a new `ContainerCommands ` target.
Simply import `ContainerCommands` and you can access almost
any command as if it were a native part of the binary. This makes
plugin development significantly easier.
Closesapple#609.
jglogan pushed a commit that referenced this pull request Sep 19, 2025
## Motivation and Context
This is an extension of #603 to cleanup the folder structure and have it
match with the new library and target names.
mazdak added a commit to mazdak/container that referenced this pull request Sep 21, 2025
@Mcrich23
Mcrich23 deleted the expose-command-structs-for-plugins branch September 23, 2025 00:02
Sign up for freeto 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

@Mcrich23@jglogan@dcantah@katiewasnothere
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Expose Command Structs for Plugins by Mcrich23 · Pull Request #603 · apple/container · GitHub
Skip to content

Expose Command Structs for Plugins - #603

Merged
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins
Sep 17, 2025
Merged

Expose Command Structs for Plugins#603
jglogan merged 33 commits into
apple:mainfrom
Mcrich23:expose-command-structs-for-plugins

Conversation

@Mcrich23

@Mcrich23Mcrich23 commented Sep 11, 2025

Copy link
Copy Markdown
Contributor

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

Plugins technically exist, but to add shortcuts or to do existing things with functions in container requires calling a compiled binary. This pull request aims to remove that hurdle and instability by exposing commands as a new ContainerCLI target.

Simply import ContainerCLI and you can access almost any command as if it were a native part of the binary. This makes plugin development significantly easier.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

No new tests or documentation in code needed?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan This also accomplishes some of the work that I found necessary when developing my initial compose effort, because it increases safety and reduces the developer workload to reproduce, funnel, or output commands being run in the background. It also will encourage devs to use the commands internal to container instead of modifying containers and other things directly.

@jglogan

Copy link
Copy Markdown
Contributor

How are you running the commands? Are you using ParseableCommand.parse()?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

You can do that, or just init it and then manually set every property after it.

Because of how Swift parser operates, we can't just init it with all of the properties, so we have to instead init it empty and then set all of the properties.

An example of this is in my Compose proposal.

@jglogan

Copy link
Copy Markdown
Contributor

Having the ParseableCommand types public so we can all import the library and use parse() totally makes sense.

I'd prefer that that be just the API, instead of making it so that any change to the member properties is potentially a breaking change. Could we start with this PR simply allowing clients to use parse(), without changing member visibility?

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We could put that in the documentation, but AsyncParsableCommand requires an empty public init to be specified in any public structure that conforms to it, so it would have to be available technically.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

However, we could make all properties and functions private to force use of the parse method.

@jglogan

Copy link
Copy Markdown
Contributor

However, we could make all properties and functions private to force use of the parse method.

Exactly! Could you try that approach - making the CLI library such that you can import it and use parse, but the property visibility isn't public? I'm curious how that'd work for your plugin.

@jglogan

Copy link
Copy Markdown
Contributor

Also, do you have an enhancement issue open for making the CLI library importable? If not, please create one and I'll assign it to you. It'd be best to have an issue backing each PR (especially for larger changes or those affecting the API). Thanks!

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

I didn't have one, so I just made #609. But as I try to update my Compose plugin to use entirely parse, it is becoming increasingly difficult to pass down global flags that I want to respect inherently such as Flags.Global.verbose or Flags.Progress.disableProgressUpdates.

I strongly think that a great DX is the only way that people will make plugins for compose, so I think we kinda only have two ways to move forward. Either, we expose every property on commands, or we can expose flag groups in a setable manner that allows developers to pass down flag groups.

If we do the latter, this would not be required because .parse() would set them by default, but it would also allow them to be easily overriden and treated in a sort of "environment" manner.

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@jglogan We can undo it, but I decided to go ahead and make all of the OptionGroup properties public to make this possible. Again these are not required to be set when using .parse it just makes passing down use flags far easier.

@Mcrich23Mcrich23 mentioned this pull request Sep 17, 2025
4 tasks
@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Actually, before we merge this, should I update the readme with information about developing plugins?

@jglogan

Copy link
Copy Markdown
Contributor

Nah, for that, could you create a git issue for plugin development information? You and I can put together an outline there first, and then we'll do that in a dedicated PR.

It's long overdue. I have some thoughts scratched down in a local branch but have been getting sidetracked a lot.

Comment threadSources/CLI/Image/ImageList.swift Outdated
Comment threadSources/CLI/Network/NetworkList.swift Outdated
Comment threadSources/CLI/System/Kernel/KernelSet.swift Outdated
Comment threadSources/CLI/Volume/VolumeList.swift Outdated
@jglogan

Copy link
Copy Markdown
Contributor

Builds now! I went through everything and left a few comments regarding some of the private methods that went to default visibility. Was that needed to compile the code without errors? For those (especially the table output stuff which we should just solve in a better way), I'm inclined to leave these private until someone actually needs to use these functions.

@jgloganjglogan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

Sounds good. I will fix these soon

@katiewasnothere

Copy link
Copy Markdown
Contributor

@Mcrich23 Do you have an example of a plugin you're hoping to use these changes with? Draft code is fine, I'm just curious to see how it looks

@Mcrich23

Copy link
Copy Markdown
ContributorAuthor

@katiewasnothere yeah. This hasn't been updated entirely for the finalized interface, but it and the conversation here should give you a decent idea of the current API with ContainerCommands. https://github.com/Mcrich23/container/blob/add-compose/Plugins/Compose/ComposeCLI/Commands/ComposeUp.swift

import ContainerCommands

@main
public struct Executable: AsyncParsableCommand {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm sufficiently dumb, but what is this target for?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

This is the new compose executable entry point since I could not otherwise get an executable and normal target for the same sources. So this is just a proxy making it executable.

@jglogan

Copy link
Copy Markdown
Contributor

Looks like we need make fmt once more...

@jglogan
jglogan merged commit dd6bdc2 into apple:mainSep 17, 2025
2 checks passed
Mcrich23 added a commit to Mcrich23/container that referenced this pull request Sep 17, 2025
commit dd6bdc2
Author: Morris Richman <81453549+Mcrich23@users.noreply.github.com>
Date: Wed Sep 17 15:24:26 2025 -0700
Expose Command Structs for Plugins (apple#603)
## Type of Change
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [ ] Documentation update
## Motivation and Context
Plugins technically exist, but to add shortcuts or to do existing things
with functions in `container` requires calling a compiled binary. This
pull request aims to remove that hurdle and instability by exposing
commands as a new `ContainerCommands ` target.
Simply import `ContainerCommands` and you can access almost
any command as if it were a native part of the binary. This makes
plugin development significantly easier.
Closesapple#609.
jglogan pushed a commit that referenced this pull request Sep 19, 2025
## Motivation and Context
This is an extension of #603 to cleanup the folder structure and have it
match with the new library and target names.
mazdak added a commit to mazdak/container that referenced this pull request Sep 21, 2025
@Mcrich23
Mcrich23 deleted the expose-command-structs-for-plugins branch September 23, 2025 00:02
Sign up for freeto 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

@Mcrich23@jglogan@dcantah@katiewasnothere