Skip to content

Extension Patterns

Extend the basic file distribution workflow for more complex scenarios.

General Pattern

These extensions build on the Matrix Distribution pattern. See that page for the underlying concepts.


Multiple File Distribution

- name: Copy files
  run: |
    cp file1.txt target/
    cp file2.yaml target/
    cp file3.md target/

Conditional Distribution

Distribute different files based on repository type:

- name: Check repository type
  id: check_type
  working-directory: target
  run: |
    if [ -f "package.json" ]; then
      echo "type=nodejs" >> $GITHUB_OUTPUT
    elif [ -f "pom.xml" ]; then
      echo "type=java" >> $GITHUB_OUTPUT
    fi

- name: Copy appropriate files
  run: |
    if [ "${{ steps.check_type.outputs.type }}" = "nodejs" ]; then
      cp node-config.json target/
    elif [ "${{ steps.check_type.outputs.type }}" = "java" ]; then
      cp java-config.xml target/
    fi

Template Rendering

Substitute variables in templates:

- name: Render template
  run: |
    sed "s/{{REPO_NAME}}/${{ matrix.repo.name }}/g" template.txt > target/file.txt

Advanced Template Rendering

Use envsubst for multiple variables:

- name: Render template
  env:
    REPO_NAME: ${{ matrix.repo.name }}
    ORG_NAME: your-org
    TIMESTAMP: ${{ github.event.head_commit.timestamp }}
  run: |
    envsubst < template.txt > target/file.txt

Directory Distribution

Distribute entire directories:

- name: Copy directory
  run: |
    cp -r source-dir/* target/destination-dir/

File Transformation

Transform files during distribution:

- name: Transform and copy
  run: |
    # Convert YAML to JSON
    yq -o=json source.yaml > target/config.json

    # Minify JavaScript
    terser source.js -o target/script.min.js

Selective Repository Targeting

Filter repositories by criteria:

- name: Query repositories with topic
  run: |
    REPOS=$(gh api graphql -f query='
    {
      search(query: "org:your-org topic:needs-config", type: REPOSITORY, first: 100) {
        nodes {
          ... on Repository {
            name
            defaultBranchRef { name }
          }
        }
      }
    }' --jq '.data.search.nodes | map({name: .name, default_branch: .defaultBranchRef.name})')