testflight distribute: groups of apple.testflight.groups missing on App Store Connect are created first (only the asked-for ones, never removed; dry run shows it); tests, skill, CHANGELOG
This commit is contained in:
parent
163e666834
commit
aceb994cae
4 changed files with 60 additions and 20 deletions
|
|
@ -29,7 +29,7 @@ import {
|
|||
parseTesters, pickBuild, planBetaInfo, planBetaReviewDetail, planDistribute, planGroups, planTesters, planWhatToTest, unknownGroupMessage, whatToTestFromListing,
|
||||
} from '../lib/testflight';
|
||||
import { type Asc, ascClient, type J } from './asc';
|
||||
import { describeReq, execute } from './jsonapi';
|
||||
import { describeReq, execute, type Req } from './jsonapi';
|
||||
import { findOrCreate, getById } from '../lib/upsert';
|
||||
import * as rt from '../lib/rt';
|
||||
|
||||
|
|
@ -87,6 +87,19 @@ async function pagesOf(c: Asc, path: string): Promise<J[]> {
|
|||
return out;
|
||||
}
|
||||
|
||||
/** Creates the planned groups (fresh lookup, then the journal for the list lag, then the create; a 409 finds it). */
|
||||
async function createGroups(c: Asc, appId: string, root: string, create: Req[], dry: boolean): Promise<void> {
|
||||
if (dry) { await execute(c.asc, create, { dry, tag: TAG }); return; }
|
||||
for (const r of create) {
|
||||
const name = (r.body as J).data.attributes.name as string;
|
||||
const made = await findOrCreate<J>({ what: `beta group "${name}"`, tag: TAG, unique: true,
|
||||
find: async () => (await c.all(`/v1/apps/${appId}/betaGroups?limit=200`)).find((g: J) => g.attributes.name === name) ?? null,
|
||||
journal: { root, key: `apple:${appId}:betaGroup:${name}`, byId: id => getById(c.asc, `/v1/betaGroups/${id}`) as Promise<J | null>, idOf: v => v.id },
|
||||
create: async () => (await c.asc('POST', r.path, r.body)).data });
|
||||
log(TAG, `${describeReq(r)}: ${made.created ? 'done' : 'exists already'}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function groups(c: Asc, appId: string): Promise<Group[]> {
|
||||
return (await c.all(`/v1/apps/${appId}/betaGroups?limit=200`)).map(groupFromApi);
|
||||
}
|
||||
|
|
@ -154,16 +167,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
}
|
||||
if (!tf?.groups?.length) warn(TAG, 'apple.testflight.groups is empty: nothing to create');
|
||||
for (const n of plan.existing) log(TAG, `group "${n}": exists`);
|
||||
if (dry) await execute(c.asc, plan.create, { dry, tag: TAG });
|
||||
else for (const r of plan.create) {
|
||||
// looked up right before the create (fresh list, then the journal for the list lag); a 409 finds it
|
||||
const name = (r.body as J).data.attributes.name as string;
|
||||
const made = await findOrCreate<J>({ what: `beta group "${name}"`, tag: TAG, unique: true,
|
||||
find: async () => (await c.all(`/v1/apps/${appId}/betaGroups?limit=200`)).find((g: J) => g.attributes.name === name) ?? null,
|
||||
journal: { root, key: `apple:${appId}:betaGroup:${name}`, byId: id => getById(c.asc, `/v1/betaGroups/${id}`) as Promise<J | null>, idOf: v => v.id },
|
||||
create: async () => (await c.asc('POST', r.path, r.body)).data });
|
||||
log(TAG, `${describeReq(r)}: ${made.created ? 'done' : 'exists already'}`);
|
||||
}
|
||||
await createGroups(c, appId, root, plan.create, dry);
|
||||
done(TAG, dry ? `groups checked (dry run): ${plan.create.length} to create` : `groups done (${plan.create.length} created)`);
|
||||
return;
|
||||
}
|
||||
|
|
@ -238,10 +242,24 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const number = need(a.get('build'), '--build N (the exact build number you uploaded)');
|
||||
const names = a.list('group');
|
||||
if (!names.length) throw new Error('--group <name>[,<name>]: which beta groups get the build');
|
||||
const gs = await groups(c, appId);
|
||||
const missing = names.filter(n => !gs.some(g => g.name === n));
|
||||
if (missing.length) throw new Error(unknownGroupMessage(missing, gs, tf?.groups ?? []));
|
||||
const target = names.map(n => gs.find(g => g.name === n)!);
|
||||
let gs = await groups(c, appId);
|
||||
// groups the config names but App Store Connect does not have yet: the groups sync first (create only the missing
|
||||
// configured ones of --group, never remove any); a name the config does not know stays an error
|
||||
const absent = names.filter(n => !gs.some(g => g.name === n));
|
||||
const configured = (tf?.groups ?? []).filter(g => absent.includes(g.name));
|
||||
const unknown = absent.filter(n => !configured.some(g => g.name === n));
|
||||
if (unknown.length) throw new Error(unknownGroupMessage(unknown, gs, tf?.groups ?? []));
|
||||
if (configured.length) {
|
||||
const sync = planGroups(appId, gs, configured);
|
||||
log(TAG, `${dry ? 'would create' : 'creating'} beta group(s) from apple.testflight.groups first: ${configured.map(g => `"${g.name}"`).join(', ')} (testers stay the owner's: apple testflight testers)`);
|
||||
await createGroups(c, appId, root, sync.create, dry);
|
||||
if (dry) {
|
||||
for (const g of configured) log(TAG, `would POST /v1/betaGroups/<new ${g.name}>/relationships/builds {build ${number}}${g.internal && g.allBuilds ? ' (none: an internal all-builds group gets it by itself)' : ''}`);
|
||||
if (configured.length === names.length) { done(TAG, 'distribute checked (dry run)'); return; }
|
||||
} else gs = await groups(c, appId);
|
||||
}
|
||||
const target = names.map(n => gs.find(g => g.name === n)).filter((g): g is Group => !!g);
|
||||
if (!dry && target.length < names.length) throw new Error(`beta group(s) ${names.filter(n => !target.some(g => g.name === n)).join(', ')} created but not listed yet (App Store Connect lists lag after a create): run distribute again in a minute; nothing is created twice`);
|
||||
const external = target.filter(g => !g.internal);
|
||||
if (a.has('internal-only') && external.length) throw new Error(`external group(s) ${external.map(g => g.name).join(', ')}: distribute to external testers from the terminal (asks first)`);
|
||||
const b = await findBuild(c, appId, number, a.get('version'));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue