Skip to content

Add MAAF GitHub Actions (install-ms-cli, ms-app-pack, ms-app-deploy)#11

Open
jbujula wants to merge 15 commits into
mainfrom
jay
Open

Add MAAF GitHub Actions (install-ms-cli, ms-app-pack, ms-app-deploy)#11
jbujula wants to merge 15 commits into
mainfrom
jay

Conversation

@jbujula

@jbujula jbujula commented Jun 19, 2026

Copy link
Copy Markdown

What this PR adds

Tooling for building, deploying, and releasing Microsoft Managed Apps (MAAF) from CI, all under github-actions/:

Three GitHub Actions

Action What it does
install-ms-cli Installs @microsoft/managed-apps-cli (binary: ms) on the runner. Supports the public npm registry and private Azure DevOps Artifacts feeds.
ms-app-pack Runs ms app pack — runs the app's build command and stages the packed artifact.
ms-app-deploy Runs ms app deploy — uploads and deploys to the target Power Platform environment. Supports --artifact, --commit, and CLI-internal pack modes based on repoType in ms.config.json.

Plus release automation (.github/workflows/release-github-actions.yml), a hand-maintained CHANGELOG.md, and tool-agnostic agent skills (github-actions/agent-skills/, indexed by github-actions/AGENTS.md): architecture, a release guide, and a BYOB setup walkthrough.

Usage

- uses: actions/checkout@v4
- uses: actions/setup-node@v4
  with: { node-version: '24' }
- run: npm install
  working-directory: my-app
- uses: microsoft/Managed-Apps/github-actions/install-ms-cli@v1
- uses: microsoft/Managed-Apps/github-actions/ms-app-deploy@v1
  with:
    working-directory: 'my-app'
    app-id:        ${{ secrets.PP_APP_ID }}
    client-secret: ${{ secrets.PP_CLIENT_SECRET }}
    tenant-id:     ${{ secrets.PP_TENANT_ID }}

