Skip to content

Commit

Permalink
Merge pull request #724 from Financial-Times/configurable-tag-filter
Browse files Browse the repository at this point in the history
feat: allow configuring the circleci tag filter regex
  • Loading branch information
apaleslimghost authored Dec 9, 2024
2 parents 28ba389 + 5696689 commit 1f5dcff
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 18 deletions.
1 change: 1 addition & 0 deletions core/cli/test/__snapshots__/config.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14254,6 +14254,7 @@ exports[`loadConfig should load an invalid config when not validating 1`] = `
"cimgNodeVersions": [
"18.19-browsers",
],
"tagFilterRegex": "/^v\\d+\\.\\d+\\.\\d+(-.+)?/",
},
"plugin": {
"children": [
Expand Down
6 changes: 6 additions & 0 deletions lib/schemas/src/plugins/circleci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export const CircleCISchema = z.object({
.optional()
.describe(
"the Cypress docker image to use. see https://github.com/cypress-io/cypress-docker-images for available images and tags. if this option is present, and you're using the [`circleci-deploy`](../circleci-deploy) plugin, this will override the default `node` executor for the `e2e-test-review` and `e2e-test-staging` jobs."
),
tagFilterRegex: z
.string()
.default(/^v\d+\.\d+\.\d+(-.+)?/.toString())
.describe(
'the regular expression used to match tags for jobs that should run on tag workflows. by default, matches tags that look like `v1.2.3`; if your releases use a different tag format, change this option to match your tags.'
)
})
export type CircleCIOptions = z.infer<typeof CircleCISchema>
Expand Down
9 changes: 5 additions & 4 deletions plugins/circleci/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@ _All properties are optional._

### `@dotcom-tool-kit/circleci`

| Property | Description | Type | Default |
| :----------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------- | :------------------- |
| `cimgNodeVersions` | list of CircleCI [Node.js image versions](https://circleci.com/developer/images/image/cimg/node) to use. if more than one is provided, a [matrix build](https://circleci.com/docs/using-matrix-jobs/) will be generated in your CircleCI config. | `Array<string>` | `["18.19-browsers"]` |
| `cypressImage` | the Cypress docker image to use. see https://github.com/cypress-io/cypress-docker-images for available images and tags. if this option is present, and you're using the [`circleci-deploy`](../circleci-deploy) plugin, this will override the default `node` executor for the `e2e-test-review` and `e2e-test-staging` jobs. | `string` | |
| Property | Description | Type | Default |
| :----------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------- | :-------------------------- |
| `cimgNodeVersions` | list of CircleCI [Node.js image versions](https://circleci.com/developer/images/image/cimg/node) to use. if more than one is provided, a [matrix build](https://circleci.com/docs/using-matrix-jobs/) will be generated in your CircleCI config. | `Array<string>` | `["18.19-browsers"]` |
| `cypressImage` | the Cypress docker image to use. see https://github.com/cypress-io/cypress-docker-images for available images and tags. if this option is present, and you're using the [`circleci-deploy`](../circleci-deploy) plugin, this will override the default `node` executor for the `e2e-test-review` and `e2e-test-staging` jobs. | `string` | |
| `tagFilterRegex` | the regular expression used to match tags for jobs that should run on tag workflows. by default, matches tags that look like `v1.2.3`; if your releases use a different tag format, change this option to match your tags. | `string` | `'/^v\d+\.\d+\.\d+(-.+)?/'` |

_All properties are optional._
<!-- end autogenerated docs -->
35 changes: 21 additions & 14 deletions plugins/circleci/src/circleci-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import type {
CircleCiWorkflow,
CircleCiWorkflowJob
} from '@dotcom-tool-kit/schemas/lib/hooks/circleci'
import type {
CircleCISchema as CircleCiPluginSchema,
} from '@dotcom-tool-kit/schemas/lib/plugins/circleci'
import type { CircleCISchema as CircleCiPluginSchema } from '@dotcom-tool-kit/schemas/lib/plugins/circleci'
import { type Conflict, isConflict } from '@dotcom-tool-kit/conflict'
import { Hook, type HookInstallation } from '@dotcom-tool-kit/base'
import { type Plugin } from '@dotcom-tool-kit/plugin'
Expand Down Expand Up @@ -98,7 +96,7 @@ const matrixBoilerplate = (jobName: string, nodeVersions: string[]) => ({
* tagFilter sets the regex for GitHub release tags: CircleCI will ignore jobs
* when doing a release if the filter isn't made explicit
*/
export const tagFilter = { filters: { tags: { only: `${/^v\d+\.\d+\.\d+(-.+)?/}` } } }
export const tagFilter = (tagFilterRegex: string) => ({ filters: { tags: { only: tagFilterRegex } } })

// helper override to the Lodash mergeWith function with a pre-defined
// customiser that will concatenate arrays rather than overriding them by index
Expand All @@ -109,9 +107,11 @@ const mergeWithConcatenatedArrays = (arg0: unknown, ...args: unknown[]) =>
}
})

const getBaseConfig = (nodeVersions: string[]): CircleCIState => {
const getBaseConfig = (nodeVersions: string[], tagFilterRegex: string): CircleCIState => {
const runsOnMultipleNodeVersions = nodeVersions.length > 1
const setupMatrix = runsOnMultipleNodeVersions ? matrixBoilerplate('tool-kit/setup', nodeVersions) : { executor: 'node' }
const setupMatrix = runsOnMultipleNodeVersions
? matrixBoilerplate('tool-kit/setup', nodeVersions)
: { executor: 'node' }
return {
version: 2.1,
orbs: {
Expand Down Expand Up @@ -148,12 +148,12 @@ const getBaseConfig = (nodeVersions: string[]): CircleCIState => {
}
},
jobs: [
{ checkout: tagFilter },
{ checkout: tagFilter(tagFilterRegex) },
{
'tool-kit/setup': {
...setupMatrix,
requires: ['checkout'],
...tagFilter
...tagFilter(tagFilterRegex)
}
}
]
Expand Down Expand Up @@ -343,7 +343,11 @@ const mergeInstallationResults = (

const toolKitOrbPrefix = (job: string) => `tool-kit/${job}`

const generateJobs = (workflow: CircleCiWorkflow, nodeVersions: string[]): Job[] | undefined => {
const generateJobs = (
workflow: CircleCiWorkflow,
nodeVersions: string[],
tagFilterRegex: string
): Job[] | undefined => {
const runsOnMultipleNodeVersions = nodeVersions.length > 1
return workflow.jobs?.map((job) => {
const splitIntoMatrix = runsOnMultipleNodeVersions && (job.splitIntoMatrix ?? true)
Expand Down Expand Up @@ -371,14 +375,17 @@ const generateJobs = (workflow: CircleCiWorkflow, nodeVersions: string[]): Job[]
return `${requiredOrb}-${splitIntoMatrix ? '<< matrix.executor >>' : 'node'}`
})
},
workflow.runOnRelease && job.runOnRelease ? tagFilter : {},
workflow.runOnRelease && job.runOnRelease ? tagFilter(tagFilterRegex) : {},
job.custom
)
}
})
}

export default class CircleCi extends Hook<{ hook: typeof CircleCiSchema, plugin: typeof CircleCiPluginSchema }, CircleCIState> {
export default class CircleCi extends Hook<
{ hook: typeof CircleCiSchema; plugin: typeof CircleCiPluginSchema },
CircleCIState
> {
circleConfigPath = path.resolve(process.cwd(), '.circleci/config.yml')
private circleConfig?: string
private generatedConfig?: CircleCIState
Expand Down Expand Up @@ -450,7 +457,7 @@ export default class CircleCi extends Hook<{ hook: typeof CircleCiSchema, plugin
}

generateConfig(): CircleCIState {
const nodeVersions = this.pluginOptions.cimgNodeVersions
const { cimgNodeVersions: nodeVersions, tagFilterRegex } = this.pluginOptions

if (!this.generatedConfig) {
const generated: CircleCIStatePartial = {}
Expand All @@ -463,15 +470,15 @@ export default class CircleCi extends Hook<{ hook: typeof CircleCiSchema, plugin
generated.workflows = Object.fromEntries(
this.options.workflows.map((workflow) => {
const generatedJobs = {
jobs: generateJobs(workflow, nodeVersions)
jobs: generateJobs(workflow, nodeVersions, tagFilterRegex)
}
return [workflow.name, mergeWithConcatenatedArrays(generatedJobs, workflow.custom)]
})
)
}
const generatedConfig = mergeWithConcatenatedArrays(
{},
this.options.disableBaseConfig ? {} : getBaseConfig(nodeVersions),
this.options.disableBaseConfig ? {} : getBaseConfig(nodeVersions, tagFilterRegex),
generated,
this.options.custom ?? {}
)
Expand Down

0 comments on commit 1f5dcff

Please sign in to comment.