Skip to content

Onboarded MS Cli actions#698

Merged
jbujula merged 17 commits into
mainfrom
users/jbujula/maaf-cli-actions
Jun 16, 2026
Merged

Onboarded MS Cli actions#698
jbujula merged 17 commits into
mainfrom
users/jbujula/maaf-cli-actions

Conversation

@jbujula

@jbujula jbujula commented May 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Onboards three new GitHub Actions wrapping the MAAF ms CLI (@microsoft/managed-apps-cli) for code-app build and deploy workflows:

  • install-ms-cli — installs the ms CLI from public npm (or an internal Azure DevOps feed for preview builds)
  • ms-app-pack — runs ms app pack to build and pack the app into a deployable artifact
  • ms-app-deploy — runs ms app deploy, with optional --artifact <zip> support for byoBuild flows

ms-app-create is intentionally not in this PR; it's tracked separately on branch onboard-maaf-create-app and will land in a follow-up.

Requires @microsoft/managed-apps-cli@>=0.7.0 for the artifact-path input (deploy --artifact flag).

Action reference

install-ms-cli

Installs @microsoft/managed-apps-cli (binary: ms) on the runner. Zero inputs required for the public npm install path; overridable for internal Azure DevOps feeds.

- uses: microsoft/powerplatform-actions/install-ms-cli@v1

Internal feed (preview/dev builds):

- uses: microsoft/powerplatform-actions/install-ms-cli@v1
  with:
    version: '0.7.0-dev.20260601.1'
    registry-url: 'https://pkgs.dev.azure.com/msazure/OneAgile/_packaging/PowerPlatform-Managed-Host-consumption/npm/registry/'
    registry-auth-token: ${{ secrets.AZURE_DEVOPS_PAT }}

Inputs: version (default latest), registry-url, registry-auth-token, npm-package-name (default @microsoft/managed-apps-cli).
Outputs: cli-version.

ms-app-pack

Runs ms app pack — executes the buildCommand from ms.config.json (default npm run build) and copies the build output to .ms/packed/apps/<appId>/client/. Requires a prior npm ci so the build can resolve dependencies.

- uses: microsoft/powerplatform-actions/ms-app-pack@v1
  with:
    working-directory: 'apps/my-maaf-app'
    app-id:        ${{ secrets.PP_SP_CLIENT_ID }}
    client-secret: ${{ secrets.PP_SP_CLIENT_SECRET }}
    tenant-id:     ${{ secrets.PP_SP_TENANT_ID }}

Inputs: working-directory, SPN auth triple (app-id, client-secret, tenant-id).

ms-app-deploy

Runs ms app deploy. Three modes are selected automatically from inputs and the repoType in ms.config.json:

Mode Trigger CLI call
Artifact upload repoType:'none' + artifact-path input ms app deploy --artifact <zip>
CLI-packed deploy repoType:'none', no artifact-path ms app deploy (CLI packs internally)
Git-backed deploy repoType:'native' or 'github' ms app deploy --commit <sha>

Mutual-exclusion guards mirror the CLI's UsageError semantics:

  • commit-sha + artifact-path together → action error
  • artifact-path on a non-'none' repoType → action error
# byoBuild — CLI packs internally
- uses: microsoft/powerplatform-actions/ms-app-deploy@v1
  with:
    working-directory: 'apps/my-maaf-app'
    app-id:        ${{ secrets.PP_SP_CLIENT_ID }}
    client-secret: ${{ secrets.PP_SP_CLIENT_SECRET }}
    tenant-id:     ${{ secrets.PP_SP_TENANT_ID }}
# byoBuild — pre-built artifact
- uses: microsoft/powerplatform-actions/ms-app-deploy@v1
  with:
    working-directory: 'apps/my-maaf-app'
    artifact-path: '../app.zip'
    app-id:        ${{ secrets.PP_SP_CLIENT_ID }}
    client-secret: ${{ secrets.PP_SP_CLIENT_SECRET }}
    tenant-id:     ${{ secrets.PP_SP_TENANT_ID }}

Inputs: working-directory, commit-sha (defaults to GITHUB_SHA), artifact-path, cloud (default test), app-name, SPN auth triple.
Outputs: app-id, environment-id, commit-sha.

End-to-end demo workflow

name: Deploy MAAF App

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - name: Setup Node 22
      uses: actions/setup-node@v4
      with:
        node-version: '22'

    - name: Install app dependencies
      working-directory: apps/my-maaf-app
      run: npm install

    - name: Install ms CLI
      uses: microsoft/powerplatform-actions/install-ms-cli@v1

    - name: Pack MAAF App
      uses: microsoft/powerplatform-actions/ms-app-pack@v1
      with:
        working-directory: 'apps/my-maaf-app'
        app-id:        ${{ secrets.PP_SP_CLIENT_ID }}
        client-secret: ${{ secrets.PP_SP_CLIENT_SECRET }}
        tenant-id:     ${{ secrets.PP_SP_TENANT_ID }}

    - name: Deploy MAAF App
      uses: microsoft/powerplatform-actions/ms-app-deploy@v1
      with:
        working-directory: 'apps/my-maaf-app'
        app-id:        ${{ secrets.PP_SP_CLIENT_ID }}
        client-secret: ${{ secrets.PP_SP_CLIENT_SECRET }}
        tenant-id:     ${{ secrets.PP_SP_TENANT_ID }}

Prerequisites for consumers

  1. MAAF app must already exist with ms.config.json committed in the repo (created via ms app create --repo none).
  2. Service Principal added as Application User on the target Power Platform environment with appropriate roles.
  3. Tenant admin has enabled AllowExternalArtifactsDeployment on the target environment for byoBuild / artifact deploy flows. Setup wiki: microsoft.ghe.com/bic/Managed-Ops-Internal/blob/main/docs/devops/MAAF/allowExternalArtifactDeployment-powershell.md
  4. SPN permissions: the SPN needs Repositories.MicrosoftApps.Deploy.Write (and Repositories.MicrosoftApps.Build.Write for git-backed flows). This is currently the most common failure mode; tracked separately with the RP team.

Test plan

  • Public npm install path proven end-to-end (CI log shows @microsoft/managed-apps-cli@latest resolving to 0.7.0 and installing cleanly).
  • Internal Azure DevOps feed install path proven with PAT-based auth.
  • ms-app-pack runs npm run build via the configured buildCommand and emits the packed artifact.
  • ms-app-deploy issues the correct CLI invocation per mode (commit / no-flags / artifact).
  • Mutual-exclusion validation (commit-sha + artifact-path → action error before any CLI call).
  • CodeQL alert on install-ms-cli regex cleared (strict URL.hostname match).
  • End-to-end deploy succeeds once SPN has the required Repositories.MicrosoftApps.Deploy.Write permission.
  • Add ms-app-create action in a follow-up PR.

Follow-ups

  • ms-app-create action on branch onboard-maaf-create-app — provisions a new app via ms app create --repo none. Separate PR.
  • Public doc describing the action family, demo workflow, and prerequisites — planned after this lands.
  • Drop ms-app-pack step once the CLI's ms app deploy (no --artifact) handles internal pack reliably for repoType:none apps in production builds; today we keep both for explicit control.

jbujula and others added 5 commits May 6, 2026 14:40
Update src/pacPackageInfo.json to the latest stable PAC CLI release on
NuGet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces two new GitHub Actions wrapping the @microsoft/apps-cli (binary: ms)
for Microsoft Apps Authoring Framework (MAAF) code-app workflows:

- setup-ms: installs the ms CLI on the runner. Supports the internal Azure
  DevOps feed (PowerPlatform-Managed-Host-consumption) since the public
  @microsoft/apps-cli on npm is currently a placeholder.

- ms-app-deploy: orchestrates the dogfood publish flow for the current
  commit — triggers ms app build, polls ms app build-status until terminal,
  then runs ms app deploy. Reads appId and environmentId from ms.config.json.

Both actions target node24 to match the recent runtime migration.

