Complete first build & point action to new entry

This commit is contained in:
Jack Boswell (boswelja)
2021-01-25 20:30:07 +13:00
parent ce216aff8e
commit 0ea696ec58
6 changed files with 360390 additions and 483 deletions

View File

@@ -45,4 +45,4 @@ outputs:
description: "The internal app sharing download url if track was 'internalsharing'"
runs:
using: 'node12'
main: 'lib/main.js'
main: 'lib/index.js'

View File

@@ -1,268 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.uploadToPlayStore = void 0;
const core = __importStar(require("@actions/core"));
const fs = __importStar(require("fs"));
const fs_1 = require("fs");
const googleapis_1 = require("googleapis");
const whatsnew_1 = require("./whatsnew");
const androidPublisher = googleapis_1.google.androidpublisher('v3');
function uploadToPlayStore(options, releaseFiles) {
return __awaiter(this, void 0, void 0, function* () {
// Check the 'track' for 'internalsharing', if so switch to a non-track api
if (options.track === 'internalsharing') {
core.debug("Track is Internal app sharing, switch to special upload api");
let downloadUrls = [];
for (const releaseFile of releaseFiles) {
core.debug(`Uploading ${releaseFile}`);
yield uploadInternalSharingRelease(options, releaseFile)
.then(downloadUrl => {
downloadUrls.push(downloadUrl);
})
.catch(reason => {
core.setFailed(reason);
return Promise.reject(reason);
});
}
core.setOutput("internalSharingDownloadUrls", downloadUrls);
core.exportVariable("INTERNAL_SHARING_DOWNLOAD_URLS", downloadUrls);
}
else {
// Create a new Edit
core.info(`Creating a new Edit for this release`);
const appEdit = yield androidPublisher.edits.insert({
auth: options.auth,
packageName: options.applicationId
});
// Validate the given track
core.info(`Validating track '${options.track}'`);
yield validateSelectedTrack(appEdit.data, options).catch(reason => {
core.setFailed(reason);
return Promise.reject(reason);
});
// Upload artifacts to Google Play, and store their version codes
const versionCodes = new Array();
for (const releaseFile of releaseFiles) {
core.info(`Uploading ${releaseFile}`);
const versionCode = yield uploadRelease(appEdit.data, options, releaseFile).catch(reason => {
core.setFailed(reason);
return Promise.reject(reason);
});
versionCodes.push(versionCode);
}
core.info(`Successfully uploaded ${versionCodes.length} artifacts`);
// Add the uploaded artifacts to the Edit track
core.info(`Adding ${versionCodes.length} artifacts to release on '${options.track}' track`);
const track = yield addReleasesToTrack(appEdit.data, options, versionCodes);
core.debug(`Track: ${track}`);
// Commit the pending Edit
core.info(`Committing the Edit`);
const res = yield androidPublisher.edits.commit({
auth: options.auth,
editId: appEdit.data.id,
packageName: options.applicationId
});
// Simple check to see whether commit was successful
if (res.data.id != null) {
core.info(`Successfully committed ${res.data.id}`);
return Promise.resolve(res.data.id);
}
else {
core.setFailed(`Error ${res.status}: ${res.statusText}`);
return Promise.reject(res.status);
}
}
});
}
exports.uploadToPlayStore = uploadToPlayStore;
function uploadInternalSharingRelease(options, releaseFile) {
return __awaiter(this, void 0, void 0, function* () {
if (releaseFile.endsWith('.apk')) {
const res = yield internalSharingUploadApk(options, releaseFile);
core.setOutput("internalSharingDownloadUrl", res.downloadUrl);
core.exportVariable("INTERNAL_SHARING_DOWNLOAD_URL", res.downloadUrl);
console.log(`${releaseFile} uploaded to Internal Sharing, download it with ${res.downloadUrl}`);
return Promise.resolve(res.downloadUrl);
}
else if (releaseFile.endsWith('.aab')) {
const res = yield internalSharingUploadBundle(options, releaseFile);
core.setOutput("internalSharingDownloadUrl", res.downloadUrl);
core.exportVariable("INTERNAL_SHARING_DOWNLOAD_URL", res.downloadUrl);
console.log(`${releaseFile} uploaded to Internal Sharing, download it with ${res.downloadUrl}`);
return Promise.resolve(res.downloadUrl);
}
else {
return Promise.reject(`${releaseFile} is invalid`);
}
});
}
function uploadRelease(appEdit, options, releaseFile) {
return __awaiter(this, void 0, void 0, function* () {
if (releaseFile.endsWith('.apk')) {
const apk = yield uploadApk(appEdit, options, releaseFile);
yield uploadMappingFile(appEdit, apk.versionCode, options);
return Promise.resolve(apk.versionCode);
}
else if (releaseFile.endsWith('.aab')) {
const bundle = yield uploadBundle(appEdit, options, releaseFile);
yield uploadMappingFile(appEdit, bundle.versionCode, options);
return Promise.resolve(bundle.versionCode);
}
else {
return Promise.reject(`${releaseFile} is invalid`);
}
});
}
function validateSelectedTrack(appEdit, options) {
return __awaiter(this, void 0, void 0, function* () {
const res = yield androidPublisher.edits.tracks.list({
auth: options.auth,
editId: appEdit.id,
packageName: options.applicationId
});
const allTracks = res.data.tracks;
if (allTracks == undefined || allTracks.find(value => value.track == options.track) == undefined) {
return Promise.reject(`Track "${options.track}" could not be found `);
}
});
}
function addReleasesToTrack(appEdit, options, versionCodes) {
return __awaiter(this, void 0, void 0, function* () {
let status;
if (options.userFraction != undefined) {
status = 'inProgress';
}
else {
status = 'completed';
}
core.debug(`Creating Track Release for Edit(${appEdit.id}) for Track(${options.track}) with a UserFraction(${options.userFraction}) and VersionCodes(${versionCodes})`);
const res = yield androidPublisher.edits.tracks
.update({
auth: options.auth,
editId: appEdit.id,
packageName: options.applicationId,
track: options.track,
requestBody: {
track: options.track,
releases: [
{
name: options.name,
userFraction: options.userFraction,
status: status,
inAppUpdatePriority: options.inAppUpdatePriority,
releaseNotes: yield whatsnew_1.readLocalizedReleaseNotes(options.whatsNewDir),
versionCodes: versionCodes.filter(x => x != 0).map(x => x.toString())
}
]
}
});
return res.data;
});
}
function uploadMappingFile(appEdit, versionCode, options) {
return __awaiter(this, void 0, void 0, function* () {
if (options.mappingFile != undefined && options.mappingFile.length > 0) {
const mapping = fs_1.readFileSync(options.mappingFile, 'utf-8');
if (mapping != undefined) {
core.debug(`[${appEdit.id}, versionCode=${versionCode}, packageName=${options.applicationId}]: Uploading Proguard mapping file @ ${options.mappingFile}`);
yield androidPublisher.edits.deobfuscationfiles.upload({
auth: options.auth,
packageName: options.applicationId,
editId: appEdit.id,
apkVersionCode: versionCode,
deobfuscationFileType: 'proguard',
media: {
mimeType: 'application/octet-stream',
body: fs.createReadStream(options.mappingFile)
}
});
}
}
});
}
function internalSharingUploadApk(options, apkReleaseFile) {
return __awaiter(this, void 0, void 0, function* () {
core.debug(`[packageName=${options.applicationId}]: Uploading Internal Sharing APK @ ${apkReleaseFile}`);
const res = yield androidPublisher.internalappsharingartifacts.uploadapk({
auth: options.auth,
packageName: options.applicationId,
media: {
mimeType: 'application/vnd.android.package-archive',
body: fs.createReadStream(apkReleaseFile)
}
});
return res.data;
});
}
function internalSharingUploadBundle(options, bundleReleaseFile) {
return __awaiter(this, void 0, void 0, function* () {
core.debug(`[packageName=${options.applicationId}]: Uploading Internal Sharing Bundle @ ${bundleReleaseFile}`);
const res = yield androidPublisher.internalappsharingartifacts.uploadbundle({
auth: options.auth,
packageName: options.applicationId,
media: {
mimeType: 'application/octet-stream',
body: fs.createReadStream(bundleReleaseFile)
}
});
return res.data;
});
}
function uploadApk(appEdit, options, apkReleaseFile) {
return __awaiter(this, void 0, void 0, function* () {
core.debug(`[${appEdit.id}, packageName=${options.applicationId}]: Uploading APK @ ${apkReleaseFile}`);
const res = yield androidPublisher.edits.apks.upload({
auth: options.auth,
packageName: options.applicationId,
editId: appEdit.id,
media: {
mimeType: 'application/vnd.android.package-archive',
body: fs.createReadStream(apkReleaseFile)
}
});
return res.data;
});
}
function uploadBundle(appEdit, options, bundleReleaseFile) {
return __awaiter(this, void 0, void 0, function* () {
core.debug(`[${appEdit.id}, packageName=${options.applicationId}]: Uploading App Bundle @ ${bundleReleaseFile}`);
const res = yield androidPublisher.edits.bundles.upload({
auth: options.auth,
packageName: options.applicationId,
editId: appEdit.id,
media: {
mimeType: 'application/octet-stream',
body: fs.createReadStream(bundleReleaseFile)
}
});
return res.data;
});
}

360388
lib/index.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,148 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const fs = __importStar(require("fs"));
const edits_1 = require("./edits");
const { google } = require('googleapis');
const auth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidpublisher']
});
function run() {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
try {
const serviceAccountJson = core.getInput('serviceAccountJson', { required: false });
const serviceAccountJsonRaw = core.getInput('serviceAccountJsonPlainText', { required: false });
const packageName = core.getInput('packageName', { required: true });
const releaseFile = core.getInput('releaseFile', { required: false });
const releaseFiles = ((_b = (_a = core.getInput('releaseFiles', { required: false })) === null || _a === void 0 ? void 0 : _a.split(',')) === null || _b === void 0 ? void 0 : _b.filter(x => x !== '')) || [];
const releaseName = core.getInput('releaseName', { required: false });
const track = core.getInput('track', { required: true });
const inAppUpdatePriority = core.getInput('inAppUpdatePriority', { required: false });
const userFraction = core.getInput('userFraction', { required: false });
const whatsNewDir = core.getInput('whatsNewDirectory', { required: false });
const mappingFile = core.getInput('mappingFile', { required: false });
// Validate that we have a service account json in some format
if (!serviceAccountJson && !serviceAccountJsonRaw) {
console.log("No service account json key provided!");
core.setFailed("You must provide one of 'serviceAccountJson' or 'serviceAccountJsonPlainText' to use this action");
return;
}
// If the user has provided the raw plain text via secrets, or w/e, then write to file and
// set appropriate env variable for the auth
if (serviceAccountJsonRaw) {
const serviceAccountFile = "./serviceAccountJson.json";
fs.writeFileSync(serviceAccountFile, serviceAccountJsonRaw, {
encoding: 'utf8'
});
// Insure that the api can find our service account credentials
core.exportVariable("GOOGLE_APPLICATION_CREDENTIALS", serviceAccountFile);
}
if (serviceAccountJson) {
// Insure that the api can find our service account credentials
core.exportVariable("GOOGLE_APPLICATION_CREDENTIALS", serviceAccountJson);
}
// Validate user fraction as a number, and within [0.0, 1.0]
let userFractionFloat = parseFloat(userFraction);
if (!isNaN(userFractionFloat)) {
if (userFractionFloat <= 0.0 || userFractionFloat >= 1.0) {
core.setFailed('A provided userFraction must be between 0.0 and 1.0, inclusive-inclusive');
return;
}
}
else {
userFractionFloat = undefined;
}
// Validate the inAppUpdatePriority to be a valid number in within [0, 5]
let inAppUpdatePriorityInt = parseInt(inAppUpdatePriority);
if (!isNaN(inAppUpdatePriorityInt)) {
if (inAppUpdatePriorityInt < 0 || inAppUpdatePriorityInt > 5) {
core.setFailed('inAppUpdatePriority must be between 0 and 5, inclusive-inclusive');
return;
}
}
else {
inAppUpdatePriorityInt = undefined;
}
// Check release files while maintaining backward compatibility
let validatedReleaseFiles = [];
if (releaseFiles.length == 0 && !releaseFile) {
core.setFailed(`You must provide either 'releaseFile' or 'releaseFiles' in your configuration.`);
return;
}
else if (releaseFiles.length == 0 && releaseFile) {
core.warning(`WARNING!! 'releaseFile' is deprecated and will be removed in a future release. Please migrate to 'releaseFiles'.`);
core.debug(`Validating ${releaseFile} exists`);
if (!fs.existsSync(releaseFile)) {
core.setFailed(`Unable to find release file @ ${releaseFile}`);
return;
}
else {
validatedReleaseFiles = [releaseFile];
}
}
else if (releaseFiles.length > 0) {
for (const file of releaseFiles) {
core.debug(`Validating ${file} exists`);
if (!fs.existsSync(file)) {
core.setFailed(`Unable to find release file @ ${file}`);
return;
}
}
validatedReleaseFiles = releaseFiles;
}
if (whatsNewDir != undefined && whatsNewDir.length > 0 && !fs.existsSync(whatsNewDir)) {
core.setFailed(`Unable to find 'whatsnew' directory @ ${whatsNewDir}`);
return;
}
if (mappingFile != undefined && mappingFile.length > 0 && !fs.existsSync(mappingFile)) {
core.setFailed(`Unable to find 'mappingFile' @ ${mappingFile}`);
return;
}
const authClient = yield auth.getClient();
const result = yield edits_1.uploadToPlayStore({
auth: authClient,
applicationId: packageName,
track: track,
inAppUpdatePriority: inAppUpdatePriorityInt || 0,
userFraction: userFractionFloat,
whatsNewDir: whatsNewDir,
mappingFile: mappingFile,
name: releaseName
}, validatedReleaseFiles);
console.log(`Finished uploading to the Play Store: ${result}`);
}
catch (error) {
core.setFailed(error.message);
}
});
}
run();

