Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
935ef9c68b | ||
|
|
84ffa89623 | ||
|
|
b6837fa35c | ||
|
|
7c9dfbb03e | ||
|
|
9cf8fd30c9 | ||
|
|
101c2cddf9 | ||
|
|
d17e3fc745 | ||
|
|
70c7397e86 | ||
|
|
853233422d |
2
.github/workflows/pr-validation.yml
vendored
2
.github/workflows/pr-validation.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
|
||||
# Check whether the compiled library changed
|
||||
- name: Check Build Updated
|
||||
uses: tj-actions/verify-changed-files@v9.2
|
||||
uses: tj-actions/verify-changed-files@v17
|
||||
id: verify-built-lib
|
||||
with:
|
||||
files: lib/index.js
|
||||
|
||||
16
README.md
16
README.md
@@ -9,7 +9,7 @@ This action will help you upload an Android `.apk` or `.aab` (Android App Bundle
|
||||
| releaseFiles | The Android release file(s) to upload (.apk or .aab) | Comma-separated paths. Supports glob via [fast-glob](https://github.com/mrmlnc/fast-glob) | true |
|
||||
| serviceAccountJsonPlainText | The service account json in plain text, provided via a secret, etc | The contents of your `service-account.json` | true (or serviceAccountJson) |
|
||||
| packageName | The package name, or Application Id, of the app you are uploading | A valid package name, e.g. `com.example.myapp`. The packageName must already exist in the play console account, so make sure you upload a manual apk or aab first through the console | true |
|
||||
| track | The track in which you want to assign the uploaded app. Defaults to `production` | One of `production`, `beta`, `alpha`, `internalsharing`, `internal`, or a custom track name (case sensitive) | true |
|
||||
| track | The track in which you want to assign the uploaded app. Defaults to `production` | See [APKs and Tracks](https://developers.google.com/android-publisher/tracks#adding_and_modifying_apks) documentation for calculating your target track name | true |
|
||||
| releaseName | The release name. Not required to be unique. Default is configured by Google Play Console | A user-friendly update name, e.g. `v1.0.0` | false |
|
||||
| inAppUpdatePriority | In-app update priority of the release. All newly added APKs in the release will be considered at this priority. Defaults to `0` | `[0-5]`, where `5` is the highest priority | false |
|
||||
| userFraction | Percentage of users who should get the staged version of the app. | `(0.0-1.0)` | false |
|
||||
@@ -48,6 +48,20 @@ with:
|
||||
debugSymbols: app/intermediates/merged_native_libs/release/out/lib
|
||||
```
|
||||
|
||||
## Configure service account
|
||||
1. Configure service account in Google Cloud Platform
|
||||
1. Navigate to https://cloud.google.com/gcp
|
||||
1. Open IAM and admin > Service accounts > Create service account
|
||||
1. Pick a name and add appropriate permissions (e.g. 'owner)
|
||||
1. Open the newly created service account, click on `keys` tab and add a new key, JSON type
|
||||
1. When successful, a JSON file will be automatically downloaded on your machine
|
||||
1. Store the content of this file to your GitHub secrets, e.g. SERVICE_ACCOUNT_JSON.
|
||||
1. Add user to Google Play Console
|
||||
1. Open https://play.google.com/console and pick your developer account
|
||||
1. Open Users and permissions
|
||||
1. Click invite new user and add the email of the service account created in the previous step
|
||||
1. Grant permissions to the app that you want the service account to deploy in `app permissions`
|
||||
|
||||
## FAQ
|
||||
### I get the error "Package not found"
|
||||
Make sure you upload an apk or aab manually first by creating a release through the play console.
|
||||
|
||||
109
__tests__/main.test.ts
Normal file
109
__tests__/main.test.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import {RunOptions, RunTarget} from 'github-action-ts-run-api';
|
||||
|
||||
const target = RunTarget.mainJs('action.yml');
|
||||
|
||||
async function expectRunInitiatesUpload(options: RunOptions) {
|
||||
const result = await target.run(options)
|
||||
expect(result.commands.errors.length).toBe(1)
|
||||
expect(result.commands.errors).toContainEqual("The incoming JSON object does not contain a client_email field")
|
||||
}
|
||||
|
||||
test("correct bare minimum inputs", async () => {
|
||||
const minimumOptions = RunOptions.create()
|
||||
.setInputs({
|
||||
serviceAccountJsonPlainText: "{}",
|
||||
packageName: "com.package.name",
|
||||
releaseFiles: "./__tests__/releasefiles/*.aab",
|
||||
track: "production",
|
||||
})
|
||||
await expectRunInitiatesUpload(minimumOptions)
|
||||
})
|
||||
|
||||
test("correct inputs for complete rollout", async () => {
|
||||
// Run with the bare minimum
|
||||
const minimumOptions = RunOptions.create()
|
||||
.setInputs({
|
||||
serviceAccountJsonPlainText: "{}",
|
||||
packageName: "com.package.name",
|
||||
releaseFiles: "./__tests__/releasefiles/*.aab",
|
||||
track: "production",
|
||||
status: "completed",
|
||||
})
|
||||
await expectRunInitiatesUpload(minimumOptions)
|
||||
|
||||
// Test with optional extras
|
||||
const extraOptions = RunOptions.create()
|
||||
.setInputs({
|
||||
serviceAccountJsonPlainText: "{}",
|
||||
packageName: "com.package.name",
|
||||
releaseFiles: "./__tests__/releasefiles/*.aab",
|
||||
releaseName: "Release name",
|
||||
track: "production",
|
||||
inAppUpdatePriority: "3",
|
||||
status: "completed",
|
||||
whatsNewDir: "./__tests__/whatsnew",
|
||||
changesNotSentForReview: "true",
|
||||
existingEditId: "123"
|
||||
})
|
||||
await expectRunInitiatesUpload(extraOptions)
|
||||
})
|
||||
|
||||
test("correct inputs for inProgress rollout", async () => {
|
||||
// Run with the bare minimum
|
||||
const minimumOptions = RunOptions.create()
|
||||
.setInputs({
|
||||
serviceAccountJsonPlainText: "{}",
|
||||
packageName: "com.package.name",
|
||||
releaseFiles: "./__tests__/releasefiles/*.aab",
|
||||
track: "production",
|
||||
userFraction: "0.99",
|
||||
status: "inProgress",
|
||||
})
|
||||
await expectRunInitiatesUpload(minimumOptions)
|
||||
|
||||
// Test with optional extras
|
||||
const extraOptions = RunOptions.create()
|
||||
.setInputs({
|
||||
serviceAccountJsonPlainText: "{}",
|
||||
packageName: "com.package.name",
|
||||
releaseFiles: "./__tests__/releasefiles/*.aab",
|
||||
releaseName: "Release name",
|
||||
track: "production",
|
||||
inAppUpdatePriority: "3",
|
||||
userFraction: "0.5",
|
||||
status: "inProgress",
|
||||
whatsNewDir: "./__tests__/whatsnew",
|
||||
changesNotSentForReview: "true",
|
||||
existingEditId: "123"
|
||||
})
|
||||
await expectRunInitiatesUpload(extraOptions)
|
||||
})
|
||||
|
||||
test("correct inputs for draft rollout", async () => {
|
||||
// Run with the bare minimum
|
||||
const minimumOptions = RunOptions.create()
|
||||
.setInputs({
|
||||
serviceAccountJsonPlainText: "{}",
|
||||
packageName: "com.package.name",
|
||||
releaseFiles: "./__tests__/releasefiles/*.aab",
|
||||
track: "production",
|
||||
status: "draft",
|
||||
})
|
||||
await expectRunInitiatesUpload(minimumOptions)
|
||||
|
||||
// Test with optional extras
|
||||
const extraOptions = RunOptions.create()
|
||||
.setInputs({
|
||||
serviceAccountJsonPlainText: "{}",
|
||||
packageName: "com.package.name",
|
||||
releaseFiles: "./__tests__/releasefiles/*.aab",
|
||||
releaseName: "Release name",
|
||||
track: "production",
|
||||
inAppUpdatePriority: "3",
|
||||
status: "draft",
|
||||
whatsNewDir: "./__tests__/whatsnew",
|
||||
changesNotSentForReview: "true",
|
||||
existingEditId: "123"
|
||||
})
|
||||
await expectRunInitiatesUpload(extraOptions)
|
||||
})
|
||||
@@ -58,5 +58,5 @@ outputs:
|
||||
internalSharingDownloadUrl:
|
||||
description: "The internal app sharing download url if track was 'internalsharing'"
|
||||
runs:
|
||||
using: 'node16'
|
||||
using: 'node20'
|
||||
main: 'lib/index.js'
|
||||
|
||||
16
lib/index.js
16
lib/index.js
File diff suppressed because one or more lines are too long
3623
package-lock.json
generated
3623
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "upload-google-play",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.3",
|
||||
"private": true,
|
||||
"description": "Upload an Android release (.apk or .aab) to Google Play Console",
|
||||
"main": "lib/index.js",
|
||||
@@ -20,21 +20,22 @@
|
||||
"author": "r0adkll",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@googleapis/androidpublisher": "^4.1.0",
|
||||
"fast-glob": "^3.2.12",
|
||||
"jszip": "^3.10.1"
|
||||
"@actions/core": "^1.10.1",
|
||||
"@googleapis/androidpublisher": "^12.1.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"jszip": "^3.10.1",
|
||||
"p-timeout": "^6.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.2.0",
|
||||
"@types/node": "^18.11.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
||||
"@typescript-eslint/parser": "^5.40.1",
|
||||
"@vercel/ncc": "^0.36.1",
|
||||
"eslint": "^8.26.0",
|
||||
"github-action-ts-run-api": "^2.2.2",
|
||||
"jest": "^29.2.1",
|
||||
"ts-jest": "^29.0.3",
|
||||
"typescript": "^4.8.4"
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.11.17",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"eslint": "^8.56.0",
|
||||
"github-action-ts-run-api": "^3.0.4",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.2",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,14 +13,13 @@ import Apk = androidpublisher_v3.Schema$Apk;
|
||||
import Bundle = androidpublisher_v3.Schema$Bundle;
|
||||
import Track = androidpublisher_v3.Schema$Track;
|
||||
import InternalAppSharingArtifact = androidpublisher_v3.Schema$InternalAppSharingArtifact;
|
||||
import { Compute } from "google-auth-library/build/src/auth/computeclient";
|
||||
import { JSONClient } from "google-auth-library/build/src/auth/googleauth"
|
||||
import { GoogleAuth } from "google-auth-library/build/src/auth/googleauth"
|
||||
import { readLocalizedReleaseNotes } from "./whatsnew";
|
||||
|
||||
const androidPublisher: AndroidPublisher = google.androidpublisher('v3');
|
||||
|
||||
export interface EditOptions {
|
||||
auth: Compute | JSONClient;
|
||||
auth: GoogleAuth;
|
||||
applicationId: string;
|
||||
track: string;
|
||||
inAppUpdatePriority: number;
|
||||
@@ -52,10 +51,8 @@ export async function runUpload(
|
||||
scopes: ['https://www.googleapis.com/auth/androidpublisher']
|
||||
});
|
||||
|
||||
const authClient = await auth.getClient();
|
||||
|
||||
const result = await uploadToPlayStore({
|
||||
auth: authClient,
|
||||
auth: auth,
|
||||
applicationId: packageName,
|
||||
track: track,
|
||||
inAppUpdatePriority: inAppUpdatePriority || 0,
|
||||
|
||||
26
src/main.ts
26
src/main.ts
@@ -1,8 +1,9 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as fs from "fs";
|
||||
import { runUpload } from "./edits";
|
||||
import * as core from '@actions/core'
|
||||
import * as fs from "fs"
|
||||
import { runUpload } from "./edits"
|
||||
import { validateInAppUpdatePriority, validateReleaseFiles, validateStatus, validateUserFraction } from "./input-validation"
|
||||
import { unlink, writeFile } from 'fs/promises';
|
||||
import { unlink, writeFile } from 'fs/promises'
|
||||
import pTimeout from 'p-timeout'
|
||||
|
||||
export async function run() {
|
||||
try {
|
||||
@@ -12,7 +13,7 @@ export async function run() {
|
||||
const releaseFile = core.getInput('releaseFile', { required: false });
|
||||
const releaseFiles = core.getInput('releaseFiles', { required: false })
|
||||
?.split(',')
|
||||
?.filter(x => x !== '') || [];
|
||||
?.filter(x => x !== '');
|
||||
const releaseName = core.getInput('releaseName', { required: false });
|
||||
const track = core.getInput('track', { required: true });
|
||||
const inAppUpdatePriority = core.getInput('inAppUpdatePriority', { required: false });
|
||||
@@ -65,8 +66,7 @@ export async function run() {
|
||||
core.warning(`Unable to find 'debugSymbols' @ ${debugSymbols}`);
|
||||
}
|
||||
|
||||
await withTimeout(
|
||||
3.6e+6, // 60 minute timeout
|
||||
await pTimeout(
|
||||
runUpload(
|
||||
packageName,
|
||||
track,
|
||||
@@ -80,7 +80,10 @@ export async function run() {
|
||||
existingEditId,
|
||||
status,
|
||||
validatedReleaseFiles
|
||||
)
|
||||
),
|
||||
{
|
||||
milliseconds: 3.6e+6
|
||||
}
|
||||
)
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
@@ -119,11 +122,4 @@ async function validateServiceAccountJson(serviceAccountJsonRaw: string | undefi
|
||||
}
|
||||
}
|
||||
|
||||
async function withTimeout<T>(timeoutMillis: number, promise: Promise<T>): Promise<T> {
|
||||
return Promise.race([
|
||||
new Promise<never>((_, reject) => setTimeout(reject, timeoutMillis)),
|
||||
promise
|
||||
])
|
||||
}
|
||||
|
||||
void run();
|
||||
|
||||
Reference in New Issue
Block a user