feature: Allow editing existing Edits (#120)
* Only take edit ID for track validation * Only take edit ID for adding releases * Only take edit ID for mapping file upload * Only take edit ID for uploading artifacts * Only use app edit ID for uploading releases * Rebuild * Allow passing in an existing edit ID * Expose existingEditId * Run build
This commit is contained in:
@@ -18,6 +18,7 @@ This action will help you upload an Android `.apk` or `.aab` (Android App Bundle
|
|||||||
| mappingFile | The mapping.txt file used to de-obfuscate your stack traces from crash reports | A path to a valid `mapping.txt` file | false |
|
| mappingFile | The mapping.txt file used to de-obfuscate your stack traces from crash reports | A path to a valid `mapping.txt` file | false |
|
||||||
| changesNotSentForReview | Indicates that the changes in this edit will not be reviewed until they are explicitly sent for review from the Google Play Console. Defaults to `false` | `true` or `false` | `false` |
|
| changesNotSentForReview | Indicates that the changes in this edit will not be reviewed until they are explicitly sent for review from the Google Play Console. Defaults to `false` | `true` or `false` | `false` |
|
||||||
| serviceAccountJson | The service account json private key file to authorize the upload request. Can be used instead of `serviceAccountJsonPlainText` to specify a file rather than provide a secret | A path to a valid `service-account.json` file | true (or serviceAccountJsonPlainText) |
|
| serviceAccountJson | The service account json private key file to authorize the upload request. Can be used instead of `serviceAccountJsonPlainText` to specify a file rather than provide a secret | A path to a valid `service-account.json` file | true (or serviceAccountJsonPlainText) |
|
||||||
|
| existingEditId | The ID of an existing edit that has not been completed. If this is supplied, the action will append information to that rather than creating an edit | A valid, unpublished Edit ID | false |
|
||||||
| ~~releaseFile~~ | Please switch to using `releaseFiles` as this will be removed in the future | | false |
|
| ~~releaseFile~~ | Please switch to using `releaseFiles` as this will be removed in the future | | false |
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ inputs:
|
|||||||
description: "Indicates that the changes in this edit will not be reviewed until they are explicitly sent for review from the Google Play Console"
|
description: "Indicates that the changes in this edit will not be reviewed until they are explicitly sent for review from the Google Play Console"
|
||||||
default: false
|
default: false
|
||||||
required: false
|
required: false
|
||||||
|
existingEditId:
|
||||||
|
description: "The ID of an existing edit that has not been completed. If this is supplied, the action will append information to that rather than creating an edit"
|
||||||
|
required: false
|
||||||
outputs:
|
outputs:
|
||||||
internalSharingDownloadUrl:
|
internalSharingDownloadUrl:
|
||||||
description: "The internal app sharing download url if track was 'internalsharing'"
|
description: "The internal app sharing download url if track was 'internalsharing'"
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
62
src/edits.ts
62
src/edits.ts
@@ -1,19 +1,18 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import {readFileSync} from "fs";
|
import { readFileSync } from "fs";
|
||||||
|
|
||||||
import * as google from '@googleapis/androidpublisher';
|
import * as google from '@googleapis/androidpublisher';
|
||||||
import {androidpublisher_v3} from "@googleapis/androidpublisher";
|
import { androidpublisher_v3 } from "@googleapis/androidpublisher";
|
||||||
|
|
||||||
import AndroidPublisher = androidpublisher_v3.Androidpublisher;
|
import AndroidPublisher = androidpublisher_v3.Androidpublisher;
|
||||||
import AppEdit = androidpublisher_v3.Schema$AppEdit;
|
|
||||||
import Apk = androidpublisher_v3.Schema$Apk;
|
import Apk = androidpublisher_v3.Schema$Apk;
|
||||||
import Bundle = androidpublisher_v3.Schema$Bundle;
|
import Bundle = androidpublisher_v3.Schema$Bundle;
|
||||||
import Track = androidpublisher_v3.Schema$Track;
|
import Track = androidpublisher_v3.Schema$Track;
|
||||||
import InternalAppSharingArtifact = androidpublisher_v3.Schema$InternalAppSharingArtifact;
|
import InternalAppSharingArtifact = androidpublisher_v3.Schema$InternalAppSharingArtifact;
|
||||||
import {Compute} from "google-auth-library/build/src/auth/computeclient";
|
import { Compute } from "google-auth-library/build/src/auth/computeclient";
|
||||||
import {JSONClient} from "google-auth-library/build/src/auth/googleauth"
|
import { JSONClient } from "google-auth-library/build/src/auth/googleauth"
|
||||||
import {readLocalizedReleaseNotes} from "./whatsnew";
|
import { readLocalizedReleaseNotes } from "./whatsnew";
|
||||||
|
|
||||||
const androidPublisher: AndroidPublisher = google.androidpublisher('v3');
|
const androidPublisher: AndroidPublisher = google.androidpublisher('v3');
|
||||||
|
|
||||||
@@ -28,6 +27,7 @@ export interface EditOptions {
|
|||||||
name?: string;
|
name?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
changesNotSentForReview?: boolean;
|
changesNotSentForReview?: boolean;
|
||||||
|
existingEditId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uploadToPlayStore(options: EditOptions, releaseFiles: string[]): Promise<string | undefined> {
|
export async function uploadToPlayStore(options: EditOptions, releaseFiles: string[]): Promise<string | undefined> {
|
||||||
@@ -51,14 +51,14 @@ export async function uploadToPlayStore(options: EditOptions, releaseFiles: stri
|
|||||||
} else {
|
} else {
|
||||||
// Create a new Edit
|
// Create a new Edit
|
||||||
core.info(`Creating a new Edit for this release`)
|
core.info(`Creating a new Edit for this release`)
|
||||||
const appEdit = await androidPublisher.edits.insert({
|
const appEditId = options.existingEditId || (await androidPublisher.edits.insert({
|
||||||
auth: options.auth,
|
auth: options.auth,
|
||||||
packageName: options.applicationId
|
packageName: options.applicationId
|
||||||
});
|
})).data.id
|
||||||
|
|
||||||
// Validate the given track
|
// Validate the given track
|
||||||
core.info(`Validating track '${options.track}'`)
|
core.info(`Validating track '${options.track}'`)
|
||||||
await validateSelectedTrack(appEdit.data, options).catch(reason => {
|
await validateSelectedTrack(appEditId!, options).catch(reason => {
|
||||||
core.setFailed(reason);
|
core.setFailed(reason);
|
||||||
return Promise.reject(reason);
|
return Promise.reject(reason);
|
||||||
});
|
});
|
||||||
@@ -67,7 +67,7 @@ export async function uploadToPlayStore(options: EditOptions, releaseFiles: stri
|
|||||||
const versionCodes = new Array<number>();
|
const versionCodes = new Array<number>();
|
||||||
for (const releaseFile of releaseFiles) {
|
for (const releaseFile of releaseFiles) {
|
||||||
core.info(`Uploading ${releaseFile}`);
|
core.info(`Uploading ${releaseFile}`);
|
||||||
const versionCode = await uploadRelease(appEdit.data, options, releaseFile).catch(reason => {
|
const versionCode = await uploadRelease(appEditId!, options, releaseFile).catch(reason => {
|
||||||
core.setFailed(reason);
|
core.setFailed(reason);
|
||||||
return Promise.reject(reason);
|
return Promise.reject(reason);
|
||||||
});
|
});
|
||||||
@@ -77,14 +77,14 @@ export async function uploadToPlayStore(options: EditOptions, releaseFiles: stri
|
|||||||
|
|
||||||
// Add the uploaded artifacts to the Edit track
|
// Add the uploaded artifacts to the Edit track
|
||||||
core.info(`Adding ${versionCodes.length} artifacts to release on '${options.track}' track`)
|
core.info(`Adding ${versionCodes.length} artifacts to release on '${options.track}' track`)
|
||||||
const track = await addReleasesToTrack(appEdit.data, options, versionCodes);
|
const track = await addReleasesToTrack(appEditId!, options, versionCodes);
|
||||||
core.debug(`Track: ${track}`);
|
core.debug(`Track: ${track}`);
|
||||||
|
|
||||||
// Commit the pending Edit
|
// Commit the pending Edit
|
||||||
core.info(`Committing the Edit`)
|
core.info(`Committing the Edit`)
|
||||||
const res = await androidPublisher.edits.commit({
|
const res = await androidPublisher.edits.commit({
|
||||||
auth: options.auth,
|
auth: options.auth,
|
||||||
editId: appEdit.data.id!,
|
editId: appEditId!,
|
||||||
packageName: options.applicationId,
|
packageName: options.applicationId,
|
||||||
changesNotSentForReview: options.changesNotSentForReview
|
changesNotSentForReview: options.changesNotSentForReview
|
||||||
});
|
});
|
||||||
@@ -118,24 +118,24 @@ async function uploadInternalSharingRelease(options: EditOptions, releaseFile: s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadRelease(appEdit: AppEdit, options: EditOptions, releaseFile: string): Promise<number | undefined | null> {
|
async function uploadRelease(appEditId: string, options: EditOptions, releaseFile: string): Promise<number | undefined | null> {
|
||||||
if (releaseFile.endsWith('.apk')) {
|
if (releaseFile.endsWith('.apk')) {
|
||||||
const apk = await uploadApk(appEdit, options, releaseFile);
|
const apk = await uploadApk(appEditId, options, releaseFile);
|
||||||
await uploadMappingFile(appEdit, apk.versionCode!, options);
|
await uploadMappingFile(appEditId, apk.versionCode!, options);
|
||||||
return Promise.resolve(apk.versionCode);
|
return Promise.resolve(apk.versionCode);
|
||||||
} else if (releaseFile.endsWith('.aab')) {
|
} else if (releaseFile.endsWith('.aab')) {
|
||||||
const bundle = await uploadBundle(appEdit, options, releaseFile);
|
const bundle = await uploadBundle(appEditId, options, releaseFile);
|
||||||
await uploadMappingFile(appEdit, bundle.versionCode!, options);
|
await uploadMappingFile(appEditId, bundle.versionCode!, options);
|
||||||
return Promise.resolve(bundle.versionCode);
|
return Promise.resolve(bundle.versionCode);
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject(`${releaseFile} is invalid`);
|
return Promise.reject(`${releaseFile} is invalid`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validateSelectedTrack(appEdit: AppEdit, options: EditOptions): Promise<undefined> {
|
async function validateSelectedTrack(appEditId: string, options: EditOptions): Promise<undefined> {
|
||||||
const res = await androidPublisher.edits.tracks.list({
|
const res = await androidPublisher.edits.tracks.list({
|
||||||
auth: options.auth,
|
auth: options.auth,
|
||||||
editId: appEdit.id!,
|
editId: appEditId,
|
||||||
packageName: options.applicationId
|
packageName: options.applicationId
|
||||||
});
|
});
|
||||||
const allTracks = res.data.tracks;
|
const allTracks = res.data.tracks;
|
||||||
@@ -144,7 +144,7 @@ async function validateSelectedTrack(appEdit: AppEdit, options: EditOptions): Pr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addReleasesToTrack(appEdit: AppEdit, options: EditOptions, versionCodes: number[]): Promise<Track> {
|
async function addReleasesToTrack(appEditId: string, options: EditOptions, versionCodes: number[]): Promise<Track> {
|
||||||
let status: string | undefined = options.status;
|
let status: string | undefined = options.status;
|
||||||
if (!status) {
|
if (!status) {
|
||||||
if (options.userFraction != undefined) {
|
if (options.userFraction != undefined) {
|
||||||
@@ -154,11 +154,11 @@ async function addReleasesToTrack(appEdit: AppEdit, options: EditOptions, versio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
core.debug(`Creating Track Release for Edit(${appEdit.id}) for Track(${options.track}) with a UserFraction(${options.userFraction}), Status(${status}), and VersionCodes(${versionCodes})`);
|
core.debug(`Creating Track Release for Edit(${appEditId}) for Track(${options.track}) with a UserFraction(${options.userFraction}), Status(${status}), and VersionCodes(${versionCodes})`);
|
||||||
const res = await androidPublisher.edits.tracks
|
const res = await androidPublisher.edits.tracks
|
||||||
.update({
|
.update({
|
||||||
auth: options.auth,
|
auth: options.auth,
|
||||||
editId: appEdit.id!,
|
editId: appEditId,
|
||||||
packageName: options.applicationId,
|
packageName: options.applicationId,
|
||||||
track: options.track,
|
track: options.track,
|
||||||
requestBody: {
|
requestBody: {
|
||||||
@@ -179,15 +179,15 @@ async function addReleasesToTrack(appEdit: AppEdit, options: EditOptions, versio
|
|||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadMappingFile(appEdit: AppEdit, versionCode: number, options: EditOptions) {
|
async function uploadMappingFile(appEditId: string, versionCode: number, options: EditOptions) {
|
||||||
if (options.mappingFile != undefined && options.mappingFile.length > 0) {
|
if (options.mappingFile != undefined && options.mappingFile.length > 0) {
|
||||||
const mapping = readFileSync(options.mappingFile, 'utf-8');
|
const mapping = readFileSync(options.mappingFile, 'utf-8');
|
||||||
if (mapping != undefined) {
|
if (mapping != undefined) {
|
||||||
core.debug(`[${appEdit.id}, versionCode=${versionCode}, packageName=${options.applicationId}]: Uploading Proguard mapping file @ ${options.mappingFile}`);
|
core.debug(`[${appEditId}, versionCode=${versionCode}, packageName=${options.applicationId}]: Uploading Proguard mapping file @ ${options.mappingFile}`);
|
||||||
await androidPublisher.edits.deobfuscationfiles.upload({
|
await androidPublisher.edits.deobfuscationfiles.upload({
|
||||||
auth: options.auth,
|
auth: options.auth,
|
||||||
packageName: options.applicationId,
|
packageName: options.applicationId,
|
||||||
editId: appEdit.id!,
|
editId: appEditId,
|
||||||
apkVersionCode: versionCode,
|
apkVersionCode: versionCode,
|
||||||
deobfuscationFileType: 'proguard',
|
deobfuscationFileType: 'proguard',
|
||||||
media: {
|
media: {
|
||||||
@@ -229,13 +229,13 @@ async function internalSharingUploadBundle(options: EditOptions, bundleReleaseFi
|
|||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadApk(appEdit: AppEdit, options: EditOptions, apkReleaseFile: string): Promise<Apk> {
|
async function uploadApk(appEditId: string, options: EditOptions, apkReleaseFile: string): Promise<Apk> {
|
||||||
core.debug(`[${appEdit.id}, packageName=${options.applicationId}]: Uploading APK @ ${apkReleaseFile}`);
|
core.debug(`[${appEditId}, packageName=${options.applicationId}]: Uploading APK @ ${apkReleaseFile}`);
|
||||||
|
|
||||||
const res = await androidPublisher.edits.apks.upload({
|
const res = await androidPublisher.edits.apks.upload({
|
||||||
auth: options.auth,
|
auth: options.auth,
|
||||||
packageName: options.applicationId,
|
packageName: options.applicationId,
|
||||||
editId: appEdit.id!,
|
editId: appEditId,
|
||||||
media: {
|
media: {
|
||||||
mimeType: 'application/vnd.android.package-archive',
|
mimeType: 'application/vnd.android.package-archive',
|
||||||
body: fs.createReadStream(apkReleaseFile)
|
body: fs.createReadStream(apkReleaseFile)
|
||||||
@@ -245,12 +245,12 @@ async function uploadApk(appEdit: AppEdit, options: EditOptions, apkReleaseFile:
|
|||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadBundle(appEdit: AppEdit, options: EditOptions, bundleReleaseFile: string): Promise<Bundle> {
|
async function uploadBundle(appEditId: string, options: EditOptions, bundleReleaseFile: string): Promise<Bundle> {
|
||||||
core.debug(`[${appEdit.id}, packageName=${options.applicationId}]: Uploading App Bundle @ ${bundleReleaseFile}`);
|
core.debug(`[${appEditId}, packageName=${options.applicationId}]: Uploading App Bundle @ ${bundleReleaseFile}`);
|
||||||
const res = await androidPublisher.edits.bundles.upload({
|
const res = await androidPublisher.edits.bundles.upload({
|
||||||
auth: options.auth,
|
auth: options.auth,
|
||||||
packageName: options.applicationId,
|
packageName: options.applicationId,
|
||||||
editId: appEdit.id!,
|
editId: appEditId,
|
||||||
media: {
|
media: {
|
||||||
mimeType: 'application/octet-stream',
|
mimeType: 'application/octet-stream',
|
||||||
body: fs.createReadStream(bundleReleaseFile)
|
body: fs.createReadStream(bundleReleaseFile)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ async function run() {
|
|||||||
const whatsNewDir = core.getInput('whatsNewDirectory', { required: false });
|
const whatsNewDir = core.getInput('whatsNewDirectory', { required: false });
|
||||||
const mappingFile = core.getInput('mappingFile', { required: false });
|
const mappingFile = core.getInput('mappingFile', { required: false });
|
||||||
const changesNotSentForReview = core.getInput('changesNotSentForReview', { required: false }) == 'true';
|
const changesNotSentForReview = core.getInput('changesNotSentForReview', { required: false }) == 'true';
|
||||||
|
const existingEditId = core.getInput('existingEditId')
|
||||||
|
|
||||||
// Validate that we have a service account json in some format
|
// Validate that we have a service account json in some format
|
||||||
if (!serviceAccountJson && !serviceAccountJsonRaw) {
|
if (!serviceAccountJson && !serviceAccountJsonRaw) {
|
||||||
@@ -118,7 +119,8 @@ async function run() {
|
|||||||
whatsNewDir: whatsNewDir,
|
whatsNewDir: whatsNewDir,
|
||||||
mappingFile: mappingFile,
|
mappingFile: mappingFile,
|
||||||
name: releaseName,
|
name: releaseName,
|
||||||
changesNotSentForReview: changesNotSentForReview
|
changesNotSentForReview: changesNotSentForReview,
|
||||||
|
existingEditId: existingEditId
|
||||||
}, validatedReleaseFiles);
|
}, validatedReleaseFiles);
|
||||||
|
|
||||||
console.log(`Finished uploading to the Play Store: ${result}`)
|
console.log(`Finished uploading to the Play Store: ${result}`)
|
||||||
|
|||||||
Reference in New Issue
Block a user