Acumatica does not ship a CI/CD product, but a customization project is just a zip of DLLs, ASPX pages, and metadata that the platform can import through its API — which means every piece of a real pipeline (build, package, deploy, smoke test) is scriptable. GitHub Actions is a reasonable home for that pipeline if your source lives on GitHub: no infrastructure to run, and the runner can build the customization project and push it to a target instance over HTTP.
What the pipeline actually builds
An Acumatica customization is typically developed as a Visual Studio class library (for code extensions — PXGraphExtension, DAC extensions) plus loose ASPX/ASCX pages for UI, packaged into a .zip customization project. The build step compiles the class library against the Acumatica SDK assemblies (referenced as NuGet packages or a local SDK folder), and the package step assembles the zip in the layout Acumatica's import expects — a ClassFiles folder for compiled DLLs, a Files folder for pages, and the manifest.
name: Build and Deploy Acumatica Customization
on:
push:
branches: [main]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- name: Restore and build extension library
run: |
nuget restore CustomizationExt.sln
msbuild CustomizationExt.sln /p:Configuration=Release
- name: Package customization zip
run: powershell ./scripts/package-customization.ps1
- uses: actions/upload-artifact@v4
with:
name: customization-package
path: dist/*.zip
deploy-staging:
needs: build
runs-on: windows-latest
steps:
- uses: actions/download-artifact@v4
with: { name: customization-package }
- name: Publish to staging via Acumatica REST/import API
env:
ACUMATICA_URL: ${{ secrets.STAGING_URL }}
ACUMATICA_USER: ${{ secrets.STAGING_USER }}
ACUMATICA_PASS: ${{ secrets.STAGING_PASS }}
run: powershell ./scripts/publish-customization.ps1
Why MSBuild and not a container
Acumatica's SDK assemblies and the class library extension model target .NET Framework in most current versions, which is why the workflow above runs on windows-latest rather than a Linux container — MSBuild against the Acumatica assemblies needs the full .NET Framework toolchain that ships with the Windows runner image. This is the single biggest surprise for teams used to Linux-first CI: you cannot lift-and-shift a typical dotnet-core pipeline here without checking which Acumatica version and edition you are targeting.
The publish step authenticates against a real Acumatica instance. Store the URL, user, and password (or better, an API-only integration user with a scoped role) as GitHub Actions repository or environment secrets, and gate the production deploy job behind a required reviewer using GitHub Environments — a customization publish is not something you want firing unattended on every merge to main.
Publishing through the API, not the screen
Acumatica exposes customization project import/publish through its contract-based REST API and through a SOAP-based screen-based API used historically for the Customization Projects (SM204505) screen. Either can be scripted from PowerShell: log in, POST the package to the import endpoint, then trigger a publish. Because publishing recompiles the site and can take several minutes, the workflow should poll for completion rather than assume the call is synchronous, and should capture the publish log so a failure surfaces in the Actions run instead of silently leaving staging on the old build.
Smoke tests after publish
A publish that "succeeds" can still leave the site broken if a screen fails to compile cleanly. After the publish step, run a lightweight smoke test — log in via the REST API, open two or three screens that exercise the customization, and check for a 200 response and absence of the generic error page markup. This does not replace real regression testing, but it catches the class of failure ("the whole site is down") that would otherwise wait for a human to notice.
Staging first, production gated
Keep production as a separate job that only runs after staging succeeds and, ideally, after a manual approval step. GitHub Environments support required reviewers natively, so the same workflow file can enforce "someone looked at staging" before the production publish job is allowed to run — no separate approval tooling needed.
Wrapping up
The mechanics are unusual compared to a typical web app pipeline — Windows runners, MSBuild, a proprietary publish API — but the shape is the same CI/CD discipline: build once, package once, deploy the same artifact through increasingly gated environments, and smoke-test after every publish. Once the scripts exist they are reusable across every customization project you maintain, which is where the investment pays off.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.