Skip to main content

Trunk Flow

PipeCraft implements a promote-on-merge trunk-based workflow with automatic branch-to-branch promotion. This is the default and currently the only supported flow pattern.

Overview​

PipeCraft currently implements a simple promote-on-merge trunk-based workflow with automatic branch-to-branch promotion. Code flows from an initial branch (typically develop) through intermediate branches (typically staging) to a final branch (typically main).

Current Implementation​

Branch Flow​

feature → develop → staging → main
↓ ↓ ↓
[tests] [tests + [deploy]
version]

How It Works​

  1. Feature Development

    • Developers create feature branches from develop
    • Feature branches merge into develop via pull request
    • Merge triggers workflow on develop
  2. Develop Branch

    • On merge to develop:
      • Run tests for all affected domains
      • If tests pass, create PR to promote to staging
    • Promotion happens via PR (not direct push)
  3. Staging Branch

    • On merge to staging:
      • Run tests for all affected domains
      • Calculate next semantic version (optional)
      • Create git tag (optional)
      • If tests pass, create PR to promote to main
  4. Main Branch

    • On merge to main:
      • Run deployment jobs
      • Represents current production state

Key Configuration Options​

Basic Configuration​

{
"ciProvider": "github",
"mergeStrategy": "fast-forward",
"initialBranch": "develop",
"finalBranch": "main",
"branchFlow": ["develop", "staging", "main"]
}

Auto-Promote Configuration​

{
"autoPromote": {
"staging": true, // Auto-promote develop → staging PRs
"main": false // Manual approval for staging → main
},
"mergeMethod": {
"staging": "squash", // Squash commits when promoting to staging
"main": "merge" // Create merge commit when promoting to main
}
}

Auto-Promote Behavior:

  • When autoPromote is true for a branch, PRs targeting that branch are automatically merged after checks pass
  • When false, PRs require manual approval
  • Typically used for automated promote-to-staging, manual promote-to-production

autoPromote controls how the promotion PR is merged, not whether one is opened. The PR is created either way — setting false leaves it waiting for a human rather than suppressing it. If you want no promotion PRs against a branch at all, remove that branch from branchFlow.

tag and promote run only for commits GitHub authored (a merged pull request) or a manual workflow_dispatch. Set "promoteDirectPushes": true to let any push to the initial branch tag and promote. See Security.

Domain-Based Testing​

PipeCraft implements path-based change detection for monorepo support:

{
"domains": {
"api": {
"paths": ["packages/api/**", "libs/shared/**"],
"description": "Backend API services",
"testable": true,
"deployable": true
},
"web": {
"paths": ["packages/web/**", "libs/shared/**"],
"description": "Frontend web application",
"testable": true,
"deployable": true
}
}
}

How It Works:

  1. Workflow runs detect-changes action
  2. Compares current commit with base branch
  3. Matches changed files against domain path patterns
  4. Sets outputs: api-changed, web-changed, etc.
  5. Downstream jobs use needs.detect-changes.outputs.{domain}-changed
  6. Only run tests/deploys for domains that changed

Generated Workflow Structure​

PipeCraft generates a single comprehensive workflow file that handles all stages of the trunk-based flow. The workflow is divided into distinct phases:

Workflow Phases​

name: Pipeline

on:
workflow_dispatch: # Manual trigger
workflow_call: # Called by other workflows
push:
branches: [develop, staging, main]
pull_request:
branches: [develop]

jobs:
# Phase 1: Change Detection (Managed by PipeCraft)
changes:
runs-on: ubuntu-latest
steps:
- uses: ./actions/detect-changes
outputs:
domain1: ${{ steps.detect.outputs.domain1 }}
domain2: ${{ steps.detect.outputs.domain2 }}

# Phase 2: Testing (Customizable)
test-domain1:
needs: changes
if: needs.changes.outputs.domain1 == 'true'
runs-on: ubuntu-latest
steps:
- run: npm test -- packages/domain1

test-domain2:
needs: changes
if: needs.changes.outputs.domain2 == 'true'
runs-on: ubuntu-latest
steps:
- run: npm test -- packages/domain2

# Phase 3: Versioning (Managed by PipeCraft)
version:
needs: [changes, test-domain1, test-domain2]
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: ./actions/calculate-version
outputs:
version: ${{ steps.version.outputs.version }}

# Phase 4: Deployment (Customizable)
deploy-domain1:
needs: [version, changes]
if: needs.changes.outputs.domain1 == 'true'
runs-on: ubuntu-latest
steps:
- run: npm run deploy -- packages/domain1

# Phase 5: Remote Testing (Customizable)
remote-test-domain1:
needs: [deploy-domain1]
if: needs.deploy-domain1.result == 'success'
runs-on: ubuntu-latest
steps:
- run: npm run test:remote -- packages/domain1

# Phase 6: Tagging (Managed by PipeCraft)
tag:
needs: [version, deploy-domain1, remote-test-domain1]
if: |
github.ref_name == 'develop' &&
needs.version.outputs.version != ''
runs-on: ubuntu-latest
steps:
- uses: ./actions/create-tag
with:
version: ${{ needs.version.outputs.version }}

# Phase 7: Promotion (Managed by PipeCraft)
promote:
needs: [version, tag]
if: |
needs.version.outputs.version != '' &&
(github.ref_name == 'develop' || github.ref_name == 'staging')
runs-on: ubuntu-latest
steps:
- uses: ./actions/promote-branch
with:
sourceBranch: ${{ github.ref_name }}
version: ${{ needs.version.outputs.version }}

# Phase 8: Release (Managed by PipeCraft)
release:
needs: [version, tag]
if: |
github.ref_name == 'main' &&
needs.version.outputs.version != ''
runs-on: ubuntu-latest
steps:
- uses: ./actions/create-release
with:
version: ${{ needs.version.outputs.version }}

What You Can Customize​

PipeCraft manages the workflow structure, but you can customize:

  • Test jobs (test-*): Add your testing commands
  • Deploy jobs (deploy-*): Add your deployment commands
  • Remote test jobs (remote-test-*): Add integration/E2E tests
  • Workflow name: Change the workflow display name

What PipeCraft Manages​

These sections are automatically generated and should not be modified:

  • Workflow triggers: Push, PR, and workflow_dispatch events
  • Changes detection: Domain-based change detection logic
  • Versioning: Semantic version calculation from commits
  • Tagging: Git tag creation on develop branch
  • Promotion: Branch-to-branch promotion logic
  • Release: GitHub release creation on main branch
  • Job dependencies: Conditional logic and job ordering

Promotion Mechanism​

How Promotions Work​

PipeCraft uses workflow dispatch to trigger promotions between branches:

  1. Version Calculation: On develop, calculate semantic version from commits
  2. Tagging: Create a git tag with the version on develop
  3. Promote: Trigger the next branch's workflow via workflow_dispatch
  4. Pass Context: Version and run number are passed to maintain traceability
  5. Next Stage: The target branch (staging or main) runs the same workflow with the version context

Promotion Flow​

develop (push)
↓
tests pass
↓
version calculated
↓
tag created on develop
↓
promote job triggers staging workflow
↓
staging (workflow_dispatch)
↓
tests run with develop's commitSha
↓
promote job triggers main workflow
↓
main (workflow_dispatch)
↓
tests run with develop's commitSha
↓
deploy & release created

Key Features​

  • Version Gating: Only commits that bump the version trigger promotions
  • Commit SHA Tracking: The same commit SHA is tested across all branches
  • Traceability: Original run number and version are passed through all stages
  • Idempotent: Re-running the same version on a branch is safe

Semantic Versioning (Optional)​

When versioning.enabled: true:

Version Calculation​

  1. Workflow runs on staging merge
  2. calculate-version action runs:
    • Reads conventional commits since last tag
    • Determines bump level (major, minor, patch) from commit types
    • Calculates next version
  3. create-tag action creates git tag
  4. Tag pushed to repository

Conventional Commit Mapping​

feat: something       → minor bump (1.0.0 → 1.1.0)
fix: something → patch bump (1.0.0 → 1.0.1)
BREAKING CHANGE: → major bump (1.0.0 → 2.0.0)
chore: something → no bump (ignored)

Configuration​

{
"versioning": {
"enabled": true,
"conventionalCommits": true,
"autoTag": true,
"changelog": true,
"bumpRules": {
"feat": "minor",
"fix": "patch",
"breaking": "major",
"chore": "patch"
}
}
}

Idempotent Regeneration​

PipeCraft only regenerates workflows when necessary:

What Triggers Regeneration?​

  • Configuration file (.pipecraftrc.json) changes
  • Template files change (if developing PipeCraft itself)
  • Explicit --force flag

What Doesn't Trigger Regeneration?​

  • Application code changes
  • Test file changes
  • Documentation changes
  • Any change outside config/templates

How It Works​

# First run
$ pipecraft generate
✓ Generating workflows...
✓ Created .github/workflows/pipeline.yml
✓ Cache updated

# Second run (unchanged config)
$ pipecraft generate
✓ No changes detected, skipping regeneration

# Force regeneration
$ pipecraft generate --force
✓ Force regeneration enabled
✓ Generating workflows...

User Comment Preservation​

PipeCraft preserves user comments when regenerating:

What Gets Preserved?​

# This is a user comment - PRESERVED
name: Pipeline

# PIPECRAFT-MANAGED: This section is managed
jobs:
test-api:
runs-on: ubuntu-latest
steps:
- run: npm test
# User comment within job - PRESERVED

