Zero-Click Releases: Building a Revit Plugin Pipeline with GitHub Actions
Eliminate manual deployment pain with automated Revit add-in deployment to Design Automation using RAPS and GitHub Actions
Validated: Design Automation๐คDesign AutomationRun Autodesk desktop apps in the cloud.View in glossary workflow๐WorkflowAutomated process triggered by events.View in glossary claims are tested via automated benchmarks. View latest results
From 20 Minutes to 0 Minutes: The Manual Tax
In our previous exploration of AEC development pain points, we discussed โThe Manual Taxโ โ the hidden cost of manual deployments. Today, we kill the manual process entirely.
Deploying a Revit๐ RevitAutodesk's BIM software for architecture and construction.View in glossary Add-in to Design Automation๐คAutomationReplacing manual processes with software.View in glossary usually involves:
- Compiling the DLLs locally (hopefully in Release mode)
- Zipping them up correctly (did you remember the dependencies?)
- Getting a new authentication token๐๏ธTokenCredential for API authentication.View in glossary (expired again?)
- Uploading the AppBundle๐ฆAppBundleCustom plugin package for Design Automation.View in glossary (please work this timeโฆ)
- Creating a new Activity๐ActivityDesign Automation job definition.View in glossary alias (production or staging?)
If you do this manually, you will eventually upload a debug build to production. Hereโs how to automate it using RAPS๐ผRAPSRust CLI for Autodesk Platform Services.View in glossary and GitHub Actions๐GitHub ActionsGitHub's built-in CI/CD platform.View in glossary.
The Zero-Click Workflow
Weโll create a pipelineโ๏ธPipelineAutomated sequence of build/test/deploy steps.View in glossary that runs on every git push to the main branch, transforming your โtime to deployโ from 20 minutes of clicking to 0 minutes.
1. Define the RAPS Configuration
First, ensure your repository has a raps.toml defining your AppBundle structure. This tells the CLI๐ปCLIText-based interface for running commands.View in glossary which DLLs to bundle and how to structure your deployment.
[appbundle]
name = "my-revit-plugin"
engine = "Autodesk.Revit+2024"
description = "Automated Revit processing plugin"
[appbundle.bundle]
main_dll = "MyPlugin.dll"
dependencies = [
"MyPlugin.Shared.dll",
"Newtonsoft.Json.dll"
]
[appbundle.activity]
command_line = "$(engine.path)\\\\revit.exe /i $(args[inputFile].path) /al $(appbundles[{appbundle.name}].path)"
2. The GitHub Action
Create .github/workflows/deploy.yml:
name: Deploy to APS Design Automation
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build-and-deploy:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build Release
run: dotnet build --configuration Release --no-restore
- name: Install RAPS
run: |
curl -fsSL https://rapscli.xyz/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Bundle & Deploy
if: github.ref == 'refs/heads/main'
env:
APS_CLIENT_ID: ${{ secrets.APS_CLIENT_ID }}
APS_CLIENT_SECRET: ${{ secrets.APS_CLIENT_SECRET }}
run: |
# Authenticate with APS
raps auth login --client-id $APS_CLIENT_ID --client-secret $APS_CLIENT_SECRET
# Create the bundle (handles zipping automatically)
raps da:bundle create --path ./bin/Release/net48
# Update the AppBundle in the cloud and alias it to 'prod'
raps da:appbundle update my-revit-plugin --alias prod
# Update the Activity to use the new bundle version
raps da:activity update my-revit-plugin --alias prod
- name: Test Deployment
if: github.ref == 'refs/heads/main'
run: |
# Run a simple test to verify the deployment
raps da:workitem run my-revit-plugin \
--input-file https://developer.api.autodesk.com/oss/v2/signedresources/test.rvt \
--output-format ifc
3. Advanced: Multi-Environment Deployment
For more sophisticated workflows, deploy to different environments based on the branch:
name: Multi-Environment Deploy
on:
push:
branches: [ "main", "staging", "develop" ]
jobs:
deploy:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Determine Environment
id: env
run: |
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "environment=production" >> $GITHUB_OUTPUT
echo "alias=prod" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == "refs/heads/staging" ]]; then
echo "environment=staging" >> $GITHUB_OUTPUT
echo "alias=staging" >> $GITHUB_OUTPUT
else
echo "environment=development" >> $GITHUB_OUTPUT
echo "alias=dev" >> $GITHUB_OUTPUT
fi
# ... build steps ...
- name: Deploy to ${{ steps.env.outputs.environment }}
env:
APS_CLIENT_ID: ${{ secrets.APS_CLIENT_ID }}
APS_CLIENT_SECRET: ${{ secrets.APS_CLIENT_SECRET }}
ENVIRONMENT: ${{ steps.env.outputs.environment }}
ALIAS: ${{ steps.env.outputs.alias }}
run: |
raps auth login --client-id $APS_CLIENT_ID --client-secret $APS_CLIENT_SECRET
# Deploy with environment-specific alias
raps da:bundle create --path ./bin/Release/net48
raps da:appbundle update my-revit-plugin --alias $ALIAS
raps da:activity update my-revit-plugin --alias $ALIAS
echo "โ
Deployed to $ENVIRONMENT environment with alias '$ALIAS'"
Deployment Time Tax
Minutes spent on manual deployment vs. automated pipeline
Why This Transforms Your Workflow
Before: The Manual Tax
- 20 minutes per deployment
- High error rate (debug builds, missing files)
- Context switching from development to deployment tasks
- Inconsistent deployment process across team members
- No audit trail of what was deployed when
After: Zero-Click Releases
- 0 minutes active deployment time
- Zero human errors (automated building and bundling)
- Consistent process across all environments
- Complete audit trail in GitHub Actions logs
- Immediate rollback capability via git revert
Advanced Features
Automatic Version Tagging
Add automatic semantic versioning to your releases:
- name: Generate Version
id: version
run: |
VERSION=$(date +%Y.%m.%d)-$(git rev-parse --short HEAD)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Generated version: $VERSION"
- name: Tag AppBundle Version
run: |
raps da:appbundle update my-revit-plugin \
--alias prod \
--version ${{ steps.version.outputs.version }} \
--description "Auto-deployed from commit ${{ github.sha }}"
Slack/Teams Notifications
Get notified when deployments complete:
- name: Notify Success
if: success()
uses: 8398a7/action-slack@v3
with:
status: success
text: "โ
Revit plugin deployed to production successfully!"
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
- name: Notify Failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
text: "โ Revit plugin deployment failed. Check GitHub Actions for details."
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Integration Testing
Test your deployed plugin automatically:
- name: Integration Test
run: |
# Test with a small sample file
WORKITEM_ID=$(raps da:workitem run my-revit-plugin \
--input-file ${{ secrets.TEST_FILE_URL }} \
--output-format ifc \
--wait \
--format json | jq -r '.id')
# Verify the workitem completed successfully
STATUS=$(raps da:workitem status $WORKITEM_ID --format json | jq -r '.status')
if [[ "$STATUS" != "succeeded" ]]; then
echo "โ Integration test failed. WorkItem status: $STATUS"
exit 1
fi
echo "โ
Integration test passed. Plugin is working correctly."
Security Best Practices
Environment Variables
Store sensitive data in GitHub Secrets:
APS_CLIENT_ID- Your Autodesk app client IDAPS_CLIENT_SECRET- Your Autodesk app client secret๐SecretEncrypted sensitive configuration value.View in glossarySLACK_WEBHOOK- Slack notification webhook๐ชWebhooksEvent notifications sent to your application.View in glossary (optional)TEST_FILE_URL- URL to your test Revit file (optional)
Least Privilege Access
Create a dedicated APSโ๏ธAPSAutodesk Platform Services - cloud APIs for CAD/BIM automation.View in glossary app for CI/CD๐CI/CDAutomated build, test, and deployment pipelines.View in glossary with minimal required scopes:
# Create a CI-specific app with limited permissions
raps auth create-app \
--name "ci-deployment-app" \
--scopes "code:all bucket:create bucket:read data:read data:write"
Monitoring and Observability
Track your deployment success rate:
- name: Record Deployment Metrics
if: always()
run: |
# Send deployment metrics to your monitoring system
curl -X POST "${{ secrets.METRICS_ENDPOINT }}" \
-H "Content-Type: application/json" \
-d '{
"deployment": "revit-plugin",
"status": "${{ job.status }}",
"duration": "${{ steps.deploy.outputs.duration }}",
"commit": "${{ github.sha }}",
"branch": "${{ github.ref_name }}"
}'
Troubleshooting Common Issues
Build Failures
- name: Debug Build Issues
if: failure()
run: |
echo "=== Build Logs ==="
cat build.log
echo "=== Assembly References ==="
raps debug assembly-info ./bin/Release/net48/*.dll
Authentication Problems
- name: Test Authentication
run: |
# Verify auth is working
raps auth test || {
echo "โ Authentication failed"
echo "Check APS_CLIENT_ID and APS_CLIENT_SECRET secrets"
exit 1
}
Bundle Size Issues
- name: Check Bundle Size
run: |
BUNDLE_SIZE=$(du -h ./bundle.zip | cut -f1)
echo "Bundle size: $BUNDLE_SIZE"
# Warn if bundle is over 100MB
if [[ $(du -b ./bundle.zip | cut -f1) -gt 104857600 ]]; then
echo "โ ๏ธ Warning: Bundle size ($BUNDLE_SIZE) is large. Consider optimizing dependencies."
fi
The Bottom Line
With this automation pipeline:
- You push code โ You go get coffee โ Your plugin is deployed
- Zero manual steps in your deployment process
- Consistent, repeatable deployments across all environments
- Complete audit trail and instant rollback capability
- Integration testing ensures quality before production
The โManual Taxโ is expensive. Automation pays for itself after the first few deployments.
Coming Up Next
In our next article, weโll explore why we chose Rust๐ฆRustSystems programming language known for safety.View in glossary over Node.js for building RAPS, and how this decision enables processing 5GB+ model files that would crash traditional JavaScript tooling.
Part of our โAEC DevOps Revolutionโ series. Because your deployment process shouldnโt require a PhD in clicking buttons.