Skip to content

Creating and Publishing Packages

This guide walks through the full process of turning a DataFlex workspace into a distributable package, from initial setup to publishing on packages.dataflex.dev.

Overview

The publishing pipeline has four steps:

  1. Configure your workspace .sws file with package metadata.
  2. Pack the workspace into a distributable archive.
  3. Push the archive to the repository.
  4. Publish the pushed version through the admin panel.

Step 1: Configure the Workspace

The package section of your .sws file tells df-cli what to include in the archive, how to version it, and under what license it is distributed. See the Workspace Configuration Format for the full schema.

Minimal Package Configuration

{
  "workspaceName": "MyLibrary",
  "df": 25.0,
  "description": "A helpful utility library for DataFlex applications",
  "paths": {
    "configFile": "Programs/Config.ws",
    "appSrc": ["AppSrc"],
    "programs": ["Programs"],
    "bitmap": ["Bitmaps"]
  },
  "package": {
    "version": "1.0.0",
    "license": "MIT"
  }
}

Required Fields

Before you can pack, the following must be set:

Field Location Description
workspaceName Top level Becomes the package name. Must be set and non-empty.
description Top level A human-readable description. Must differ from the workspace name.
df Top level The DataFlex version this package targets (e.g. 25.0).
package.version package A valid semver version string (e.g. "1.0.0").
package.license package A valid SPDX license expression (e.g. "MIT", "Apache-2.0").

Optional Package Fields

Field Description
package.icon Path to a package icon image. Must exist on disk if specified. Automatically included in the archive.
package.licenseFile Path to a license text file. Automatically included in the archive. Required when using a custom license (LicenseRef-*).
package.includeFiles Additional files or glob patterns to include in the archive beyond the defaults.
package.excludeFiles Glob patterns to exclude from the archive. Applied after all includes.
package.install Files that should be copied into consumer workspaces when they install this package.
package.htmlIncludes Scripts and stylesheets to inject into consumer index.html files.

Step 2: What Goes Into the Archive

When you run df-cli package pack, the archive is assembled from three layers of file selection.

Default Includes (Automatic)

Files from your appSrc and bitmap directories are included automatically with the following patterns:

