Calq Flow

Calq Flow is a workflow and software delivery engine. Designed for fully automated, error-free versioning and release management of modular software and CLI-tool-driven workflows.

Comparison

Versioning & Release

Feature Calq Flow Commit-Convention Release Tools Manual-Changeset Release Tools Tag/History-Based Versioners Monorepo Version Managers
Version calculation basis ✅ IL binary diff ⚠️ commit message keywords ⚠️ changeset files ⚠️ tag distance / commit count ⚠️ commit messages per package
Breaking change detection ✅ automatic (API surface) ❌ manual classification ❌ manual classification ❌ none ❌ manual classification
Convention-free operation ✅ no conventions required ❌ conventional commits ❌ changeset files per PR ✅ tag-only ❌ conventional commits
False positive prevention ✅ compiler attribute filtering ❌ any "feat:" bumps ❌ any changeset bumps ⚠️ height-based increment ❌ any "feat:" bumps
Unnecessary release prevention ✅ binary no-change = no release ❌ commit = release ❌ changeset = release ⚠️ release decision external ❌ commit = release
Monorepo support ✅ automatic project discovery ⚠️ plugin per package ⚠️ config per package ⚠️ tag prefix per project ✅ built-in
.NET-native ✅ IL analysis + .csproj ❌ Node.js ecosystem ❌ Node.js ecosystem ✅ .NET-native ❌ Node.js ecosystem
Reproducible builds ✅ deterministic + lock file ❌ not in scope ❌ not in scope ❌ not in scope ❌ not in scope
Test enforcement before publish ✅ automatic discovery + gating ❌ separate CI step ❌ separate CI step ❌ separate CI step ❌ separate CI step
Change metadata ✅ structured JSON (IL diff) ✅ markdown changelog ✅ markdown changelog ✅ markdown changelog
Pre-release / alpha channel ❌ manual version edit ✅ built-in branch rules ✅ pre-release mode ✅ automatic suffix ✅ built-in

Build & Publish Orchestration

Feature Calq Flow .NET Build Automation Frameworks CI/CD Pipeline YAML Package Manager Publish Commands
Zero configuration ✅ convention-based discovery ❌ build script required ❌ YAML per workflow ❌ manual per package
Topological build order ✅ automatic from project references ⚠️ task-level only ⚠️ manual job dependencies ❌ manual ordering
Binary-level change detection ✅ IL / metadata comparison
Build isolation ✅ shadow copy, source unmodified ⚠️ user responsibility ❌ shared workspace ❌ shared workspace
Cross-source republishing ✅ re-push existing package ❌ not built-in ⚠️ manual artifact forwarding ❌ not built-in
Idempotent push --skip-duplicate built-in ⚠️ user responsibility ⚠️ user responsibility ⚠️ manual flag
Package signing ✅ single --sign parameter ⚠️ custom target ⚠️ manual step ⚠️ manual step
Dry-run mode ✅ full simulation + JSON output ⚠️ execution plan only
Machine-readable output ✅ structured JSON on stdout ❌ log output ❌ log output ❌ log output
Scale-to-zero ✅ CLI tool, no daemon ✅ CLI tool ✅ runner-based ✅ CLI tool
Multi-language support ❌ .NET only ⚠️ .NET-focused ✅ any language ⚠️ per-ecosystem

Table of Contents

Usage - Calq Flow

1. Setup

1.1 Configuration

Action inputs:

Input Required Default Description
subcommand Yes The flow subcommand to execute (e.g., publish, publish --dry-run)
nuget-config-repo No .nuget Repository to pull NuGet.Config from (under the same GitHub owner)
cache No true Enable caching for the tool binary and NuGet packages

Global CLI options:

Option Type Default Description
--sources / -s List<string> ["main"] NuGet source names to push packages to
--remote / -r string origin Git remote name for tag resolution and fetch operations
--tag-prefix / -t string v Prefix for version tags
calq-flow --sources nuget.org --remote upstream --tag-prefix release/ publish