Known constraint: the Power Apps RP currently rejects service principal
identities for MAAF operations (ServicePrincipalNotSupportedForMaafOperations).
The action exposes SPN inputs against the CLI's documented env-var contract
but will fail at the RP gate until SPN is enabled or federated auth lands.
See action.yml comments for the tracking ask with the MAAF CLI team.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renames the install action to match the repository's existing
`actions-install` precedent and remove ambiguity around the bare
"ms" name. The companion `ms-app-deploy` action keeps its name
since it mirrors the underlying `ms app deploy` CLI command.

No behavior change. Updates:
- Folder: setup-ms/ → install-ms-cli/
- Folder: src/actions/setup-ms/ → src/actions/install-ms-cli/
- Folder: dist/actions/setup-ms/ → dist/actions/install-ms-cli/
- action.yml `name:` field, description, and inline example
- ms-app-deploy import path and prereq error message
- Group label and tmp-dir prefix in the install action

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Azure DevOps Artifacts npm feeds use HTTP basic auth with a base64-encoded
PAT in the `_password` field. The `_authToken` field expects an OAuth bearer
token, which a PAT is not — passing a PAT there returns
"npm error code E401 Incorrect or missing password".

Detect the ADO host pattern (pkgs.dev.azure.com) and emit username + base64
`_password` + email instead of `_authToken`. Non-ADO feeds continue to use
`_authToken` as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread src/actions/install-ms-cli/index.ts Fixed
jbujula and others added 12 commits May 14, 2026 14:33
The ms-app-deploy bundle was built before the setup-ms → install-ms-cli
rename completed cleanly, so it still embedded the old core.startGroup
label "setup-ms:". The label is bundled at build time because
ms-app-deploy imports MsInstalledEnvVarName from install-ms-cli, which
also triggers install-ms-cli's top-level IIFE on require.

Clean-rebuild (gulp clean + compile + dist) regenerates the bundle
against the current install-ms-cli source. No behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The MAAF Git Inner Loop spec (§7.1) makes builds JIT — `ms app deploy`
auto-triggers a server-side build for a commit if one doesn't exist. The
older `ms app build` verb is deprecated, and the CLI itself now rejects it:

  "`ms app build-app` is deprecated and the `--environment-id` flag has
   been removed. The replacement flow is `ms app create` (scaffolds the
   app and writes power.config.json) followed by `ms app deploy`."

Changes:
- Remove runBuild() and pollBuildStatus() — only ms app deploy is called.
- Drop unused inputs: build-timeout-seconds, build-poll-interval-seconds,
  skip-build. Drop unused output: build-operation-id.
- Look for power.config.json first (current CLI), fall back to ms.config.json.
- Refresh action.yml example: install-ms-cli needs no inputs by default now
  that @microsoft/apps-cli@0.4.0 is on public npm (published 2026-05-14).
- Update install-ms-cli docs to lead with the public-npm zero-input pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CLI's deprecated `ms app build-app` error message references
`power.config.json`, but the active config filename in current
@microsoft/apps-cli releases (0.3.x, 0.4.x) is still `ms.config.json`
(see microsoft-apps-common AppConfig.ts). Only check ms.config.json
and update the action.yml comments accordingly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switches the action to the MAAF artifact / escape-hatch deploy path
(spec §7.5). Calls `ms app pack` to build and pack a deployable artifact
locally, then `ms app deploy` to upload and deploy it. This bypasses the
server-side build and the Repositories.MicrosoftApps.Build.Write permission
requirement that blocked the previous git-build flow.

Matches the working local flow:
  npm ci → ms app pack → ms app deploy

Caller is expected to run `npm ci` (or `npm install`) in the working
directory before the action — `ms app pack` runs `npm run build` and
needs node_modules to be present.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Separates pack from deploy so each is a distinct workflow step. Matches
the repo's existing one-verb-per-action convention (export-solution,
import-solution, etc.).

When the upcoming CLI PR ships `ms app deploy --artifact` and folds pack
into deploy for repoType:'none' apps, consumers will be able to drop the
`- uses: ms-app-pack` step from their workflow with no other changes.

