9 Commits

Author SHA1 Message Date
Jack Boswell
935ef9c68b Update dependencies & target node20 (#212)
* Update dependencies & target Node 20

* Bump version
2024-02-10 00:01:23 +13:00
dependabot[bot]
84ffa89623 Bump tj-actions/verify-changed-files in /.github/workflows (#206)
Bumps [tj-actions/verify-changed-files](https://github.com/tj-actions/verify-changed-files) from 9.2 to 17.
- [Release notes](https://github.com/tj-actions/verify-changed-files/releases)
- [Changelog](https://github.com/tj-actions/verify-changed-files/blob/main/HISTORY.md)
- [Commits](https://github.com/tj-actions/verify-changed-files/compare/v9.2...v17)

---
updated-dependencies:
- dependency-name: tj-actions/verify-changed-files
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-03 11:18:09 +13:00
Emil Tabakov
b6837fa35c Add instructions to configure service account in README (#201) 2023-11-13 22:57:13 +13:00
dependabot[bot]
7c9dfbb03e Bump @babel/traverse from 7.19.6 to 7.23.2 (#198)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.19.6 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-19 11:57:55 +13:00
Jack Boswell
9cf8fd30c9 Update documentation to link to Google's track name information (#191) 2023-09-19 16:44:00 +12:00
Jack Boswell
101c2cddf9 Update all dependencies and bump version (#192) 2023-09-19 16:43:48 +12:00
dependabot[bot]
d17e3fc745 Bump word-wrap from 1.2.3 to 1.2.4 (#184)
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-19 10:14:15 +12:00
Jack Boswell
70c7397e86 Fix deprecated releaseFile input not being considered (#174) 2023-02-11 12:10:44 +13:00
Jack Boswell
853233422d Fix a bug where our new timeout caused the action to stay "alive" (#173) 2023-02-11 12:06:30 +13:00
9 changed files with 2057 additions and 1777 deletions

View File

@@ -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

View File

@@ -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
View 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)
})

View File

@@ -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'

File diff suppressed because one or more lines are too long

3623
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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"
}
}

View File

@@ -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,

View File

@@ -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();