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:

  1. The on block runs the job for each push to main and for each pull request, which is the check that protects main.

  2. permissions: contents: read gives the job a read-only token; nothing here needs more.

  3. The cache key contains the GnuCOBOL version and the operating system, so changing GNUCOBOL_VERSION rebuilds once and then caches again.

  4. The build step is the usual configure, make, make install into 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.

  5. The compile step uses -Wall so that every warning is visible in the log, and -std=ibm so that a program that does not fit the IBM dialect fails early instead of on the mainframe. Add -Werror to make warnings fail the build.

  6. make -C tests test is the Makefile of the testing page; its exit status is the number of failed cases, so a failed case fails the job.

  7. 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

cobc -x -Wall -std=ibm for every program; a copybook error appears here.

No new warnings

-Werror, or compare the warning count with the previous run.

Unit tests pass

The driver’s exit status.

Coverage does not fall

--coverage with gcov, described on Debugging and Troubleshooting; the report can be uploaded as an artifact.

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.

flowchart LR A["Git push or pull request"] --> B["CI server, for example Jenkins"] B --> C["Zowe CLI: upload changed sources"] C --> D["Dependency based build on z/OS: compile, link-edit"] D --> E["Unit tests: zUnit or Test4z"] E --> F["Integration tests: Galasa"] F --> G["Package and deploy to the next environment"] G --> H["Zowe CLI: submit the job and read the spool"]
  1. 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 .gitattributes mapping tells the tools which files are text and which code page they use.

  2. 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.

  3. 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.

  4. Deploy with Zowe CLI. The CI server runs Zowe CLI commands: zowe zos-files upload file-to-data-set to put a load module or JCL into a data set, zowe zos-jobs submit data-set to run a job and zowe zos-jobs view all-spool-content to 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.

  5. 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 (LOANREC is 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 a FILLER leaves 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.