Skip to content

Automation & CI/CD

Logsmith is designed for automation-first workflows. Integrate changelog generation seamlessly into your CI/CD pipelines, release processes, and development workflows.

Overview

Logsmith excels in automated environments because it:

  • Runs headless without user interaction
  • Provides consistent output across environments
  • Supports multiple formats for different consumers
  • Handles errors gracefully with proper exit codes
  • Includes verbose logging for debugging automation issues

GitHub Actions

Basic Changelog Generation

yaml
# .github/workflows/changelog.yml
name: Generate Changelog

on:
  push:
    tags: ['v*']

jobs:
  changelog:
    runs-on: ubuntu-latest
    permissions:
      contents: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Bun
        uses: oven-sh/setup-bun@v1

      - name: Generate Changelog
        run: |
          bun add -g logsmith
          logsmith --theme github --output CHANGELOG.md

      - name: Commit Changes
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add CHANGELOG.md
          git commit -m "docs: update changelog" || exit 0
          git push

Release Workflow

Complete release workflow with changelog generation:

yaml
# .github/workflows/release.yml
name: Release

on:
  push:
    tags: ['v*']

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Bun
        uses: oven-sh/setup-bun@v1

      - name: Install Dependencies
        run: |
          bun install
          bun add -g logsmith

      - name: Extract Version
        id: version
        run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

      - name: Generate Release Changelog
        run: |
          # Generate changelog for this release only
          PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "")
          if [ -n "$PREVIOUS_TAG" ]; then
            logsmith --from "$PREVIOUS_TAG" --to "${{ steps.version.outputs.VERSION }}" \
              --theme github --format markdown --output RELEASE_NOTES.md
          else
            logsmith --to "${{ steps.version.outputs.VERSION }}" \
              --theme github --format markdown --output RELEASE_NOTES.md
          fi

      - name: Update Full Changelog
        run: |
          logsmith --theme github --output CHANGELOG.md
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add CHANGELOG.md
          git commit -m "docs: update changelog for ${{ steps.version.outputs.VERSION }}" || exit 0
          git push origin HEAD:main

      - name: Create GitHub Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ steps.version.outputs.VERSION }}
          release_name: Release ${{ steps.version.outputs.VERSION }}
          body_path: RELEASE_NOTES.md
          draft: false
          prerelease: false

Pull Request Previews

Generate changelog previews for pull requests:

yaml
# .github/workflows/pr-changelog.yml
name: PR Changelog Preview

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  preview:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Bun
        uses: oven-sh/setup-bun@v1

      - name: Install logsmith
        run: bun add -g logsmith

      - name: Generate Preview
        id: changelog
        run: |
          # Generate changelog for commits in this PR
          BASE_SHA=${{ github.event.pull_request.base.sha }}
          HEAD_SHA=${{ github.event.pull_request.head.sha }}

          logsmith --from "$BASE_SHA" --to "$HEAD_SHA" \
            --theme github --no-output > changelog_preview.md || echo "No conventional commits found" > changelog_preview.md

      - name: Comment PR
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const changelog = fs.readFileSync('changelog_preview.md', 'utf8');

            const body = `## 📋 Changelog Preview

            Changes that would be included in the next release:

            ${changelog}

            <sub>Generated by logsmith</sub>`;

            // Update existing comment or create new one
            const { data: comments } = await github.rest.issues.listComments({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
            });

            const botComment = comments.find(comment =>
              comment.user.type === 'Bot' && comment.body.includes('📋 Changelog Preview')
            );

            if (botComment) {
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: botComment.id,
                body: body
              });
            } else {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body: body
              });
            }

Repository Statistics

Automated repository insights and reporting:

yaml
# .github/workflows/stats.yml
name: Repository Statistics

on:
  schedule:
    - cron: '0 0 * * 1' # Weekly on Monday
  workflow_dispatch:

