Compare commits

..
Author SHA1 Message Date
Brent Vatne aadef09d1e [eas-cli] Pass through platform to export command 2022-09-21 15:30:16 -07:00
142 changed files with 870 additions and 1883 deletions

No files matched your search

-1
View File
@@ -17,7 +17,6 @@ This is the log of notable changes to EAS CLI and related packages.
- Add pagination to channel commands. ([#1352](https://github.com/expo/eas-cli/pull/1352) by [@kgc00](https://github.com/kgc00))
- Add pagination to build commands. ([#1353](https://github.com/expo/eas-cli/pull/1353) by [@kgc00](https://github.com/kgc00))
- Provide suggestions for developers when archive size is large. ([#1363](https://github.com/expo/eas-cli/pull/1363) by [@szdziedzic](https://github.com/szdziedzic))
- Improve `eas init` command. ([#1376](https://github.com/expo/eas-cli/pull/1376) by [@wschurman](https://github.com/wschurman))
### 🐛 Bug fixes
-331
View File
@@ -474,30 +474,6 @@
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "limit",
"description": "",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "offset",
"description": "",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
@@ -620,30 +596,6 @@
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "limit",
"description": "",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "offset",
"description": "",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
@@ -6806,18 +6758,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "githubRepositorySettings",
"description": "",
"args": [],
"type": {
"kind": "OBJECT",
"name": "GitHubRepositorySettings",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "githubUrl",
"description": "githubUrl field from most recent classic update manifest",
@@ -15637,49 +15577,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "CreateGitHubRepositorySettingsInput",
"description": "",
"fields": null,
"inputFields": [
{
"name": "appId",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "baseDirectory",
"description": "The base directory is the directory to change to before starting a build. This string should be a properly formatted Unix path starting with '/', './', or the name of the directory relative to the root of the repository. Valid examples include: '/apps/expo-app', './apps/expo-app', and 'apps/expo-app'. This is intended for monorepos or apps that live in a subdirectory of a repository.",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "CreateIosSubmissionInput",
@@ -18049,191 +17946,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "GitHubRepositorySettings",
"description": "",
"fields": [
{
"name": "app",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "App",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "baseDirectory",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "GitHubRepositorySettingsMutation",
"description": "",
"fields": [
{
"name": "createGitHubRepositorySettings",
"description": "Create GitHub repository settings for an App",
"args": [
{
"name": "githubRepositorySettingsData",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "CreateGitHubRepositorySettingsInput",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "GitHubRepositorySettings",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "deleteGitHubRepositorySettings",
"description": "Delete GitHub repository settings by ID",
"args": [
{
"name": "githubRepositorySettingsId",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "GitHubRepositorySettings",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateGitHubRepositorySettings",
"description": "Update GitHub repository settings",
"args": [
{
"name": "githubRepositorySettingsData",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "UpdateGitHubRepositorySettingsInput",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "githubRepositorySettingsId",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "GitHubRepositorySettings",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "GoogleServiceAccountKey",
@@ -23677,22 +23389,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "githubRepositorySettings",
"description": "Mutations for GitHub repository settings",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "GitHubRepositorySettingsMutation",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "googleServiceAccountKey",
"description": "Mutations that modify a Google Service Account Key",
@@ -27822,33 +27518,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "UpdateGitHubRepositorySettingsInput",
"description": "",
"fields": null,
"inputFields": [
{
"name": "baseDirectory",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "UpdateInfoGroup",
+4 -3
View File
@@ -15,6 +15,7 @@ import {
} from '../../project/android/applicationId';
import { resolveGradleBuildContextAsync } from '../../project/android/gradle';
import { toggleConfirmAsync } from '../../prompts';
import { findAccountByName } from '../../user/Account';
import {
BuildRequestSender,
CredentialsResult,
@@ -57,7 +58,7 @@ This means that it will most likely produce an AAB and you will not be able to i
const gradleContext = await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile);
if (ctx.workflow === Workflow.MANAGED) {
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp, ctx.user);
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp);
}
const applicationId = await getApplicationIdAsync(ctx.projectDir, ctx.exp, gradleContext);
@@ -91,7 +92,7 @@ export async function prepareAndroidBuildAsync(
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
? false
: ctx.buildProfile.autoIncrement,
projectId: ctx.projectId,
nonInteractive: ctx.nonInteractive,
});
},
prepareJobAsync: async (
@@ -136,7 +137,7 @@ async function ensureAndroidCredentialsAsync(
const provider = new AndroidCredentialsProvider(ctx.credentialsCtx, {
app: {
account: nullthrows(
ctx.user.accounts.find(a => a.name === ctx.accountName),
findAccountByName(ctx.user.accounts, ctx.accountName),
`You do not have access to account: ${ctx.accountName}`
),
projectName: ctx.projectName,
@@ -5,6 +5,7 @@ import slash from 'slash';
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
import { getUsername } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
@@ -23,7 +24,10 @@ export async function prepareJobAsync(
ctx: BuildContext<Platform.ANDROID>,
jobData: JobData
): Promise<Job> {
const username = getUsername(ctx.exp, ctx.user);
const username = getUsername(
ctx.exp,
await ensureLoggedInAsync({ nonInteractive: ctx.nonInteractive })
);
const buildProfile: BuildProfile<Platform.ANDROID> = ctx.buildProfile;
const projectRootDirectory =
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
@@ -16,12 +16,12 @@ export async function syncProjectConfigurationAsync({
projectDir,
exp,
localAutoIncrement,
projectId,
nonInteractive,
}: {
projectDir: string;
exp: ExpoConfig;
localAutoIncrement?: AndroidVersionAutoIncrement;
projectId: string;
nonInteractive: boolean;
}): Promise<void> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
@@ -29,7 +29,7 @@ export async function syncProjectConfigurationAsync({
if (workflow === Workflow.GENERIC) {
await cleanUpOldEasBuildGradleScriptAsync(projectDir);
if (isExpoUpdatesInstalled(projectDir)) {
await syncUpdatesConfigurationAsync(projectDir, exp, projectId);
await syncUpdatesConfigurationAsync(projectDir, exp, { nonInteractive });
}
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy });
} else {
+10 -9
View File
@@ -10,9 +10,9 @@ import { Analytics, BuildEvent } from '../analytics/events';
import { CredentialsContext } from '../credentials/context';
import { BuildResourceClass } from '../graphql/generated';
import { getExpoConfig } from '../project/expoConfig';
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
import { getOwnerAccountForProjectIdAsync, getProjectIdAsync } from '../project/projectUtils';
import { resolveWorkflowAsync } from '../project/workflow';
import { Actor } from '../user/User';
import { ensureLoggedInAsync } from '../user/actions';
import { createAndroidContextAsync } from './android/build';
import { BuildContext, CommonContext } from './context';
import { createIosContextAsync } from './ios/build';
@@ -30,8 +30,6 @@ export async function createBuildContextAsync<T extends Platform>({
projectDir,
resourceClass,
message,
actor,
projectId,
}: {
buildProfileName: string;
buildProfile: BuildProfile<T>;
@@ -44,22 +42,25 @@ export async function createBuildContextAsync<T extends Platform>({
projectDir: string;
resourceClass: BuildResourceClass;
message?: string;
actor: Actor;
projectId: string;
}): Promise<BuildContext<T>> {
const exp = getExpoConfig(projectDir, { env: buildProfile.env });
const user = await ensureLoggedInAsync({ nonInteractive });
const projectName = exp.slug;
const projectId = await getProjectIdAsync(exp, {
env: buildProfile.env,
nonInteractive,
});
const account = await getOwnerAccountForProjectIdAsync(projectId);
const workflow = await resolveWorkflowAsync(projectDir, platform);
const accountId = account.id;
const runFromCI = getenv.boolish('CI', false);
const credentialsCtx = new CredentialsContext({
projectInfo: { exp, projectId },
exp,
nonInteractive,
projectDir,
user: actor,
user,
env: buildProfile.env,
easJsonCliConfig,
});
@@ -98,7 +99,7 @@ export async function createBuildContextAsync<T extends Platform>({
projectId,
projectName,
trackingCtx,
user: actor,
user,
workflow,
message,
runFromCI,
+2 -2
View File
@@ -23,7 +23,7 @@ export async function createIosContextAsync(
const { buildProfile } = ctx;
if (ctx.workflow === Workflow.MANAGED) {
await ensureBundleIdentifierIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp, ctx.user);
await ensureBundleIdentifierIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp);
}
checkNodeEnvVariable(ctx);
@@ -80,7 +80,7 @@ export async function prepareIosBuildAsync(
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
? false
: ctx.buildProfile.autoIncrement,
projectId: ctx.projectId,
nonInteractive: ctx.nonInteractive,
});
},
prepareJobAsync: async (
+5 -1
View File
@@ -4,6 +4,7 @@ import slash from 'slash';
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
import { getUsername } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
@@ -25,7 +26,10 @@ export async function prepareJobAsync(
): Promise<Job> {
const projectRootDirectory =
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
const username = getUsername(ctx.exp, ctx.user);
const username = getUsername(
ctx.exp,
await ensureLoggedInAsync({ nonInteractive: ctx.nonInteractive })
);
const buildCredentials: Ios.Job['secrets']['buildCredentials'] = {};
if (jobData.credentials) {
const targetNames = Object.keys(jobData.credentials);
@@ -13,20 +13,20 @@ export async function syncProjectConfigurationAsync({
exp,
targets,
localAutoIncrement,
projectId,
nonInteractive,
}: {
projectDir: string;
exp: ExpoConfig;
targets: Target[];
localAutoIncrement?: IosVersionAutoIncrement;
projectId: string;
nonInteractive: boolean;
}): Promise<void> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
if (workflow === Workflow.GENERIC) {
if (isExpoUpdatesInstalled(projectDir)) {
await syncUpdatesConfigurationAsync(projectDir, exp, projectId);
await syncUpdatesConfigurationAsync(projectDir, exp, { nonInteractive });
}
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, targets });
} else {
+5 -1
View File
@@ -14,6 +14,7 @@ import {
readChannelSafelyAsync as readIosChannelSafelyAsync,
readReleaseChannelSafelyAsync as readIosReleaseChannelSafelyAsync,
} from '../update/ios/UpdatesModule';
import { ensureLoggedInAsync } from '../user/actions';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
@@ -48,7 +49,10 @@ export async function collectMetadataAsync<T extends Platform>(
(await vcsClient.getLastCommitMessageAsync()) ?? undefined
),
isGitWorkingTreeDirty: await vcsClient.hasUncommittedChangesAsync(),
username: getUsername(ctx.exp, ctx.user),
username: getUsername(
ctx.exp,
await ensureLoggedInAsync({ nonInteractive: ctx.nonInteractive })
),
message: ctx.message,
...(ctx.platform === Platform.IOS && {
iosEnterpriseProvisioning: resolveIosEnterpriseProvisioning(
@@ -41,7 +41,6 @@ import {
waitToCompleteAsync as waitForSubmissionsToCompleteAsync,
} from '../submit/submit';
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
import { Actor } from '../user/User';
import { printJsonOnlyOutput } from '../utils/json';
import { ProfileData, getProfilesAsync } from '../utils/profiles';
import { getVcsClient } from '../vcs';
@@ -88,12 +87,7 @@ const platformToGraphQLResourceClassMapping: Record<
},
};
export async function runBuildAndSubmitAsync(
projectDir: string,
flags: BuildFlags,
actor: Actor,
projectId: string
): Promise<void> {
export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFlags): Promise<void> {
await getVcsClient().ensureRepoExistsAsync();
await ensureRepoIsCleanAsync(flags.nonInteractive);
@@ -140,8 +134,6 @@ export async function runBuildAndSubmitAsync(
flags.userInputResourceClass ?? UserInputResourceClass.DEFAULT
],
easJsonCliConfig,
actor,
projectId,
});
if (maybeBuild) {
startedBuilds.push({ build: maybeBuild, buildProfile });
@@ -235,8 +227,6 @@ async function prepareAndStartBuildAsync({
buildProfile,
resourceClass,
easJsonCliConfig,
actor,
projectId,
}: {
projectDir: string;
flags: BuildFlags;
@@ -244,8 +234,6 @@ async function prepareAndStartBuildAsync({
buildProfile: ProfileData<'build'>;
resourceClass: BuildResourceClass;
easJsonCliConfig: EasJson['cli'];
actor: Actor;
projectId: string;
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
const buildCtx = await createBuildContextAsync({
buildProfileName: buildProfile.profileName,
@@ -259,8 +247,6 @@ async function prepareAndStartBuildAsync({
localBuildOptions: flags.localBuildOptions,
easJsonCliConfig,
message: flags.message,
actor,
projectId,
});
if (moreBuilds) {
@@ -332,7 +318,6 @@ async function prepareAndStartSubmissionAsync({
env: buildProfile.env,
credentialsCtx: buildCtx.credentialsCtx,
applicationIdentifier: buildCtx.android?.applicationId ?? buildCtx.ios?.bundleIdentifier,
actor: buildCtx.user,
});
if (moreBuilds) {
+8 -103
View File
@@ -13,113 +13,20 @@ import {
logEvent,
} from '../analytics/rudderstackClient';
import { learnMore } from '../log';
import { getExpoConfig } from '../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../project/projectUtils';
import { Actor, getUserAsync } from '../user/User';
import { findProjectRootAsync } from '../project/projectUtils';
import { getUserAsync } from '../user/User';
import { ensureLoggedInAsync } from '../user/actions';
import { easCliVersion } from '../utils/easCli';
import { setVcsClient } from '../vcs';
import GitClient from '../vcs/clients/git';
export interface CommandConfiguration {
export default abstract class EasCommand extends Command {
/**
* When user data is unavailable locally, determines if the command will
* force the user to log in. By default, all commands require authentication.
* force the user to log in
*/
allowUnauthenticated?: boolean;
/**
* Whether a command can be run outside of an Expo project directory.
* By default, all commands must be run inside an Expo project directory.
*/
canRunOutsideProject?: boolean;
}
export interface ContextOptions {
nonInteractive: boolean;
}
export abstract class ContextField<T> {
abstract getValueAsync(options: ContextOptions): Promise<T>;
}
export class ProjectIdContextField extends ContextField<string> {
async getValueAsync({ nonInteractive }: ContextOptions): Promise<string> {
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
return await getProjectIdAsync(exp, { nonInteractive });
}
}
export class OptionalProjectIdContextField extends ContextField<string | undefined> {
async getValueAsync({ nonInteractive }: ContextOptions): Promise<string | undefined> {
const projectDir = await findProjectRootAsync();
if (!projectDir) {
return undefined;
}
const exp = getExpoConfig(projectDir);
return await getProjectIdAsync(exp, { nonInteractive });
}
}
export class ActorContextField extends ContextField<Actor> {
async getValueAsync({ nonInteractive }: ContextOptions): Promise<Actor> {
return await ensureLoggedInAsync({ nonInteractive });
}
}
export const EASCommandProjectIdContext = {
projectId: new ProjectIdContextField(),
};
export const EASCommandProjectIdIfProjectDirContext = {
projectId: new OptionalProjectIdContextField(),
};
export const EASCommandLoggedInContext = {
actor: new ActorContextField(),
};
type ContextInput<
T extends {
[name: string]: any;
} = object
> = {
[P in keyof T]: ContextField<T[P]>;
};
type ContextOutput<
T extends {
[name: string]: any;
} = object
> = {
[P in keyof T]: T[P];
};
export default abstract class EasCommand extends Command {
static contextDefinition: ContextInput = {};
protected commandConfiguration: CommandConfiguration = {};
protected async getContextAsync<
C extends {
[name: string]: any;
} = object
>(
commandClass: { contextDefinition: ContextInput<C> },
{ nonInteractive }: { nonInteractive: boolean }
): Promise<ContextOutput<C>> {
const contextDefinition = commandClass.contextDefinition;
const contextValuePairs = await Promise.all(
Object.keys(contextDefinition).map(async contextKey => {
return [contextKey, await contextDefinition[contextKey].getValueAsync({ nonInteractive })];
})
);
return Object.fromEntries(contextValuePairs);
}
protected requiresAuthentication = true;
protected mustBeRunInsideProject = true;
protected abstract runAsync(): Promise<any>;
@@ -127,26 +34,24 @@ export default abstract class EasCommand extends Command {
async run(): Promise<any> {
await initAnalyticsAsync();
if (!this.commandConfiguration.canRunOutsideProject) {
if (this.mustBeRunInsideProject) {
const projectDir = await findProjectRootAsync();
await this.applyCliConfigAsync(projectDir);
await this.ensureEasCliIsNotInDependenciesAsync(projectDir);
}
if (!this.commandConfiguration.allowUnauthenticated) {
if (this.requiresAuthentication) {
const { flags } = await this.parse();
const nonInteractive = (flags as any)['non-interactive'] ?? false;
await ensureLoggedInAsync({ nonInteractive });
} else {
await getUserAsync();
}
logEvent(AnalyticsEvent.ACTION, {
// id is assigned by oclif in constructor based on the filepath:
// commands/submit === submit, commands/build/list === build:list
action: `eas ${this.id}`,
});
return this.runAsync();
}
@@ -2,7 +2,7 @@ import { flushAsync, initAsync, logEvent } from '../../analytics/rudderstackClie
import { jester as mockJester } from '../../credentials/__tests__/fixtures-constants';
import { getUserAsync } from '../../user/User';
import { ensureLoggedInAsync } from '../../user/actions';
import EasCommand, { CommandConfiguration } from '../EasCommand';
import EasCommand from '../EasCommand';
jest.mock('../../user/User');
jest.mock('../../user/actions', () => ({ ensureLoggedInAsync: jest.fn() }));
@@ -43,9 +43,7 @@ beforeEach(() => {
const createTestEasCommand = (authValue: boolean): typeof EasCommand => {
class TestEasCommand extends EasCommand {
protected override commandConfiguration: CommandConfiguration = {
allowUnauthenticated: !authValue,
};
override requiresAuthentication = authValue;
async runAsync(): Promise<void> {}
}
@@ -1,4 +1,4 @@
import EasCommand, { CommandConfiguration } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { showLoginPromptAsync } from '../../user/actions';
@@ -6,10 +6,8 @@ export default class AccountLogin extends EasCommand {
static override description = 'log in with your Expo account';
static override aliases = ['login'];
protected override commandConfiguration: CommandConfiguration = {
allowUnauthenticated: true,
canRunOutsideProject: true,
};
protected override mustBeRunInsideProject = false;
protected override requiresAuthentication = false;
async runAsync(): Promise<void> {
await showLoginPromptAsync();
@@ -1,4 +1,4 @@
import EasCommand, { CommandConfiguration } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { logoutAsync } from '../../user/User';
@@ -6,10 +6,8 @@ export default class AccountLogout extends EasCommand {
static override description = 'log out';
static override aliases = ['logout'];
protected override commandConfiguration: CommandConfiguration = {
allowUnauthenticated: true,
canRunOutsideProject: true,
};
protected override mustBeRunInsideProject = false;
protected override requiresAuthentication = false;
async runAsync(): Promise<void> {
await logoutAsync();
@@ -1,6 +1,6 @@
import chalk from 'chalk';
import EasCommand, { CommandConfiguration } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { getActorDisplayName, getUserAsync } from '../../user/User';
@@ -8,10 +8,8 @@ export default class AccountView extends EasCommand {
static override description = 'show the username you are logged in as';
static override aliases = ['whoami'];
protected override commandConfiguration: CommandConfiguration = {
allowUnauthenticated: true,
canRunOutsideProject: true,
};
protected override mustBeRunInsideProject = false;
protected override requiresAuthentication = false;
async runAsync(): Promise<void> {
const user = await getUserAsync();
+2 -4
View File
@@ -1,4 +1,4 @@
import EasCommand, { CommandConfiguration } from '../commandUtils/EasCommand';
import EasCommand from '../commandUtils/EasCommand';
import Log from '../log';
import UserSettings from '../user/UserSettings';
@@ -7,9 +7,7 @@ export default class AnalyticsView extends EasCommand {
static override args = [{ name: 'STATUS', options: ['on', 'off'] }];
protected override commandConfiguration: CommandConfiguration = {
allowUnauthenticated: true,
};
protected override requiresAuthentication = false;
async runAsync(): Promise<void> {
const { STATUS: status } = (await this.parse(AnalyticsView)).args;
+10 -10
View File
@@ -2,7 +2,7 @@ import chalk from 'chalk';
import gql from 'graphql-tag';
import { getDefaultBranchNameAsync } from '../../branch/utils';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
@@ -11,7 +11,12 @@ import {
UpdateBranch,
} from '../../graphql/generated';
import Log from '../../log';
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
@@ -61,23 +66,18 @@ export default class BranchCreate extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
let {
args: { name },
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
} = await this.parse(BranchCreate);
const { projectId } = await this.getContextAsync(BranchCreate, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
if (!name) {
+11 -9
View File
@@ -2,7 +2,7 @@ import chalk from 'chalk';
import gql from 'graphql-tag';
import { selectBranchOnAppAsync } from '../../branch/queries';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
@@ -14,7 +14,12 @@ import {
GetBranchInfoQueryVariables,
} from '../../graphql/generated';
import Log from '../../log';
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { toggleConfirmAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
@@ -76,10 +81,6 @@ async function deleteBranchOnAppAsync({
export default class BranchDelete extends EasCommand {
static override description = 'delete a branch';
static override contextDefinition = {
...EASCommandProjectIdContext,
};
static override args = [
{
name: 'name',
@@ -97,14 +98,15 @@ export default class BranchDelete extends EasCommand {
args: { name: branchName },
flags,
} = await this.parse(BranchDelete);
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
if (jsonFlag) {
enableJsonOutput();
}
const { projectId } = await this.getContextAsync(BranchDelete, { nonInteractive });
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
if (!branchName) {
+8 -8
View File
@@ -1,7 +1,9 @@
import { listAndRenderBranchesOnAppAsync } from '../../branch/queries';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { EasPaginatedQueryFlags, getPaginatedQueryOptions } from '../../commandUtils/pagination';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { enableJsonOutput } from '../../utils/json';
export default class BranchList extends EasCommand {
@@ -12,21 +14,19 @@ export default class BranchList extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(BranchList);
const { projectId } = await this.getContextAsync(BranchList, {
nonInteractive: flags['non-interactive'],
});
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
if (paginatedQueryOptions.json) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, {
nonInteractive: flags['non-interactive'],
});
await listAndRenderBranchesOnAppAsync({ projectId, paginatedQueryOptions });
}
}
+10 -9
View File
@@ -2,7 +2,7 @@ import { Flags } from '@oclif/core';
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
@@ -12,7 +12,12 @@ import {
UpdateBranch,
} from '../../graphql/generated';
import Log from '../../log';
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
@@ -62,21 +67,17 @@ export default class BranchRename extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
let {
flags: { json: jsonFlag, from: currentName, to: newName, 'non-interactive': nonInteractive },
} = await this.parse(BranchRename);
const { projectId } = await this.getContextAsync(BranchRename, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
if (!currentName) {
+7 -8
View File
@@ -1,11 +1,13 @@
import { selectBranchOnAppAsync } from '../../branch/queries';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import {
EasPaginatedQueryFlags,
getLimitFlagWithCustomValues,
getPaginatedQueryOptions,
} from '../../commandUtils/pagination';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { listAndRenderUpdateGroupsOnBranchAsync } from '../../update/queries';
import { enableJsonOutput } from '../../utils/json';
@@ -26,25 +28,22 @@ export default class BranchView extends EasCommand {
limit: getLimitFlagWithCustomValues({ defaultTo: 25, limit: 50 }),
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
let {
args: { name: branchName },
flags,
} = await this.parse(BranchView);
const { 'non-interactive': nonInteractive } = flags;
const { projectId } = await this.getContextAsync(BranchView, {
nonInteractive,
});
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
if (paginatedQueryOptions.json) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
if (!branchName) {
if (nonInteractive) {
throw new Error('Branch name may not be empty.');
+10 -9
View File
@@ -1,7 +1,7 @@
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
@@ -14,7 +14,12 @@ import { BuildQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { appPlatformEmojis } from '../../platform';
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { confirmAsync, selectAsync } from '../../prompts';
async function cancelBuildAsync(buildId: string): Promise<Pick<Build, 'id' | 'status'>> {
@@ -128,19 +133,15 @@ export default class BuildCancel extends EasCommand {
...EASNonInteractiveFlag,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const {
args: { BUILD_ID: buildIdFromArg },
flags: { 'non-interactive': nonInteractive },
} = await this.parse(BuildCancel);
const { projectId } = await this.getContextAsync(BuildCancel, {
nonInteractive,
});
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
const displayName = await getDisplayNameForProjectIdAsync(projectId);
if (buildIdFromArg) {
@@ -4,7 +4,7 @@ import chalk from 'chalk';
import { cleanUpOldEasBuildGradleScriptAsync } from '../../build/android/syncProjectConfiguration';
import { ensureProjectConfiguredAsync } from '../../build/configure';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import Log, { learnMore } from '../../log';
import { RequestedPlatform } from '../../platform';
import { getExpoConfig } from '../../project/expoConfig';
@@ -26,15 +26,8 @@ export default class BuildConfigure extends EasCommand {
}),
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(BuildConfigure);
const { projectId } = await this.getContextAsync(BuildConfigure, {
nonInteractive: false,
});
Log.log(
'💡 The following process will configure your iOS and/or Android project to be compatible with EAS Build. These changes only apply to your local project files and you can safely revert them at any time.'
@@ -67,14 +60,14 @@ export default class BuildConfigure extends EasCommand {
if ([RequestedPlatform.Android, RequestedPlatform.All].includes(platform)) {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
if (workflow === Workflow.GENERIC) {
await syncAndroidUpdatesConfigurationAsync(projectDir, exp, projectId);
await syncAndroidUpdatesConfigurationAsync(projectDir, exp, { nonInteractive: false });
}
}
if ([RequestedPlatform.Ios, RequestedPlatform.All].includes(platform)) {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
if (workflow === Workflow.GENERIC) {
await syncIosUpdatesConfigurationAsync(projectDir, exp, projectId);
await syncIosUpdatesConfigurationAsync(projectDir, exp, { nonInteractive: false });
}
}
}
+2 -14
View File
@@ -7,10 +7,7 @@ import path from 'path';
import { BuildFlags, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
import { UserInputResourceClass } from '../../build/types';
import EasCommand, {
EASCommandLoggedInContext,
EASCommandProjectIdContext,
} from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { StatuspageServiceName } from '../../graphql/generated';
import Log, { link } from '../../log';
@@ -99,11 +96,6 @@ export default class Build extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandLoggedInContext,
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags: rawFlags } = await this.parse(Build);
@@ -112,10 +104,6 @@ export default class Build extends EasCommand {
}
const flags = this.sanitizeFlags(rawFlags);
const { actor, projectId } = await this.getContextAsync(Build, {
nonInteractive: flags.nonInteractive,
});
const projectDir = await findProjectRootAsync();
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
@@ -130,7 +118,7 @@ export default class Build extends EasCommand {
const flagsWithPlatform = await this.ensurePlatformSelectedAsync(flags);
await runBuildAndSubmitAsync(projectDir, flagsWithPlatform, actor, projectId);
await runBuildAndSubmitAsync(projectDir, flagsWithPlatform);
}
private sanitizeFlags(
+17 -34
View File
@@ -5,10 +5,7 @@ import path from 'path';
import { v4 as uuidv4 } from 'uuid';
import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
import EasCommand, {
EASCommandLoggedInContext,
EASCommandProjectIdContext,
} from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { ora } from '../../ora';
import { RequestedPlatform } from '../../platform';
@@ -65,17 +62,8 @@ export default class BuildInspect extends EasCommand {
}),
};
static override contextDefinition = {
...EASCommandLoggedInContext,
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(BuildInspect);
const { actor, projectId } = await this.getContextAsync(BuildInspect, {
nonInteractive: false,
});
const outputDirectory = path.resolve(process.cwd(), flags.output);
const tmpWorkingdir = path.join(getTmpDirectory(), uuidv4());
@@ -95,28 +83,23 @@ export default class BuildInspect extends EasCommand {
} else {
const projectDir = await findProjectRootAsync();
try {
await runBuildAndSubmitAsync(
projectDir,
{
nonInteractive: false,
wait: true,
clearCache: false,
json: false,
autoSubmit: false,
requestedPlatform: flags.platform,
profile: flags.profile,
localBuildOptions: {
enable: true,
...(flags.stage === InspectStage.PRE_BUILD ? { skipNativeBuild: true } : {}),
...(flags.stage === InspectStage.POST_BUILD ? { skipCleanup: true } : {}),
verbose: flags.verbose,
workingdir: tmpWorkingdir,
artifactsDir: path.join(tmpWorkingdir, 'artifacts'),
},
await runBuildAndSubmitAsync(projectDir, {
nonInteractive: false,
wait: true,
clearCache: false,
json: false,
autoSubmit: false,
requestedPlatform: flags.platform,
profile: flags.profile,
localBuildOptions: {
enable: true,
...(flags.stage === InspectStage.PRE_BUILD ? { skipNativeBuild: true } : {}),
...(flags.stage === InspectStage.POST_BUILD ? { skipCleanup: true } : {}),
verbose: flags.verbose,
workingdir: tmpWorkingdir,
artifactsDir: path.join(tmpWorkingdir, 'artifacts'),
},
actor,
projectId
);
});
if (!flags.verbose) {
Log.log(chalk.green('Build successful'));
}
+11 -9
View File
@@ -2,7 +2,7 @@ import { Flags } from '@oclif/core';
import { BUILDS_LIMIT, listAndRenderBuildsOnAppAsync } from '../../build/queries';
import { BuildDistributionType, BuildStatus } from '../../build/types';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import {
EasPaginatedQueryFlags,
@@ -15,7 +15,12 @@ import {
BuildStatus as GraphQLBuildStatus,
} from '../../graphql/generated';
import { RequestedPlatform } from '../../platform';
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { enableJsonOutput } from '../../utils/json';
export default class BuildList extends EasCommand {
@@ -55,10 +60,6 @@ export default class BuildList extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(BuildList);
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
@@ -69,9 +70,6 @@ export default class BuildList extends EasCommand {
distribution: buildDistribution,
'non-interactive': nonInteractive,
} = flags;
const { projectId } = await this.getContextAsync(BuildList, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
@@ -79,6 +77,10 @@ export default class BuildList extends EasCommand {
const platform = toAppPlatform(requestedPlatform);
const graphqlBuildStatus = toGraphQLBuildStatus(buildStatus);
const graphqlBuildDistribution = toGraphQLBuildDistribution(buildDistribution);
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
const displayName = await getDisplayNameForProjectIdAsync(projectId);
await listAndRenderBuildsOnAppAsync({
@@ -4,10 +4,7 @@ import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand, {
EASCommandLoggedInContext,
EASCommandProjectIdContext,
} from '../../../commandUtils/EasCommand';
import EasCommand from '../../../commandUtils/EasCommand';
import { AppVersionMutation } from '../../../graphql/mutations/AppVersionMutation';
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
@@ -20,6 +17,7 @@ import { BUILD_NUMBER_REQUIREMENTS, isValidBuildNumber } from '../../../project/
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getProjectIdAsync,
} from '../../../project/projectUtils';
import {
ensureVersionSourceIsRemoteAsync,
@@ -44,16 +42,8 @@ export default class BuildVersionSetView extends EasCommand {
}),
};
static override contextDefinition = {
...EASCommandLoggedInContext,
...EASCommandProjectIdContext,
};
public async runAsync(): Promise<void> {
const { flags } = await this.parse(BuildVersionSetView);
const { actor, projectId } = await this.getContextAsync(BuildVersionSetView, {
nonInteractive: false,
});
const projectDir = await findProjectRootAsync();
@@ -67,6 +57,9 @@ export default class BuildVersionSetView extends EasCommand {
);
const exp = getExpoConfig(projectDir, { env: profile.env });
// this command is always interactive (see prompt below)
const projectId = await getProjectIdAsync(exp, { nonInteractive: false });
const displayName = await getDisplayNameForProjectIdAsync(projectId);
validateAppConfigForRemoteVersionSource(exp, platform);
@@ -75,8 +68,7 @@ export default class BuildVersionSetView extends EasCommand {
projectDir,
exp,
profile,
platform,
actor
platform
);
const remoteVersions = await AppVersionQuery.latestVersionAsync(
projectId,
@@ -6,10 +6,7 @@ import chalk from 'chalk';
import { updateNativeVersionsAsync as updateAndroidNativeVersionsAsync } from '../../../build/android/version';
import { updateNativeVersionsAsync as updateIosNativeVersionsAsync } from '../../../build/ios/version';
import EasCommand, {
EASCommandLoggedInContext,
EASCommandProjectIdContext,
} from '../../../commandUtils/EasCommand';
import EasCommand from '../../../commandUtils/EasCommand';
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
import Log from '../../../log';
@@ -25,7 +22,7 @@ import { getExpoConfig } from '../../../project/expoConfig';
import { resolveXcodeBuildContextAsync } from '../../../project/ios/scheme';
import { resolveTargetsAsync } from '../../../project/ios/target';
import { BUILD_NUMBER_REQUIREMENTS, isValidBuildNumber } from '../../../project/ios/versions';
import { findProjectRootAsync } from '../../../project/projectUtils';
import { findProjectRootAsync, getProjectIdAsync } from '../../../project/projectUtils';
import {
ensureVersionSourceIsRemoteAsync,
getBuildVersionName,
@@ -59,16 +56,8 @@ export default class BuildVersionSyncView extends EasCommand {
}),
};
static override contextDefinition = {
...EASCommandLoggedInContext,
...EASCommandProjectIdContext,
};
public async runAsync(): Promise<void> {
const { flags } = await this.parse(BuildVersionSyncView);
const { actor, projectId } = await this.getContextAsync(BuildVersionSyncView, {
nonInteractive: true,
});
const projectDir = await findProjectRootAsync();
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
@@ -85,6 +74,9 @@ export default class BuildVersionSyncView extends EasCommand {
for (const profileInfo of buildProfiles) {
const exp = getExpoConfig(projectDir, { env: profileInfo.profile.env });
// this command is always non-interactive
const projectId = await getProjectIdAsync(exp, { nonInteractive: true });
validateAppConfigForRemoteVersionSource(exp, profileInfo.platform);
const platformDisplayName = appPlatformDisplayNames[toAppPlatform(profileInfo.platform)];
@@ -92,8 +84,7 @@ export default class BuildVersionSyncView extends EasCommand {
projectDir,
exp,
profileInfo.profile,
profileInfo.platform,
actor
profileInfo.platform
);
const remoteVersions = await AppVersionQuery.latestVersionAsync(
projectId,
+12 -9
View File
@@ -1,11 +1,16 @@
import { formatGraphQLBuild } from '../../build/utils/formatBuild';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasJsonOnlyFlag } from '../../commandUtils/flags';
import { BuildFragment } from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export default class BuildView extends EasCommand {
@@ -17,22 +22,20 @@ export default class BuildView extends EasCommand {
...EasJsonOnlyFlag,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const {
args: { BUILD_ID: buildId },
flags,
} = await this.parse(BuildView);
const { projectId } = await this.getContextAsync(BuildView, {
nonInteractive: true,
});
if (flags.json) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
// this command is always non-interactive
const projectId = await getProjectIdAsync(exp, { nonInteractive: true });
const displayName = await getDisplayNameForProjectIdAsync(projectId);
const spinner = ora().start('Fetching the build…');
@@ -2,7 +2,7 @@ import chalk from 'chalk';
import gql from 'graphql-tag';
import { BranchNotFoundError } from '../../branch/utils';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
@@ -11,7 +11,12 @@ import {
} from '../../graphql/generated';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import Log from '../../log';
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
@@ -70,22 +75,19 @@ export default class ChannelCreate extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
let {
args: { name: channelName },
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
} = await this.parse(ChannelCreate);
const { projectId } = await this.getContextAsync(ChannelCreate, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
if (!channelName) {
const validationMessage = 'Channel name may not be empty.';
if (nonInteractive) {
@@ -1,7 +1,7 @@
import gql from 'graphql-tag';
import { selectChannelOnAppAsync } from '../../channel/queries';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
@@ -11,6 +11,8 @@ import {
} from '../../graphql/generated';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { toggleConfirmAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
@@ -29,22 +31,19 @@ export default class ChannelDelete extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const {
args: { name: nameArg },
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
} = await this.parse(ChannelDelete);
const { projectId } = await this.getContextAsync(ChannelDelete, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
let channelId, channelName;
if (nameArg) {
const { id, name } = await ChannelQuery.viewUpdateChannelAsync({
@@ -4,7 +4,7 @@ import gql from 'graphql-tag';
import { selectBranchOnAppAsync } from '../../branch/queries';
import { selectChannelOnAppAsync } from '../../channel/queries';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
@@ -14,6 +14,8 @@ import {
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export async function updateChannelBranchMappingAsync({
@@ -65,22 +67,19 @@ export default class ChannelEdit extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const {
args,
flags: { branch: branchFlag, json, 'non-interactive': nonInteractive },
} = await this.parse(ChannelEdit);
const { projectId } = await this.getContextAsync(ChannelEdit, {
nonInteractive,
});
if (json) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
const existingChannel = args.name
? await ChannelQuery.viewUpdateChannelAsync({ appId: projectId, channelName: args.name })
: await selectChannelOnAppAsync({
@@ -1,11 +1,13 @@
import { CHANNELS_LIMIT, listAndRenderChannelsOnAppAsync } from '../../channel/queries';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import {
EasPaginatedQueryFlags,
getLimitFlagWithCustomValues,
getPaginatedQueryOptions,
} from '../../commandUtils/pagination';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { enableJsonOutput } from '../../utils/json';
export default class ChannelList extends EasCommand {
@@ -17,21 +19,18 @@ export default class ChannelList extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(ChannelList);
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
const { projectId } = await this.getContextAsync(ChannelList, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
await listAndRenderChannelsOnAppAsync({
projectId,
paginatedQueryOptions,
@@ -2,13 +2,18 @@ import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { BranchMapping, getBranchMapping } from '../../channel/utils';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { UpdateBranch } from '../../graphql/generated';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import { ChannelQuery, UpdateChannelByNameObject } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync, selectAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { updateChannelBranchMappingAsync } from './edit';
@@ -311,10 +316,6 @@ export default class ChannelRollout extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const {
args: { channel: channelName },
@@ -326,13 +327,13 @@ export default class ChannelRollout extends EasCommand {
'non-interactive': nonInteractive,
},
} = await this.parse(ChannelRollout);
const { projectId } = await this.getContextAsync(ChannelRollout, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
const channel = await ChannelQuery.viewUpdateChannelAsync({
@@ -4,9 +4,11 @@ import {
listAndRenderBranchesAndUpdatesOnChannelAsync,
selectChannelOnAppAsync,
} from '../../channel/queries';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { EasPaginatedQueryFlags, getPaginatedQueryOptions } from '../../commandUtils/pagination';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { enableJsonOutput } from '../../utils/json';
export default class ChannelView extends EasCommand {
@@ -25,10 +27,6 @@ export default class ChannelView extends EasCommand {
...EasPaginatedQueryFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
let {
args: { name: channelName },
@@ -36,13 +34,14 @@ export default class ChannelView extends EasCommand {
} = await this.parse(ChannelView);
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
const { projectId } = await this.getContextAsync(ChannelView, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
if (!channelName) {
const validationMessage = 'A channel name is required to view a specific channel.';
if (nonInteractive) {
+2 -4
View File
@@ -4,7 +4,7 @@ import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand, { CommandConfiguration } from '../commandUtils/EasCommand';
import EasCommand from '../commandUtils/EasCommand';
import { toAppPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
import { appPlatformEmojis } from '../platform';
@@ -25,9 +25,7 @@ export default class Config extends EasCommand {
}),
};
protected override commandConfiguration: CommandConfiguration = {
allowUnauthenticated: true,
};
protected override requiresAuthentication = false;
async runAsync(): Promise<void> {
const { flags } = await this.parse(Config);
+2 -17
View File
@@ -1,12 +1,7 @@
import { Flags } from '@oclif/core';
import EasCommand, {
EASCommandLoggedInContext,
EASCommandProjectIdContext,
} from '../commandUtils/EasCommand';
import EasCommand from '../commandUtils/EasCommand';
import { SelectPlatform } from '../credentials/manager/SelectPlatform';
import { getExpoConfig } from '../project/expoConfig';
import { findProjectRootAsync } from '../project/projectUtils';
export default class Credentials extends EasCommand {
static override description = 'manage credentials';
@@ -15,18 +10,8 @@ export default class Credentials extends EasCommand {
platform: Flags.enum({ char: 'p', options: ['android', 'ios'] }),
};
static override contextDefinition = {
...EASCommandLoggedInContext,
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(Credentials);
const { actor, projectId } = await this.getContextAsync(Credentials, { nonInteractive: false });
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
await new SelectPlatform(actor, { projectId, exp }, flags.platform).runAsync();
await new SelectPlatform(flags.platform).runAsync();
}
}
+4 -13
View File
@@ -1,26 +1,17 @@
import EasCommand, {
EASCommandLoggedInContext,
EASCommandProjectIdContext,
} from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import AppStoreApi from '../../credentials/ios/appstore/AppStoreApi';
import { createContextAsync } from '../../devices/context';
import DeviceManager from '../../devices/manager';
import { ensureLoggedInAsync } from '../../user/actions';
export default class DeviceCreate extends EasCommand {
static override description = 'register new Apple Devices to use for internal distribution';
static override contextDefinition = {
...EASCommandLoggedInContext,
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
// this command is interactive by design
const { actor, projectId } = await this.getContextAsync(DeviceCreate, {
nonInteractive: false,
});
const user = await ensureLoggedInAsync({ nonInteractive: false });
const ctx = await createContextAsync({ appStore: new AppStoreApi(), user: actor, projectId });
const ctx = await createContextAsync({ appStore: new AppStoreApi(), user });
const manager = new DeviceManager(ctx);
await manager.createAsync();
}
+11 -9
View File
@@ -2,7 +2,7 @@ import { Device, DeviceStatus } from '@expo/apple-utils';
import { Flags } from '@oclif/core';
import assert from 'assert';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { chooseDevicesToDeleteAsync } from '../../credentials/ios/actions/DeviceUtils';
import { AppleDeviceMutation } from '../../credentials/ios/api/graphql/mutations/AppleDeviceMutation';
import {
@@ -16,7 +16,12 @@ import formatDevice from '../../devices/utils/formatDevice';
import { AppleDevice, Maybe } from '../../graphql/generated';
import Log from '../../log';
import { ora } from '../../ora';
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getOwnerAccountForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
export default class DeviceDelete extends EasCommand {
@@ -27,18 +32,15 @@ export default class DeviceDelete extends EasCommand {
udid: Flags.string({ multiple: true }),
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
let {
flags: { 'apple-team-id': appleTeamIdentifier, udid: udids },
} = await this.parse(DeviceDelete);
const { projectId } = await this.getContextAsync(DeviceDelete, {
nonInteractive: false,
});
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
// this command is interactive by design
const projectId = await getProjectIdAsync(exp, { nonInteractive: false });
const account = await getOwnerAccountForProjectIdAsync(projectId);
if (!appleTeamIdentifier) {
+12 -9
View File
@@ -2,13 +2,18 @@ import { Flags } from '@oclif/core';
import assert from 'assert';
import chalk from 'chalk';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleTeamQuery';
import formatDevice from '../../devices/utils/formatDevice';
import Log from '../../log';
import { Ora, ora } from '../../ora';
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getOwnerAccountForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
export default class BuildList extends EasCommand {
@@ -18,16 +23,14 @@ export default class BuildList extends EasCommand {
'apple-team-id': Flags.string(),
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
let appleTeamIdentifier = (await this.parse(BuildList)).flags['apple-team-id'];
const { projectId } = await this.getContextAsync(BuildList, {
nonInteractive: false,
});
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
// this command is interactive by design
const projectId = await getProjectIdAsync(exp, { nonInteractive: false });
const account = await getOwnerAccountForProjectIdAsync(projectId);
let spinner: Ora;
+13 -9
View File
@@ -1,19 +1,20 @@
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
import formatDevice from '../../devices/utils/formatDevice';
import Log from '../../log';
import { ora } from '../../ora';
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getOwnerAccountForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
export default class DeviceView extends EasCommand {
static override description = 'view a device for your project';
static override args = [{ name: 'UDID' }];
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { UDID } = (await this.parse(DeviceView)).args;
@@ -30,9 +31,12 @@ If you are not sure what is the UDID of the device you are looking for, run:
);
throw new Error('Device UDID is missing');
}
const { projectId } = await this.getContextAsync(DeviceView, {
nonInteractive: true,
});
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
// this command is non-interactive by design
const projectId = await getProjectIdAsync(exp, { nonInteractive: true });
const account = await getOwnerAccountForProjectIdAsync(projectId);
const spinner = ora().start(`Fetching device details for ${UDID}…`);
+2 -4
View File
@@ -1,7 +1,7 @@
import { Platform } from '@expo/eas-build-job';
import envinfo from 'envinfo';
import EasCommand, { CommandConfiguration } from '../commandUtils/EasCommand';
import EasCommand from '../commandUtils/EasCommand';
import Log from '../log';
import { findProjectRootAsync } from '../project/projectUtils';
import { resolveWorkflowAsync } from '../project/workflow';
@@ -10,9 +10,7 @@ import { easCliVersion } from '../utils/easCli';
export default class Diagnostics extends EasCommand {
static override description = 'display environment info';
protected override commandConfiguration: CommandConfiguration = {
allowUnauthenticated: true,
};
protected override requiresAuthentication = false;
async runAsync(): Promise<void> {
const info = await envinfo.run(
+6 -16
View File
@@ -4,16 +4,14 @@ import chalk from 'chalk';
import path from 'path';
import { ensureProjectConfiguredAsync } from '../../build/configure';
import EasCommand, {
EASCommandLoggedInContext,
EASCommandProjectIdContext,
} from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { CredentialsContext } from '../../credentials/context';
import Log, { learnMore } from '../../log';
import { createMetadataContextAsync } from '../../metadata/context';
import { downloadMetadataAsync } from '../../metadata/download';
import { handleMetadataError } from '../../metadata/errors';
import { findProjectRootAsync } from '../../project/projectUtils';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
export default class MetadataPull extends EasCommand {
static override description = 'generate the local store configuration from the app stores';
@@ -26,29 +24,21 @@ export default class MetadataPull extends EasCommand {
}),
};
static override contextDefinition = {
...EASCommandProjectIdContext,
...EASCommandLoggedInContext,
};
async runAsync(): Promise<void> {
Log.warn('EAS Metadata is in beta and subject to breaking changes.');
const { flags } = await this.parse(MetadataPull);
const { actor, projectId } = await this.getContextAsync(MetadataPull, {
nonInteractive: false,
});
const projectDir = await findProjectRootAsync();
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
// this command is interactive (all nonInteractive flags passed to utility functions are false)
await getProjectIdAsync(exp, { nonInteractive: false });
await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false });
const credentialsCtx = new CredentialsContext({
projectInfo: { exp, projectId },
exp,
projectDir,
user: actor,
user: await ensureLoggedInAsync({ nonInteractive: false }),
nonInteractive: false,
});
+6 -16
View File
@@ -2,16 +2,14 @@ import { getConfig } from '@expo/config';
import { Flags } from '@oclif/core';
import { ensureProjectConfiguredAsync } from '../../build/configure';
import EasCommand, {
EASCommandLoggedInContext,
EASCommandProjectIdContext,
} from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { CredentialsContext } from '../../credentials/context';
import Log, { learnMore } from '../../log';
import { createMetadataContextAsync } from '../../metadata/context';
import { handleMetadataError } from '../../metadata/errors';
import { uploadMetadataAsync } from '../../metadata/upload';
import { findProjectRootAsync } from '../../project/projectUtils';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
export default class MetadataPush extends EasCommand {
static override description = 'sync the local store configuration to the app stores';
@@ -24,29 +22,21 @@ export default class MetadataPush extends EasCommand {
}),
};
static override contextDefinition = {
...EASCommandProjectIdContext,
...EASCommandLoggedInContext,
};
async runAsync(): Promise<void> {
Log.warn('EAS Metadata is in beta and subject to breaking changes.');
const { flags } = await this.parse(MetadataPush);
const { actor, projectId } = await this.getContextAsync(MetadataPush, {
nonInteractive: false,
});
const projectDir = await findProjectRootAsync();
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
// this command is interactive (all nonInteractive flags passed to utility functions are false)
await getProjectIdAsync(exp, { nonInteractive: false });
await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false });
const credentialsCtx = new CredentialsContext({
projectInfo: { exp, projectId },
exp,
projectDir,
user: actor,
user: await ensureLoggedInAsync({ nonInteractive: false }),
nonInteractive: false,
});
+9 -10
View File
@@ -1,26 +1,25 @@
import openBrowserAsync from 'better-opn';
import { getProjectDashboardUrl } from '../build/utils/url';
import EasCommand, { EASCommandProjectIdContext } from '../commandUtils/EasCommand';
import EasCommand from '../commandUtils/EasCommand';
import { ora } from '../ora';
import { getExpoConfig } from '../project/expoConfig';
import { findProjectRootAsync, getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
import {
findProjectRootAsync,
getOwnerAccountForProjectIdAsync,
getProjectIdAsync,
} from '../project/projectUtils';
export default class Open extends EasCommand {
static override description = 'open the project page in a web browser';
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
// this command is interactive by nature (only really run by humans in a terminal)
const { projectId } = await this.getContextAsync(Open, {
nonInteractive: false,
});
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
// this command is interactive by nature (only really run by humans in a terminal)
// this ensures the project exists before opening the browser
const projectId = await getProjectIdAsync(exp, { nonInteractive: false });
const account = await getOwnerAccountForProjectIdAsync(projectId);
const projectName = exp.slug;
@@ -1,236 +0,0 @@
import { AppJSONConfig, PackageJSONConfig, getConfig } from '@expo/config';
import chalk from 'chalk';
import { vol } from 'memfs';
import { jester } from '../../../credentials/__tests__/fixtures-constants';
import { AppMutation } from '../../../graphql/mutations/AppMutation';
import { findProjectIdByAccountNameAndSlugNullableAsync } from '../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
import { findProjectRootAsync, saveProjectIdToAppConfigAsync } from '../../../project/projectUtils';
import { confirmAsync, promptAsync } from '../../../prompts';
import { ensureLoggedInAsync } from '../../../user/actions';
import ProjectInit from '../init';
jest.mock('fs');
jest.mock('@expo/config');
jest.mock('../../../project/projectUtils');
jest.mock('../../../prompts');
jest.mock('../../../user/actions');
jest.mock('../../../graphql/mutations/AppMutation');
jest.mock('../../../ora', () => ({
ora: () => ({
start: () => ({ succeed: () => {}, fail: () => {} }),
}),
}));
jest.mock('../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync');
let originalProcessArgv: string[];
beforeAll(() => {
originalProcessArgv = process.argv;
process.argv = [];
});
afterAll(() => {
process.argv = originalProcessArgv;
});
function mockTestProject(options: {
configuredProjectId?: string;
configuredOwner?: string;
}): void {
const projectRoot = '/test-project';
const packageJSON: PackageJSONConfig = {
name: 'testing123',
version: '0.1.0',
description: 'fake description',
main: 'index.js',
};
const appJSON: AppJSONConfig = {
expo: {
name: 'testing 123',
version: '0.1.0',
slug: 'testing-123',
sdkVersion: '33.0.0',
owner: options.configuredOwner,
extra: {
eas: {
projectId: options.configuredProjectId,
},
},
},
};
vol.fromJSON(
{
[projectRoot + '/package.json']: JSON.stringify(packageJSON, null, 2),
[projectRoot + '/app.json']: JSON.stringify(appJSON, null, 2),
},
'/'
);
const mockManifest = { exp: appJSON.expo };
jest.mocked(getConfig).mockReturnValue(mockManifest as any);
jest.mocked(findProjectRootAsync).mockResolvedValue('/test-project');
}
const commandOptions = { root: '/test-project' } as any;
beforeEach(() => {
jest.resetAllMocks();
jest.mocked(ensureLoggedInAsync).mockResolvedValue(jester);
});
describe(ProjectInit.name, () => {
describe('when id flag is provided', () => {
describe('when it is already configured', () => {
beforeEach(() => {
mockTestProject({ configuredProjectId: '1234', configuredOwner: jester.accounts[0].name });
});
describe('interactive', () => {
it('is no-op if already configured for id', async () => {
await new ProjectInit(['--id', '1234'], commandOptions).run();
expect(saveProjectIdToAppConfigAsync).not.toHaveBeenCalled();
});
it('prompts to overwrite when different', async () => {
jest.mocked(confirmAsync).mockResolvedValue(true);
await new ProjectInit(['--id', '12345'], commandOptions).run();
expect(saveProjectIdToAppConfigAsync).toHaveBeenCalledWith('/test-project', '12345');
expect(confirmAsync).toHaveBeenCalled();
});
it('aborts when prompt to overwrite is declined', async () => {
jest.mocked(confirmAsync).mockResolvedValue(false);
await new ProjectInit(['--id', '12345'], commandOptions).run();
expect(saveProjectIdToAppConfigAsync).not.toHaveBeenCalled();
expect(confirmAsync).toHaveBeenCalled();
});
});
describe('force', () => {
it('does not prompt to overwrite when different', async () => {
await new ProjectInit(['--id', '12345', '--force'], {
root: '/test-project',
} as any).run();
expect(saveProjectIdToAppConfigAsync).toHaveBeenCalledWith('/test-project', '12345');
expect(confirmAsync).not.toHaveBeenCalled();
});
});
describe('non-interactive', () => {
it('aborts when different', async () => {
await expect(
new ProjectInit(['--id', '12345', '--non-interactive'], {
root: '/test-project',
} as any).run()
).rejects.toThrowError(
`Project is already linked to a different ID: ${chalk.bold(
'1234'
)}. Use --force flag to overwrite.`
);
expect(saveProjectIdToAppConfigAsync).not.toHaveBeenCalled();
});
});
});
describe('when it is not yet configured', () => {
beforeEach(() => {
mockTestProject({});
});
it('configures', async () => {
await new ProjectInit(['--id', '1234'], commandOptions).run();
expect(saveProjectIdToAppConfigAsync).toHaveBeenCalledWith('/test-project', '1234');
});
});
});
describe('when no arguments are provided', () => {
describe('when it is already configured', () => {
beforeEach(() => {
mockTestProject({ configuredProjectId: '1234', configuredOwner: jester.accounts[0].name });
});
it('does not configure', async () => {
await new ProjectInit([], commandOptions).run();
expect(saveProjectIdToAppConfigAsync).not.toHaveBeenCalled();
});
});
describe('when it is not yet configured', () => {
describe('when owner is specified', () => {
beforeEach(() => {
mockTestProject({ configuredOwner: jester.accounts[0].name });
});
describe('when project exists on server', () => {
beforeEach(() => {
jest.mocked(findProjectIdByAccountNameAndSlugNullableAsync).mockResolvedValue('123456');
});
it('prompts for confirmation to link', async () => {
jest.mocked(confirmAsync).mockResolvedValue(true);
await new ProjectInit([], commandOptions).run();
expect(saveProjectIdToAppConfigAsync).toHaveBeenCalledWith('/test-project', '123456');
expect(confirmAsync).toHaveBeenCalledTimes(1);
});
it('does not save when confirmation to link is denied', async () => {
jest.mocked(confirmAsync).mockResolvedValue(false);
await expect(new ProjectInit([], commandOptions).run()).rejects.toThrow(
'Project ID configuration canceled. Re-run the command to select a different account/project.'
);
expect(saveProjectIdToAppConfigAsync).not.toHaveBeenCalled();
expect(confirmAsync).toHaveBeenCalledTimes(1);
});
});
describe('when project does not exist on server', () => {
it('asks to create it', async () => {
jest.mocked(confirmAsync).mockResolvedValue(true);
jest.mocked(AppMutation.createAppAsync).mockResolvedValue('0129');
await new ProjectInit([], commandOptions).run();
expect(saveProjectIdToAppConfigAsync).toHaveBeenCalledWith('/test-project', '0129');
expect(confirmAsync).toHaveBeenCalledTimes(1);
});
it('does not create it if declined', async () => {
jest.mocked(confirmAsync).mockResolvedValue(false);
await expect(new ProjectInit([], commandOptions).run()).rejects.toThrowError(
'Project ID configuration canceled for @jester/testing-123.'
);
expect(saveProjectIdToAppConfigAsync).not.toHaveBeenCalled();
expect(confirmAsync).toHaveBeenCalledTimes(1);
});
});
});
describe('when owner is not specified', () => {
beforeEach(() => {
mockTestProject({});
});
it('prompts for an account', async () => {
jest.mocked(promptAsync).mockResolvedValue({ account: { name: 'other' } });
await expect(new ProjectInit([], commandOptions).run()).rejects.toThrowError(
`You don't have permission to create a new project on the other account and no matching project already exists on the account.`
);
expect(promptAsync).toHaveBeenCalledTimes(1);
});
});
it('throws when the user does not have permission to create projects on account', async () => {
mockTestProject({ configuredOwner: jester.accounts[1].name });
jest.mocked(AppMutation.createAppAsync).mockResolvedValue('0129');
await expect(new ProjectInit([], commandOptions).run()).rejects.toThrowError(
`You don't have permission to create a new project on the other account and no matching project already exists on the account.`
);
expect(saveProjectIdToAppConfigAsync).not.toHaveBeenCalled();
});
});
});
});
@@ -1,9 +1,11 @@
import gql from 'graphql-tag';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import { AppInfoQuery, AppInfoQueryVariables } from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import formatFields from '../../utils/formatFields';
async function projectInfoByIdAsync(appId: string): Promise<AppInfoQuery> {
@@ -32,14 +34,12 @@ async function projectInfoByIdAsync(appId: string): Promise<AppInfoQuery> {
export default class ProjectInfo extends EasCommand {
static override description = 'information about the current project';
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { projectId } = await this.getContextAsync(ProjectInfo, {
nonInteractive: true,
});
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
// this command is non-interactive by design
const projectId = await getProjectIdAsync(exp, { nonInteractive: true });
const { app } = await projectInfoByIdAsync(projectId);
if (!app) {
+13 -215
View File
@@ -1,229 +1,27 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import terminalLink from 'terminal-link';
import { getProjectDashboardUrl } from '../../build/utils/url';
import EasCommand, { EASCommandLoggedInContext } from '../../commandUtils/EasCommand';
import { AppPrivacy, Role } from '../../graphql/generated';
import { AppMutation } from '../../graphql/mutations/AppMutation';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectIdByAccountNameAndSlugNullableAsync } from '../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
import {
findProjectRootAsync,
saveProjectIdToAppConfigAsync,
toAppPrivacy,
} from '../../project/projectUtils';
import { confirmAsync, promptAsync } from '../../prompts';
import { Actor } from '../../user/User';
type InitializeMethodOptions = {
force: boolean;
nonInteractive: boolean;
};
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
export default class ProjectInit extends EasCommand {
static override description = 'create or link an EAS project';
static override aliases = ['init'];
static override flags = {
id: Flags.string({
description: 'ID of the EAS project to link',
}),
force: Flags.boolean({
description: 'Whether to overwrite any existing project ID',
dependsOn: ['id'],
}),
// this is the same as EASNonInteractiveFlag but with the dependsOn
'non-interactive': Flags.boolean({
description: 'Run the command in non-interactive mode.',
dependsOn: ['id'],
}),
};
static override contextDefinition = {
...EASCommandLoggedInContext,
};
private static async saveProjectIdAndLogSuccessAsync(
projectDir: string,
projectId: string
): Promise<void> {
await saveProjectIdToAppConfigAsync(projectDir, projectId);
Log.withTick(`Project successfully linked (ID: ${chalk.bold(projectId)}) (modified app.json)`);
}
private static async initializeWithExplicitIDAsync(
projectId: string,
{ force, nonInteractive }: InitializeMethodOptions
): Promise<void> {
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const existingProjectId = exp.extra?.eas?.projectId;
if (projectId === existingProjectId) {
Log.succeed(`Project already linked (ID: ${chalk.bold(existingProjectId)})`);
return;
}
if (!existingProjectId) {
await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, projectId);
return;
}
if (projectId !== existingProjectId) {
if (force) {
await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, projectId);
return;
}
if (nonInteractive) {
throw new Error(
`Project is already linked to a different ID: ${chalk.bold(
existingProjectId
)}. Use --force flag to overwrite.`
);
}
const confirm = await confirmAsync({
message: `Project is already linked to a different ID: ${chalk.bold(
existingProjectId
)}. Do you wish to overwrite it?`,
});
if (!confirm) {
Log.log('Aborting');
return;
}
await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, projectId);
}
}
private static async initializeWithInteractiveSelectionAsync(actor: Actor): Promise<void> {
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const existingProjectId = exp.extra?.eas?.projectId;
if (existingProjectId) {
Log.succeed(
`Project already linked (ID: ${chalk.bold(
existingProjectId
)}). To re-configure, remove the extra.eas.projectId field from your app config.`
);
return;
}
const allAccounts = actor.accounts;
const accountNamesWhereUserHasSufficientPermissionsToCreateApp = new Set(
allAccounts
.filter(a => a.users.find(it => it.actor.id === actor.id)?.role !== Role.ViewOnly)
.map(it => it.name)
);
// if no owner field, ask the user which account they want to use to create/link the project
let accountName = exp.owner;
if (!accountName) {
if (allAccounts.length === 1) {
accountName = allAccounts[0].name;
} else {
// if regular user, put primary account first
const sortedAccounts =
actor.__typename === 'Robot'
? allAccounts
: [...allAccounts].sort((a, _b) => (a.name === actor.username ? -1 : 1));
const choices = sortedAccounts.map(account => ({
title: account.name,
value: account,
description: !accountNamesWhereUserHasSufficientPermissionsToCreateApp.has(account.name)
? '(Viewer Role)'
: undefined,
}));
accountName = (
await promptAsync({
type: 'select',
name: 'account',
message: 'Which account should own this project?',
choices,
})
).account.name;
}
}
if (!accountName) {
throw new Error('No account selected for project. Canceling.');
}
const projectName = exp.slug;
const projectFullName = `@${accountName}/${projectName}`;
const existingProjectIdOnServer = await findProjectIdByAccountNameAndSlugNullableAsync(
accountName,
projectName
);
if (existingProjectIdOnServer) {
const affirmedLink = await confirmAsync({
message: `Existing project found: ${projectFullName} (ID: ${existingProjectIdOnServer}). Link this project?`,
});
if (!affirmedLink) {
throw new Error(
`Project ID configuration canceled. Re-run the command to select a different account/project.`
);
}
await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, existingProjectIdOnServer);
return;
}
if (!accountNamesWhereUserHasSufficientPermissionsToCreateApp.has(accountName)) {
throw new Error(
`You don't have permission to create a new project on the ${accountName} account and no matching project already exists on the account.`
);
}
const affirmedCreate = await confirmAsync({
message: `Would you like to create a project for ${projectFullName}?`,
});
if (!affirmedCreate) {
throw new Error(`Project ID configuration canceled for ${projectFullName}.`);
}
const projectDashboardUrl = getProjectDashboardUrl(accountName, projectName);
const projectLink = terminalLink(projectFullName, projectDashboardUrl, {
// https://github.com/sindresorhus/terminal-link/issues/18#issuecomment-1068020361
fallback: () => `${projectFullName} (${projectDashboardUrl})`,
});
const account = nullthrows(allAccounts.find(a => a.name === accountName));
const spinner = ora(`Creating ${chalk.bold(projectFullName)}`).start();
let createdProjectId: string;
try {
createdProjectId = await AppMutation.createAppAsync({
accountId: account.id,
projectName,
privacy: toAppPrivacy(exp.privacy) ?? AppPrivacy.Public,
});
spinner.succeed(`Created ${chalk.bold(projectLink)}`);
} catch (err) {
spinner.fail();
throw err;
}
await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, createdProjectId);
}
async runAsync(): Promise<void> {
const {
flags: { id, force, 'non-interactive': nonInteractive },
} = await this.parse(ProjectInit);
const { actor } = await this.getContextAsync(ProjectInit, { nonInteractive });
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
if (id) {
await ProjectInit.initializeWithExplicitIDAsync(id, { force, nonInteractive });
} else {
await ProjectInit.initializeWithInteractiveSelectionAsync(actor);
if (exp.extra?.eas?.projectId) {
Log.error(
`app.json is already linked to project with id: ${chalk.bold(exp.extra?.eas?.projectId)}`
);
return;
}
// this command is interactive by nature
const projectId = await getProjectIdAsync(exp, { nonInteractive: false });
Log.withTick(`Linked app.json to project with id ${chalk.bold(projectId)}`);
}
}
@@ -4,7 +4,7 @@ import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation';
import {
@@ -16,9 +16,12 @@ import {
SecretTypeToEnvironmentSecretType,
} from '../../graphql/types/EnvironmentSecret';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getOwnerAccountForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync, selectAsync } from '../../prompts';
@@ -49,10 +52,6 @@ export default class EnvironmentSecretCreate extends EasCommand {
...EASNonInteractiveFlag,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
let {
flags: {
@@ -64,10 +63,10 @@ export default class EnvironmentSecretCreate extends EasCommand {
'non-interactive': nonInteractive,
},
} = await this.parse(EnvironmentSecretCreate);
const { projectId } = await this.getContextAsync(EnvironmentSecretCreate, {
nonInteractive,
});
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
const ownerAccount = await getOwnerAccountForProjectIdAsync(projectId);
@@ -1,6 +1,6 @@
import { Flags } from '@oclif/core';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation';
import {
@@ -9,6 +9,8 @@ import {
EnvironmentSecretsQuery,
} from '../../graphql/queries/EnvironmentSecretsQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
export default class EnvironmentSecretDelete extends EasCommand {
@@ -21,19 +23,16 @@ export default class EnvironmentSecretDelete extends EasCommand {
...EASNonInteractiveFlag,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
let {
flags: { id, 'non-interactive': nonInteractive },
} = await this.parse(EnvironmentSecretDelete);
const { projectId } = await this.getContextAsync(EnvironmentSecretDelete, {
nonInteractive,
});
let secret: EnvironmentSecretWithScope | undefined;
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
if (!id) {
const validationMessage = 'You must select which secret to delete.';
if (nonInteractive) {
+11 -8
View File
@@ -2,23 +2,26 @@ import chalk from 'chalk';
import Table from 'cli-table3';
import dateFormat from 'dateformat';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EnvironmentSecretsQuery } from '../../graphql/queries/EnvironmentSecretsQuery';
import { EnvironmentSecretTypeToSecretType } from '../../graphql/types/EnvironmentSecret';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
export default class EnvironmentSecretList extends EasCommand {
static override description = 'list environment secrets available for your current app';
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { projectId } = await this.getContextAsync(EnvironmentSecretList, {
nonInteractive: true,
});
const projectDir = await findProjectRootAsync();
if (!projectDir) {
throw new Error("Run this command inside your project's directory");
}
const exp = getExpoConfig(projectDir);
// this command is non-interacive by design
const projectId = await getProjectIdAsync(exp, { nonInteractive: true });
const secrets = await EnvironmentSecretsQuery.allAsync(projectId);
const table = new Table({
+5 -12
View File
@@ -2,10 +2,7 @@ import { EasJsonAccessor } from '@expo/eas-json';
import { Errors, Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand, {
EASCommandLoggedInContext,
EASCommandProjectIdContext,
} from '../commandUtils/EasCommand';
import EasCommand from '../commandUtils/EasCommand';
import { StatuspageServiceName, SubmissionFragment } from '../graphql/generated';
import { toAppPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
@@ -16,7 +13,8 @@ import {
selectRequestedPlatformAsync,
toPlatforms,
} from '../platform';
import { findProjectRootAsync } from '../project/projectUtils';
import { getExpoConfig } from '../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../project/projectUtils';
import { SubmitArchiveFlags, createSubmissionContextAsync } from '../submit/context';
import {
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish,
@@ -93,18 +91,14 @@ export default class Submit extends EasCommand {
}),
};
static override contextDefinition = {
...EASCommandLoggedInContext,
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags: rawFlags } = await this.parse(Submit);
const { actor, projectId } = await this.getContextAsync(Submit, { nonInteractive: false });
const flags = this.sanitizeFlags(rawFlags);
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive: flags.nonInteractive });
await maybeWarnAboutEasOutagesAsync([StatuspageServiceName.EasSubmit]);
@@ -127,7 +121,6 @@ export default class Submit extends EasCommand {
profile: submissionProfile.profile,
archiveFlags: flagsWithPlatform.archiveFlags,
nonInteractive: flagsWithPlatform.nonInteractive,
actor,
});
if (submissionProfiles.length > 1) {
@@ -5,13 +5,14 @@ import assert from 'assert';
import chalk from 'chalk';
import { getEASUpdateURL } from '../../api';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { AppPlatform } from '../../graphql/generated';
import Log, { learnMore } from '../../log';
import { RequestedPlatform, appPlatformDisplayNames } from '../../platform';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectIdAsync,
installExpoUpdatesAsync,
isExpoUpdatesInstalledOrAvailable,
} from '../../project/projectUtils';
@@ -34,18 +35,11 @@ export default class UpdateConfigure extends EasCommand {
}),
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
Log.log(
'💡 The following process will configure your project to run EAS Update. These changes only apply to your local project files and you can safely revert them at any time.'
);
const { flags } = await this.parse(UpdateConfigure);
const { projectId } = await this.getContextAsync(UpdateConfigure, {
nonInteractive: true,
});
const platform = flags.platform as RequestedPlatform;
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
@@ -67,7 +61,6 @@ export default class UpdateConfigure extends EasCommand {
android: androidWorkflow,
ios: iosWorkflow,
},
projectId,
});
Log.withTick(`Configured ${chalk.bold('app.json')} for EAS Update`);
@@ -76,14 +69,14 @@ export default class UpdateConfigure extends EasCommand {
[RequestedPlatform.Android, RequestedPlatform.All].includes(platform) &&
androidWorkflow === Workflow.GENERIC
) {
await syncAndroidUpdatesConfigurationAsync(projectDir, updatedExp, projectId);
await syncAndroidUpdatesConfigurationAsync(projectDir, updatedExp, { nonInteractive: true });
Log.withTick(`Configured ${chalk.bold('AndroidManifest.xml')} for EAS Update`);
}
if (
[RequestedPlatform.Ios, RequestedPlatform.All].includes(platform) &&
iosWorkflow === Workflow.GENERIC
) {
await syncIosUpdatesConfigurationAsync(projectDir, updatedExp, projectId);
await syncIosUpdatesConfigurationAsync(projectDir, updatedExp, { nonInteractive: true });
Log.withTick(`Configured ${chalk.bold('Expo.plist')} for EAS Update`);
}
@@ -97,7 +90,6 @@ async function configureAppJSONForEASUpdateAsync({
exp,
platform,
workflows,
projectId,
}: {
projectDir: string;
exp: ExpoConfig;
@@ -105,9 +97,9 @@ async function configureAppJSONForEASUpdateAsync({
workflows: {
[key in RequestedPlatform.Android | RequestedPlatform.Ios]: Workflow;
};
projectId: string;
}): Promise<ExpoConfig> {
// this command is non-interactive in the way it was designed
const projectId = await getProjectIdAsync(exp, { nonInteractive: true });
const easUpdateURL = getEASUpdateURL(projectId);
const updates = { ...exp.updates, url: easUpdateURL };
@@ -10,7 +10,7 @@ import { getEASUpdateURL } from '../../api';
import { selectBranchOnAppAsync } from '../../branch/queries';
import { BranchNotFoundError, getDefaultBranchNameAsync } from '../../branch/utils';
import { getUpdateGroupUrl } from '../../build/utils/url';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
import fetch from '../../fetch';
@@ -31,6 +31,7 @@ import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getOwnerAccountForProjectIdAsync,
getProjectIdAsync,
installExpoUpdatesAsync,
isExpoUpdatesInstalledOrAvailable,
} from '../../project/projectUtils';
@@ -168,10 +169,6 @@ export default class UpdatePublish extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(UpdatePublish);
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
@@ -188,9 +185,6 @@ export default class UpdatePublish extends EasCommand {
'non-interactive': nonInteractive,
json: jsonFlag,
} = flags;
const { projectId } = await this.getContextAsync(UpdatePublish, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
@@ -240,6 +234,7 @@ export default class UpdatePublish extends EasCommand {
}
const runtimeVersions = await getRuntimeVersionObjectAsync(exp, platformFlag, projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
await checkEASUpdateURLIsSetAsync(exp, projectId);
if (!branchName) {
@@ -383,7 +378,7 @@ export default class UpdatePublish extends EasCommand {
if (!skipBundler) {
const bundleSpinner = ora().start('Building bundle...');
try {
await buildBundlesAsync({ projectDir, inputDir });
await buildBundlesAsync({ projectDir, inputDir, platform: platformFlag });
bundleSpinner.succeed('Built bundle!');
} catch (e) {
bundleSpinner.fail('Failed to build bundle!');
+7 -8
View File
@@ -1,13 +1,15 @@
import { Flags } from '@oclif/core';
import { selectBranchOnAppAsync } from '../../branch/queries';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import {
EasPaginatedQueryFlags,
getLimitFlagWithCustomValues,
getPaginatedQueryOptions,
} from '../../commandUtils/pagination';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import {
listAndRenderUpdateGroupsOnAppAsync,
listAndRenderUpdateGroupsOnBranchAsync,
@@ -32,22 +34,19 @@ export default class UpdateList extends EasCommand {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(UpdateList);
const { branch: branchFlag, all, json: jsonFlag, 'non-interactive': nonInteractive } = flags;
const { projectId } = await this.getContextAsync(UpdateList, {
nonInteractive,
});
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
if (jsonFlag) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
if (all) {
listAndRenderUpdateGroupsOnAppAsync({ projectId, paginatedQueryOptions });
} else {
@@ -1,10 +1,12 @@
import { Flags } from '@oclif/core';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
import { WebhookType } from '../../graphql/generated';
import { WebhookMutation } from '../../graphql/mutations/WebhookMutation';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { prepareInputParamsAsync } from '../../webhooks/input';
export default class WebhookCreate extends EasCommand {
@@ -25,17 +27,15 @@ export default class WebhookCreate extends EasCommand {
...EASNonInteractiveFlag,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(WebhookCreate);
const { projectId } = await this.getContextAsync(WebhookCreate, {
nonInteractive: flags['non-interactive'],
});
const webhookInputParams = await prepareInputParamsAsync(flags);
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive: flags['non-interactive'] });
const spinner = ora('Creating a webhook').start();
try {
await WebhookMutation.createWebhookAsync(projectId, webhookInputParams);
@@ -2,13 +2,15 @@ import assert from 'assert';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
import { WebhookFragment } from '../../graphql/generated';
import { WebhookMutation } from '../../graphql/mutations/WebhookMutation';
import { WebhookQuery } from '../../graphql/queries/WebhookQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { formatWebhook } from '../../webhooks/formatWebhook';
@@ -27,18 +29,15 @@ export default class WebhookDelete extends EasCommand {
...EASNonInteractiveFlag,
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
let {
args: { ID: webhookId },
flags: { 'non-interactive': nonInteractive },
} = await this.parse(WebhookDelete);
const { projectId } = await this.getContextAsync(WebhookDelete, {
nonInteractive,
});
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp, { nonInteractive });
let webhook: WebhookFragment | undefined =
webhookId && (await WebhookQuery.byIdAsync(webhookId));
+12 -9
View File
@@ -1,12 +1,17 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand, { EASCommandProjectIdContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { WebhookType } from '../../graphql/generated';
import { WebhookQuery } from '../../graphql/queries/WebhookQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getDisplayNameForProjectIdAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { formatWebhook } from '../../webhooks/formatWebhook';
export default class WebhookList extends EasCommand {
@@ -19,18 +24,16 @@ export default class WebhookList extends EasCommand {
}),
};
static override contextDefinition = {
...EASCommandProjectIdContext,
};
async runAsync(): Promise<void> {
const {
flags: { event },
} = await this.parse(WebhookList);
const { projectId } = await this.getContextAsync(WebhookList, {
nonInteractive: true,
});
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
// this command is non-interactive by design
const projectId = await getProjectIdAsync(exp, { nonInteractive: true });
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
const spinner = ora(`Fetching the list of webhook on project ${projectDisplayName}`).start();
@@ -7,7 +7,6 @@ import {
AppFragment,
CommonAndroidAppCredentialsFragment,
GoogleServiceAccountKeyFragment,
Role,
} from '../../graphql/generated';
import * as AndroidGraphqlClient from '../android/api/GraphqlClient';
import { Keystore } from '../android/credentials';
@@ -59,14 +58,6 @@ export const testAppFragment: AppFragment = {
ownerAccount: {
id: 'test-account-id',
name: 'testuser',
users: [
{
role: Role.Owner,
actor: {
id: 'test-user-id',
},
},
],
},
};
@@ -1,40 +1,19 @@
import { AppFragment, Role } from '../../graphql/generated';
import { AppFragment } from '../../graphql/generated';
import { Actor } from '../../user/User';
export const jester = {
__typename: 'User' as const,
export const jester: Actor = {
__typename: 'User',
id: 'jester-id',
username: 'jester',
primaryAccount: {
id: 'jester-account-id',
name: 'jester',
users: [{ role: Role.Admin, actor: { id: 'jester-id' } }],
},
accounts: [
{
id: 'jester-account-id',
name: 'jester',
users: [{ role: Role.Admin, actor: { id: 'jester-id' } }],
},
{
id: 'other-account-id',
name: 'other',
users: [{ role: Role.ViewOnly, actor: { id: 'jester-id' } }],
},
],
accounts: [{ id: 'jester-account-id', name: 'jester' }],
isExpoAdmin: false,
};
export const jester2 = {
export const jester2: Actor = {
__typename: 'User',
id: 'jester2-id',
username: 'jester2',
accounts: [
{
id: 'jester2-account-id',
name: 'jester2',
users: [{ role: Role.Admin, actor: { id: 'jester2-id' } }],
},
],
accounts: [{ id: 'jester2-account-id', name: 'jester2' }],
isExpoAdmin: false,
};
@@ -12,7 +12,6 @@ import {
CommonIosAppCredentialsFragment,
IosAppBuildCredentialsFragment,
IosDistributionType,
Role,
} from '../../graphql/generated';
import * as IosGraphqlClient from '../ios/api/GraphqlClient';
import {
@@ -59,14 +58,6 @@ export const testAppFragment: AppFragment = {
ownerAccount: {
id: 'test-account-id',
name: 'testuser',
users: [
{
role: Role.Owner,
actor: {
id: 'test-user-id',
},
},
],
},
};
@@ -8,6 +8,7 @@ import { GradleBuildContext } from '../../../project/android/gradle';
import {
getOwnerAccountForProjectIdAsync,
getProjectConfigDescription,
getProjectIdAsync,
} from '../../../project/projectUtils';
import { promptAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
@@ -90,8 +91,9 @@ export async function getAppLookupParamsFromContextAsync(
ctx: CredentialsContext,
gradleContext?: GradleBuildContext
): Promise<AppLookupParams> {
ctx.ensureProjectContext();
const projectName = ctx.exp.slug;
const projectId = ctx.projectId;
const projectId = await getProjectIdAsync(ctx.exp, { nonInteractive: ctx.nonInteractive });
const account = await getOwnerAccountForProjectIdAsync(projectId);
const androidApplicationIdentifier = await getApplicationIdAsync(
@@ -1,10 +1,11 @@
import { AccountFragment, AndroidFcmFragment, AndroidFcmVersion } from '../../../graphql/generated';
import { AndroidFcmFragment, AndroidFcmVersion } from '../../../graphql/generated';
import Log from '../../../log';
import { promptAsync } from '../../../prompts';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
export class CreateFcm {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
public async runAsync(ctx: CredentialsContext): Promise<AndroidFcmFragment> {
if (ctx.nonInteractive) {
@@ -1,9 +1,10 @@
import chalk from 'chalk';
import fs from 'fs-extra';
import { AccountFragment, GoogleServiceAccountKeyFragment } from '../../../graphql/generated';
import { GoogleServiceAccountKeyFragment } from '../../../graphql/generated';
import Log, { learnMore } from '../../../log';
import { promptAsync } from '../../../prompts';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
import { GoogleServiceAccountKey } from '../credentials';
import {
@@ -12,7 +13,7 @@ import {
} from '../utils/googleServiceAccountKey';
export class CreateGoogleServiceAccountKey {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
public async runAsync(ctx: CredentialsContext): Promise<GoogleServiceAccountKeyFragment> {
if (ctx.nonInteractive) {
@@ -1,5 +1,7 @@
import { AccountFragment, AndroidKeystoreFragment } from '../../../graphql/generated';
import { AndroidKeystoreFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { getProjectIdAsync } from '../../../project/projectUtils';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
import { askForUserProvidedAsync } from '../../utils/promptForCredentials';
import { KeystoreWithType, keystoreSchema } from '../credentials';
@@ -7,14 +9,14 @@ import { generateRandomKeystoreAsync } from '../utils/keystore';
import { getKeystoreWithType, validateKeystore } from '../utils/keystoreNew';
export class CreateKeystore {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
public async runAsync(ctx: CredentialsContext): Promise<AndroidKeystoreFragment> {
if (ctx.nonInteractive) {
throw new Error(`New keystore cannot be created in non-interactive mode.`);
}
const projectId = ctx.projectId;
const projectId = await getProjectIdAsync(ctx.exp, { nonInteractive: ctx.nonInteractive });
const keystore = await this.provideOrGenerateAsync(projectId);
const keystoreFragment = await ctx.android.createKeystoreAsync(this.account, keystore);
Log.succeed('Created keystore');
@@ -1,11 +1,12 @@
import { AccountFragment, GoogleServiceAccountKeyFragment } from '../../../graphql/generated';
import { GoogleServiceAccountKeyFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { confirmAsync } from '../../../prompts';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
import { selectGoogleServiceAccountKeyAsync } from '../utils/googleServiceAccountKey';
export class SelectAndRemoveGoogleServiceAccountKey {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
async runAsync(ctx: CredentialsContext): Promise<void> {
if (ctx.nonInteractive) {
@@ -1,10 +1,11 @@
import { AccountFragment, GoogleServiceAccountKeyFragment } from '../../../graphql/generated';
import { GoogleServiceAccountKeyFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
import { selectGoogleServiceAccountKeyAsync } from '../utils/googleServiceAccountKey';
export class UseExistingGoogleServiceAccountKey {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
public async runAsync(ctx: CredentialsContext): Promise<GoogleServiceAccountKeyFragment | null> {
if (ctx.nonInteractive) {
@@ -1,5 +1,4 @@
import {
AccountFragment,
AndroidAppBuildCredentialsFragment,
AndroidFcmFragment,
AndroidFcmVersion,
@@ -9,6 +8,7 @@ import {
GoogleServiceAccountKeyFragment,
} from '../../../graphql/generated';
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { Account } from '../../../user/Account';
import { GoogleServiceAccountKey, KeystoreWithType } from '../credentials';
import { AndroidAppBuildCredentialsMutation } from './graphql/mutations/AndroidAppBuildCredentialsMutation';
import { AndroidAppCredentialsMutation } from './graphql/mutations/AndroidAppCredentialsMutation';
@@ -19,7 +19,7 @@ import { AndroidAppCredentialsQuery } from './graphql/queries/AndroidAppCredenti
import { GoogleServiceAccountKeyQuery } from './graphql/queries/GoogleServiceAccountKeyQuery';
export interface AppLookupParams {
account: AccountFragment;
account: Account;
projectName: string;
androidApplicationIdentifier: string; // 'android.package' field in app.json
}
@@ -208,7 +208,7 @@ export async function createOrUpdateDefaultIosAppBuildCredentialsAsync(): Promis
}
export async function createKeystoreAsync(
account: AccountFragment,
account: Account,
keystore: KeystoreWithType
): Promise<AndroidKeystoreFragment> {
return await AndroidKeystoreMutation.createAndroidKeystoreAsync(
@@ -228,7 +228,7 @@ export async function deleteKeystoreAsync(keystore: AndroidKeystoreFragment): Pr
}
export async function createFcmAsync(
account: AccountFragment,
account: Account,
fcmApiKey: string,
version: AndroidFcmVersion
): Promise<AndroidFcmFragment> {
@@ -243,7 +243,7 @@ export async function deleteFcmAsync(fcm: AndroidFcmFragment): Promise<void> {
}
export async function createGoogleServiceAccountKeyAsync(
account: AccountFragment,
account: Account,
jsonKey: GoogleServiceAccountKey
): Promise<GoogleServiceAccountKeyFragment> {
return await GoogleServiceAccountKeyMutation.createGoogleServiceAccountKeyAsync(
@@ -261,7 +261,7 @@ export async function deleteGoogleServiceAccountKeyAsync(
}
export async function getGoogleServiceAccountKeysForAccountAsync(
account: AccountFragment
account: Account
): Promise<GoogleServiceAccountKeyFragment[]> {
return await GoogleServiceAccountKeyQuery.getAllForAccountAsync(account.name);
}
+43 -19
View File
@@ -4,6 +4,7 @@ import { EasJson } from '@expo/eas-json';
import chalk from 'chalk';
import Log from '../log';
import { getExpoConfig } from '../project/expoConfig';
import { confirmAsync } from '../prompts';
import { Actor } from '../user/User';
import * as AndroidGraphqlClient from './android/api/GraphqlClient';
@@ -11,11 +12,6 @@ import * as IosGraphqlClient from './ios/api/GraphqlClient';
import AppStoreApi from './ios/appstore/AppStoreApi';
import { AuthenticationMode } from './ios/appstore/authenticateTypes';
export type CredentialsContextProjectInfo = {
exp: ExpoConfig;
projectId: string;
};
export class CredentialsContext {
public readonly android = AndroidGraphqlClient;
public readonly appStore = new AppStoreApi();
@@ -26,30 +22,58 @@ export class CredentialsContext {
public readonly easJsonCliConfig?: EasJson['cli'];
private shouldAskAuthenticateAppStore: boolean = true;
private resolvedExp?: ExpoConfig;
private projectInfo: CredentialsContextProjectInfo;
constructor(options: {
projectInfo: CredentialsContextProjectInfo;
easJsonCliConfig?: EasJson['cli'];
nonInteractive: boolean;
projectDir: string;
user: Actor;
env?: Env;
}) {
constructor(
private options: {
exp?: ExpoConfig;
easJsonCliConfig?: EasJson['cli'];
nonInteractive: boolean;
projectDir: string;
user: Actor;
env?: Env;
}
) {
this.easJsonCliConfig = options.easJsonCliConfig;
this.projectDir = options.projectDir;
this.user = options.user;
this.nonInteractive = options.nonInteractive ?? false;
this.projectInfo = options.projectInfo;
this.resolvedExp = options.exp;
if (!this.resolvedExp) {
this.resolvedExp =
CredentialsContext.getExpoConfigInProject(this.projectDir, { env: options.env }) ??
undefined;
}
}
static getExpoConfigInProject(
projectDir: string,
{ env }: { env?: Env } = {}
): ExpoConfig | null {
try {
return getExpoConfig(projectDir, { env });
} catch {
// ignore error, context might be created outside of expo project
return null;
}
}
get hasProjectContext(): boolean {
return !!this.resolvedExp;
}
get exp(): ExpoConfig {
return this.projectInfo!.exp;
this.ensureProjectContext();
return this.resolvedExp!;
}
get projectId(): string {
return this.projectInfo!.projectId;
public ensureProjectContext(): void {
if (this.hasProjectContext) {
return;
}
// trigger getConfig error
getExpoConfig(this.options.projectDir);
}
async bestEffortAppStoreAuthenticateAsync(): Promise<void> {
@@ -62,7 +62,6 @@ describe(IosCredentialsProvider, () => {
account: {
id: `id-${appLookupParams.account.name}`,
name: appLookupParams.account.name,
users: [],
},
projectName: appLookupParams.projectName,
},
@@ -106,7 +105,6 @@ describe(IosCredentialsProvider, () => {
account: {
id: `id-${appLookupParams.account.name}`,
name: appLookupParams.account.name,
users: [],
},
projectName: appLookupParams.projectName,
},
@@ -1,6 +1,6 @@
import { AppleTeamFragment } from '../../../graphql/generated';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
export async function resolveAppleTeamIfAuthenticatedAsync(
ctx: CredentialsContext,
@@ -3,9 +3,10 @@ import fs from 'fs-extra';
import { nanoid } from 'nanoid';
import path from 'path';
import { AccountFragment, AppStoreConnectApiKeyFragment } from '../../../graphql/generated';
import { AppStoreConnectApiKeyFragment } from '../../../graphql/generated';
import Log, { learnMore } from '../../../log';
import { confirmAsync, promptAsync } from '../../../prompts';
import { Account } from '../../../user/Account';
import { fromNow } from '../../../utils/date';
import { CredentialsContext } from '../../context';
import {
@@ -166,7 +167,7 @@ async function getBestEffortIssuerIdAsync(
export async function getAscApiKeysFromAccountAsync(
ctx: CredentialsContext,
account: AccountFragment,
account: Account,
{ filterDifferentAppleTeam }: { filterDifferentAppleTeam?: boolean } = {}
): Promise<AppStoreConnectApiKeyFragment[]> {
const ascApiKeysForAccount = await ctx.ios.getAscApiKeysForAccountAsync(account);
@@ -180,7 +181,7 @@ export async function getAscApiKeysFromAccountAsync(
export async function selectAscApiKeysFromAccountAsync(
ctx: CredentialsContext,
account: AccountFragment,
account: Account,
{ filterDifferentAppleTeam }: { filterDifferentAppleTeam?: boolean } = {}
): Promise<AppStoreConnectApiKeyFragment | null> {
const ascApiKeysForAccount = await getAscApiKeysFromAccountAsync(ctx, account, {
@@ -4,7 +4,7 @@ import {
} from '../../../graphql/generated';
import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
import { AppStoreApiKeyPurpose } from './AscApiKeyUtils';
@@ -1,7 +1,7 @@
import { ApplePushKeyFragment, CommonIosAppCredentialsFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
export class AssignPushKey {
@@ -7,9 +7,9 @@ import {
IosDistributionType as GraphQLIosDistributionType,
IosAppBuildCredentialsFragment,
} from '../../../graphql/generated';
import { getOwnerAccountForProjectIdAsync } from '../../../project/projectUtils';
import { getOwnerAccountForProjectIdAsync, getProjectIdAsync } from '../../../project/projectUtils';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { App, Target } from '../types';
import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
@@ -82,8 +82,9 @@ export async function assignBuildCredentialsAsync(
}
export async function getAppFromContextAsync(ctx: CredentialsContext): Promise<App> {
ctx.ensureProjectContext();
const projectName = ctx.exp.slug;
const projectId = ctx.projectId;
const projectId = await getProjectIdAsync(ctx.exp, { nonInteractive: ctx.nonInteractive });
const account = await getOwnerAccountForProjectIdAsync(projectId);
return {
account,
@@ -10,8 +10,8 @@ import { ora } from '../../../ora';
import { getApplePlatformFromSdkRoot } from '../../../project/ios/target';
import { CredentialsContext } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
import { AppleProvisioningProfileMutationResult } from '../api/graphql/mutations/AppleProvisioningProfileMutation';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { ProvisioningProfileStoreInfo } from '../appstore/Credentials.types';
import { AuthCtx } from '../appstore/authenticateTypes';
import { Target } from '../types';
@@ -1,10 +1,11 @@
import { AccountFragment, AppStoreConnectApiKeyFragment } from '../../../graphql/generated';
import { AppStoreConnectApiKeyFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
import { AppStoreApiKeyPurpose, provideOrGenerateAscApiKeyAsync } from './AscApiKeyUtils';
export class CreateAscApiKey {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
public async runAsync(
ctx: CredentialsContext,
@@ -1,11 +1,11 @@
import { AccountFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
import { AppleDistributionCertificateMutationResult } from '../api/graphql/mutations/AppleDistributionCertificateMutation';
import { provideOrGenerateDistributionCertificateAsync } from './DistributionCertificateUtils';
export class CreateDistributionCertificate {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
public async runAsync(
ctx: CredentialsContext
@@ -6,8 +6,8 @@ import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { askForUserProvidedAsync } from '../../utils/promptForCredentials';
import { AppLookupParams } from '../api/GraphqlClient';
import { AppleProvisioningProfileMutationResult } from '../api/graphql/mutations/AppleProvisioningProfileMutation';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { ProvisioningProfile } from '../appstore/Credentials.types';
import { AuthCtx } from '../appstore/authenticateTypes';
import { provisioningProfileSchema } from '../credentials';
@@ -1,10 +1,11 @@
import { AccountFragment, ApplePushKeyFragment } from '../../../graphql/generated';
import { ApplePushKeyFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
import { provideOrGeneratePushKeyAsync } from './PushKeyUtils';
export class CreatePushKey {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
public async runAsync(ctx: CredentialsContext): Promise<ApplePushKeyFragment> {
if (ctx.nonInteractive) {
@@ -2,13 +2,14 @@ import assert from 'assert';
import chalk from 'chalk';
import dateformat from 'dateformat';
import { AccountFragment, AppleDistributionCertificateFragment } from '../../../graphql/generated';
import { AppleDistributionCertificateFragment } from '../../../graphql/generated';
import Log, { learnMore } from '../../../log';
import { promptAsync } from '../../../prompts';
import { Account } from '../../../user/Account';
import { fromNow } from '../../../utils/date';
import { CredentialsContext } from '../../context';
import { askForUserProvidedAsync } from '../../utils/promptForCredentials';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import {
DistributionCertificate,
DistributionCertificateStoreInfo,
@@ -84,7 +85,7 @@ async function selectDistributionCertificateAsync(
* */
export async function selectDistributionCertificateWithDependenciesAsync(
ctx: CredentialsContext,
account: AccountFragment
account: Account
): Promise<AppleDistributionCertificateFragment | null> {
const distCertsForAccount = await ctx.ios.getDistributionCertificatesForAccountAsync(account);
if (distCertsForAccount.length === 0) {
@@ -1,8 +1,9 @@
import chalk from 'chalk';
import { AccountFragment, ApplePushKeyFragment } from '../../../graphql/generated';
import { ApplePushKeyFragment } from '../../../graphql/generated';
import Log, { learnMore } from '../../../log';
import { confirmAsync, promptAsync } from '../../../prompts';
import { Account } from '../../../user/Account';
import { fromNow } from '../../../utils/date';
import { CredentialsContext } from '../../context';
import { askForUserProvidedAsync } from '../../utils/promptForCredentials';
@@ -113,7 +114,7 @@ function formatPushKeyFromApple(pushKey: PushKeyStoreInfo): string {
* */
export async function selectPushKeyAsync(
ctx: CredentialsContext,
account: AccountFragment
account: Account
): Promise<ApplePushKeyFragment | null> {
const pushKeysForAccount = await ctx.ios.getPushKeysForAccountAsync(account);
if (pushKeysForAccount.length === 0) {
@@ -1,11 +1,12 @@
import { AccountFragment, AppStoreConnectApiKeyFragment } from '../../../graphql/generated';
import { AppStoreConnectApiKeyFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { confirmAsync } from '../../../prompts';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
import { selectAscApiKeysFromAccountAsync } from './AscApiKeyUtils';
export class SelectAndRemoveAscApiKey {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
async runAsync(ctx: CredentialsContext): Promise<void> {
const selected = await selectAscApiKeysFromAccountAsync(ctx, this.account);
@@ -1,17 +1,17 @@
import {
AccountFragment,
AppleDistributionCertificateFragment,
AppleProvisioningProfileIdentifiersFragment,
} from '../../../graphql/generated';
import Log from '../../../log';
import { confirmAsync } from '../../../prompts';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { selectDistributionCertificateWithDependenciesAsync } from './DistributionCertificateUtils';
import { RemoveProvisioningProfiles } from './RemoveProvisioningProfile';
export class SelectAndRemoveDistributionCertificate {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
async runAsync(ctx: CredentialsContext): Promise<void> {
const selected = await selectDistributionCertificateWithDependenciesAsync(ctx, this.account);
@@ -25,7 +25,7 @@ export class SelectAndRemoveDistributionCertificate {
export class RemoveDistributionCertificate {
constructor(
private account: AccountFragment,
private account: Account,
private distributionCertificate: AppleDistributionCertificateFragment
) {}
@@ -3,7 +3,7 @@ import assert from 'assert';
import { AppleProvisioningProfileIdentifiersFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
export class RemoveProvisioningProfiles {
constructor(
@@ -1,11 +1,12 @@
import { AccountFragment, ApplePushKeyFragment } from '../../../graphql/generated';
import { ApplePushKeyFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { confirmAsync } from '../../../prompts';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
import { selectPushKeyAsync } from './PushKeyUtils';
export class SelectAndRemovePushKey {
constructor(private account: AccountFragment) {}
constructor(private account: Account) {}
async runAsync(ctx: CredentialsContext): Promise<void> {
const selected = await selectPushKeyAsync(ctx, this.account);
@@ -18,7 +18,7 @@ import { confirmAsync, pressAnyKeyToContinueAsync, promptAsync } from '../../../
import differenceBy from '../../../utils/expodash/differenceBy';
import { CredentialsContext } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { ApplePlatform } from '../appstore/constants';
import { Target } from '../types';
import { validateProvisioningProfileAsync } from '../validators/validateProvisioningProfile';
@@ -12,7 +12,7 @@ import {
MissingCredentialsNonInteractiveError,
UnsupportedCredentialsChoiceError,
} from '../../errors';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { getValidAndTrackedAscApiKeysAsync } from '../validators/validateAscApiKey';
import {
AppStoreApiKeyPurpose,
@@ -10,8 +10,8 @@ import { confirmAsync, promptAsync } from '../../../prompts';
import sortBy from '../../../utils/expodash/sortBy';
import { CredentialsContext } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
import { AppleDistributionCertificateMutationResult } from '../api/graphql/mutations/AppleDistributionCertificateMutation';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { getValidCertSerialNumbers } from '../appstore/CredentialsUtils';
import { AppleTeamMissingError } from '../errors';
import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
@@ -2,7 +2,7 @@ import { IosAppBuildCredentialsFragment, IosDistributionType } from '../../../gr
import Log, { learnMore } from '../../../log';
import { promptAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { Target } from '../types';
import { getAllBuildCredentialsAsync } from './BuildCredentialsUtils';
import { SetUpAdhocProvisioningProfile } from './SetUpAdhocProvisioningProfile';
@@ -10,7 +10,7 @@ import { getApplePlatformFromSdkRoot } from '../../../project/ios/target';
import { confirmAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { ProvisioningProfileStoreInfo } from '../appstore/Credentials.types';
import { Target } from '../types';
import { validateProvisioningProfileAsync } from '../validators/validateProvisioningProfile';
@@ -5,7 +5,7 @@ import { ApplePushKeyFragment, CommonIosAppCredentialsFragment } from '../../../
import Log from '../../../log';
import { confirmAsync, promptAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { AssignPushKey } from './AssignPushKey';
import { CreatePushKey } from './CreatePushKey';
import {
@@ -7,7 +7,7 @@ import Log, { learnMore } from '../../../log';
import { promptAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
import { UnsupportedCredentialsChoiceError } from '../../errors';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { AppStoreApiKeyPurpose } from './AscApiKeyUtils';
import { SetUpAscApiKey } from './SetUpAscApiKey';
@@ -7,7 +7,7 @@ import {
} from '../../../graphql/generated';
import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { AppLookupParams as GraphQLAppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams as GraphQLAppLookupParams } from '../api/GraphqlClient';
import { Target } from '../types';
import { SetUpAdhocProvisioningProfile } from './SetUpAdhocProvisioningProfile';
import { SetUpInternalProvisioningProfile } from './SetUpInternalProvisioningProfile';
@@ -12,7 +12,7 @@ import Log from '../../../log';
import { confirmAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
import { IosTargetCredentials } from '../../credentialsJson/types';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { AppLookupParams } from '../api/GraphqlClient';
import { displayProjectCredentials } from '../utils/printCredentials';
import { readAppleTeam } from '../utils/provisioningProfile';
import {
@@ -43,7 +43,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name', users: [] },
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
@@ -77,7 +77,7 @@ describe(SetUpInternalProvisioningProfile, () => {
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name', users: [] },
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
@@ -118,7 +118,7 @@ describe(SetUpInternalProvisioningProfile, () => {
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name', users: [] },
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
@@ -152,7 +152,7 @@ describe(SetUpInternalProvisioningProfile, () => {
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name', users: [] },
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
@@ -188,7 +188,7 @@ describe(SetUpInternalProvisioningProfile, () => {
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name', users: [] },
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
@@ -222,7 +222,7 @@ describe(SetUpInternalProvisioningProfile, () => {
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name', users: [] },
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
@@ -255,7 +255,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name', users: [] },
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
@@ -276,7 +276,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name', users: [] },
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
@@ -295,7 +295,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name', users: [] },
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
@@ -320,7 +320,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name', users: [] },
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
@@ -2,7 +2,6 @@ import { UserRole } from '@expo/apple-utils';
import nullthrows from 'nullthrows';
import {
AccountFragment,
AppFragment,
AppStoreConnectApiKeyFragment,
AppStoreConnectUserRole,
@@ -17,6 +16,7 @@ import {
} from '../../../graphql/generated';
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { isWildcardBundleIdentifier } from '../../../project/ios/bundleIdentifier';
import { Account } from '../../../user/Account';
import { DistributionCertificate, PushKey } from '../appstore/Credentials.types';
import { MinimalAscApiKey } from '../credentials';
import { AppleTeamMissingError } from '../errors';
@@ -45,7 +45,13 @@ import {
import { ApplePushKeyQuery } from './graphql/queries/ApplePushKeyQuery';
import { AppleTeamQuery } from './graphql/queries/AppleTeamQuery';
import { IosAppCredentialsQuery } from './graphql/queries/IosAppCredentialsQuery';
import { AppLookupParams } from './graphql/types/AppLookupParams';
export interface AppLookupParams {
account: Account;
projectName: string;
bundleIdentifier: string;
parentBundleIdentifier?: string;
}
async function getAppAsync(appLookupParams: AppLookupParams): Promise<AppFragment> {
const projectFullName = formatProjectFullName(appLookupParams);
@@ -225,7 +231,7 @@ async function createOrGetExistingIosAppCredentialsWithBuildCredentialsAsync(
}
export async function createOrGetExistingAppleTeamAsync(
account: AccountFragment,
account: Account,
{ appleTeamIdentifier, appleTeamName }: { appleTeamIdentifier: string; appleTeamName?: string }
): Promise<AppleTeamFragment> {
const appleTeam = await AppleTeamQuery.getByAppleTeamIdentifierAsync(
@@ -354,13 +360,13 @@ export async function getDistributionCertificateForAppAsync(
}
export async function getDistributionCertificatesForAccountAsync(
account: AccountFragment
account: Account
): Promise<AppleDistributionCertificateFragment[]> {
return await AppleDistributionCertificateQuery.getAllForAccountAsync(account.name);
}
export async function createDistributionCertificateAsync(
account: AccountFragment,
account: Account,
distCert: DistributionCertificate
): Promise<AppleDistributionCertificateMutationResult> {
const appleTeam = await createOrGetExistingAppleTeamAsync(account, {
@@ -388,7 +394,7 @@ export async function deleteDistributionCertificateAsync(
}
export async function createPushKeyAsync(
account: AccountFragment,
account: Account,
pushKey: PushKey
): Promise<ApplePushKeyFragment> {
const appleTeam = await createOrGetExistingAppleTeamAsync(account, {
@@ -406,7 +412,7 @@ export async function createPushKeyAsync(
}
export async function getPushKeysForAccountAsync(
account: AccountFragment
account: Account
): Promise<ApplePushKeyFragment[]> {
return await ApplePushKeyQuery.getAllForAccountAsync(account.name);
}
@@ -423,7 +429,7 @@ export async function deletePushKeyAsync(pushKeyId: string): Promise<void> {
}
export async function createAscApiKeyAsync(
account: AccountFragment,
account: Account,
ascApiKey: MinimalAscApiKey
): Promise<AppStoreConnectApiKeyFragment> {
const maybeAppleTeam = ascApiKey.teamId
@@ -446,7 +452,7 @@ export async function createAscApiKeyAsync(
}
export async function getAscApiKeysForAccountAsync(
account: AccountFragment
account: Account
): Promise<AppStoreConnectApiKeyFragment[]> {
return await AppStoreConnectApiKeyQuery.getAllForAccountAsync(account.name);
}
@@ -3,16 +3,15 @@ import gql from 'graphql-tag';
import { graphqlClient, withErrorHandlingAsync } from '../../../../../graphql/client';
import {
AccountFragment,
AppleTeamFragment,
AppleTeamInput,
CreateAppleTeamMutation,
} from '../../../../../graphql/generated';
import { AccountFragmentNode } from '../../../../../graphql/types/Account';
import { AppleTeamFragmentNode } from '../../../../../graphql/types/credentials/AppleTeam';
import { Account } from '../../../../../user/Account';
export type AppleTeamMutationResult = AppleTeamFragment & {
account: AccountFragment;
account: Account;
};
export const AppleTeamMutation = {
@@ -31,13 +30,12 @@ export const AppleTeamMutation = {
...AppleTeamFragment
account {
id
...AccountFragment
name
}
}
}
}
${print(AppleTeamFragmentNode)}
${print(AccountFragmentNode)}
`,
{
appleTeamInput,
@@ -1,8 +0,0 @@
import { AccountFragment } from '../../../../../graphql/generated';
export interface AppLookupParams {
account: AccountFragment;
projectName: string;
bundleIdentifier: string;
parentBundleIdentifier?: string;
}
Loaded 100 of 142 files, more files were not shown because too many files have changed in this diff. Show more