feat: add issue create and comment (task 0004)
Introduce the first mutations, along with the shared machinery the later
issue and PR mutation slices reuse.
- `issue create` with --title/--body/--body-file/--assignee/--label/
--milestone/--fields, emitting `issue: { number, title, state, url }`
- `issue comment <n>`, echoing the created comment from the POST response
with the body cleaned and truncated at 800 chars
- body-source resolution (--body vs --body-file), label and milestone
name->ID lookup, repeatable flags, and the `joined`/`selectExtraFields`
field extractors
Label lookup pages until exhausted, since a repo with more labels than one
page would otherwise fail to resolve a valid name. The end-to-end tier seeds
a mixed-case label and milestone and passes both in a different case, so the
case-insensitive lookup is verified against live Gitea rather than only
against fixtures.
This commit was merged in pull request #3.
This commit is contained in:
41
src/flags.ts
41
src/flags.ts
@@ -1,12 +1,22 @@
|
||||
import { axiError } from "./errors.js";
|
||||
|
||||
export interface FlagSpec {
|
||||
/** Flag names (e.g. "--state") mapped to whether they take a value. */
|
||||
[name: string]: { takesValue: boolean };
|
||||
/** Flag names (e.g. "--state") mapped to how they are parsed. */
|
||||
[name: string]: {
|
||||
takesValue: boolean;
|
||||
/** Accumulate every occurrence into `lists` instead of `flags` (e.g. `--label`). */
|
||||
repeatable?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ParsedFlags {
|
||||
/** Single-valued flags: the value, or `true` for a bare switch. */
|
||||
flags: Record<string, string | true>;
|
||||
/**
|
||||
* Values of each repeatable flag in argv order. Every repeatable flag in the
|
||||
* spec is present, holding an empty array when it was not passed.
|
||||
*/
|
||||
lists: Record<string, string[]>;
|
||||
positionals: string[];
|
||||
}
|
||||
|
||||
@@ -15,6 +25,19 @@ export interface SplitFlag {
|
||||
inlineValue?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a value-taking flag. `parseFlags` rejects such a flag without a value,
|
||||
* so anything present here is a string; the `true` case only arises for bare
|
||||
* switches, which callers never read through this helper.
|
||||
*/
|
||||
export function flagValue(
|
||||
flags: Record<string, string | true>,
|
||||
name: string,
|
||||
): string | undefined {
|
||||
const value = flags[name];
|
||||
return typeof value === "string" ? value : undefined;
|
||||
}
|
||||
|
||||
/** Split "--flag=value" into name and inline value; "--flag" has none. */
|
||||
export function splitFlag(arg: string): SplitFlag {
|
||||
const equals = arg.indexOf("=");
|
||||
@@ -53,6 +76,12 @@ export function parseFlags(
|
||||
helpCommand: string,
|
||||
): ParsedFlags {
|
||||
const flags: Record<string, string | true> = {};
|
||||
const lists: Record<string, string[]> = {};
|
||||
for (const [name, entry] of Object.entries(spec)) {
|
||||
if (entry.repeatable) {
|
||||
lists[name] = [];
|
||||
}
|
||||
}
|
||||
const positionals: string[] = [];
|
||||
const helpSuggestion = [`Run \`gitea-axi ${helpCommand} --help\` to see available flags`];
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
@@ -74,8 +103,12 @@ export function parseFlags(
|
||||
continue;
|
||||
}
|
||||
const consumed = consumeFlagValue(args, i, flag, helpSuggestion);
|
||||
flags[flag.name] = consumed.value;
|
||||
if (entry.repeatable) {
|
||||
lists[flag.name]!.push(consumed.value);
|
||||
} else {
|
||||
flags[flag.name] = consumed.value;
|
||||
}
|
||||
i = consumed.lastIndex;
|
||||
}
|
||||
return { flags, positionals };
|
||||
return { flags, lists, positionals };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user