From appSrc: - Source files: *.pkg, *.wo, *.vw, *.dg, *.sl, *.bp, *.rv, *.dd, *.mn, *.inc, *.src, *.pkd - Config files: config/*classlist.xml, config/*classlist.deploy.xml, config/*templates.xml, config/*.tpl - Data objects: config/*.dbo, config/*.dfo, config/*.ico - Data files: *.json, *.xml, *.sql, *.txt

From bitmap: - All files: *.*

These are optional — missing files or empty directories are silently skipped.

Explicit Includes (includeFiles)

Add files or directories that fall outside the defaults:

"includeFiles": [
  "AppHtml/views/custom-view.html",
  "Data/SeedData/*.xml",
  "Docs/API-Reference.pdf"
]

Glob wildcards (*, ?) are supported.

Excludes (excludeFiles)

Remove files that were matched by the default or explicit includes:

"excludeFiles": [
  "AppSrc/internal-tools/*",
  "Data/SeedData/test-data.xml"
]

Excludes are applied after all includes are gathered. The file LoginEncryptionKey.inc is always excluded automatically as a security measure.

Archive Structure

The resulting ZIP file is named <workspaceName>-<version>.zip and written to the workspace root. Inside the archive, files keep their relative paths from the workspace root. The workspace .sws file is renamed to df.sws:

df.sws
AppSrc/MyComponent.pkg
AppSrc/MyHelper.pkg
Bitmaps/icon.png
Programs/MyPlugin.dll

Running the Pack

df-cli package pack MyLibrary.sws

If validation fails, df-cli will report what needs to be fixed (missing version, invalid license, icon file not found, etc.) and abort without creating the archive.


Step 3: Install Patterns

The install field defines files that should be copied into the workspace of anyone who installs your package. This is typically used for runtime binaries, DLLs, or configuration files that need to live outside the dfpkg/ directory.

Path Variables

Install patterns support workspace path variables that resolve relative to the consumer's workspace layout:

Variable Resolves to
${Programs} The consumer's programs path
${AppSrc} The consumer's appSrc path (first entry)
${Data} The consumer's data path (first entry)
${DDSrc} The consumer's ddSrc path (first entry)
${Bitmap} The consumer's bitmap path (first entry)
${AppHtml} The consumer's appHtml path
${IdeSrc} The consumer's ideSrc path
${Help} The consumer's help path (first entry)

Variables must appear at the start of the pattern and are case-insensitive. Only one variable per pattern is allowed.

Examples

"install": [
  "${Programs}/*.dll",
  "${Programs}/config.ini",
  { "path": "${Data}/seed-data.xml" }
]

This will copy matching DLLs, config.ini, and seed-data.xml into the corresponding directories of the consumer's workspace.

Install entries can be strings (glob patterns) or objects with a path property. Both forms are functionally equivalent.

Conflict Detection

When files are installed into a consumer workspace, df-cli compares file hashes to detect conflicts. If a file already exists with different content, the user is prompted before overwriting.


Step 4: HTML Includes

The htmlIncludes field lists scripts and stylesheets that should be automatically injected into the index.html of consumer workspaces. This is how packages deliver client-side JavaScript or CSS without requiring manual HTML editing.

How Injection Works

When a consumer installs your package, df-cli looks for injection markers in their index.html:

<!-- Managed Includes (do not remove this line, used for automatic insertion) -->
<!-- your includes get injected here -->
<!-- End of Managed Includes (do not remove this line, used for automatic insertion) -->

If these markers are not found, df-cli falls back to injecting before </head>.

Generated HTML

The injected HTML depends on the file type:

File Extension Generated Tag
.js <script src="path/file.js?v=1.0.0"></script>
.mjs (or type: "mjs") <script type="module" src="path/file.mjs?v=1.0.0"></script>
.css <link rel="stylesheet" href="path/file.css?v=1.0.0">

A version query string (?v=1.0.0) is appended automatically for cache busting, unless the workspace has no-html-include-versioning set to true.

Loading Attributes

For JavaScript files, you can control loading behavior:

"htmlIncludes": [
  {
    "file": "includes/heavy-module.js",
    "async": true
  },
  {
    "file": "includes/deferred-init.js",
    "defer": true
  }
]
Attribute Default Description
async false Load the script asynchronously, without blocking page rendering.
defer false Defer execution until the HTML document has been fully parsed.

Setting both async and defer to true is redundant — async implies defer, so df-cli will automatically drop the defer flag and emit a warning.

The async and defer attributes are ignored for CSS files.


Step 5: Set Up Your Repository

Before you can push packages, you need a repository on packages.dataflex.dev.

  1. Sign in to the admin panel at packages.dataflex.dev.
  2. Create a repository for your packages. The repository name will be used as the publisher prefix when others install your packages.

Then authenticate df-cli with your repository:

# Interactive login (opens OAuth flow)
df-cli login --endpoint https://packages.dataflex.dev

# Or use tokens directly (useful for CI)
df-cli login --endpoint https://packages.dataflex.dev --access-token <token>

# Optionally include a refresh token for automatic token renewal
df-cli login --endpoint https://packages.dataflex.dev --access-token <token> --refresh-token <token>

Step 6: Push a Version

Pushing uploads the packed archive to the repository. The pack step runs automatically as part of the push, so you do not need to run package pack separately.

df-cli package push MyLibrary.sws my-repository

df-cli will: 1. Validate the workspace (version, license, files). 2. Pack the archive. 3. Display the license and ask for confirmation (skip with -y). 4. Upload the archive to the repository.

Each push must have a unique version. If you try to push a version that already exists, the server will reject it with a conflict error. Increment your version in the .sws file before pushing again:

"package": {
  "version": "1.1.0"
}

For CI environments, skip the confirmation prompt:

df-cli package push MyLibrary.sws my-repository -y

Step 7: Publish the Version

After pushing, the version is not yet visible to consumers. You must publish it through the admin panel:

  1. Go to packages.dataflex.dev and sign in.
  2. Navigate to your repository and package.
  3. Find the pushed version and publish it.

This two-step process (push then publish) gives you the opportunity to review the uploaded archive before making it available to others.

Once published, the package can be installed by anyone:

df-cli package install ConsumerWorkspace.sws my-repository/MyLibrary

Complete Example

Here is a full .sws file for a package that provides a UI component library with runtime JavaScript:

{
  "workspaceName": "AwesomeUI",
  "df": 25.0,
  "description": "A collection of modern UI components for DataFlex web applications",
  "paths": {
    "configFile": "Programs/Config.ws",
    "appHtml": "AppHtml",
    "appSrc": ["AppSrc"],
    "data": ["Data"],
    "ddSrc": ["DDSrc"],
    "bitmap": ["Bitmaps"],
    "programs": ["Programs"]
  },
  "dependencies": [
    "DAE/CoreUtils"
  ],
  "projects": [
    "AwesomeUI.src"
  ],
  "package": {
    "version": "2.1.0",
    "license": "MIT",
    "licenseFile": "LICENSE.txt",
    "icon": "Bitmaps/package-icon.png",
    "install": [
      "${Programs}/AwesomeUI.dll",
      "${Programs}/AwesomeUI64.dll"
    ],
    "includeFiles": [
      "AppHtml/AwesomeUI/**/*"
    ],
    "excludeFiles": [
      "AppSrc/DevTools/*",
      "AppSrc/Tests/*"
    ],
    "htmlIncludes": [
      "AwesomeUI/css/awesome-ui.css",
      {
        "file": "AwesomeUI/js/awesome-ui.js",
        "type": "mjs",
        "async": true
      }
    ]
  }
}

Publishing Workflow

# One-time setup
df-cli login --endpoint https://packages.dataflex.dev

# For each new version
# 1. Update "version" in AwesomeUI.sws
# 2. Push to repository
df-cli package push AwesomeUI.sws my-repository
# 3. Publish via admin panel at packages.dataflex.dev

Consumer Installation

df-cli package install MyApp.sws my-repository/AwesomeUI

This will: - Download the package and place it under dfpkg/my-repository_AwesomeUI-2_1_0/. - Copy AwesomeUI.dll and AwesomeUI64.dll into the consumer's Programs directory. - Inject the CSS and JS includes into the consumer's index.html. - Recursively install DAE/CoreUtils if not already present.


Updating a Published Package

To release a new version:

  1. Make your changes.
  2. Bump the version in the .sws file.
  3. Push: df-cli package push AwesomeUI.sws my-repository
  4. Publish the new version in the admin panel.

Consumers can then update:

# Check what updates are available
df-cli package update ConsumerWorkspace.sws

# Upgrade to latest compatible versions
df-cli package upgrade ConsumerWorkspace.sws

Version constraints in the consumer's workspace determine which versions are eligible. See Versioning and Dependency Resolution for details on how constraints like ^2.0.0 control which updates are accepted.