View File

@@ -1,65 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readLocalizedReleaseNotes = void 0;
const core = __importStar(require("@actions/core"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const fs_1 = require("fs");
function readLocalizedReleaseNotes(whatsNewDir) {
return __awaiter(this, void 0, void 0, function* () {
if (whatsNewDir != undefined && whatsNewDir.length > 0) {
const releaseNotes = fs.readdirSync(whatsNewDir)
.filter(value => /whatsnew-.*-.{2,3}\b/.test(value));
const pattern = /whatsnew-(?<local>.*-.*)/;
let localizedReleaseNotes = [];
core.debug(`Found files: ${releaseNotes}`);
releaseNotes.forEach(value => {
const matches = value.match(pattern);
core.debug(`Matches for ${value} = ${matches}`);
if (matches != undefined && matches.length == 2) {
const lang = matches[1];
const filePath = path.join(whatsNewDir, value);
const content = fs_1.readFileSync(filePath, 'utf-8');
if (content != undefined) {
core.debug(`Found localized 'whatsnew-*-*' for Lang(${lang})`);
localizedReleaseNotes.push({
language: lang,
text: content
});
}
}
});
return localizedReleaseNotes;
}
return undefined;
});
}
exports.readLocalizedReleaseNotes = readLocalizedReleaseNotes;

View File

@@ -3,7 +3,7 @@
"version": "1.0.10",
"private": true,
"description": "Upload an Android release (.apk or .aab) to Google Play Console",
"main": "lib/main.js",
"main": "lib/index.js",
"scripts": {
"build": "ncc build src/main.ts -o lib/",
"test": "jest"