Skip to content

Publish from CI

Goal: ship a new version of your app to the marketplace from a GitHub Actions (or any) CI run on every push to a release branch, without human intervention.

Prerequisites:

  • A locally working orion publish flow.
  • A release branch convention (e.g. push to release/* triggers publish).
  • An OAuth client allowed to publish from headless contexts. The standard orion login flow uses OAuth2 PKCE with an interactive browser callback, which doesn't work in CI. The pragmatic workaround is to capture an existing token (access + refresh) on your dev machine and inject it into CI as a secret. This works but is fragile — tokens rotate and the secret has to be rotated with them. If your tenant administrator can issue a service-account credential for you, that's a cleaner long-term option; check with them.

Steps

  1. Capture a token locally. Run orion login on your dev machine, then read the saved token from your platform's Conf directory. On macOS this is ~/Library/Preferences/orion-cli-nodejs/config.json. The CLI nests credentials under an auth key — auth.accessToken, auth.refreshToken, auth.expiresAt, and auth.scope. Copy all four values.

  2. Add the credentials as CI secrets. In GitHub Actions: repo Settings → Secrets and variables → Actions → New repository secret. Add ORION_ACCESS_TOKEN, ORION_REFRESH_TOKEN, ORION_TOKEN_EXPIRES_AT, and ORION_TOKEN_SCOPE. expiresAt is stored as epoch milliseconds (a number); copy it verbatim.

  3. Reconstitute the token at the start of the workflow. The CLI reads from the Conf directory, so your CI job must write a config.json to the right path before running any orion command. The credentials must be nested under the auth key — a flat top-level shape will be silently ignored by the Conf schema. On a Linux runner that's ~/.config/orion-cli-nodejs/:

    yaml
    - name: Restore Orion CLI credentials
      run: |
        mkdir -p ~/.config/orion-cli-nodejs
        cat > ~/.config/orion-cli-nodejs/config.json <<EOF
        {
          "auth": {
            "accessToken": "${{ secrets.ORION_ACCESS_TOKEN }}",
            "refreshToken": "${{ secrets.ORION_REFRESH_TOKEN }}",
            "expiresAt": ${{ secrets.ORION_TOKEN_EXPIRES_AT }},
            "scope": "${{ secrets.ORION_TOKEN_SCOPE }}"
          }
        }
        EOF
  4. Bump the version in orion-app.json (semver). The marketplace rejects re-uploads of the same version. If your manifest version is wired to package.json, use npm version patch --no-git-tag-version. Otherwise, a jq one-liner works:

    bash
    jq '.version = "1.2.3"' orion-app.json > orion-app.json.tmp && mv orion-app.json.tmp orion-app.json
  5. Run the build and publish. orion publish runs npm run build for you by default, so a single command is enough. Use -c, --changelog to skip the interactive prompt:

    bash
    orion publish --changelog "$(git log -1 --pretty=%B)"

    If your CI job already ran the build in a prior step (e.g. to share the bundle with a test job), pass --no-build to skip the redundant rebuild. Note: --no-build requires a dist/.orion-build-stamp written by an earlier orion publish run in the same workflow; the CLI refuses to publish if your source has moved on since then. The simplest CI pattern is orion publish --dry-run (writes the stamp + validates) followed by orion publish --no-build (reuses it):

    bash
    npm ci
    orion publish --dry-run                                                # writes dist/.orion-build-stamp
    orion publish --no-build --changelog "$(git log -1 --pretty=%B)"       # reuses it
  6. Use --dry-run on PR builds. A separate workflow that runs on pull requests can use orion publish --dry-run to verify the upload would succeed without consuming a marketplace version slot. Dry-run still authenticates, so the token-restoration step is still required.

Caveats

  • Tokens rotate. If CI fails with Auth failed (401), regenerate locally and update the secrets. There is no programmatic refresh path that bypasses the original PKCE flow.
  • Tokens are tied to a single developer account. If that developer leaves the team, every CI workflow using their token breaks.
  • Long-term, ask your tenant admin about service-account credentials — they're the right fit for headless workflows.

Verify

A clean run prints Uploaded for review! followed by the new version's status (e.g. submitted or pending_review). The app becomes live in the marketplace only after a reviewer approves it.

Documents @orion-ehr/cli v0.0.15 — released under the MIT License.