Fluent CI/CD Pipelines

SkillWeb & browsing

This skill should be used when the user asks to "set up ci for fluent", "github actions for servicenow", "deploy fluent from a pipeline", "fluent pull request checks", "automate now-sdk", or reports "keys.ts out of date" — anything about running the ServiceNow SDK (now-sdk) headless in CI/CD.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Fluent CI/CD Pipelines skill

What this skill tells your AI

The instructions your AI receives, as published by serac-labs/serac in packages/skills/fluent-ci-cd/SKILL.md and read by ahel’s review.

How to run a ServiceNow Fluent (SDK 4.x, now-sdk CLI) project through version control and CI: what to commit, the PR check that catches stale keys.ts, headless auth, a minimal GitHub Actions workflow, and the hard line between CI deploys and production promotion. For the official reference, run snow_fluent_explain with topic ci-integration (offline now-sdk explain ci-integration).

Where these tools run: the snow_fluent_* tools are local-only — they execute the ServiceNow SDK (now-sdk) on the developer's machine, so they only exist when the MCP server runs locally over stdio. Over a hosted HTTP transport (e.g. a web chat) they are not available, so do not call them; drive the same flow through git + CI instead: commit the project files, trigger the build/deploy pipeline, and follow the workflow runs.

1. Repo discipline

The .gitignore that snow_fluent_init scaffolds is already correct — keep it as-is:

.DS_Store
.now/
dist/
node_modules/
target/
*.tsbuildinfo
.jest_cache

dist/ and .now/ are build outputs; never commit them.

src/fluent/generated/keys.ts MUST be committed. It is the registry mapping every Now.ID['...'] to a sys_id — the record-identity source of truth. It is regenerated by every build. If a teammate's CI or machine builds without your committed keys, records get NEW sys_ids and install creates duplicates on the instance.

❌ Add keys.ts to .gitignore ("it's generated, so ignore it")
   → duplicate records on the next install from another machine
✅ Commit keys.ts on every change, enforce with --frozenKeys in PR checks (section 2)

One app per repo is the simplest layout and what these examples assume. The SDK itself doesn't mandate a layout — projects can be arranged freely as long as now.config.json is set up correctly. now.config.json binds the project to one scope (scope/scopeId/name; optionally tsconfigPath) and contains no instance connection info, so it is safe to commit.

2. The PR check: build with frozen keys

Every pull request should run:

npm ci
npx now-sdk build --frozenKeys

--frozenKeys makes the build FAIL if compiling the Fluent code would change keys.ts. The exact failure message:

Keys file is out-of-date. To update it, run the build again without frozen keys.

What that failure means: a developer added or renamed a Now.ID and forgot to commit the regenerated keys.ts. The fix is on the PR author's machine, not in CI:

now-sdk build      # regenerates keys.ts
git add src/fluent/generated/keys.ts && git commit

Locally, the same gate is snow_fluent_build with frozen_keys=true. Add error_on_conflict=true (--errorOnConflict) on brownfield projects so a record that exists both as Fluent code and as XML in metadata/ fails the build instead of silently shipping the XML version.

ES5 note: Fluent DSL files (*.now.ts) are modern TypeScript — const, arrow functions, template literals are all correct there, and CI compiles them with the SDK's own toolchain. The ES5-only rule applies ONLY to script content that executes on the instance's Rhino engine, i.e. the string you put in a script: property (Business Rule bodies, Script Includes, etc.). A --frozenKeys build will not catch ES6 inside a script: string; review those by hand or with the code-review skill.

3. Headless auth (env vars, no keychain)

now-sdk auth --add is interactive (inquirer prompts, no --username/--password flags) — it cannot run in CI. Instead set env vars, which "take precedence over stored credentials" and bypass the OS keychain entirely:

# Always required to enable CI mode:
SN_SDK_NODE_ENV=SN_SDK_CI_INSTALL

# Option A — basic auth (default if SN_SDK_AUTH_TYPE unset):
SN_SDK_AUTH_TYPE=basic
SN_SDK_INSTANCE_URL=https://yourtest.service-now.com
SN_SDK_USER=ci.user
SN_SDK_USER_PWD=********

# Option B — OAuth client_credentials (SDK 4.7.0+, token fetched per run from ${SN_SDK_INSTANCE_URL}/oauth_token.do):
SN_SDK_AUTH_TYPE=oauth
SN_SDK_INSTANCE_URL=https://yourtest.service-now.com
SN_SDK_OAUTH_CLIENT_ID=...
SN_SDK_OAUTH_CLIENT_SECRET=...

Instance-side prerequisites for Option B (do these once on the target instance):

  1. Set sys_property glide.oauth.inbound.client.credential.grant_type.enabled = true.
  2. Application Registry entry of type "OAuth API endpoint for external clients", Public Client = false, Grant type includes Client Credentials.
  3. Map an OAuth Application User that has install roles (typically admin) AND Identity Type = Human — Machine identities are blocked during the SDK's session-token handshake.
❌ Run `now-sdk auth --add` in a CI step          → hangs on interactive prompts
❌ OAuth app user with Identity Type = Machine     → install fails at the CSRF/session step
✅ SN_SDK_NODE_ENV=SN_SDK_CI_INSTALL + SN_SDK_* secrets from the CI secret store

4. Minimal GitHub Actions workflow

There is no official GitHub Action for the Fluent SDK (the marketplace "ServiceNow CI/CD" actions are the classic App-Repo flow). Official guidance is plain now-sdk steps. Node 20+ is required (engines: node >=20.18.0).

name: fluent-ci
on:
  pull_request:
  push:
    branches: [main]

jobs:
  verify:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - name: Verify keys.ts is up to date
        run: npx now-sdk build --frozenKeys

  deploy-test:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    env:
      SN_SDK_NODE_ENV: SN_SDK_CI_INSTALL
      SN_SDK_AUTH_TYPE: oauth
      SN_SDK_INSTANCE_URL: ${{ secrets.SN_TEST_INSTANCE_URL }}
      SN_SDK_OAUTH_CLIENT_ID: ${{ secrets.SN_TEST_OAUTH_CLIENT_ID }}
      SN_SDK_OAUTH_CLIENT_SECRET: ${{ secrets.SN_TEST_OAUTH_CLIENT_SECRET }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx now-sdk build --frozenKeys
      - name: Install to TEST instance
        run: npx now-sdk install

Tuning the install step: --demoData defaults to TRUE (pass nothing to keep it; relevant when your app ships demo records), --skip-flow-activation disables the automatic flow publish that install does since SDK 4.5.

5. Production: NEVER install from CI

Official SDK guidance, verbatim: "Do not use now-sdk install from CI to deploy to production instances." The promotion path is git plus the Application Repository: publish the app from the test instance to the App Repo and install it in production from there — that is where change management and rollback live.

Why this is a hard rule: now-sdk install bypasses update sets ("Installing via now-sdk does not generate changes in update sets as of now") and "there is no rollback context created for app installed via now-sdk". The only SDK-side undo is --reinstall (uninstall + fresh install — destructive).

❌ main-branch job that runs now-sdk install against prod
✅ CI installs to a shared TEST instance → publish from that instance to the
   Application Repository → install in test/prod via the App Repo (change
   management + rollback live there)

6. Exit-code and output gotchas

  • Fluent compile errors → exit 1 with ERROR: Build failed due to errors. Good, fails the job.
  • Missing/unfindable package.json[now-sdk] ERROR: Could not find package.json... but exit code 0. A misconfigured working-directory makes the build step "pass" while building nothing. Guard it:
      - name: Build
        run: |
          npx now-sdk build --frozenKeys
          test -d dist/app   # fail the job if nothing was actually built
  • There is no JSON output mode anywhere in the CLI; output is human log lines prefixed [now-sdk]. Grep stdout/stderr if you need to assert on messages.
  • Locally, mirror the pipeline before pushing: snow_fluent_status to confirm scope/SDK pin/keys.ts state, snow_fluent_build with frozen_keys=true for the PR gate, snow_fluent_install for the test-instance deploy.

Signals

GitHub stars
78
Forks
26
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
fluent-ci-cd
Source
github.com/serac-labs/serac