Skip to content

Update config to allow for a simpler setup by default #847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
46 changes: 37 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ The match object allows control over the matching options. You can specify the l

The base match object is defined as:
```yml
- changed-files:
- changed-files:
- any-glob-to-any-file: ['list', 'of', 'globs']
- any-glob-to-all-files: ['list', 'of', 'globs']
- all-globs-to-any-file: ['list', 'of', 'globs']
@@ -87,7 +87,35 @@ Documentation:
- any-glob-to-any-file: 'docs/*'
```

If path globs are combined with `!` negation, you can write complex matching rules. See the examples below for more information.
If path globs are combined with `!` negation, you can write complex matching rules. See the examples below for more information.

Furthermore, if a top-level key is omitted, or is `any` then a `changed-files` key without any further options will default to `any-glob-to-any-file`, though if an `all` key is provided it will default to `all-globs-to-all-files`.

For example the following would be the same:
```yml
Documentation:
- changed-files: 'docs/*'
```
and
```yml
Documentation:
- any
- changed-files:
- any-glob-to-any-file: 'docs/*'
```
along with
```yml
Documentation:
- all:
- changed-files: 'docs/*'
```
and
```yml
Documentation:
- all:
- changed-files:
- all-globs-to-all-files: 'docs/*'
```

#### Basic Examples

@@ -125,7 +153,7 @@ Documentation:
- changed-files:
- any-glob-to-any-file: ['docs/*', 'guides/*']

# Add 'Documentation' label to any change to .md files within the entire repository
# Add 'Documentation' label to any change to .md files within the entire repository
Documentation:
- changed-files:
- any-glob-to-any-file: '**/*.md'
@@ -206,10 +234,10 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:

# Label PRs 1, 2, and 3
- uses: actions/labeler@v5
with:
with:
pr-number: |
1
2
@@ -218,9 +246,9 @@ jobs:

**Note:** in normal usage the `pr-number` input is not required as the action will detect the PR number from the workflow context.

#### Outputs
#### Outputs

Labeler provides the following outputs:
Labeler provides the following outputs:

| Name | Description |
|--------------|-----------------------------------------------------------|
@@ -242,13 +270,13 @@ jobs:
steps:
- id: label-the-PR
uses: actions/labeler@v5

- id: run-frontend-tests
if: contains(steps.label-the-PR.outputs.all-labels, 'frontend')
run: |
echo "Running frontend tests..."
# Put your commands for running frontend tests here

- id: run-backend-tests
if: contains(steps.label-the-PR.outputs.all-labels, 'backend')
run: |
20 changes: 14 additions & 6 deletions __tests__/changedFiles.test.ts
Original file line number Diff line number Diff line change
@@ -100,12 +100,20 @@ describe('toChangedFilesMatchConfig', () => {
describe('but the glob pattern config key is not provided', () => {
const config = {'changed-files': ['bar']};

it('throws the error', () => {
expect(() => {
toChangedFilesMatchConfig(config);
}).toThrow(
`The "changed-files" section must have a valid config structure. Please read the action documentation for more information`
);
it('defaults to anyGlobToAnyFile', () => {
const result = toChangedFilesMatchConfig(config);
expect(result).toEqual<ChangedFilesMatchConfig>({
changedFiles: [{anyGlobToAnyFile: ['bar']}]
});
});

describe('and the defaultToAll option is passed', () => {
it('defaults to allGlobToAllFiles', () => {
const result = toChangedFilesMatchConfig(config, true);
expect(result).toEqual<ChangedFilesMatchConfig>({
changedFiles: [{allGlobsToAllFiles: ['bar']}]
});
});
});
});

6 changes: 6 additions & 0 deletions __tests__/fixtures/default_any_and_all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
default_any:
- changed-files: ['glob']

default_all:
- all:
- changed-files: ['glob']
80 changes: 49 additions & 31 deletions __tests__/labeler.test.ts
Original file line number Diff line number Diff line change
@@ -24,37 +24,55 @@ const loadYaml = (filepath: string) => {
};

describe('getLabelConfigMapFromObject', () => {
const yamlObject = loadYaml('__tests__/fixtures/all_options.yml');
const expected = new Map<string, MatchConfig[]>();
expected.set('label1', [
{
any: [
{changedFiles: [{anyGlobToAnyFile: ['glob']}]},
{baseBranch: undefined, headBranch: ['regexp']},
{baseBranch: ['regexp'], headBranch: undefined}
]
},
{
all: [
{changedFiles: [{allGlobsToAllFiles: ['glob']}]},
{baseBranch: undefined, headBranch: ['regexp']},
{baseBranch: ['regexp'], headBranch: undefined}
]
}
]);
expected.set('label2', [
{
any: [
{changedFiles: [{anyGlobToAnyFile: ['glob']}]},
{baseBranch: undefined, headBranch: ['regexp']},
{baseBranch: ['regexp'], headBranch: undefined}
]
}
]);

it('returns a MatchConfig', () => {
const result = getLabelConfigMapFromObject(yamlObject);
expect(result).toEqual(expected);
describe('when all options are present', () => {
const yamlObject = loadYaml('__tests__/fixtures/all_options.yml');
const expected = new Map<string, MatchConfig[]>();
expected.set('label1', [
{
any: [
{changedFiles: [{anyGlobToAnyFile: ['glob']}]},
{baseBranch: undefined, headBranch: ['regexp']},
{baseBranch: ['regexp'], headBranch: undefined}
]
},
{
all: [
{changedFiles: [{allGlobsToAllFiles: ['glob']}]},
{baseBranch: undefined, headBranch: ['regexp']},
{baseBranch: ['regexp'], headBranch: undefined}
]
}
]);
expected.set('label2', [
{
any: [
{changedFiles: [{anyGlobToAnyFile: ['glob']}]},
{baseBranch: undefined, headBranch: ['regexp']},
{baseBranch: ['regexp'], headBranch: undefined}
]
}
]);

it('returns a MatchConfig', () => {
const result = getLabelConfigMapFromObject(yamlObject);
expect(result).toEqual(expected);
});
});

describe('when no any or all key are present', () => {
const yamlObject = loadYaml('__tests__/fixtures/default_any_and_all.yml');
const expected = new Map<string, MatchConfig[]>();
expected.set('default_any', [
{any: [{changedFiles: [{anyGlobToAnyFile: ['glob']}]}]}
]);
expected.set('default_all', [
{all: [{changedFiles: [{allGlobsToAllFiles: ['glob']}]}]}
]);

it('returns a MatchConfig', () => {
const result = getLabelConfigMapFromObject(yamlObject);
expect(result).toEqual(expected);
});
});
});

5 changes: 4 additions & 1 deletion __tests__/main.test.ts
Original file line number Diff line number Diff line change
@@ -37,7 +37,10 @@ const yamlFixtures = {
'branches.yml': fs.readFileSync('__tests__/fixtures/branches.yml'),
'only_pdfs.yml': fs.readFileSync('__tests__/fixtures/only_pdfs.yml'),
'not_supported.yml': fs.readFileSync('__tests__/fixtures/not_supported.yml'),
'any_and_all.yml': fs.readFileSync('__tests__/fixtures/any_and_all.yml')
'any_and_all.yml': fs.readFileSync('__tests__/fixtures/any_and_all.yml'),
'default_any_and_all.yml': fs.readFileSync(
'__tests__/fixtures/default_any_and_all.yml'
)
};

const configureInput = (
18 changes: 14 additions & 4 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -320,7 +320,7 @@ function getLabelConfigMapFromObject(configObject) {
// our config objects.
if (key === 'any' || key === 'all') {
if (Array.isArray(value)) {
const newConfigs = value.map(toMatchConfig);
const newConfigs = value.map(config => toMatchConfig(config, key === 'all'));
updatedConfig.push({ [key]: newConfigs });
}
}
@@ -349,8 +349,8 @@ function getLabelConfigMapFromObject(configObject) {
}
return labelMap;
}
function toMatchConfig(config) {
const changedFilesConfig = (0, changedFiles_1.toChangedFilesMatchConfig)(config);
function toMatchConfig(config, defaultToAll = false) {
const changedFilesConfig = (0, changedFiles_1.toChangedFilesMatchConfig)(config, defaultToAll);
const branchConfig = (0, branch_1.toBranchMatchConfig)(config);
return Object.assign(Object.assign({}, changedFilesConfig), branchConfig);
}
@@ -663,7 +663,7 @@ function getChangedFiles(client, prNumber) {
return changedFiles;
});
}
function toChangedFilesMatchConfig(config) {
function toChangedFilesMatchConfig(config, defaultToAll = false) {
if (!config['changed-files'] || !config['changed-files'].length) {
return {};
}
@@ -672,6 +672,16 @@ function toChangedFilesMatchConfig(config) {
: [config['changed-files']];
const validChangedFilesConfigs = [];
changedFilesConfigs.forEach(changedFilesConfig => {
if (typeof changedFilesConfig === 'string' ||
(Array.isArray(changedFilesConfig) &&
changedFilesConfig.every(config => typeof config === 'string'))) {
const key = defaultToAll ? 'allGlobsToAllFiles' : 'anyGlobToAnyFile';
const value = typeof changedFilesConfig === 'string'
? [changedFilesConfig]
: changedFilesConfig;
validChangedFilesConfigs.push({ [key]: value });
return;
}
if (!(0, utils_1.isObject)(changedFilesConfig)) {
throw new Error(`The "changed-files" section must have a valid config structure. Please read the action documentation for more information`);
}
11 changes: 8 additions & 3 deletions src/api/get-label-configs.ts
Original file line number Diff line number Diff line change
@@ -83,7 +83,9 @@ export function getLabelConfigMapFromObject(
// our config objects.
if (key === 'any' || key === 'all') {
if (Array.isArray(value)) {
const newConfigs = value.map(toMatchConfig);
const newConfigs = value.map(config =>
toMatchConfig(config, key === 'all')
);
updatedConfig.push({[key]: newConfigs});
}
} else if (ALLOWED_CONFIG_KEYS.includes(key)) {
@@ -115,8 +117,11 @@ export function getLabelConfigMapFromObject(
return labelMap;
}

export function toMatchConfig(config: any): BaseMatchConfig {
const changedFilesConfig = toChangedFilesMatchConfig(config);
export function toMatchConfig(
config: any,
defaultToAll = false
): BaseMatchConfig {
const changedFilesConfig = toChangedFilesMatchConfig(config, defaultToAll);
const branchConfig = toBranchMatchConfig(config);

return {
17 changes: 16 additions & 1 deletion src/changedFiles.ts
Original file line number Diff line number Diff line change
@@ -45,7 +45,8 @@ export async function getChangedFiles(
}

export function toChangedFilesMatchConfig(
config: any
config: any,
defaultToAll = false
): ChangedFilesMatchConfig {
if (!config['changed-files'] || !config['changed-files'].length) {
return {};
@@ -57,6 +58,20 @@ export function toChangedFilesMatchConfig(
const validChangedFilesConfigs: ChangedFilesGlobPatternsConfig[] = [];

changedFilesConfigs.forEach(changedFilesConfig => {
if (
typeof changedFilesConfig === 'string' ||
(Array.isArray(changedFilesConfig) &&
changedFilesConfig.every(config => typeof config === 'string'))
) {
const key = defaultToAll ? 'allGlobsToAllFiles' : 'anyGlobToAnyFile';
const value =
typeof changedFilesConfig === 'string'
? [changedFilesConfig]
: changedFilesConfig;
validChangedFilesConfigs.push({[key]: value});
return;
}

if (!isObject(changedFilesConfig)) {
throw new Error(
`The "changed-files" section must have a valid config structure. Please read the action documentation for more information`