Compare commits

..
Author SHA1 Message Date
Evan Bacon a6434bf6b8 v3.7.2 2023-02-24 02:18:15 -06:00
Evan Bacon 19bad84109 [eas-cli] bump apple-utils to support hashcash for authentication (#1719)
* bump apple-utils to support hashcash for authentication

* update CHANGELOG.md
2023-02-24 02:16:06 -06:00
Expo CI 5d9f8f9d76 update CHANGELOG.md 2023-02-23 21:08:09 +00:00
Brent Vatne d9d884d211 v3.7.1 2023-02-23 12:59:33 -08:00
Brent Vatne f902bb845b Add changelog entry 2023-02-23 12:59:05 -08:00
Brent Vatne 03c76c1e0e [eas-cli] Bump apple-utils 2023-02-23 12:58:33 -08:00
Expo CI 1d0d329cdb update CHANGELOG.md 2023-02-23 10:48:34 +00:00
Dominik Sokal 7ed1efb7d6 v3.7.0 2023-02-23 11:23:55 +01:00
Dominik Sokal b7bfec0366 [ENG-7565] add basic support for custom builds (#1710)
* custom builds - wip

* upgrade @expo/eas-build-job

* [eas-json] clean up build profile schema + types

* validate if custom build config exists

* don't configure credentials for custom builds

* set buildMode in metadata

* address PR feedback

* regenerate graphql code
2023-02-23 10:16:22 +01:00
Frederick Widjaja a5308f0d37 Support variant detection for package*UniversalApk Gradle Commands (#1708)
* Support variant detection for package*UniversalApk Gradle Commands

* Update CHANGELOG.md
2023-02-23 09:31:54 +01:00
Cedric van Putten 3ad644bb9d [eas-cli] Provide more context about uploaded assets when publishing (#1488) 2023-02-22 17:39:44 -08:00
Szymon Dziedzic 233e5451a8 update changelog 2023-02-20 11:40:47 +01:00
Szymon Dziedzic ce954cdecc v3.6.1 2023-02-20 11:19:14 +01:00
Szymon Dziedzic 59bd1caf98 [ENG-7376][eas-cli] link to build phase in error message (#1699)
* [eas-cli] link to build phase in error message

* add better fallback

* use fallback for all links

* use terminalLink.isSupported

* improvements after code review

* use old fallback

* update CHANGELOG.md
2023-02-17 15:09:54 +00:00
dependabot[bot] 86877fdda3 Bump undici from 5.8.2 to 5.19.1 (#1706)
Bumps [undici](https://github.com/nodejs/undici) from 5.8.2 to 5.19.1.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.8.2...v5.19.1)

---
updated-dependencies:
- dependency-name: undici
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 09:00:13 +00:00
Will Schurman 9299c9b7cc [eas-cli] Clarify missing owner field error message for Robot users (#1702)
* [eas-cli] Clarify missing owner field error message for Robot users

* update CHANGELOG.md

* Address comment
2023-02-16 18:34:25 +00:00
Szymon Dziedzic 64fb2e5b3a [ci] fix release script (#1704)
* fix release script

* apply suggested changes
2023-02-16 11:19:53 +00:00
Szymon Dziedzic 91fdbac773 [eas-cli] display build message data when picking build in submit, build:run and build:resign commands (#1700)
* [eas-cli] display build message data when picking build in `submit`, `build:run` and `build:resign` commands

* display only 200 first characters of message if it exists

* print only defined values

* add '...' to message if trimmed

* use array instead of object

* update CHANGELOG.md
2023-02-16 10:36:23 +00:00
Szymon Dziedzic 507eadb914 [ci] commit release changelog update using expo bot PAT (#1697) 2023-02-15 16:09:18 +00:00
Wojciech Kozyra f1cde7a291 [eas-cli] Fix README.md (#1701) 2023-02-15 13:01:56 +00:00
Wojciech Kozyra 8b9ad47d16 [eas-cli] Validate platform for local builds earlier (#1698)
* [eas-cli] Validate platform for local builds earlier

* update CHANGELOG.md
2023-02-15 11:28:19 +00:00
Wojciech Kozyra 192984b47d [eas-cli] Disable analytics for environments behind proxy (#1696)
* [eas-cli] Disable analytics for environments behind proxy

* update CHANGELOG.md
2023-02-15 09:46:55 +00:00
Szymon Dziedzic 2c36810ce7 update changelog 2023-02-14 14:31:10 +01:00
Szymon Dziedzic bfa63f95fe v3.6.0 2023-02-14 13:32:03 +01:00
Szymon Dziedzic c414b9f975 [ENG-7389][eas-json] add Xcode 14.2 image to schema (#1693)
* add Xcode 14.2 image to schema

* update CHANGELOG.md
2023-02-14 11:21:07 +00:00
Raul Riera 6946a085cb feat: return the build message when one is available (#1691)
* Return the message when available

* Update CHANGELOG.md

* run generate-graphql-code
2023-02-13 15:56:41 +00:00
41 changed files with 2853 additions and 614 deletions

No files matched your search

+2
View File
@@ -161,6 +161,8 @@ jobs:
needs: publish-to-npm
steps:
- uses: actions/checkout@v2
with:
token: ${{ secrets.EXPO_BOT_PAT }}
- uses: actions/setup-node@v2
with:
node-version: 18
+38 -1
View File
@@ -8,16 +8,53 @@ This is the log of notable changes to EAS CLI and related packages.
### 🎉 New features
### 🐛 Bug fixes
- Implement Apple's proprietary Hashcash algorithm for signing authentication requests. ([#1719](https://github.com/expo/eas-cli/pull/1719) by [@EvanBacon](https://github.com/EvanBacon))
### 🧹 Chores
## [3.7.1](https://github.com/expo/eas-cli/releases/tag/v3.7.1) - 2023-02-23
### 🐛 Bug fixes
- Bump `@expo/apple-utils` to fix Apple developer auth
## [3.7.0](https://github.com/expo/eas-cli/releases/tag/v3.7.0) - 2023-02-23
### 🎉 New features
- Support variant detection for package*UniversalApk Gradle Commands. ([#1708](https://github.com/expo/eas-cli/pull/1708) by [@frw](https://github.com/frw))
## [3.6.1](https://github.com/expo/eas-cli/releases/tag/v3.6.1) - 2023-02-20
### 🐛 Bug fixes
- Disable analytics when running behind a proxy. ([#1696](https://github.com/expo/eas-cli/pull/1696) by [@wkozyra95](https://github.com/wkozyra95))
- Clarify missing owner field error message for Robot users. ([#1702](https://github.com/expo/eas-cli/pull/1702) by [@wschurman](https://github.com/wschurman))
### 🧹 Chores
- Validate the platform for local builds earlier. ([#1698](https://github.com/expo/eas-cli/pull/1698) by [@wkozyra95](https://github.com/wkozyra95))
- Display build message when picking build in `eas submit`, `eas build:resign`, and `eas build:run` commands. ([#1700](https://github.com/expo/eas-cli/pull/1700) by [@szdziedzic](https://github.com/szdziedzic))
- Add a link directly to a build phase logs in the EAS CLI build error message. ([#1699](https://github.com/expo/eas-cli/pull/1699) by [@szdziedzic](https://github.com/szdziedzic))
## [3.6.0](https://github.com/expo/eas-cli/releases/tag/v3.6.0) - 2023-02-14
### 🎉 New features
- Use `sdkVersion` as default runtime version policy when running `eas update:configure`. ([#1669](https://github.com/expo/eas-cli/pull/1669) by [@jonsamp](https://github.com/jonsamp))
- Warn when project ID but no owner specified and mismatch logged in user. ([#1667](https://github.com/expo/eas-cli/pull/1667) by [@wschurman](https://github.com/wschurman))
- Return the build `message` if one is available. ([#1691](https://github.com/expo/eas-cli/pull/1691) by [@raulriera](https://github.com/raulriera))
### 🐛 Bug fixes
- Fix suggested application identifier to match owning account name. ([#1670](https://github.com/expo/eas-cli/pull/1670) by [@wschurman](https://github.com/wschurman))
- Modify update:configure to add possibly missing EAS update config properties. ([#1686](https://github.com/expo/eas-cli/pull/1686) by [@douglowder](https://github.com/douglowder))
### 🧹 Chores
- Add Xcode 14.2 image to the VSCode schema. ([#1693](https://github.com/expo/eas-cli/pull/1693) by [@szdziedzic](https://github.com/szdziedzic))
## [3.5.2](https://github.com/expo/eas-cli/releases/tag/v3.5.2) - 2023-02-05
### 🐛 Bug fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "3.5.2",
"version": "3.7.2",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+1 -1
View File
@@ -14,7 +14,7 @@
"eas": "packages/eas-cli/bin/run",
"lint": "eslint .",
"lint-changelog": "./scripts/bin/run lint-changelog",
"release": "./scripts/bin/run release",
"release": "lerna run rebuild && ./scripts/bin/run release",
"test": "jest",
"clean": "lerna run clean && rm -rf node_modules coverage"
},
File diff suppressed because it is too large. Load diff
+753 -323
View File
File diff suppressed because it is too large. Load diff
+6 -5
View File
@@ -1,20 +1,20 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "3.5.2",
"version": "3.7.2",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
},
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/apple-utils": "0.0.0-alpha.37",
"@expo/apple-utils": "1.1.0",
"@expo/code-signing-certificates": "0.0.5",
"@expo/config": "7.0.3",
"@expo/config-plugins": "5.0.4",
"@expo/config-types": "47.0.0",
"@expo/eas-build-job": "0.2.103",
"@expo/eas-json": "3.5.0",
"@expo/eas-build-job": "0.2.106",
"@expo/eas-json": "3.7.0",
"@expo/json-file": "8.2.37",
"@expo/multipart-body-parser": "1.1.0",
"@expo/osascript": "2.0.33",
@@ -202,7 +202,8 @@
"repository": "expo/eas-cli",
"scripts": {
"postpack": "rimraf oclif.manifest.json",
"prepack": "rimraf build && yarn build && node ./scripts/prepack.js",
"prepack": "yarn rebuild && node ./scripts/prepack.js",
"rebuild": "rimraf build && yarn build",
"pretarball-ci": "./scripts/pretarball-ci.sh",
"build": "tsc --project tsconfig.build.json",
"watch": "yarn build --watch --preserveWatchOutput",
@@ -133,7 +133,9 @@ export async function createAnalyticsAsync(): Promise<AnalyticsWithOrchestration
await UserSettings.setAsync(USER_SETTINGS_KEY_ANALYTICS_ENABLED, false);
}
const analyticsEnabled = await UserSettings.getAsync(USER_SETTINGS_KEY_ANALYTICS_ENABLED, true);
const analyticsEnabled =
!process.env.https_proxy && // disable analytics if running behind proxy
(await UserSettings.getAsync(USER_SETTINGS_KEY_ANALYTICS_ENABLED, true));
if (!analyticsEnabled) {
return new NoOpAnalytics();
}
+2 -1
View File
@@ -124,7 +124,8 @@ export async function prepareAndroidBuildAsync(
}
function shouldProvideCredentials(ctx: BuildContext<Platform.ANDROID>): boolean {
return !ctx.buildProfile.withoutCredentials;
const isCustomBuild = ctx.buildProfile.config !== undefined;
return !ctx.buildProfile.withoutCredentials && !isCustomBuild;
}
async function ensureAndroidCredentialsAsync(
@@ -1,7 +1,12 @@
import { Android } from '@expo/eas-build-job';
import { AndroidBuildType, AndroidJobInput } from '../../graphql/generated';
import { transformBuildTrigger, transformProjectArchive, transformWorkflow } from '../graphql';
import {
transformBuildMode,
transformBuildTrigger,
transformProjectArchive,
transformWorkflow,
} from '../graphql';
export function transformJob(job: Android.Job): AndroidJobInput {
return {
@@ -22,6 +27,8 @@ export function transformJob(job: Android.Job): AndroidJobInput {
buildType: job.buildType && transformBuildType(job.buildType),
developmentClient: job.developmentClient,
experimental: job.experimental,
mode: transformBuildMode(job.mode),
customBuildConfig: job.customBuildConfig,
};
}
@@ -12,6 +12,7 @@ import path from 'path';
import slash from 'slash';
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
import { getCustomBuildConfigPath } from '../../project/customBuildConfig';
import { getUsername } from '../../project/projectUtils';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
@@ -49,11 +50,15 @@ export async function prepareJobAsync(
}
: {};
let buildType = ctx.buildProfile.buildType;
if (!buildType && !buildProfile.gradleCommand && ctx.buildProfile.distribution === 'internal') {
let buildType = buildProfile.buildType;
if (!buildType && !buildProfile.gradleCommand && buildProfile.distribution === 'internal') {
buildType = Android.BuildType.APK;
}
const maybeCustomBuildConfigPath = buildProfile.config
? getCustomBuildConfigPath(buildProfile.config)
: undefined;
const job: Android.Job = {
type: ctx.workflow,
platform: Platform.ANDROID,
@@ -75,8 +80,8 @@ export async function prepareJobAsync(
secrets: {
...buildCredentials,
},
releaseChannel: ctx.buildProfile.releaseChannel,
updates: { channel: ctx.buildProfile.channel },
releaseChannel: buildProfile.releaseChannel,
updates: { channel: buildProfile.channel },
developmentClient: buildProfile.developmentClient,
gradleCommand: buildProfile.gradleCommand,
applicationArchivePath: buildProfile.applicationArchivePath ?? buildProfile.artifactPath,
@@ -89,10 +94,15 @@ export async function prepareJobAsync(
},
}),
experimental: {
prebuildCommand: ctx.buildProfile.prebuildCommand,
prebuildCommand: buildProfile.prebuildCommand,
},
mode: BuildMode.BUILD,
mode: buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
triggeredBy: BuildTrigger.EAS_CLI,
...(maybeCustomBuildConfigPath && {
customBuildConfig: {
path: maybeCustomBuildConfigPath,
},
}),
};
return sanitizeJob(job);
+7 -2
View File
@@ -91,11 +91,16 @@ export function transformIosEnterpriseProvisioning(
}
}
export function transformBuildMode(buildMode: Metadata['buildMode']): BuildMode {
// TODO: check what in metadata
export function transformBuildMode(buildMode: string): BuildMode {
if (buildMode === 'build') {
return BuildMode.Build;
} else {
} else if (buildMode === 'resign') {
return BuildMode.Resign;
} else if (buildMode === 'custom') {
return BuildMode.Custom;
} else {
throw new Error(`Unsupported build mode: ${buildMode}`);
}
}
@@ -55,5 +55,6 @@ export async function ensureIosCredentialsForBuildResignAsync(
}
function shouldProvideCredentials(buildCtx: BuildContext<Platform.IOS>): boolean {
return !buildCtx.buildProfile.simulator;
const isCustomBuild = buildCtx.buildProfile.config !== undefined;
return !buildCtx.buildProfile.simulator && !isCustomBuild;
}
+9 -2
View File
@@ -2,7 +2,12 @@ import { Ios } from '@expo/eas-build-job';
import nullthrows from 'nullthrows';
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated';
import { transformBuildTrigger, transformProjectArchive, transformWorkflow } from '../graphql';
import {
transformBuildMode,
transformBuildTrigger,
transformProjectArchive,
transformWorkflow,
} from '../graphql';
export function transformJob(job: Ios.Job): IosJobInput {
return {
@@ -12,7 +17,7 @@ export function transformJob(job: Ios.Job): IosJobInput {
projectRootDirectory: nullthrows(job.projectRootDirectory),
releaseChannel: job.releaseChannel,
updates: job.updates,
secrets: transformIosSecrets(job.secrets),
secrets: job.secrets ? transformIosSecrets(job.secrets) : undefined,
builderEnvironment: job.builderEnvironment,
cache: job.cache,
version: job.version?.buildNumber ? { buildNumber: job.version.buildNumber } : undefined,
@@ -24,6 +29,8 @@ export function transformJob(job: Ios.Job): IosJobInput {
developmentClient: job.developmentClient,
simulator: job.simulator,
experimental: job.experimental,
mode: transformBuildMode(job.mode),
customBuildConfig: job.customBuildConfig,
};
}
+32 -21
View File
@@ -7,12 +7,14 @@ import {
Platform,
sanitizeJob,
} from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import nullthrows from 'nullthrows';
import path from 'path';
import slash from 'slash';
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
import { IosJobSecretsInput } from '../../graphql/generated';
import { getCustomBuildConfigPath } from '../../project/customBuildConfig';
import { getUsername } from '../../project/projectUtils';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
@@ -33,10 +35,11 @@ export async function prepareJobAsync(
ctx: BuildContext<Platform.IOS>,
jobData: JobData
): Promise<Job> {
const username = getUsername(ctx.exp, ctx.user);
const buildProfile: BuildProfile<Platform.IOS> = ctx.buildProfile;
const projectRootDirectory =
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
const username = getUsername(ctx.exp, ctx.user);
const buildCredentials: Ios.Job['secrets']['buildCredentials'] = {};
const buildCredentials: Ios.BuildSecrets['buildCredentials'] = {};
if (jobData.credentials) {
const targetNames = Object.keys(jobData.credentials);
for (const targetName of targetNames) {
@@ -44,38 +47,41 @@ export async function prepareJobAsync(
}
}
const maybeCustomBuildConfigPath = buildProfile.config
? getCustomBuildConfigPath(buildProfile.config)
: undefined;
const job: Ios.Job = {
type: ctx.workflow,
platform: Platform.IOS,
projectArchive: jobData.projectArchive,
projectRootDirectory,
builderEnvironment: {
image: ctx.buildProfile.image,
node: ctx.buildProfile.node,
yarn: ctx.buildProfile.yarn,
bundler: ctx.buildProfile.bundler,
cocoapods: ctx.buildProfile.cocoapods,
fastlane: ctx.buildProfile.fastlane,
expoCli: ctx.buildProfile.expoCli,
env: ctx.buildProfile.env,
image: buildProfile.image,
node: buildProfile.node,
yarn: buildProfile.yarn,
bundler: buildProfile.bundler,
cocoapods: buildProfile.cocoapods,
fastlane: buildProfile.fastlane,
expoCli: buildProfile.expoCli,
env: buildProfile.env,
},
cache: {
...cacheDefaults,
...ctx.buildProfile.cache,
...buildProfile.cache,
clear: ctx.clearCache,
},
secrets: {
buildCredentials,
},
releaseChannel: ctx.buildProfile.releaseChannel,
updates: { channel: ctx.buildProfile.channel },
developmentClient: ctx.buildProfile.developmentClient,
simulator: ctx.buildProfile.simulator,
releaseChannel: buildProfile.releaseChannel,
updates: { channel: buildProfile.channel },
developmentClient: buildProfile.developmentClient,
simulator: buildProfile.simulator,
scheme: jobData.buildScheme,
buildConfiguration: ctx.buildProfile.buildConfiguration,
applicationArchivePath:
ctx.buildProfile.applicationArchivePath ?? ctx.buildProfile.artifactPath,
buildArtifactPaths: ctx.buildProfile.buildArtifactPaths,
buildConfiguration: buildProfile.buildConfiguration,
applicationArchivePath: buildProfile.applicationArchivePath ?? buildProfile.artifactPath,
buildArtifactPaths: buildProfile.buildArtifactPaths,
username,
...(ctx.ios.buildNumberOverride && {
version: {
@@ -83,10 +89,15 @@ export async function prepareJobAsync(
},
}),
experimental: {
prebuildCommand: ctx.buildProfile.prebuildCommand,
prebuildCommand: buildProfile.prebuildCommand,
},
mode: BuildMode.BUILD,
mode: buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
triggeredBy: BuildTrigger.EAS_CLI,
...(maybeCustomBuildConfigPath && {
customBuildConfig: {
path: maybeCustomBuildConfigPath,
},
}),
};
return sanitizeJob(job);
}
+2 -1
View File
@@ -1,5 +1,5 @@
import { Updates } from '@expo/config-plugins';
import { Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
import { BuildMode, Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
import { IosEnterpriseProvisioning } from '@expo/eas-json';
import fs from 'fs-extra';
import resolveFrom from 'resolve-from';
@@ -57,6 +57,7 @@ export async function collectMetadataAsync<T extends Platform>(
}),
runWithNoWaitFlag: ctx.noWait,
runFromCI: ctx.runFromCI,
buildMode: ctx.buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
};
return sanitizeMetadata(metadata);
}
+23 -12
View File
@@ -99,10 +99,6 @@ export async function listAndSelectBuildOnAppAsync(
}
}
function formatBuildChoiceValue(value: string | undefined | null): string {
return value ? chalk.bold(value) : 'Unknown';
}
function createBuildToPartialChoiceMaker(
selectPromptDisabledFunction?: (build: BuildFragment) => boolean
) {
@@ -119,19 +115,34 @@ function createBuildToPartialChoiceMaker(
? `${build.gitCommitHash.slice(0, 7)} "${chalk.bold(
splitCommitMessage[0] + (splitCommitMessage.length > 1 ? '…' : '')
)}"`
: 'Unknown';
: null;
const descriptionItems: { name: string; value: string | null }[] = [
{ name: 'Version', value: build.appVersion ? chalk.bold(build.appVersion) : null },
{ name: 'Commit', value: formattedCommitData },
{
name: 'Message',
value: build.message
? chalk.bold(
build.message.length > 200 ? `${build.message.slice(0, 200)}...` : build.message
)
: null,
},
{
name: build.platform === AppPlatform.Ios ? 'Build number' : 'Version code',
value: build.appBuildVersion ? chalk.bold(build.appBuildVersion) : null,
},
];
const filteredDescriptionArray: string[] = descriptionItems
.filter(item => item.value)
.map(item => `${chalk.bold(item.name)}: ${item.value}`);
return {
title: `${chalk.bold(`ID:`)} ${build.id} (${chalk.bold(
`${fromNow(new Date(build.completedAt))} ago`
)})`,
description: [
`\t${chalk.bold(`Version:`)} ${formatBuildChoiceValue(build.appVersion)}`,
`\t${chalk.bold(
build.platform === AppPlatform.Ios ? 'Build number:' : 'Version code:'
)} ${formatBuildChoiceValue(build.appBuildVersion)}`,
`\t${chalk.bold(`Commit:`)} ${formattedCommitData}`,
].join('\n'),
description: filteredDescriptionArray.length > 0 ? filteredDescriptionArray.join('\n') : '',
disabled: selectPromptDisabledFunction?.(build),
};
};
@@ -31,6 +31,7 @@ import {
appPlatformEmojis,
toPlatforms,
} from '../platform';
import { validateCustomBuildConfigAsync } from '../project/customBuildConfig';
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
import { validateAppVersionRuntimePolicySupportAsync } from '../project/projectUtils';
@@ -118,6 +119,7 @@ export async function runBuildAndSubmitAsync(
for (const buildProfile of buildProfiles) {
validateBuildProfileVersionSettings(buildProfile, easJsonCliConfig);
await validateCustomBuildConfigAsync(projectDir, buildProfile.profile);
}
const startedBuilds: {
@@ -38,6 +38,10 @@ export function formatGraphQLBuild(build: BuildFragment): string {
}
},
},
{
label: 'Message',
value: build.message,
},
{
label: 'Distribution',
value: build.distribution?.toLowerCase(),
@@ -15,6 +15,32 @@ import Log, { learnMore, link } from '../../log';
import { appPlatformDisplayNames, appPlatformEmojis } from '../../platform';
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url';
function terminalLinkFallback(url: string, text: string): string {
return `${text} (${url})`;
}
const errorCodeToErrorMessageOverride: Record<string, (build: BuildFragment) => string> = {
EAS_BUILD_UNKNOWN_FASTLANE_ERROR: build =>
`The ${link(getBuildLogsUrl(build, 'run-fastlane'), {
text: '"Run fastlane"',
fallback: terminalLinkFallback(getBuildLogsUrl(build, 'run-fastlane'), '"Run fastlane"'),
})} step failed with an unknown error. Refer to the ${link(
getBuildLogsUrl(build, 'xcode-logs'),
{
text: '"Xcode logs"',
fallback: terminalLinkFallback(getBuildLogsUrl(build, 'xcode-logs'), '"Xcode logs"'),
}
)} phase for additional, more detailed logs`,
EAS_BUILD_UNKNOWN_GRADLE_ERROR: build =>
`Gradle build failed with unknown error. See logs for the ${link(
getBuildLogsUrl(build, 'run-gradlew'),
{
text: '"Run gradlew"',
fallback: terminalLinkFallback(getBuildLogsUrl(build, 'run-gradlew'), '"Run gradlew"'),
}
)} phase for more information.`,
};
export function printLogsUrls(builds: BuildFragment[]): void {
if (builds.length === 1) {
Log.log(`Build details: ${link(getBuildLogsUrl(builds[0]))}`);
@@ -31,7 +57,6 @@ export function printLogsUrls(builds: BuildFragment[]): void {
}
export function printBuildResults(builds: (BuildFragment | null)[]): void {
Log.newLine();
if (builds.length === 1) {
const [build] = builds;
assert(build, 'Build should be defined');
@@ -42,8 +67,8 @@ export function printBuildResults(builds: (BuildFragment | null)[]): void {
}
function printBuildResult(build: BuildFragment): void {
Log.addNewLineIfNone();
if (build.status === BuildStatus.Errored) {
Log.addNewLineIfNone();
const userError = build.error;
Log.error(
`${appPlatformEmojis[build.platform]} ${
@@ -51,11 +76,12 @@ function printBuildResult(build: BuildFragment): void {
} build failed${userError ? ':' : ''}`
);
if (userError) {
printUserError(userError);
printUserError(userError, build);
}
return;
}
if (build.status === BuildStatus.Canceled) {
Log.addNewLineIfNone();
Log.error(
`${appPlatformEmojis[build.platform]} ${
appPlatformDisplayNames[build.platform]
@@ -65,6 +91,7 @@ function printBuildResult(build: BuildFragment): void {
}
if (build.distribution === DistributionType.Internal) {
Log.addNewLineIfNone();
const logsUrl = getBuildLogsUrl(build);
// It's tricky to install the .apk file directly on Android so let's fallback
// to the build details page and let people press the button to download there
@@ -82,9 +109,14 @@ function printBuildResult(build: BuildFragment): void {
} else {
// TODO: it looks like buildUrl could possibly be undefined, based on the code below.
// we should account for this case better if it is possible
const url = build.artifacts?.buildUrl ?? '';
Log.log(`${appPlatformEmojis[build.platform]} ${appPlatformDisplayNames[build.platform]} app:`);
Log.log(`${link(url)}`);
const url = build.artifacts?.buildUrl;
if (url) {
Log.addNewLineIfNone();
Log.log(
`${appPlatformEmojis[build.platform]} ${appPlatformDisplayNames[build.platform]} app:`
);
Log.log(link(url));
}
}
}
@@ -105,9 +137,22 @@ export function printDeprecationWarnings(deprecationInfo?: EasBuildDeprecationIn
}
}
export function printUserError(error: BuildError): void {
Log.error(error.message);
if (error.docsUrl) {
Log.error(learnMore(error.docsUrl, { dim: false }));
export function printUserError(error: BuildError, build: BuildFragment): void {
const maybeErrorMessageOverride = maybeGetErrorMessageOverride(error, build);
if (maybeErrorMessageOverride) {
Log.error(maybeErrorMessageOverride);
} else {
Log.error(error.message);
if (error.docsUrl) {
Log.error(learnMore(error.docsUrl, { dim: false }));
}
}
}
function maybeGetErrorMessageOverride(error: BuildError, build: BuildFragment): string | null {
if (!(error.errorCode in errorCodeToErrorMessageOverride)) {
return null;
}
return errorCodeToErrorMessageOverride[error.errorCode](build);
}
+4 -2
View File
@@ -10,12 +10,14 @@ export function getProjectDashboardUrl(accountName: string, projectName: string)
).toString();
}
export function getBuildLogsUrl(build: BuildFragment): string {
export function getBuildLogsUrl(build: BuildFragment, hash?: string): string {
const { project } = build;
const url =
project.__typename !== 'App'
? `/builds/${build.id}`
: `/accounts/${project.ownerAccount.name}/projects/${project.slug}/builds/${build.id}`;
: `/accounts/${project.ownerAccount.name}/projects/${project.slug}/builds/${build.id}${
hash ? `#${hash}` : ''
}`;
return new URL(url, getExpoWebsiteBaseUrl()).toString();
}
@@ -136,6 +136,54 @@ describe(getProjectIdAsync, () => {
);
});
it('throws when the owner is not specified and is different than logged in user which is a robot', async () => {
const sessionManagerMock = mock<SessionManager>();
when(sessionManagerMock.ensureLoggedInAsync(anything())).thenResolve({
actor: {
__typename: 'Robot',
id: 'robot_id',
accounts: [
{
id: 'account_id_1',
name: 'notnotbrent',
users: [{ role: Role.Admin, actor: { id: 'robot_id' } }],
},
{
id: 'account_id_2',
name: 'dominik',
users: [{ role: Role.ViewOnly, actor: { id: 'robot_id' } }],
},
],
isExpoAdmin: false,
featureGates: {},
},
authenticationInfo: { accessToken: 'fake', sessionSecret: null },
});
const sessionManagerRobot = instance(sessionManagerMock);
jest.mocked(getConfig).mockReturnValue({
exp: { name: 'test', slug: 'test', extra: { eas: { projectId: '1234' } } },
} as any);
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
fullName: '@totallybrent/test',
slug: 'test',
ownerAccount: { name: 'totallybrent' } as any,
});
await expect(
getProjectIdAsync(
sessionManagerRobot,
{ name: 'test', slug: 'test', extra: { eas: { projectId: '1234' } } },
{ nonInteractive: false }
)
).rejects.toThrow(
`Project config: Owner of project identified by "extra.eas.projectId" (totallybrent) must be specified in "owner" field when using a robot access token. To ensure all libraries work correctly, "owner": "totallybrent" should be added to the project config, which can be done automatically by re-running "eas init". ${learnMore(
'https://expo.fyi/eas-project-id'
)}`
);
});
it('throws when the slug is out of sync', async () => {
jest.mocked(getConfig).mockReturnValue({
exp: { name: 'test', slug: 'wat', extra: { eas: { projectId: '1234' } } },
@@ -103,15 +103,28 @@ export async function getProjectIdAsync(
const actorUsername = getActorUsername(actor);
if (!exp.owner && appForProjectId.ownerAccount.name !== actorUsername) {
throw new Error(
`Project config: Owner of project identified by "extra.eas.projectId" (${
appForProjectId.ownerAccount.name
}) does not match the logged in user (${actorUsername}) and the "owner" field is not specified. To ensure all libraries work correctly, "owner": "${
appForProjectId.ownerAccount.name
}" should be added to the project config, which can be done automatically by re-running "eas init". ${learnMore(
'https://expo.fyi/eas-project-id'
)}`
);
if (actorUsername) {
throw new Error(
`Project config: Owner of project identified by "extra.eas.projectId" (${
appForProjectId.ownerAccount.name
}) does not match the logged in user (${actorUsername}) and the "owner" field is not specified. To ensure all libraries work correctly, "owner": "${
appForProjectId.ownerAccount.name
}" should be added to the project config, which can be done automatically by re-running "eas init". ${learnMore(
'https://expo.fyi/eas-project-id'
)}`
);
} else {
// robot caller
throw new Error(
`Project config: Owner of project identified by "extra.eas.projectId" (${
appForProjectId.ownerAccount.name
}) must be specified in "owner" field when using a robot access token. To ensure all libraries work correctly, "owner": "${
appForProjectId.ownerAccount.name
}" should be added to the project config, which can be done automatically by re-running "eas init". ${learnMore(
'https://expo.fyi/eas-project-id'
)}`
);
}
}
if (exp.slug && exp.slug !== appForProjectId.slug) {
@@ -227,6 +227,13 @@ export default class Build extends EasCommand {
});
} else if (process.platform !== 'darwin' && requestedPlatform === RequestedPlatform.Ios) {
Errors.error('Unsupported platform, macOS is required to build apps for iOS', { exit: 1 });
} else if (
!['linux', 'darwin'].includes(process.platform) &&
requestedPlatform === RequestedPlatform.Android
) {
Errors.error('Unsupported platform, macOS or Linux is required to build apps for Android', {
exit: 1,
});
}
}
@@ -46,7 +46,7 @@ jest.mock('../../../graphql/queries/AppQuery');
jest.mock('../../../graphql/queries/UpdateQuery');
jest.mock('../../../ora', () => ({
ora: () => ({
start: () => ({ succeed: () => {}, fail: () => {} }),
start: () => ({ succeed: () => {}, fail: () => {}, stop: () => {} }),
}),
}));
jest.mock('../../../project/publish', () => ({
@@ -246,6 +246,8 @@ function mockTestExport({
uniqueAssetCount: platforms.length,
uniqueUploadedAssetCount: platforms.length,
assetLimitPerUpdateGroup: 9001,
launchAssetCount: 2,
uniqueUploadedAssetPaths: [],
});
jest.mocked(Updates.getRuntimeVersion).mockReturnValue(runtimeVersion);
+28 -4
View File
@@ -318,10 +318,34 @@ export default class UpdatePublish extends EasCommand {
uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
unsortedUpdateInfoGroups = await buildUnsortedUpdateInfoGroupAsync(assets, exp);
const uploadAssetSuccessMessage = uploadedAssetCount
? `Uploaded ${uploadedAssetCount} ${uploadedAssetCount === 1 ? 'platform' : 'platforms'}`
: `Uploaded: No changes detected`;
assetSpinner.succeed(uploadAssetSuccessMessage);
// NOTE(cedric): we assume that bundles are always uploaded, and always are part of
// `uploadedAssetCount`, perferably we don't assume. For that, we need to refactor the
// `uploadAssetsAsync` and be able to determine asset type from the uploaded assets.
const uploadedBundleCount = uploadResults.launchAssetCount;
const uploadedNormalAssetCount = Math.max(0, uploadedAssetCount - uploadedBundleCount);
const reusedNormalAssetCount = uploadResults.uniqueAssetCount - uploadedNormalAssetCount;
assetSpinner.stop();
Log.withTick(
`Uploaded ${uploadedBundleCount} app ${uploadedBundleCount === 1 ? 'bundle' : 'bundles'}`
);
if (uploadedNormalAssetCount === 0) {
Log.withTick(`Uploading assets skipped - no new assets found`);
} else {
let message = `Uploaded ${uploadedNormalAssetCount} ${
uploadedNormalAssetCount === 1 ? 'asset' : 'assets'
}`;
if (reusedNormalAssetCount > 0) {
message += ` (reused ${reusedNormalAssetCount} ${
reusedNormalAssetCount === 1 ? 'asset' : 'assets'
})`;
}
Log.withTick(message);
}
for (const uploadedAssetPath of uploadResults.uniqueUploadedAssetPaths) {
Log.debug(chalk.dim(`- ${uploadedAssetPath}`));
}
} catch (e) {
assetSpinner.fail('Failed to upload');
throw e;
+126 -62
View File
@@ -126,11 +126,15 @@ export type Account = {
subscription?: Maybe<SubscriptionDetails>;
/** @deprecated No longer needed */
subscriptionChangesPending?: Maybe<Scalars['Boolean']>;
/** Coalesced project activity for an app using pagination */
timelineActivity: TimelineActivityConnection;
/** @deprecated See isCurrent */
unlimitedBuilds: Scalars['Boolean'];
updatedAt: Scalars['DateTime'];
/** Account query object for querying EAS usage metrics */
usageMetrics: AccountUsageMetrics;
/** Owning UserActor of this account if personal account */
userActorOwner?: Maybe<UserActor>;
/** Pending user invitations for this account */
userInvitations: Array<UserInvitation>;
/** Actors associated with this account and permissions they hold */
@@ -262,6 +266,19 @@ export type AccountSnacksArgs = {
offset: Scalars['Int'];
};
/**
* An account is a container owning projects, credentials, billing and other organization
* data and settings. Actors may own and be members of accounts.
*/
export type AccountTimelineActivityArgs = {
after?: InputMaybe<Scalars['String']>;
before?: InputMaybe<Scalars['String']>;
filter?: InputMaybe<TimelineActivityFilterInput>;
first?: InputMaybe<Scalars['Int']>;
last?: InputMaybe<Scalars['Int']>;
};
export type AccountDataInput = {
name: Scalars['String'];
};
@@ -493,6 +510,7 @@ export type AccountUsageMetricAndCost = {
/** The limit, in units, allowed by this plan */
limit: Scalars['Float'];
metricType: UsageMetricType;
service: EasService;
serviceMetric: EasServiceMetric;
/** Total cost of this particular metric, in cents */
totalCost: Scalars['Float'];
@@ -508,6 +526,7 @@ export type AccountUsageMetrics = {
export type AccountUsageMetricsByBillingPeriodArgs = {
date: Scalars['DateTime'];
service?: InputMaybe<EasService>;
};
@@ -788,6 +807,7 @@ export type AndroidJobInput = {
buildType?: InputMaybe<AndroidBuildType>;
builderEnvironment?: InputMaybe<AndroidBuilderEnvironmentInput>;
cache?: InputMaybe<BuildCacheInput>;
customBuildConfig?: InputMaybe<CustomBuildConfigInput>;
developmentClient?: InputMaybe<Scalars['Boolean']>;
experimental?: InputMaybe<Scalars['JSONObject']>;
gradleCommand?: InputMaybe<Scalars['String']>;
@@ -931,10 +951,8 @@ export type App = Project & {
/** Classic update release channel names that have at least one build */
buildsReleaseChannels: Array<Scalars['String']>;
deployment?: Maybe<Deployment>;
deploymentNew?: Maybe<DeploymentNew>;
/** Deployments associated with this app */
deployments: Array<Deployment>;
deploymentsNew: DeploymentsConnection;
deployments: DeploymentsConnection;
description: Scalars['String'];
/** Environment secrets for an app */
environmentSecrets: Array<EnvironmentSecret>;
@@ -987,6 +1005,8 @@ export type App = Project & {
slug: Scalars['String'];
/** EAS Submissions associated with this app */
submissions: Array<Submission>;
/** Coalesced project activity for an app using pagination */
timelineActivity: TimelineActivityConnection;
/** @deprecated 'likes' have been deprecated. */
trendScore: Scalars['Float'];
/** get an EAS branch owned by the app by name */
@@ -1056,14 +1076,6 @@ export type AppBuildsArgs = {
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppDeploymentArgs = {
channel: Scalars['String'];
options?: InputMaybe<DeploymentOptions>;
runtimeVersion: Scalars['String'];
};
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppDeploymentNewArgs = {
channel: Scalars['String'];
runtimeVersion: Scalars['String'];
};
@@ -1071,14 +1083,6 @@ export type AppDeploymentNewArgs = {
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppDeploymentsArgs = {
limit: Scalars['Int'];
mostRecentlyUpdatedAt?: InputMaybe<Scalars['DateTime']>;
options?: InputMaybe<DeploymentOptions>;
};
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppDeploymentsNewArgs = {
after?: InputMaybe<Scalars['String']>;
before?: InputMaybe<Scalars['String']>;
first?: InputMaybe<Scalars['Int']>;
@@ -1127,6 +1131,16 @@ export type AppSubmissionsArgs = {
};
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppTimelineActivityArgs = {
after?: InputMaybe<Scalars['String']>;
before?: InputMaybe<Scalars['String']>;
filter?: InputMaybe<TimelineActivityFilterInput>;
first?: InputMaybe<Scalars['Int']>;
last?: InputMaybe<Scalars['Int']>;
};
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppUpdateBranchByNameArgs = {
name: Scalars['String'];
@@ -1898,6 +1912,7 @@ export enum BuildIosEnterpriseProvisioning {
/** Represents an Standalone App build job */
export type BuildJob = ActivityTimelineProjectActivity & BuildOrBuildJob & {
__typename?: 'BuildJob';
accountUserActor?: Maybe<UserActor>;
activityTimestamp: Scalars['DateTime'];
actor?: Maybe<Actor>;
app?: Maybe<App>;
@@ -2005,6 +2020,7 @@ export type BuildMetrics = {
export enum BuildMode {
Build = 'BUILD',
Custom = 'CUSTOM',
Resign = 'RESIGN'
}
@@ -2186,6 +2202,8 @@ export enum BuildResourceClass {
IosLarge = 'IOS_LARGE',
IosM1Large = 'IOS_M1_LARGE',
IosM1Medium = 'IOS_M1_MEDIUM',
IosM2Medium = 'IOS_M2_MEDIUM',
IosM2ProMedium = 'IOS_M2_PRO_MEDIUM',
IosMedium = 'IOS_MEDIUM',
Legacy = 'LEGACY'
}
@@ -2346,6 +2364,10 @@ export type CreateSubmissionResult = {
submission: Submission;
};
export type CustomBuildConfigInput = {
path: Scalars['String'];
};
export type DeleteAccessTokenResult = {
__typename?: 'DeleteAccessTokenResult';
id: Scalars['ID'];
@@ -2434,16 +2456,19 @@ export type DeployServerlessFunctionResult = {
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
export type Deployment = {
__typename?: 'Deployment';
channel?: Maybe<UpdateChannel>;
/**
* The name of this deployment's associated channel. It is specified separately from the `channel`
* field to allow specifying a deployment before an EAS Update channel has been created.
*/
channelName: Scalars['String'];
builds: DeploymentBuildsConnection;
channel: UpdateChannel;
id: Scalars['ID'];
mostRecentlyUpdatedAt: Scalars['DateTime'];
recentBuilds: Array<Build>;
runtimeVersion: Scalars['String'];
runtime: Runtime;
};
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
export type DeploymentBuildsArgs = {
after?: InputMaybe<Scalars['String']>;
before?: InputMaybe<Scalars['String']>;
first?: InputMaybe<Scalars['Int']>;
last?: InputMaybe<Scalars['Int']>;
};
export type DeploymentBuildEdge = {
@@ -2462,33 +2487,10 @@ export type DeploymentBuildsConnection = {
export type DeploymentEdge = {
__typename?: 'DeploymentEdge';
cursor: Scalars['String'];
node: DeploymentNew;
node: Deployment;
};
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
export type DeploymentNew = {
__typename?: 'DeploymentNew';
builds: DeploymentBuildsConnection;
channel: UpdateChannel;
id: Scalars['ID'];
runtime: Runtime;
};
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
export type DeploymentNewBuildsArgs = {
after?: InputMaybe<Scalars['String']>;
before?: InputMaybe<Scalars['String']>;
first?: InputMaybe<Scalars['Int']>;
last?: InputMaybe<Scalars['Int']>;
};
export type DeploymentOptions = {
/** Max number of associated builds to return */
buildListMaxSize?: InputMaybe<Scalars['Int']>;
};
/** Represents the connection over the deploymentsNew edge of an App */
/** Represents the connection over the deployments edge of an App */
export type DeploymentsConnection = {
__typename?: 'DeploymentsConnection';
edges: Array<DeploymentEdge>;
@@ -2514,6 +2516,11 @@ export enum EasBuildDeprecationInfoType {
export type EasBuildOrClassicBuildJob = Build | BuildJob;
export enum EasService {
Builds = 'BUILDS',
Updates = 'UPDATES'
}
export enum EasServiceMetric {
AssetsRequests = 'ASSETS_REQUESTS',
BandwidthUsage = 'BANDWIDTH_USAGE',
@@ -3103,6 +3110,7 @@ export type IosJobInput = {
buildType?: InputMaybe<IosBuildType>;
builderEnvironment?: InputMaybe<IosBuilderEnvironmentInput>;
cache?: InputMaybe<BuildCacheInput>;
customBuildConfig?: InputMaybe<CustomBuildConfigInput>;
developmentClient?: InputMaybe<Scalars['Boolean']>;
/** @deprecated */
distribution?: InputMaybe<DistributionType>;
@@ -3794,6 +3802,8 @@ export type RootQuery = {
updatesByGroup: Array<Update>;
/** Top-level query object for querying Users. */
user: UserQuery;
/** Top-level query object for querying UserActors. */
userActor: UserActorQuery;
/** @deprecated Use 'byId' field under 'user'. */
userByUserId?: Maybe<User>;
/** @deprecated Use 'byUsername' field under 'user'. */
@@ -3857,6 +3867,7 @@ export type SsoUser = Actor & UserActor & {
appetizeCode?: Maybe<Scalars['String']>;
/** Apps this user has published. If this user is the viewer, this field returns the apps the user has access to. */
apps: Array<App>;
bestContactEmail?: Maybe<Scalars['String']>;
created: Scalars['DateTime'];
displayName: Scalars['String'];
/**
@@ -3872,6 +3883,7 @@ export type SsoUser = Actor & UserActor & {
isExpoAdmin: Scalars['Boolean'];
lastName?: Maybe<Scalars['String']>;
location?: Maybe<Scalars['String']>;
notificationSubscriptions: Array<NotificationSubscription>;
/** Associated accounts */
primaryAccount: Account;
profilePhoto: Scalars['String'];
@@ -3904,6 +3916,12 @@ export type SsoUserFeatureGatesArgs = {
};
/** Represents a human SSO (not robot) actor. */
export type SsoUserNotificationSubscriptionsArgs = {
filter?: InputMaybe<NotificationSubscriptionFilter>;
};
/** Represents a human SSO (not robot) actor. */
export type SsoUserSnacksArgs = {
limit: Scalars['Int'];
@@ -4304,6 +4322,25 @@ export type SubscriptionDetailsPlanEnablementArgs = {
serviceMetric: EasServiceMetric;
};
export type TimelineActivityConnection = {
__typename?: 'TimelineActivityConnection';
edges: Array<TimelineActivityEdge>;
pageInfo: PageInfo;
};
export type TimelineActivityEdge = {
__typename?: 'TimelineActivityEdge';
cursor: Scalars['String'];
node: ActivityTimelineProjectActivity;
};
export type TimelineActivityFilterInput = {
channels?: InputMaybe<Array<Scalars['String']>>;
platforms?: InputMaybe<Array<AppPlatform>>;
releaseChannels?: InputMaybe<Array<Scalars['String']>>;
types?: InputMaybe<Array<ActivityTimelineProjectActivityType>>;
};
export type UnsubscribeFromNotificationResult = {
__typename?: 'UnsubscribeFromNotificationResult';
notificationSubscription: NotificationSubscription;
@@ -4542,6 +4579,7 @@ export type User = Actor & UserActor & {
appetizeCode?: Maybe<Scalars['String']>;
/** Apps this user has published */
apps: Array<App>;
bestContactEmail?: Maybe<Scalars['String']>;
created: Scalars['DateTime'];
displayName: Scalars['String'];
email?: Maybe<Scalars['String']>;
@@ -4625,6 +4663,7 @@ export type UserActor = {
appetizeCode?: Maybe<Scalars['String']>;
/** Apps this user has published */
apps: Array<App>;
bestContactEmail?: Maybe<Scalars['String']>;
created: Scalars['DateTime'];
/**
* Best-effort human readable name for this human actor for use in user interfaces during action attribution.
@@ -4644,6 +4683,7 @@ export type UserActor = {
isExpoAdmin: Scalars['Boolean'];
lastName?: Maybe<Scalars['String']>;
location?: Maybe<Scalars['String']>;
notificationSubscriptions: Array<NotificationSubscription>;
/** Associated accounts */
primaryAccount: Account;
profilePhoto: Scalars['String'];
@@ -4676,12 +4716,36 @@ export type UserActorFeatureGatesArgs = {
};
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
export type UserActorNotificationSubscriptionsArgs = {
filter?: InputMaybe<NotificationSubscriptionFilter>;
};
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
export type UserActorSnacksArgs = {
limit: Scalars['Int'];
offset: Scalars['Int'];
};
export type UserActorQuery = {
__typename?: 'UserActorQuery';
/** Query a UserActor by ID */
byId: UserActor;
/** Query a UserActor by username */
byUsername: UserActor;
};
export type UserActorQueryByIdArgs = {
id: Scalars['ID'];
};
export type UserActorQueryByUsernameArgs = {
username: Scalars['String'];
};
export type UserDataInput = {
appetizeCode?: InputMaybe<Scalars['String']>;
email?: InputMaybe<Scalars['String']>;
@@ -5393,7 +5457,7 @@ export type CreateAndroidBuildMutationVariables = Exact<{
}>;
export type CreateAndroidBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createAndroidBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
export type CreateAndroidBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createAndroidBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
export type CreateIosBuildMutationVariables = Exact<{
appId: Scalars['ID'];
@@ -5403,7 +5467,7 @@ export type CreateIosBuildMutationVariables = Exact<{
}>;
export type CreateIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createIosBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
export type CreateIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createIosBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
export type UpdateBuildMetadataMutationVariables = Exact<{
buildId: Scalars['ID'];
@@ -5411,7 +5475,7 @@ export type UpdateBuildMetadataMutationVariables = Exact<{
}>;
export type UpdateBuildMetadataMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', updateBuildMetadata: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type UpdateBuildMetadataMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', updateBuildMetadata: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type RetryIosBuildMutationVariables = Exact<{
buildId: Scalars['ID'];
@@ -5419,7 +5483,7 @@ export type RetryIosBuildMutationVariables = Exact<{
}>;
export type RetryIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', retryIosBuild: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type RetryIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', retryIosBuild: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type CreateEnvironmentSecretForAccountMutationVariables = Exact<{
input: CreateEnvironmentSecretInput;
@@ -5576,14 +5640,14 @@ export type BuildsByIdQueryVariables = Exact<{
}>;
export type BuildsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type BuildsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type BuildsWithSubmissionsByIdQueryVariables = Exact<{
buildId: Scalars['ID'];
}>;
export type BuildsWithSubmissionsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, resourceClass: BuildResourceClass, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type BuildsWithSubmissionsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type ViewBuildsOnAppQueryVariables = Exact<{
appId: Scalars['String'];
@@ -5593,7 +5657,7 @@ export type ViewBuildsOnAppQueryVariables = Exact<{
}>;
export type ViewBuildsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, builds: Array<{ __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }> } } };
export type ViewBuildsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, builds: Array<{ __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }> } } };
export type ViewUpdateChannelOnAppQueryVariables = Exact<{
appId: Scalars['String'];
@@ -5710,9 +5774,9 @@ export type AccountFragment = { __typename?: 'Account', id: string, name: string
export type AppFragment = { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, users: Array<{ __typename?: 'UserPermission', role?: Role | null, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } };
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
export type BuildWithSubmissionsFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, resourceClass: BuildResourceClass, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
export type BuildWithSubmissionsFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
export type EnvironmentSecretFragment = { __typename?: 'EnvironmentSecret', id: string, name: string, type: EnvironmentSecretType, createdAt: any };
@@ -52,6 +52,7 @@ export const BuildFragmentNode = gql`
priority
createdAt
updatedAt
message
completedAt
resourceClass
}
+3 -2
View File
@@ -90,11 +90,12 @@ export default class Log {
*/
export function link(
url: string,
{ text = url, dim = true }: { text?: string; dim?: boolean } = {}
{ text = url, fallback, dim = true }: { text?: string; dim?: boolean; fallback?: string } = {}
): string {
// Links can be disabled via env variables https://github.com/jamestalmage/supports-hyperlinks/blob/master/index.js
const output = terminalLink(text, url, {
fallback: () => (text === url ? chalk.underline(url) : `${text}: ${chalk.underline(url)}`),
fallback: () =>
fallback ?? (text === url ? chalk.underline(url) : `${text}: ${chalk.underline(url)}`),
});
return dim ? chalk.dim(output) : output;
}
@@ -16,7 +16,9 @@ import {
convertAssetToUpdateInfoGroupFormatAsync,
filterExportedPlatformsByFlag,
filterOutAssetsThatAlreadyExistAsync,
getAssetHashFromPath,
getBase64URLEncoding,
getOriginalPathFromAssetMap,
getStorageKey,
getStorageKeyForAssetAsync,
guessContentTypeFromExtension,
@@ -279,9 +281,55 @@ describe(resolveInputDirectoryAsync, () => {
});
});
describe(getAssetHashFromPath, () => {
it('returns asset hash from path', () => {
expect(getAssetHashFromPath('assets/5b2a819c71d035ca45d223e4c47ed4f9')).toBe(
'5b2a819c71d035ca45d223e4c47ed4f9'
);
});
it('returns null for incorrect path', () => {
expect(getAssetHashFromPath('assets/this/is/not/a/hash.jpg')).toBeNull();
});
});
describe(getOriginalPathFromAssetMap, () => {
// Partial assetmap.json, with only fields we need
const fakeAssetMap = {
'5b2a819c71d035ca45d223e4c47ed4f9': {
httpServerLocation: '/assets/src/assets',
name: 'asset-420',
type: 'jpg',
},
};
it('returns null path when asset map is null', () => {
expect(
getOriginalPathFromAssetMap(null, {
path: 'assets/5b2a819c71d035ca45d223e4c47ed4f9',
ext: 'jpg',
})
).toBeNull();
});
it('returns null when asset is not found in asset map', () => {
expect(
getOriginalPathFromAssetMap(fakeAssetMap, {
path: 'assets/fb64d3b2fb71b3d739ad5c13a93e12c5',
ext: 'jpg',
})
).toBeNull();
});
it('returns reconstructed original path from existing asset in asset map', () => {
expect(
getOriginalPathFromAssetMap(fakeAssetMap, {
path: 'assets/5b2a819c71d035ca45d223e4c47ed4f9',
ext: 'jpg',
})
).toBe('/src/assets/asset-420.jpg');
});
});
describe(collectAssetsAsync, () => {
it('builds an update info group', async () => {
const fakeHash = 'md5-hash-of-jpg';
const fakeHash = 'hdahukw8adhawi8fawhfa8';
const bundles = {
android: 'android-bundle-code',
ios: 'ios-bundle-code',
@@ -294,6 +342,7 @@ describe(collectAssetsAsync, () => {
fileExtension: '.jpg',
contentType: 'image/jpeg',
path: `${inputDir}/assets/${fakeHash}`,
originalPath: `assets/wat.jpg`,
},
];
@@ -326,6 +375,24 @@ describe(collectAssetsAsync, () => {
},
})
);
await fs.writeFile(
path.resolve(inputDir, 'assetmap.json'),
JSON.stringify({
[fakeHash]: {
__packager_asset: true,
fileSystemLocation: '/Users/blah/temp/assets',
httpServerLocation: 'assets/assets',
width: 2339,
height: 1560,
scales: [1],
files: ['/Users/blah/temp/assets/wat.jpg'],
hash: fakeHash,
name: 'wat',
type: 'jpg',
fileHashes: [fakeHash],
},
})
);
expect(await collectAssetsAsync(inputDir)).toEqual({
android: {
@@ -410,6 +477,7 @@ describe(uploadAssetsAsync, () => {
const androidBundlePath = uuidv4();
const iosBundlePath = uuidv4();
const dummyFilePath = uuidv4();
const dummyOriginalFilePath = uuidv4();
const userDefinedPath = uuidv4();
const testProjectId = uuidv4();
const expectedAssetLimit = 1400;
@@ -427,7 +495,15 @@ describe(uploadAssetsAsync, () => {
contentType: 'application/javascript',
path: androidBundlePath,
},
assets: [userDefinedAsset, { type: 'jpg', contentType: 'image/jpeg', path: dummyFilePath }],
assets: [
userDefinedAsset,
{
type: 'jpg',
contentType: 'image/jpeg',
path: dummyFilePath,
originalPath: dummyOriginalFilePath,
},
],
},
ios: {
launchAsset: {
@@ -435,7 +511,15 @@ describe(uploadAssetsAsync, () => {
contentType: 'application/javascript',
path: androidBundlePath,
},
assets: [userDefinedAsset, { type: 'jpg', contentType: 'image/jpeg', path: dummyFilePath }],
assets: [
userDefinedAsset,
{
type: 'jpg',
contentType: 'image/jpeg',
path: dummyFilePath,
originalPath: dummyOriginalFilePath,
},
],
},
};
@@ -490,8 +574,10 @@ describe(uploadAssetsAsync, () => {
uploadAssetsAsync(graphqlClient, assetsForUpdateInfoGroup, testProjectId)
).resolves.toEqual({
assetCount: 6,
launchAssetCount: 2,
uniqueAssetCount: 3,
uniqueUploadedAssetCount: 0,
uniqueUploadedAssetPaths: [],
assetLimitPerUpdateGroup: expectedAssetLimit,
});
});
@@ -526,8 +612,10 @@ describe(uploadAssetsAsync, () => {
uploadAssetsAsync(graphqlClient, assetsForUpdateInfoGroup, testProjectId)
).resolves.toEqual({
assetCount: 6,
launchAssetCount: 2,
uniqueAssetCount: 3,
uniqueUploadedAssetCount: 2,
uniqueUploadedAssetPaths: [],
assetLimitPerUpdateGroup: expectedAssetLimit,
});
});
@@ -74,7 +74,7 @@ export function parseGradleCommand(cmd: string, buildGradle: AppBuildGradle): Gr
const [moduleName, taskName] =
splitCmd.length > 1 ? [splitCmd[0], splitCmd[1]] : [undefined, splitCmd[0]];
const matchResult = taskName.match(/(build|bundle|assemble)(.*)(Release|Debug)/);
const matchResult = taskName.match(/(build|bundle|assemble|package)(.*)(Release|Debug)/);
if (!matchResult) {
throw new Error(`Failed to parse gradle command: ${cmd}`);
}
@@ -0,0 +1,26 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
export async function validateCustomBuildConfigAsync(
projectDir: string,
profile: BuildProfile<Platform>
): Promise<void> {
if (!profile.config) {
return;
}
const relativeBuildConfigPath = getCustomBuildConfigPath(profile.config);
const buildConfigPath = path.join(projectDir, relativeBuildConfigPath);
if (!(await fs.pathExists(buildConfigPath))) {
throw new Error(
`Custom build configuration file ${chalk.bold(relativeBuildConfigPath)} does not exist.`
);
}
}
export function getCustomBuildConfigPath(configFilename: string): string {
return path.join('.eas/build', configFilename);
}
+68
View File
@@ -15,6 +15,7 @@ import Log from '../log';
import { PresignedPost, uploadWithPresignedPostWithRetryAsync } from '../uploads';
import { expoCommandAsync, shouldUseVersionedExpoCLI } from '../utils/expoCli';
import chunk from '../utils/expodash/chunk';
import { truthy } from '../utils/expodash/filter';
import uniqBy from '../utils/expodash/uniqBy';
export type ExpoCLIExportPlatformFlag = Platform | 'all';
@@ -30,6 +31,8 @@ export type RawAsset = {
fileExtension?: string;
contentType: string;
path: string;
/** Original asset path derrived from asset map, or exported folder */
originalPath?: string;
};
type CollectedAssets = {
@@ -52,6 +55,16 @@ type UpdateInfoGroup = {
[key in Platform]: ManifestFragment;
};
// Partial copy of `@expo/dev-server` `BundleAssetWithFileHashes`
type AssetMap = Record<
string,
{
httpServerLocation: string;
name: string;
type: string;
}
>;
const fileMetadataJoi = Joi.object({
assets: Joi.array()
.required()
@@ -170,6 +183,7 @@ export async function buildBundlesAsync({
'--output-dir',
inputDir,
'--dump-sourcemap',
'--dump-assetmap',
'--platform',
platformFlag,
]);
@@ -182,6 +196,7 @@ export async function buildBundlesAsync({
'--experimental-bundle',
'--non-interactive',
'--dump-sourcemap',
'--dump-assetmap',
'--platform',
platformFlag,
]);
@@ -246,9 +261,48 @@ export function filterExportedPlatformsByFlag<T extends Partial<Record<Platform,
return { [platform]: record[platform] } as T;
}
/** Try to load the asset map for logging the names of assets published */
export async function loadAssetMapAsync(distRoot: string): Promise<AssetMap | null> {
const assetMapPath = path.join(distRoot, 'assetmap.json');
if (!(await fs.pathExists(assetMapPath))) {
return null;
}
const assetMap: AssetMap = JsonFile.read(path.join(distRoot, 'assetmap.json'));
// TODO: basic validation?
return assetMap;
}
// exposed for testing
export function getAssetHashFromPath(assetPath: string): string | null {
const [, hash] = assetPath.match(new RegExp(/assets\/([a-z0-9]+)$/, 'i')) ?? [];
return hash ?? null;
}
// exposed for testing
export function getOriginalPathFromAssetMap(
assetMap: AssetMap | null,
asset: { path: string; ext: string }
): string | null {
if (!assetMap) {
return null;
}
const assetHash = getAssetHashFromPath(asset.path);
const assetMapEntry = assetHash && assetMap[assetHash];
if (!assetMapEntry) {
return null;
}
const pathPrefix = assetMapEntry.httpServerLocation.substring('/assets'.length);
return `${pathPrefix}/${assetMapEntry.name}.${assetMapEntry.type}`;
}
/** Given a directory, load the metadata.json and collect the assets for each platform. */
export async function collectAssetsAsync(dir: string): Promise<CollectedAssets> {
const metadata = loadMetadata(dir);
const assetmap = await loadAssetMapAsync(dir);
const collectedAssets: CollectedAssets = {};
@@ -261,6 +315,7 @@ export async function collectAssetsAsync(dir: string): Promise<CollectedAssets>
},
assets: metadata.fileMetadata[platform].assets.map(asset => ({
fileExtension: asset.ext ? ensureLeadingPeriod(asset.ext) : undefined,
originalPath: getOriginalPathFromAssetMap(assetmap, asset) ?? undefined,
contentType: guessContentTypeFromExtension(asset.ext),
path: path.join(dir, asset.path),
})),
@@ -294,9 +349,17 @@ export async function filterOutAssetsThatAlreadyExistAsync(
}
type AssetUploadResult = {
/** All found assets within the exported folder per platform */
assetCount: number;
/** The uploaded JS bundles, per platform */
launchAssetCount: number;
/** All unique assets within the exported folder with platforms combined */
uniqueAssetCount: number;
/** All unique assets uploaded */
uniqueUploadedAssetCount: number;
/** All (non-launch) asset original paths, used for logging */
uniqueUploadedAssetPaths: string[];
/** The asset limit received from the server */
assetLimitPerUpdateGroup: number;
};
@@ -308,7 +371,9 @@ export async function uploadAssetsAsync(
): Promise<AssetUploadResult> {
let assets: RawAsset[] = [];
let platform: keyof CollectedAssets;
const launchAssets: RawAsset[] = [];
for (platform in assetsForUpdateInfoGroup) {
launchAssets.push(assetsForUpdateInfoGroup[platform]!.launchAsset);
assets = [
...assets,
assetsForUpdateInfoGroup[platform]!.launchAsset,
@@ -335,6 +400,7 @@ export async function uploadAssetsAsync(
updateSpinnerText?.(totalAssets, totalAssets);
let missingAssets = await filterOutAssetsThatAlreadyExistAsync(graphqlClient, uniqueAssets);
const uniqueUploadedAssetCount = missingAssets.length;
const uniqueUploadedAssetPaths = missingAssets.map(asset => asset.originalPath).filter(truthy);
const missingAssetChunks = chunk(missingAssets, 100);
const specifications: string[] = [];
@@ -372,8 +438,10 @@ export async function uploadAssetsAsync(
}
return {
assetCount: assets.length,
launchAssetCount: launchAssets.length,
uniqueAssetCount: uniqueAssets.length,
uniqueUploadedAssetCount,
uniqueUploadedAssetPaths,
assetLimitPerUpdateGroup,
};
}
+33 -15
View File
@@ -317,30 +317,48 @@ async function handleBuildListSourceAsync(
}
function formatBuildChoice(build: BuildFragment, expiryDate: Date): prompts.Choice {
const { id, updatedAt, runtimeVersion, buildProfile, gitCommitHash, gitCommitMessage, channel } =
build;
const formatValue = (field?: string | null): string => (field ? chalk.bold(field) : 'Unknown');
const {
id,
updatedAt,
runtimeVersion,
buildProfile,
gitCommitHash,
gitCommitMessage,
channel,
message,
} = build;
const buildDate = new Date(updatedAt);
const splitCommitMessage = gitCommitMessage?.split('\n');
const formattedCommitData =
gitCommitHash && gitCommitMessage
? `${gitCommitHash.slice(0, 7)} "${chalk.bold(gitCommitMessage)}"`
: 'Unknown';
gitCommitHash && splitCommitMessage && splitCommitMessage.length > 0
? `${gitCommitHash.slice(0, 7)} "${chalk.bold(
splitCommitMessage[0] + (splitCommitMessage.length > 1 ? '…' : '')
)}"`
: null;
const title = `${chalk.bold(`ID:`)} ${id} (${chalk.bold(`${fromNow(buildDate)} ago`)})`;
const description = [
`\t${chalk.bold(`Profile:`)} ${formatValue(buildProfile)}`,
`\t${chalk.bold(`Channel:`)} ${formatValue(channel)}`,
`\t${chalk.bold(`Runtime version:`)} ${formatValue(runtimeVersion)}`,
`\t${chalk.bold(`Commit:`)} ${formattedCommitData}`,
].join('\n');
const descriptionItems: { name: string; value: string | null }[] = [
{ name: 'Profile', value: buildProfile ? chalk.bold(buildProfile) : null },
{ name: 'Channel', value: channel ? chalk.bold(channel) : null },
{ name: 'Runtime version', value: runtimeVersion ? chalk.bold(runtimeVersion) : null },
{ name: 'Commit', value: formattedCommitData },
{
name: 'Message',
value: message
? chalk.bold(message.length > 200 ? `${message.slice(0, 200)}...` : message)
: null,
},
];
const filteredDescriptionArray: string[] = descriptionItems
.filter(item => item.value)
.map(item => `${chalk.bold(item.name)}: ${item.value}`);
return {
title,
description,
description: filteredDescriptionArray.length > 0 ? filteredDescriptionArray.join('\n') : '',
value: build,
disabled: buildDate < expiryDate,
};
+1 -15
View File
@@ -61,11 +61,7 @@ function replaceUndefinedObjectValues(
function mergeExpoConfig(exp: ExpoConfig, modifyExp: Partial<ExpoConfig>): Partial<ExpoConfig> {
return {
runtimeVersion: modifyExp.runtimeVersion ?? exp.runtimeVersion,
assetBundlePatterns: modifyExp.assetBundlePatterns ?? exp.assetBundlePatterns,
updates: {
...exp.updates,
url: modifyExp?.updates?.url || exp.updates?.url,
},
updates: { ...exp.updates, ...modifyExp.updates },
android: {
...exp.android,
...modifyExp.android,
@@ -103,10 +99,6 @@ async function ensureEASUpdatesIsConfiguredInExpoConfigAsync({
modifyConfig.updates = { url: getEASUpdateURL(projectId) };
}
if (!exp.assetBundlePatterns) {
modifyConfig.assetBundlePatterns = ['*/**'];
}
let androidRuntimeVersion = exp.android?.runtimeVersion ?? exp.runtimeVersion;
let iosRuntimeVersion = exp.ios?.runtimeVersion ?? exp.runtimeVersion;
@@ -193,12 +185,6 @@ function logEasUpdatesAutoConfig({
} with "${JSON.stringify(modifyConfig.ios?.runtimeVersion ?? modifyConfig.runtimeVersion)}"`
);
}
if (modifyConfig?.assetBundlePatterns) {
Log.withTick(
`Configured assetBundlePatterns to ${JSON.stringify(modifyConfig.assetBundlePatterns)}`
);
}
}
function warnEASUpdatesManualConfig({
+4 -3
View File
@@ -1,12 +1,12 @@
{
"name": "@expo/eas-json",
"description": "A library for interacting with eas.json",
"version": "3.5.0",
"version": "3.7.0",
"author": "Expo <[email protected]>",
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@babel/code-frame": "7.18.6",
"@expo/eas-build-job": "0.2.102",
"@expo/eas-build-job": "0.2.106",
"chalk": "4.1.2",
"env-string": "1.0.1",
"fs-extra": "10.1.0",
@@ -36,7 +36,8 @@
"build": "tsc --project tsconfig.build.json",
"watch": "yarn build --watch --preserveWatchOutput",
"typecheck": "tsc",
"prepack": "rimraf build && yarn build",
"prepack": "yarn rebuild",
"rebuild": "rimraf build && yarn build",
"test": "jest",
"clean": "rimraf build node_modules yarn-error.log"
},
+3 -2
View File
@@ -329,6 +329,7 @@
"enum": [
"default",
"latest",
"macos-monterey-12.6-xcode-14.2",
"macos-monterey-12.6-xcode-14.1",
"macos-monterey-12.6-xcode-14.0",
"macos-monterey-12.4-xcode-13.4",
@@ -338,8 +339,8 @@
"macos-big-sur-11.4-xcode-12.5"
],
"markdownEnumDescriptions": [
"`macos-monterey-12.6-xcode-14.0`",
"`macos-monterey-12.6-xcode-14.1`"
"`macos-monterey-12.6-xcode-14.1`",
"`macos-monterey-12.6-xcode-14.2`"
]
},
{
@@ -399,3 +399,64 @@ test('Android-specific resourceClass', async () => {
EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'production')
).resolves.not.toThrow();
});
test('build profile with platform-specific custom build config', async () => {
await fs.writeJson('/project/eas.json', {
build: {
production: {
android: {
config: 'production.android.yml',
},
ios: {
config: 'production.ios.yml',
},
},
},
});
const accessor = new EasJsonAccessor('/project');
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
accessor,
Platform.ANDROID,
'production'
);
const iosProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'production');
expect(androidProfile).toEqual({
config: 'production.android.yml',
distribution: 'store',
credentialsSource: 'remote',
});
expect(iosProfile).toEqual({
config: 'production.ios.yml',
distribution: 'store',
credentialsSource: 'remote',
});
});
test('build profiles with both platform build config', async () => {
await fs.writeJson('/project/eas.json', {
build: {
production: {
config: 'production.yml',
},
},
});
const accessor = new EasJsonAccessor('/project');
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
accessor,
Platform.ANDROID,
'production'
);
const iosProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'production');
expect(androidProfile).toEqual({
config: 'production.yml',
distribution: 'store',
credentialsSource: 'remote',
});
expect(iosProfile).toEqual({
config: 'production.yml',
distribution: 'store',
credentialsSource: 'remote',
});
});
+67 -38
View File
@@ -24,69 +24,98 @@ const CacheSchema = Joi.object({
});
const CommonBuildProfileSchema = Joi.object({
credentialsSource: Joi.string().valid('local', 'remote').default('remote'),
distribution: Joi.string().valid('store', 'internal').default('store'),
cache: CacheSchema,
releaseChannel: Joi.string().regex(/^[a-z\d][a-z\d._-]*$/),
channel: Joi.string().regex(/^[a-z\d][a-z\d._-]*$/),
developmentClient: Joi.boolean(),
prebuildCommand: Joi.string(),
buildArtifactPaths: Joi.array().items(Joi.string()),
// builder
resourceClass: Joi.string().valid(...AllowedCommonResourceClasses),
// build environment
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)),
node: Joi.string().empty(null).custom(semverCheck),
yarn: Joi.string().empty(null).custom(semverCheck),
expoCli: Joi.string().empty(null).custom(semverCheck),
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)),
// credentials
credentialsSource: Joi.string().valid('local', 'remote').default('remote'),
distribution: Joi.string().valid('store', 'internal').default('store'),
// updates
releaseChannel: Joi.string().regex(/^[a-z\d][a-z\d._-]*$/),
channel: Joi.string().regex(/^[a-z\d][a-z\d._-]*$/),
// build configuration
developmentClient: Joi.boolean(),
prebuildCommand: Joi.string(),
// versions
autoIncrement: Joi.alternatives().try(Joi.boolean()),
resourceClass: Joi.string().valid(...AllowedCommonResourceClasses),
// artifacts
buildArtifactPaths: Joi.array().items(Joi.string()),
// cache
cache: CacheSchema,
// custom build configuration
config: Joi.string(),
});
const AndroidBuildProfileSchema = CommonBuildProfileSchema.concat(
const PlatformBuildProfileSchema = CommonBuildProfileSchema.concat(
Joi.object({
credentialsSource: Joi.string().valid('local', 'remote'),
distribution: Joi.string().valid('store', 'internal'),
// build environment
image: Joi.string(),
// artifacts
artifactPath: Joi.string(),
applicationArchivePath: Joi.string(),
}).oxor('artifactPath', 'applicationArchivePath')
);
const AndroidBuildProfileSchema = PlatformBuildProfileSchema.concat(
Joi.object({
// builder
resourceClass: Joi.string().valid(...AllowedAndroidResourceClasses),
// build environment
ndk: Joi.string().empty(null).custom(semverCheck),
// credentials
withoutCredentials: Joi.boolean(),
image: Joi.string(),
ndk: Joi.string().empty(null).custom(semverCheck),
// build configuration
gradleCommand: Joi.string(),
buildType: Joi.string().valid('apk', 'app-bundle'),
// versions
autoIncrement: Joi.alternatives().try(
Joi.boolean(),
Joi.string().valid('version', 'versionCode')
),
resourceClass: Joi.string().valid(...AllowedAndroidResourceClasses),
artifactPath: Joi.string(),
applicationArchivePath: Joi.string(),
buildArtifactPaths: Joi.array().items(Joi.string()),
gradleCommand: Joi.string(),
buildType: Joi.string().valid('apk', 'app-bundle'),
}).oxor('artifactPath', 'applicationArchivePath')
})
);
const IosBuildProfileSchema = CommonBuildProfileSchema.concat(
const IosBuildProfileSchema = PlatformBuildProfileSchema.concat(
Joi.object({
credentialsSource: Joi.string().valid('local', 'remote'),
distribution: Joi.string().valid('store', 'internal'),
enterpriseProvisioning: Joi.string().valid('adhoc', 'universal'),
autoIncrement: Joi.alternatives().try(
Joi.boolean(),
Joi.string().valid('version', 'buildNumber')
),
simulator: Joi.boolean(),
// builder
resourceClass: Joi.string().valid(...AllowedIosResourceClasses),
image: Joi.string(),
// build environment
bundler: Joi.string().empty(null).custom(semverCheck),
fastlane: Joi.string().empty(null).custom(semverCheck),
cocoapods: Joi.string().empty(null).custom(semverCheck),
artifactPath: Joi.string(),
applicationArchivePath: Joi.string(),
buildArtifactPaths: Joi.array().items(Joi.string()),
// credentials
enterpriseProvisioning: Joi.string().valid('adhoc', 'universal'),
// build configuration
simulator: Joi.boolean(),
scheme: Joi.string(),
buildConfiguration: Joi.string(),
}).oxor('artifactPath', 'applicationArchivePath')
// versions
autoIncrement: Joi.alternatives().try(
Joi.boolean(),
Joi.string().valid('version', 'buildNumber')
),
})
);
export const BuildProfileSchema = CommonBuildProfileSchema.concat(
+55 -28
View File
@@ -29,32 +29,42 @@ export type IosVersionAutoIncrement = VersionAutoIncrement | 'buildNumber';
export type AndroidVersionAutoIncrement = VersionAutoIncrement | 'versionCode';
export interface CommonBuildProfile {
credentialsSource: CredentialsSource;
distribution: DistributionType;
cache?: Omit<Cache, 'clear'>;
releaseChannel?: string;
channel?: string;
developmentClient?: boolean;
prebuildCommand?: string;
autoIncrement?: boolean;
// builder
resourceClass?: ResourceClass;
buildArtifactPaths?: string[];
// build environment
env?: Record<string, string>;
node?: string;
yarn?: string;
expoCli?: string;
env?: Record<string, string>;
// credentials
credentialsSource: CredentialsSource;
distribution: DistributionType;
// updates
releaseChannel?: string;
channel?: string;
// build configuration
developmentClient?: boolean;
prebuildCommand?: string;
// versions
autoIncrement?: boolean;
// artifacts
buildArtifactPaths?: string[];
// cache
cache?: Omit<Cache, 'clear'>;
// custom build configuration
config?: string;
}
export interface AndroidBuildProfile extends Omit<CommonBuildProfile, 'autoIncrement'> {
withoutCredentials?: boolean;
image?: Android.BuilderEnvironment['image'];
ndk?: string;
autoIncrement?: AndroidVersionAutoIncrement;
buildType?: Android.BuildType.APK | Android.BuildType.APP_BUNDLE;
gradleCommand?: string;
interface PlatformBuildProfile extends Omit<CommonBuildProfile, 'autoIncrement'> {
// artifacts
/**
* @deprecated use applicationArchivePath
*/
@@ -62,22 +72,39 @@ export interface AndroidBuildProfile extends Omit<CommonBuildProfile, 'autoIncre
applicationArchivePath?: string;
}
export interface IosBuildProfile extends Omit<CommonBuildProfile, 'autoIncrement'> {
enterpriseProvisioning?: IosEnterpriseProvisioning;
autoIncrement?: IosVersionAutoIncrement;
simulator?: boolean;
export interface AndroidBuildProfile extends PlatformBuildProfile {
// build environment
image?: Android.BuilderEnvironment['image'];
ndk?: string;
// credentials
withoutCredentials?: boolean;
// build configuration
gradleCommand?: string;
buildType?: Android.BuildType.APK | Android.BuildType.APP_BUNDLE;
// versions
autoIncrement?: AndroidVersionAutoIncrement;
}
export interface IosBuildProfile extends PlatformBuildProfile {
// build environment
image?: Ios.BuilderEnvironment['image'];
bundler?: string;
fastlane?: string;
cocoapods?: string;
/**
* @deprecated use applicationArchivePath
*/
artifactPath?: string;
applicationArchivePath?: string;
// credentials
enterpriseProvisioning?: IosEnterpriseProvisioning;
// build configuration
simulator?: boolean;
scheme?: string;
buildConfiguration?: string;
// versions
autoIncrement?: IosVersionAutoIncrement;
}
export type BuildProfile<TPlatform extends Platform = Platform> = TPlatform extends Platform.ANDROID
Generated
+25 -40
View File
@@ -1324,10 +1324,10 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@expo/apple-utils@0.0.0-alpha.37":
version "0.0.0-alpha.37"
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-0.0.0-alpha.37.tgz#e85dea5c4ada8f1e8e26ce9670fb6d38c0092299"
integrity sha512-/v7adya/u6GDBj7py+HsnKmA11VwG34z4/7peJdMfu8M3LZ2G14dsaxEqdndrCAuEyJ6gQ88sggjcjPjzQXdmg==
"@expo/apple-utils@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-1.1.0.tgz#62cd724436b8eaa9cafcc3648ea211af8409fc71"
integrity sha512-caw0o9HeQHX7xeXppkXI3B/PmPyVOkEjWgXdv0dYzgW/rwvZRLwtMNFXX5t+dJy2hd2UpZlrixaoYyvxuXPtbQ==
"@expo/bunyan@^4.0.0":
version "4.0.0"
@@ -1390,21 +1390,13 @@
slugify "^1.3.4"
sucrase "^3.20.0"
"@expo/[email protected]2":
version "0.2.102"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.102.tgz#6c23388a7ac12d60a46d2a9ef779075f5c1a874b"
integrity sha512-1gav4gHhZCnRDIscd0/sat/fjts543QmexiWXUWDv15uzCxx/NnewN13tcmOwMV0K3Z3agOlJLRPN2t6UvIMAg==
"@expo/[email protected]6":
version "0.2.106"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.106.tgz#a10f791986bfa9777a13b2b0e9c820673e9e38ba"
integrity sha512-V7ihSFNFaATM31P1b/tGrGG7sjuXBT5ZAzKnybTWr5Xl1ODOdEpSC1c9bzs9SUr2iWUFVsxyd2/7+f2+7r/l4A==
dependencies:
joi "^17.4.2"
semver "^7.3.5"
"@expo/[email protected]":
version "0.2.103"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.103.tgz#0b587ada854570bc664f59dcd43dc3ca4221d451"
integrity sha512-cstgN8sVIYjq37xHm/4E6jnAlYf1gndvWfuVzRicUjp1TVyGjV9IbilSOxJp8AEqgPZK6+bZOcMRXT634M0Nmg==
dependencies:
joi "^17.4.2"
semver "^7.3.5"
joi "^17.7.0"
semver "^7.3.8"
"@expo/[email protected]":
version "0.3.22"
@@ -3985,13 +3977,6 @@
component-type "^1.2.1"
join-component "^1.1.0"
"@sideway/address@^4.1.0":
version "4.1.2"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1"
integrity sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==
dependencies:
"@hapi/hoek" "^9.0.0"
"@sideway/address@^4.1.3":
version "4.1.3"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27"
@@ -4004,6 +3989,11 @@
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c"
integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==
"@sideway/formula@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
"@sideway/pinpoint@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
@@ -9328,15 +9318,15 @@ [email protected]:
"@sideway/formula" "^3.0.0"
"@sideway/pinpoint" "^2.0.0"
joi@^17.4.2:
version "17.4.2"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.2.tgz#02f4eb5cf88e515e614830239379dcbbe28ce7f7"
integrity sha512-Lm56PP+n0+Z2A2rfRvsfWVDXGEWjXxatPopkQ8qQ5mxCEhwHG+Ettgg5o98FFaxilOxozoa14cFhrE/hOzh/Nw==
joi@^17.7.0:
version "17.8.3"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.3.tgz#d772fe27a87a5cda21aace5cf11eee8671ca7e6f"
integrity sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w==
dependencies:
"@hapi/hoek" "^9.0.0"
"@hapi/topo" "^5.0.0"
"@sideway/address" "^4.1.0"
"@sideway/formula" "^3.0.0"
"@sideway/address" "^4.1.3"
"@sideway/formula" "^3.0.1"
"@sideway/pinpoint" "^2.0.0"
join-component@^1.1.0:
@@ -13190,18 +13180,13 @@ unc-path-regex@^0.1.2:
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo=
undici@^5.12.0:
version "5.14.0"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.14.0.tgz#1169d0cdee06a4ffdd30810f6228d57998884d00"
integrity sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==
undici@^5.12.0, undici@^5.8.0:
version "5.19.1"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.19.1.tgz#92b1fd3ab2c089b5a6bd3e579dcda8f1934ebf6d"
integrity sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==
dependencies:
busboy "^1.6.0"
undici@^5.8.0:
version "5.8.2"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.8.2.tgz#071fc8a6a5d24db0ad510ad442f607d9b09d5eec"
integrity sha512-3KLq3pXMS0Y4IELV045fTxqz04Nk9Ms7yfBBHum3yxsTR4XNn+ZCaUbf/mWitgYDAhsplQ0B1G4S5D345lMO3A==
unique-filename@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"