DevOps and CI/CD for COBOL
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; an upcoming 4th edition is in ballot), which says nothing about build pipelines; against IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026) on z/OS 3.2; against GnuCOBOL 3.2 (28 July 2023, the latest release), which the workflow below installs; and against Zowe 3.x (the 3.6.x line is the long-term-support line at the time of writing). The workflow was checked for syntax with the actionlint tool and parsed as YAML on 2026-10-20; the compile and test commands it runs are the ones used on Testing COBOL Programs, which were compiled and run with GnuCOBOL 3.2. The workflow as a whole was not executed on a GitHub runner. The mainframe pipeline is an outline from the product documentation and is labelled "z/OS; not run here".
A COBOL repository can be built and tested like any other once its programs are in a version control system (Git is the standard on every platform now, including z/OS) and its build is a script rather than a person with a screen. Two pipelines are useful and are often combined: a fast one on a laptop-class runner that compiles with GnuCOBOL and runs the unit tests of the portable code, and a slower one that builds with Enterprise COBOL on z/OS and runs the tests that need the real environment. The first is the focus here because anyone can run it; the second is outlined. Tools to edit and transfer code are on Modern Tooling: VS Code and Zowe, the style rules a review checks are on Coding Standards and Best Practices, and the build options of Enterprise COBOL are on Compiling, Linking and Language Environment.
Where the book is outdated: Taulli’s Modern Mainframe Development (2022, chapter 9) is a consulted overview of Agile and DevOps for the mainframe, of configuration management and of CI/CD, and demonstrates commercial tools of its time. The tool demonstrations are not reproduced here; the pipeline below uses open tools and the documented commands of Zowe CLI, whose current line is 3.x, not the Zowe v1-era commands the book shows.
Repository layout
A small layout keeps the workflow simple:
circulation/
src/ LOANCHK.cob, FINECALC.cob, ... programs
copybooks/ BOOKREC.cpy, MEMBREC.cpy, LOANREC.cpy
tests/ TESTFINE.cob, expected.txt, Makefile
.github/workflows/cobol.yml
Source files are kept as plain text in the repository. On z/OS they live in partitioned data sets in EBCDIC; the tools that move them convert to and from UTF-8 (see the Git section below), so the repository holds UTF-8 and the line ending of the repository, not of the host.
A GitHub Actions workflow
The workflow installs GnuCOBOL 3.2 from the release tarball (and caches the installation so that later runs skip the build), compiles every program with -Wall and the IBM dialect, runs the test harness and keeps its output. It uses only the GITHUB_PATH and GITHUB_ENV files, the official actions/checkout, actions/cache and actions/upload-artifact actions, and the shell steps that are listed.
name: COBOL CI
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
env:
GNUCOBOL_VERSION: "3.2"
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Check out the sources
uses: actions/checkout@v7
- name: Restore the GnuCOBOL install
id: cache-gnucobol
uses: actions/cache@v6
with:
path: ~/gnucobol
key: gnucobol-${{ env.GNUCOBOL_VERSION }}-${{ runner.os }}
- name: Build GnuCOBOL from the release tarball
if: steps.cache-gnucobol.outputs.cache-hit != 'true'
run: |
sudo apt-get update
sudo apt-get install -y build-essential libgmp-dev libdb-dev libncurses-dev
wget -q "https://ftp.gnu.org/gnu/gnucobol/gnucobol-${GNUCOBOL_VERSION}.tar.xz"
tar xf "gnucobol-${GNUCOBOL_VERSION}.tar.xz"
cd "gnucobol-${GNUCOBOL_VERSION}"
./configure --prefix="$HOME/gnucobol" --with-db
make -j"$(nproc)"
make install
- name: Put cobc on the path
run: |
echo "$HOME/gnucobol/bin" >> "$GITHUB_PATH"
echo "LD_LIBRARY_PATH=$HOME/gnucobol/lib" >> "$GITHUB_ENV"
- name: Compile every program with warnings on
run: |
cobc --version
mkdir -p build
for f in src/*.cob; do
cobc -x -Wall -std=ibm -I copybooks -o "build/$(basename "${f%.cob}")" "$f"
done
- name: Run the unit tests
run: make -C tests test
- name: Keep the test output
if: always()
uses: actions/upload-artifact@v7
with:
name: test-output
path: tests/actual.txt
How it works, step by step:
-
The
onblock runs the job for each push tomainand for each pull request, which is the check that protectsmain. -
permissions: contents: readgives the job a read-only token; nothing here needs more. -
The cache key contains the GnuCOBOL version and the operating system, so changing
GNUCOBOL_VERSIONrebuilds once and then caches again. -
The build step is the usual
configure,make,make installinto a directory in the home of the runner; no administrator install of GnuCOBOL is needed. The libraries it needs (libgmp-dev,libdb-dev,libncurses-dev) are installed first. -
The compile step uses
-Wallso that every warning is visible in the log, and-std=ibmso that a program that does not fit the IBM dialect fails early instead of on the mainframe. Add-Werrorto make warnings fail the build. -
make -C tests testis theMakefileof the testing page; its exit status is the number of failed cases, so a failed case fails the job. -
The last step runs even after a failure (
if: always()) so the output can be downloaded.
The workflow ran through actionlint without a finding and through a YAML parser; the compile, link and test commands in it were run in a GnuCOBOL 3.2 container (on a layout with all files in one directory) and passed. The GnuCOBOL tarball name used in the download step is that of the release on the GNU FTP site.
What a CI run for COBOL should check
| Check | How |
|---|---|
It compiles |
|
No new warnings |
|
Unit tests pass |
The driver’s exit status. |
Coverage does not fall |
|
Copybook users still compile |
A change to a copybook triggers the compile of every program that copies it, which the loop above does for the whole repository. |
Secrets are not committed |
A secret scanner step; mainframe credentials never go in the repository. |
What GnuCOBOL cannot check is Enterprise COBOL-specific behaviour: EXEC SQL, EXEC CICS, IBM-only intrinsic functions, the exact numeric behaviour of TRUNC and NUMPROC. Those programs are built on z/OS by the second pipeline.
The mainframe pipeline in outline
A pipeline for z/OS has the same stages with different tools. The outline below is from the vendor and Zowe documentation and is not run here.
-
Source in Git. The sources, copybooks and JCL are in a Git repository; the code is stored as UTF-8 and converted to the host code page when it is transferred. A file-tagging attribute or a
.gitattributesmapping tells the tools which files are text and which code page they use. -
Dependency-based build. A build tool (IBM Dependency Based Build, DBB, is IBM’s) computes which programs depend on a changed copybook or subprogram and rebuilds only those: this is the answer to a copybook shared by hundreds of programs. The tool’s scripts run the compile and link-edit steps that JCL for COBOL Programmers shows as JCL.
-
Tests. The unit tests run in a batch region with zUnit or Test4z (see the testing page); Galasa runs integration tests that drive the transaction and the job.
-
Deploy with Zowe CLI. The CI server runs Zowe CLI commands:
zowe zos-files upload file-to-data-setto put a load module or JCL into a data set,zowe zos-jobs submit data-setto run a job andzowe zos-jobs view all-spool-contentto read the output into the CI log. Connection details come from a team configuration file kept outside the repository (see the tooling page), and the credentials live in the CI server’s secret store, not in the code. -
Promotion. The same load module moves from the test environment to production under the change-management process of the installation, rather than being rebuilt, so that what was tested is what runs.
Branching, review and copybooks
-
Trunk-based or short branches. Mainframe teams used to lock a member while a developer changed it; Git replaces the lock with branches and merges. Keep branches short, because merging two edits of the same long paragraph is difficult.
-
Pull requests as the review. The review checklist of the coding standards page applies to every pull request, and the CI result is a required check.
-
A shared copybook is an interface. A record layout used by many programs (
LOANRECis read by the validation, the update and the report programs) is changed only in a pull request that lists the programs affected, that rebuilds all of them in CI, and that comes with a note for downstream teams when the file format changes. Adding a field at the end of aFILLERleaves the record length unchanged and is the safest change; inserting a field in the middle changes every offset and needs a migration of the files. -
Change management. On a production mainframe the pipeline hands over to a change record: the build artifact, the test report and the approval are attached to it, and the deploy is carried out by the process of the installation.
-
Versioning the contract. Version a copybook or an interface when its meaning changes, and keep the old one until every consumer has moved.
Common mistakes
-
Treating the GnuCOBOL pipeline as proof for z/OS. It proves portable logic; the real build still has to run on the target compiler.
-
Rebuilding in production what was tested. Promote the tested module.
-
Credentials in the repository or the workflow. Use the CI secret store; the examples in this section use no real host or user names.
-
Not caching the compiler. Building GnuCOBOL on every run takes minutes that the cache removes.
-
Letting the formatting differ between the mainframe editor and Git. A conversion that changes trailing spaces or line ends shows every line as modified; fix the conversion once and test it.