Developer → Branch → PR → CI Builds → Merge → Release Workflow → Network Share → Pro Update → User PCs
Before (PTI Publish)
Manual process using a Visual Studio extension. Developer selects project, picks version bump, builds, stages, then deploys — all from within VS.
Now (GitHub Actions)
Automated pipeline triggered from GitHub. CI validates builds on every PR. Releases are triggered via workflow dispatch with version bumping, building, and deploying handled automatically.
Step
Old Way (PTI Publish)
New Way (GitHub Actions)
Version bump
Manual in VS extension
Automated by bump-version.ps1
Build
VS on developer’s machine
MSBuild on dedicated runner VM
Deploy to share
VS extension “Deploy” button
Automated by deploy-to-share.ps1
Notification
Manual email from developer
Automated Teams group chat via Bot Framework
Beta tracking
Email thread / nothing
GitHub Issue + Teams chat + 7-day reminders
Trigger
Developer remembers to do it
Workflow dispatch with audit trail
Application Manager is unchanged. It still polls version.txt on the network share. The CI/CD pipeline just automates how files get there.
Daily Development Workflow
Instructions show Visual Studio 2022 GUI first, with CLI alternatives.
1. Create a Branch
In VS, click the branch name in the status bar (bottom-left) to open the branch picker
Make sure you’re on main — if not, click main to switch
Click Pull (down arrow) in the Git Changes toolbar to get the latest
Click the branch name again, then New Branch
Name it feature/PSI.ProViewer/fix-print-dialog, set Based on to main, check Checkout branch, click Create
Select “Release: Deploy App” from the left sidebar
Click “Run workflow”
Fill in the fields:
Field
What to enter
Application to deploy
Select the app from the dropdown
Release channel
alpha, beta, minor, or major
Release notes
Required — describe what changed. Shown in Teams chat and tracking issue
Test instructions
Optional — specific steps for testers. Defaults to generic instructions if blank
Beta users
Optional for beta — comma-separated AD usernames to override registry (leave blank to use existing registry config managed via Admin Panel)
Click “Run workflow”
The workflow summary (visible on the run page) shows a formatted deploy summary including release notes, version, and test instructions.
What Happens for Each Channel
Alpha:
Builds and deploys to _alpha/ subfolder
No version bump commit, no notifications
Can re-deploy as many times as needed
Only visible in Pro Update for users with alpha access
Beta:
Builds and deploys to the beta channel (writes betaversion.txt)
Beta testers come from app-registry.json — managed via the Pro Update Admin Panel. If you enter beta_users in the workflow, it overrides the registry for this deploy.
Creates a GitHub Issue to track the beta test ([Beta] AppName vX.X.X.X)
Creates a Teams group chat between the deployer and testers (via Bot Framework)
Sends the deploy notification message to the group chat with release notes and test instructions
If a previous beta for the same app was open, it’s automatically superseded
No version bump commit — beta reuses the same version on re-deploys
After 7 days without promotion, an automated reminder is posted to the chat and issue
Minor (stable):
Bumps the last version segment (e.g., 1.0.0.22 → 1.0.0.23)
Builds, deploys, and updates version.txt — all users get this
Commits the version bump back to main
Creates a GitHub Release with a version tag
Major (stable):
Same as minor, but bumps the second-to-last segment (e.g., 1.0.0.22 → 1.0.1.0)
No rollback: There’s no undo for a stable deploy. If something is broken, merge a fix and deploy a new version. When in doubt, beta first.
Promoting Beta to Stable
When beta testing passes and you’re ready for all users to get the update:
Find the beta tracking issue in GitHub (titled [Beta] AppName vX.X.X.X)
Comment /promote on the issue
This triggers the promote workflow which will:
Copy betaversion.txt → version.txt (all users now see the update)
Delete betaversion.txt
PATCH the existing GitHub Release’s prerelease flag to false (tag stays pinned to the original build commit)
Close the tracking issue
Send a “promoted to stable” message to the original Teams group chat
betaAccess is intentionally left in place. Tester rosters are typically stable across iterations, so the same list applies to the next beta of the same app. To remove a tester, edit the access list directly in the Pro Update Admin Panel.
The promote workflow reacts to the /promote comment with a rocket emoji so you know it was received.
Pro Update (Self-Updater)
Pro Update uses the standard “Release: Deploy App” workflow — select “PSIUpdater” from the app dropdown. It handles its own self-update mechanism automatically (batch script workaround). See Pro Update for details on the self-update process.
Managing Beta/Alpha Access (Admin Panel)
Instead of entering beta user initials each time you deploy, you can pre-configure access lists using the Pro Update Admin Panel:
Open Pro Update — the gear icon (top-right, “Beta Management”) appears automatically if you’re a member of the AD\IT - RF security group. (As of v1.0.0.55, AD is the only gate; the legacy developers JSON-group fallback was removed.)
Click the gear icon to open the admin panel
Find your app in the list, click the Beta Access cell to edit
Enter AD usernames or groups (comma-separated): AMD,BMC,JPT
Click Save — changes are written to app-registry.json on the network share and synced to txt files
These testers are used automatically for every beta deploy — no need to re-enter names. You can also enter beta users directly in the workflow’s Beta users field for a one-time override.
Beta Testing Workflow
Beta releases have a structured tracking lifecycle:
Configure beta testers (Admin Panel or workflow override)
↓
Deploy to Beta
↓
app-registry.json betaAccess used (synced to PSApplications.txt)
GitHub Issue created: [Beta] AppName vX.X.X.X
Teams group chat created (deployer + testers)
Bot sends notification message to chat
↓
Testers use the app (auto-updates via Pro Update)
Report issues in the Teams group chat
↓
[7-day reminder if not promoted or closed]
↓
Developer comments /promote on the tracking issue
↓
version.txt updated (all users get it)
GitHub Release flipped from prerelease to stable
betaAccess preserved in registry (same testers usually validate the next iteration)
Tracking issue auto-closed
Bot sends "Promoted to stable" to group chat
For beta testers: You’ll get a Teams notification in a group chat telling you what to test. Run ProReset or open Pro Update to get the beta version. Report any issues directly in the group chat.
For developers: After deploying a beta:
Watch the Teams group chat for tester feedback (“found a bug,” “working great,” etc.)
Watch the GitHub tracking issue for automated updates. After 7 days without promotion, an automated reminder is posted.
Need to fix a bug? Merge the fix to main, then re-run the beta workflow for the same app. The old tracking issue closes automatically, a new one is created, and a new Teams chat is started. Testers run ProReset or Pro Update to get the fix.
Ready to ship? Comment /promote on the tracking issue.
You can re-deploy a beta as many times as you need. Each re-deploy is a fresh beta with its own tracking issue and Teams chat.
CI/CD Architecture
Workflows
Workflow
Trigger
Purpose
CI: Changed Apps
PR to main
Detects changed folders, builds affected apps
CI: Core Libraries
PR/push touching PSI.Shared or PSI.Common.Forms
Builds core libraries in dependency order
CI: Core Impact
PR touching PSI.Common/DataAccess
Builds downstream apps to catch breaking changes
Tests
PR touching PSIUpdater or deploy/
Runs xUnit and Pester tests
Release: Deploy App
Manual dispatch
Version bump → build → deploy → notify (all apps including Pro Update)
Release: Promote Beta
/promote comment on beta-test issue
Promote beta → stable, close tracking issue
Beta: Stale Reminder
Daily at 10 AM ET (weekdays)
Nags about betas open > 7 days
Removed workflows (March 2026):release-updater.yml (Pro Update now uses the standard release workflow) and manage-beta-users.yml (replaced by the Pro Update Admin Panel).
Runner
All workflows run on a self-hosted Windows runner VM (PS-GR-RUNNER) with:
Fields 4 and 5 are optional. Missing fields default to empty (no access).
Source of truth:deploy/app-registry.json is the canonical source for access lists. The txt files are generated from the registry by sync-registry.ps1 (run automatically after Admin Panel saves and CI/CD deploys). Do not edit txt files directly — changes will be overwritten on the next sync.
App Registry
deploy/app-registry.json maps repo folders to deployment targets. All 37 apps are registered. Apps are onboarded by setting enabled: true.
Important: Repo folder names, deploy share folder names, and exe names are often different. See App Name Mapping for the complete lookup table.
Teams Notifications (Bot Framework)
Bot identity, secrets, Graph permissions, and chat-creation pitfalls are documented in Teams Notifications — the shared guide for all PSI bot notifications. This section covers only the deploy-specific behavior.
Beta deploys use the per-event group chat pattern: each beta gets a fresh group chat between the deployer and the testers, scoped to that one release.
Deploy-specific flow:
send-teams-notification.ps1 resolves tester AD usernames (SAM account names) to AAD object IDs via Graph API (User.Read.All with onPremisesSamAccountName filter).
Creates a new group chat with the deployer + testers (Graph POST /chats, chatType: "group").
Installs the bot into the chat (Graph POST /chats/{id}/installedApps).
Sends the deploy notification via Bot Framework REST API.
Chat ID is stored in the GitHub tracking-issue body as an HTML comment so follow-up messages (promote, stale reminder) can find the chat.
Card types (deploy-specific HTML message shapes built in send-teams-notification.ps1):
CardType
Trigger
Message contents
beta-deploy
Beta deploy workflow run
Release notes, test instructions, tracking issue link
beta-promoted
/promote comment on tracking issue
”Promoted — all users get this update”
beta-reminder
Beta-reminder workflow run (7+ days open)
“Please test and report back”
stable-deploy
(Reserved — not currently emitted)
—
Alpha deploys send no notification.
Solo-tester fallback: when the deployer is also the only tester, $allUpns dedupes to 1 unique person and Graph’s chatType: "group" rejects with 'user@odata.bind' field is missing (see pitfalls in Teams Notifications → Graph chat-creation pitfalls). send-teams-notification.ps1 falls back to the bot’s personal 1:1 chat with that user — install the bot into the user’s personal scope (POST /users/{upn}/teamwork/installedApps) and use the resulting user↔bot chat ID. The user gets a DM instead of a group chat.
Secrets (still in GH Actions secrets — GRAPH_*, TEAMS_BOT_CATALOG_ID). These mirror psi-notify--* in ps-certificates-kv. Migration to OIDC + KV fetch is on the roadmap.
Secrets
Secret
Purpose
ADMIN_PAT
GitHub PAT with SSO authorization — used for pushing version bumps, creating releases, managing issues
GRAPH_TENANT_ID
Azure AD tenant ID (a83ae943-0a50-49cc-83c3-479b7a44b7fb)
GRAPH_CLIENT_ID
App registration client ID (5f54d8fc-fca1-4c78-9894-c267018efadb)
Teams app catalog ID for PSI Deploy Bot (eb9901e7-e87c-437d-803a-b766ec007a16)
GHE Repository Policies
Branch Rules (Ruleset)
The PSI.All repo uses a repository ruleset on main:
Rule
Setting
Pull request required
Yes — but approval is optional
Merge method
Squash and merge (commits combined into one)
Auto-delete branches
Yes — branches deleted after merge
Bypass actors
Organization admins
Version Bumps
After a minor or major (stable) release, the workflow automatically commits the AssemblyInfo.cs version bump directly to main with [skip ci] in the commit message (to avoid re-triggering CI). This uses ADMIN_PAT to bypass branch protection.
Alpha and beta releases do NOT commit version bumps — the version is only used for the build and deploy.
Share-Aware Version Bumping
bump-version.ps1 checks the network share for existing version folders before finalizing the version number. If a version folder already exists (from a prior deploy or PTI Publish), it keeps incrementing until it finds an unused version.
Key Files
File
Purpose
deploy/app-registry.json
Maps apps to solution paths and deploy names
deploy/bump-version.ps1
Increments AssemblyVersion in AssemblyInfo.cs
deploy/deploy-to-share.ps1
Copies build output to network share, writes version files
Release pipeline for all apps (including Pro Update)
.github/workflows/promote-beta.yml
Promote beta to stable (triggered by /promote comment)
.github/workflows/beta-reminder.yml
Daily stale beta check
.github/workflows/build-app.yml
PR build validation
.github/workflows/build-core.yml
Core library builds
.github/workflows/core-impact.yml
Downstream impact validation
.github/workflows/test.yml
xUnit and Pester tests
Troubleshooting
Build failed on PR
Click “Details” next to the failed check to see the build log
Fix the issue, commit, and push to the same branch — the PR updates automatically
”Permission denied” when pushing
Make sure you’re pushing to a branch, not main directly
Check that your GHE Personal Access Token hasn’t expired
Merge conflicts
In Visual Studio: When you pull and conflicts occur, the Git Changes window shows conflicted files under Merge Changes. Double-click a file to open the Merge Editor (three-pane view). Use the checkboxes to accept changes from either side, or edit the Result pane directly. Click Accept Merge when done, then Commit Staged and Push.
Or via command line:
git checkout feature/my-branchgit pull origin main# Resolve conflicts in your editorgit add .git commit -m "Resolve merge conflicts"git push
Release workflow failed
Check the Actions tab for the failed run’s logs
Common issues: network share unreachable, MSBuild errors, ADMIN_PAT expired
The deploy script has retry logic with exponential backoff for network issues
App not updating on user PCs
Verify version.txt was updated on the network share
Check that Application Manager is running in the user’s system tray
User must be on the internal network — Application Manager skips update checks on other subnets
For beta: verify the user is listed in PSApplications.txt beta column
For alpha: verify alpha access in PSApplications.txt 5th field, then use Pro Update (not Application Manager)
Teams notifications not sending
Verify all GRAPH_* secrets and TEAMS_BOT_CATALOG_ID are set in the repo
Check that the bot app registration client secret hasn’t expired (rotate in Azure AD → App registrations → Certificates & secrets)
User resolution uses Graph API (onPremisesSamAccountName filter) — verify the AD usernames in app-registry.json match actual SAM account names
Notifications use continue-on-error — a failed notification will not block the deploy
Check the workflow run logs for the “Send Teams notification” step
If the bot was removed from a chat, the follow-up messages (promote/reminder) for that chat will fail silently
Bot setup / maintenance
Azure Bot resource:PSIDeployNotifyBot in resource group PS-WEBAPPS
App registration:PSI.All Deploy Notifications in Azure AD
Teams app manifest:deploy/teams-bot/ in the repo — if you need to update the manifest, repackage as zip and re-upload to the Teams org catalog
To upload the Teams app: Use the MSAL device code script at deploy/teams-bot/upload-app.ps1 with AppCatalog.ReadWrite.All scope, or upload via Teams Admin Center > Manage apps > Upload new app
Client secret rotation: Create a new secret in Azure AD, update GRAPH_CLIENT_SECRET GitHub secret
See Also
Deploy FAQ — Quick Q&A for common deployment questions