Skip to content

Instantly share code, notes, and snippets.

@BransonGitomeh
Last active January 19, 2022 10:27
Show Gist options
  • Save BransonGitomeh/4f9f04c23e0f413e9c2f38d38697ed20 to your computer and use it in GitHub Desktop.
Save BransonGitomeh/4f9f04c23e0f413e9c2f38d38697ed20 to your computer and use it in GitHub Desktop.
Script to upload apk to playstore, can be added to CI step to automate publications
#!/usr/bin/env NODE_NO_WARNINGS=1 node
const { google } = require('googleapis');
const Promise = require('bluebird');
const fs = require('fs');
const settings = require('./config/settings.json');
const { version } = require('./package.json');
// Enable API access into the Developer Console: https://play.google.com/apps/publish/?account=7639196906174529268#ApiAccessPlace
// Create a service account
// Download the JSON and save it here
// Make sure the email of the JSON is added to the apps for release manager role:
// https://play.google.com/apps/publish/?account=7639196906174529268#AdminPlace
const key = require('./secrets.json');
// editing "scope" allowed for OAuth2
const scopes = ['https://www.googleapis.com/auth/androidpublisher'];
process.exitCode = 1;
const { OAuth2 } = google.auth;
const oauth2Client = new OAuth2();
const jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
scopes,
null,
);
const play = google.androidpublisher({
version: 'v3',
auth: oauth2Client,
params: {
// default options
// this is the package name for your initial app you've already set up on the Play Store
packageName: settings.app.id,
},
});
google.options({ auth: oauth2Client });
/**
* Sets our authorization token and begins an edit transaction.
*/
function startEdit() {
return new Promise(((resolve, reject) => {
jwtClient.authorize((err, tokens) => {
if (err) {
console.log('authorize error', err);
process.exit(1);
return;
}
// Set the credentials before we doing anything.
oauth2Client.setCredentials(tokens);
play.edits.insert(
{
packageName: settings.app.id,
},
(insertError, edit) => {
if (insertError || !edit) {
console.log('Insert errors', insertError);
reject(err);
}
resolve({
edit: edit.data,
});
},
);
});
}));
}
const start = async () => {
const { packageName = 'io.braiven.databank' } = process.env;
console.log(`Upload started for ${packageName} for version ${version}`);
const {
edit: { id: editId },
} = await startEdit();
const builds = ['app-armeabi-v7a-release.apk', 'app-x86-release.apk'];
const availableOutputFiles = fs.readdirSync('./android/app/build/outputs/apk/release');
console.log('Available builds are', availableOutputFiles.join(', '));
const uploadRes = await Promise.all(
builds.map(async (build) => {
try {
// dont try read a build thats not on the FS
if (!availableOutputFiles.includes(build)) { return; }
const apk = fs.readFileSync(`./android/app/build/outputs/apk/release/${build}`);
console.log(`Attempting Upload for cpu build ${build}`);
const {
data: {
versionCode: uploadedVersionCode,
binary: { sha256 },
},
} = await play.edits.apks.upload({
editId,
packageName,
media: {
mimeType: 'application/vnd.android.package-archive',
body: apk,
},
});
console.log(`Successfully uploaded version:${uploadedVersionCode}, build:${build}, sha256:${sha256}`);
return { versionCode: uploadedVersionCode, sha256 };
} catch (err) {
console.log(`Upload for ${build} failed`, err.message);
throw err;
}
}),
);
console.log(JSON.stringify({ uploadRes }, null, '\t'));
// Assign apk to beta track.
const { data: trackRes } = await play.edits.tracks.update({
editId,
track: 'beta',
packageName,
releases: [
{
versionCodes: [uploadRes[0].versionCode],
status: 'completed',
},
],
});
console.log('Placing edit on beta track', trackRes);
const { data: lists } = await play.edits.apks.list({ editId });
console.log('Edits apk list', lists.apks.map(apk => apk.versionCode));
// const { data: lists } = await play.edits.apks.u;
const { data: commit } = await play.edits.commit({
editId,
packageName,
});
console.log({ commit });
};
start().catch((err) => {
const errString = 'Upload failed with the following errors';
if (err.errors) {
console.error(`${errString} ${JSON.stringify(err.errors, null, '\t')}`);
} else {
console.error(`${errString} ${err}`);
}
process.exit(1);
});
@BransonGitomeh
Copy link
Author

has served my team for quite some time now, added some improvements to exit the process with clear logs incase of failures

@woodcockjosh
Copy link

Is this up to date? I'm getting invalid request errors. Also how is this different / better than the apkup package?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment