31 May 2019

Deploying to Design Automation from Visual Studio

Deploying to Design Automation from Visual Studio

Since pretty much all the Design Automation v3 engines require Windows environment for building their addins or plugins, wouldn't it be great if we could automatically deploy build artifacts to the Design Automation service right from Visual Studio? Well, it just so happens that there's a command line tool that can help with that: forge-cli-utils. Let's take a look at how you can use it from within Visual Studio's post build steps.

Note: there are multiple efforts to provide cross-platform CLI tools for accessing Forge services (for example, my colleague Denis Grigor works on a similar tool implemented using Go, forge-cli) but we will use forge-cli-utils as those have a more mature support for Design Automation.

Prerequisites

We assume that you already have a Visual Studio project setup to build a plugin for one of the Design Automation engines, e.g., for Revit or Inventor. If not, you can try one of the sample projects or templates from our blog posts, for example:

You'll also need the following:

Setup

First of all, let's make sure that your Visual Studio project bundles the build output into a zip file with the structure required by Design Automation. In Visual Studio, right-click on your plugin project, choose Properties, and in the Properties tab, go to the Build Events section. The Post-build event command line area should already contain a couple of commands for signing and packaging the output, similar to the following:

call "%vsappiddir%..\..\VC\Auxiliary\Build\vcvars32.bat" x86        

mt.exe -manifest "$(ProjectDir)MyDesignAutomationPlugin.X.manifest" -outputresource:"$(TargetPath)"



xcopy "$(ProjectDir)PackageContents.xml" "$(TargetDir)..\Bundle\MyDesignAutomationPlugin.bundle\" /Y

xcopy "$(TargetDir)*.*" "$(TargetDir)..\Bundle\MyDesignAutomationPlugin.bundle\Contents\" /Y



del "$(SolutionDir)Output\MyDesignAutomationPlugin.bundle.zip"

7za.exe a -aoa -tzip -xr!*.exe "$(SolutionDir)Output\MyDesignAutomationPlugin.bundle.zip" "$(TargetDir)..\Bundle\MyDesignAutomationPlugin.bundle"

Next, let's create a PowerShell script that will deploy the bundled archive to Design Automation. In Visual Studio, add a new file to the plugin project, name it deploy-forge-da.ps1, and paste in the following content (setting $appbundle_name, $appbundle_alias, $appbundle_engine, $activity_name, and $activity_alias with your own values):

# FORGE_CLIENT_ID and FORGE_CLIENT_SECRET must be set before running this script.

param([string]$bundle) # This script expects a named parameter "bundle" with path to the appbundle zip file

# Helper function invoking forge-da.exe with custom arguments and intercepting non-zero exit codes
function Request-DA {
    $command = $args[0]
    $result = Invoke-Expression "forge-da.exe $command"
    if ($LASTEXITCODE -ne 0) {
        Write-Error "forge-da command failed: $command" -ErrorAction "Stop"
    }
    return $result
}

$appbundle_file = $bundle
$appbundle_name = "MyDesignAutomationBundle" # Your own appbundle name here
$appbundle_alias = "dev" # Your own alias name here
$appbundle_engine = "Autodesk.Inventor+24" # Your own appbundle engine here

$activity_name = "MyDesignAutomationActivity" # Your own activity name here
$activity_alias = "dev" # Your own activity alias here

# Create or update an appbundle
Write-Host "Creating an appbundle $appbundle_name with zip file $appbundle_file"
$result = Request-DA "list-appbundles --short"
$result = $result | Select-String -Pattern $appbundle_name | Measure-Object -Line
if ($result.Lines -eq 0) {
    Write-Host "Creating new appbundle"
    Request-DA "create-appbundle $appbundle_name $appbundle_file $appbundle_engine"
} else {
    Write-Host "Updating existing appbundle"
    Request-DA "update-appbundle $appbundle_name $appbundle_file $appbundle_engine"
}

# Create or update an appbundle alias
Write-Host "Creating an appbundle alias $appbundle_alias"
$result = Request-DA "list-appbundle-versions $appbundle_name --short"
$appbundle_version = $result | Select-Object -Last 1
Write-Host "Last appbundle version: $appbundle_version"
$result = Request-DA "list-appbundle-aliases $appbundle_name --short"
$result = $result | Select-String -Pattern $appbundle_alias | Measure-Object -Line
if ($result.Lines -eq 0) {
    Write-Host "Creating new appbundle alias"
    Request-DA "create-appbundle-alias $appbundle_alias $appbundle_name $appbundle_version"
} else {
    Write-Host "Updating existing appbundle alias"
    Request-DA "update-appbundle-alias $appbundle_alias $appbundle_name $appbundle_version"
}

# Create or update an activity
Write-Host "Creating an activity $activity_name"
$result = Request-DA "list-activities --short"
$result = $result | Select-String -Pattern $activity_name | Measure-Object -Line
if ($result.Lines -eq 0) {
    Write-Host "Creating new activity"
    Request-DA "create-activity $activity_name $appbundle_name $appbundle_alias $appbundle_engine --input PartFile --output Thumbnail --output-local-name thumbnail.bmp"
} else {
    Write-Host "Updating existing activity"
    Request-DA "update-activity $activity_name $appbundle_name $appbundle_alias $appbundle_engine --input PartFile --output Thumbnail --output-local-name thumbnail.bmp"
}

# Create or update an activity alias
Write-Host "Creating an activity alias $activity_alias"
$result = Request-DA "list-activity-versions $activity_name --short"
$activity_version = $result | Select-Object -Last 1
Write-Host "Last activity version: $activity_version"
$result = Request-DA "list-activity-aliases $activity_name --short"
$result = $result | Select-String -Pattern $activity_alias | Measure-Object -Line
if ($result.Lines -eq 0) {
    Write-Host "Creating new activity alias"
    Request-DA "create-activity-alias $activity_alias $activity_name $activity_version"
} else {
    Write-Host "Updating existing activity alias"
    Request-DA "update-activity-alias $activity_alias $activity_name $activity_version"
}

Finally, go back to the Properties of your plugin project, and add one more post-build step at the end that will call our PowerShell script (replacing MyDesignAutomationPlugin.bundle.zip with the name and location of your own appbundle archive):

powershell.exe -ExecutionPolicy RemoteSigned -file $(ProjectDir)\deploy-forge-da.ps1 -bundle "$(SolutionDir)Output\MyDesignAutomationPlugin.bundle.zip"

Note: for more details on why the -ExecutionPolicy RemoteSigned flag is needed, see https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-5.1.

Running & Troubleshooting

Build your Visual Studio project or solution, and check out the Output tab. You should see a couple of logs from the PowerShell script informing you of appbundles and activities being created or updated.

If you run into troubles, and the build log doesn't provide sufficient feedback, make sure to check the following:

  • FORGE_CLIENT_ID and FORGE_CLIENT_SECRET env. variables are available in command line
  • 7za.exe is available in command line (its folder must be included in the PATH env. variable)
  • forge-da.exe is available in command line (its folder must be included in the PATH env. variable)
  • you have not exceeded the limit of 100 versions across all appbundles
  • appbundle or activity names are not clashing with other customers' names

Additional Resources

 

Related Article