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. -->
|
||||
|
||||
[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.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
matches: Array<LabelRef>;
|
||||
requires: Array<LabelRef>;
|
||||
```
|
|
@ -23,11 +23,11 @@
|
|||
|
||||
| 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. |
|
||||
| [RemoteOptions](./cautious-journey.remoteoptions.md) | |
|
||||
| [ResolveInput](./cautious-journey.resolveinput.md) | Collected inputs for 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. |
|
||||
| [StateValue](./cautious-journey.statevalue.md) | One of many values for a particular state. |
|
||||
| [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>
|
||||
|
||||
```typescript
|
||||
becomes: Array<StateChange>;
|
||||
becomes: Array<ChangeSet>;
|
||||
```
|
||||
|
|
|
@ -11,11 +11,11 @@ One of many values for a particular state.
|
|||
```typescript
|
||||
export interface StateValue extends BaseLabel, ChangeSet
|
||||
```
|
||||
<b>Extends:</b> BaseLabel, ChangeSet
|
||||
<b>Extends:</b> BaseLabel, [ChangeSet](./cautious-journey.changeset.md)
|
||||
|
||||
## Properties
|
||||
|
||||
| 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 { FlagLabel, StateLabel } from '../labels';
|
||||
import { RemoteOptions } from '../remote';
|
||||
import * as SCHEMA_DATA from './schema.yml';
|
||||
|
||||
/**
|
||||
* 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';
|
||||
|
||||
export { FlagLabel, StateChange, StateLabel, StateValue } from './labels';
|
||||
export { ChangeSet, FlagLabel, StateLabel, StateValue } from './labels';
|
||||
export { Remote, RemoteOptions } from './remote';
|
||||
export { GithubRemote } from './remote/github';
|
||||
export { GitlabRemote } from './remote/gitlab';
|
||||
|
|
|
@ -16,6 +16,11 @@ export interface LabelRef {
|
|||
export interface ChangeSet {
|
||||
adds: 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.
|
||||
*/
|
||||
priority: number;
|
||||
|
||||
requires: Array<LabelRef>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,16 +53,6 @@ export interface BaseLabel extends ChangeSet {
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
@ -67,7 +60,7 @@ export interface StateValue extends BaseLabel, ChangeSet {
|
|||
/**
|
||||
* 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 { alea } from 'seedrandom';
|
||||
|
||||
import { ConfigData } from './config';
|
||||
import { ConfigData, validateConfig } from './config';
|
||||
import { Commands, createParser } from './config/args';
|
||||
import { BunyanLogger } from './logger/bunyan';
|
||||
import { GithubRemote } from './remote/github';
|
||||
|
@ -36,7 +36,7 @@ async function loadConfig(path: string): Promise<ConfigData> {
|
|||
});
|
||||
const config = safeLoad(rawConfig, { schema });
|
||||
|
||||
if (isNil(config) || typeof config === 'string') {
|
||||
if (!validateConfig(config)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ export class GithubRemote implements Remote {
|
|||
const repo = await mustExist(this.request).issues.listForRepo(path);
|
||||
for (const issue of repo.data) {
|
||||
issues.push({
|
||||
issue: issue.id.toString(10),
|
||||
issue: issue.number.toString(),
|
||||
labels: issue.labels.map((l) => l.name),
|
||||
name: issue.title,
|
||||
project: options.project,
|
||||
|
|
|
@ -5,7 +5,7 @@ import { prng } from 'seedrandom';
|
|||
import { FlagLabel, getLabelColor, getLabelNames, getValueName, StateLabel } from './labels';
|
||||
import { LabelUpdate, Remote } from './remote';
|
||||
import { resolveLabels } from './resolve';
|
||||
import { defaultTo, defaultUntil } from './utils';
|
||||
import { defaultTo, defaultUntil, compareItems } from './utils';
|
||||
|
||||
export interface SyncOptions {
|
||||
/**
|
||||
|
@ -45,8 +45,11 @@ export async function syncIssueLabels(options: SyncOptions): Promise<unknown> {
|
|||
states: options.states,
|
||||
});
|
||||
|
||||
options.logger.debug({ changes, errors, issue, labels }, 'resolved labels');
|
||||
|
||||
// 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');
|
||||
await options.remote.updateIssue({
|
||||
...issue,
|
||||
|
@ -143,7 +146,7 @@ export async function syncLabelDiff(options: SyncOptions, oldLabel: LabelUpdate,
|
|||
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);
|
||||
|
||||
|
|
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;
|
||||
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