How it's built

  • TypeScript sources in src/actions/<name>/index.ts, compiled with gulp + bundled per-action by esbuild (target: node24) into dist/actions/<name>/index.js.
  • dist/ is committed (referenced by each action.yml's main:), so it must be rebuilt when sources change: npm install && npm run build.
  • Action runtime: node24.

Design notes

  • Shared constants live in src/shared/env.ts (a side-effect-free module). The actions import the install env-var names from there rather than from install-ms-cli/index, so ms-app-pack/ms-app-deploy bundles never embed the installer's load-time entry point. Verified: the installer's code is absent from the pack/deploy bundles.
  • Service Principal auth is all-or-nothing. SPN mode activates only when app-id + client-secret + tenant-id are all supplied; partial input warns instead of silently using a stale identity. Secrets are masked via core.setSecret.
  • Private registry auth (install-ms-cli) writes a 0600 temp .npmrc and uses Azure DevOps _password (base64 PAT) vs. bearer _authToken based on a strict hostname match (*.pkgs.dev.azure.com), preventing PAT exfiltration via a spoofed registry URL.

Versioning & releases

github-actions/package.json is the single source of truth for the version.

Two tags are maintained:

Tag Role
v1 (moving major) The alias consumers pin (@v1). Re-pointed to the latest v1.x.y on each release.
v1.x.y (immutable) A fixed, never-moves build — rollback point and exact-pin option.

Release workflow (release-github-actions.yml) runs on pushes to main that touch github-actions/**. It:

  1. Rebuilds dist/ and fails if the committed bundle is stale (drift guard).
  2. Reads the full version from package.json and fails if that tag already exists (signal to bump).
  3. Creates the immutable vX.Y.Z tag and force-moves the vX alias to the merge commit.

Bump version in package.json to control the release: patch (bug fix), minor (new optional input/output/action), major (breaking — produces a fresh vN alias and leaves older majors untouched).

Changelog (CHANGELOG.md) is hand-maintained — updated in the same PR that bumps the version (promote [Unreleased] → the new version). The release workflow does not modify it.

Release guide (github-actions/agent-skills/release-guide.md) is a plain runbook for cutting a release PR — branch from latest origin/main, bump package.json, update the changelog, rebuild dist/, open the PR. Follow it by hand or hand it to any coding-agent CLI.

Note: the v1 / v1.0.0 tags currently point at this branch's head for testing. After merge, the release workflow cuts v1.0.1 from main and moves v1 to it.

Testing

Validated end-to-end from an external consumer workflow against the @jay ref: the action resolves and runs on the node24 runtime (no node20 deprecation warning for these actions), and install-ms-cli installs the CLI successfully.

Architecture

See github-actions/agent-skills/architecture.md for the end-to-end MAAF flow (setup / local dev / CI loops), the ms-app-deploy sequence with RP rejection points, and the release/versioning flow — with Mermaid diagrams. All agent skills live under github-actions/agent-skills/ (indexed by github-actions/AGENTS.md) and are linked from the github-actions/README.md.

🤖 Generated with Claude Code

Adds the actions under github-actions/ (TypeScript sources + committed
esbuild bundles in dist/) so they can be consumed at the @jay ref from
an external workflow, e.g.:

  uses: microsoft/Managed-Apps/github-actions/install-ms-cli@jay
  uses: microsoft/Managed-Apps/github-actions/ms-app-deploy@jay

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jbujula and others added 9 commits June 22, 2026 10:58
On pushes to main that touch github-actions/, rebuilds the esbuild
bundles, fails if committed dist/ is stale, then cuts an immutable
vX.Y.Z tag and moves the vX alias. MAJOR.MINOR come from
package.json; PATCH auto-increments from existing tags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tag the exact vMAJOR.MINOR.PATCH from github-actions/package.json
(single source of truth) instead of auto-incrementing patch from tags.
Fail the run if that tag already exists, signalling a needed version bump.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First release cut from main will be v1.0.1, avoiding the existing
manual v1.0.0 tag on the jay test branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hand-maintained changelog (Keep a Changelog format) scoped to the
actions. Updated in the same PR that bumps package.json version; the
release workflow does not modify it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Colocates the /create-pr release-PR skill with the component. Branches
from origin/main, bumps version, updates CHANGELOG, rebuilds dist, and
opens a PR. Loads when Claude is rooted at github-actions/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Documents the end-to-end activity flow (setup / local dev / CI loops),
the ms-app-deploy sequence with RP rejection points, and the release
versioning flow. Captures real test learnings: AllowExternalArtifact-
Deployment gate, repoType none BYOB, preprod cloud, SPN RP block, and
app-scope sharing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The real RP error and environment setting is AllowExternalArtifactDeployment
(singular "Artifact"); README and the deploy action.yml comment had an extra
"s". Corrected to match the actual setting name.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Version-controls the corrected BYOB setup walkthrough (SKILL.md + BAP
role-grant .http asset) alongside the actions it documents. Refs point to
microsoft/Managed-Apps/github-actions/@v1; setting name and node version
corrected. Marked user-invocable for /maaf-byob-setup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems these files get generated by after compiling ts files. Do we need to check-in these files?

#
# ms-app-deploy/action.yml
#
# Deploys a MAAF code app via `ms app deploy`. Three deploy modes are

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not use the term MAAF in customer facing docs

# - Tenant admin has enabled `AllowExternalArtifactDeployment` on the
# target environment. Without it, deploy returns a policy error from
# the RP. Wiki:
# microsoft.ghe.com/bic/Managed-Ops-Internal/blob/main/docs/devops/MAAF/allowExternalArtifactDeployment-powershell.md

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an internal Wiki and should not be referenced in customer facing code

Comment thread github-actions/ms-app-deploy/action.yml Outdated
# action's auth inputs (`app-id`, `client-secret`, `tenant-id`) are wired
# to the CLI's documented SPN env vars, but the server will reject them
# until either:
# (a) the RP enables SPN for MAAF operations, OR

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal detail & team name

# tenant-id: ${{ secrets.PP_SP_TENANT_ID }}
#
name: 'ms-app-deploy'
description: 'Power Platform MS App Deploy — deploys a MAAF code app for the given commit'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not "Power Platform". Change this, and other references to "Microsoft managed apps"

// is loaded.

/** Set to 'true' after install-ms-cli successfully installs the CLI. */
export const MsInstalledEnvVarName = 'POWERPLATFORMTOOLS_MSINSTALLED';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

managed apps are not an offering under Power Platform.
We should change this to avoid confusion

- **`repoType: none` needs no git remote.** `ms app create --repo none` scaffolds
from a template and writes `ms.config.json`; deploy builds locally and uploads
the artifact. No `git init`/remote required.
- **Cloud target** is set via `MS_CLI_CLOUD_INSTANCE` (tested `preprod`). The

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove "preprod"

- **Sharing:** for `repoType: none` apps (no platform-managed repo),
`ms app share <id> --access edit` grants contributor access at the **app scope**
rather than the repository scope.
- **Debug env override:** `MS_CLI_MAAF_DEBUG_ENVIRONMENT_ID` was used in testing to

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This env var should not be surfaced to customers


```pwsh
$Env:MS_CLI_MAAF_DEBUG_ENVIRONMENT_ID = '<environment-guid>'
$Env:MS_CLI_CLOUD_INSTANCE = 'preprod'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this env var should not be surfaced to customers

@@ -0,0 +1,95 @@
---
name: create-pr

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is a mix of internal development and customer facing artifacts.
I'd suggest we keep the internal development separate and not in the public facing repo - otherwise internal processes, comments and iterations are all visible in git history

jbujula and others added 5 commits June 23, 2026 14:49
Relocates architecture / release / BYOB-setup content from
github-actions/.claude/skills/ to github-actions/docs/ as plain Markdown,
strips Claude skill frontmatter, and neutralizes Claude-specific tool
references (AskUserQuestion/TodoWrite) so any coding-agent CLI can use
them. Updates the README to a Docs section linking all three.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Power Apps RP now accepts Service Principal identities for MAAF
operations, so the ms-app-deploy "ServicePrincipalNotSupportedForMaaf-
Operations / RP currently rejects SPN" warning is no longer valid.
Removes that core.warning, rewrites the deploy action.yml auth notes,
and updates the architecture + BYOB docs accordingly. Rebuilds dist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
actions/checkout@v5 and actions/setup-node@v5 target node24; @v4 targets
node20 and triggers the runner deprecation warning. Updates all example
workflows in the README, BYOB doc, and action.yml comments, and bumps the
README quick-start node-version to 24. No action source change (our actions
already run on node24), so no dist rebuild.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Renames github-actions/docs to github-actions/agent-skills so the folder
name signals it holds agent-usable skills (tool-agnostic). Adds
github-actions/AGENTS.md (cross-tool standard) as an index so any coding
agent auto-discovers the skills. Updates README links.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The ms CLI emits NDJSON progress lines followed by a multi-line result
object. The greedy first-{ to last-} parse spliced them into one invalid
blob and threw "Unexpected non-whitespace character after JSON" — even
though the deploy succeeded. Now extract top-level JSON objects (string-
aware brace scan) and return the last valid one.

Also fix output mapping: read appId (the CLI's field, not id), source
environment-id from ms.config.json, set commit-sha from the result, and
add an app-play-uri output. Rebuilds dist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants