Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,40 @@ with: |
```
- GitHub Actions has several bugs impacting nested composite actions (e.g. https://github.com/actions/runner/issues/2800, https://github.com/actions/runner/issues/2009). When you use dynamic-uses to call another composite action, these bugs can cause problems like blank/wrong `inputs` or `ouputs` within that action. As a workaround, you can try passing data along with `GITHUB_ENV` instead.

## dynamic-uses/matrix

This is a variant of `dynamic-uses` that allows you to call another action multiple times with different parameters, using a matrix pattern. Each item in the matrix is passed as separate input parameters to the action, rather than being treated as a single `matrix` input.

This version doesn't support capturing outputs yet. It also runs everything in sequence, not in parallel. For parallelism, you need to use different jobs, which already support matrix on their own.

### When would I use this?

When you want to run the same action multiple times with different configurations. For example:
- Building multiple sub-applications with different settings
- Processing multiple items with the same action

### Usage

Deploy multiple microservices to different environments:

```yaml
- uses: jenseng/dynamic-uses/matrix@matrix-action
with:
uses: my-org/deploy-service@v1
matrix: |
[
{"service": "api", "env": "staging"},
{"service": "web", "env": "staging"},
{"service": "api", "env": "production"},
{"service": "web", "env": "production"}
]
with: |
region: us-east-1
registry: ${{ secrets.REGISTRY }}
```

This will deploy 4 services in sequence, each with the appropriate `service` and `env`, while sharing the same `region` and `registry` credentials.

## License

The scripts and documentation in this project are released under the [ISC License](./LICENSE.md)
62 changes: 62 additions & 0 deletions matrix/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Dynamic Uses
description: Dynamically resolve and use another GitHub action. Workaround for https://github.com/actions/runner/issues/895
author: Jon Jensen
inputs:
uses:
description: Action reference or path, e.g. `actions/setup-node@v3`
required: true
matrix:
description: 'Array of objects for matrix, e.g. `[{"node": "18"}, {"node": "20"}]` - values accessible as MATRIX_FIELD in env'
required: false
default: "[]"
with:
description: 'YAML/JSON string of `inputs` for the action (shared across all matrix items), e.g. `node-version: 18` or `{"node-version": "18"}`'
required: false
default: "{}"
runs:
using: composite
steps:
- name: Setup
shell: bash
env:
uses: ${{ inputs.uses }}
matrix: ${{ inputs.matrix }}
with: ${{ inputs.with }}
run: |
mkdir -p ./.tmp-dynamic-uses

# Initialize the action.yml file with the header
cat > ./.tmp-dynamic-uses/action.yml <<DYNAMIC_USES_EOF
runs:
using: composite
steps:
- run: rm -rf ./.tmp-dynamic-uses
shell: bash
DYNAMIC_USES_EOF

# Count matrix items
count=$(echo "$matrix" | jq 'if type == "array" then length else 0 end')

# Generate a Run-N step for each item in the matrix array
echo "$matrix" | jq -r 'if type == "array" then . else [] end | to_entries | .[] | @base64' | while IFS= read -r entry_b64; do
entry=$(echo "$entry_b64" | base64 -d)
index=$(echo "$entry" | jq -r '.key')
matrix_item=$(echo "$entry" | jq -c '.value')

# Append the step to the file
cat >> ./.tmp-dynamic-uses/action.yml <<DYNAMIC_USES_EOF
- name: Run-$index
id: run-$index
uses: '$(echo "$uses" | sed "s/'/''/g")'
with:
$(echo "$matrix_item" | jq -r 'to_entries[] | " \(.key): " + (.value | @json)')
$(echo "$with" | sed -e 's/^/ /' -e '/^[[:space:]]*$/d')
DYNAMIC_USES_EOF
done
- name: Run
id: run
uses: ./.tmp-dynamic-uses
- name: Cleanup
if: always() && steps.run.outcome != 'success'
shell: bash
run: rm -rf ./.tmp-dynamic-uses