Skip to main content

The 5-Step Deploy Pipeline

AcuOps uses a CI/CD pipeline to deploy Acumatica customization projects safely and repeatably. Every deploy follows 5 steps.

Pipeline Overview

Step 1: Validate → Step 2: Package → Step 3: Import → Step 4: Publish → Step 5: Verify

Step 1: Validate

The validate-project.py script checks the project XML for 15+ known issues:

  • Missing IsActive() methods on cache/graph extensions
  • Invalid <Table> elements (cause NullReferenceException on cloud)
  • Incorrect XML element names (<SqlScript> vs <Sql>)
  • References to non-public types (e.g., ARCustomerClass)
  • Missing namespace declarations
python3 scripts/validate-project.py Customization/_project/project.xml

Step 2: Package

The project directory is packaged into a .zip file that the Customization API accepts:

cd Customization/_project && zip -r ../../package.zip . && cd ../..

Step 3: Import

The package is uploaded via the Customization API:

POST /CustomizationApi/Import
Content-Type: application/json

{
"projectName": "YourProjectName",
"projectDescription": "Deployed via CI/CD",
"projectLevel": 0,
"isReplaceIfExists": true,
"projectContentBase64": "<base64-encoded-zip>"
}
info

The URL path is case-sensitive: /CustomizationApi/Import (capital I). All Customization API endpoints require POST.

Step 4: Publish

Publishing compiles the C# code and applies database changes:

POST /CustomizationApi/publishBegin
{
"projectNames": ["YourProject", "ISVPackage1", "ISVPackage2"],
"isMergeConflictsForced": true,
"tenantMode": "Current"
}

Co-publishing is critical — the projectNames array must include ALL active customization projects. Otherwise, omitted projects are deactivated during publish.

The pipeline polls publishEnd until completion:

POST /CustomizationApi/publishEnd
{}

Returns HTTP 200 (still running) or 400 with isCompleted: true / isFailed: true.

warning

Publishing restarts the Acumatica app pool. All active sessions are disconnected. Schedule deploys during maintenance windows.

Step 5: Verify

Post-publish validation confirms the deploy succeeded:

  1. Schema checkGET /entity/default/24.200.001/{Entity}/$adHocSchema verifies custom fields appear
  2. Entity queryGET /entity/default/24.200.001/{Entity}?$top=1 confirms entities are accessible
  3. Test suite — 66 Playwright tests covering all customized entities

GitHub Actions Workflow

The pipeline runs automatically on push to main:

on:
push:
branches: [main]
paths: ['Customization/**']

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Acumatica
run: bash scripts/deploy.sh
env:
ACUMATICA_URL: ${{ secrets.ACUMATICA_PROD_URL }}
ACUMATICA_USERNAME: ${{ secrets.ACUMATICA_PROD_USERNAME }}
ACUMATICA_PASSWORD: ${{ secrets.ACUMATICA_PROD_PASSWORD }}
ACUMATICA_TENANT: ${{ secrets.ACUMATICA_PROD_TENANT }}

Multi-Project Deploys

For environments with multiple customization projects:

bash scripts/deploy.sh \
--extra-import StudioBPORelations:path/to/StudioBPORelations/_project

This imports the extra project alongside the primary project and co-publishes both.

Incremental Deploy Strategy

For complex changes, deploy incrementally:

  1. Step A: Deploy graph extensions only (no ASPX changes) — verify API smoke test passes
  2. Step B: Deploy ASPX screen changes in a separate commit — verify screen renders in browser

This isolates C# compilation failures from UI rendering failures.