Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/core/release/statusCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { NodeHttpClient } from '@effect/platform-node';
import { createLogger, makeLaunchLoggerTest } from '../services/logger.js';
import type { AppDescriptor } from '../types/app.js';
import { classifyVerdict, type ReleaseStatus } from './appStoreRelease.js';
import type { MutableDeep } from '../types/mutable.js';
import {
formatStatusLine,
selectIosApps,
Expand Down Expand Up @@ -32,7 +33,7 @@ const releaseStatus = (overrides: Partial<ReleaseStatus> = {}): ReleaseStatus =>
};

const discoveredApp = (appName: string, bundleId?: string): AppDescriptor => {
const appDescriptor: AppDescriptor = {
const appDescriptor: MutableDeep<AppDescriptor> = {
name: appName,
dir: `/repo/${appName}`,
configPath: `/repo/${appName}/app.json`,
Expand Down
2 changes: 1 addition & 1 deletion src/core/release/testflightFeedback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ const safeFeedbackIdentifier = (feedbackId: string): string => {
/** Download screenshot attachments serially into one directory. */
export const downloadFeedbackAttachments = (
appleStore: AscFeedbackApi,
feedbackEntries: BetaFeedback[],
feedbackEntries: readonly BetaFeedback[],
outputDirectory: string,
): Effect.Effect<DownloadedAttachment[], unknown, FileSystem.FileSystem | Path.Path> =>
Effect.gen(function* () {
Expand Down
2 changes: 1 addition & 1 deletion src/core/release/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const compareVersions = (a: string, b: string): number => {
* Used to fold App Store + TestFlight versions into a single "latest on record" without trusting the
* store's own (lexical) sort, which would order `1.10.0` below `1.9.0`.
*/
export const highestVersion = (versions: string[]): string | null => {
export const highestVersion = (versions: readonly string[]): string | null => {
const parseable = versions.filter((version) => parseVersion(version) !== null);
if (parseable.length === 0) return null;
return parseable.reduce((highest, version) => {
Expand Down
11 changes: 8 additions & 3 deletions src/core/releaseTrain/orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
TrainState,
} from '../types/releaseTrain.js';
import { isCarTerminal, isNativeCar, isOtaCar } from './guards.js';
import type { MutableDeep } from '../types/mutable.js';

/** Store and OTA operations driven by the release-train state machine. */
export type TrainEngine<Requirements = never> = Readonly<{
Expand Down Expand Up @@ -100,7 +101,7 @@ export const startTrain = <Requirements>(
Effect.gen(function* () {
const trainCars: Car[] = [];
for (const platform of trainInput.platforms) {
const nativeCar: NativeCar = {
const nativeCar: MutableDeep<NativeCar> = {
kind: platform,
state: 'building',
updatedAt: trainInput.now,
Expand Down Expand Up @@ -148,7 +149,7 @@ export const advanceTrain = <Requirements>(
if (trainRecord.state === 'done') return trainRecord;
if (trainRecord.state === 'aborted') return trainRecord;
const forced = advanceOptions.force === true;
const trainCars = trainRecord.cars.map((trainCar): Car => ({ ...trainCar }));
const trainCars = trainRecord.cars.map((trainCar): MutableDeep<Car> => ({ ...trainCar }));

for (const trainCar of trainCars) {
if (!isNativeCar(trainCar)) continue;
Expand All @@ -160,7 +161,11 @@ export const advanceTrain = <Requirements>(
if (!isNativeFailure(trainCar)) delete trainCar.error;
}

const nativeCars = trainCars.filter(isNativeCar);
const nativeCars: MutableDeep<NativeCar>[] = [];
for (const trainCar of trainCars) {
if (!isNativeCar(trainCar)) continue;
nativeCars.push(trainCar);
}
const hasNativeFailure = nativeCars.some(isNativeFailure);
const allApproved = nativeCars.every(isNativeApprovedOrReleased);
const gateOpen = isReleaseGateOpen(trainRecord.hold, forced, hasNativeFailure, allApproved);
Expand Down
76 changes: 35 additions & 41 deletions src/core/releaseTrain/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,41 @@ import {
} from '../services/paths.js';
import type { TrainRecord } from '../types/releaseTrain.js';

const NativeCarSchema = Schema.mutable(
Schema.Struct({
kind: Schema.Literal('ios', 'android'),
state: Schema.Literal(
'building',
'submitted',
'in-review',
'approved',
'released',
'rejected',
'failed',
),
buildId: Schema.optionalWith(Schema.String, { exact: true }),
error: Schema.optionalWith(Schema.String, { exact: true }),
updatedAt: Schema.String,
}),
);

const OtaCarSchema = Schema.mutable(
Schema.Struct({
kind: Schema.Literal('ota'),
platform: Schema.Literal('ios', 'android'),
channel: Schema.String,
runtimeVersion: Schema.String,
state: Schema.Literal('pending', 'published'),
manifestId: Schema.optionalWith(Schema.String, { exact: true }),
updatedAt: Schema.String,
}),
);

const TrainRecordSchema: Schema.Schema<TrainRecord> = Schema.mutable(
Schema.Struct({
id: Schema.String,
app: Schema.String,
hold: Schema.Boolean,
state: Schema.Literal('running', 'blocked', 'done', 'aborted'),
createdAt: Schema.String,
updatedAt: Schema.String,
cars: Schema.mutable(Schema.Array(Schema.Union(NativeCarSchema, OtaCarSchema))),
}),
);
const NativeCarSchema = Schema.Struct({
kind: Schema.Literal('ios', 'android'),
state: Schema.Literal(
'building',
'submitted',
'in-review',
'approved',
'released',
'rejected',
'failed',
),
buildId: Schema.optionalWith(Schema.String, { exact: true }),
error: Schema.optionalWith(Schema.String, { exact: true }),
updatedAt: Schema.String,
});

const OtaCarSchema = Schema.Struct({
kind: Schema.Literal('ota'),
platform: Schema.Literal('ios', 'android'),
channel: Schema.String,
runtimeVersion: Schema.String,
state: Schema.Literal('pending', 'published'),
manifestId: Schema.optionalWith(Schema.String, { exact: true }),
updatedAt: Schema.String,
});

const TrainRecordSchema: Schema.Schema<TrainRecord> = Schema.Struct({
id: Schema.String,
app: Schema.String,
hold: Schema.Boolean,
state: Schema.Literal('running', 'blocked', 'done', 'aborted'),
createdAt: Schema.String,
updatedAt: Schema.String,
cars: Schema.Array(Schema.Union(NativeCarSchema, OtaCarSchema)),
});

/** A persisted release-train record could not be read or written. */
export type TrainRecordFailure = Readonly<{
Expand Down
Loading