stub more docs, check for errors in issue sync, sort labels by priority and name
This commit is contained in:
parent
b404ecf62e
commit
7bb4e95875
|
@ -42,7 +42,6 @@ const bundle = {
|
||||||
},
|
},
|
||||||
manualChunks(id) {
|
manualChunks(id) {
|
||||||
if (id.includes(`${sep}test${sep}`)) {
|
if (id.includes(`${sep}test${sep}`)) {
|
||||||
console.log(id, 'belongs to test chunk');
|
|
||||||
return 'test';
|
return 'test';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +54,6 @@ const bundle = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testModules.some(mod => id.includes(`${sep}${mod}${sep}`))) {
|
if (testModules.some(mod => id.includes(`${sep}${mod}${sep}`))) {
|
||||||
console.log(id, 'belongs to test chunk');
|
|
||||||
return 'test';
|
return 'test';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
| Function | Description |
|
| Function | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| [resolveLabels(options)](./cautious-journey.resolvelabels.md) | |
|
| [resolveLabels(options)](./cautious-journey.resolvelabels.md) | Resolve the desired set of labels, given a starting set and the flags/states to be applied. |
|
||||||
| [syncIssues(options)](./cautious-journey.syncissues.md) | |
|
| [syncIssues(options)](./cautious-journey.syncissues.md) | |
|
||||||
| [syncLabels(options)](./cautious-journey.synclabels.md) | |
|
| [syncLabels(options)](./cautious-journey.synclabels.md) | |
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
## resolveLabels() function
|
## resolveLabels() function
|
||||||
|
|
||||||
|
Resolve the desired set of labels, given a starting set and the flags/states to be applied.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
|
|
@ -7,5 +7,5 @@
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
errors: Array<unknown>;
|
errors: Array<ErrorRecord>;
|
||||||
```
|
```
|
||||||
|
|
|
@ -17,6 +17,6 @@ export interface ResolveResult
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| [changes](./cautious-journey.resolveresult.changes.md) | Array<ChangeRecord> | |
|
| [changes](./cautious-journey.resolveresult.changes.md) | Array<ChangeRecord> | |
|
||||||
| [errors](./cautious-journey.resolveresult.errors.md) | Array<unknown> | |
|
| [errors](./cautious-journey.resolveresult.errors.md) | Array<ErrorRecord> | |
|
||||||
| [labels](./cautious-journey.resolveresult.labels.md) | Array<string> | |
|
| [labels](./cautious-journey.resolveresult.labels.md) | Array<string> | |
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ projects:
|
||||||
privateKey: |
|
privateKey: |
|
||||||
KEY MATERIAL HERE
|
KEY MATERIAL HERE
|
||||||
token: ''
|
token: ''
|
||||||
type: 'installation'
|
type: 'app'
|
||||||
type: 'github'
|
type: 'github'
|
||||||
states:
|
states:
|
||||||
- name: scope
|
- name: scope
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
# Getting Started
|
||||||
|
|
||||||
|
This guide explains how to start using `cautious-journey` to manage project and issue labels.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
- [Getting Started](#getting-started)
|
||||||
|
- [Contents](#contents)
|
||||||
|
- [Setup](#setup)
|
||||||
|
- [Installing](#installing)
|
||||||
|
- [Running](#running)
|
||||||
|
- [Configuring](#configuring)
|
||||||
|
- [Labels](#labels)
|
||||||
|
- [Flag Labels](#flag-labels)
|
||||||
|
- [State Labels](#state-labels)
|
||||||
|
- [Projects](#projects)
|
||||||
|
- [Configuring Multiple Projects](#configuring-multiple-projects)
|
||||||
|
- [Running Select Projects](#running-select-projects)
|
||||||
|
- [Remotes](#remotes)
|
||||||
|
- [Copying Project Remotes](#copying-project-remotes)
|
||||||
|
- [Commands](#commands)
|
||||||
|
- [Sync Labels](#sync-labels)
|
||||||
|
- [Sync Issues](#sync-issues)
|
||||||
|
- [Transitions](#transitions)
|
||||||
|
- [Flag Transitions](#flag-transitions)
|
||||||
|
- [State Transitions](#state-transitions)
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Installing
|
||||||
|
|
||||||
|
TODO: how would you install the app?
|
||||||
|
|
||||||
|
### Running
|
||||||
|
|
||||||
|
TODO: how would you run the app?
|
||||||
|
|
||||||
|
### Configuring
|
||||||
|
|
||||||
|
TODO: how would you configure the app?
|
||||||
|
|
||||||
|
## Labels
|
||||||
|
|
||||||
|
TODO: what are labels? what kinds of labels do we support?
|
||||||
|
|
||||||
|
### Flag Labels
|
||||||
|
|
||||||
|
TODO: what are flag labels?
|
||||||
|
|
||||||
|
### State Labels
|
||||||
|
|
||||||
|
TODO: what are state labels?
|
||||||
|
|
||||||
|
## Projects
|
||||||
|
|
||||||
|
TODO: what are projects? how are they managed? how do you pick which projects to run?
|
||||||
|
|
||||||
|
### Configuring Multiple Projects
|
||||||
|
|
||||||
|
TODO: how would you configure the app to run for multiple projects?
|
||||||
|
|
||||||
|
### Running Select Projects
|
||||||
|
|
||||||
|
TODO: how would you run a single project, or a subset?
|
||||||
|
|
||||||
|
## Remotes
|
||||||
|
|
||||||
|
TODO: what are remotes? how are they configured?
|
||||||
|
|
||||||
|
### Copying Project Remotes
|
||||||
|
|
||||||
|
TODO: how would you share a remote config block between two projects?
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
TODO: what commands are present?
|
||||||
|
|
||||||
|
### Sync Labels
|
||||||
|
|
||||||
|
TODO: what happens when you sync the project labels?
|
||||||
|
|
||||||
|
### Sync Issues
|
||||||
|
|
||||||
|
TODO: what happens when you sync the issue labels?
|
||||||
|
|
||||||
|
## Transitions
|
||||||
|
|
||||||
|
### Flag Transitions
|
||||||
|
|
||||||
|
TODO: how are flag labels added and removed?
|
||||||
|
|
||||||
|
### State Transitions
|
||||||
|
|
||||||
|
TODO: how are state labels added and removed?
|
|
@ -95,3 +95,21 @@ export function splitName(name: string): Array<string> {
|
||||||
export function valueName(state: StateLabel, value: StateValue): string {
|
export function valueName(state: StateLabel, value: StateValue): string {
|
||||||
return `${state.name}/${value.name}`;
|
return `${state.name}/${value.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort labels by their priority field, highest first.
|
||||||
|
*
|
||||||
|
* TODO: add some sort options: high-first or low-first, case-sensitivity
|
||||||
|
*/
|
||||||
|
export function prioritizeLabels<TLabel extends BaseLabel>(labels: Array<TLabel>): Array<TLabel> {
|
||||||
|
return labels.sort((a, b) => {
|
||||||
|
if (a.priority === b.priority) {
|
||||||
|
const aName = a.name.toLocaleLowerCase();
|
||||||
|
const bName = b.name.toLocaleLowerCase();
|
||||||
|
return aName.localeCompare(bName);
|
||||||
|
} else {
|
||||||
|
// B first for high-to-low
|
||||||
|
return b.priority - a.priority;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { FlagLabel, StateLabel, valueName } from './labels';
|
import { FlagLabel, prioritizeLabels, StateLabel, valueName } from './labels';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How a label changed.
|
* How a label changed.
|
||||||
*/
|
*/
|
||||||
export enum ChangeEffect {
|
export enum ChangeVerb {
|
||||||
EXISTING = 'existing',
|
EXISTING = 'existing',
|
||||||
CREATED = 'created',
|
CREATED = 'created',
|
||||||
REMOVED = 'removed',
|
REMOVED = 'removed',
|
||||||
|
@ -14,8 +14,24 @@ export enum ChangeEffect {
|
||||||
* Details of a label change.
|
* Details of a label change.
|
||||||
*/
|
*/
|
||||||
export interface ChangeRecord {
|
export interface ChangeRecord {
|
||||||
|
/**
|
||||||
|
* The label which caused this change.
|
||||||
|
*/
|
||||||
cause: string;
|
cause: string;
|
||||||
effect: ChangeEffect;
|
|
||||||
|
/**
|
||||||
|
* How the label was changed.
|
||||||
|
*/
|
||||||
|
effect: ChangeVerb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The label being changed.
|
||||||
|
*/
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ErrorRecord {
|
||||||
|
error: Error;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,16 +49,20 @@ export interface ResolveInput {
|
||||||
*/
|
*/
|
||||||
export interface ResolveResult {
|
export interface ResolveResult {
|
||||||
changes: Array<ChangeRecord>;
|
changes: Array<ChangeRecord>;
|
||||||
errors: Array<unknown>;
|
errors: Array<ErrorRecord>;
|
||||||
labels: Array<string>;
|
labels: Array<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the desired set of labels, given a starting set and the flags/states to be
|
||||||
|
* applied.
|
||||||
|
*/
|
||||||
export function resolveLabels(options: ResolveInput): ResolveResult {
|
export function resolveLabels(options: ResolveInput): ResolveResult {
|
||||||
const activeLabels = new Set(options.labels);
|
const activeLabels = new Set(options.labels);
|
||||||
const changes: Array<ChangeRecord> = [];
|
const changes: Array<ChangeRecord> = [];
|
||||||
const errors: Array<unknown> = [];
|
const errors: Array<ErrorRecord> = [];
|
||||||
|
|
||||||
const sortedFlags = options.flags.sort((a, b) => a.priority - b.priority);
|
const sortedFlags = prioritizeLabels(options.flags);
|
||||||
for (const flag of sortedFlags) {
|
for (const flag of sortedFlags) {
|
||||||
const { name } = flag;
|
const { name } = flag;
|
||||||
if (activeLabels.has(name)) {
|
if (activeLabels.has(name)) {
|
||||||
|
@ -51,11 +71,13 @@ export function resolveLabels(options: ResolveInput): ResolveResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedStates = options.states.sort((a, b) => a.priority - b.priority);
|
const sortedStates = prioritizeLabels(options.states);
|
||||||
for (const state of sortedStates) {
|
for (const state of sortedStates) {
|
||||||
for (const value of state.values) {
|
const sortedValues = prioritizeLabels(state.values);
|
||||||
|
for (const value of sortedValues) {
|
||||||
const name = valueName(state, value);
|
const name = valueName(state, value);
|
||||||
if (activeLabels.has(name)) {
|
if (activeLabels.has(name)) {
|
||||||
|
// TODO: check higher-priority values
|
||||||
// TODO: check removes
|
// TODO: check removes
|
||||||
// TODO: check requires
|
// TODO: check requires
|
||||||
// TODO: check becomes
|
// TODO: check becomes
|
||||||
|
|
|
@ -29,7 +29,7 @@ export async function syncIssues(options: SyncOptions): Promise<unknown> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: prompt user to update this particular issue
|
// TODO: prompt user to update this particular issue
|
||||||
if (resolution.changes.length > 0) {
|
if (resolution.changes.length > 0 && resolution.errors.length === 0) {
|
||||||
options.logger.info({ issue, resolution }, 'updating issue');
|
options.logger.info({ issue, resolution }, 'updating issue');
|
||||||
await options.remote.updateIssue({
|
await options.remote.updateIssue({
|
||||||
...issue,
|
...issue,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
|
|
||||||
import { getLabelNames } from '../src/labels';
|
import { getLabelNames, prioritizeLabels } from '../src/labels';
|
||||||
|
|
||||||
describe('labels', () => {
|
describe('label helpers', () => {
|
||||||
describe('label name helper', () => {
|
describe('label name helper', () => {
|
||||||
it('should return an empty set', () => {
|
it('should return an empty set', () => {
|
||||||
expect(getLabelNames([], []).size).to.equal(0);
|
expect(getLabelNames([], []).size).to.equal(0);
|
||||||
|
@ -58,4 +58,38 @@ describe('labels', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('prioritize labels helper', () => {
|
||||||
|
it('should sort by priority', () => {
|
||||||
|
const HIGH_LABEL = {
|
||||||
|
name: 'high',
|
||||||
|
priority: 5,
|
||||||
|
requires: [],
|
||||||
|
};
|
||||||
|
const LOW_LABEL = {
|
||||||
|
name: 'low',
|
||||||
|
priority: 1,
|
||||||
|
requires: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const sorted = prioritizeLabels([LOW_LABEL, HIGH_LABEL]);
|
||||||
|
expect(sorted[0]).to.deep.equal(HIGH_LABEL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sort by name when priority is equal', () => {
|
||||||
|
const FIRST_LABEL = {
|
||||||
|
name: 'label-a',
|
||||||
|
priority: 1,
|
||||||
|
requires: [],
|
||||||
|
};
|
||||||
|
const SECOND_LABEL = {
|
||||||
|
name: 'label-b',
|
||||||
|
priority: 1,
|
||||||
|
requires: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const sorted = prioritizeLabels([SECOND_LABEL, FIRST_LABEL]);
|
||||||
|
expect(sorted[0]).to.deep.equal(FIRST_LABEL);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,8 +3,30 @@ import { expect } from 'chai';
|
||||||
import { resolveLabels } from '../src/resolve';
|
import { resolveLabels } from '../src/resolve';
|
||||||
import { TEST_CASES } from './resolve/cases';
|
import { TEST_CASES } from './resolve/cases';
|
||||||
|
|
||||||
|
const TEST_LABELS = ['foo', 'bar'];
|
||||||
|
|
||||||
describe('resolve labels', () => {
|
describe('resolve labels', () => {
|
||||||
it('should return the existing labels when no rules are provided');
|
describe('with empty rule set', () => {
|
||||||
|
it('should return the existing labels', () => {
|
||||||
|
const result = resolveLabels({
|
||||||
|
flags: [],
|
||||||
|
labels: TEST_LABELS,
|
||||||
|
states: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.labels).to.deep.equal(TEST_LABELS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not make any changes', () => {
|
||||||
|
const result = resolveLabels({
|
||||||
|
flags: [],
|
||||||
|
labels: TEST_LABELS,
|
||||||
|
states: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.changes.length).to.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// procedural tests
|
// procedural tests
|
||||||
describe('resolver test cases', () => {
|
describe('resolver test cases', () => {
|
||||||
|
|
Loading…
Reference in New Issue