Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
changes after testing
Browse files Browse the repository at this point in the history
  • Loading branch information
quinlanj committed Mar 23, 2020
1 parent 48b4c72 commit a565a89
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 120 deletions.
6 changes: 4 additions & 2 deletions packages/expo-cli/src/commands/publish-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import {
DetailOptions,
HistoryOptions,
Publication,
getPublicationDetailAsync,
getPublishHistoryAsync,
printPublishDetailsAsync,
printPublicationDetailAsync,
} from './utils/PublishUtils';

const HORIZ_CELL_WIDTH_SMALL = 15;
Expand Down Expand Up @@ -92,6 +93,7 @@ export default (program: any) => {
throw new Error('--publish-id must be specified.');
}

await printPublishDetailsAsync(projectDir, options);
const detail = await getPublicationDetailAsync(projectDir, options);
await printPublicationDetailAsync(detail, options);
});
};
104 changes: 5 additions & 99 deletions packages/expo-cli/src/commands/publish-modify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import prompt from '../prompt';
import * as table from '../commands/utils/cli-table';
import {
Publication,
RollbackOptions,
getPublicationDetailAsync,
getPublishHistoryAsync,
printPublishDetailsAsync,
printPublicationDetailAsync,
rollbackPublicationFromChannelAsync,
setPublishToChannelAsync,
} from './utils/PublishUtils';

Expand Down Expand Up @@ -62,104 +65,7 @@ export default function(program: Command) {
if (!options.channelId) {
throw new Error('You must specify a channel id. You can find ids using publish:history.');
}
const user = await UserManager.getCurrentUserAsync();
const api = ApiV2.clientForUser(user);
const { exp } = getConfig(projectDir, {
skipSDKVersionRequirement: true,
});

const channelQueryResult = await api.postAsync('publish/channel-details', {
owner: exp.owner,
slug: await Project.getSlugAsync(projectDir),
channelId: options.channelId,
});

const { channel, platform, sdkVersion } = channelQueryResult.queryResult;

if (channelQueryResult.queryResult.errorCode) {
throw new Error(`The channel id ${options.channelId} could not be found`);
}

// get the 2 most recent things in the channel history
const historyQueryResult = await getPublishHistoryAsync(projectDir, {
releaseChannel: channel,
platform,
sdkVersion,
count: 2,
});

// if the channelId is the most recent thing in the channel history
const history = historyQueryResult.queryResult as Publication[];
if (history.length === 0) {
throw new Error(
`The channel id ${options.channelId} could not be found in the publish history of channel: ${channel}`
);
}

const mostRecent = history[0];
const secondMostRecent = history[history.length - 1];

// the channel entry we want to roll back is the most recent thing in history
if (mostRecent.channelId === options.channelId) {
if (history.length === 1) {
throw new Error(
`There is no commit of the same sdkVersion (${sdkVersion}) and platform (${platform})for users to receive if we rollback`
);
}

// confirm that users will be receiving the secondMostRecent item in the Publish history
await printAndConfirm(projectDir, channel, secondMostRecent);

// apply the revert publication to channel
const revertProgress = ora(`Applying a revert publication to channel ${channel}`).start();
await setPublishToChannelAsync(projectDir, {
releaseChannel: channel,
publishId: secondMostRecent.publicationId,
});
revertProgress.succeed(
'Successfully applied revert publication. You can view it with `publish:history`'
);
} else {
// confirm that users will be receiving the mostRecent item in the Publish history
await printAndConfirm(projectDir, channel, mostRecent);
}

// rollback channel entry
const rollbackProgress = ora(
`Rolling back entry (channel id ${options.channelId})`
).start();
try {
let result = await api.postAsync('publish/rollback', {
channelId: options.channelId,
slug: await Project.getSlugAsync(projectDir),
});
rollbackProgress.succeed();
let tableString = table.printTableJson(
result.queryResult,
'Channel Rollback Status ',
'SUCCESS'
);
console.log(tableString);
} catch (e) {
rollbackProgress.stop();
log.error(e);
}
await rollbackPublicationFromChannelAsync(projectDir, options as RollbackOptions);
}
);
}

async function printAndConfirm(projectDir: string, channel: string, channelEntry: Publication) {
await printPublishDetailsAsync(projectDir, { publishId: channelEntry.publicationId });

const { confirm } = await prompt([
{
type: 'confirm',
name: 'confirm',
message: `Users on the '${channel}' channel will receive the above publication as a result of the rollback.`,
},
]);

if (!confirm) {
throw new Error(`Please run 'publish:set' to send the desired publication to users`);
}
}
187 changes: 168 additions & 19 deletions packages/expo-cli/src/commands/utils/PublishUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { getConfig } from '@expo/config';
import { Api, ApiV2, FormData, Project, UserManager } from '@expo/xdl';
import { ExpoConfig, getConfig } from '@expo/config';
import { Api, ApiV2, FormData, Project, User, UserManager } from '@expo/xdl';
import dateFormat from 'dateformat';
import ora from 'ora';
import * as table from './cli-table';
import log from '../../log';
import prompt from '../../prompt';

export type HistoryOptions = {
releaseChannel?: string;
Expand All @@ -16,6 +19,10 @@ export type DetailOptions = {
raw?: boolean;
};

export type SetOptions = { releaseChannel: string; publishId: string };

export type RollbackOptions = { channelId: string; parent?: { nonInteractive?: boolean } };

export type Publication = {
fullName: string;
channel: string;
Expand All @@ -27,6 +34,28 @@ export type Publication = {
platform: 'android' | 'ios';
};

export type PublicationDetail = {
manifest: {
[key: string]: string;
};
publishedTime: string;
publishingUsername: string;
packageUsername: string;
packageName: string;
fullName: string;
hash: string;
sdkVersion: string;
s3Key: string;
s3Url: string;
abiVersion: string | null;
bundleUrl: string | null;
platform: string;
version: string;
revisionId: string;
channels: { [key: string]: string }[];
publicationId: string;
};

const VERSION = 2;

export async function getPublishHistoryAsync(
Expand Down Expand Up @@ -86,7 +115,7 @@ export async function getPublishHistoryAsync(

export async function setPublishToChannelAsync(
projectDir: string,
options: { releaseChannel: string; publishId: string }
options: SetOptions
): Promise<any> {
const user = await UserManager.ensureLoggedInAsync();
const api = ApiV2.clientForUser(user);
Expand All @@ -97,14 +126,128 @@ export async function setPublishToChannelAsync(
});
}

export async function printPublishDetailsAsync(projectDir: string, options: DetailOptions) {
export async function rollbackPublicationFromChannelAsync(
projectDir: string,
options: RollbackOptions
) {
const user = await UserManager.getCurrentUserAsync();
const api = ApiV2.clientForUser(user);
const { exp } = getConfig(projectDir, {
skipSDKVersionRequirement: true,
});

const channelQueryResult = await api.postAsync('publish/channel-details', {
owner: exp.owner,
slug: await Project.getSlugAsync(projectDir),
channelId: options.channelId,
});

const { channel, platform, sdkVersion } = channelQueryResult.queryResult;

if (channelQueryResult.queryResult.errorCode) {
throw new Error(`The channel id ${options.channelId} could not be found`);
}

// get the 2 most recent things in the channel history
const historyQueryResult = await getPublishHistoryAsync(projectDir, {
releaseChannel: channel,
platform,
sdkVersion,
count: 2,
});

// if the channelId is the most recent thing in the channel history
const history = historyQueryResult.queryResult as Publication[];
if (history.length === 0) {
throw new Error(
`The channel id ${options.channelId} could not be found in the publish history of channel: ${channel}`
);
} else if (history.length === 1) {
throw new Error(
`There is no publication assigned to channel ${channel} with the same sdkVersion (${sdkVersion}) and platform (${platform}) for users to receive if we rollback`
);
}

const mostRecent = history[0];
const secondMostRecent = history[history.length - 1];

// the channel entry we want to roll back is the most recent thing in history
if (mostRecent.channelId === options.channelId) {
// confirm that users will be receiving the secondMostRecent item in the Publish history
await _printAndConfirm(projectDir, channel, secondMostRecent, options);

// apply the revert publication to channel
const revertProgress = ora(`Applying a revert publication to channel ${channel}`).start();
await setPublishToChannelAsync(projectDir, {
releaseChannel: channel,
publishId: secondMostRecent.publicationId,
});
revertProgress.succeed(
'Successfully applied revert publication. You can view it with `publish:history`'
);
} else {
// confirm that users will be receiving the mostRecent item in the Publish history
await _printAndConfirm(projectDir, channel, mostRecent, options);
}

// rollback channel entry
const rollbackProgress = ora(`Rolling back entry (channel id ${options.channelId})`).start();
try {
let result = await api.postAsync('publish/rollback', {
channelId: options.channelId,
slug: await Project.getSlugAsync(projectDir),
});
rollbackProgress.succeed();
let tableString = table.printTableJson(
result.queryResult,
'Channel Rollback Status ',
'SUCCESS'
);
console.log(tableString);
} catch (e) {
rollbackProgress.stop();
throw e;
}
}

async function _printAndConfirm(
projectDir: string,
channel: string,
channelEntry: Publication,
rollbackOptions: RollbackOptions
): Promise<void> {
const detailOptions = {
publishId: channelEntry.publicationId,
};
const detail = await getPublicationDetailAsync(projectDir, detailOptions);
await printPublicationDetailAsync(detail, detailOptions);

if (rollbackOptions.parent && rollbackOptions.parent.nonInteractive) {
return;
}
const { confirm } = await prompt([
{
type: 'confirm',
name: 'confirm',
message: `Users on the '${channel}' channel will receive the above publication as a result of the rollback.`,
},
]);

if (!confirm) {
throw new Error(`Please run 'publish:set' to send the desired publication to users`);
}
}

export async function getPublicationDetailAsync(
projectDir: string,
options: DetailOptions
): Promise<PublicationDetail> {
// TODO(ville): handle the API result for not authenticated user instead of checking upfront
const user = await UserManager.ensureLoggedInAsync();
const { exp } = getConfig(projectDir, {
skipSDKVersionRequirement: true,
});
const slug = await Project.getSlugAsync(projectDir);

let result: any;
if (process.env.EXPO_LEGACY_API === 'true') {
let formData = new FormData();
Expand All @@ -128,24 +271,30 @@ export async function printPublishDetailsAsync(projectDir: string, options: Deta
});
}

if (!result.queryResult) {
throw new Error('No records found matching your query.');
}

return result.queryResult;
}

export async function printPublicationDetailAsync(
detail: PublicationDetail,
options: DetailOptions
) {
if (options.raw) {
console.log(JSON.stringify(result));
console.log(JSON.stringify(detail));
return;
}

if (result.queryResult) {
let queryResult = result.queryResult;
let manifest = queryResult.manifest;
delete queryResult.manifest;
let manifest = detail.manifest;
delete detail.manifest;

// Print general release info
let generalTableString = table.printTableJson(queryResult, 'Release Description');
console.log(generalTableString);
// Print general release info
let generalTableString = table.printTableJson(detail, 'Release Description');
console.log(generalTableString);

// Print manifest info
let manifestTableString = table.printTableJson(manifest, 'Manifest Details');
console.log(manifestTableString);
} else {
throw new Error('No records found matching your query.');
}
// Print manifest info
let manifestTableString = table.printTableJson(manifest, 'Manifest Details');
console.log(manifestTableString);
}

0 comments on commit a565a89

Please sign in to comment.