@witbor/sdf
v2.0.1
Published
Deploy pipeline for NetSuite SDF projects on SuiteCloud CLI 4.x: files-check, dryrun, deploy, dependency fixing and audit log.
Readme
@witbor/sdf
Deploy pipeline for NetSuite SDF account customization projects, on top of SuiteCloud CLI 4.x.
files-check ──► [dryrun] ──► deploy
git changes mandatory for production,
→ deploy.xml optional elsewhere (--include-preview)
+ adddependencies- Builds
deploy.xmlwith exactly what the branch changed (gitflow: never another branch's work, never wildcards), and declares inmanifest.xmleverything those items reference. - Stops when nothing changed, so a stale
deploy.xmlis never deployed. - Reads SuiteCloud's
--jsonresult instead of its console output: status, errors and warnings per file, the exact list of files/objects that change, the account the result belongs to. - When the account lacks a dependency, says which one, who references it, and whether it is in the repository (someone deleted it from the account?) or not.
- Production gates: preview always runs; clean and pushed working tree; confirmation outside the production branch.
- Optional audit log per day in
logs/deploy_YYYYMMDD.log("log": true; one run id across every process of a run).
Requirements
- Node.js 24+, Oracle JDK 17 or 21 (SuiteCloud CLI requirements).
- SuiteCloud CLI
>=4.1.0 <5, installed once per machine with npm (not pnpm: its postinstall downloads the SDK jar, and a pnpm install can lack the CLI's bundled sdk-core). This package does not install it (optional peer dependency).
Install
Once per machine (accepts the Oracle Free Use Terms):
$env:SUITECLOUD_CLI_ACCEPT_LICENSE = 'true'; npm install -g @oracle/suitecloud-cli@4
# bash: SUITECLOUD_CLI_ACCEPT_LICENSE=true npm install -g @oracle/suitecloud-cli@4In the project:
pnpm add -D @witbor/sdfwtb-sdf uses the CLI installed in the project if there is one, otherwise the npm global one next to
suitecloud on the PATH, and stops with the install command when it finds neither (or a version out of range).
suitecloud.config.js:
const { projectDeploy, fileUpload } = require('@witbor/sdf/hooks');
module.exports = {
defaultProjectFolder: 'src',
commands: {
'project:deploy': projectDeploy,
'file:upload': fileUpload,
},
};package.json scripts:
{
"deploy:help": "wtb-sdf --help",
"deploy": "wtb-sdf deploy",
"deploy:sbx": "wtb-sdf deploy --branch sandbox",
"deploy:prd": "wtb-sdf deploy --branch production",
"deploy-preview": "wtb-sdf preview",
"deploy-preview:sbx": "wtb-sdf preview --branch sandbox",
"deploy-preview:prd": "wtb-sdf preview --branch production"
}.gitignore: logs and temp.
Configuration: wtb-sdf.config.json (optional)
Example with every key at its default: node_modules/@witbor/sdf/wtb-sdf.config.json.example (copy it to the
project root as wtb-sdf.config.json).
{
"eslint": true,
"log": false,
"branches": { "production": "main", "sandbox": "development" }
}| Key | Default | Meaning |
|---|---|---|
| eslint | true | Run the project's own eslint over the project folder before every deploy/upload. Rules and ignores live in the project's eslint.config.js. |
| log | false | Write the audit log to logs/deploy_YYYYMMDD.log (one file per day; every line carries the run id: grep "run <id>"). Off: nothing is written and no logs/ folder is created. |
| branches.production | main | Base branch of --branch production, and the branch production deploys are expected from. |
| branches.sandbox | development | Base branch of --branch sandbox. |
Unknown keys or wrong types are errors.
Commands
wtb-sdf deploy [--branch current|sandbox|production] [--skip-file-check] [--include-preview] [--yes]
wtb-sdf preview [--branch current|sandbox|production] [--skip-file-check] [--yes]
wtb-sdf init
wtb-sdf <command> --helpThrough npm, options go after --: npm run deploy -- --skip-file-check. --yes accepts the deploy.xml
changes of --branch sandbox|production without a terminal (CI); the production deploy confirmation still asks.
What goes into deploy.xml and manifest.xml
| Command | Changes taken | deploy.xml / manifest.xml afterwards |
|---|---|---|
| preview / deploy (current) | uncommitted + untracked + unpushed commits (branch never pushed: commits since main) | restored (work in progress) |
| --branch sandbox | since the branch left development | kept if it passes: commit them |
| --branch production | since the branch left main: what the branch takes to production | kept if it passes: commit them |
- One diff from that base to the working tree: a change committed and then undone, or a file added and
deleted again, is not deployed. The base uses the newest of the local branch and
origin/<branch>; during a merge (conflict being resolved) the commit being merged in counts as already part of the branch. - current and sandbox leave out (and list) files identical to
main: main's work merged into the branch is not the branch's change. Not applied when working onmainitself. Deletions are always reported. - Only
FileCabinetfiles andObjectsXML are deployed. A changed.attributes/x.js.attr.xmldeploysx.js. - Reported, never deployed (SDF cannot or must not do it per branch): deletions and the old name of renames,
folder attributes,
AccountConfiguration/Translations/InstallationPreferences. - References of a deployed object (
[scriptid=...],[/SuiteScripts/...]):- deployed too → nothing to declare;
- in the repository but unchanged in the branch → declared in
manifest.xml, not deployed (deploying it would overwrite what another branch put in the account). The account must already have it; - not in the repository → declared by
project:adddependencies.
- A dependency the account does not have fails the preview with a "Not in the account" list: in the repository (a person must check: deleted from the account? never deployed?) or not (create or import it).
manifest.xmlis rebuilt on every run: the version at the base commit +main's declarations and features that the branch contains + what the branch needs. Dependencies from earlier runs or other branches do not pile up and main's are never dropped. Hand edits tomanifest.xmlin a branch are not kept: make them in the base branch, or use--skip-file-check.- Unresolved merge conflicts stop the run, except in
deploy.xml/manifest.xml: they are regenerated (take either side,git addthem after a passing run). Withmerge=oursfor both files in.gitattributesthose conflicts never happen.
Exit codes: 0 ok or nothing to deploy, 1 failed, 2 invalid arguments, 130 interrupted.
A run killed hard leaves its backup in temp/: the next run restores it when it is on the same branch and
commit, discards it otherwise, and refuses to start while the run that made it is still alive.
Troubleshooting
- "SuiteCloud CLI not found": install it with npm (see Install). A
suitecloudinstalled another way (pnpm global, volta) is named in the message but not used. - "SuiteCloud CLI ... is incomplete" /
Cannot find module '@oracle/suitecloud-sdk-core': the CLI was installed with pnpm. Remove it and install it with npm. ERR_PNPM_IGNORED_BUILDSfor@oracle/suitecloud-cli: the project lists the CLI in its own dependencies. Remove it (pnpm remove @oracle/suitecloud-cli) and use the global one.- "Java is not installed on this machine" while Java is installed: run
java -version. A malformedJAVA_TOOL_OPTIONS(e.g. without the leading-) stops Java from starting, and the CLI reports it this way. - Generic SuiteCloud "Server error" behind an antivirus that intercepts HTTPS: set
JAVA_TOOL_OPTIONS=-Djavax.net.ssl.trustStoreType=Windows-ROOT.