ms-app-pack:
  - Invokes `ms app pack --non-interactive --json`
  - Inputs: working-directory (only). No auth or cloud needed for pack.
  - Requires install-ms-cli to have run earlier.
  - Requires a prior `npm ci` so `npm run build` can resolve node_modules.

ms-app-deploy:
  - Drops the internal runPack() call.
  - Description updated; example workflow shows the explicit pack step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`ms app pack` does not make RP calls, but the CLI auto-activates Service
Principal auth in CI mode (CI=true is set by GitHub Actions runners) and
validates that MS_CLI_SP_CLIENT_ID / MS_CLI_SP_CLIENT_SECRET /
MS_CLI_SP_TENANT_ID are set at startup. Without them, pack exits with:

  "Service principal environment variables MS_CLI_SP_CLIENT_ID,
   MS_CLI_SP_CLIENT_SECRET, and MS_CLI_SP_TENANT_ID must be set"

Add the same app-id / client-secret / tenant-id inputs to ms-app-pack as
ms-app-deploy has, and forward them to the child process's env.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The MAAF CLI was republished on public npm under a new name:
  @microsoft/apps-cli         → @microsoft/managed-apps-cli

Source files (install-ms-cli/action.yml, src/actions/install-ms-cli/index.ts)
were already updated to reference the new package name, but the dist
bundles still embedded the old name and are out of sync.

Also picks up the ms-app-deploy repoType branching: reads `repoType` from
ms.config.json and skips `--commit` when repoType is 'none' (escape-hatch
apps that pack and upload an artifact, with no git binding on the server).

No behavior change beyond the package-name rename and the existing
repoType-aware deploy logic that was already in the source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@microsoft/managed-apps-cli@0.7.0 (published 2026-05-23) adds the
`--artifact <zip>` flag to `ms app deploy`, completing the byoBuild
escape-hatch story per MAAF Git Inner Loop spec §7.5.

Adds an optional `artifact-path` input to ms-app-deploy. When provided,
the action passes `--artifact <resolved-path>` to the deploy call and
uploads the pre-built zip directly — useful for workflows that want to
build with their own toolchain (e.g. cached npm install + npm run build
+ zip) and skip the CLI's internal pack.

The action validates the same mutual-exclusion rules the CLI enforces:
  - commit-sha + artifact-path together → UsageError
  - artifact-path on a non-'none' repoType → UsageError

Three deploy modes are now selected automatically from inputs and the
repoType in ms.config.json:

  1. repoType:'none' + artifact-path  → `ms app deploy --artifact <zip>`
  2. repoType:'none' + no artifact    → `ms app deploy` (CLI packs internally)
  3. repoType:'native' or 'github'    → `ms app deploy --commit <sha>`

Also documents the AllowExternalArtifactsDeployment tenant-admin prereq
in the action.yml header (with the wiki link to the PowerShell guide).
The Power Apps RP rejects external artifact deployments unless the
setting is on for the target environment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #698 review (github-advanced-security[bot]) flagged the regex
  /pkgs\.dev\.azure\.com/i.test(registryUrl)
as a missing-anchor pattern: it matches anywhere in the URL string, so
an attacker-controlled value like
  https://attacker.com/pkgs.dev.azure.com/registry/
would also pass and trigger the Azure DevOps basic-auth code path —
base64-encoding the user's PAT into an .npmrc that points npm at the
attacker's host. That's a token-exfiltration vector if the registry-url
input ever flows from untrusted source.

Replace the regex with strict hostname checking:
  - parse the URL via new URL(...)
  - compare hostname.toLowerCase() against 'pkgs.dev.azure.com'
  - accept legitimate subdomains via host.endsWith('.pkgs.dev.azure.com')
  - return false on URL parse error (fall through to default _authToken
    path, never expose the basic-auth flow)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jbujula jbujula changed the title Users/jbujula/maaf cli actions Onboarded MS Cli actions Jun 11, 2026
@jbujula jbujula enabled auto-merge (squash) June 11, 2026 23:21
@jbujula jbujula merged commit 0e44beb into main Jun 16, 2026
5 checks passed
@jbujula jbujula deleted the users/jbujula/maaf-cli-actions branch June 16, 2026 22:31
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