Skip to content

CI/CD Integration

Learn how to integrate gi into your continuous integration and deployment pipelines.

GitHub Actions

Basic Integration

Create a workflow that automatically updates .gitignore files:

# .github/workflows/update-gitignore.yml
name: Update .gitignore

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  update-gitignore:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.11'

    - name: Install gi
      run: |
        pip install gi

    - name: Update .gitignore
      run: |
        gi python node --force

    - name: Check for changes
      id: verify-changed-files
      run: |
        if [ -n "$(git status --porcelain)" ]; then
          echo "changed=true" >> $GITHUB_OUTPUT
        else
          echo "changed=false" >> $GITHUB_OUTPUT
        fi

    - name: Commit changes
      if: steps.verify-changed-files.outputs.changed == 'true'
      run: |
        git config --local user.email "action@github.com"
        git config --local user.name "GitHub Action"
        git add .gitignore
        git commit -m "Update .gitignore with latest templates"
        git push

Multi-Project Workflow

For repositories with multiple projects:

# .github/workflows/update-gitignores.yml
name: Update .gitignore files

on:
  schedule:
    - cron: '0 0 * * 0'  # Weekly on Sunday
  workflow_dispatch:

jobs:
  update-gitignores:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        project:
          - { name: "frontend", templates: "node react" }
          - { name: "backend", templates: "python django" }
          - { name: "mobile", templates: "react-native" }

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.11'

    - name: Install gi
      run: pip install gi

    - name: Update ${{ matrix.project.name }} .gitignore
      run: |
        cd ${{ matrix.project.name }}
        gi ${{ matrix.project.templates }} --force

    - name: Commit changes
      run: |
        git config --local user.email "action@github.com"
        git config --local user.name "GitHub Action"
        git add .
        git diff --staged --quiet || git commit -m "Update ${{ matrix.project.name }} .gitignore"
        git push

Template Validation

Validate .gitignore files in CI:

# .github/workflows/validate-gitignore.yml
name: Validate .gitignore

on:
  pull_request:
    paths:
      - '.gitignore'
      - '**/.gitignore'

jobs:
  validate:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.11'

    - name: Install gi
      run: pip install gi

    - name: Validate .gitignore syntax
      run: |
        # Check if .gitignore files are valid
        find . -name ".gitignore" -exec git check-ignore -v {} \; || true

        # Test with gi doctor
        gi doctor

    - name: Check for outdated templates
      run: |
        # List available templates
        gi list > available_templates.txt

        # Check if current .gitignore uses outdated patterns
        if [ -f .gitignore ]; then
          echo "Current .gitignore patterns:"
          grep -v "^#" .gitignore | grep -v "^$" || true
        fi

GitLab CI

Basic Pipeline

# .gitlab-ci.yml
stages:
  - update-gitignore
  - test

update-gitignore:
  stage: update-gitignore
  image: python:3.11
  script:
    - pip install gi
    - gi python node --force
    - |
      if [ -n "$(git status --porcelain)" ]; then
        git config user.email "ci@gitlab.com"
        git config user.name "GitLab CI"
        git add .gitignore
        git commit -m "Update .gitignore"
        git push origin $CI_COMMIT_REF_NAME
      fi
  only:
    - main
    - develop

Multi-Stage Pipeline

# .gitlab-ci.yml
stages:
  - setup
  - update-gitignore
  - validate
  - test

variables:
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"

cache:
  paths:
    - .cache/pip/
    - ~/.cache/gi/

setup:
  stage: setup
  image: python:3.11
  script:
    - pip install gi
  artifacts:
    paths:
      - ~/.cache/gi/

update-gitignore:
  stage: update-gitignore
  image: python:3.11
  dependencies:
    - setup
  script:
    - gi python node --force
  artifacts:
    paths:
      - .gitignore
    expire_in: 1 hour

validate-gitignore:
  stage: validate
  image: python:3.11
  dependencies:
    - update-gitignore
  script:
    - gi doctor
    - git check-ignore -v .gitignore || true

test:
  stage: test
  image: python:3.11
  dependencies:
    - validate-gitignore
  script:
    - python -m pytest tests/

Jenkins

Pipeline Script

// Jenkinsfile
pipeline {
    agent any

    stages {
        stage('Setup') {
            steps {
                sh 'python3 -m pip install gi'
            }
        }

        stage('Update .gitignore') {
            steps {
                sh 'gi python node --force'
            }
        }

        stage('Validate') {
            steps {
                sh 'gi doctor'
                sh 'git check-ignore -v .gitignore || true'
            }
        }

        stage('Commit Changes') {
            when {
                changeset "**/.gitignore"
            }
            steps {
                sh '''
                    if [ -n "$(git status --porcelain)" ]; then
                        git config user.email "jenkins@example.com"
                        git config user.name "Jenkins"
                        git add .gitignore
                        git commit -m "Update .gitignore"
                        git push origin ${GIT_BRANCH#origin/}
                    fi
                '''
            }
        }
    }

    post {
        always {
            cleanWs()
        }
    }
}

Multi-Configuration Build

// Jenkinsfile
pipeline {
    agent none

    stages {
        stage('Update .gitignore for all projects') {
            parallel {
                stage('Frontend') {
                    agent any
                    steps {
                        dir('frontend') {
                            sh 'python3 -m pip install gi'
                            sh 'gi node react --force'
                        }
                    }
                }

                stage('Backend') {
                    agent any
                    steps {
                        dir('backend') {
                            sh 'python3 -m pip install gi'
                            sh 'gi python django --force'
                        }
                    }
                }

                stage('Mobile') {
                    agent any
                    steps {
                        dir('mobile') {
                            sh 'python3 -m pip install gi'
                            sh 'gi react-native --force'
                        }
                    }
                }
            }
        }
    }
}

Azure DevOps

YAML Pipeline

# azure-pipelines.yml
trigger:
- main
- develop

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: UpdateGitignore
  displayName: 'Update .gitignore'
  jobs:
  - job: UpdateGitignore
    displayName: 'Update .gitignore files'
    steps:
    - task: UsePythonVersion@0
      inputs:
        versionSpec: '3.11'
      displayName: 'Use Python 3.11'

    - script: |
        pip install gi
      displayName: 'Install gi'

    - script: |
        gi python node --force
      displayName: 'Update .gitignore'

    - script: |
        if [ -n "$(git status --porcelain)" ]; then
          git config user.email "azure-pipelines@example.com"
          git config user.name "Azure Pipelines"
          git add .gitignore
          git commit -m "Update .gitignore"
          git push origin $(Build.SourceBranchName)
        fi
      displayName: 'Commit changes'

CircleCI

Configuration

# .circleci/config.yml
version: 2.1

jobs:
  update-gitignore:
    docker:
      - image: cimg/python:3.11
    steps:
      - checkout
      - run:
          name: Install gi
          command: pip install gi
      - run:
          name: Update .gitignore
          command: gi python node --force
      - run:
          name: Commit changes
          command: |
            if [ -n "$(git status --porcelain)" ]; then
              git config user.email "circleci@example.com"
              git config user.name "CircleCI"
              git add .gitignore
              git commit -m "Update .gitignore"
              git push origin $CIRCLE_BRANCH
            fi

workflows:
  version: 2
  update-gitignore:
    jobs:
      - update-gitignore:
          filters:
            branches:
              only:
                - main
                - develop

Best Practices

1. Cache Management

Cache the gi installation and templates:

# GitHub Actions example
- name: Cache gi templates
  uses: actions/cache@v3
  with:
    path: ~/.cache/gi
    key: gi-templates-${{ hashFiles('**/pyproject.toml') }}
    restore-keys: |
      gi-templates-

2. Error Handling

Handle network failures gracefully:

#!/bin/bash
# update-gitignore.sh

set -e

# Try to update .gitignore with retries
for i in {1..3}; do
    if gi python node --force; then
        echo "Successfully updated .gitignore"
        break
    else
        echo "Attempt $i failed, retrying..."
        sleep 5
    fi
done

# Fallback to cached templates if network fails
if [ $? -ne 0 ]; then
    echo "Network failed, using cached templates"
    GI_OFFLINE=true gi python node --force
fi

3. Conditional Updates

Only update when necessary:

# Only run on specific file changes
on:
  push:
    paths:
      - 'pyproject.toml'
      - 'package.json'
      - 'requirements.txt'

4. Security

Use secure methods for authentication:

# Use GitHub token for authentication
- name: Commit changes
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: |
    git config user.email "action@github.com"
    git config user.name "GitHub Action"
    git add .gitignore
    git commit -m "Update .gitignore"
    git push https://$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY.git

5. Notifications

Notify team members of updates:

- name: Notify on Slack
  if: steps.verify-changed-files.outputs.changed == 'true'
  uses: 8398a7/action-slack@v3
  with:
    status: success
    text: '.gitignore has been updated with latest templates'
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Monitoring and Alerting

Health Checks

Monitor the health of your .gitignore files:

#!/bin/bash
# health-check.sh

# Check if .gitignore exists
if [ ! -f .gitignore ]; then
    echo "ERROR: .gitignore file missing"
    exit 1
fi

# Check if .gitignore is not empty
if [ ! -s .gitignore ]; then
    echo "ERROR: .gitignore file is empty"
    exit 1
fi

# Check for common issues
if grep -q "node_modules" .gitignore && [ -d node_modules ]; then
    echo "WARNING: node_modules directory exists but should be ignored"
fi

# Validate with gi doctor
gi doctor

Metrics Collection

Collect metrics about .gitignore usage:

#!/usr/bin/env python3
# metrics.py

import json
import subprocess
from datetime import datetime

def collect_gitignore_metrics():
    """Collect metrics about .gitignore files."""
    metrics = {
        "timestamp": datetime.now().isoformat(),
        "templates_used": [],
        "file_size": 0,
        "line_count": 0
    }

    try:
        # Get template information
        result = subprocess.run(['gi', 'list'], capture_output=True, text=True)
        if result.returncode == 0:
            metrics["available_templates"] = len(result.stdout.strip().split('\n'))

        # Analyze current .gitignore
        with open('.gitignore', 'r') as f:
            content = f.read()
            metrics["file_size"] = len(content)
            metrics["line_count"] = len(content.splitlines())

            # Try to identify templates used
            if 'python' in content.lower():
                metrics["templates_used"].append("python")
            if 'node' in content.lower():
                metrics["templates_used"].append("node")

    except Exception as e:
        metrics["error"] = str(e)

    return metrics

if __name__ == "__main__":
    metrics = collect_gitignore_metrics()
    print(json.dumps(metrics, indent=2))

This comprehensive guide covers integrating gi into various CI/CD platforms and best practices for automated .gitignore management.