Publish parameters:

Parameter Type Default Description
--dry-run bool false Log actions without modifying filesystem, Git, or NuGet
--ignore-access-modifiers bool false Include internal member changes (for InternalsVisibleTo)
--sign string "" Certificate fingerprint for signing .nupkg files before push
--rolling-branch string latest Branch pointer to force-update on release (empty string disables)
--api-key string "" API key for authenticated NuGet push operations
calq-flow publish --dry-run --ignore-access-modifiers
calq-flow publish --sign ABC123 --rolling-branch main
calq-flow publish --rolling-branch ""  # Disable rolling branch

Key points:

  • If --sources is not provided, it defaults to ["main"]
  • Source names reference entries in the system's NuGet.Config for credentials unless --api-key is provided
  • --tag-prefix affects both tag resolution (git ls-remote) and tag creation

1.2 NuGet authentication

Calq Flow separates read and write credentials for NuGet operations:

  • Read (restore/download): Credentials come from a NuGet.Config file, sourced from a dedicated repository (default: .nuget under the same GitHub owner). The action clones this repo and installs NuGet/NuGet.Config into the runner's ~/.nuget/NuGet/ directory, expanding environment variables via envsubst.
  • Write (push): Credentials are passed via the --api-key CLI parameter, completely independent of NuGet.Config.

Setting up the .nuget repository:

Create a repository named .nuget under your GitHub organization with the following structure:

.nuget/
└── NuGet/
    └── NuGet.Config

Example NuGet.Config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
    <add key="main" value="https://nuget.pkg.github.com/your-org/index.json" />
  </packageSources>
  <packageSourceCredentials>
    <main>
      <add key="Username" value="your-username" />
      <add key="ClearTextPassword" value="${MAIN_NUGET_PAT}" />
    </main>
  </packageSourceCredentials>
</configuration>

Key points:

  • ${MAIN_NUGET_PAT} is expanded at runtime from your workflow's environment. Pass it via env: ${{ secrets }} or explicitly as env: { MAIN_NUGET_PAT: ${{ secrets.MAIN_NUGET_PAT }} }
  • The PAT only needs packages:read scope — it is used exclusively for dotnet restore operations (dependency resolution and base DLL downloads)
  • Push authentication is handled separately by --api-key, which accepts ${{ github.token }} (for GitHub Packages) or a nuget.org API key
  • The main source key in NuGet.Config corresponds to the default --sources ["main"] value — these names must match
  • Override the config repository name with the nuget-config-repo action input if needed

See also: 1.1 Configuration

1.3 GitHub Action setup

Because Calq Flow is architected as a C# Composite Action, it eliminates the container image pull and initialization latency associated with conventional Docker-based DevOps actions. It leverages the GitHub Runner's native .NET runtime directly.

Publish workflows should utilize concurrency to prevent race conditions on version tags:

name: Publish

on:
  workflow_dispatch:

concurrency:
  group: publish
  cancel-in-progress: false

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      packages: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '9.0.x'
      - name: Calq Flow (publish)
        uses: calq-framework/flow@latest
        with:
          subcommand: 'publish --api-key ${{ github.token }}'
        env: ${{ secrets }}

Pin to a specific version:

- uses: calq-framework/[email protected]
  with:
    subcommand: 'publish'

Dry-run on pull requests:

- uses: calq-framework/flow@latest
  with:
    subcommand: 'publish --dry-run'

Publish to nuget.org:

- uses: calq-framework/flow@latest
  with:
    subcommand: 'publish --sources nuget.org --api-key ${{ secrets.NUGET_API_KEY }}'

Key points:

  • fetch-depth: 0 is required for full Git history (tag resolution and diff)
  • contents: write permission is required for Git tag creation
  • packages: write permission is required for GitHub Packages NuGet push
  • Dry-run logs exactly which packages would be published and which versions would be bumped, without modifying the filesystem, Git state, or NuGet registries

