From 7073e407d339b00f628bc6471f0e8e6235e92257 Mon Sep 17 00:00:00 2001 From: Gabriel Donadel <donadeldev@gmail.com> Date: Thu, 6 Mar 2025 10:45:34 -0500 Subject: [PATCH 1/8] Add share command --- packages/eas-cli/src/commands/share.ts | 157 ++++++++++++++++++ .../graphql/mutations/ShareBuildMutation.ts | 42 +++++ 2 files changed, 199 insertions(+) create mode 100644 packages/eas-cli/src/commands/share.ts create mode 100644 packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts diff --git a/packages/eas-cli/src/commands/share.ts b/packages/eas-cli/src/commands/share.ts new file mode 100644 index 0000000000..aceb0b8ff4 --- /dev/null +++ b/packages/eas-cli/src/commands/share.ts @@ -0,0 +1,157 @@ +import { Platform } from '@expo/eas-build-job'; +import { Flags } from '@oclif/core'; +import fg from 'fast-glob'; +import fs from 'fs-extra'; +import path from 'path'; + +import EasCommand from '../commandUtils/EasCommand'; +import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient'; +import { EASNonInteractiveFlag } from '../commandUtils/flags'; +import { UploadSessionType } from '../graphql/generated'; +import { ShareBuildMutation } from '../graphql/mutations/ShareBuildMutation'; +import Log from '../log'; +import { promptAsync } from '../prompts'; +import { uploadFileAtPathToGCSAsync } from '../uploads'; +import { createProgressTracker } from '../utils/progress'; + +export default class BuildUpload extends EasCommand { + static override description = 'upload a local build and generate a sharable link'; + + static override flags = { + platform: Flags.enum<Platform.IOS | Platform.ANDROID>({ + char: 'p', + options: [Platform.IOS, Platform.ANDROID], + }), + 'build-path': Flags.string({ + description: 'Path for the local build', + }), + ...EASNonInteractiveFlag, + }; + + static override contextDefinition = { + ...this.ContextOptions.ProjectId, + ...this.ContextOptions.LoggedIn, + }; + + async runAsync(): Promise<void> { + const { flags } = await this.parse(BuildUpload); + const { 'build-path': buildPath } = flags; + const { + projectId, + loggedIn: { graphqlClient }, + } = await this.getContextAsync(BuildUpload, { + nonInteractive: false, + }); + + const platform = await this.selectPlatformAsync(flags.platform); + const localBuildPath = await resolveLocalBuildPathAsync({ + platform, + buildPath, + }); + + Log.log('Uploading your app archive to EAS Share'); + const bucketKey = await uploadAppArchiveAsync(graphqlClient, localBuildPath); + + // @TODO: use Share build mutation + const { debugInfoUrl } = await ShareBuildMutation.uploadLocalBuildAsync( + graphqlClient, + projectId, + { bucketKey }, + { + // @TODO: read the fingerprint from the local build + hash: '', + } + ); + + Log.withTick(`Here is a sharable link of your build: ${debugInfoUrl}`); + } + + private async selectPlatformAsync(platform?: Platform): Promise<Platform> { + if (platform) { + return platform; + } + const { resolvedPlatform } = await promptAsync({ + type: 'select', + message: 'Select platform', + name: 'resolvedPlatform', + choices: [ + { title: 'Android', value: Platform.ANDROID }, + { title: 'iOS', value: Platform.IOS }, + ], + }); + return resolvedPlatform; + } +} + +async function resolveLocalBuildPathAsync({ + platform, + buildPath, +}: { + platform: Platform; + buildPath?: string; +}): Promise<string> { + const applicationArchivePatternOrPath = + buildPath ?? platform === Platform.ANDROID + ? 'android/app/build/outputs/**/*.{apk,aab}' + : 'ios/build/Build/Products/*simulator/*.app'; + + const applicationArchives = await findArtifactsAsync({ + rootDir: process.cwd(), + patternOrPath: applicationArchivePatternOrPath, + }); + + const count = applicationArchives.length; + Log.log( + `Found ${count} application archive${count > 1 ? 's' : ''}:\n- ${applicationArchives.join( + '\n- ' + )}` + ); + return applicationArchives[0]; +} + +async function findArtifactsAsync({ + rootDir, + patternOrPath, +}: { + rootDir: string; + patternOrPath: string; +}): Promise<string[]> { + const files: string[] = path.isAbsolute(patternOrPath) + ? (await fs.pathExists(patternOrPath)) + ? [patternOrPath] + : [] + : await fg(patternOrPath, { cwd: rootDir, onlyFiles: false }); + if (files.length === 0) { + throw new Error(`Found no application archives for "${patternOrPath}".`); + } + + return files.map(filePath => { + // User may provide an absolute path as input in which case + // fg will return an absolute path. + if (path.isAbsolute(filePath)) { + return filePath; + } + + // User may also provide a relative path in which case + // fg will return a path relative to rootDir. + return path.join(rootDir, filePath); + }); +} + +async function uploadAppArchiveAsync( + graphqlClient: ExpoGraphqlClient, + path: string +): Promise<string> { + const fileSize = (await fs.stat(path)).size; + const bucketKey = await uploadFileAtPathToGCSAsync( + graphqlClient, + UploadSessionType.EasSubmitGcsAppArchive, + path, + createProgressTracker({ + total: fileSize, + message: 'Uploading to EAS Share', + completedMessage: 'Uploaded to EAS Share', + }) + ); + return bucketKey; +} diff --git a/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts b/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts new file mode 100644 index 0000000000..2514bf81ab --- /dev/null +++ b/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts @@ -0,0 +1,42 @@ +import { FingerprintSource } from '@expo/eas-build-job'; +import { print } from 'graphql'; +import gql from 'graphql-tag'; + +import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient'; +import { withErrorHandlingAsync } from '../client'; +import { CreateFingeprintMutation, FingerprintFragment } from '../generated'; +import { FingerprintFragmentNode } from '../types/Fingerprint'; + +export const ShareBuildMutation = { + async uploadLocalBuildAsync( + graphqlClient: ExpoGraphqlClient, + appId: string, + buildSource: { + bucketKey: string; + }, + fingerprintData: { hash: string; source?: FingerprintSource } + ): Promise<FingerprintFragment> { + const data = await withErrorHandlingAsync( + graphqlClient + .mutation<CreateFingeprintMutation>( + gql` + mutation CreateFingeprintMutation( + $fingerprintData: CreateFingerprintInput! + $appId: ID! + ) { + fingerprint { + createOrGetExistingFingerprint(fingerprintData: $fingerprintData, appId: $appId) { + id + ...FingerprintFragment + } + } + } + ${print(FingerprintFragmentNode)} + `, + { appId, fingerprintData } + ) + .toPromise() + ); + return data.fingerprint.createOrGetExistingFingerprint; + }, +}; From cad966e0757a2859bc32533f7dd63f498768209f Mon Sep 17 00:00:00 2001 From: Gabriel Donadel <donadeldev@gmail.com> Date: Mon, 10 Mar 2025 14:20:31 -0400 Subject: [PATCH 2/8] Use EasShareGcsAppArchive upload type --- packages/eas-cli/graphql.schema.json | 1543 ++++++++++++++++++--- packages/eas-cli/src/commands/share.ts | 2 +- packages/eas-cli/src/graphql/generated.ts | 161 ++- 3 files changed, 1523 insertions(+), 183 deletions(-) diff --git a/packages/eas-cli/graphql.schema.json b/packages/eas-cli/graphql.schema.json index ef6a2413c7..e3af8f6664 100644 --- a/packages/eas-cli/graphql.schema.json +++ b/packages/eas-cli/graphql.schema.json @@ -1893,6 +1893,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "pendingSentryInstallation", + "description": null, + "args": [], + "type": { + "kind": "OBJECT", + "name": "PendingSentryInstallation", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "profileImageUrl", "description": null, @@ -1941,6 +1953,18 @@ "isDeprecated": true, "deprecationReason": "Legacy access tokens are deprecated" }, + { + "name": "sentryInstallation", + "description": null, + "args": [], + "type": { + "kind": "OBJECT", + "name": "SentryInstallation", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "snacks", "description": "Snacks associated with this account", @@ -6665,7 +6689,7 @@ "fields": [ { "name": "createAndroidFcm", - "description": "Create an FCM credential", + "description": "Create an FCM V0/Legacy credential", "args": [ { "name": "accountId", @@ -6709,12 +6733,12 @@ "ofType": null } }, - "isDeprecated": false, - "deprecationReason": null + "isDeprecated": true, + "deprecationReason": "FCM Legacy credentials are no longer supported by Google. Use createFcmV1Credential instead." }, { "name": "deleteAndroidFcm", - "description": "Delete an FCM credential", + "description": "Delete an FCM V0/Legacy credential", "args": [ { "name": "id", @@ -9703,6 +9727,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "filter", + "description": null, + "type": { + "kind": "INPUT_OBJECT", + "name": "RuntimeFilterInput", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "first", "description": null, @@ -9772,6 +9808,18 @@ "isDeprecated": true, "deprecationReason": "Classic updates have been deprecated." }, + { + "name": "sentryProject", + "description": null, + "args": [], + "type": { + "kind": "OBJECT", + "name": "SentryProject", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "slug", "description": null, @@ -18475,6 +18523,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "cliVersion", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "completedAt", "description": null, @@ -18668,8 +18728,8 @@ "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "isDeprecated": true, + "deprecationReason": "Use 'githubRepository' field instead" }, { "name": "id", @@ -23886,6 +23946,65 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "CreateSentryProjectInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "appId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "sentryProjectId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "sentryProjectSlug", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "INPUT_OBJECT", "name": "CreateSharedEnvironmentVariableInput", @@ -25356,7 +25475,7 @@ }, { "kind": "OBJECT", - "name": "DeleteUpdateBranchResult", + "name": "DeleteSentryProjectResult", "description": null, "fields": [ { @@ -25383,7 +25502,7 @@ }, { "kind": "OBJECT", - "name": "DeleteUpdateChannelResult", + "name": "DeleteUpdateBranchResult", "description": null, "fields": [ { @@ -25410,11 +25529,11 @@ }, { "kind": "OBJECT", - "name": "DeleteUpdateGroupResult", + "name": "DeleteUpdateChannelResult", "description": null, "fields": [ { - "name": "group", + "name": "id", "description": null, "args": [], "type": { @@ -25437,11 +25556,11 @@ }, { "kind": "OBJECT", - "name": "DeleteWebhookResult", + "name": "DeleteUpdateGroupResult", "description": null, "fields": [ { - "name": "id", + "name": "group", "description": null, "args": [], "type": { @@ -25464,25 +25583,9 @@ }, { "kind": "OBJECT", - "name": "DeleteWorkerDeploymentResult", + "name": "DeleteWebhookResult", "description": null, "fields": [ - { - "name": "deploymentIdentifier", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "WorkerDeploymentIdentifier", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "id", "description": null, @@ -25507,121 +25610,164 @@ }, { "kind": "OBJECT", - "name": "Deployment", - "description": "Represents a Deployment - a set of Builds with the same Runtime Version and Channel", + "name": "DeleteWorkerDeploymentResult", + "description": null, "fields": [ { - "name": "buildCount", - "description": null, - "args": [ - { - "name": "statuses", - "description": null, - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "BuildStatus", - "ofType": null - } - } - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "builds", - "description": null, - "args": [ - { - "name": "after", - "description": null, - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "before", - "description": null, - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "first", - "description": null, - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "last", - "description": null, - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "DeploymentBuildsConnection", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "channel", + "name": "deploymentIdentifier", "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "OBJECT", - "name": "UpdateChannel", + "kind": "SCALAR", + "name": "WorkerDeploymentIdentifier", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "Deployment", + "description": "Represents a Deployment - a set of Builds with the same Runtime Version and Channel", + "fields": [ + { + "name": "buildCount", + "description": null, + "args": [ + { + "name": "statuses", + "description": null, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "BuildStatus", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "builds", + "description": null, + "args": [ + { + "name": "after", + "description": null, + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "before", + "description": null, + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "first", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "last", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "DeploymentBuildsConnection", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "channel", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "UpdateChannel", "ofType": null } }, @@ -27284,6 +27430,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "LogRocketOrganizationEntity", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "LogRocketProjectEntity", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "UserInvitationEntity", "description": null, @@ -30148,6 +30306,65 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "GenerateSentryTokenResult", + "description": null, + "fields": [ + { + "name": "installationId", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "orgSlug", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "token", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "GetSignedAssetUploadSpecificationsResult", @@ -36863,6 +37080,81 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "LinkSentryInstallationToExpoAccountInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "accountId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "code", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "installationId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "sentryOrgSlug", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "INPUT_OBJECT", "name": "LinkSharedEnvironmentVariableInput", @@ -39446,6 +39738,97 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "PendingSentryInstallation", + "description": null, + "fields": [ + { + "name": "account", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Account", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "createdAt", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "installationId", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "orgSlug", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "ENUM", "name": "Permission", @@ -40352,6 +40735,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platform", + "description": null, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "UserAgentPlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "requestId", "description": null, @@ -41317,7 +41720,7 @@ }, { "name": "androidFcm", - "description": "Mutations that modify an FCM credential", + "description": "Mutations that modify an FCM V0/Legacy credential", "args": [], "type": { "kind": "NON_NULL", @@ -41993,6 +42396,38 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "sentryInstallation", + "description": "Mutations for Sentry installations", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "SentryInstallationMutation", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "sentryProject", + "description": "Mutations for Sentry projects", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "SentryProjectMutation", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "submission", "description": "Mutations that modify an EAS Submit submission", @@ -43318,6 +43753,22 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "isFingerprint", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "updatedAt", "description": null, @@ -43603,6 +44054,26 @@ "defaultValue": null, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "runtimeVersions", + "description": null, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null } ], "interfaces": null, @@ -44642,52 +45113,311 @@ }, { "kind": "OBJECT", - "name": "SecondFactorInitiationResult", + "name": "SecondFactorInitiationResult", + "description": null, + "fields": [ + { + "name": "configurationResults", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "SecondFactorDeviceConfigurationResult", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "plaintextBackupCodes", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "SecondFactorMethod", + "description": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "AUTHENTICATOR", + "description": "Google Authenticator (TOTP)", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "SMS", + "description": "SMS", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SecondFactorRegenerateBackupCodesResult", + "description": null, + "fields": [ + { + "name": "plaintextBackupCodes", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SentryInstallation", + "description": null, + "fields": [ + { + "name": "account", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Account", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "createdAt", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "installationId", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "orgSlug", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SentryInstallationMutation", "description": null, "fields": [ { - "name": "configurationResults", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { + "name": "confirmPendingSentryInstallation", + "description": "Confirm a pending Sentry installation", + "args": [ + { + "name": "installationId", + "description": null, + "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "OBJECT", - "name": "SecondFactorDeviceConfigurationResult", + "kind": "SCALAR", + "name": "ID", "ofType": null } - } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "SentryInstallation", + "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "plaintextBackupCodes", - "description": null, - "args": [], + "name": "generateSentryToken", + "description": "Generate a Sentry token for an installation", + "args": [ + { + "name": "accountId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "LIST", - "name": null, - "ofType": { + "kind": "OBJECT", + "name": "GenerateSentryTokenResult", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "linkSentryInstallationToExpoAccount", + "description": "Link a Sentry installation to an Expo account", + "args": [ + { + "name": "input", + "description": null, + "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "String", + "kind": "INPUT_OBJECT", + "name": "LinkSentryInstallationToExpoAccountInput", "ofType": null } - } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PendingSentryInstallation", + "ofType": null } }, "isDeprecated": false, @@ -44700,52 +45430,194 @@ "possibleTypes": null }, { - "kind": "ENUM", - "name": "SecondFactorMethod", + "kind": "OBJECT", + "name": "SentryProject", "description": null, - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ + "fields": [ { - "name": "AUTHENTICATOR", - "description": "Google Authenticator (TOTP)", + "name": "app", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "App", + "ofType": null + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "SMS", - "description": "SMS", + "name": "createdAt", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "sentryInstallationId", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "sentryProjectId", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "sentryProjectSlug", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updatedAt", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + }, "isDeprecated": false, "deprecationReason": null } ], + "inputFields": null, + "interfaces": [], + "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "SecondFactorRegenerateBackupCodesResult", + "name": "SentryProjectMutation", "description": null, "fields": [ { - "name": "plaintextBackupCodes", - "description": null, - "args": [], + "name": "createSentryProject", + "description": "Create a Sentry project", + "args": [ + { + "name": "input", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CreateSentryProjectInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "LIST", - "name": null, - "ofType": { + "kind": "OBJECT", + "name": "SentryProject", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "deleteSentryProject", + "description": "Delete a Sentry project by ID", + "args": [ + { + "name": "sentryProjectId", + "description": null, + "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "String", + "name": "ID", "ofType": null } - } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "DeleteSentryProjectResult", + "ofType": null } }, "isDeprecated": false, @@ -49935,6 +50807,12 @@ "isDeprecated": true, "deprecationReason": "Use EAS_BUILD_GCS_PROJECT_SOURCES instead." }, + { + "name": "EAS_SHARE_GCS_APP_ARCHIVE", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "EAS_SUBMIT_APP_ARCHIVE", "description": null, @@ -52225,6 +53103,41 @@ ], "possibleTypes": null }, + { + "kind": "ENUM", + "name": "UserAgentPlatform", + "description": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "ANDROID", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "APPLE", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "UNKNOWN", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "WEB", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, { "kind": "OBJECT", "name": "UserAppPinMutation", @@ -56974,6 +57887,22 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platform", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "UserAgentPlatform", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "region", "description": null, @@ -57395,6 +58324,104 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "byPathname", + "description": null, + "args": [ + { + "name": "limit", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "orderBy", + "description": null, + "type": { + "kind": "INPUT_OBJECT", + "name": "RequestsOrderBy", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WorkerDeploymentRequestsPathnameEdge", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "byPlatform", + "description": null, + "args": [ + { + "name": "limit", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "orderBy", + "description": null, + "type": { + "kind": "INPUT_OBJECT", + "name": "RequestsOrderBy", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WorkerDeploymentRequestsPlatformEdge", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "byResponseType", "description": null, @@ -58440,6 +59467,92 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "WorkerDeploymentRequestsPathnameEdge", + "description": null, + "fields": [ + { + "name": "node", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WorkerDeploymentRequestsAggregationNode", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "pathname", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "WorkerDeploymentRequestsPlatformEdge", + "description": null, + "fields": [ + { + "name": "node", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WorkerDeploymentRequestsAggregationNode", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "platform", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "UserAgentPlatform", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "WorkerDeploymentRequestsResponseTypeEdge", @@ -58671,6 +59784,54 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "byPathname", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WorkerDeploymentRequestsPathnameEdge", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "byPlatform", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WorkerDeploymentRequestsPlatformEdge", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "byResponseType", "description": null, @@ -59700,6 +60861,12 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "FINGERPRINT", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "GET_BUILD", "description": null, @@ -60490,6 +61657,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "triggeringLabelName", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "updatedAt", "description": null, @@ -60914,6 +62093,12 @@ "inputFields": null, "interfaces": null, "enumValues": [ + { + "name": "GITHUB_PULL_REQUEST_LABELED", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "GITHUB_PULL_REQUEST_OPENED", "description": null, diff --git a/packages/eas-cli/src/commands/share.ts b/packages/eas-cli/src/commands/share.ts index aceb0b8ff4..f3475bcd08 100644 --- a/packages/eas-cli/src/commands/share.ts +++ b/packages/eas-cli/src/commands/share.ts @@ -145,7 +145,7 @@ async function uploadAppArchiveAsync( const fileSize = (await fs.stat(path)).size; const bucketKey = await uploadFileAtPathToGCSAsync( graphqlClient, - UploadSessionType.EasSubmitGcsAppArchive, + UploadSessionType.EasShareGcsAppArchive, path, createProgressTracker({ total: fileSize, diff --git a/packages/eas-cli/src/graphql/generated.ts b/packages/eas-cli/src/graphql/generated.ts index a71f81f8d9..127ec1f441 100644 --- a/packages/eas-cli/src/graphql/generated.ts +++ b/packages/eas-cli/src/graphql/generated.ts @@ -147,10 +147,12 @@ export type Account = { owner?: Maybe<User>; /** Owning UserActor of this account if personal account */ ownerUserActor?: Maybe<UserActor>; + pendingSentryInstallation?: Maybe<PendingSentryInstallation>; profileImageUrl: Scalars['String']['output']; pushSecurityEnabled: Scalars['Boolean']['output']; /** @deprecated Legacy access tokens are deprecated */ requiresAccessTokenForPushSecurity: Scalars['Boolean']['output']; + sentryInstallation?: Maybe<SentryInstallation>; /** Snacks associated with this account */ snacks: Array<Snack>; /** SSO configuration for this account */ @@ -1053,9 +1055,12 @@ export type AndroidFcmInput = { export type AndroidFcmMutation = { __typename?: 'AndroidFcmMutation'; - /** Create an FCM credential */ + /** + * Create an FCM V0/Legacy credential + * @deprecated FCM Legacy credentials are no longer supported by Google. Use createFcmV1Credential instead. + */ createAndroidFcm: AndroidFcm; - /** Delete an FCM credential */ + /** Delete an FCM V0/Legacy credential */ deleteAndroidFcm: DeleteAndroidFcmResult; }; @@ -1342,6 +1347,7 @@ export type App = Project & { * @deprecated Classic updates have been deprecated. */ sdkVersion: Scalars['String']['output']; + sentryProject?: Maybe<SentryProject>; slug: Scalars['String']['output']; /** EAS Submissions associated with this app */ submissions: Array<Submission>; @@ -1525,6 +1531,7 @@ export type AppLikedByArgs = { export type AppRuntimesArgs = { after?: InputMaybe<Scalars['String']['input']>; before?: InputMaybe<Scalars['String']['input']>; + filter?: InputMaybe<RuntimeFilterInput>; first?: InputMaybe<Scalars['Int']['input']>; last?: InputMaybe<Scalars['Int']['input']>; }; @@ -2749,6 +2756,7 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & { /** @deprecated Use 'updateChannel' field instead. */ channel?: Maybe<Scalars['String']['output']>; childBuild?: Maybe<Build>; + cliVersion?: Maybe<Scalars['String']['output']>; completedAt?: Maybe<Scalars['DateTime']['output']>; createdAt: Scalars['DateTime']['output']; customNodeVersion?: Maybe<Scalars['String']['output']>; @@ -2764,6 +2772,7 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & { gitCommitHash?: Maybe<Scalars['String']['output']>; gitCommitMessage?: Maybe<Scalars['String']['output']>; gitRef?: Maybe<Scalars['String']['output']>; + /** @deprecated Use 'githubRepository' field instead */ githubRepositoryOwnerAndName?: Maybe<Scalars['String']['output']>; id: Scalars['ID']['output']; /** Queue position is 1-indexed */ @@ -3472,6 +3481,12 @@ export type CreateIosSubmissionInput = { submittedBuildId?: InputMaybe<Scalars['ID']['input']>; }; +export type CreateSentryProjectInput = { + appId: Scalars['ID']['input']; + sentryProjectId: Scalars['String']['input']; + sentryProjectSlug: Scalars['String']['input']; +}; + export type CreateSharedEnvironmentVariableInput = { environments?: InputMaybe<Array<EnvironmentVariableEnvironment>>; fileName?: InputMaybe<Scalars['String']['input']>; @@ -3691,6 +3706,11 @@ export type DeleteSsoUserResult = { id: Scalars['ID']['output']; }; +export type DeleteSentryProjectResult = { + __typename?: 'DeleteSentryProjectResult'; + id: Scalars['ID']['output']; +}; + export type DeleteUpdateBranchResult = { __typename?: 'DeleteUpdateBranchResult'; id: Scalars['ID']['output']; @@ -4001,6 +4021,8 @@ export enum EntityTypeName { CustomerEntity = 'CustomerEntity', GoogleServiceAccountKeyEntity = 'GoogleServiceAccountKeyEntity', IosAppCredentialsEntity = 'IosAppCredentialsEntity', + LogRocketOrganizationEntity = 'LogRocketOrganizationEntity', + LogRocketProjectEntity = 'LogRocketProjectEntity', UserInvitationEntity = 'UserInvitationEntity', UserPermissionEntity = 'UserPermissionEntity', WorkerCustomDomainEntity = 'WorkerCustomDomainEntity', @@ -4388,6 +4410,13 @@ export type GenerateLogRocketReplayTokenResult = { replayToken: Scalars['String']['output']; }; +export type GenerateSentryTokenResult = { + __typename?: 'GenerateSentryTokenResult'; + installationId: Scalars['ID']['output']; + orgSlug: Scalars['String']['output']; + token: Scalars['String']['output']; +}; + export type GetSignedAssetUploadSpecificationsResult = { __typename?: 'GetSignedAssetUploadSpecificationsResult'; specifications: Array<Scalars['String']['output']>; @@ -5287,6 +5316,13 @@ export type LinkLogRocketOrganizationToExpoAccountInput = { state: Scalars['String']['input']; }; +export type LinkSentryInstallationToExpoAccountInput = { + accountId: Scalars['ID']['input']; + code: Scalars['String']['input']; + installationId: Scalars['ID']['input']; + sentryOrgSlug: Scalars['String']['input']; +}; + export type LinkSharedEnvironmentVariableInput = { appId: Scalars['ID']['input']; environment?: InputMaybe<EnvironmentVariableEnvironment>; @@ -5695,6 +5731,15 @@ export type PaymentDetails = { id: Scalars['ID']['output']; }; +export type PendingSentryInstallation = { + __typename?: 'PendingSentryInstallation'; + account: Account; + createdAt: Scalars['DateTime']['output']; + id: Scalars['ID']['output']; + installationId: Scalars['String']['output']; + orgSlug: Scalars['String']['output']; +}; + export enum Permission { Admin = 'ADMIN', Own = 'OWN', @@ -5797,6 +5842,7 @@ export type RequestsFilters = { method?: InputMaybe<Array<RequestMethod>>; os?: InputMaybe<Array<UserAgentOs>>; pathname?: InputMaybe<Scalars['String']['input']>; + platform?: InputMaybe<Array<UserAgentPlatform>>; requestId?: InputMaybe<Array<Scalars['WorkerDeploymentRequestID']['input']>>; responseType?: InputMaybe<Array<ResponseType>>; status?: InputMaybe<Array<Scalars['Int']['input']>>; @@ -5945,7 +5991,7 @@ export type RootMutation = { androidAppBuildCredentials: AndroidAppBuildCredentialsMutation; /** Mutations that modify the credentials for an Android app */ androidAppCredentials: AndroidAppCredentialsMutation; - /** Mutations that modify an FCM credential */ + /** Mutations that modify an FCM V0/Legacy credential */ androidFcm: AndroidFcmMutation; /** Mutations that modify a Keystore */ androidKeystore: AndroidKeystoreMutation; @@ -6021,6 +6067,10 @@ export type RootMutation = { notificationSubscription: NotificationSubscriptionMutation; /** Mutations that create, update, and delete Robots */ robot: RobotMutation; + /** Mutations for Sentry installations */ + sentryInstallation: SentryInstallationMutation; + /** Mutations for Sentry projects */ + sentryProject: SentryProjectMutation; /** Mutations that modify an EAS Submit submission */ submission: SubmissionMutation; update: UpdateMutation; @@ -6211,6 +6261,7 @@ export type Runtime = { fingerprint?: Maybe<Fingerprint>; firstBuildCreatedAt?: Maybe<Scalars['DateTime']['output']>; id: Scalars['ID']['output']; + isFingerprint: Scalars['Boolean']['output']; updatedAt: Scalars['DateTime']['output']; updates: AppUpdatesConnection; version: Scalars['String']['output']; @@ -6264,6 +6315,7 @@ export type RuntimeEdge = { export type RuntimeFilterInput = { /** Only return runtimes shared with this branch */ branchId?: InputMaybe<Scalars['String']['input']>; + runtimeVersions?: InputMaybe<Array<Scalars['String']['input']>>; }; export type RuntimeQuery = { @@ -6423,6 +6475,69 @@ export type SecondFactorRegenerateBackupCodesResult = { plaintextBackupCodes: Array<Scalars['String']['output']>; }; +export type SentryInstallation = { + __typename?: 'SentryInstallation'; + account: Account; + createdAt: Scalars['DateTime']['output']; + id: Scalars['ID']['output']; + installationId: Scalars['String']['output']; + orgSlug: Scalars['String']['output']; +}; + +export type SentryInstallationMutation = { + __typename?: 'SentryInstallationMutation'; + /** Confirm a pending Sentry installation */ + confirmPendingSentryInstallation: SentryInstallation; + /** Generate a Sentry token for an installation */ + generateSentryToken: GenerateSentryTokenResult; + /** Link a Sentry installation to an Expo account */ + linkSentryInstallationToExpoAccount: PendingSentryInstallation; +}; + + +export type SentryInstallationMutationConfirmPendingSentryInstallationArgs = { + installationId: Scalars['ID']['input']; +}; + + +export type SentryInstallationMutationGenerateSentryTokenArgs = { + accountId: Scalars['ID']['input']; +}; + + +export type SentryInstallationMutationLinkSentryInstallationToExpoAccountArgs = { + input: LinkSentryInstallationToExpoAccountInput; +}; + +export type SentryProject = { + __typename?: 'SentryProject'; + app: App; + createdAt: Scalars['DateTime']['output']; + id: Scalars['ID']['output']; + sentryInstallationId: Scalars['ID']['output']; + sentryProjectId: Scalars['String']['output']; + sentryProjectSlug: Scalars['String']['output']; + updatedAt: Scalars['DateTime']['output']; +}; + +export type SentryProjectMutation = { + __typename?: 'SentryProjectMutation'; + /** Create a Sentry project */ + createSentryProject: SentryProject; + /** Delete a Sentry project by ID */ + deleteSentryProject: DeleteSentryProjectResult; +}; + + +export type SentryProjectMutationCreateSentryProjectArgs = { + input: CreateSentryProjectInput; +}; + + +export type SentryProjectMutationDeleteSentryProjectArgs = { + sentryProjectId: Scalars['ID']['input']; +}; + export type Snack = Project & { __typename?: 'Snack'; /** Description of the Snack */ @@ -7162,6 +7277,7 @@ export enum UploadSessionType { EasBuildGcsProjectSources = 'EAS_BUILD_GCS_PROJECT_SOURCES', /** @deprecated Use EAS_BUILD_GCS_PROJECT_SOURCES instead. */ EasBuildProjectSources = 'EAS_BUILD_PROJECT_SOURCES', + EasShareGcsAppArchive = 'EAS_SHARE_GCS_APP_ARCHIVE', /** @deprecated Use EAS_SUBMIT_GCS_APP_ARCHIVE instead. */ EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE', EasSubmitGcsAppArchive = 'EAS_SUBMIT_GCS_APP_ARCHIVE', @@ -7492,6 +7608,13 @@ export enum UserAgentOs { Windows = 'WINDOWS' } +export enum UserAgentPlatform { + Android = 'ANDROID', + Apple = 'APPLE', + Unknown = 'UNKNOWN', + Web = 'WEB' +} + export type UserAppPinMutation = { __typename?: 'UserAppPinMutation'; pinApp: Scalars['ID']['output']; @@ -8108,6 +8231,7 @@ export type WorkerDeploymentRequestNode = { method: Scalars['String']['output']; os?: Maybe<UserAgentOs>; pathname: Scalars['String']['output']; + platform: UserAgentPlatform; region?: Maybe<Scalars['String']['output']>; requestId: Scalars['WorkerDeploymentRequestID']['output']; requestTimestamp: Scalars['DateTime']['output']; @@ -8126,6 +8250,8 @@ export type WorkerDeploymentRequests = { byCountry: Array<WorkerDeploymentRequestsCountryEdge>; byMethod: Array<WorkerDeploymentRequestsMethodEdge>; byOS: Array<WorkerDeploymentRequestsOperatingSystemEdge>; + byPathname: Array<WorkerDeploymentRequestsPathnameEdge>; + byPlatform: Array<WorkerDeploymentRequestsPlatformEdge>; byResponseType: Array<WorkerDeploymentRequestsResponseTypeEdge>; byStatusType: Array<WorkerDeploymentRequestsStatusTypeEdge>; interval: Scalars['Int']['output']; @@ -8172,6 +8298,18 @@ export type WorkerDeploymentRequestsByOsArgs = { }; +export type WorkerDeploymentRequestsByPathnameArgs = { + limit?: InputMaybe<Scalars['Int']['input']>; + orderBy?: InputMaybe<RequestsOrderBy>; +}; + + +export type WorkerDeploymentRequestsByPlatformArgs = { + limit?: InputMaybe<Scalars['Int']['input']>; + orderBy?: InputMaybe<RequestsOrderBy>; +}; + + export type WorkerDeploymentRequestsByResponseTypeArgs = { limit?: InputMaybe<Scalars['Int']['input']>; orderBy?: InputMaybe<RequestsOrderBy>; @@ -8262,6 +8400,18 @@ export type WorkerDeploymentRequestsOperatingSystemEdge = { os?: Maybe<UserAgentOs>; }; +export type WorkerDeploymentRequestsPathnameEdge = { + __typename?: 'WorkerDeploymentRequestsPathnameEdge'; + node: WorkerDeploymentRequestsAggregationNode; + pathname: Scalars['String']['output']; +}; + +export type WorkerDeploymentRequestsPlatformEdge = { + __typename?: 'WorkerDeploymentRequestsPlatformEdge'; + node: WorkerDeploymentRequestsAggregationNode; + platform: UserAgentPlatform; +}; + export type WorkerDeploymentRequestsResponseTypeEdge = { __typename?: 'WorkerDeploymentRequestsResponseTypeEdge'; node: WorkerDeploymentRequestsAggregationNode; @@ -8282,6 +8432,8 @@ export type WorkerDeploymentRequestsTimeseriesEdge = { byCountry: Array<WorkerDeploymentRequestsCountryEdge>; byMethod: Array<WorkerDeploymentRequestsMethodEdge>; byOS: Array<WorkerDeploymentRequestsOperatingSystemEdge>; + byPathname: Array<WorkerDeploymentRequestsPathnameEdge>; + byPlatform: Array<WorkerDeploymentRequestsPlatformEdge>; byResponseType: Array<WorkerDeploymentRequestsResponseTypeEdge>; byStatusType: Array<WorkerDeploymentRequestsStatusTypeEdge>; node?: Maybe<WorkerDeploymentRequestsAggregationNode>; @@ -8405,6 +8557,7 @@ export enum WorkflowJobType { Build = 'BUILD', Custom = 'CUSTOM', Deploy = 'DEPLOY', + Fingerprint = 'FINGERPRINT', GetBuild = 'GET_BUILD', MaestroTest = 'MAESTRO_TEST', RequireApproval = 'REQUIRE_APPROVAL', @@ -8503,6 +8656,7 @@ export type WorkflowRun = ActivityTimelineProjectActivity & { sourceExpiresAt?: Maybe<Scalars['DateTime']['output']>; status: WorkflowRunStatus; triggerEventType: WorkflowRunTriggerEventType; + triggeringLabelName?: Maybe<Scalars['String']['output']>; updatedAt: Scalars['DateTime']['output']; workflow: Workflow; workflowRevision?: Maybe<WorkflowRevision>; @@ -8570,6 +8724,7 @@ export enum WorkflowRunStatus { } export enum WorkflowRunTriggerEventType { + GithubPullRequestLabeled = 'GITHUB_PULL_REQUEST_LABELED', GithubPullRequestOpened = 'GITHUB_PULL_REQUEST_OPENED', GithubPullRequestReopened = 'GITHUB_PULL_REQUEST_REOPENED', GithubPullRequestSynchronize = 'GITHUB_PULL_REQUEST_SYNCHRONIZE', From 6c97875e9f4b6fbf29e2726df4c54931fdb4921a Mon Sep 17 00:00:00 2001 From: Gabriel Donadel <donadeldev@gmail.com> Date: Wed, 12 Mar 2025 15:22:19 -0400 Subject: [PATCH 3/8] Add share build mutation --- packages/eas-cli/graphql.schema.json | 210 ++++++++++++++++++ packages/eas-cli/src/commands/share.ts | 17 +- packages/eas-cli/src/graphql/generated.ts | 38 ++++ .../graphql/mutations/ShareBuildMutation.ts | 49 ++-- 4 files changed, 287 insertions(+), 27 deletions(-) diff --git a/packages/eas-cli/graphql.schema.json b/packages/eas-cli/graphql.schema.json index e3af8f6664..2ad781f7c7 100644 --- a/packages/eas-cli/graphql.schema.json +++ b/packages/eas-cli/graphql.schema.json @@ -20890,6 +20890,12 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "LOCAL", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "REPACK", "description": null, @@ -21105,6 +21111,83 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "createShareBuild", + "description": "Create an local build", + "args": [ + { + "name": "appId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "artifactSource", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ShareArchiveSourceInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "job", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ShareJobInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "metadata", + "description": null, + "type": { + "kind": "INPUT_OBJECT", + "name": "BuildMetadataInput", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CreateBuildResult", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "deleteBuild", "description": "Delete an EAS Build build", @@ -45629,6 +45712,133 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "ShareArchiveSourceInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "bucketKey", + "description": null, + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "type", + "description": null, + "type": { + "kind": "ENUM", + "name": "ShareArchiveSourceType", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "ShareArchiveSourceType", + "description": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "GCS", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "ShareJobInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "developmentClient", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "experimental", + "description": null, + "type": { + "kind": "SCALAR", + "name": "JSONObject", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "platform", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppPlatform", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "simulator", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "triggeredBy", + "description": null, + "type": { + "kind": "ENUM", + "name": "BuildTrigger", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "Snack", diff --git a/packages/eas-cli/src/commands/share.ts b/packages/eas-cli/src/commands/share.ts index f3475bcd08..4f1d2dea47 100644 --- a/packages/eas-cli/src/commands/share.ts +++ b/packages/eas-cli/src/commands/share.ts @@ -4,11 +4,13 @@ import fg from 'fast-glob'; import fs from 'fs-extra'; import path from 'path'; +import { getBuildLogsUrl } from '../build/utils/url'; import EasCommand from '../commandUtils/EasCommand'; import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient'; import { EASNonInteractiveFlag } from '../commandUtils/flags'; -import { UploadSessionType } from '../graphql/generated'; +import { DistributionType, ShareArchiveSourceType, UploadSessionType } from '../graphql/generated'; import { ShareBuildMutation } from '../graphql/mutations/ShareBuildMutation'; +import { toAppPlatform } from '../graphql/types/AppPlatform'; import Log from '../log'; import { promptAsync } from '../prompts'; import { uploadFileAtPathToGCSAsync } from '../uploads'; @@ -52,18 +54,15 @@ export default class BuildUpload extends EasCommand { Log.log('Uploading your app archive to EAS Share'); const bucketKey = await uploadAppArchiveAsync(graphqlClient, localBuildPath); - // @TODO: use Share build mutation - const { debugInfoUrl } = await ShareBuildMutation.uploadLocalBuildAsync( + const build = await ShareBuildMutation.uploadLocalBuildAsync( graphqlClient, projectId, - { bucketKey }, - { - // @TODO: read the fingerprint from the local build - hash: '', - } + { platform: toAppPlatform(platform), simulator: platform === Platform.IOS }, + { type: ShareArchiveSourceType.Gcs, bucketKey }, + { distribution: DistributionType.Internal } ); - Log.withTick(`Here is a sharable link of your build: ${debugInfoUrl}`); + Log.withTick(`Here is a sharable link of your build: ${getBuildLogsUrl(build)}`); } private async selectPlatformAsync(platform?: Platform): Promise<Platform> { diff --git a/packages/eas-cli/src/graphql/generated.ts b/packages/eas-cli/src/graphql/generated.ts index 127ec1f441..8c5f5a0cda 100644 --- a/packages/eas-cli/src/graphql/generated.ts +++ b/packages/eas-cli/src/graphql/generated.ts @@ -3029,6 +3029,7 @@ export type BuildMetrics = { export enum BuildMode { Build = 'BUILD', Custom = 'CUSTOM', + Local = 'LOCAL', Repack = 'REPACK', Resign = 'RESIGN' } @@ -3046,6 +3047,8 @@ export type BuildMutation = { createAndroidBuild: CreateBuildResult; /** Create an iOS build */ createIosBuild: CreateBuildResult; + /** Create an local build */ + createShareBuild: CreateBuildResult; /** Delete an EAS Build build */ deleteBuild: Build; /** Retry an Android EAS Build */ @@ -3083,6 +3086,14 @@ export type BuildMutationCreateIosBuildArgs = { }; +export type BuildMutationCreateShareBuildArgs = { + appId: Scalars['ID']['input']; + artifactSource: ShareArchiveSourceInput; + job: ShareJobInput; + metadata?: InputMaybe<BuildMetadataInput>; +}; + + export type BuildMutationDeleteBuildArgs = { buildId: Scalars['ID']['input']; }; @@ -6538,6 +6549,23 @@ export type SentryProjectMutationDeleteSentryProjectArgs = { sentryProjectId: Scalars['ID']['input']; }; +export type ShareArchiveSourceInput = { + bucketKey?: InputMaybe<Scalars['String']['input']>; + type?: InputMaybe<ShareArchiveSourceType>; +}; + +export enum ShareArchiveSourceType { + Gcs = 'GCS' +} + +export type ShareJobInput = { + developmentClient?: InputMaybe<Scalars['Boolean']['input']>; + experimental?: InputMaybe<Scalars['JSONObject']['input']>; + platform: AppPlatform; + simulator?: InputMaybe<Scalars['Boolean']['input']>; + triggeredBy?: InputMaybe<BuildTrigger>; +}; + export type Snack = Project & { __typename?: 'Snack'; /** Description of the Snack */ @@ -9437,6 +9465,16 @@ export type SetRolloutPercentageMutationVariables = Exact<{ export type SetRolloutPercentageMutation = { __typename?: 'RootMutation', update: { __typename?: 'UpdateMutation', setRolloutPercentage: { __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', username: string, id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null } } }; +export type UploadLocalBuildMutationVariables = Exact<{ + appId: Scalars['ID']['input']; + jobInput: ShareJobInput; + artifactSource: ShareArchiveSourceInput; + metadata?: InputMaybe<BuildMetadataInput>; +}>; + + +export type UploadLocalBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createShareBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: 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, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: 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 }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null } } } }; + export type CreateAndroidSubmissionMutationVariables = Exact<{ appId: Scalars['ID']['input']; config: AndroidSubmissionConfigInput; diff --git a/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts b/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts index 2514bf81ab..ce7e3a4789 100644 --- a/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts +++ b/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts @@ -1,42 +1,55 @@ -import { FingerprintSource } from '@expo/eas-build-job'; import { print } from 'graphql'; import gql from 'graphql-tag'; import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient'; import { withErrorHandlingAsync } from '../client'; -import { CreateFingeprintMutation, FingerprintFragment } from '../generated'; -import { FingerprintFragmentNode } from '../types/Fingerprint'; +import { + BuildFragment, + BuildMetadataInput, + ShareArchiveSourceInput, + ShareJobInput, + UploadLocalBuildMutation, +} from '../generated'; +import { BuildFragmentNode } from '../types/Build'; export const ShareBuildMutation = { async uploadLocalBuildAsync( graphqlClient: ExpoGraphqlClient, appId: string, - buildSource: { - bucketKey: string; - }, - fingerprintData: { hash: string; source?: FingerprintSource } - ): Promise<FingerprintFragment> { + job: ShareJobInput, + artifactSource: ShareArchiveSourceInput, + metadata: BuildMetadataInput + ): Promise<BuildFragment> { const data = await withErrorHandlingAsync( graphqlClient - .mutation<CreateFingeprintMutation>( + .mutation<UploadLocalBuildMutation>( gql` - mutation CreateFingeprintMutation( - $fingerprintData: CreateFingerprintInput! + mutation uploadLocalBuildMutation( $appId: ID! + $jobInput: ShareJobInput! + $artifactSource: ShareArchiveSourceInput! + $metadata: BuildMetadataInput ) { - fingerprint { - createOrGetExistingFingerprint(fingerprintData: $fingerprintData, appId: $appId) { - id - ...FingerprintFragment + build { + createShareBuild( + appId: $appId + job: $jobInput + artifactSource: $artifactSource + metadata: $metadata + ) { + build { + id + ...BuildFragment + } } } } - ${print(FingerprintFragmentNode)} + ${print(BuildFragmentNode)} `, - { appId, fingerprintData } + { appId, jobInput: job, artifactSource, metadata } ) .toPromise() ); - return data.fingerprint.createOrGetExistingFingerprint; + return data.build.createShareBuild.build; }, }; From f1ad7427abcec96198108bf24084065c83b5cc81 Mon Sep 17 00:00:00 2001 From: Gabriel Donadel <donadeldev@gmail.com> Date: Tue, 18 Mar 2025 01:53:01 -0300 Subject: [PATCH 4/8] Fix app file extension not persisting --- packages/eas-cli/src/commands/share.ts | 66 ++++++++++++------- packages/eas-cli/src/graphql/generated.ts | 4 ++ .../mutations/UploadSessionMutation.ts | 8 ++- packages/eas-cli/src/uploads.ts | 6 +- 4 files changed, 56 insertions(+), 28 deletions(-) diff --git a/packages/eas-cli/src/commands/share.ts b/packages/eas-cli/src/commands/share.ts index 4f1d2dea47..b9555cac95 100644 --- a/packages/eas-cli/src/commands/share.ts +++ b/packages/eas-cli/src/commands/share.ts @@ -46,12 +46,9 @@ export default class BuildUpload extends EasCommand { }); const platform = await this.selectPlatformAsync(flags.platform); - const localBuildPath = await resolveLocalBuildPathAsync({ - platform, - buildPath, - }); + const localBuildPath = await resolveLocalBuildPathAsync(platform, buildPath); - Log.log('Uploading your app archive to EAS Share'); + Log.log('Uploading your app archive to EAS'); const bucketKey = await uploadAppArchiveAsync(graphqlClient, localBuildPath); const build = await ShareBuildMutation.uploadLocalBuildAsync( @@ -82,30 +79,54 @@ export default class BuildUpload extends EasCommand { } } -async function resolveLocalBuildPathAsync({ - platform, - buildPath, -}: { - platform: Platform; - buildPath?: string; -}): Promise<string> { +async function resolveLocalBuildPathAsync( + platform: Platform, + inputBuildPath?: string +): Promise<string> { const applicationArchivePatternOrPath = - buildPath ?? platform === Platform.ANDROID + inputBuildPath ?? platform === Platform.ANDROID ? 'android/app/build/outputs/**/*.{apk,aab}' : 'ios/build/Build/Products/*simulator/*.app'; - const applicationArchives = await findArtifactsAsync({ + let applicationArchives = await findArtifactsAsync({ rootDir: process.cwd(), patternOrPath: applicationArchivePatternOrPath, }); - const count = applicationArchives.length; - Log.log( - `Found ${count} application archive${count > 1 ? 's' : ''}:\n- ${applicationArchives.join( - '\n- ' - )}` - ); - return applicationArchives[0]; + if (applicationArchives.length === 0 && !inputBuildPath) { + Log.warn(`No application archives found at ${applicationArchivePatternOrPath}.`); + const { path } = await promptAsync({ + type: 'text', + name: 'path', + message: 'Provide a path to the application archive:', + validate: value => (value ? true : 'Path may not be empty.'), + }); + applicationArchives = await findArtifactsAsync({ + rootDir: process.cwd(), + patternOrPath: path, + }); + } + + if (applicationArchives.length === 1) { + return applicationArchives[0]; + } + + if (applicationArchives.length > 1) { + const { path } = await promptAsync({ + type: 'select', + name: 'path', + message: 'Found multiple application archives. Select one:', + choices: applicationArchives.map(archivePath => { + return { + title: archivePath, + value: archivePath, + }; + }), + }); + return path; + } + + throw new Error(`Found no application archives at ${inputBuildPath}.`); } async function findArtifactsAsync({ @@ -120,9 +141,6 @@ async function findArtifactsAsync({ ? [patternOrPath] : [] : await fg(patternOrPath, { cwd: rootDir, onlyFiles: false }); - if (files.length === 0) { - throw new Error(`Found no application archives for "${patternOrPath}".`); - } return files.map(filePath => { // User may provide an absolute path as input in which case diff --git a/packages/eas-cli/src/graphql/generated.ts b/packages/eas-cli/src/graphql/generated.ts index 8c5f5a0cda..e2356e12d2 100644 --- a/packages/eas-cli/src/graphql/generated.ts +++ b/packages/eas-cli/src/graphql/generated.ts @@ -7297,6 +7297,7 @@ export type UploadSessionCreateAppScopedUploadSessionArgs = { export type UploadSessionCreateUploadSessionArgs = { + filename?: InputMaybe<Scalars['String']['input']>; type: UploadSessionType; }; @@ -7670,7 +7671,9 @@ export type UserAuditLog = { targetEntityMutationType: TargetEntityMutationType; targetEntityTypeName: UserEntityTypeName; targetEntityTypePublicName: Scalars['String']['output']; + /** @deprecated Use userActor instead */ user: User; + userActor: UserActor; websiteMessage: Scalars['String']['output']; }; @@ -9497,6 +9500,7 @@ export type CreateIosSubmissionMutation = { __typename?: 'RootMutation', submiss export type CreateUploadSessionMutationVariables = Exact<{ type: UploadSessionType; + filename?: InputMaybe<Scalars['String']['input']>; }>; diff --git a/packages/eas-cli/src/graphql/mutations/UploadSessionMutation.ts b/packages/eas-cli/src/graphql/mutations/UploadSessionMutation.ts index f5703e3f24..9aa8188019 100644 --- a/packages/eas-cli/src/graphql/mutations/UploadSessionMutation.ts +++ b/packages/eas-cli/src/graphql/mutations/UploadSessionMutation.ts @@ -20,20 +20,22 @@ export interface SignedUrl { export const UploadSessionMutation = { async createUploadSessionAsync( graphqlClient: ExpoGraphqlClient, - type: UploadSessionType + type: UploadSessionType, + filename?: string ): Promise<SignedUrl> { const data = await withErrorHandlingAsync( graphqlClient .mutation<CreateUploadSessionMutation, CreateUploadSessionMutationVariables>( gql` - mutation CreateUploadSessionMutation($type: UploadSessionType!) { + mutation CreateUploadSessionMutation($type: UploadSessionType!, $filename: String) { uploadSession { - createUploadSession(type: $type) + createUploadSession(type: $type, filename: $filename) } } `, { type, + filename, } ) .toPromise() diff --git a/packages/eas-cli/src/uploads.ts b/packages/eas-cli/src/uploads.ts index e4427b696e..8c68eac6d9 100644 --- a/packages/eas-cli/src/uploads.ts +++ b/packages/eas-cli/src/uploads.ts @@ -20,7 +20,11 @@ export async function uploadFileAtPathToGCSAsync( path: string, handleProgressEvent: ProgressHandler = () => {} ): Promise<string> { - const signedUrl = await UploadSessionMutation.createUploadSessionAsync(graphqlClient, type); + const signedUrl = await UploadSessionMutation.createUploadSessionAsync( + graphqlClient, + type, + type === UploadSessionType.EasShareGcsAppArchive ? path : undefined + ); await uploadWithSignedUrlWithProgressAsync(path, signedUrl, handleProgressEvent); return signedUrl.bucketKey; From 0fc5321e2b2ad0109c7a4b6a260292158a126b89 Mon Sep 17 00:00:00 2001 From: Gabriel Donadel <donadeldev@gmail.com> Date: Thu, 20 Mar 2025 11:38:03 -0300 Subject: [PATCH 5/8] Add function to extract App Metadata --- packages/eas-cli/graphql.schema.json | 28 +++++++++ packages/eas-cli/package.json | 1 + packages/eas-cli/src/commands/share.ts | 82 ++++++++++++++++++++++++-- 3 files changed, 107 insertions(+), 4 deletions(-) diff --git a/packages/eas-cli/graphql.schema.json b/packages/eas-cli/graphql.schema.json index 2ad781f7c7..94d438aacc 100644 --- a/packages/eas-cli/graphql.schema.json +++ b/packages/eas-cli/graphql.schema.json @@ -50956,6 +50956,18 @@ "name": "createUploadSession", "description": "Create an Upload Session", "args": [ + { + "name": "filename", + "description": null, + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "type", "description": null, @@ -53575,6 +53587,22 @@ "ofType": null } }, + "isDeprecated": true, + "deprecationReason": "Use userActor instead" + }, + { + "name": "userActor", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserActor", + "ofType": null + } + }, "isDeprecated": false, "deprecationReason": null }, diff --git a/packages/eas-cli/package.json b/packages/eas-cli/package.json index b4cbe34f7a..49e531db54 100644 --- a/packages/eas-cli/package.json +++ b/packages/eas-cli/package.json @@ -68,6 +68,7 @@ "nanoid": "3.3.8", "node-fetch": "2.6.7", "node-forge": "1.3.1", + "node-stream-zip": "1.15.0", "nullthrows": "1.1.1", "ora": "5.1.0", "pkg-dir": "4.2.0", diff --git a/packages/eas-cli/src/commands/share.ts b/packages/eas-cli/src/commands/share.ts index b9555cac95..29fb3f7aab 100644 --- a/packages/eas-cli/src/commands/share.ts +++ b/packages/eas-cli/src/commands/share.ts @@ -2,6 +2,7 @@ import { Platform } from '@expo/eas-build-job'; import { Flags } from '@oclif/core'; import fg from 'fast-glob'; import fs from 'fs-extra'; +import StreamZip from 'node-stream-zip'; import path from 'path'; import { getBuildLogsUrl } from '../build/utils/url'; @@ -9,6 +10,7 @@ import EasCommand from '../commandUtils/EasCommand'; import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient'; import { EASNonInteractiveFlag } from '../commandUtils/flags'; import { DistributionType, ShareArchiveSourceType, UploadSessionType } from '../graphql/generated'; +import { FingerprintMutation } from '../graphql/mutations/FingerprintMutation'; import { ShareBuildMutation } from '../graphql/mutations/ShareBuildMutation'; import { toAppPlatform } from '../graphql/types/AppPlatform'; import Log from '../log'; @@ -48,15 +50,25 @@ export default class BuildUpload extends EasCommand { const platform = await this.selectPlatformAsync(flags.platform); const localBuildPath = await resolveLocalBuildPathAsync(platform, buildPath); + const { fingerprintHash, developmentClient, simulator } = await extractAppMetadataAsync( + localBuildPath, + platform + ); + if (fingerprintHash) { + await FingerprintMutation.createFingerprintAsync(graphqlClient, projectId, { + hash: fingerprintHash, + }); + } + Log.log('Uploading your app archive to EAS'); const bucketKey = await uploadAppArchiveAsync(graphqlClient, localBuildPath); const build = await ShareBuildMutation.uploadLocalBuildAsync( graphqlClient, projectId, - { platform: toAppPlatform(platform), simulator: platform === Platform.IOS }, + { platform: toAppPlatform(platform), simulator }, { type: ShareArchiveSourceType.Gcs, bucketKey }, - { distribution: DistributionType.Internal } + { distribution: DistributionType.Internal, fingerprintHash, developmentClient } ); Log.withTick(`Here is a sharable link of your build: ${getBuildLogsUrl(build)}`); @@ -84,9 +96,10 @@ async function resolveLocalBuildPathAsync( inputBuildPath?: string ): Promise<string> { const applicationArchivePatternOrPath = - inputBuildPath ?? platform === Platform.ANDROID + inputBuildPath ?? + (platform === Platform.ANDROID ? 'android/app/build/outputs/**/*.{apk,aab}' - : 'ios/build/Build/Products/*simulator/*.app'; + : 'ios/build/Build/Products/*simulator/*.app'); let applicationArchives = await findArtifactsAsync({ rootDir: process.cwd(), @@ -172,3 +185,64 @@ async function uploadAppArchiveAsync( ); return bucketKey; } + +type AppMetadata = { + fingerprintHash?: string; + developmentClient: boolean; + simulator: boolean; +}; + +async function extractAppMetadataAsync( + buildPath: string, + platform: Platform +): Promise<AppMetadata> { + let developmentClient = false; + let fingerprintHash: string | undefined; + let simulator = platform === Platform.IOS; + + let basePath = platform === Platform.ANDROID ? 'assets/' : buildPath; + const fingerprintFilePath = + platform === Platform.ANDROID ? 'fingerprint' : 'EXUpdates.bundle/fingerprint'; + const devMenuBundlePath = + platform === Platform.ANDROID ? 'EXDevMenuApp.android.js' : 'EXDevMenu.bundle'; + + const buildExtension = path.extname(buildPath); + // check extension if, .apk, .ipa [] = .aab + if (['.apk', '.ipa'].includes(buildExtension)) { + const zip = new StreamZip.async({ file: buildPath }); + try { + if (buildExtension === '.ipa') { + const entries = await zip.entries(); + basePath = + Object.keys(entries).find( + entry => entry.startsWith('Payload/') && entry.endsWith('.app/') + ) ?? basePath; + } + + developmentClient = Boolean(await zip.entry(path.join(basePath, devMenuBundlePath))); + if (await zip.entry(path.join(basePath, fingerprintFilePath))) { + fingerprintHash = (await zip.entryData(path.join(basePath, fingerprintFilePath))).toString( + 'utf-8' + ); + } + } catch (err) { + Log.error(`Error reading ${buildExtension}: ${err}`); + } finally { + await zip.close(); + } + } else if (buildExtension === '.app') { + developmentClient = await fs.exists(path.join(basePath, devMenuBundlePath)); + + if (await fs.exists(path.join(basePath, fingerprintFilePath))) { + fingerprintHash = await fs.readFile(path.join(basePath, fingerprintFilePath), 'utf8'); + } + } else { + //TODO: extract from .tar + } + + return { + developmentClient, + fingerprintHash, + simulator, + }; +} From f0ec5f5b562c1c61bc06ff9f4d810843d9d0e317 Mon Sep 17 00:00:00 2001 From: Gabriel Donadel <donadeldev@gmail.com> Date: Thu, 20 Mar 2025 20:19:59 -0300 Subject: [PATCH 6/8] Add tar support --- packages/eas-cli/src/commands/share.ts | 40 ++++++++++++++++++++++---- yarn.lock | 5 ++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/eas-cli/src/commands/share.ts b/packages/eas-cli/src/commands/share.ts index 29fb3f7aab..4dc6836ee1 100644 --- a/packages/eas-cli/src/commands/share.ts +++ b/packages/eas-cli/src/commands/share.ts @@ -4,6 +4,7 @@ import fg from 'fast-glob'; import fs from 'fs-extra'; import StreamZip from 'node-stream-zip'; import path from 'path'; +import tar from 'tar'; import { getBuildLogsUrl } from '../build/utils/url'; import EasCommand from '../commandUtils/EasCommand'; @@ -198,17 +199,16 @@ async function extractAppMetadataAsync( ): Promise<AppMetadata> { let developmentClient = false; let fingerprintHash: string | undefined; - let simulator = platform === Platform.IOS; + const simulator = platform === Platform.IOS; let basePath = platform === Platform.ANDROID ? 'assets/' : buildPath; const fingerprintFilePath = platform === Platform.ANDROID ? 'fingerprint' : 'EXUpdates.bundle/fingerprint'; const devMenuBundlePath = - platform === Platform.ANDROID ? 'EXDevMenuApp.android.js' : 'EXDevMenu.bundle'; + platform === Platform.ANDROID ? 'EXDevMenuApp.android.js' : 'EXDevMenu.bundle/'; const buildExtension = path.extname(buildPath); - // check extension if, .apk, .ipa [] = .aab - if (['.apk', '.ipa'].includes(buildExtension)) { + if (['.apk', '.ipa', '.aab'].includes(buildExtension)) { const zip = new StreamZip.async({ file: buildPath }); try { if (buildExtension === '.ipa') { @@ -237,7 +237,37 @@ async function extractAppMetadataAsync( fingerprintHash = await fs.readFile(path.join(basePath, fingerprintFilePath), 'utf8'); } } else { - //TODO: extract from .tar + // Use tar to list files in the archive + try { + let fingerprintHashPromise: Promise<string> | undefined; + await tar.list({ + file: buildPath, + // eslint-disable-next-line async-protect/async-suffix + onentry: entry => { + if (entry.path.endsWith(devMenuBundlePath)) { + developmentClient = true; + } + if (entry.path.endsWith(fingerprintFilePath)) { + fingerprintHashPromise = new Promise<string>(async (resolve, reject) => { + try { + let content = ''; + for await (const chunk of entry) { + content += chunk.toString('utf8'); + } + resolve(content); + } catch (error) { + reject(error); + } + }); + } + }, + }); + if (fingerprintHashPromise !== undefined) { + fingerprintHash = await fingerprintHashPromise; + } + } catch (err) { + Log.error(`Error reading ${buildExtension}: ${err}`); + } } return { diff --git a/yarn.lock b/yarn.lock index 74ec939181..8d0374e1df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10960,6 +10960,11 @@ node-rsa@^1.1.1: dependencies: asn1 "^0.2.4" +node-stream-zip@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" + integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== + nopt@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" From 1089c53987edded5495c997ace28224dc3da8b60 Mon Sep 17 00:00:00 2001 From: Gabriel Donadel <donadeldev@gmail.com> Date: Mon, 24 Mar 2025 14:35:29 -0300 Subject: [PATCH 7/8] Use shareLocalBuild --- packages/eas-cli/graphql.schema.json | 182 ++++++++++-------- packages/eas-cli/src/graphql/generated.ts | 32 +-- .../graphql/mutations/ShareBuildMutation.ts | 4 +- 3 files changed, 123 insertions(+), 95 deletions(-) diff --git a/packages/eas-cli/graphql.schema.json b/packages/eas-cli/graphql.schema.json index 94d438aacc..7b8a429339 100644 --- a/packages/eas-cli/graphql.schema.json +++ b/packages/eas-cli/graphql.schema.json @@ -9599,6 +9599,18 @@ "isDeprecated": true, "deprecationReason": "No longer supported" }, + { + "name": "profileImageUrl", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "published", "description": "Whether there have been any classic update publishes", @@ -21112,11 +21124,11 @@ "deprecationReason": null }, { - "name": "createShareBuild", - "description": "Create an local build", + "name": "deleteBuild", + "description": "Delete an EAS Build build", "args": [ { - "name": "appId", + "name": "buildId", "description": null, "type": { "kind": "NON_NULL", @@ -21130,32 +21142,33 @@ "defaultValue": null, "isDeprecated": false, "deprecationReason": null - }, - { - "name": "artifactSource", - "description": null, - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "ShareArchiveSourceInput", - "ofType": null - } - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - }, + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Build", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "retryAndroidBuild", + "description": "Retry an Android EAS Build", + "args": [ { - "name": "job", + "name": "buildId", "description": null, "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "INPUT_OBJECT", - "name": "ShareJobInput", + "kind": "SCALAR", + "name": "ID", "ofType": null } }, @@ -21164,11 +21177,11 @@ "deprecationReason": null }, { - "name": "metadata", + "name": "jobOverrides", "description": null, "type": { "kind": "INPUT_OBJECT", - "name": "BuildMetadataInput", + "name": "AndroidJobOverridesInput", "ofType": null }, "defaultValue": null, @@ -21181,7 +21194,7 @@ "name": null, "ofType": { "kind": "OBJECT", - "name": "CreateBuildResult", + "name": "Build", "ofType": null } }, @@ -21189,8 +21202,8 @@ "deprecationReason": null }, { - "name": "deleteBuild", - "description": "Delete an EAS Build build", + "name": "retryBuild", + "description": "Retry an EAS Build build", "args": [ { "name": "buildId", @@ -21218,12 +21231,12 @@ "ofType": null } }, - "isDeprecated": false, - "deprecationReason": null + "isDeprecated": true, + "deprecationReason": "Use retryAndroidBuild and retryIosBuild instead" }, { - "name": "retryAndroidBuild", - "description": "Retry an Android EAS Build", + "name": "retryIosBuild", + "description": "Retry an iOS EAS Build", "args": [ { "name": "buildId", @@ -21246,7 +21259,7 @@ "description": null, "type": { "kind": "INPUT_OBJECT", - "name": "AndroidJobOverridesInput", + "name": "IosJobOverridesInput", "ofType": null }, "defaultValue": null, @@ -21267,11 +21280,11 @@ "deprecationReason": null }, { - "name": "retryBuild", - "description": "Retry an EAS Build build", + "name": "shareLocalBuild", + "description": "Share a local build", "args": [ { - "name": "buildId", + "name": "appId", "description": null, "type": { "kind": "NON_NULL", @@ -21285,33 +21298,16 @@ "defaultValue": null, "isDeprecated": false, "deprecationReason": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Build", - "ofType": null - } - }, - "isDeprecated": true, - "deprecationReason": "Use retryAndroidBuild and retryIosBuild instead" - }, - { - "name": "retryIosBuild", - "description": "Retry an iOS EAS Build", - "args": [ + }, { - "name": "buildId", + "name": "artifactSource", "description": null, "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "ID", + "kind": "INPUT_OBJECT", + "name": "ShareArchiveSourceInput", "ofType": null } }, @@ -21320,11 +21316,27 @@ "deprecationReason": null }, { - "name": "jobOverrides", + "name": "job", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ShareJobInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "metadata", "description": null, "type": { "kind": "INPUT_OBJECT", - "name": "IosJobOverridesInput", + "name": "BuildMetadataInput", "ofType": null }, "defaultValue": null, @@ -21337,7 +21349,7 @@ "name": null, "ofType": { "kind": "OBJECT", - "name": "Build", + "name": "CreateBuildResult", "ofType": null } }, @@ -45722,9 +45734,13 @@ "name": "bucketKey", "description": null, "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } }, "defaultValue": null, "isDeprecated": false, @@ -45734,9 +45750,13 @@ "name": "type", "description": null, "type": { - "kind": "ENUM", - "name": "ShareArchiveSourceType", - "ofType": null + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "ShareArchiveSourceType", + "ofType": null + } }, "defaultValue": null, "isDeprecated": false, @@ -45821,18 +45841,6 @@ "defaultValue": null, "isDeprecated": false, "deprecationReason": null - }, - { - "name": "triggeredBy", - "description": null, - "type": { - "kind": "ENUM", - "name": "BuildTrigger", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null } ], "interfaces": null, @@ -61907,6 +61915,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "triggeringSchedule", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "updatedAt", "description": null, @@ -62366,6 +62386,12 @@ "description": null, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "SCHEDULE", + "description": null, + "isDeprecated": false, + "deprecationReason": null } ], "possibleTypes": null diff --git a/packages/eas-cli/src/graphql/generated.ts b/packages/eas-cli/src/graphql/generated.ts index e2356e12d2..7636dc7fb7 100644 --- a/packages/eas-cli/src/graphql/generated.ts +++ b/packages/eas-cli/src/graphql/generated.ts @@ -1323,6 +1323,7 @@ export type App = Project & { privacy: Scalars['String']['output']; /** @deprecated No longer supported */ privacySetting: AppPrivacy; + profileImageUrl?: Maybe<Scalars['String']['output']>; /** * Whether there have been any classic update publishes * @deprecated Classic updates have been deprecated. @@ -3047,8 +3048,6 @@ export type BuildMutation = { createAndroidBuild: CreateBuildResult; /** Create an iOS build */ createIosBuild: CreateBuildResult; - /** Create an local build */ - createShareBuild: CreateBuildResult; /** Delete an EAS Build build */ deleteBuild: Build; /** Retry an Android EAS Build */ @@ -3060,6 +3059,8 @@ export type BuildMutation = { retryBuild: Build; /** Retry an iOS EAS Build */ retryIosBuild: Build; + /** Share a local build */ + shareLocalBuild: CreateBuildResult; /** Update metadata for EAS Build build */ updateBuildMetadata: Build; }; @@ -3086,14 +3087,6 @@ export type BuildMutationCreateIosBuildArgs = { }; -export type BuildMutationCreateShareBuildArgs = { - appId: Scalars['ID']['input']; - artifactSource: ShareArchiveSourceInput; - job: ShareJobInput; - metadata?: InputMaybe<BuildMetadataInput>; -}; - - export type BuildMutationDeleteBuildArgs = { buildId: Scalars['ID']['input']; }; @@ -3116,6 +3109,14 @@ export type BuildMutationRetryIosBuildArgs = { }; +export type BuildMutationShareLocalBuildArgs = { + appId: Scalars['ID']['input']; + artifactSource: ShareArchiveSourceInput; + job: ShareJobInput; + metadata?: InputMaybe<BuildMetadataInput>; +}; + + export type BuildMutationUpdateBuildMetadataArgs = { buildId: Scalars['ID']['input']; metadata: BuildMetadataInput; @@ -6550,8 +6551,8 @@ export type SentryProjectMutationDeleteSentryProjectArgs = { }; export type ShareArchiveSourceInput = { - bucketKey?: InputMaybe<Scalars['String']['input']>; - type?: InputMaybe<ShareArchiveSourceType>; + bucketKey: Scalars['String']['input']; + type: ShareArchiveSourceType; }; export enum ShareArchiveSourceType { @@ -6563,7 +6564,6 @@ export type ShareJobInput = { experimental?: InputMaybe<Scalars['JSONObject']['input']>; platform: AppPlatform; simulator?: InputMaybe<Scalars['Boolean']['input']>; - triggeredBy?: InputMaybe<BuildTrigger>; }; export type Snack = Project & { @@ -8688,6 +8688,7 @@ export type WorkflowRun = ActivityTimelineProjectActivity & { status: WorkflowRunStatus; triggerEventType: WorkflowRunTriggerEventType; triggeringLabelName?: Maybe<Scalars['String']['output']>; + triggeringSchedule?: Maybe<Scalars['String']['output']>; updatedAt: Scalars['DateTime']['output']; workflow: Workflow; workflowRevision?: Maybe<WorkflowRevision>; @@ -8760,7 +8761,8 @@ export enum WorkflowRunTriggerEventType { GithubPullRequestReopened = 'GITHUB_PULL_REQUEST_REOPENED', GithubPullRequestSynchronize = 'GITHUB_PULL_REQUEST_SYNCHRONIZE', GithubPush = 'GITHUB_PUSH', - Manual = 'MANUAL' + Manual = 'MANUAL', + Schedule = 'SCHEDULE' } export type WorkflowRunsConnection = { @@ -9476,7 +9478,7 @@ export type UploadLocalBuildMutationVariables = Exact<{ }>; -export type UploadLocalBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createShareBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: 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, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: 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 }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null } } } }; +export type UploadLocalBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', shareLocalBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: 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, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: 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 }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null } } } }; export type CreateAndroidSubmissionMutationVariables = Exact<{ appId: Scalars['ID']['input']; diff --git a/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts b/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts index ce7e3a4789..e98228ba53 100644 --- a/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts +++ b/packages/eas-cli/src/graphql/mutations/ShareBuildMutation.ts @@ -31,7 +31,7 @@ export const ShareBuildMutation = { $metadata: BuildMetadataInput ) { build { - createShareBuild( + shareLocalBuild( appId: $appId job: $jobInput artifactSource: $artifactSource @@ -50,6 +50,6 @@ export const ShareBuildMutation = { ) .toPromise() ); - return data.build.createShareBuild.build; + return data.build.shareLocalBuild.build; }, }; From 0fb1cce770f8c4de9b7ff462e3f2fba6e8e8eecc Mon Sep 17 00:00:00 2001 From: Gabriel Donadel <donadeldev@gmail.com> Date: Tue, 25 Mar 2025 10:24:07 -0300 Subject: [PATCH 8/8] Add fingerprint flag --- packages/eas-cli/src/commands/share.ts | 36 ++++++++++++++++++++------ 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/eas-cli/src/commands/share.ts b/packages/eas-cli/src/commands/share.ts index 4dc6836ee1..29fb944b49 100644 --- a/packages/eas-cli/src/commands/share.ts +++ b/packages/eas-cli/src/commands/share.ts @@ -30,6 +30,9 @@ export default class BuildUpload extends EasCommand { 'build-path': Flags.string({ description: 'Path for the local build', }), + fingerprint: Flags.string({ + description: 'Fingerprint hash of the local build', + }), ...EASNonInteractiveFlag, }; @@ -40,7 +43,7 @@ export default class BuildUpload extends EasCommand { async runAsync(): Promise<void> { const { flags } = await this.parse(BuildUpload); - const { 'build-path': buildPath } = flags; + const { 'build-path': buildPath, fingerprint: manualFingerprintHash } = flags; const { projectId, loggedIn: { graphqlClient }, @@ -51,13 +54,30 @@ export default class BuildUpload extends EasCommand { const platform = await this.selectPlatformAsync(flags.platform); const localBuildPath = await resolveLocalBuildPathAsync(platform, buildPath); - const { fingerprintHash, developmentClient, simulator } = await extractAppMetadataAsync( - localBuildPath, - platform - ); - if (fingerprintHash) { + const { + fingerprintHash: buildFingerprintHash, + developmentClient, + simulator, + } = await extractAppMetadataAsync(localBuildPath, platform); + + let fingerprint = manualFingerprintHash ?? buildFingerprintHash; + if (fingerprint) { + if ( + manualFingerprintHash && + buildFingerprintHash && + manualFingerprintHash !== buildFingerprintHash + ) { + const selectedAnswer = await promptAsync({ + name: 'fingerprint', + message: `The provided fingerprint hash ${manualFingerprintHash} does not match the fingerprint hash of the build ${buildFingerprintHash}. Which fingerprint do you want to use?`, + type: 'select', + choices: [{ title: manualFingerprintHash }, { title: buildFingerprintHash }], + }); + fingerprint = String(selectedAnswer.fingerprint); + } + await FingerprintMutation.createFingerprintAsync(graphqlClient, projectId, { - hash: fingerprintHash, + hash: fingerprint, }); } @@ -69,7 +89,7 @@ export default class BuildUpload extends EasCommand { projectId, { platform: toAppPlatform(platform), simulator }, { type: ShareArchiveSourceType.Gcs, bucketKey }, - { distribution: DistributionType.Internal, fingerprintHash, developmentClient } + { distribution: DistributionType.Internal, fingerprintHash: fingerprint, developmentClient } ); Log.withTick(`Here is a sharable link of your build: ${getBuildLogsUrl(build)}`);