Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENG-1347] eas submit: suggest using build id instead of build details page #620

Merged
merged 3 commits into from
Sep 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ This is the log of notable changes to EAS CLI and related packages.
### 🎉 New features

- Use envs from build profile to resolve app config when auto-submitting. ([#614](https://github.com/expo/eas-cli/pull/614) by [@dsokal](https://github.com/dsokal))
- Support multiflavor Android projects. ([#595](https://github.com/expo/eas-cli/pull/595) by [@wkozyra95](https://github.com/wkozyra95))
- Support multi flavor Android projects. ([#595](https://github.com/expo/eas-cli/pull/595) by [@wkozyra95](https://github.com/wkozyra95))
- Improve experience when using the build details page as a build artifact URL in `eas submit`. ([#620](https://github.com/expo/eas-cli/pull/620) by [@dsokal](https://github.com/dsokal))

### 🐛 Bug fixes

Expand Down
68 changes: 60 additions & 8 deletions packages/eas-cli/src/submissions/ArchiveSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as uuid from 'uuid';
import { BuildFragment } from '../graphql/generated';
import { toAppPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
import { promptAsync } from '../prompts';
import { confirmAsync, promptAsync } from '../prompts';
import { getBuildByIdForSubmissionAsync, getLatestBuildForSubmissionAsync } from './utils/builds';
import { isExistingFileAsync, uploadAppArchiveAsync } from './utils/files';

Expand All @@ -22,6 +22,7 @@ interface ArchiveSourceBase {
sourceType: ArchiveSourceType;
platform: Platform;
projectId: string;
nonInteractive: boolean;
}

interface ArchiveUrlSource extends ArchiveSourceBase {
Expand Down Expand Up @@ -81,16 +82,29 @@ export async function getArchiveAsync(source: ArchiveSource): Promise<Archive> {
}

async function handleUrlSourceAsync(source: ArchiveUrlSource): Promise<Archive> {
if (!validateUrl(source.url)) {
Log.error(chalk.bold(`The URL you provided is invalid: ${source.url}`));
const { url } = source;

if (!validateUrl(url)) {
Log.error(chalk.bold(`The URL you provided is invalid: ${url}`));
return getArchiveAsync({
...source,
sourceType: ArchiveSourceType.prompt,
});
}

const maybeBuildId = isBuildDetailsPage(url);
if (maybeBuildId) {
if (await askIfUseBuildIdFromUrlAsync(source, maybeBuildId)) {
return getArchiveAsync({
...source,
sourceType: ArchiveSourceType.buildId,
id: maybeBuildId,
});
}
}

return {
url: source.url,
url,
source,
};
}
Expand Down Expand Up @@ -216,8 +230,6 @@ async function handlePromptSourceAsync(source: ArchivePromptSource): Promise<Arc
}
}

/* PROMPTS */

async function askForArchiveUrlAsync(): Promise<string> {
const defaultArchiveUrl = 'https://url.to/your/archive.aab';
const { url } = await promptAsync({
Expand Down Expand Up @@ -266,8 +278,8 @@ async function askForBuildIdAsync(): Promise<string> {
message: 'Build ID:',
type: 'text',
validate: (val: string): string | boolean => {
if (!uuid.validate(val)) {
return `${val} is not a valid id`;
if (!isUuidV4(val)) {
return `${val} is not a valid ID`;
} else {
return true;
}
Expand All @@ -276,6 +288,42 @@ async function askForBuildIdAsync(): Promise<string> {
return id;
}

async function askIfUseBuildIdFromUrlAsync(
source: ArchiveUrlSource,
buildId: string
): Promise<boolean> {
const { url } = source;
Log.warn(`It seems that you provided a build details page URL: ${url}`);
Log.warn('We expected to see the build artifact URL.');
if (!source.nonInteractive) {
const useAsBuildId = await confirmAsync({
message: `Do you want to submit build ${buildId} instead?`,
});
if (useAsBuildId) {
return true;
} else {
Log.warn('The submission will most probably fail.');
}
} else {
Log.warn("Proceeding because you've run this command in non-interactive mode.");
}
return false;
}

function isBuildDetailsPage(url: string): string | false {
const maybeExpoUrl = url.match(/expo\.(dev|io).*\/builds\/(.{36}).*/);
if (maybeExpoUrl) {
const maybeBuildId = maybeExpoUrl[2];
if (isUuidV4(maybeBuildId)) {
return maybeBuildId;
} else {
return false;
}
} else {
return false;
}
}

function validateUrl(url: string): boolean {
const protocols = ['http', 'https'];
try {
Expand All @@ -289,3 +337,7 @@ function validateUrl(url: string): boolean {
return false;
}
}

export function isUuidV4(s: string): boolean {
return uuid.validate(s) && uuid.version(s) === 4;
}
22 changes: 21 additions & 1 deletion packages/eas-cli/src/submissions/__tests__/ArchiveSource-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../__tests__/utils';
import { AppPlatform, BuildFragment, UploadSessionType } from '../../graphql/generated';
import { toAppPlatform } from '../../graphql/types/AppPlatform';
import { promptAsync } from '../../prompts';
import { confirmAsync, promptAsync } from '../../prompts';
import { uploadAsync } from '../../uploads';
import { Archive, ArchiveSourceType, getArchiveAsync } from '../ArchiveSource';
import { getBuildByIdForSubmissionAsync, getLatestBuildForSubmissionAsync } from '../utils/builds';
Expand All @@ -32,6 +32,7 @@ const SOURCE_STUB_INPUT = {
projectId: uuidv4(),
platform: Platform.ANDROID,
projectDir: '.',
nonInteractive: false,
};

describe(getArchiveAsync, () => {
Expand All @@ -42,6 +43,11 @@ describe(getArchiveAsync, () => {
asMock(promptAsync).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});

asMock(confirmAsync).mockReset();
asMock(confirmAsync).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
});

it('handles URL source', async () => {
Expand Down Expand Up @@ -69,6 +75,20 @@ describe(getArchiveAsync, () => {
assertArchiveResult(archive, ArchiveSourceType.url);
});

it('asks the user if use build id instead of build details page url', async () => {
asMock(confirmAsync).mockResolvedValueOnce(true);

const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
sourceType: ArchiveSourceType.url,
url: 'https://expo.dev/accounts/turtle/projects/blah/builds/81da6b36-efe4-4262-8970-84f03efeec81',
});

expect(confirmAsync).toHaveBeenCalled();
assertArchiveResult(archive, ArchiveSourceType.buildId);
expect((archive.source as any).id).toBe('81da6b36-efe4-4262-8970-84f03efeec81');
});

it('handles prompt source', async () => {
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
Expand Down
12 changes: 8 additions & 4 deletions packages/eas-cli/src/submissions/commons.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Platform } from '@expo/eas-build-job';
import * as uuid from 'uuid';

import { ArchiveSource, ArchiveSourceType } from './ArchiveSource';
import { ArchiveSource, ArchiveSourceType, isUuidV4 } from './ArchiveSource';
import { SubmissionContext } from './context';

export function resolveArchiveSource<T extends Platform>(
Expand All @@ -20,29 +19,33 @@ export function resolveArchiveSource<T extends Platform>(
url,
platform,
projectId: ctx.projectId,
nonInteractive: ctx.nonInteractive,
};
} else if (path) {
return {
sourceType: ArchiveSourceType.path,
path,
platform,
projectId: ctx.projectId,
nonInteractive: ctx.nonInteractive,
};
} else if (id) {
if (!uuid.validate(id)) {
throw new Error(`${id} is not an ID`);
if (!isUuidV4(id)) {
throw new Error(`${id} is not a valid ID`);
}
return {
sourceType: ArchiveSourceType.buildId,
id,
platform,
projectId: ctx.projectId,
nonInteractive: ctx.nonInteractive,
};
} else if (latest) {
return {
sourceType: ArchiveSourceType.latest,
platform,
projectId: ctx.projectId,
nonInteractive: ctx.nonInteractive,
};
} else if (ctx.nonInteractive) {
throw new Error('You need to specify the archive source when running in non-interactive mode ');
Expand All @@ -51,6 +54,7 @@ export function resolveArchiveSource<T extends Platform>(
sourceType: ArchiveSourceType.prompt,
platform,
projectId: ctx.projectId,
nonInteractive: ctx.nonInteractive,
};
}
}