See also: 1.1 Configuration, 1.2 NuGet authentication

1.4 CLI tool installation

Calq Flow enables local execution of the same deterministic pipeline that runs in CI. The orchestration logic is not confined to YAML.

Install globally:

dotnet tool install --global CalqFramework.Flow.Cli

Run locally:

calq-flow publish
calq-flow publish --dry-run
calq-flow publish --sources nuget.org --api-key <key>

Key points:

  • Requires git and dotnet CLI on the system PATH
  • Must be executed within a Git repository
  • Produces identical results to the GitHub Action

See also: 1.1 Configuration, 1.3 GitHub Action setup


2. Change Detection

2.1 Git-based impact analysis

Calq Flow detects exactly which modules changed since the last version tag using git diff --name-only {lastTag}..HEAD. If no tags exist, all committed files are considered changed.

Key points:

  • Diff is computed against the most recent version tag matching --tag-prefix
  • Only committed changes are considered — unstaged or untracked files are excluded
  • The diff operates on file paths, not content, for performance

See also: 1.3 GitHub Action setup

2.2 Project-scoped diff boundaries

A project is marked "changed" only if a file modification occurs within the project's own directory or its subdirectories. Changes to root files (e.g., .gitignore, README.md) do not trigger project changes.

Key points:

  • Each project's change boundary is its directory subtree
  • Shared files outside any project directory never trigger a version bump
  • This scoping prevents unrelated repository-level changes from causing unnecessary releases

See also: 2.1 Git-based impact analysis


3. Binary Comparison

3.1 IL / metadata-level analysis

Assemblies are analyzed at the binary level using System.Reflection.MetadataLoadContext:

Change Type Classification Example
Deleted member Breaking Removing a public method
Modified attributes Breaking Changing method attributes
Added member Non-breaking Adding a new public method
IL bytecode change Non-breaking Modifying method body

Key points:

  • Comparison operates on compiled assemblies, not source code — captures the true public surface
  • Both current and base DLLs are loaded into MetadataLoadContext for reflection without executing code
  • Classification drives the version bump decision in the versioning stage

3.2 Byte-level fallback

If no syntactic changes are found but the DLL bytes differ, the diff falls back to byte-level comparison and classifies the change as ILChanged (non-breaking).

Key points:

  • Byte-level fallback ensures that changes not captured by metadata reflection (e.g., method body modifications) still trigger a patch bump
  • ByteLevelFallback is reported in the JSON output for visibility
  • This fallback never classifies a change as breaking — only as non-breaking

See also: 3.1 IL / metadata-level analysis

3.3 Filtered compiler attributes

Compiler-generated attributes are filtered out to prevent false positives:

  • AsyncStateMachineAttribute
  • NullableContextAttribute
  • NullableAttribute
  • CompilerGeneratedAttribute
  • IteratorStateMachineAttribute

Key points:

  • These attributes change between compilations without any source-level modification
  • Filtering prevents spurious version bumps caused by compiler implementation details
  • The filter list is fixed — custom attributes are always included in comparison

See also: 3.1 IL / metadata-level analysis

3.4 Access modifier control

Use --ignore-access-modifiers to include internal member changes in the comparison, relevant for projects using InternalsVisibleTo.

Key points:

  • Default behavior compares only public and protected members
  • When enabled, internal and private protected members are included in breaking change detection
  • Useful for libraries that expose internals to friend assemblies via InternalsVisibleTo

See also: 3.1 IL / metadata-level analysis, 1.1 Configuration


4. Versioning

4.1 Version resolution

Key points:

  • Reads <Version> (or <VersionPrefix>) from .csproj using XML parsing, stripping pre-release suffixes
  • Resolves the latest version tag from the remote using git ls-remote --tags --sort -version:refname
  • Uses System.Version (3-component: major.minor.build)
  • Pre-release suffixes (e.g., -alpha.1) are stripped before comparison — only the numeric components participate in precedence

See also: 1.1 Configuration, 2.1 Git-based impact analysis

4.2 Version bump rules

Calq Flow algorithmically calculates version bumps based on IL changes:

Condition Bump Example
Breaking change (deleted/modified member) Minor 0.1.00.2.0
Non-breaking change (added member / IL change) Patch 0.1.00.1.1
Major version Manual only Edit <Version> in .csproj

Key points:

  • Follows pre-1.0 SemVer convention: breaking changes bump minor, not major
  • Major version bumps are intentional — achieved by manually editing <Version> in the .csproj
  • If no changes are detected, no version bump occurs

See also: 3.1 IL / metadata-level analysis, 4.1 Version resolution

4.3 Version precedence

The higher version between the .csproj <Version> and the computed syntactic version always prevails.

Key points:

  • Ensures idempotency — re-running the pipeline never produces a lower version than what is already declared
  • Enables manual override — setting a higher <Version> in .csproj takes priority over algorithmic computation
  • Lockstep versioning: all projects in the monorepo are packed at the single computed target version

See also: 4.2 Version bump rules, 4.1 Version resolution


5. Build Orchestration

5.1 Monorepo project discovery

Calq Flow recursively scans the working directory for *.*proj files with automatic exclusion logic.

Exclusion rules:

  • Projects matching *Test*, *Example*, *Sample* (and identically named directories) are excluded
  • If a project file resides in the same directory or a subdirectory of another discovered project, the nested one is ignored
my-monorepo/
├── MyLibrary/
│   └── MyLibrary.csproj          ← Discovered
├── MyLibrary.Tests/
│   └── MyLibrary.Tests.csproj    ← Associated test project (auto-discovered)
├── Example.App/
│   └── Example.App.csproj        ← Excluded (matches *Example*)
└── OtherLib/
    └── OtherLib.csproj           ← Discovered

Key points:

  • Discovery is recursive from the Git repository root
  • No configuration file is required — convention-based detection
  • Nested projects (project within another project's directory) are automatically excluded to prevent duplicate builds

See also: 2.2 Project-scoped diff boundaries, 4.3 Version precedence

5.2 Topological build order

Projects are built in dependency order using a topological sort with queue-retry fallback.

Key points:

  • Transitive dependencies are built before their dependents
  • The queue-retry fallback handles circular or unresolvable dependency graphs gracefully
  • Changed projects and their transitive dependents are built; unchanged projects are only built when publishing (not during dry-run) to ensure lockstep packing

See also: 5.1 Monorepo project discovery, 2.1 Git-based impact analysis

5.3 Locked-mode restore

Projects with a packages.lock.json are restored in --locked-mode for reproducibility; projects without one proceed normally with a warning.

Key points:

  • --locked-mode ensures the exact dependency graph from the lock file is restored — no floating resolution
  • Projects without a lock file receive a warning but are not blocked
  • Lock file presence is detected per-project, not globally

See also: 5.2 Topological build order, 1.2 NuGet authentication

5.4 Reproducible builds

All builds use deterministic flags:

-p:Deterministic=true -p:ContinuousIntegrationBuild=true -p:PathMap="$(MSBuildProjectDirectory)=/src"

Key points:

  • Deterministic=true ensures identical source produces identical binary output
  • ContinuousIntegrationBuild=true normalizes file paths in PDBs for reproducibility across machines
  • PathMap replaces local absolute paths with /src in debug information
  • Projects with SourceLink are built with embedded debug symbols and source tracking

See also: 5.3 Locked-mode restore, 3.2 Byte-level fallback

5.5 Test association and enforcement

For each library project, Calq Flow searches upward from the project's directory for {ProjectName}Test*.*proj files. Traversal is bounded by the Git repository root.

Key points:

  • Test projects are built and tests are strictly enforced before publishing
  • If no test project is found, only the library project is built
  • Test failure aborts the pipeline — no packages are published if any test fails
  • Test projects themselves are never published as packages

See also: 5.1 Monorepo project discovery, 5.2 Topological build order


6. Build Isolation

6.1 Base DLL resolution

For each changed project, Calq Flow resolves the previous version's DLL using a prioritized fallback:

  1. NuGet Download: Attempts to download the previous version package from configured sources using a temporary project restore into the global NuGet cache
  2. Shadow Copy Build: If NuGet download fails, creates an isolated shadow copy of the repository and builds the old version there

Key points:

  • The shadow copy is created at most once per publish cycle (shared across all projects)
  • The shadow copy is automatically purged upon completion
  • The primary working directory is never mutated
  • NuGet download is preferred for performance — shadow copy is the guaranteed fallback

See also: 1.2 NuGet authentication, 3.1 IL / metadata-level analysis, 4.1 Version resolution

6.2 Shadow copy mechanism

When the previous version's DLL cannot be retrieved from NuGet, Calq Flow creates a temporary workspace reflecting the repository's previous state:

  1. Copy: Creates a physical copy of the working directory into a system temporary path, excluding bin, obj, and .vs folders
  2. Fetch: Resolves the base commit from the last version tag, then fetches it with git fetch {remote} {baseCommit} --depth 1
  3. Sanitize: Executes git reset --hard and git clean -d -x --force inside the shadow copy only
  4. Checkout: Executes git checkout {baseCommit} to synchronize the filesystem with the base version
  5. Build: Builds the old version of changed projects in isolation
  6. Cleanup: Recursively deletes the temporary directory (resets read-only attributes on Windows for git pack files)

Key points:

  • The shadow copy guarantees the pipeline never fails due to an unreachable package registry
  • Uses CD (AsyncLocal) for thread-safe directory switching
  • Exclusion of bin, obj, and .vs reduces copy time and avoids stale build artifacts

See also: 6.1 Base DLL resolution, 5.4 Reproducible builds, 2.1 Git-based impact analysis

6.3 Working directory safety

Key points:

  • Destructive Git commands (reset --hard, clean -d -x --force) are never executed on the user's source path
  • All destructive operations are confined to the temporary shadow copy directory
  • The primary working directory remains unmodified throughout the entire pipeline execution
  • On pipeline failure, the shadow copy is still cleaned up — no temporary directories are left behind

See also: 6.2 Shadow copy mechanism


7. Branch & Tag Management

7.1 Version tagging

After a successful publish, Calq Flow creates a global tag in the format {prefix}{version} (e.g., v0.2.0) representing the state of the entire repository.

Key points:

  • Tag prefix is configurable via --tag-prefix (default: v)
  • The tag represents all projects at the computed version — not per-project tags
  • Tag creation requires contents: write permission in GitHub Actions
  • Tags are pushed to the configured remote

See also: 1.3 GitHub Action setup, 1.1 Configuration, 2.1 Git-based impact analysis, 4.1 Version resolution

7.2 Rolling branch

Force-updates a configurable branch pointer (default latest) to the release commit.

calq-flow publish --rolling-branch main
calq-flow publish --rolling-branch ""  # Disable rolling branch

Key points:

  • The rolling branch provides a stable reference (e.g., @latest) for consumers who want the most recent release
  • Force-update means the branch always points to the latest release commit, not a merge
  • Set to empty string to disable rolling branch behavior entirely

See also: 7.1 Version tagging, 1.1 Configuration


8. Packaging & Distribution

8.1 Pack and push lifecycle

After version computation and build completion, all projects are packed at the computed version and pushed to configured NuGet sources.

Key points:

  • All projects are packed at the single computed target version (lockstep versioning)
  • Unchanged projects are built during publish (not during dry-run) to ensure all packages are available for packing
  • Push uses dotnet nuget push with --skip-duplicate for idempotency
  • Pack produces standard .nupkg files compatible with any NuGet feed

See also: 1.2 NuGet authentication, 4.3 Version precedence, 5.2 Topological build order, 5.5 Test association and enforcement

8.2 Package signing

.nupkg files can be signed with a certificate fingerprint before push using the --sign parameter.

calq-flow publish --sign ABC123

Key points:

  • Signing is performed via dotnet nuget sign with --certificate-fingerprint
  • Signing occurs after pack and before push — the signed package is what reaches the registry
  • If --sign is empty (default), signing is skipped entirely

See also: 8.1 Pack and push lifecycle, 1.1 Configuration

8.3 Cross-source republishing

When no source changes are detected but a tagged version exists, Calq Flow downloads the existing .nupkg from any configured source and re-pushes to the requested targets.

Key points:

  • Enables publishing an existing package to additional registries (e.g., GitHub Packages → nuget.org) without rebuilding
  • The downloaded package is identical to the originally published artifact
  • Requires the package to be available on at least one configured source

See also: 1.2 NuGet authentication, 7.1 Version tagging, 2.1 Git-based impact analysis

8.4 Idempotent push

All push operations use --skip-duplicate to tolerate re-runs.

Key points:

  • Re-running the pipeline against an already-published version does not fail
  • --skip-duplicate is a NuGet CLI flag that returns success when the package version already exists on the feed
  • Combined with cross-source republishing, this enables safe multi-target delivery across pipeline retries

See also: 8.1 Pack and push lifecycle, 8.3 Cross-source republishing


9. Output & Machine Integration

9.1 JSON result format

publish returns a PublishResult serialized to JSON on stdout:

{
  "TargetVersion": "0.2.0",
  "PreviousVersion": "0.1.0",
  "ChangedProjects": ["MyLibrary"],
  "PublishedPackages": ["MyLibrary"],
  "Diffs": [
    {
      "ProjectName": "MyLibrary",
      "Changes": [
        {
          "MemberIdentity": "MyNamespace.MyClass.NewMethod(System.String)",
          "Kind": "Added"
        }
      ],
      "HasBreakingChanges": false,
      "HasNonBreakingChanges": true,
      "ByteLevelFallback": false
    }
  ],
  "DryRun": false
}

Key points:

  • Safe for piping to jq, scripts, or other tools
  • Contains version information, project lists, and detailed diff metadata
  • DryRun field indicates whether the result represents an actual publish or a simulation

See also: 4.2 Version bump rules, 3.1 IL / metadata-level analysis

9.2 Stream separation

All build/test output is routed to stderr, maintaining a pristine stdout stream for machine consumption.

Key points:

  • stdout contains only the JSON result
  • stderr contains all build, test, and diagnostic output
  • This separation enables calq-flow publish | jq .TargetVersion without interference from build logs

See also: 9.1 JSON result format

9.3 Diff metadata

The Diffs array in the JSON output provides member-level change metadata for each changed project.

Key points:

  • MemberIdentity contains the fully qualified member signature
  • Kind indicates the change type (e.g., Added, Deleted, Modified, ILChanged)
  • HasBreakingChanges and HasNonBreakingChanges provide project-level classification
  • ByteLevelFallback indicates whether the diff fell back to byte-level comparison for that project

See also: 9.1 JSON result format, 3.2 Byte-level fallback, 4.2 Version bump rules, 3.1 IL / metadata-level analysis


10. Workflow Distribution

10.1 CLI-first architecture

Calq Flow's pipeline logic is a standalone .NET console application with no CI platform dependencies. The tool requires only git and dotnet on the system PATH, accepts configuration via CLI parameters, and produces structured JSON on stdout.

This design enables two distribution paths:

  • Package distribution (Section 8): libraries are pushed to NuGet feeds for programmatic consumption
  • Workflow distribution (this section): the CLI tool is wrapped in platform-specific CI/CD integration for declarative pipeline consumption

Any CI/CD platform can invoke the tool identically:

dotnet tool install --global CalqFramework.Flow.Cli
calq-flow publish --api-key <key>

Key points:

  • No GitHub API calls — uses git CLI for all repository operations
  • No environment variable detection — all values are passed explicitly via parameters
  • Stream separation (stdout/stderr) enables machine consumption on any platform
  • Platform-specific concerns (caching, credential injection, step format) belong to the wrapper, not the tool

See also: 1.4 CLI tool installation, 9.1 JSON result format

10.2 GitHub Action packaging

The CLI-first architecture allows any .NET tool to be packaged as a GitHub Action using a composite action wrapper. The wrapper handles tool installation, caching, and credential injection while delegating all logic to the CLI.

1. Add these properties to your .csproj:

<PackAsTool>true</PackAsTool>
<ToolCommandName>your-tool</ToolCommandName>
<PackageId>YourTool.Cli</PackageId>

2. Copy action.yaml to your repository root. Update the four variables in the first step:

TOOL_NAME="Your Tool"
TOOL_REPO_URL="https://github.com/your-org/your-tool.git"
TOOL_PACKAGE="YourTool.Cli"
TOOL_CMD="your-tool"

3. Adapt action.yaml to your requirements — remove steps you do not need, such as the self-install step.

Key points:

  • Eliminates container image pull and initialization latency associated with Docker-based actions
  • Leverages the GitHub Runner's native .NET runtime directly
  • Provides strong typing, IDE support, and testability compared to bash/YAML
  • The wrapper is thin — platform-specific concerns are isolated from pipeline logic

See also: 10.1 CLI-first architecture, 1.3 GitHub Action setup

10.3 Workflow templates

Three workflow templates enable both self-publishing and consumer distribution:

Template Trigger Purpose
ci.yaml Push / PR Dry-run validation — verifies versioning and build without publishing
publish.yaml Manual Publishes packages to GitHub Packages and creates version tags
publish-nuget-org.yaml Manual Republishes existing packages to nuget.org

For your tool's own CI — place these in your tool repository's .github/workflows/ directory using uses: ./ (local action reference):

- uses: ./
  with:
    subcommand: 'publish --dry-run'

For consumer distribution — place these in your organization's .github repository under workflow-templates/ using the full action reference:

- uses: your-org/your-tool@latest
  with:
    subcommand: 'publish --api-key ${{ github.token }}'

Add a properties.json alongside each template to control visibility in the GitHub "New workflow" UI:

{
  "name": "Your Tool Publish",
  "description": "Publish workflow with your-tool.",
  "iconName": "octicon package",
  "categories": ["C#", "F#"],
  "filePatterns": [".*\\.csproj$", ".*\\.fsproj$"]
}

Key points:

  • uses: ./ is for development — the tool publishes itself using its own local action
  • uses: your-org/your-tool@latest is for consumers — references the published action
  • filePatterns controls which repositories see the template in the GitHub Actions UI
  • The publish concurrency group prevents race conditions on version tags across all publish workflows

See also: 10.2 GitHub Action packaging, 7.2 Rolling branch

10.4 Version-pinned consumption

Consumers reference a specific version or track a rolling release:

# Pin to exact version
- uses: your-org/[email protected]

# Track latest within a minor
- uses: your-org/[email protected]

# Always use latest release
- uses: your-org/your-tool@latest

The action wrapper resolves the reference to a NuGet package version:

  • @v1.2.3 → installs exact version 1.2.3
  • @v1.2 → resolves latest v1.2.* tag via git ls-remote
  • @latest or branch name → resolves latest v*.*.* tag

Key points:

  • Version tags created by the publish pipeline directly enable version-pinned consumption
  • The rolling branch provides @latest tracking without requiring consumers to update their workflows
  • Package binary is cached per resolved version — subsequent runs skip installation

See also: 10.3 Workflow templates, 7.1 Version tagging, 7.2 Rolling branch, 8.1 Pack and push lifecycle

Quick Start

dotnet tool install --global CalqFramework.Flow.Cli
cd your-monorepo
calq-flow publish --dry-run

License

Calq Flow is dual-licensed under PolyForm Noncommercial (with Evaluation Grant) and the Calq Commercial License.

An unhandled error has occurred. Reload 🗙