Skip to content
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

CustomExecution2 Feedback: expose variable resolvers #81007

Closed
bwateratmsft opened this issue Sep 16, 2019 · 40 comments
Closed

CustomExecution2 Feedback: expose variable resolvers #81007

bwateratmsft opened this issue Sep 16, 2019 · 40 comments
Assignees
Labels
api api-finalization feature-request Request for new features or functionality insiders-released Patch has been released in VS Code Insiders on-testplan
Milestone

Comments

@bwateratmsft
Copy link
Contributor

bwateratmsft commented Sep 16, 2019

One shortcoming I realized with CustomExecution2 is that each task would have to implement their own way of resolving common variables--e.g. ${workspaceFolder} or ${file}, etc.

Can we get an API to reach the variable resolvers e.g. ConfigurationResolverService? This way any variable input to CustomExecution2 tasks would not require the task to reimplement the wheel.

@bwateratmsft
Copy link
Contributor Author

@alexr00 @philliphoff

@alexr00 alexr00 self-assigned this Sep 17, 2019
@alexr00
Copy link
Member

alexr00 commented Sep 17, 2019

@bwateratmsft what if we just resolved variables for custom execution tasks instead of exposing the configuration resolver?

Edit: This changes the task ID, which causes a whole host of problems.

@alexr00 alexr00 added the feature-request Request for new features or functionality label Sep 17, 2019
@alexr00
Copy link
Member

alexr00 commented Sep 17, 2019

Can you outline a situation in which you couldn't use VS Code API to get the current workspace folder or current file from within your task?

@bwateratmsft
Copy link
Contributor Author

bwateratmsft commented Sep 17, 2019

File and folder are just some of the available variables. There are commands, configs, prompts, environment variables, every imagineable thing about workspace/file/line...end users would expect all of these to work regardless of how the task is implemented behind the scenes. If the extension implementer had to implement support for all of these, it would be very difficult (and probably not even possible in some cases), and it would essentially just end up being a reimplementation of what VSCode has already.

@alexr00
Copy link
Member

alexr00 commented Sep 18, 2019

I think I'm still missing something. Why would you need to use the ${} variables at all in custom execution tasks? The VS Code API should expose all of those things to you to use in your task without ${} variables.

@bwateratmsft
Copy link
Contributor Author

bwateratmsft commented Sep 18, 2019

Why would you not need ${} variables? To take away that capability would be to artificially limit CustomExecution, and end users would not understand why, because they don't know or care how a task is implemented behind-the-scenes.

Let me give an example. In the Docker extension, we cannot realistically make ASP.NET Core Blazor web apps work without CustomExecution, because of the order of execution of a debug launch. We have to do some steps after the dotnet build task, which is not possible without either having CustomExecution, or else offloading them to an external process (ShellExecution, ProcessExecution) (which also makes for an ugly tasks.json, because resolveDebugConfiguration cannot resolve a task dynamically).

But, having CustomExecution does not suddenly mean that users don't need to configure the docker build and docker run tasks. There are a large selection of options available for configuring these tasks: docker-build and docker-run.

Users have already stated the need to be able to pass in ${env:xx}, and we ourselves already use ${workspaceFolder} and ${file}. We also have users who want a ${command} variable. Every flavor of ${relativeFile} etc. would also be useful to us.

So, unless we get an API that allows us to resolve task variables, here are our choices:

  1. Never switch to CustomExecution, which means high perf cost of the constant calls to resolveTask, and no ability to ever support ASP.NET Core Blazor apps. But, users could use any built-in variable (or env/command/prompt/config/etc.).
  2. Implement our own variable resolver, which will be a waste of work considering a variable resolver already exists in VSCode. It would also always lag behind the capabilities of the built-in one, and users will not care why--they will simply see it as broken functionality.

@philliphoff
Copy link

Just to add: the ability to resolve tokens is not exclusive to the use of CustomExecution. If the user makes use of tokens such as ${workspaceFolder} in any of a provider's custom properties, even "traditional" TaskProviders and DebugConfigurationProviders may need to resolve those tokens in order to properly resolve a task or debug configuration.

We're doing this already in our providers, for the tokens we know are being used. But, as the features gain adoption, we can be sure more tokens will be used, and we'd rather not reimplement the wheel.

@alexr00
Copy link
Member

alexr00 commented Sep 19, 2019

@bwateratmsft it sounds like you want to pass values from tasks.json into tasks, which is related #58836. If we implement #58836 then we can easily resolve variables in the passed in values. Would that be enough?

@bwateratmsft
Copy link
Contributor Author

I think that that implementation might be insufficient. We are building extremely complicated docker build and docker run commands based on user input, universal defaults, language-specific defaults, and platform-specific defaults. Simple substitutions into a command line (like --port ${task:port} from #58836) would generally not work, which is why we need to be in control of building the command line ourselves.

As an example, this relatively simple docker-run task configuration for a .NET Core app:

        {
            "type": "docker-run",
            "label": "docker-run: debug",
            "dependsOn": [
                "docker-build: debug"
            ],
            "dockerRun": {
                "volumes": [
                    {
                        "containerPath": "/myvolume",
                        "localPath": "${workspaceFolder}",
                        "permissions": "ro"
                    }
                ]
            },
            "netCore": {
                "appProject": "${workspaceFolder}/TestWeb/TestWeb.csproj"
            }
        }

becomes this ShellExecution command:

docker run -dt -P --name 'testsln-dev' -e 'ASPNETCORE_ENVIRONMENT=Development' -e 'ASPNETCORE_URLS=https://+:443;http://+:80' --label 'com.microsoft.created-by=visual-studio-code' -v 'C:\Users\brandonw\source\repos\TestSln:/myvolume:ro' -v 'c:\Users\brandonw\source\repos\TestSln\TestWeb:/app:rw' -v 'c:\Users\brandonw\source\repos\TestSln:/src:rw' -v 'C:\Users\brandonw\.vsdbg:/remote_debugger:ro' -v 'C:\Users\brandonw\.nuget\packages:/root/.nuget/packages:ro' -v 'C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages:ro' -v 'C:\Users\brandonw\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro' -v 'C:\Users\brandonw\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro' 'testsln:dev'

As you can see, the input from the task cannot be trivially mapped into the command line. If we wanted a "simple" task definition that was just that huge command line, without all the complicated logic, then we may as well not implement a task at all. The goal is to make it easier for users to configure the docker build and docker run tasks without needing to deal with the difficult syntax, and without needing to know all the language- and platform-specific subtleties.

I don't really understand why this sounds so difficult to implement? The heart of the code is already all there, we just need to be able to access it as extension developers.

@alexr00
Copy link
Member

alexr00 commented Sep 19, 2019

Looks like we have an issue for exposing the configuration resolver API already: #2809
Duplicate of that issue.

@alexr00 alexr00 added the *duplicate Issue identified as a duplicate of another issue(s) label Sep 19, 2019
@vscodebot
Copy link

vscodebot bot commented Sep 19, 2019

Thanks for creating this issue! We figured it's covering the same as another one we already have. Thus, we closed this one as a duplicate. You can search for existing issues here. See also our issue reporting guidelines.

Happy Coding!

@vscodebot vscodebot bot closed this as completed Sep 19, 2019
@alexr00 alexr00 reopened this Sep 23, 2019
@alexr00 alexr00 added api api-proposal and removed *duplicate Issue identified as a duplicate of another issue(s) labels Sep 23, 2019
@alexr00 alexr00 added this to the October 2019 milestone Sep 23, 2019
@alexr00
Copy link
Member

alexr00 commented Sep 23, 2019

@bwateratmsft What if we resolved all the variables in your TaskDefinition and then passed that back into the custom execution callback?

	export class CustomExecution {
		/**
		 * @param process The [Pseudoterminal](#Pseudoterminal) to be used by the task to display output.
		 * @param callback The callback that will be called when the task is started by a user.
		 */
		constructor(callback: (resolvedDefinition?: TaskDefinition) => Thenable<Pseudoterminal>);
	}

@alexr00 alexr00 modified the milestones: October 2019, On Deck Sep 23, 2019
@bwateratmsft
Copy link
Contributor Author

That sounds great, I can't think of any scenarios where that wouldn't work. It gives extension developers the freedom to use the resolved config, or not use it if they're certain it isn't needed, or mix the two.

One question--is this something that has to ship with CustomExecution from the start, or can it be added later? My thinking is that if it meant delaying CustomExecution, I'd rather release what we have now and add this later, if that is possible.

@alexr00
Copy link
Member

alexr00 commented Sep 24, 2019

It's an optional parameter, so I would add it later. The new parameter would sit in vscode.proposed.d.ts for a few months then we would try to finalize it. CustomExecution would not be delayed because of this.

@alexr00 alexr00 modified the milestones: On Deck, October 2019 Sep 24, 2019
@alexr00
Copy link
Member

alexr00 commented Oct 7, 2019

Moving to November since we are taking October for issue grooming.

@alexr00 alexr00 removed this from the October 2019 milestone Oct 7, 2019
@bwateratmsft
Copy link
Contributor Author

This should work for the Docker extension. Would it be possible to make it object-recursive? Otherwise we can pretty easily deal with the recursion ourselves.

@alexr00
Copy link
Member

alexr00 commented Aug 14, 2020

After much discussion, we are back to the tasks specific API proposal:

export class CustomExecution2 extends CustomExecution {
/**
* Constructs a CustomExecution task object. The callback will be executed the task is run, at which point the
* extension should return the Pseudoterminal it will "run in". The task should wait to do further execution until
* [Pseudoterminal.open](#Pseudoterminal.open) is called. Task cancellation should be handled using
* [Pseudoterminal.close](#Pseudoterminal.close). When the task is complete fire
* [Pseudoterminal.onDidClose](#Pseudoterminal.onDidClose).
* @param callback The callback that will be called when the task is started by a user.
*/
constructor(callback: (resolvedDefinition?: TaskDefinition) => Thenable<Pseudoterminal>);
}

Summary of discussion:

  • Debug has a solution for resolving variables already. This is necessary because variable resolving in debug requires some debug context that can't easily be passed in from the extension on demand.
  • Because debug has a separate solution and requires context, it would need to be specifically disallowed from calling any on demand resolving API to prevent inconsistencies.
  • Tasks also requires some context when resolving variables. It will be easier for extensions to simply get the appropriately resolved variables at the appropriate time rather than for them to figure out the context correctly then resolve on demand.
  • While a task specific solution doesn't solve the problem for settings, there are types of variables that tasks will want to resolve but with never make sense for settings to resolve (input and command for example). Having a single solution that sometimes resolves those variables and sometimes doesn't is confusing.

@alexr00
Copy link
Member

alexr00 commented Aug 14, 2020

Folks who are interested in this API, I merged the change that will allow input and command variables to resolve with the resolvedDefinition in the callback for the proposal. Thinking about it a bit more, I might need to move where the task definition is resolved, but in the next insiders you should be able to try it out and give feedback.

@alexr00
Copy link
Member

alexr00 commented Aug 18, 2020

I will be finalizing this API this week, please speak now if this API doesn't fulfill your custom execution variable resolving needs! https://github.com/microsoft/vscode/blob/master/src/vs/vscode.proposed.d.ts#L1000-L1010

@kaysonwu
Copy link

Hi, @alexr00 Before it is implemented, how should I get the name of the currently opened ${file}?

@alexr00
Copy link
Member

alexr00 commented Aug 19, 2020

If your task is a CustomExecution task then all variables in the task definition will be resolved in the resolvedDefinition in the callback.

@kaysonwu
Copy link

20200819182322

resolvedDefinition I tried it, but it didn't resolve. Am I doing something wrong? @alexr00

@alexr00
Copy link
Member

alexr00 commented Aug 19, 2020

@kaysonwu what version of VS Code are you using? If you aren't using the latest Insiders build, please do try that since there are important changes there.

@kaysonwu
Copy link

I used 14.8.0

@alexr00
Copy link
Member

alexr00 commented Aug 19, 2020

@kaysonwu please try with the latest insiders: https://code.visualstudio.com/insiders/

@kaysonwu
Copy link

okay. thanks.

@philliphoff
Copy link

@alexr00 @bwateratmsft is on leave at the moment, so I don't know how soon we'll be able to check out the new API (cc @karolz-ms) That said, looking at it, it seems like it should work for the bulk of our needs in vscode-docker and vscode-dapr.

@karolz-ms
Copy link
Contributor

I trust @philliphoff judgement on this ☺️

@bwateratmsft
Copy link
Contributor Author

bwateratmsft commented Aug 25, 2020

I'm back! I will take a look today.

Update: works great, thanks @alexr00!

My PR for it: microsoft/vscode-docker#2250

@github-actions github-actions bot locked and limited conversation to collaborators Oct 4, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api api-finalization feature-request Request for new features or functionality insiders-released Patch has been released in VS Code Insiders on-testplan
Projects
None yet
Development

No branches or pull requests

9 participants