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
2 changes: 1 addition & 1 deletion src/core/adopt/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export const capabilitiesAdopter: Adopter<ProfileEntitlementRequirements> = {
let profileEntitlements: Record<string, EntitlementValue> | null = null;
if (profileContent !== null)
profileEntitlements = yield* extractProfileEntitlements(profileContent);
const settingsByType: Record<string, CapabilitySetting[]> = {};
const settingsByType: Record<string, readonly CapabilitySetting[]> = {};
for (const capability of capabilities) {
if (capability.settings !== undefined)
settingsByType[capability.capabilityType] = capability.settings;
Expand Down
2 changes: 1 addition & 1 deletion src/core/adopt/certs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export const certsAdopter: Adopter<CertAdopterRequirements> = {
Effect.gen(function* () {
const bundleResource = yield* appleCatalog.findBundleId(target.bundleId);
const certificates = yield* appleCatalog.listDistributionCertificates();
let profiles: ProfileResource[] = [];
let profiles: readonly ProfileResource[] = [];
if (bundleResource !== null)
profiles = yield* appleCatalog.listProfilesForBundleId(bundleResource.id);
const storedCredentials = yield* describeStoredCredentials(target.keyId);
Expand Down
10 changes: 6 additions & 4 deletions src/core/adopt/configWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ export const aggregateProductPieces = (pieces: readonly ProductPiece[]): AppProd
}
subscriptionGroups.push(piece.group);
}
const products: AppProducts = {};
if (inAppPurchases.length > 0) products.inAppPurchases = inAppPurchases;
if (subscriptionGroups.length > 0) products.subscriptionGroups = subscriptionGroups;
return products;
if (inAppPurchases.length > 0 && subscriptionGroups.length > 0) {
return { inAppPurchases, subscriptionGroups };
}
if (inAppPurchases.length > 0) return { inAppPurchases };
if (subscriptionGroups.length > 0) return { subscriptionGroups };
return {};
};

/** Serialize a products block keyed by bundle id as an indented, paste-ready TypeScript section. */
Expand Down
3 changes: 2 additions & 1 deletion src/core/adopt/products.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
SubscriptionGroupConfig,
SubscriptionPeriod,
} from '../types/catalog.js';
import type { MutableDeep } from '../types/mutable.js';

/** Narrow an App Store in-app purchase type to the modeled config union. */
const parseInAppPurchaseType = (purchaseType: string): InAppPurchaseType | null => {
Expand Down Expand Up @@ -48,7 +49,7 @@ const productLocalizationsFromResources = (
localizations: readonly LocalizationResource[],
): ProductLocalization[] =>
localizations.map((localization) => {
const productLocalization: ProductLocalization = {
const productLocalization: MutableDeep<ProductLocalization> = {
locale: localization.locale,
name: localization.name,
};
Expand Down
2 changes: 1 addition & 1 deletion src/core/doctor/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ const reconcileDoctorExportCompliance = (

/** Best-effort export-compliance repair for selected iOS apps. */
const fixExportCompliance = (
selectedApps: AppDescriptor[],
selectedApps: readonly AppDescriptor[],
): Effect.Effect<void, unknown, DoctorCommandRequirements> =>
Effect.gen(function* () {
const resolveAppleStore = createAscClientResolver();
Expand Down
2 changes: 1 addition & 1 deletion src/core/migrate/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const readMigration = (

const printMigrationNotes = (
logger: Logger,
notes: MigrationNote[],
notes: readonly MigrationNote[],
): Effect.Effect<void, unknown> =>
Effect.gen(function* () {
for (const migrationNote of notes) {
Expand Down
4 changes: 2 additions & 2 deletions src/core/migrate/eas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ const app = (over: Partial<AppDescriptor> = {}): AppDescriptor => ({
});

/** The artifact at `path`, asserting it was emitted. */
const artifact = (artifacts: MigrationArtifact[], path: string): MigrationArtifact => {
const artifact = (artifacts: readonly MigrationArtifact[], path: string): MigrationArtifact => {
const found = artifacts.find((entry) => entry.path === path);
expect(found, `expected artifact ${path}`).toBeDefined();
return expectDefined(found, `artifact ${path}`);
};

/** Notes at a given level. */
const notesAt = (notes: MigrationNote[], level: MigrationNoteLevel): MigrationNote[] =>
const notesAt = (notes: readonly MigrationNote[], level: MigrationNoteLevel): MigrationNote[] =>
notes.filter((note) => note.level === level);

const sampleEasConfiguration = (): EasJson => Effect.runSync(parseEasJson(SAMPLE_EAS));
Expand Down
5 changes: 3 additions & 2 deletions src/core/migrate/eas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
MigrationNote,
MigrationResult,
} from '../types/migrate.js';
import type { MutableDeep } from '../types/mutable.js';
import { buildEnvExample, scaffoldStoreConfig } from './scaffold.js';

export type EasMigrationFailure = Readonly<{
Expand Down Expand Up @@ -116,7 +117,7 @@ const EasSubmitProfilesSchema = Schema.transform(
for (const [profileName, unknownProfile] of Object.entries(unknownProfiles)) {
const decodedProfile = Schema.decodeUnknownOption(EasSubmitProfileSchema)(unknownProfile);
if (Option.isNone(decodedProfile)) continue;
const submitProfile: EasSubmitProfile = {};
const submitProfile: MutableDeep<EasSubmitProfile> = {};
const iosSubmission = decodedProfile.value.ios;
if (iosSubmission !== undefined && hasIosSubmitFields(iosSubmission)) {
submitProfile.ios = iosSubmission;
Expand Down Expand Up @@ -375,7 +376,7 @@ type CredentialsDocument = Schema.Schema.Type<typeof CredentialsDocumentSchema>;
export const credentialsSummaryFromDocument = (
credentialsDocument: CredentialsDocument,
): CredentialsSummary | null => {
const credentialsSummary: CredentialsSummary = {};
const credentialsSummary: MutableDeep<CredentialsSummary> = {};
const iosCredentials = credentialsDocument.ios;
if (iosCredentials !== undefined) {
let distributionCertificatePath: string | undefined;
Expand Down
9 changes: 6 additions & 3 deletions src/core/migrate/fastlane.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,18 @@ const app = (overrides: Partial<AppDescriptor> = {}): AppDescriptor => {
};
/** The artifact at `path`, asserting it was emitted. */
const artifact = (
migrationArtifacts: MigrationArtifact[],
migrationArtifacts: readonly MigrationArtifact[],
artifactPath: string,
): MigrationArtifact => {
const matchingArtifact = migrationArtifacts.find((entry) => entry.path === artifactPath);
expect(matchingArtifact, `expected artifact ${artifactPath}`).toBeDefined();
return expectDefined(matchingArtifact, `artifact ${artifactPath}`);
};
/** Notes at a given level. */
const notesAt = (migrationNotes: MigrationNote[], level: MigrationNoteLevel): MigrationNote[] => {
const notesAt = (
migrationNotes: readonly MigrationNote[],
level: MigrationNoteLevel,
): MigrationNote[] => {
return migrationNotes.filter((note) => note.level === level);
};
const runReadFastlaneSetup = (workingDirectory: string) =>
Expand Down Expand Up @@ -149,7 +152,7 @@ describe('parseFastfile', () => {
expect(beta).toBeDefined();
if (beta === undefined) return;
expect(beta.platform).toBe('ios');
expect(beta.actions.sort()).toEqual(['gym', 'match', 'pilot']);
expect([...beta.actions].sort()).toEqual(['gym', 'match', 'pilot']);
expect(laneLaunchCommands(beta)).toEqual(['launch build', 'launch release --track testing']);
const play = lanes.find((lane) => lane.name === 'play');
expect(play).toBeDefined();
Expand Down
41 changes: 30 additions & 11 deletions src/core/migrate/fastlane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type {
MigrationResult,
SupplyfileData,
} from '../types/migrate.js';
import type { MutableDeep } from '../types/mutable.js';
import { buildEnvExample, scaffoldStoreConfig } from './scaffold.js';

export type FastlaneMigrationFailure = Readonly<{
Expand All @@ -45,7 +46,7 @@ export const readRubyString = (rubySource: string, directiveName: string): strin
};

export const parseAppfile = (appfileSource: string): AppfileData => {
const appfile: AppfileData = {};
const appfile: MutableDeep<AppfileData> = {};
const appIdentifier = readRubyString(appfileSource, 'app_identifier');
if (appIdentifier !== undefined) appfile.appIdentifier = appIdentifier;
const appleId = readRubyString(appfileSource, 'apple_id');
Expand All @@ -60,7 +61,7 @@ export const parseAppfile = (appfileSource: string): AppfileData => {
};

export const parseMatchfile = (matchfileSource: string): MatchfileData => {
const matchfile: MatchfileData = {};
const matchfile: MutableDeep<MatchfileData> = {};
const gitUrl = readRubyString(matchfileSource, 'git_url');
if (gitUrl !== undefined) matchfile.gitUrl = gitUrl;
const signingType = readRubyString(matchfileSource, 'type');
Expand All @@ -73,7 +74,7 @@ export const parseMatchfile = (matchfileSource: string): MatchfileData => {
};

export const parseSupplyfile = (supplyfileSource: string): SupplyfileData => {
const supplyfile: SupplyfileData = {};
const supplyfile: MutableDeep<SupplyfileData> = {};
const packageName = readRubyString(supplyfileSource, 'package_name');
if (packageName !== undefined) supplyfile.packageName = packageName;
const jsonKey = readRubyString(supplyfileSource, 'json_key');
Expand Down Expand Up @@ -189,7 +190,7 @@ export const parseFastfile = (
containsAction(laneSource, actionName),
);
const lanePlatform = findLanePlatform(fastfileSource, laneStartIndex);
const fastlaneLane: FastlaneLane = { name: laneName, actions: laneActions };
const fastlaneLane: MutableDeep<FastlaneLane> = { name: laneName, actions: laneActions };
if (lanePlatform !== undefined) fastlaneLane.platform = lanePlatform;
fastlaneLanes.push(fastlaneLane);
}
Expand Down Expand Up @@ -274,21 +275,39 @@ export const readFastlaneSetup = (
if (fastlaneSources.fastfile !== undefined) {
parsedFastfile = parseFastfile(fastlaneSources.fastfile);
}
let appfile: AppfileData | undefined;
if (fastlaneSources.appfile !== undefined) {
appfile = parseAppfile(fastlaneSources.appfile);
}
let matchfile: MatchfileData | undefined;
if (fastlaneSources.matchfile !== undefined) {
matchfile = parseMatchfile(fastlaneSources.matchfile);
}
let supply: SupplyfileData | undefined;
if (fastlaneSources.supplyfile !== undefined) {
supply = parseSupplyfile(fastlaneSources.supplyfile);
}
const fastlaneSetup: FastlaneSetup = {
lanes: parsedFastfile.lanes,
actions: parsedFastfile.actions,
hasDeliverfile: fastlaneSources.deliverfile !== undefined,
envKeys: fastlaneSources.environmentKeys,
};
if (fastlaneSources.appfile !== undefined) {
fastlaneSetup.appfile = parseAppfile(fastlaneSources.appfile);
if (appfile !== undefined && matchfile !== undefined && supply !== undefined) {
return { ...fastlaneSetup, appfile, matchfile, supply };
}
if (fastlaneSources.matchfile !== undefined) {
fastlaneSetup.matchfile = parseMatchfile(fastlaneSources.matchfile);
if (appfile !== undefined && matchfile !== undefined) {
return { ...fastlaneSetup, appfile, matchfile };
}
if (fastlaneSources.supplyfile !== undefined) {
fastlaneSetup.supply = parseSupplyfile(fastlaneSources.supplyfile);
if (appfile !== undefined && supply !== undefined) {
return { ...fastlaneSetup, appfile, supply };
}
if (matchfile !== undefined && supply !== undefined) {
return { ...fastlaneSetup, matchfile, supply };
}
if (appfile !== undefined) return { ...fastlaneSetup, appfile };
if (matchfile !== undefined) return { ...fastlaneSetup, matchfile };
if (supply !== undefined) return { ...fastlaneSetup, supply };
return fastlaneSetup;
});

Expand All @@ -303,7 +322,7 @@ export const laneLaunchCommands = (fastlaneLane: FastlaneLane): string[] => {
return launchCommands;
};

const laneNotes = (fastlaneLanes: FastlaneLane[]): MigrationNote[] => {
const laneNotes = (fastlaneLanes: readonly FastlaneLane[]): MigrationNote[] => {
const migrationNotes: MigrationNote[] = [];
const customLaneNames: string[] = [];
for (const fastlaneLane of fastlaneLanes) {
Expand Down
2 changes: 1 addition & 1 deletion src/core/migrate/scaffold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const scaffoldStoreConfig = (
* `.env.example` body from imported env KEYS only (values dropped; may be secrets).
* Falls back to the starter template when no keys were found. Shared by EAS and Fastlane.
*/
export const buildEnvExample = (keys: string[]): string => {
export const buildEnvExample = (keys: readonly string[]): string => {
if (keys.length === 0) return ENV_EXAMPLE_TEMPLATE;
const header = ENV_EXAMPLE_TEMPLATE.split('\n')
.filter((line) => line.startsWith('#'))
Expand Down
2 changes: 1 addition & 1 deletion src/core/plan/orchestrator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const planner = (plan: SurfacePlan): SurfacePlanner => {
/** Execute the planner orchestrator at the test boundary. */
const runPlannerSet = (
planContext: PlanContext,
planners: SurfacePlanner[],
planners: readonly SurfacePlanner[],
options: Parameters<typeof runPlanners>[2],
) =>
Effect.runPromise(
Expand Down
2 changes: 1 addition & 1 deletion src/core/plan/orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export const planExitCode = ({
*/
export const runPlanners = (
planContext: PlanContext,
planners: SurfacePlanner[],
planners: readonly SurfacePlanner[],
options: PlanRunOptions,
): Effect.Effect<PlanOutcome, unknown, FileSystem.FileSystem | Path.Path> =>
Effect.gen(function* () {
Expand Down
7 changes: 5 additions & 2 deletions src/core/plan/planners/appStoreSurface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export type AppStoreSurfaceSpec<TConfig> = {
config: TConfig,
) => Effect.Effect<
{
actions: PlannedAction[];
actions: readonly PlannedAction[];
},
unknown
>;
Expand Down Expand Up @@ -111,7 +111,10 @@ export type TeamSurfaceSpec<TConfig> = {
| TConfig
| Effect.Effect<TConfig | undefined, unknown, FileSystem.FileSystem>
| undefined;
reconcile: (api: AscSurfacesApi, config: TConfig) => Effect.Effect<PlannedAction[], unknown>;
reconcile: (
api: AscSurfacesApi,
config: TConfig,
) => Effect.Effect<readonly PlannedAction[], unknown>;
};
/**
* Plan one team-level App Store surface: omit when nothing is declared, skip with a hint when no Apple
Expand Down
3 changes: 2 additions & 1 deletion src/core/plan/planners/euDistribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from '@core/store/euDistribution.js';
import { planTeamSurface } from './appStoreSurface.js';
import type { SurfacePlanner } from '@core/types/plan.js';
import type { EuDistributionConfig } from '@core/types/storeSurface.js';
/** Surface id - also the value users pass as `launch plan eu-distribution`. */
const SURFACE = 'eu-distribution';
export const euDistributionPlanner: SurfacePlanner = {
Expand All @@ -15,7 +16,7 @@ export const euDistributionPlanner: SurfacePlanner = {
surface: SURFACE,
direction: 'additive',
config: () =>
resolveSidecarConfig({
resolveSidecarConfig<EuDistributionConfig>({
typed: planContext.config.euDistribution,
configPath: 'eu-distribution.config.json',
explicitPath: false,
Expand Down
3 changes: 2 additions & 1 deletion src/core/plan/planners/gameCenter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { resolveSidecarConfig } from '@core/config/config.js';
import { loadGameCenterConfig, reconcileGameCenter } from '@core/store/gameCenter.js';
import { planAppStoreSurface } from './appStoreSurface.js';
import type { SurfacePlanner } from '@core/types/plan.js';
import type { GameCenterConfig } from '@core/types/storeSurface.js';
/** Surface id - also the value users pass as `launch plan game-center`. */
const SURFACE = 'game-center';
export const gameCenterPlanner: SurfacePlanner = {
Expand All @@ -12,7 +13,7 @@ export const gameCenterPlanner: SurfacePlanner = {
surface: SURFACE,
direction: 'additive',
configFor: (bundleId) =>
resolveSidecarConfig({
resolveSidecarConfig<GameCenterConfig>({
typed: planContext.config.gameCenter?.[bundleId],
configPath: 'gamecenter.config.json',
explicitPath: false,
Expand Down
2 changes: 1 addition & 1 deletion src/core/plan/planners/playProducts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type PlayProductsTarget = {
products: InAppPurchaseConfig[];
};
/** Resolve the apps that declare at least one Play-overridden in-app product, with their package + products. */
const targetsFor = (apps: AppDescriptor[], config: LaunchConfig): PlayProductsTarget[] => {
const targetsFor = (apps: readonly AppDescriptor[], config: LaunchConfig): PlayProductsTarget[] => {
const targets: PlayProductsTarget[] = [];
for (const app of apps) {
if (!app.packageName) continue;
Expand Down
4 changes: 3 additions & 1 deletion src/core/plan/planners/playSubscriptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ const APPLE_ONLY_SUB: SubscriptionConfig = {
localizations: [{ locale: 'en-US', name: 'Pro Yearly' }],
};
/** Wrap subscriptions in the one group `products[bundleId].subscriptionGroups` requires. */
const productsWith = (subscriptions: SubscriptionConfig[]): Record<string, AppProducts> => {
const productsWith = (
subscriptions: readonly SubscriptionConfig[],
): Record<string, AppProducts> => {
return {
'com.acme.alpha': {
subscriptionGroups: [
Expand Down
5 changes: 4 additions & 1 deletion src/core/plan/planners/playSubscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ type PlaySubscriptionsTarget = {
subscriptions: SubscriptionConfig[];
};
/** Resolve the apps that declare at least one Play-overridden subscription, with their package + subscriptions. */
const targetsFor = (apps: AppDescriptor[], config: LaunchConfig): PlaySubscriptionsTarget[] => {
const targetsFor = (
apps: readonly AppDescriptor[],
config: LaunchConfig,
): PlaySubscriptionsTarget[] => {
const targets: PlaySubscriptionsTarget[] = [];
for (const app of apps) {
if (!app.packageName) continue;
Expand Down
3 changes: 2 additions & 1 deletion src/core/plan/planners/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { resolveSidecarConfig } from '@core/config/config.js';
import { loadWalletConfig, reconcileWalletIds } from '@core/store/walletIds.js';
import { planTeamSurface } from './appStoreSurface.js';
import type { SurfacePlanner } from '@core/types/plan.js';
import type { WalletConfig } from '@core/types/storeSurface.js';
/** Surface id - also the value users pass as `launch plan wallet`. */
const SURFACE = 'wallet';
export const walletPlanner: SurfacePlanner = {
Expand All @@ -12,7 +13,7 @@ export const walletPlanner: SurfacePlanner = {
surface: SURFACE,
direction: 'additive',
config: () =>
resolveSidecarConfig({
resolveSidecarConfig<WalletConfig>({
typed: planContext.config.wallet,
configPath: 'wallet.config.json',
explicitPath: false,
Expand Down
4 changes: 2 additions & 2 deletions src/core/readiness/appScopes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ export type ScopedApp = {
name: string;
identifier: string;
};
export const iosApps = (apps: AppDescriptor[]): ScopedApp[] => {
export const iosApps = (apps: readonly AppDescriptor[]): ScopedApp[] => {
return apps.flatMap((app) => {
if (app.bundleId) return [{ name: app.name, identifier: app.bundleId }];
return [];
});
};
export const androidApps = (apps: AppDescriptor[]): ScopedApp[] => {
export const androidApps = (apps: readonly AppDescriptor[]): ScopedApp[] => {
return apps.flatMap((app) => {
if (app.packageName) return [{ name: app.name, identifier: app.packageName }];
return [];
Expand Down
2 changes: 1 addition & 1 deletion src/core/readiness/orchestrator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const probe = (id: string, fixedProbeResult: ProbeResult | (() => never)): Readi
};

/** Run probe aggregation with the platform services available to production probes. */
const runProbeSet = (readinessProbes: ReadinessProbe[]) =>
const runProbeSet = (readinessProbes: readonly ReadinessProbe[]) =>
Effect.runPromise(
runProbes(readinessContext, readinessProbes).pipe(
Effect.provide(NodeHttpClient.layer),
Expand Down
Loading
Loading