jobs:
  stats:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Bun
        uses: oven-sh/setup-bun@v1

      - name: Install logsmith
        run: bun add -g logsmith

      - name: Generate Statistics
        run: |
          # Generate stats for last week
          logsmith stats --from "1 week ago" --json > weekly-stats.json
          logsmith stats --from "1 week ago" > weekly-stats.txt

          # Generate stats for last month
          logsmith stats --from "1 month ago" --json > monthly-stats.json
          logsmith stats --from "1 month ago" > monthly-stats.txt

      - name: Create Issue Report
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const weeklyStats = fs.readFileSync('weekly-stats.txt', 'utf8');
            const date = new Date().toISOString().split('T')[0];

            const body = `# 📊 Weekly Repository Statistics - ${date}

            ## Summary
            ${weeklyStats}

            ## 📈 Trends
            - View detailed trends in the [monthly report]
            - Compare with [previous weeks]

            <sub>Automated report generated by logsmith</sub>`;

            await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: `📊 Weekly Stats Report - ${date}`,
              body: body,
              labels: ['statistics', 'automated']
            });

      - name: Upload Artifacts
        uses: actions/upload-artifact@v3
        with:
          name: repository-statistics
          path: |
            weekly-stats.json
            weekly-stats.txt
            monthly-stats.json
            monthly-stats.txt

GitLab CI/CD

Basic Pipeline

yaml
# .gitlab-ci.yml
stages:
  - generate
  - deploy

variables:
  BUN_VERSION: 1.2.0

generate-changelog:
  stage: generate
  image: oven/bun:${BUN_VERSION}

  before_script:
    - bun add -g logsmith

  script:
    - logsmith --theme github --output CHANGELOG.md
    - logsmith stats --json > stats.json

  artifacts:
    paths:
      - CHANGELOG.md
      - stats.json
    expire_in: 1 week

  only:
    - tags
    - main

release-notes:
  stage: generate
  image: oven/bun:${BUN_VERSION}

  before_script:
    - bun add -g logsmith

  script:
    - |
      if [ -n "$CI_COMMIT_TAG" ]; then
        # Generate release notes for this tag
        PREVIOUS_TAG=$(git describe --tags --abbrev=0 $CI_COMMIT_TAG~1 2>/dev/null || echo "")
        if [ -n "$PREVIOUS_TAG" ]; then
          logsmith --from "$PREVIOUS_TAG" --to "$CI_COMMIT_TAG" \
            --theme github --format markdown --output RELEASE_NOTES.md
        else
          logsmith --to "$CI_COMMIT_TAG" \
            --theme github --format markdown --output RELEASE_NOTES.md
        fi
      fi

  artifacts:
    paths:
      - RELEASE_NOTES.md
    expire_in: 1 day

  only:
    - tags

Jenkins Pipeline

Declarative Pipeline

groovy
// Jenkinsfile
pipeline {
    agent any

    environment {
        BUN_VERSION = '1.2.0'
    }

    stages {
        stage('Setup') {
            steps {
                // Install Bun
                sh '''
                    curl -fsSL https://bun.sh/install | bash
                    export PATH="$HOME/.bun/bin:$PATH"
                    bun add -g logsmith
                '''
            }
        }

        stage('Generate Changelog') {
            when {
                anyOf {
                    tag 'v*'
                    branch 'main'
                }
            }
            steps {
                sh '''
                    export PATH="$HOME/.bun/bin:$PATH"
                    logsmith --theme corporate --output CHANGELOG.md --verbose
                '''

                archiveArtifacts artifacts: 'CHANGELOG.md', fingerprint: true
            }
        }

        stage('Repository Statistics') {
            steps {
                sh '''
                    export PATH="$HOME/.bun/bin:$PATH"
                    logsmith stats --json > repository-stats.json
                    logsmith stats > repository-stats.txt
                '''

                archiveArtifacts artifacts: 'repository-stats.*', fingerprint: true

                // Publish stats to external system
                script {
                    def stats = readJSON file: 'repository-stats.json'
                    echo "Total commits: ${stats.totalCommits}"
                    echo "Contributors: ${stats.contributors}"
                }
            }
        }

        stage('Release') {
            when {
                tag 'v*'
            }
            steps {
                sh '''
                    export PATH="$HOME/.bun/bin:$PATH"

                    # Generate release notes
                    PREVIOUS_TAG=$(git describe --tags --abbrev=0 ${TAG_NAME}~1 2>/dev/null || echo "")
                    if [ -n "$PREVIOUS_TAG" ]; then
                        logsmith --from "$PREVIOUS_TAG" --to "${TAG_NAME}" \
                          --theme github --format markdown --output RELEASE_NOTES.md
                    fi
                '''

                // Create GitHub release
                script {
                    def releaseNotes = readFile 'RELEASE_NOTES.md'
                    // Use GitHub API or plugin to create release
                }
            }
        }
    }

    post {
        always {
            cleanWs()
        }
        success {
            // Notify team of successful changelog generation
            slackSend(
                channel: '#dev-notifications',
                message: "✅ Changelog generated successfully for ${env.BUILD_TAG}"
            )
        }
        failure {
            slackSend(
                channel: '#dev-notifications',
                message: "❌ Changelog generation failed for ${env.BUILD_TAG}"
            )
        }
    }
}

Docker Integration

Dockerfile for Automation

dockerfile
# Dockerfile.logsmith
FROM oven/bun:1.2.0-alpine

WORKDIR /app

# Install logsmith globally
RUN bun add -g logsmith

# Copy git repository (or mount as volume)
COPY . .

# Default command
CMD ["logsmith", "--help"]

Docker Compose for Development

yaml
# docker-compose.yml
version: '3.8'

services:
  changelog:
    build:
      context: .
      dockerfile: Dockerfile.logsmith
    volumes:
      - .:/app
      - ./output:/output
    environment:
      - LOGSMITH_OUTPUT=/output/CHANGELOG.md
      - LOGSMITH_THEME=github
    command: logsmith --output /output/CHANGELOG.md --theme github --verbose

  stats:
    build:
      context: .
      dockerfile: Dockerfile.logsmith
    volumes:
      - .:/app
      - ./output:/output
    command: logsmith stats --json --output /output/stats.json

  multi-format:
    build:
      context: .
      dockerfile: Dockerfile.logsmith
    volumes:
      - .:/app
      - ./output:/output
    command: >
      sh -c "
        logsmith --output /output/CHANGELOG.md --theme github &&
        logsmith --output /output/changelog.json --format json &&
        logsmith --output /output/changelog.html --format html --theme colorful
      "

Custom Automation Scripts

Pre-commit Hook

bash
#!/bin/bash
# .git/hooks/pre-commit

# Generate changelog preview for staging area
if command -v logsmith >/dev/null 2>&1; then
    echo "Generating changelog preview..."

    # Check if there are conventional commits
    if git log --oneline | grep -E "^[a-f0-9]+\s+(feat|fix|docs|style|refactor|perf|test|build|ci|chore)(\(.+\))?:" >/dev/null; then
        logsmith --no-output --verbose 2>&1 | head -20
        echo ""
        echo "💡 Tip: Your changes will appear in the next changelog!"
    fi
fi

exit 0

Release Script

bash
#!/bin/bash
# scripts/release.sh

set -e

# Configuration
THEME="github"
CHANGELOG_FILE="CHANGELOG.md"
RELEASE_NOTES_FILE="RELEASE_NOTES.md"

# Check if logsmith is installed
if ! command -v logsmith >/dev/null 2>&1; then
    echo "❌ logsmith not found. Installing..."
    bun add -g logsmith
fi

# Get version from tag or ask user
if [ -z "$1" ]; then
    echo "Usage: $0 <version-tag>"
    echo "Example: $0 v1.2.0"
    exit 1
fi

VERSION_TAG="$1"

echo "🚀 Preparing release $VERSION_TAG"

# Generate full changelog
echo "📝 Generating full changelog..."
logsmith --theme "$THEME" --output "$CHANGELOG_FILE" --verbose

# Generate release notes for this version
echo "📋 Generating release notes..."
PREVIOUS_TAG=$(git describe --tags --abbrev=0 "$VERSION_TAG"~1 2>/dev/null || echo "")

if [ -n "$PREVIOUS_TAG" ]; then
    echo "📈 Changes from $PREVIOUS_TAG to $VERSION_TAG"
    logsmith --from "$PREVIOUS_TAG" --to "$VERSION_TAG" \
        --theme "$THEME" --output "$RELEASE_NOTES_FILE" --verbose
else
    echo "📈 All changes up to $VERSION_TAG"
    logsmith --to "$VERSION_TAG" \
        --theme "$THEME" --output "$RELEASE_NOTES_FILE" --verbose
fi

# Commit changelog
echo "💾 Committing changelog..."
git add "$CHANGELOG_FILE"
git commit -m "docs: update changelog for $VERSION_TAG" || echo "No changelog changes to commit"

# Show release notes
echo ""
echo "📋 Release Notes Preview:"
echo "========================="
cat "$RELEASE_NOTES_FILE"

echo ""
echo "✅ Release preparation complete!"
echo ""
echo "Next steps:"
echo "1. Review the generated files: $CHANGELOG_FILE, $RELEASE_NOTES_FILE"
echo "2. Push changes: git push"
echo "3. Create and push tag: git tag $VERSION_TAG && git push origin $VERSION_TAG"
echo "4. Create GitHub release with notes from $RELEASE_NOTES_FILE"

Monorepo Automation

bash
#!/bin/bash
# scripts/monorepo-changelogs.sh

# Generate changelogs for each package in a monorepo
set -e

PACKAGES_DIR="packages"
THEME="minimal"

echo "🏗️ Generating changelogs for monorepo packages..."

# Find all packages
for package_dir in "$PACKAGES_DIR"/*; do
    if [ -d "$package_dir" ] && [ -f "$package_dir/package.json" ]; then
        package_name=$(basename "$package_dir")
        echo ""
        echo "📦 Processing package: $package_name"

        # Generate package-specific changelog
        logsmith --dir "$package_dir" \
            --output "$package_dir/CHANGELOG.md" \
            --theme "$THEME" \
            --include-scopes "$package_name" \
            --verbose

        # Generate package statistics
        logsmith stats --dir "$package_dir" \
            --json > "$package_dir/stats.json"

        echo "✅ Completed $package_name"
    fi
done

# Generate root changelog
echo ""
echo "📋 Generating root changelog..."
logsmith --output CHANGELOG.md --theme "$THEME" --verbose

echo ""
echo "✅ All monorepo changelogs generated!"

Environment Configuration

Environment Variables

Configure logsmith behavior through environment variables:

bash
# Set default theme
export LOGSMITH_THEME="corporate"

# Set default output format
export LOGSMITH_FORMAT="markdown"

# Set verbosity level
export LOGSMITH_VERBOSE="true"

# Set default exclude authors
export LOGSMITH_EXCLUDE_AUTHORS="dependabot[bot],renovate[bot]"

# GitHub token for enhanced features
export GITHUB_TOKEN="ghp_your_token_here"

Configuration for CI

typescript
// logsmith.config.ci.ts
import { defineConfig } from 'logsmith'

export default defineConfig({
  theme: 'corporate',
  verbose: process.env.CI_DEBUG === 'true',
  excludeAuthors: ['dependabot[bot]', 'renovate[bot]'],
  repo: process.env.GITHUB_REPOSITORY
    ? `https://github.com/${process.env.GITHUB_REPOSITORY}`
    : undefined,
})

Best Practices for Automation

General Guidelines

  1. Always fetch full Git history in CI/CD (use fetch-depth: 0)
  2. Pin logsmith version for reproducible builds
  3. Use configuration files instead of CLI arguments for complex setups
  4. Implement proper error handling and notifications
  5. Cache dependencies when possible to speed up builds
  6. Test automation locally before deploying to CI/CD

Security Considerations

  1. Protect GitHub tokens and other secrets
  2. Use least-privilege permissions for automation accounts
  3. Validate inputs and sanitize outputs
  4. Audit automation logs for security issues
  5. Rotate tokens regularly and monitor usage

Performance Optimization

  1. Use specific Git ranges instead of full history when possible
  2. Filter out unnecessary commits early
  3. Parallelize multiple format generation
  4. Cache Git operations when running multiple logsmith commands
  5. Use appropriate verbosity levels (verbose only for debugging)

Next Steps

Released under the MIT License.