Skip to content

Instantly share code, notes, and snippets.

@thlorenz
Last active August 16, 2023 03:25
Show Gist options
  • Save thlorenz/5c044652333031c82886551be8f3b637 to your computer and use it in GitHub Desktop.
Save thlorenz/5c044652333031c82886551be8f3b637 to your computer and use it in GitHub Desktop.
script: Dorny path filter generator (derived from deps of the provided package)
import path from 'path'
// 1. Define root of project from which to resolve all packages
const ROOT = path.resolve(__dirname, '..')
// 2. Define the relative path of the package that we generate the dorny filters for
const PACK = process.argv[2]
if (PACK == null) {
throw new Error(
'Need to supply relative path argument for package you want to render dorny filter for'
)
}
const PACKAGE_PATH = path.join(ROOT, PACK)
// NOTE: adapt the below to your project
const TASK = 'build-test-lint'
const LOCAL_PREFIX = '@my-platform/'
// 3. Read the package and derive Job labels from its name
const pack = require(`${PACKAGE_PATH}/package.json`)
const jobLabel = pack.name.replace(LOCAL_PREFIX, '').replace(/-/g, '_')
const changesJobLabel = `${jobLabel}_changes`
const taskJobLabel = `${jobLabel}_${TASK}`
// 4. Extract local deps and sort them and resolve their relative path
const deps = Object.keys(pack.dependencies ?? {}).filter((dep) =>
dep.startsWith(LOCAL_PREFIX)
)
const devDeps = Object.keys(pack.devDependencies ?? {}).filter((dep) =>
dep.startsWith(LOCAL_PREFIX)
)
const allDeps = [...deps, ...devDeps]
allDeps.sort()
allDeps.unshift(pack.name)
const resolvedDeps = allDeps.map((dep) => {
const label = dep.replace(LOCAL_PREFIX, '').replace(/-/g, '_')
return { label, relPath: path.relative(ROOT, require.resolve(dep)) }
})
// 5. Render functions for the different section pieces
function dornyOutput({ label }: { label: string }, indent: string) {
return `${indent}${label}: \${{ steps.filter.outputs.${label} }}`
}
function dornyFilter(
{ label, relPath }: { label: string; relPath: string },
indent: string = ' '
) {
// Simplest (naive) way to find package root (one above first `/src`)
// A more sophisticated way, i.e. finding `package.json` could be used
const idxOfSrc = relPath.indexOf('/src')
const packageRoot = relPath.slice(0, idxOfSrc)
return `${indent}${label}:\n` + `${indent} - './${packageRoot}/**'`
}
function dornyNeeds({ label }: { label: string }, indent: string) {
return `${indent}needs.${changesJobLabel}.outputs.${label} == 'true'`
}
const packOutputs = resolvedDeps.map((x) => dornyOutput(x, ' ')).join('\n')
const packFilters = resolvedDeps
.map((x) => dornyFilter(x, ' '))
.join('\n')
const taskNeeds = resolvedDeps.map((x) => dornyNeeds(x, ' ')).join(' ||\n')
// 6. Piece it all together
const changesJob = `
jobs:
${changesJobLabel}:
runs-on:
group: Default
outputs:
${packOutputs}
action: \${{ steps.filter.outputs.action }}
workflow: \${{ steps.filter.outputs.workflow }}
steps:
- uses: actions/checkout@master
if: github.event_name != 'pull_request'
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
${packFilters}
workflow:
- '.github/workflows/${jobLabel}.yml'
action:
- '.github/actions/build-test-lint/action.yml'
${taskJobLabel}:
needs: ${changesJobLabel}
if: \${{
${taskNeeds} ||
needs.account_runtime_changes.outputs.action == 'true' ||
needs.account_runtime_changes.outputs.workflow == 'true'
}}
`
console.log(changesJob)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment