1
0
Fork 0
cautious-journey/src/sync.ts

189 lines
5.2 KiB
TypeScript
Raw Normal View History

import { doesExist, mustExist } from '@apextoaster/js-utils';
2020-08-14 04:07:47 +00:00
import { Logger } from 'noicejs';
import { prng } from 'seedrandom';
import { ProjectConfig } from './config';
import { getLabelColor, getLabelNames, getValueName } 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';
import { compareItems, defaultTo, defaultUntil } from './utils';
export interface SyncOptions {
2020-08-14 04:07:47 +00:00
logger: Logger;
project: ProjectConfig;
random: prng;
remote: Remote;
}
/**
* goes through and resolves each issue in the project.
* if there are changes and no errors, then updates the issue.
*/
export async function syncIssueLabels(options: SyncOptions): Promise<unknown> {
const { logger, project, remote } = options;
const issues = await remote.listIssues({
project: project.name,
});
for (const issue of issues) {
logger.info({ issue }, 'project issue');
2020-08-14 03:36:30 +00:00
const { changes, errors, labels } = resolveLabels({
flags: project.flags,
2020-08-14 03:36:30 +00:00
labels: issue.labels,
states: project.states,
2020-08-14 03:36:30 +00:00
});
logger.debug({ changes, errors, issue, labels }, 'resolved labels');
2020-08-14 03:36:30 +00:00
// TODO: prompt user to update this particular issue
2020-08-16 22:07:07 +00:00
const sameLabels = compareItems(issue.labels, labels) || changes.length === 0;
if (sameLabels === false && errors.length === 0) {
logger.info({ changes, errors, issue, labels }, 'updating issue');
await remote.updateIssue({
2020-08-14 03:36:30 +00:00
...issue,
labels,
2020-08-14 03:36:30 +00:00
});
if (project.comment) {
await remote.createComment({
...issue,
changes,
errors,
});
}
2020-08-14 03:36:30 +00:00
}
}
return undefined;
}
export async function syncProjectLabels(options: SyncOptions): Promise<unknown> {
const { logger, project, remote } = options;
const labels = await remote.listLabels({
project: project.name,
});
2020-08-13 01:40:28 +00:00
const present = new Set(labels.map((l) => l.name));
const desired = getLabelNames(project.flags, project.states);
2020-08-13 01:40:28 +00:00
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
logger.info({
2020-08-14 04:07:47 +00:00
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 {
logger.warn({ label }, 'remove label');
await remote.deleteLabel({
name: label,
project: project.name,
});
2020-08-13 00:24:05 +00:00
}
} else {
if (expected) {
logger.info({ label }, 'create label');
await createLabel(options, label);
2020-08-13 00:24:05 +00:00
} else {
// skip
}
}
}
return undefined;
}
export async function createLabel(options: SyncOptions, name: string) {
const { project, remote } = options;
const flag = project.flags.find((it) => name === it.name);
if (doesExist(flag)) {
await remote.createLabel({
color: getLabelColor(project.colors, options.random, flag),
desc: mustExist(flag.desc),
name,
project: project.name,
});
return;
}
const state = project.states.find((it) => name.startsWith(it.name));
if (doesExist(state)) {
2020-08-15 17:38:33 +00:00
const value = state.values.find((it) => getValueName(state, it) === name);
if (doesExist(value)) {
await remote.createLabel({
color: getLabelColor(project.colors, options.random, state, value),
desc: defaultUntil(value.desc, state.desc, ''),
2020-08-15 17:38:33 +00:00
name: getValueName(state, value),
project: project.name,
});
return;
}
2020-08-13 01:40:28 +00:00
}
}
export async function syncLabelDiff(options: SyncOptions, oldLabel: LabelUpdate, newLabel: LabelUpdate) {
const { logger, project } = options;
const dirty =
oldLabel.color !== mustExist(newLabel.color) ||
oldLabel.desc !== mustExist(newLabel.desc);
if (dirty) {
2020-08-13 01:40:28 +00:00
const body = {
color: defaultTo(newLabel.color, oldLabel.color),
desc: defaultTo(newLabel.desc, oldLabel.desc),
name: oldLabel.name,
project: project.name,
2020-08-13 01:40:28 +00:00
};
logger.debug({ body, newLabel, oldLabel }, 'updating label');
2020-08-13 01:40:28 +00:00
const resp = await options.remote.updateLabel(body);
logger.debug({ resp }, 'update response');
2020-08-13 01:40:28 +00:00
}
}
export async function syncSingleLabel(options: SyncOptions, label: LabelUpdate): Promise<void> {
const { project } = options;
const flag = project.flags.find((it) => label.name === it.name);
2020-08-13 01:40:28 +00:00
if (doesExist(flag)) {
const color = getLabelColor(project.colors, options.random, flag);
2020-08-13 01:40:28 +00:00
await syncLabelDiff(options, label, {
color,
2020-08-13 01:40:28 +00:00
desc: defaultTo(flag.desc, label.desc),
name: flag.name,
project: project.name,
2020-08-13 01:40:28 +00:00
});
return;
}
const state = project.states.find((it) => label.name.startsWith(it.name));
2020-08-13 01:40:28 +00:00
if (doesExist(state)) {
2020-08-15 17:38:33 +00:00
const value = state.values.find((it) => getValueName(state, it) === label.name);
2020-08-13 01:40:28 +00:00
if (doesExist(value)) {
const color = mustExist(getLabelColor(project.colors, options.random, state, value));
2020-08-13 01:40:28 +00:00
await syncLabelDiff(options, label, {
color,
2020-08-13 01:40:28 +00:00
desc: defaultTo(value.desc, label.desc),
2020-08-15 17:38:33 +00:00
name: getValueName(state, value),
project: project.name,
2020-08-13 01:40:28 +00:00
});
return;
}
}
}