feat(config): validate using json schema, compare issue labels for update
This commit is contained in:
parent
8aaa3976d8
commit
272a566acb
|
@ -0,0 +1,11 @@
|
||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [cautious-journey](./cautious-journey.md) > [ChangeSet](./cautious-journey.changeset.md) > [adds](./cautious-journey.changeset.adds.md)
|
||||||
|
|
||||||
|
## ChangeSet.adds property
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
adds: Array<LabelRef>;
|
||||||
|
```
|
|
@ -0,0 +1,22 @@
|
||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [cautious-journey](./cautious-journey.md) > [ChangeSet](./cautious-journey.changeset.md)
|
||||||
|
|
||||||
|
## ChangeSet interface
|
||||||
|
|
||||||
|
A set of labels to add and/or remove.
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface ChangeSet
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| [adds](./cautious-journey.changeset.adds.md) | Array<LabelRef> | |
|
||||||
|
| [removes](./cautious-journey.changeset.removes.md) | Array<LabelRef> | |
|
||||||
|
| [requires](./cautious-journey.changeset.requires.md) | Array<LabelRef> | Required labels for this state change to occur. |
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [cautious-journey](./cautious-journey.md) > [ChangeSet](./cautious-journey.changeset.md) > [removes](./cautious-journey.changeset.removes.md)
|
||||||
|
|
||||||
|
## ChangeSet.removes property
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
removes: Array<LabelRef>;
|
||||||
|
```
|
|
@ -1,13 +1,13 @@
|
||||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
[Home](./index.md) > [cautious-journey](./cautious-journey.md) > [StateChange](./cautious-journey.statechange.md) > [matches](./cautious-journey.statechange.matches.md)
|
[Home](./index.md) > [cautious-journey](./cautious-journey.md) > [ChangeSet](./cautious-journey.changeset.md) > [requires](./cautious-journey.changeset.requires.md)
|
||||||
|
|
||||||
## StateChange.matches property
|
## ChangeSet.requires property
|
||||||
|
|
||||||
Required labels for this state change to occur.
|
Required labels for this state change to occur.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
matches: Array<LabelRef>;
|
requires: Array<LabelRef>;
|
||||||
```
|
```
|
|
@ -23,11 +23,11 @@
|
||||||
|
|
||||||
| Interface | Description |
|
| Interface | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
|
| [ChangeSet](./cautious-journey.changeset.md) | A set of labels to add and/or remove. |
|
||||||
| [Remote](./cautious-journey.remote.md) | Basic functions which every remote API must provide. |
|
| [Remote](./cautious-journey.remote.md) | Basic functions which every remote API must provide. |
|
||||||
| [RemoteOptions](./cautious-journey.remoteoptions.md) | |
|
| [RemoteOptions](./cautious-journey.remoteoptions.md) | |
|
||||||
| [ResolveInput](./cautious-journey.resolveinput.md) | Collected inputs for a resolver run. |
|
| [ResolveInput](./cautious-journey.resolveinput.md) | Collected inputs for a resolver run. |
|
||||||
| [ResolveResult](./cautious-journey.resolveresult.md) | Collected results from a resolver run. |
|
| [ResolveResult](./cautious-journey.resolveresult.md) | Collected results from a resolver run. |
|
||||||
| [StateChange](./cautious-journey.statechange.md) | The transition between two state values. |
|
|
||||||
| [StateLabel](./cautious-journey.statelabel.md) | Grouped labels: the equivalent of a radio group. |
|
| [StateLabel](./cautious-journey.statelabel.md) | Grouped labels: the equivalent of a radio group. |
|
||||||
| [StateValue](./cautious-journey.statevalue.md) | One of many values for a particular state. |
|
| [StateValue](./cautious-journey.statevalue.md) | One of many values for a particular state. |
|
||||||
| [SyncOptions](./cautious-journey.syncoptions.md) | |
|
| [SyncOptions](./cautious-journey.syncoptions.md) | |
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
|
||||||
|
|
||||||
[Home](./index.md) > [cautious-journey](./cautious-journey.md) > [StateChange](./cautious-journey.statechange.md)
|
|
||||||
|
|
||||||
## StateChange interface
|
|
||||||
|
|
||||||
The transition between two state values.
|
|
||||||
|
|
||||||
<b>Signature:</b>
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export interface StateChange extends ChangeSet
|
|
||||||
```
|
|
||||||
<b>Extends:</b> ChangeSet
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| [matches](./cautious-journey.statechange.matches.md) | Array<LabelRef> | Required labels for this state change to occur. |
|
|
||||||
|
|
|
@ -9,5 +9,5 @@ State changes that could occur to this value.
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
becomes: Array<StateChange>;
|
becomes: Array<ChangeSet>;
|
||||||
```
|
```
|
||||||
|
|
|
@ -11,11 +11,11 @@ One of many values for a particular state.
|
||||||
```typescript
|
```typescript
|
||||||
export interface StateValue extends BaseLabel, ChangeSet
|
export interface StateValue extends BaseLabel, ChangeSet
|
||||||
```
|
```
|
||||||
<b>Extends:</b> BaseLabel, ChangeSet
|
<b>Extends:</b> BaseLabel, [ChangeSet](./cautious-journey.changeset.md)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| [becomes](./cautious-journey.statevalue.becomes.md) | Array<[StateChange](./cautious-journey.statechange.md)<!-- -->> | State changes that could occur to this value. |
|
| [becomes](./cautious-journey.statevalue.becomes.md) | Array<[ChangeSet](./cautious-journey.changeset.md)<!-- -->> | State changes that could occur to this value. |
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import Ajv from 'ajv';
|
||||||
import { LogLevel, NullLogger } from 'noicejs';
|
import { LogLevel, NullLogger } from 'noicejs';
|
||||||
|
|
||||||
import { FlagLabel, StateLabel } from '../labels';
|
import { FlagLabel, StateLabel } from '../labels';
|
||||||
import { RemoteOptions } from '../remote';
|
import { RemoteOptions } from '../remote';
|
||||||
|
import * as SCHEMA_DATA from './schema.yml';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config data for the app, loaded from CLI or DOM.
|
* Config data for the app, loaded from CLI or DOM.
|
||||||
|
@ -64,3 +66,21 @@ export function initConfig(): ConfigData {
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SCHEMA_OPTIONS: Ajv.Options = {
|
||||||
|
allErrors: true,
|
||||||
|
coerceTypes: 'array',
|
||||||
|
missingRefs: 'fail',
|
||||||
|
removeAdditional: 'failing',
|
||||||
|
schemaId: 'auto',
|
||||||
|
useDefaults: true,
|
||||||
|
verbose: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function validateConfig(it: unknown): it is ConfigData {
|
||||||
|
const ajv = new Ajv(SCHEMA_OPTIONS);
|
||||||
|
ajv.addSchema(SCHEMA_DATA, 'cautious-journey');
|
||||||
|
|
||||||
|
return ajv.validate('cautious-journey#/definitions/config', it) === true;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
$schema: "http://json-schema.org/schema#"
|
||||||
|
$id: cautious-journey
|
||||||
|
definitions:
|
||||||
|
label-ref:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
change-set:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
adds:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/label-ref"
|
||||||
|
default: []
|
||||||
|
removes:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/label-ref"
|
||||||
|
default: []
|
||||||
|
|
||||||
|
base-label:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
color:
|
||||||
|
type: string
|
||||||
|
desc:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
requires:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/label-ref"
|
||||||
|
default: []
|
||||||
|
|
||||||
|
flag-label:
|
||||||
|
allOf:
|
||||||
|
- $ref: "#/definitions/change-set"
|
||||||
|
- $ref: "#/definitions/base-label"
|
||||||
|
|
||||||
|
state-label:
|
||||||
|
allOf:
|
||||||
|
- $ref: "#/definitions/change-set"
|
||||||
|
- $ref: "#/definitions/base-label"
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
values:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/state-value"
|
||||||
|
default: []
|
||||||
|
|
||||||
|
state-value:
|
||||||
|
allOf:
|
||||||
|
- $ref: "#/definitions/change-set"
|
||||||
|
- $ref: "#/definitions/base-label"
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
becomes:
|
||||||
|
type: array
|
||||||
|
default: []
|
||||||
|
|
||||||
|
config:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
logger:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
level:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
projects:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
colors:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
flags:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/flag-label"
|
||||||
|
default: []
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
remote:
|
||||||
|
type: object
|
||||||
|
states:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/state-label"
|
||||||
|
default: []
|
||||||
|
|
||||||
|
type: object
|
|
@ -1,6 +1,6 @@
|
||||||
import { main } from './main';
|
import { main } from './main';
|
||||||
|
|
||||||
export { FlagLabel, StateChange, StateLabel, StateValue } from './labels';
|
export { ChangeSet, FlagLabel, StateLabel, StateValue } from './labels';
|
||||||
export { Remote, RemoteOptions } from './remote';
|
export { Remote, RemoteOptions } from './remote';
|
||||||
export { GithubRemote } from './remote/github';
|
export { GithubRemote } from './remote/github';
|
||||||
export { GitlabRemote } from './remote/gitlab';
|
export { GitlabRemote } from './remote/gitlab';
|
||||||
|
|
|
@ -16,6 +16,11 @@ export interface LabelRef {
|
||||||
export interface ChangeSet {
|
export interface ChangeSet {
|
||||||
adds: Array<LabelRef>;
|
adds: Array<LabelRef>;
|
||||||
removes: Array<LabelRef>;
|
removes: Array<LabelRef>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required labels for this state change to occur.
|
||||||
|
*/
|
||||||
|
requires: Array<LabelRef>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,8 +46,6 @@ export interface BaseLabel extends ChangeSet {
|
||||||
* Label priority.
|
* Label priority.
|
||||||
*/
|
*/
|
||||||
priority: number;
|
priority: number;
|
||||||
|
|
||||||
requires: Array<LabelRef>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,16 +53,6 @@ export interface BaseLabel extends ChangeSet {
|
||||||
*/
|
*/
|
||||||
export type FlagLabel = BaseLabel;
|
export type FlagLabel = BaseLabel;
|
||||||
|
|
||||||
/**
|
|
||||||
* The transition between two state values.
|
|
||||||
*/
|
|
||||||
export interface StateChange extends ChangeSet {
|
|
||||||
/**
|
|
||||||
* Required labels for this state change to occur.
|
|
||||||
*/
|
|
||||||
matches: Array<LabelRef>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* One of many values for a particular state.
|
* One of many values for a particular state.
|
||||||
*/
|
*/
|
||||||
|
@ -67,7 +60,7 @@ export interface StateValue extends BaseLabel, ChangeSet {
|
||||||
/**
|
/**
|
||||||
* State changes that could occur to this value.
|
* State changes that could occur to this value.
|
||||||
*/
|
*/
|
||||||
becomes: Array<StateChange>;
|
becomes: Array<ChangeSet>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { DEFAULT_SAFE_SCHEMA, safeLoad } from 'js-yaml';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { alea } from 'seedrandom';
|
import { alea } from 'seedrandom';
|
||||||
|
|
||||||
import { ConfigData } from './config';
|
import { ConfigData, validateConfig } from './config';
|
||||||
import { Commands, createParser } from './config/args';
|
import { Commands, createParser } from './config/args';
|
||||||
import { BunyanLogger } from './logger/bunyan';
|
import { BunyanLogger } from './logger/bunyan';
|
||||||
import { GithubRemote } from './remote/github';
|
import { GithubRemote } from './remote/github';
|
||||||
|
@ -36,7 +36,7 @@ async function loadConfig(path: string): Promise<ConfigData> {
|
||||||
});
|
});
|
||||||
const config = safeLoad(rawConfig, { schema });
|
const config = safeLoad(rawConfig, { schema });
|
||||||
|
|
||||||
if (isNil(config) || typeof config === 'string') {
|
if (!validateConfig(config)) {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ export class GithubRemote implements Remote {
|
||||||
const repo = await mustExist(this.request).issues.listForRepo(path);
|
const repo = await mustExist(this.request).issues.listForRepo(path);
|
||||||
for (const issue of repo.data) {
|
for (const issue of repo.data) {
|
||||||
issues.push({
|
issues.push({
|
||||||
issue: issue.id.toString(10),
|
issue: issue.number.toString(),
|
||||||
labels: issue.labels.map((l) => l.name),
|
labels: issue.labels.map((l) => l.name),
|
||||||
name: issue.title,
|
name: issue.title,
|
||||||
project: options.project,
|
project: options.project,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { prng } from 'seedrandom';
|
||||||
import { FlagLabel, getLabelColor, getLabelNames, getValueName, StateLabel } from './labels';
|
import { FlagLabel, getLabelColor, getLabelNames, getValueName, StateLabel } from './labels';
|
||||||
import { LabelUpdate, Remote } from './remote';
|
import { LabelUpdate, Remote } from './remote';
|
||||||
import { resolveLabels } from './resolve';
|
import { resolveLabels } from './resolve';
|
||||||
import { defaultTo, defaultUntil } from './utils';
|
import { defaultTo, defaultUntil, compareItems } from './utils';
|
||||||
|
|
||||||
export interface SyncOptions {
|
export interface SyncOptions {
|
||||||
/**
|
/**
|
||||||
|
@ -45,8 +45,11 @@ export async function syncIssueLabels(options: SyncOptions): Promise<unknown> {
|
||||||
states: options.states,
|
states: options.states,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
options.logger.debug({ changes, errors, issue, labels }, 'resolved labels');
|
||||||
|
|
||||||
// TODO: prompt user to update this particular issue
|
// TODO: prompt user to update this particular issue
|
||||||
if (changes.length > 0 && errors.length === 0) {
|
const sameLabels = !compareItems(issue.labels, labels) || changes.length > 0;
|
||||||
|
if (sameLabels && errors.length === 0) {
|
||||||
options.logger.info({ issue, labels }, 'updating issue');
|
options.logger.info({ issue, labels }, 'updating issue');
|
||||||
await options.remote.updateIssue({
|
await options.remote.updateIssue({
|
||||||
...issue,
|
...issue,
|
||||||
|
@ -143,7 +146,7 @@ export async function syncLabelDiff(options: SyncOptions, oldLabel: LabelUpdate,
|
||||||
project: options.project,
|
project: options.project,
|
||||||
};
|
};
|
||||||
|
|
||||||
options.logger.debug({ body, newLabel, oldLabel, options }, 'update label');
|
options.logger.debug({ body, newLabel, oldLabel }, 'update label');
|
||||||
|
|
||||||
const resp = await options.remote.updateLabel(body);
|
const resp = await options.remote.updateLabel(body);
|
||||||
|
|
||||||
|
|
17
src/utils.ts
17
src/utils.ts
|
@ -18,3 +18,20 @@ export function randomItem<T>(items: Array<T>, source: prng): T {
|
||||||
const idx = Math.abs(source.int32()) % items.length;
|
const idx = Math.abs(source.int32()) % items.length;
|
||||||
return items[idx];
|
return items[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function compareItems<T>(a: Array<T>, b: Array<T>): boolean {
|
||||||
|
if (a.length !== b.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aSorted = a.sort();
|
||||||
|
const bSorted = b.sort();
|
||||||
|
|
||||||
|
for (let i = 0; i < aSorted.length; ++i) {
|
||||||
|
if (aSorted[i] !== bSorted[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue