Skip to content

Using Primary Outputs for Post Actions

Vlada Shubina edited this page Nov 4, 2022 · 2 revisions

Primary outputs

Primary outputs define the list of template files for further processing. The primary outputs are returned by template engine API after template instantiation (Microsoft.TemplateEngine.IDE.Bootstrapper.CreateAsync, Microsoft.TemplateEngine.Edge.Template.TemplateCreator.InstantiateAsync) or dry run (Microsoft.TemplateEngine.IDE.Bootstrapper.GetCreationEffectsAsync). The main use for primary outputs is to provide the list of files to perform post actions implemented by the host. dotnet CLI host and Visual Studio host implement the default set of post actions based on primary outputs returned by API, described in post-action registry. In case the template files have to be used in post actions, they should be added as the primary outputs in template.json definition.

The template can define any number of primary outputs. Each primary output contains the following information:

  • path - the path should contain the relative path to the file after template instantiation is done and prior to symbol based renaming is applied.
  • condition - if the condition evaluates to true, the corresponding path will be added to primary outputs, if false, the path is ignored. If no condition is provided for a path, the condition defaults to true.

Basic example:

template files:

  • MyTestProject.csproj
  • Class.cs

template.json definition

{
  "author": "Test Asset",
  "classifications": [ "Test Asset" ],
  "name": "BasicTemplate",
  "generatorVersions": "[1.0.0.0-*)",
  "groupIdentity": "TestAssets.BasicTemplate",
  "precedence": "100",
  "identity": "TestAssets.BasicTemplate",
  "shortName": "TestAssets.BasicTemplate",
  "tags": {
    "language": "C#",
    "type": "project"
  },
  "sourceName": "MyTestProject",
  "primaryOutputs": [
    {
      "path": "MyTestProject.csproj"        
    }
  ],
  "postActions": [
    {
      "description": "Restore NuGet packages required by this project.",
      "manualInstructions": [
        { "text": "Run 'dotnet restore'" }
      ],
      "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
      "continueOnError": true,
      "args": {
        "files": [ "MyTestProject.csproj" ]
      }
    }
  ]
}

Note that the example above contains primary output with original name. The actual primary output returned by API, will contain the final file name after renames are applied. Example:

dotnet new TestAssets.BasicTemplate --name Awesome 

The primary output returned by API is Awesome.csproj and actual file produced is Awesome.csproj.

Using primary outputs with source modifiers

Source modifiers defined in template.json can override source or target location during template instantiation, or rename files during instantiation. It is important that primary outputs are specified correctly in combination with source modifiers.

Changing source

When changing the source, make sure that primary output path contains final location of the file after instantiation relative to default target path (./) Example: template files:

  • ./Custom/Path/MyTestProject.csproj
  • ./Custom/Path/Class.cs

template.json definition

{
  "author": "Test Asset",
  "classifications": [ "Test Asset" ],
  "name": "TemplateWithSourceNameAndCustomSourcePath",
  "generatorVersions": "[1.0.0.0-*)",
  "groupIdentity": "TestAssets.TemplateWithSourceNameAndCustomSourcePath",
  "precedence": "100",
  "identity": "TestAssets.TemplateWithSourceNameAndCustomSourcePath",
  "shortName": "TestAssets.TemplateWithSourceNameAndCustomSourcePath",
  "sourceName": "MyTestProject",
  "sources": [
    {
      "source": "./Custom/Path/"
    }
  ],
  "primaryOutputs": [
    {
      "path": "MyTestProject.csproj"        
    }
  ],
  "postActions": [
    {
      "description": "Restore NuGet packages required by this project.",
      "manualInstructions": [
        { "text": "Run 'dotnet restore'" }
      ],
      "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
      "continueOnError": true,
      "args": {
        "files": [ "./Custom/Path/MyTestProject.csproj" ]
      }
    }
  ]
}

Note that the example above contains primary output with original name in the final location. The actual primary output returned by API, will contain the final file name after renames are applied. Post action files should contain the original location in template definition. Example:

dotnet new TestAssets.TemplateWithSourceNameAndCustomSourcePath --name Awesome 

The primary output returned by API is: Awesome.csproj. The final location of the files is:

  • ./Awesome.csproj
  • ./Class.cs

Changing target

When changing the target for template instantiation, make sure that primary output path contains final location of the file after instantiation including target path. Example: template files:

  • MyTestProject.csproj
  • Class.cs

template.json definition

{
  "author": "Test Asset",
  "classifications": [ "Test Asset" ],
  "name": "TemplateWithSourceNameAndCustomTargetPath",
  "generatorVersions": "[1.0.0.0-*)",
  "groupIdentity": "TestAssets.TemplateWithSourceNameAndCustomTargetPath",
  "precedence": "100",
  "identity": "TestAssets.TemplateWithSourceNameAndCustomTargetPath",
  "shortName": "TestAssets.TemplateWithSourceNameAndCustomTargetPath",
  "sourceName": "MyTestProject",
  "sources": [
    {
      "target": "./Custom/Path/"
    }
  ],
  "primaryOutputs": [
    {
      "path": "./Custom/Path/MyTestProject.csproj"        
    }
  ],
  "postActions": [
    {
      "description": "Restore NuGet packages required by this project.",
      "manualInstructions": [
        { "text": "Run 'dotnet restore'" }
      ],
      "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
      "continueOnError": true,
      "args": {
        "files": [ "MyTestProject.csproj" ]
      }
    }
  ]
}

Note that the example above contains primary output with original name in the final location. The actual primary output returned by API, will contain the final file name after renames are applied. files argument should contain the original location in template definition. Example:

dotnet new TestAssets.TemplateWithSourceNameAndCustomTargetPath --name Awesome 

The primary output returned by API is: /Custom/Path/Awesome.csproj. The final location of the files is:

  • ./Custom/Path/Awesome.csproj
  • ./Custom/Path/Class.cs

Changing source and target

When changing both source and target for template instantiation, make sure that primary output path contains final location of the file after instantiation including target path. Example: template files:

  • ./Src/Custom/Path/MyTestProject.csproj
  • ./Src/Custom/Path/Class.cs

template.json definition

{
  "author": "Test Asset",
  "classifications": [ "Test Asset" ],
  "name": "TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "generatorVersions": "[1.0.0.0-*)",
  "groupIdentity": "TestAssets.TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "precedence": "100",
  "identity": "TestAssets.TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "shortName": "TestAssets.TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "sourceName": "MyTestProject",
  "sources": [
    {
      "source": "./Src/Custom/Path/",
      "target": "./Target/Output/"
    }
  ],
  "primaryOutputs": [
    {
      "path": "./Target/Output/MyTestProject.csproj"        
    }
  ],
  "postActions": [
    {
      "description": "Restore NuGet packages required by this project.",
      "manualInstructions": [
        { "text": "Run 'dotnet restore'" }
      ],
      "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
      "continueOnError": true,
      "args": {
        "files": [ "./Src/Custom/Path/MyTestProject.csproj" ]
      }
    }
  ]
}

Note that the example above contains primary output with original name in the final location. The actual primary output returned by API, will contain the final file name after renames are applied. Post action files should contain the original location in template definition. Example:

dotnet new TestAssets.TemplateWithSourceNameAndCustomSourceAndTargetPaths --name Awesome 

The primary output returned by API is: Target/Output/Awesome.csproj. The final location of the files is:

  • ./Target/Output/Awesome.csproj,
  • ./Target/Output/Class.cs

Renaming

When using renames in source modifiers, make sure that primary output path contains the filename after source based file rename is applied, however before symbol based renames are applied. Example: template files:

  • MyTestProject.csproj

template.json definition

{
  "author": "Test Asset",
  "classifications": [ "Test Asset" ],
  "name": "TemplateWithSourceBasedRenames",
  "generatorVersions": "[1.0.0.0-*)",
  "groupIdentity": "TestAssets.TemplateWithSourceBasedRenames",
  "precedence": "100",
  "identity": "TestAssets.TemplateWithSourceBasedRenames",
  "shortName": "TestAssets.TemplateWithSourceBasedRenames",
  "sourceName": "foo",
  "sources": [
    {
      "rename": {
        "MyTestProject.csproj": "MyFirstTestProject.csproj"
      }
    }
  ],
  "symbols": {
    "firstRename": {
      "type": "parameter",
      "dataType": "string",
      "fileRename": "First"
    }
  },
  "primaryOutputs": [
    {
      "path": "MyFirstTestProject.csproj"
    }
  ],
  "postActions": [
    {
      "description": "Restore NuGet packages required by this project.",
      "manualInstructions": [
        { "text": "Run 'dotnet restore'" }
      ],
      "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
      "continueOnError": true,
      "args": {
        "files": [ "MyTestProject.csproj" ]
      }
    }
  ]
}

Note that the example above contains primary output with name after source based rename is applied. The actual primary output returned by API, contains the final file name after symbol based renames are applied. Post action files should contain the original location in template definition. Example:

dotnet new TestAssets.TemplateWithSourceBasedRenames --firstRename Awesome 

The primary output returned by API is: MyAwesomeTestProject.csproj. The final location of the files is: ./MyAwesomeTestProject.csproj.

Different ways of using primary outputs in post actions

Post actions support different ways of choosing the files to process, for more details see the documentation for specific post action.

Example 1 - apply post action to all primary outputs

The following example restores the projects mentioned in primary outputs: template files:

  • ./Custom/MyTestProject/MyTestProject.csproj
  • ./Custom/MyTestProject/Class.cs
  • ./Custom/MyTestProject.Tests/MyTestProject.Tests.csproj
  • ./Custom/MyTestProject.Tests/Class.cs

template.json definition

{
  "author": "Test Asset",
  "classifications": [ "Test Asset" ],
  "name": "TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "generatorVersions": "[1.0.0.0-*)",
  "groupIdentity": "TestAssets.TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "precedence": "100",
  "identity": "TestAssets.TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "shortName": "TestAssets.TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "sourceName": "MyTestProject",
  "sources": [
    {
      "source": "./Custom/MyTestProject/",
      "target": "./src/MyTestProject/"
    },
    {
      "source": "./Custom/MyTestProject.Tests/",
      "target": "./test/MyTestProject.Tests/"
    },
  ],
  "primaryOutputs": [
    {
      "path": "./src/MyTestProject/MyTestProject.csproj"        
    }
  ],
  "postActions": [
    {
      "description": "Restore NuGet packages required by this project.",
      "manualInstructions": [
        { "text": "Run 'dotnet restore'" }
      ],
      "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
      "continueOnError": true,
    }
  ]
}

Example 2 - reference source files in post action arguments

The following example restores individual project specified in files property. Defining them in primary outputs is optional. template files:

  • ./Custom/MyTestProject/MyTestProject.csproj
  • ./Custom/MyTestProject/Class.cs
  • ./Custom/MyTestProject.Tests/MyTestProject.Tests.csproj
  • ./Custom/MyTestProject.Tests/Class.cs

template.json definition

{
  "author": "Test Asset",
  "classifications": [ "Test Asset" ],
  "name": "TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "generatorVersions": "[1.0.0.0-*)",
  "groupIdentity": "TestAssets.TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "precedence": "100",
  "identity": "TestAssets.TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "shortName": "TestAssets.TemplateWithSourceNameAndCustomSourceAndTargetPaths",
  "sourceName": "MyTestProject",
  "sources": [
    {
      "source": "./Custom/MyTestProject/",
      "target": "./src/MyTestProject/"
    },
    {
      "source": "./Custom/MyTestProject.Tests/",
      "target": "./test/MyTestProject.Tests/"
    },
  ],

  "postActions": [
    {
      "description": "Restore NuGet packages required by this project.",
      "manualInstructions": [
        { "text": "Run 'dotnet restore'" }
      ],
      "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
      "continueOnError": true,
      "args": {
        "files": [ "./Custom/MyTestProject/MyTestProject.csproj" ]  //here the source location should be used as the post action supports this notation
      }
    }
  ]
}

Specifying files in post action arguments is supported by:

  • restore NuGet packages (dotnet CLI) - files argument
  • add reference to a project file (dotnet CLI) - targetFiles argument
  • add project(s) to a solution file (dotnet CLI) - projectFiles argument

Example 3 - using primary output indexes in post action arguments

Opens the file from primary outputs defined by index.

template.json definition:

"primaryOutputs": [
  { "path": "Company.ClassLibrary1.csproj" },
  { "path": "Class1.cs" }
],
"postActions": [
  {
    "description": "Opens Class1.cs in the editor",
    "manualInstructions": [ ],
    "actionId": "84C0DA21-51C8-4541-9940-6CA19AF04EE6",
    "args": {
      "files": "1"
    },
    "continueOnError": true
  }
]

Using indexes in post action arguments is supported by:

  • open a file in the editor (Visual Studio)