1
0
Fork 0

stub more docs, check for errors in issue sync, sort labels by priority and name

This commit is contained in:
ssube 2020-08-15 09:31:28 -05:00
parent b404ecf62e
commit 7bb4e95875
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
14 changed files with 208 additions and 18 deletions

View File

@ -42,7 +42,6 @@ const bundle = {
},
manualChunks(id) {
if (id.includes(`${sep}test${sep}`)) {
console.log(id, 'belongs to test chunk');
return 'test';
}
@ -55,7 +54,6 @@ const bundle = {
}
if (testModules.some(mod => id.includes(`${sep}${mod}${sep}`))) {
console.log(id, 'belongs to test chunk');
return 'test';
}

View File

@ -15,7 +15,7 @@
| 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) | |
| [syncLabels(options)](./cautious-journey.synclabels.md) | |

View File

@ -4,6 +4,8 @@
## resolveLabels() function
Resolve the desired set of labels, given a starting set and the flags/states to be applied.
<b>Signature:</b>
```typescript

View File

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
errors: Array<unknown>;
errors: Array<ErrorRecord>;
```

View File

@ -17,6 +17,6 @@ export interface ResolveResult
| Property | Type | Description |
| --- | --- | --- |
| [changes](./cautious-journey.resolveresult.changes.md) | Array&lt;ChangeRecord&gt; | |
| [errors](./cautious-journey.resolveresult.errors.md) | Array&lt;unknown&gt; | |
| [errors](./cautious-journey.resolveresult.errors.md) | Array&lt;ErrorRecord&gt; | |
| [labels](./cautious-journey.resolveresult.labels.md) | Array&lt;string&gt; | |

View File

@ -12,7 +12,7 @@ projects:
privateKey: |
KEY MATERIAL HERE
token: ''
type: 'installation'
type: 'app'
type: 'github'
states:
- name: scope

94
docs/getting-started.md Normal file
View File

@ -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?

0
docs/remote/github.md Normal file
View File

0
docs/remote/gitlab.md Normal file
View File

View File

@ -95,3 +95,21 @@ export function splitName(name: string): Array<string> {
export function valueName(state: StateLabel, value: StateValue): string {
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;
}
});
}

View File

@ -1,9 +1,9 @@
import { FlagLabel, StateLabel, valueName } from './labels';
import { FlagLabel, prioritizeLabels, StateLabel, valueName } from './labels';
/**
* How a label changed.
*/
export enum ChangeEffect {
export enum ChangeVerb {
EXISTING = 'existing',
CREATED = 'created',
REMOVED = 'removed',
@ -14,8 +14,24 @@ export enum ChangeEffect {
* Details of a label change.
*/
export interface ChangeRecord {
/**
* The label which caused this change.
*/
cause: string;
effect: ChangeEffect;
/**
* How the label was changed.
*/
effect: ChangeVerb;
/**
* The label being changed.
*/
label: string;
}
export interface ErrorRecord {
error: Error;
label: string;
}
@ -33,16 +49,20 @@ export interface ResolveInput {
*/
export interface ResolveResult {
changes: Array<ChangeRecord>;
errors: Array<unknown>;
errors: Array<ErrorRecord>;
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 {
const activeLabels = new Set(options.labels);
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) {
const { name } = flag;
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 value of state.values) {
const sortedValues = prioritizeLabels(state.values);
for (const value of sortedValues) {
const name = valueName(state, value);
if (activeLabels.has(name)) {
// TODO: check higher-priority values
// TODO: check removes
// TODO: check requires
// TODO: check becomes

View File

@ -29,7 +29,7 @@ export async function syncIssues(options: SyncOptions): Promise<unknown> {
});
// 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');
await options.remote.updateIssue({
...issue,

View File

@ -1,8 +1,8 @@
import { expect } from 'chai';
import { getLabelNames } from '../src/labels';
import { getLabelNames, prioritizeLabels } from '../src/labels';
describe('labels', () => {
describe('label helpers', () => {
describe('label name helper', () => {
it('should return an empty set', () => {
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);
});
});
});

View File

@ -3,8 +3,30 @@ import { expect } from 'chai';
import { resolveLabels } from '../src/resolve';
import { TEST_CASES } from './resolve/cases';
const TEST_LABELS = ['foo', 'bar'];
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
describe('resolver test cases', () => {