Skip to main content

GitHub Actions CI/CD for NetSuite

Automate your NetSuite SDF deployments using GitHub Actions. This guide covers complete workflow templates, use cases, and step-by-step setup instructions.


Why Use GitHub Actions for NetSuite?

When to Use GitHub Actions

Use CaseRecommended?Reason
Team of 2+ developers✅ YesEnsures consistent deployments
Multiple environments✅ YesAutomates promotion path
Frequent releases✅ YesReduces manual effort
Compliance requirements✅ YesProvides audit trail
Solo developer, infrequent changes⚠️ OptionalManual may be sufficient

CI/CD Flow Overview


Prerequisites

Before setting up GitHub Actions, ensure you have:

1. NetSuite Account Configuration

# Required token-based authentication credentials
NETSUITE_ACCOUNT_ID # Your NetSuite account ID (e.g., TSTDRV1234567)
NETSUITE_TOKEN_ID # Token ID from NetSuite
NETSUITE_TOKEN_SECRET # Token Secret from NetSuite

2. Generate Token-Based Authentication (TBA) Credentials

3. Add GitHub Repository Secrets

Navigate to your repository: Settings → Secrets and variables → Actions

Secret NameDescriptionExample
NS_ACCOUNT_ID_SANDBOXSandbox account IDTSTDRV1234567
NS_TOKEN_ID_SANDBOXSandbox token IDabc123...
NS_TOKEN_SECRET_SANDBOXSandbox token secretxyz789...
NS_ACCOUNT_ID_PRODProduction account ID1234567
NS_TOKEN_ID_PRODProduction token IDdef456...
NS_TOKEN_SECRET_PRODProduction token secretuvw321...

Workflow Templates

Template 1: Validation on Pull Request

Validates SDF project on every pull request to catch issues early.

# .github/workflows/validate-sdf.yml
name: Validate SDF Project

on:
pull_request:
branches: [main, develop]
paths:
- 'src/**'
- 'Objects/**'
- 'FileCabinet/**'
- 'manifest.xml'

jobs:
validate:
name: Validate SDF Project
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'

- name: Install SuiteCloud CLI
run: npm install -g @oracle/suitecloud-cli

- name: Validate project structure
run: |
echo "📋 Checking project structure..."
if [ ! -f "manifest.xml" ]; then
echo "❌ manifest.xml not found"
exit 1
fi
if [ ! -d "Objects" ]; then
echo "❌ Objects directory not found"
exit 1
fi
echo "✅ Project structure valid"

- name: Validate SDF project
run: |
echo "🔍 Running SDF validation..."
suitecloud project:validate
echo "✅ SDF validation passed"

- name: Comment on PR
if: success()
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '✅ **SDF Validation Passed**\n\nProject structure and objects validated successfully.'
})

Template 2: Deploy to Sandbox on Merge

Automatically deploys to sandbox when code is merged to develop branch.

# .github/workflows/deploy-sandbox.yml
name: Deploy to Sandbox

on:
push:
branches: [develop]
paths:
- 'src/**'
- 'Objects/**'
- 'FileCabinet/**'

env:
NODE_VERSION: '18'

jobs:
deploy-sandbox:
name: Deploy to Dev Sandbox
runs-on: ubuntu-latest
environment: sandbox

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

- name: Install SuiteCloud CLI
run: npm install -g @oracle/suitecloud-cli

- name: Configure SuiteCloud account
run: |
suitecloud account:savetoken \
--account ${{ secrets.NS_ACCOUNT_ID_SANDBOX }} \
--authid "GitHubActions" \
--tokenid ${{ secrets.NS_TOKEN_ID_SANDBOX }} \
--tokensecret ${{ secrets.NS_TOKEN_SECRET_SANDBOX }}

- name: Validate before deploy
run: suitecloud project:validate

- name: Deploy to Sandbox
run: |
echo "🚀 Deploying to Sandbox..."
suitecloud project:deploy --accountspecificvalues WARNING
echo "✅ Deployment complete"

- name: Deployment Summary
run: |
echo "## 🎉 Sandbox Deployment Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Detail | Value |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Environment | Sandbox |" >> $GITHUB_STEP_SUMMARY
echo "| Branch | ${{ github.ref_name }} |" >> $GITHUB_STEP_SUMMARY
echo "| Commit | ${{ github.sha }} |" >> $GITHUB_STEP_SUMMARY
echo "| Deployed by | ${{ github.actor }} |" >> $GITHUB_STEP_SUMMARY

Template 3: Production Deployment with Approval

Deploys to production with manual approval gate and backup.

# .github/workflows/deploy-production.yml
name: Deploy to Production

on:
push:
branches: [main]
paths:
- 'src/**'
- 'Objects/**'
- 'FileCabinet/**'
workflow_dispatch:
inputs:
deploy_type:
description: 'Deployment type'
required: true
default: 'full'
type: choice
options:
- full
- partial

env:
NODE_VERSION: '18'

jobs:
# Job 1: Validate
validate:
name: Validate Project
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

- name: Install and Validate
run: |
npm install -g @oracle/suitecloud-cli
suitecloud project:validate

