Deploy ProApps (Desktop Applications)

How to release PSI desktop applications from the PSI.All mono-repo using GitHub Actions CI/CD.


Quick Reference

ActionHow
Release an appActions tab → “Release: Deploy App” → Run workflow
Beta releaseSame, select “beta” — testers come from registry (or override in workflow)
Manage beta/alpha accessPro Update Admin Panel (gear icon — visible to AD\IT - RF members; no admin argument needed)
Promote beta to stableComment /promote on the beta tracking issue
Check build statusLook at PR “Checks” section
View deploy logsActions tab → click the workflow run

Repository: PSI.All on GitHub Enterprise


How It Works

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.

StepOld Way (PTI Publish)New Way (GitHub Actions)
Version bumpManual in VS extensionAutomated by bump-version.ps1
BuildVS on developer’s machineMSBuild on dedicated runner VM
Deploy to shareVS extension “Deploy” buttonAutomated by deploy-to-share.ps1
NotificationManual email from developerAutomated Teams group chat via Bot Framework
Beta trackingEmail thread / nothingGitHub Issue + Teams chat + 7-day reminders
TriggerDeveloper remembers to do itWorkflow 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

  1. In VS, click the branch name in the status bar (bottom-left) to open the branch picker
  2. Make sure you’re on main — if not, click main to switch
  3. Click Pull (down arrow) in the Git Changes toolbar to get the latest
  4. Click the branch name again, then New Branch
  5. Name it feature/PSI.ProViewer/fix-print-dialog, set Based on to main, check Checkout branch, click Create

Branch naming: {type}/{app-folder}/{short-description}

PrefixUse for
feature/New features or enhancements
bugfix/Bug fixes

Or via command line:

git checkout main && git pull origin main
git checkout -b feature/PSI.ProViewer/fix-print-dialog

2. Make Changes and Commit

  1. Make your code changes in VS as usual
  2. Open the Git Changes window (Ctrl+Shift+G)
  3. Hover over changed files and click + to stage them (or Stage All)
  4. Enter a commit message — include AB# followed by the work item number if applicable: Fix print dialog crash when no printer selected AB#892
  5. Click Commit Staged

Or via command line:

git add PSI.ProViewer/Trunk/PSI.ProViewer/SomeFile.cs
git commit -m "Fix print dialog crash when no printer selected AB#892"

3. Push and Create a Pull Request

  1. In Git Changes, click the Push button (up arrow) to push your branch to GitHub
  2. VS may show a “Create a pull request” notification — click it to open the browser

Or go to PSI.All on GHE and click “Compare & pull request”.

Or via command line:

git push -u origin feature/PSI.ProViewer/fix-print-dialog

4. CI Builds Run Automatically

When a PR is opened, GitHub Actions automatically:

  • Detects which app folders changed (only builds what you touched)
  • Builds each changed app with MSBuild in Release configuration
  • If PSI.Common changed: also builds downstream apps to catch breaking changes
  • Reports pass/fail directly on the PR

You must wait for builds to pass before merging.

5. Merge

After passing checks:

  1. Click “Squash and merge” (this is the default — all commits are combined into one)
  2. The branch is automatically deleted after merge

PR approval is optional. You can merge your own PR once CI passes.


Releasing an Application

Release Channels

ChannelWho sees itVersion fileUse case
AlphaAlpha testers onlyalphaversion.txtDev testing, invisible to most users
BetaNamed testers onlybetaversion.txtStructured testing before general release
Stable (minor/major)All usersversion.txtProduction release

Step-by-Step: Deploy to Any Channel

  1. Go to the Actions tab in PSI.All on GHE
  2. Select “Release: Deploy App” from the left sidebar
  3. Click “Run workflow”
  4. Fill in the fields:
FieldWhat to enter
Application to deploySelect the app from the dropdown
Release channelalpha, beta, minor, or major
Release notesRequired — describe what changed. Shown in Teams chat and tracking issue
Test instructionsOptional — specific steps for testers. Defaults to generic instructions if blank
Beta usersOptional for beta — comma-separated AD usernames to override registry (leave blank to use existing registry config managed via Admin Panel)
  1. 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:

  1. Find the beta tracking issue in GitHub (titled [Beta] AppName vX.X.X.X)
  2. Comment /promote on the issue

This triggers the promote workflow which will:

  • Copy betaversion.txtversion.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:

  1. 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.)
  2. Click the gear icon to open the admin panel
  3. Find your app in the list, click the Beta Access cell to edit
  4. Enter AD usernames or groups (comma-separated): AMD,BMC,JPT
  5. 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

WorkflowTriggerPurpose
CI: Changed AppsPR to mainDetects changed folders, builds affected apps
CI: Core LibrariesPR/push touching PSI.Shared or PSI.Common.FormsBuilds core libraries in dependency order
CI: Core ImpactPR touching PSI.Common/DataAccessBuilds downstream apps to catch breaking changes
TestsPR touching PSIUpdater or deploy/Runs xUnit and Pester tests
Release: Deploy AppManual dispatchVersion bump → build → deploy → notify (all apps including Pro Update)
Release: Promote Beta/promote comment on beta-test issuePromote beta → stable, close tracking issue
Beta: Stale ReminderDaily 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:

  • Visual Studio Build Tools 2022
  • .NET Framework 4.8 SDK
  • MSBuild, NuGet CLI, PowerShell 7, Git, GitHub CLI (gh)
  • Network access to Azure AD (Graph API resolves user names to UPNs)
  • Network access to the deployment share

Runner labels: [self-hosted, windows, dotnet-framework]

Network Share Structure

\\ad.ptihome.com\DFS\Data\APPS\Approved\DotNet\AppDeployments\
├── PSApplications.txt          # App list (name;exe;access;betaUsers;alphaUsers)
├── PSServices.txt              # Service list (same format)
├── Apps\
│   └── {AppName}\
│       ├── version.txt              # Current stable version
│       ├── betaversion.txt          # Current beta version (if active)
│       ├── alphaversion.txt         # Current alpha version (if active)
│       ├── release-notes.json       # Latest release notes
│       ├── {version}\               # Stable/beta binaries folder
│       │   ├── AppName.exe
│       │   ├── *.dll
│       │   └── *.config
│       └── _alpha\                  # Alpha-only subfolder
│           └── {version}\           # Alpha binaries folder
└── Telemetry\                       # Update telemetry (JSON)

PSApplications.txt / PSServices.txt Format

AppName;ExecutableName;Access;BetaAccess;AlphaAccess
FieldDescriptionExample
AppNameDisplay name on the sharePrint Part Label
ExecutableNameThe .exe filePSI.PrintPartLabel.exe
AccessStable access list (usually ALL)ALL
BetaAccessComma-separated beta tester AD usernamesBMC,JPT,AMD
AlphaAccessComma-separated alpha tester AD usernamesAMD

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:

  1. send-teams-notification.ps1 resolves tester AD usernames (SAM account names) to AAD object IDs via Graph API (User.Read.All with onPremisesSamAccountName filter).
  2. Creates a new group chat with the deployer + testers (Graph POST /chats, chatType: "group").
  3. Installs the bot into the chat (Graph POST /chats/{id}/installedApps).
  4. Sends the deploy notification via Bot Framework REST API.
  5. 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):

CardTypeTriggerMessage contents
beta-deployBeta deploy workflow runRelease notes, test instructions, tracking issue link
beta-promoted/promote comment on tracking issue”Promoted — all users get this update”
beta-reminderBeta-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

SecretPurpose
ADMIN_PATGitHub PAT with SSO authorization — used for pushing version bumps, creating releases, managing issues
GRAPH_TENANT_IDAzure AD tenant ID (a83ae943-0a50-49cc-83c3-479b7a44b7fb)
GRAPH_CLIENT_IDApp registration client ID (5f54d8fc-fca1-4c78-9894-c267018efadb)
GRAPH_CLIENT_SECRETApp registration client secret (rotate periodically)
TEAMS_BOT_CATALOG_IDTeams 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:

RuleSetting
Pull request requiredYes — but approval is optional
Merge methodSquash and merge (commits combined into one)
Auto-delete branchesYes — branches deleted after merge
Bypass actorsOrganization 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

FilePurpose
deploy/app-registry.jsonMaps apps to solution paths and deploy names
deploy/bump-version.ps1Increments AssemblyVersion in AssemblyInfo.cs
deploy/deploy-to-share.ps1Copies build output to network share, writes version files
deploy/send-teams-notification.ps1Creates Teams group chats and sends bot messages
deploy/teams-bot/manifest.jsonTeams app manifest for PSI Deploy Bot
deploy/sync-registry.ps1Syncs app-registry.json → PSApplications.txt / PSServices.txt
.github/workflows/release.ymlRelease pipeline for all apps (including Pro Update)
.github/workflows/promote-beta.ymlPromote beta to stable (triggered by /promote comment)
.github/workflows/beta-reminder.ymlDaily stale beta check
.github/workflows/build-app.ymlPR build validation
.github/workflows/build-core.ymlCore library builds
.github/workflows/core-impact.ymlDownstream impact validation
.github/workflows/test.ymlxUnit 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-branch
git pull origin main
# Resolve conflicts in your editor
git 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


Last updated: March 2026