diff --git a/docs/api/cautious-journey.syncoptions.colors.md b/docs/api/cautious-journey.syncoptions.colors.md
new file mode 100644
index 0000000..b812f3c
--- /dev/null
+++ b/docs/api/cautious-journey.syncoptions.colors.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [cautious-journey](./cautious-journey.md) > [SyncOptions](./cautious-journey.syncoptions.md) > [colors](./cautious-journey.syncoptions.colors.md)
+
+## SyncOptions.colors property
+
+Signature:
+
+```typescript
+colors: Array;
+```
diff --git a/docs/api/cautious-journey.syncoptions.md b/docs/api/cautious-journey.syncoptions.md
index d6d7900..2817b9b 100644
--- a/docs/api/cautious-journey.syncoptions.md
+++ b/docs/api/cautious-journey.syncoptions.md
@@ -14,6 +14,7 @@ export interface SyncOptions
| Property | Type | Description |
| --- | --- | --- |
+| [colors](./cautious-journey.syncoptions.colors.md) | Array<string> | |
| [flags](./cautious-journey.syncoptions.flags.md) | Array<[FlagLabel](./cautious-journey.flaglabel.md)> | |
| [logger](./cautious-journey.syncoptions.logger.md) | Logger | |
| [project](./cautious-journey.syncoptions.project.md) | string | |
diff --git a/src/labels.ts b/src/labels.ts
index a8a9a03..2154cb7 100644
--- a/src/labels.ts
+++ b/src/labels.ts
@@ -1,3 +1,6 @@
+import { doesExist, InvalidArgumentError } from '@apextoaster/js-utils';
+import { randomItem } from './utils';
+
/**
* A reference to another label.
*/
@@ -113,3 +116,32 @@ export function prioritizeLabels(labels: Array
}
});
}
+
+/**
+ * Pick a label color, preferring the label data if set, falling back to a randomly selected color.
+ *
+ * TODO: this is a terrible overload
+ */
+export function colorizeLabel(flag: FlagLabel, colors: Array): string;
+export function colorizeLabel(state: StateLabel, value: StateValue, colors: Array): string;
+export function colorizeLabel(label: BaseLabel, colorsOrValue: Array | StateValue, maybeColors?: Array): string {
+ if (Array.isArray(colorsOrValue)) {
+ const { color } = label;
+ if (doesExist(color)) {
+ return color;
+ } else {
+ return randomItem(colorsOrValue);
+ }
+ } else {
+ if (!Array.isArray(maybeColors)) {
+ throw new InvalidArgumentError();
+ }
+
+ const { color = colorsOrValue.color } = label;
+ if (doesExist(color)) {
+ return color;
+ } else {
+ return randomItem(maybeColors);
+ }
+ }
+}
diff --git a/src/main.ts b/src/main.ts
index c870c1f..0d7d560 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,4 +1,4 @@
-import { InvalidArgumentError, isNil, doesExist } from '@apextoaster/js-utils';
+import { doesExist, InvalidArgumentError, isNil } from '@apextoaster/js-utils';
import { createSchema } from '@apextoaster/js-yaml-schema';
import { existsSync, readFileSync, realpathSync } from 'fs';
import { DEFAULT_SAFE_SCHEMA, safeLoad } from 'js-yaml';
@@ -57,25 +57,29 @@ export async function main(argv: Array): Promise {
}, 'startup environment');
for (const project of config.projects) {
- if (doesExist(args.project) && !args.project.includes(project.name)) {
- logger.info({ project: project.name }, 'skipping project');
+ const { colors, flags, name, states } = project;
+
+ if (doesExist(args.project) && !args.project.includes(name)) {
+ logger.info({ project: name }, 'skipping project');
continue;
}
const remote = new GithubRemote({
- ...project.remote,
- dryrun: args.dryrun,
+ data: project.remote.data,
+ dryrun: args.dryrun || project.remote.dryrun,
logger,
+ type: project.remote.type,
});
await remote.connect();
// mode switch
const options: SyncOptions = {
- flags: project.flags,
+ colors,
+ flags,
logger,
- project: project.name,
+ project: name,
remote,
- states: project.states,
+ states,
};
switch (mode) {
case Commands.ISSUES:
@@ -85,7 +89,7 @@ export async function main(argv: Array): Promise {
await syncLabels(options);
break;
default:
- throw new InvalidArgumentError('unknown mode');
+ throw new InvalidArgumentError('unknown command');
}
}
diff --git a/src/sync.ts b/src/sync.ts
index 675dc77..6219d5c 100644
--- a/src/sync.ts
+++ b/src/sync.ts
@@ -1,12 +1,13 @@
import { doesExist, mustExist } from '@apextoaster/js-utils';
import { Logger } from 'noicejs';
-import { FlagLabel, getLabelNames, StateLabel, valueName } from './labels';
+import { colorizeLabel, FlagLabel, getLabelNames, StateLabel, valueName } from './labels';
import { LabelUpdate, Remote } from './remote';
import { resolveLabels } from './resolve';
-import { defaultTo } from './utils';
+import { defaultTo, defaultUntil } from './utils';
export interface SyncOptions {
+ colors: Array;
flags: Array;
logger: Logger;
project: string;
@@ -88,7 +89,7 @@ export async function createLabel(options: SyncOptions, name: string) {
const flag = options.flags.find((it) => name === it.name);
if (doesExist(flag)) {
await options.remote.createLabel({
- color: mustExist(flag.color),
+ color: colorizeLabel(flag, options.colors),
desc: mustExist(flag.desc),
name,
project: options.project,
@@ -102,8 +103,8 @@ export async function createLabel(options: SyncOptions, name: string) {
const value = state.values.find((it) => valueName(state, it) === name);
if (doesExist(value)) {
await options.remote.createLabel({
- color: defaultTo(defaultTo(value.color, state.color), ''),
- desc: defaultTo(defaultTo(value.desc, state.desc), ''),
+ color: colorizeLabel(state, value, options.colors),
+ desc: defaultUntil(value.desc, state.desc, ''),
name: valueName(state, value),
project: options.project,
});
@@ -138,7 +139,7 @@ export async function syncSingleLabel(options: SyncOptions, label: LabelUpdate):
const flag = options.flags.find((it) => label.name === it.name);
if (doesExist(flag)) {
await syncLabelDiff(options, label, {
- color: defaultTo(flag.color, label.color),
+ color: colorizeLabel(flag, options.colors),
desc: defaultTo(flag.desc, label.desc),
name: flag.name,
project: options.project,
@@ -152,7 +153,7 @@ export async function syncSingleLabel(options: SyncOptions, label: LabelUpdate):
const value = state.values.find((it) => valueName(state, it) === label.name);
if (doesExist(value)) {
await syncLabelDiff(options, label, {
- color: defaultTo(value.color, label.color),
+ color: colorizeLabel(state, value, options.colors),
desc: defaultTo(value.desc, label.desc),
name: valueName(state, value),
project: options.project,
diff --git a/src/utils.ts b/src/utils.ts
index ee20c81..f25ac25 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,4 +1,4 @@
-import { doesExist, Optional } from '@apextoaster/js-utils';
+import { doesExist, Optional, mustExist } from '@apextoaster/js-utils';
export function defaultTo(a: Optional, b: T): T {
if (doesExist(a)) {
@@ -7,3 +7,13 @@ export function defaultTo(a: Optional, b: T): T {
return b;
}
}
+
+export function defaultUntil(...items: Array>): T {
+ const result = items.reduce(defaultTo, undefined);
+ return mustExist(result);
+}
+
+export function randomItem(items: Array, source = Math.random): T {
+ const idx = Math.floor(source() * items.length);
+ return items[idx];
+}
diff --git a/test/TestUtils.ts b/test/TestUtils.ts
index 91d1ec9..01fdc2e 100644
--- a/test/TestUtils.ts
+++ b/test/TestUtils.ts
@@ -1,6 +1,6 @@
import { expect } from 'chai';
-import { defaultTo } from '../src/utils';
+import { defaultTo, defaultUntil } from '../src/utils';
const TEST_TRUE = 'foo';
const TEST_FALSE = 'bar';
@@ -14,4 +14,15 @@ describe('utils', () => {
expect(defaultTo(TEST_TRUE, TEST_FALSE)).to.equal(TEST_TRUE);
});
});
+
+ describe('default until value helper', () => {
+ it('should return the first defined value', () => {
+ /* eslint-disable-next-line no-null/no-null */
+ expect(defaultUntil(null, null, TEST_TRUE)).to.equal(TEST_TRUE);
+ /* eslint-disable-next-line no-null/no-null */
+ expect(defaultUntil(null, undefined, TEST_TRUE)).to.equal(TEST_TRUE);
+ expect(defaultUntil(undefined, TEST_TRUE, undefined, undefined, TEST_FALSE)).to.equal(TEST_TRUE);
+ expect(defaultUntil(undefined, undefined, TEST_TRUE, undefined)).to.equal(TEST_TRUE);
+ });
+ });
});