Skip to content

CI/CD Integration

df-cli is designed to work in automated pipelines. This guide covers non-interactive authentication, build validation, automated publishing, reproducible dependency resolution, and exit code handling.

Runner Prerequisites

Provider examples target a self-hosted or pre-provisioned Windows runner. Stock hosted images do not include the licensed DataFlex toolchain required by these examples.

Before any pipeline step invokes df-cli, provision the runner with:

  1. A licensed DataFlex toolchain compatible with the workspace.
  2. df-cli installed and available on PATH.
  3. A runner label identifying this provisioned environment. The examples use dataflex; assign that label only to runners with both prerequisites.

Run this verification as the first df-cli step, after checking out the workspace:

df-cli system

Continue only after df-cli system confirms the expected DataFlex toolchain and df-cli installation.


Authentication

Interactive OAuth login is not available in CI. Use token-based authentication instead:

df-cli login --endpoint https://packages.dataflex.dev \
  --access-token "$ACCESS_TOKEN"

Optionally include a refresh token so df-cli can automatically refresh the session when the access token expires:

df-cli login --endpoint https://packages.dataflex.dev \
  --access-token "$ACCESS_TOKEN" \
  --refresh-token "$REFRESH_TOKEN"

Tokens are JWT-based and validated locally for expiration before being stored. On Windows runners, credentials are persisted in Windows Credential Manager.

Generate tokens through the admin panel at packages.dataflex.dev and store them as pipeline secrets.


Build Validation

Check If a Build Is Needed

The --check flag validates whether any files need recompilation without actually producing output:

df-cli build MyProject.sws --target MyApp --check
Exit Code Meaning
0 Build is up to date — no recompilation needed.
-1 Rebuild is needed.
Other Configuration or compilation error.

For CI gating, use the targeted form above. A workspace-wide --check converts the “rebuild needed” result into a fatal status instead of returning -1.

Build with Error Handling

df-cli build MyProject.sws --target MyApp

A non-zero exit code indicates failure. Combine with --verbose for detailed compiler output in build logs:

df-cli build MyProject.sws --target MyApp --verbose

Force Build Past Configuration Errors

If the workspace configuration has non-fatal issues (e.g. a missing optional dependency) but you want the build to proceed anyway:

df-cli build MyProject.sws --force

Without --force, configuration errors abort the build immediately. With it, df-cli skips dependency cache verification and file installation, logs a warning, and continues to build. Dependency paths are still registered so the compiler can find include directories, but no files are copied or hash-checked. This is most useful when combined with a lock file that pins known-good dependency versions.


Reproducible Builds with Lock Files

The .sws.lock file pins every dependency to an exact version. When present, df-cli uses the locked versions instead of resolving the latest compatible release from the repository.

Workflow

  1. A developer runs df-cli package install or df-cli package upgrade locally, which creates or updates MyProject.sws.lock.
  2. The lock file is committed to version control.
  3. In CI, df-cli build reads the lock file and installs the exact same dependency versions.

This ensures that builds are deterministic — a pipeline will never break because a dependency published a new version overnight.

When the Lock File Is Ignored

If a version constraint in the .sws file changes and the locked version no longer satisfies it, the lock is bypassed for that dependency and a new version is fetched. The lock file is updated after successful resolution.


Automated Publishing

To publish a package from CI without interactive prompts: package push requires a prior df-cli login for the target package endpoint. The API upload can fail while df-cli still exits successfully; CI must inspect the emitted Package Push state/log rather than relying on the exit status.

# Verify the provisioned runner and toolchain
df-cli system

# Authenticate (once per pipeline, or cached)
df-cli login --endpoint https://packages.dataflex.dev \
  --access-token "$ACCESS_TOKEN"

# Push without confirmation prompt
df-cli package push MyLibrary.sws my-repository/MyLibrary -y

The -y / --yes flag skips the confirmation dialog that normally displays the license and asks for approval. The full pipeline for a new version would be:

# Verify the provisioned runner and toolchain
df-cli system

# Authenticate
df-cli login --endpoint https://packages.dataflex.dev \
  --access-token "$ACCESS_TOKEN"

# Validate the workspace configuration
df-cli config MyLibrary.sws

# Build to verify compilation succeeds
df-cli build MyLibrary.sws

# Push the package
df-cli package push MyLibrary.sws my-repository/MyLibrary -y

After pushing, the version must still be published through the admin panel at packages.dataflex.dev. See Creating and Publishing Packages for the full workflow.


Machine-Readable Output

The config command supports --json for parsing workspace information programmatically:

df-cli config MyProject.sws --json

This outputs the full workspace configuration as JSON to stdout, including: - Workspace name, version, and license - All configured paths - The complete dependency tree (recursively loaded) - Project list with toolchain info - Repair information (broken dependencies, cache mismatches) - Package metadata

When --json is active, all interactive prompts are disabled and informational log messages are suppressed, making the output safe to pipe into tools like jq:

# Extract the product version
df-cli config MyProject.sws --json | jq -r '.product_version'

# List all direct dependencies
df-cli config MyProject.sws --json | jq '.dependencies | keys[]'

# Check if workspace loaded successfully
df-cli config MyProject.sws --json | jq '.loadedSuccessfully'

Colored Output

df-cli detects whether stdout is a terminal and disables color when output is piped. Some CI systems (GitHub Actions, GitLab CI, Azure Pipelines) support ANSI colors in their log viewers. To force colored output in these environments:

df-cli build MyProject.sws --force-color

Pipeline Examples

Each provider job below assumes the runner was provisioned as described above. It runs df-cli system before any other df-cli command. After every package push, inspect the Package Push state/log in the job output; an API upload failure can still produce a successful df-cli exit status.

GitHub Actions — Build and Validate

jobs:
  build:
    runs-on: [self-hosted, windows, dataflex]
    steps:
      - uses: actions/checkout@v4

      - name: Verify DataFlex toolchain
        run: df-cli system

      - name: Check if build needed
        run: df-cli build MyProject.sws --target MyApp --check
        continue-on-error: true
        id: check

      - name: Build
        if: steps.check.outcome == 'failure'
        run: df-cli build MyProject.sws --verbose --force-color

GitHub Actions — Publish Package

jobs:
  publish:
    runs-on: [self-hosted, windows, dataflex]
    steps:
      - uses: actions/checkout@v4

      - name: Verify DataFlex toolchain
        run: df-cli system

      - name: Authenticate
        run: >
          df-cli login
          --endpoint https://packages.dataflex.dev
          --access-token "${{ secrets.DF_ACCESS_TOKEN }}"

      - name: Build
        run: df-cli build MyLibrary.sws --force-color

      - name: Push package
        run: df-cli package push MyLibrary.sws my-repository/MyLibrary -y

GitHub Actions — Auto-Bump Version and Publish

This example reads the current version from the .sws file, bumps the patch number, commits the change, and pushes the package. It uses jq to modify the JSON in place.

jobs:
  release:
    runs-on: [self-hosted, windows, dataflex]
    steps:
      - uses: actions/checkout@v4

      - name: Verify DataFlex toolchain
        run: df-cli system

      - name: Bump patch version
        shell: bash
        run: |
          # Read current version from the .sws file
          CURRENT=$(jq -r '.package.version' MyLibrary.sws)
          echo "Current version: $CURRENT"

          # Split into major.minor.patch and increment patch
          IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
          NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))"
          echo "New version: $NEW_VERSION"

          # Write back to .sws file
          jq --arg v "$NEW_VERSION" '.package.version = $v' MyLibrary.sws > tmp.sws && mv tmp.sws MyLibrary.sws

      - name: Commit version bump
        run: |
          git config user.name "CI"
          git config user.email "ci@example.com"
          git add MyLibrary.sws
          git commit -m "chore: bump version to $(jq -r '.package.version' MyLibrary.sws)"
          git push

      - name: Authenticate
        run: >
          df-cli login
          --endpoint https://packages.dataflex.dev
          --access-token "${{ secrets.DF_ACCESS_TOKEN }}"

      - name: Build and push
        run: |
          df-cli build MyLibrary.sws --force-color
          df-cli package push MyLibrary.sws my-repository/MyLibrary -y

To bump the minor version instead, replace the increment line:

NEW_VERSION="$MAJOR.$((MINOR + 1)).0"

GitLab CI — Build and Push

build:
  tags:
    - self-hosted
    - windows
    - dataflex
  stage: build
  script:
    - df-cli system
    - df-cli build MyProject.sws --verbose --force-color

publish:
  tags:
    - self-hosted
    - windows
    - dataflex
  stage: deploy
  script:
    - df-cli system
    - df-cli login --endpoint https://packages.dataflex.dev --access-token "$ACCESS_TOKEN"
    - df-cli package push MyLibrary.sws my-repository/MyLibrary -y
  only:
    - tags