2020-08-13 23:44:13 +00:00
|
|
|
import { doesExist, mustExist } from '@apextoaster/js-utils';
|
2020-08-14 04:07:47 +00:00
|
|
|
import { Logger } from 'noicejs';
|
2020-08-13 01:01:48 +00:00
|
|
|
|
2020-08-13 23:44:13 +00:00
|
|
|
import { FlagLabel, getLabelNames, StateLabel, valueName } from './labels';
|
2020-08-13 01:40:28 +00:00
|
|
|
import { LabelUpdate, Remote } from './remote';
|
2020-08-14 03:36:30 +00:00
|
|
|
import { resolveLabels } from './resolve';
|
2020-08-13 23:44:13 +00:00
|
|
|
import { defaultTo } from './utils';
|
2020-08-12 01:39:53 +00:00
|
|
|
|
|
|
|
export interface SyncOptions {
|
2020-08-13 00:33:53 +00:00
|
|
|
flags: Array<FlagLabel>;
|
2020-08-14 04:07:47 +00:00
|
|
|
logger: Logger;
|
2020-08-12 01:39:53 +00:00
|
|
|
project: string;
|
|
|
|
remote: Remote;
|
2020-08-13 00:33:53 +00:00
|
|
|
states: Array<StateLabel>;
|
2020-08-12 01:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export async function syncIssues(options: SyncOptions): Promise<unknown> {
|
2020-08-12 23:21:35 +00:00
|
|
|
const issues = await options.remote.listIssues({
|
|
|
|
project: options.project,
|
|
|
|
});
|
2020-08-12 01:39:53 +00:00
|
|
|
|
|
|
|
for (const issue of issues) {
|
2020-08-14 04:07:47 +00:00
|
|
|
options.logger.info({ issue }, 'issue');
|
2020-08-14 03:36:30 +00:00
|
|
|
|
|
|
|
const resolution = resolveLabels({
|
|
|
|
flags: options.flags,
|
|
|
|
labels: issue.labels,
|
|
|
|
states: options.states,
|
|
|
|
});
|
|
|
|
|
|
|
|
// TODO: prompt user to update this particular issue
|
|
|
|
if (resolution.changes.length > 0) {
|
2020-08-14 04:07:47 +00:00
|
|
|
options.logger.info({ issue, resolution }, 'updating issue');
|
2020-08-14 03:36:30 +00:00
|
|
|
await options.remote.updateIssue({
|
|
|
|
...issue,
|
|
|
|
labels: resolution.labels,
|
|
|
|
});
|
|
|
|
}
|
2020-08-12 01:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function syncLabels(options: SyncOptions): Promise<unknown> {
|
2020-08-12 23:21:35 +00:00
|
|
|
const labels = await options.remote.listLabels({
|
|
|
|
project: options.project,
|
|
|
|
});
|
2020-08-12 01:39:53 +00:00
|
|
|
|
2020-08-13 01:40:28 +00:00
|
|
|
const present = new Set(labels.map((l) => l.name));
|
|
|
|
const desired = getLabelNames(options.flags, options.states);
|
|
|
|
const combined = new Set([...desired, ...present]);
|
2020-08-13 00:24:05 +00:00
|
|
|
|
2020-08-13 01:40:28 +00:00
|
|
|
for (const label of combined) {
|
|
|
|
const exists = present.has(label);
|
|
|
|
const expected = desired.has(label);
|
2020-08-14 04:07:47 +00:00
|
|
|
|
|
|
|
options.logger.info({
|
|
|
|
exists,
|
|
|
|
expected,
|
|
|
|
label,
|
|
|
|
}, 'label');
|
2020-08-13 00:24:05 +00:00
|
|
|
|
|
|
|
if (exists) {
|
|
|
|
if (expected) {
|
2020-08-13 01:40:28 +00:00
|
|
|
const data = mustExist(labels.find((l) => l.name === label));
|
|
|
|
await syncSingleLabel(options, data);
|
2020-08-13 00:24:05 +00:00
|
|
|
} else {
|
2020-08-14 04:07:47 +00:00
|
|
|
options.logger.warn({ label }, 'remove label');
|
2020-08-13 23:44:13 +00:00
|
|
|
await options.remote.deleteLabel({
|
|
|
|
name: label,
|
|
|
|
project: options.project,
|
|
|
|
});
|
2020-08-13 00:24:05 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (expected) {
|
2020-08-14 04:07:47 +00:00
|
|
|
options.logger.info({ label }, 'create label');
|
2020-08-13 23:44:13 +00:00
|
|
|
await createLabel(options, label);
|
2020-08-13 00:24:05 +00:00
|
|
|
} else {
|
|
|
|
// skip
|
|
|
|
}
|
|
|
|
}
|
2020-08-12 01:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
}
|
2020-08-13 01:01:48 +00:00
|
|
|
|
2020-08-13 23:44:13 +00:00
|
|
|
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),
|
|
|
|
desc: mustExist(flag.desc),
|
|
|
|
name,
|
|
|
|
project: options.project,
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const state = options.states.find((it) => name.startsWith(it.name));
|
|
|
|
if (doesExist(state)) {
|
|
|
|
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), ''),
|
|
|
|
name: valueName(state, value),
|
|
|
|
project: options.project,
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2020-08-13 01:40:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function syncLabelDiff(options: SyncOptions, current: LabelUpdate, expected: LabelUpdate) {
|
2020-08-13 01:01:48 +00:00
|
|
|
const dirty =
|
2020-08-13 01:40:28 +00:00
|
|
|
current.color !== expected.color ||
|
|
|
|
current.desc !== expected.desc;
|
2020-08-13 01:01:48 +00:00
|
|
|
|
|
|
|
if (dirty) {
|
2020-08-13 01:40:28 +00:00
|
|
|
const body = {
|
|
|
|
color: defaultTo(expected.color, current.color),
|
|
|
|
desc: defaultTo(expected.desc, current.desc),
|
|
|
|
name: current.name,
|
|
|
|
project: options.project,
|
|
|
|
};
|
|
|
|
|
2020-08-14 04:07:47 +00:00
|
|
|
options.logger.debug({ body, current, expected }, 'update label');
|
2020-08-13 01:40:28 +00:00
|
|
|
|
|
|
|
const resp = await options.remote.updateLabel(body);
|
|
|
|
|
2020-08-14 04:07:47 +00:00
|
|
|
options.logger.debug({ resp }, 'update resp');
|
2020-08-13 01:40:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function syncSingleLabel(options: SyncOptions, label: LabelUpdate): Promise<void> {
|
|
|
|
const flag = options.flags.find((it) => label.name === it.name);
|
|
|
|
if (doesExist(flag)) {
|
|
|
|
await syncLabelDiff(options, label, {
|
|
|
|
color: defaultTo(flag.color, label.color),
|
|
|
|
desc: defaultTo(flag.desc, label.desc),
|
|
|
|
name: flag.name,
|
|
|
|
project: options.project,
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const state = options.states.find((it) => label.name.startsWith(it.name));
|
|
|
|
if (doesExist(state)) {
|
2020-08-13 23:44:13 +00:00
|
|
|
const value = state.values.find((it) => valueName(state, it) === label.name);
|
2020-08-13 01:40:28 +00:00
|
|
|
if (doesExist(value)) {
|
|
|
|
await syncLabelDiff(options, label, {
|
|
|
|
color: defaultTo(value.color, label.color),
|
|
|
|
desc: defaultTo(value.desc, label.desc),
|
2020-08-13 23:44:13 +00:00
|
|
|
name: valueName(state, value),
|
2020-08-13 01:40:28 +00:00
|
|
|
project: options.project,
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2020-08-13 01:01:48 +00:00
|
|
|
}
|
|
|
|
}
|