Fluent CI/CD Pipelines
SkillWeb & browsingThis 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.
No other account needed.
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):
- Set sys_property
glide.oauth.inbound.client.credential.grant_type.enabled=true. - Application Registry entry of type "OAuth API endpoint for external clients", Public Client = false, Grant type includes Client Credentials.
- 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 misconfiguredworking-directorymakes 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_statusto confirm scope/SDK pin/keys.ts state,snow_fluent_buildwithfrozen_keys=truefor the PR gate,snow_fluent_installfor 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