What Gets Overwritten?​

jobs:
# PIPECRAFT-MANAGED: detect-changes
detect-changes:
# This comment is within a managed block - OVERWRITTEN
runs-on: ubuntu-latest

Best Practices​

  1. Add user comments OUTSIDE PipeCraft-managed blocks
  2. Look for # PIPECRAFT-MANAGED markers
  3. Don't modify jobs with PIPECRAFT markers
  4. Add custom jobs after PipeCraft-managed jobs

Limitations & Constraints​

Current Limitations​

  1. Single Flow Pattern: Only supports the promote-through-branches pattern
  2. GitHub Only: GitLab support is planned but not implemented
  3. No Manual Gates: Can't require manual approval mid-workflow (only at PR level)
  4. No Matrix Builds: Can't test across multiple Node versions/OSes automatically
  5. Linear Branch Flow: Must be linear (A → B → C), no branching/rejoining

GitHub API Requirements​

The following GitHub settings must be configured (can use pipecraft setup):

  • Branch Protection: Require status checks before merging
  • Workflow Permissions: Allow workflow to create PRs
  • Auto-Promote: Enable auto-merge for the repository

Git Workflow Constraints​

  • Fast-Forward Strategy: Requires linear history (rebase before merge)
  • Conventional Commits: Required if versioning is enabled
  • Tag Format: Must be vX.Y.Z for version detection

Configuration Examples​

Minimal Configuration​

{
"ciProvider": "github",
"mergeStrategy": "fast-forward",
"requireConventionalCommits": false,
"initialBranch": "develop",
"finalBranch": "main",
"branchFlow": ["develop", "main"],
"semver": {
"bumpRules": {}
},
"domains": {
"app": {
"paths": ["src/**"],
"description": "Application code",
"testable": true,
"deployable": true
}
}
}

Full Configuration with Versioning​

{
"ciProvider": "github",
"mergeStrategy": "fast-forward",
"requireConventionalCommits": true,
"initialBranch": "develop",
"finalBranch": "main",
"branchFlow": ["develop", "staging", "main"],
"autoPromote": {
"staging": true,
"main": false
},
"mergeMethod": {
"staging": "squash",
"main": "merge"
},
"semver": {
"bumpRules": {
"feat": "minor",
"fix": "patch",
"breaking": "major"
}
},
"domains": {
"api": {
"paths": ["packages/api/**", "libs/shared/**"],
"description": "Backend API",
"testable": true,
"deployable": true
},
"web": {
"paths": ["packages/web/**", "libs/shared/**"],
"description": "Frontend",
"testable": true,
"deployable": true
}
},
"versioning": {
"enabled": true,
"conventionalCommits": true,
"autoTag": true,
"changelog": true
},
"rebuild": {
"enabled": true,
"skipIfUnchanged": true
}
}

Troubleshooting​

Workflows Not Running​

Symptom: Merge to develop/staging doesn't trigger workflow

Causes:

  1. Workflow file not in .github/workflows/
  2. Branch name mismatch in configuration
  3. GitHub Actions disabled for repository
  4. Workflow file has syntax errors

Solutions:

# Verify workflow exists
ls -la .github/workflows/

# Validate workflow syntax
pipecraft validate

# Check branch names
git branch --show-current
# Should match one of branchFlow values

Auto-Promote Not Working​

Symptom: PRs created but not automatically merged

Causes:

  1. Auto-promote not enabled in repo settings
  2. Branch protection rules blocking
  3. Required checks not passing
  4. Insufficient permissions

Solutions:

# Setup GitHub permissions
pipecraft setup

# Check PR status
gh pr status

# Enable auto-merge manually
gh pr merge --auto --squash

Version Not Bumping​

Symptom: Version stays same after staging merge

Causes:

  1. No conventional commits since last tag
  2. All commits are chore: or docs: (ignored types)
  3. Versioning not enabled in config
  4. release-it not configured

Solutions:

# Check commit history
git log $(git describe --tags --abbrev=0)..HEAD --oneline

# Verify conventional format
pipecraft validate

# Check versioning config
cat .pipecraftrc.json | jq .versioning

Next Steps​

Now that you understand the current implementation:

  1. Try It: Run pipecraft init in your project
  2. Customize: Edit .pipecraftrc.json for your workflow
  3. Review: Check generated workflows in .github/workflows/
  4. Test: Create a feature branch and merge to see promotion
  5. Monitor: Watch GitHub Actions to see workflow execution

Future Enhancements​

See TRUNK_FLOW_PLAN.md for planned improvements:

  • Multiple flow patterns
  • GitLab support
  • Manual approval gates
  • Matrix builds
  • Custom action hooks
  • Deployment environments