# Job 2: Approval Gate
approval:
name: Await Approval
needs: validate
runs-on: ubuntu-latest
environment: production

steps:
- name: Approval checkpoint
run: echo "✅ Deployment approved by ${{ github.actor }}"

# Job 3: Backup
backup:
name: Backup Production
needs: approval
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

- name: Install SuiteCloud CLI
run: npm install -g @oracle/suitecloud-cli

- name: Configure Production Account
run: |
suitecloud account:savetoken \
--account ${{ secrets.NS_ACCOUNT_ID_PROD }} \
--authid "GitHubActions" \
--tokenid ${{ secrets.NS_TOKEN_ID_PROD }} \
--tokensecret ${{ secrets.NS_TOKEN_SECRET_PROD }}

- name: Backup current production objects
run: |
echo "📦 Creating backup..."
mkdir -p backup
suitecloud object:import --destinationfolder backup --scriptid "customscript_*" || true
echo "✅ Backup created"

- name: Upload backup artifact
uses: actions/upload-artifact@v4
with:
name: production-backup-${{ github.run_number }}
path: backup/
retention-days: 30

# Job 4: Deploy
deploy:
name: Deploy to Production
needs: backup
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

- name: Install SuiteCloud CLI
run: npm install -g @oracle/suitecloud-cli

- name: Configure Production Account
run: |
suitecloud account:savetoken \
--account ${{ secrets.NS_ACCOUNT_ID_PROD }} \
--authid "GitHubActions" \
--tokenid ${{ secrets.NS_TOKEN_ID_PROD }} \
--tokensecret ${{ secrets.NS_TOKEN_SECRET_PROD }}

- name: Deploy to Production
run: |
echo "🚀 Deploying to Production..."
suitecloud project:deploy --accountspecificvalues WARNING
echo "✅ Production deployment complete"

- name: Create Git tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "release-${{ github.run_number }}" -m "Production release #${{ github.run_number }}"
git push origin "release-${{ github.run_number }}"

- name: Production Summary
run: |
echo "## 🎉 Production Deployment Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Detail | Value |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Environment | Production |" >> $GITHUB_STEP_SUMMARY
echo "| Release Tag | release-${{ github.run_number }} |" >> $GITHUB_STEP_SUMMARY
echo "| Commit | ${{ github.sha }} |" >> $GITHUB_STEP_SUMMARY
echo "| Approved by | ${{ github.actor }} |" >> $GITHUB_STEP_SUMMARY
echo "| Backup | production-backup-${{ github.run_number }} |" >> $GITHUB_STEP_SUMMARY

Template 4: Multi-Environment Pipeline

Complete pipeline for Dev → QA → UAT → Production flow.

# .github/workflows/multi-env-pipeline.yml
name: Multi-Environment Pipeline

on:
push:
branches:
- develop
- 'release/*'
- main
workflow_dispatch:
inputs:
target_env:
description: 'Target environment'
required: true
type: choice
options:
- dev
- qa
- uat
- production

env:
NODE_VERSION: '18'

jobs:
# Determine target environment
setup:
name: Determine Environment
runs-on: ubuntu-latest
outputs:
environment: ${{ steps.set-env.outputs.environment }}
account_id_secret: ${{ steps.set-env.outputs.account_id_secret }}
token_id_secret: ${{ steps.set-env.outputs.token_id_secret }}
token_secret_secret: ${{ steps.set-env.outputs.token_secret_secret }}

steps:
- name: Set environment based on branch
id: set-env
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
ENV="${{ github.event.inputs.target_env }}"
elif [ "${{ github.ref }}" == "refs/heads/develop" ]; then
ENV="dev"
elif [[ "${{ github.ref }}" == refs/heads/release/* ]]; then
ENV="qa"
elif [ "${{ github.ref }}" == "refs/heads/main" ]; then
ENV="production"
else
ENV="dev"
fi

echo "environment=$ENV" >> $GITHUB_OUTPUT

case $ENV in
dev)
echo "account_id_secret=NS_ACCOUNT_ID_DEV" >> $GITHUB_OUTPUT
echo "token_id_secret=NS_TOKEN_ID_DEV" >> $GITHUB_OUTPUT
echo "token_secret_secret=NS_TOKEN_SECRET_DEV" >> $GITHUB_OUTPUT
;;
qa)
echo "account_id_secret=NS_ACCOUNT_ID_QA" >> $GITHUB_OUTPUT
echo "token_id_secret=NS_TOKEN_ID_QA" >> $GITHUB_OUTPUT
echo "token_secret_secret=NS_TOKEN_SECRET_QA" >> $GITHUB_OUTPUT
;;
uat)
echo "account_id_secret=NS_ACCOUNT_ID_UAT" >> $GITHUB_OUTPUT
echo "token_id_secret=NS_TOKEN_ID_UAT" >> $GITHUB_OUTPUT
echo "token_secret_secret=NS_TOKEN_SECRET_UAT" >> $GITHUB_OUTPUT
;;
production)
echo "account_id_secret=NS_ACCOUNT_ID_PROD" >> $GITHUB_OUTPUT
echo "token_id_secret=NS_TOKEN_ID_PROD" >> $GITHUB_OUTPUT
echo "token_secret_secret=NS_TOKEN_SECRET_PROD" >> $GITHUB_OUTPUT
;;
esac

validate:
name: Validate
needs: setup
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

- name: Validate
run: |
npm install -g @oracle/suitecloud-cli
suitecloud project:validate

deploy:
name: Deploy to ${{ needs.setup.outputs.environment }}
needs: [setup, validate]
runs-on: ubuntu-latest
environment: ${{ needs.setup.outputs.environment }}

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

- name: Install SuiteCloud CLI
run: npm install -g @oracle/suitecloud-cli

- name: Configure account
env:
ACCOUNT_ID: ${{ secrets[needs.setup.outputs.account_id_secret] }}
TOKEN_ID: ${{ secrets[needs.setup.outputs.token_id_secret] }}
TOKEN_SECRET: ${{ secrets[needs.setup.outputs.token_secret_secret] }}
run: |
suitecloud account:savetoken \
--account "$ACCOUNT_ID" \
--authid "GitHubActions" \
--tokenid "$TOKEN_ID" \
--tokensecret "$TOKEN_SECRET"

- name: Deploy
run: |
echo "🚀 Deploying to ${{ needs.setup.outputs.environment }}..."
suitecloud project:deploy --accountspecificvalues WARNING
echo "✅ Deployment complete"

- name: Summary
run: |
echo "## Deployment to ${{ needs.setup.outputs.environment }}" >> $GITHUB_STEP_SUMMARY
echo "✅ Successfully deployed" >> $GITHUB_STEP_SUMMARY

Template 5: Partial/Selective Deployment

Deploy specific scripts or objects only.

# .github/workflows/deploy-partial.yml
name: Partial Deployment

on:
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
type: choice
options:
- sandbox
- production
script_ids:
description: 'Script IDs to deploy (comma-separated)'
required: true
type: string
default: 'customscript_example'

jobs:
deploy-partial:
name: Partial Deploy to ${{ github.event.inputs.environment }}
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment }}

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: '18'

- name: Install SuiteCloud CLI
run: npm install -g @oracle/suitecloud-cli

- name: Configure account
run: |
if [ "${{ github.event.inputs.environment }}" == "production" ]; then
suitecloud account:savetoken \
--account ${{ secrets.NS_ACCOUNT_ID_PROD }} \
--authid "GitHubActions" \
--tokenid ${{ secrets.NS_TOKEN_ID_PROD }} \
--tokensecret ${{ secrets.NS_TOKEN_SECRET_PROD }}
else
suitecloud account:savetoken \
--account ${{ secrets.NS_ACCOUNT_ID_SANDBOX }} \
--authid "GitHubActions" \
--tokenid ${{ secrets.NS_TOKEN_ID_SANDBOX }} \
--tokensecret ${{ secrets.NS_TOKEN_SECRET_SANDBOX }}
fi

- name: Deploy specific objects
run: |
echo "🚀 Deploying: ${{ github.event.inputs.script_ids }}"
suitecloud object:deploy \
--scriptid "${{ github.event.inputs.script_ids }}" \
--accountspecificvalues WARNING
echo "✅ Partial deployment complete"

- name: Summary
run: |
echo "## Partial Deployment" >> $GITHUB_STEP_SUMMARY
echo "| Detail | Value |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Environment | ${{ github.event.inputs.environment }} |" >> $GITHUB_STEP_SUMMARY
echo "| Objects | ${{ github.event.inputs.script_ids }} |" >> $GITHUB_STEP_SUMMARY

Which Template Should I Use?

ScenarioRecommended TemplateKey Features
Solo developer, simple projectTemplate 1 + ManualPR validation only
Small team, single sandboxTemplate 1 + 2Auto sandbox deploy
Medium team, approval neededTemplate 1 + 2 + 3Full pipeline with approval
Enterprise, multi-environmentTemplate 4Complete Dev→QA→UAT→Prod
Hotfix deploymentTemplate 5Selective object deploy
Compliance requirementsTemplate 3 + 4Approval gates + backup
Need help deciding between CI/CD and manual deployment?

See the Deployment Decision Guide for detailed scenarios comparing CI/CD Pipeline vs Single File Upload vs Manual Deploy.


Step-by-Step Setup Guide

Step 1: Repository Structure

Ensure your repository follows this structure:

your-sdf-project/
├── .github/
│ └── workflows/
│ ├── validate-sdf.yml
│ ├── deploy-sandbox.yml
│ └── deploy-production.yml
├── FileCabinet/
│ └── SuiteScripts/
│ └── your_scripts.js
├── Objects/
│ └── customscript_*.xml
├── src/
│ └── (TypeScript source if using)
├── manifest.xml
└── project.json

Step 2: Create Workflow Files

Step 3: Configure GitHub Environments

For production protection, create environments:

  1. Go to Settings → Environments
  2. Create sandbox environment (no protection needed)
  3. Create production environment with:
    • ✅ Required reviewers (add team leads)
    • ✅ Wait timer (optional, e.g., 5 minutes)
    • ✅ Limit to specific branches: main

Complete Pipeline Flow Diagram