Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7406d2c7a1 | ||
|
|
623184c55d | ||
|
|
91c8f42cf7 | ||
|
|
e00bb8ce1e | ||
|
|
7f42989517 | ||
|
|
6fefbe4681 | ||
|
|
f1479e715c | ||
|
|
7b7381ed55 | ||
|
|
642ba59b8a | ||
|
|
dd8dcab872 | ||
|
|
cf500ad293 | ||
|
|
354ae8f121 | ||
|
|
b36eb787b2 | ||
|
|
c23d6291fa | ||
|
|
8eb5399590 | ||
|
|
51e4495788 | ||
|
|
15ee0081ab | ||
|
|
8c2ebfdb6e | ||
|
|
060a2efa0e |
42
.github/workflows/pr-validation.yml
vendored
Normal file
42
.github/workflows/pr-validation.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: PR Validation
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the "master" branch
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# Set up NPM and deps
|
||||
- name: Set up NPM
|
||||
run: npm ci
|
||||
|
||||
# Do a build
|
||||
- name: Run Build
|
||||
run: npm run build
|
||||
|
||||
# Check whether the compiled library changed
|
||||
- name: Check Build Updated
|
||||
uses: tj-actions/verify-changed-files@v9.2
|
||||
id: verify-built-lib
|
||||
with:
|
||||
files: lib/index.js
|
||||
|
||||
# If it did change, the authro forgot to build. Fail the action
|
||||
- name: Verify Build Updated
|
||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||
run: exit 1
|
||||
|
||||
- name: Run Tests
|
||||
run: npm test
|
||||
127
README.md
127
README.md
@@ -4,115 +4,54 @@ This action will help you upload an Android `.apk` or `.aab` (Android App Bundle
|
||||
|
||||
## Inputs
|
||||
|
||||
_You must provide one of either `serviceAccountJson` or `serviceAccountJsonPlainText`_
|
||||
|
||||
### `serviceAccountJson`
|
||||
|
||||
The service account json private key file to authorize the upload request
|
||||
|
||||
### `serviceAccountJsonPlainText`
|
||||
|
||||
The service account json in plain text, provided via a secret, etc.
|
||||
|
||||
### `packageName`
|
||||
|
||||
**Required:** The package name, or Application Id, of the app you are uploading
|
||||
|
||||
### `releaseFile`
|
||||
**DEPRECATED:** Please switch to using `releaseFiles` as this will be removed in the future
|
||||
The Android release file to upload (.apk or .aab)
|
||||
|
||||
### `releaseFiles`
|
||||
**CAVEAT:** Either this or `releaseFile` are required
|
||||
|
||||
The Android release file(s) to upload (.apk or .aab). Multiple files are separated by ','. Supports glob.
|
||||
|
||||
### `releaseName`
|
||||
|
||||
The release name. Not required to be unique. If not set, the name is generated from the APK's versionName. If the release contains multiple APKs, the name is generated from the date.
|
||||
|
||||
### `track`
|
||||
|
||||
**Required:** The track in which you want to assign the uploaded app.
|
||||
**Default:** `production`
|
||||
_Values:_ `alpha`, `beta`, `internal`, `production`, `internalsharing`
|
||||
|
||||
### `inAppUpdatePriority`
|
||||
|
||||
In-app update priority of the release. All newly added APKs in the release will be considered at this priority. Can take values in the range [0, 5], with 5 the highest priority.
|
||||
|
||||
**Default:** `0`
|
||||
_Values:_ `[0, 5]`
|
||||
|
||||
### `userFraction`
|
||||
|
||||
Portion of users who should get the staged version of the app. Accepts values between 0.0 and 1.0 (exclusive-exclusive). Omitting this value will execute a full rollout.
|
||||
|
||||
### `whatsNewDirectory`
|
||||
|
||||
The directory of localized whats new files to upload as the release notes. The files contained in the `whatsNewDirectory` MUST use the pattern `whatsnew-<LOCALE>` where `LOCALE` is using the [`BCP 47`](https://tools.ietf.org/html/bcp47) format, e.g.
|
||||
* `en-US` - English/America
|
||||
* `de-DE` - German/Germany
|
||||
* `ja-JP` - Japanese/Japan
|
||||
|
||||
and contain plain `utf8` encoded text with no extension on the file. The resulting directory in your project should look something like this:
|
||||
```
|
||||
projectDir/
|
||||
└── whatsNewDirectory/
|
||||
├── whatsnew-en-US
|
||||
├── whatsnew-de-DE
|
||||
└── whatsnew-ja-JP
|
||||
```
|
||||
where `whatsNewDirectory` is the path you pass to the action.
|
||||
|
||||
|
||||
### `mappingFile`
|
||||
|
||||
The mapping.txt file used to de-obfuscate your stack traces from crash reports
|
||||
| Input | Description | Value | Required |
|
||||
| --- | --- | --- | --- |
|
||||
| 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` | 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 |
|
||||
| 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. Defaults to `1.0` | `(0.0-1.0)` | false |
|
||||
| status | Release status. This can be set to `draft` to complete the release at some other time. Defaults to `completed` if targeting 100% rollout, else `inProgress` | One of `completed`, `inProgress`, `halted`, `draft` | false |
|
||||
| whatsNewDirectory | The directory of localized "whats new" files to upload as the release notes. The files contained in the `whatsNewDirectory` MUST use the pattern `whatsnew-<LOCALE>` where `LOCALE` is using the [`BCP 47`](https://tools.ietf.org/html/bcp47) format | A path to a valid `whatsNewDirectory` | 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` |
|
||||
| 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 |
|
||||
|
||||
## Outputs
|
||||
|
||||
### `internalSharingDownloadUrls`
|
||||
|
||||
A JSON list containing the download urls for every release file uploaded using the `track` `internalsharing`
|
||||
|
||||
### ENV VAR: `INTERNAL_SHARING_DOWNLOAD_URLS`
|
||||
|
||||
The environment variable that is set when using the `track` `internalsharing`
|
||||
|
||||
### `internalSharingDownloadUrl`
|
||||
|
||||
The download url for the last release file uploaded using the `track` `internalsharing`, useful when a single release file is uploaded
|
||||
|
||||
### ENV VAR: `INTERNAL_SHARING_DOWNLOAD_URL`
|
||||
|
||||
The environment variable that is set when using the `track` `internalsharing`
|
||||
| Output | Environment Variable | Description |
|
||||
| --- | --- | --- |
|
||||
| internalSharingDownloadUrls | INTERNAL_SHARING_DOWNLOAD_URLS | A JSON list containing the download urls for every release file uploaded using the `internalsharing` track |
|
||||
| internalSharingDownloadUrl | INTERNAL_SHARING_DOWNLOAD_URL | The download url for the last release file uploaded using the `internalsharing` track |
|
||||
|
||||
## Example usage
|
||||
|
||||
The below example publishes `MyApp` to Google Play, targetting 33% (`0.33`) of users with a priority of `2`.
|
||||
|
||||
```yaml
|
||||
uses: r0adkll/upload-google-play@v1
|
||||
with:
|
||||
serviceAccountJson: ${{ SERVICE_ACCOUNT_JSON }}
|
||||
serviceAccountJsonPlainText: ${{ SERVICE_ACCOUNT_JSON }}
|
||||
packageName: com.example.MyApp
|
||||
releaseFile: ${{ SIGNED_RELEASE_FILE}}
|
||||
releaseFiles: app/build/outputs/bundle/release/app-release.aab
|
||||
track: production
|
||||
status: inProgress
|
||||
inAppUpdatePriority: 2
|
||||
userFraction: 0.33
|
||||
whatsNewDirectory: distribution/whatsnew
|
||||
mappingFile: app/build/outputs/mapping/release/mapping.txt
|
||||
```
|
||||
|
||||
Using glob to get release files
|
||||
```yaml
|
||||
uses: r0adkll/upload-google-play@v1
|
||||
with:
|
||||
serviceAccountJson: ${{ SERVICE_ACCOUNT_JSON }}
|
||||
packageName: com.example.MyApp
|
||||
releaseFiles: app/build/outputs/bundle/release/*.aab
|
||||
track: production
|
||||
inAppUpdatePriority: 2
|
||||
userFraction: 0.33
|
||||
whatsNewDirectory: distribution/whatsnew
|
||||
mappingFile: app/build/outputs/mapping/release/mapping.txt
|
||||
The `whatsNewDirectory` in this example supplies changelogs for English, German and Japanese
|
||||
|
||||
```
|
||||
distribution/
|
||||
└─ whatsnew/
|
||||
├─ whatsnew-en-US
|
||||
├─ whatsnew-de-DE
|
||||
└─ whatsnew-ja-JP
|
||||
```
|
||||
|
||||
1
__tests__/whatsnew-ca
Normal file
1
__tests__/whatsnew-ca
Normal file
@@ -0,0 +1 @@
|
||||
test_changelog_file_catalan
|
||||
1
__tests__/whatsnew-es-ES
Normal file
1
__tests__/whatsnew-es-ES
Normal file
@@ -0,0 +1 @@
|
||||
test_changelog_file_spanish
|
||||
1
__tests__/whatsnew-et
Normal file
1
__tests__/whatsnew-et
Normal file
@@ -0,0 +1 @@
|
||||
test_changelog_file_estonian
|
||||
@@ -2,12 +2,12 @@ import * as process from 'process'
|
||||
import * as cp from 'child_process'
|
||||
import * as path from 'path'
|
||||
import {readLocalizedReleaseNotes} from "../src/whatsnew";
|
||||
import {androidpublisher_v3} from "googleapis";
|
||||
import {androidpublisher_v3} from "@googleapis/androidpublisher";
|
||||
import LocalizedText = androidpublisher_v3.Schema$LocalizedText;
|
||||
|
||||
test("read localized whatsnew files", async () => {
|
||||
let texts = await readLocalizedReleaseNotes("./__tests__");
|
||||
expect(texts).toHaveLength(3);
|
||||
expect(texts).toHaveLength(6);
|
||||
expect(texts).toContainEqual({
|
||||
language: "en-US",
|
||||
text: "test_changelog_file"
|
||||
@@ -20,4 +20,16 @@ test("read localized whatsnew files", async () => {
|
||||
language: "ja-JP",
|
||||
text: "test_changelog_file_japanese"
|
||||
});
|
||||
expect(texts).toContainEqual({
|
||||
language: "ca",
|
||||
text: "test_changelog_file_catalan"
|
||||
});
|
||||
expect(texts).toContainEqual({
|
||||
language: "es-ES",
|
||||
text: "test_changelog_file_spanish"
|
||||
});
|
||||
expect(texts).toContainEqual({
|
||||
language: "et",
|
||||
text: "test_changelog_file_estonian"
|
||||
});
|
||||
});
|
||||
12
action.yml
12
action.yml
@@ -34,15 +34,25 @@ inputs:
|
||||
userFraction:
|
||||
description: "Portion of users who should get the staged version of the app. Accepts values between 0.0 and 1.0 (exclusive-exclusive)."
|
||||
required: false
|
||||
status:
|
||||
description: "Release status. This can be set to 'draft' to complete the release at some other time."
|
||||
required: false
|
||||
whatsNewDirectory:
|
||||
description: "The directory of localized whats new files"
|
||||
required: false
|
||||
mappingFile:
|
||||
description: "The mapping.txt file used to de-obfuscate your stack traces from crash reports"
|
||||
required: false
|
||||
changesNotSentForReview:
|
||||
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
|
||||
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:
|
||||
internalSharingDownloadUrl:
|
||||
description: "The internal app sharing download url if track was 'internalsharing'"
|
||||
runs:
|
||||
using: 'node12'
|
||||
using: 'node16'
|
||||
main: 'lib/index.js'
|
||||
|
||||
41
lib/index.js
41
lib/index.js
File diff suppressed because one or more lines are too long
13310
package-lock.json
generated
13310
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "upload-google-play",
|
||||
"version": "1.0.13",
|
||||
"version": "1.0.17",
|
||||
"private": true,
|
||||
"description": "Upload an Android release (.apk or .aab) to Google Play Console",
|
||||
"main": "lib/index.js",
|
||||
@@ -20,17 +20,16 @@
|
||||
"author": "r0adkll",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.6",
|
||||
"fast-glob": "^3.2.5",
|
||||
"googleapis": "^68.0.0"
|
||||
"@actions/core": "^1.9.1",
|
||||
"@googleapis/androidpublisher": "^3.0.0",
|
||||
"fast-glob": "^3.2.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.22",
|
||||
"@types/node": "^14.14.37",
|
||||
"@vercel/ncc": "^0.27.0",
|
||||
"jest": "^26.6.3",
|
||||
"jest-circus": "^26.6.3",
|
||||
"ts-jest": "^26.5.4",
|
||||
"typescript": "^4.2.3"
|
||||
"@types/jest": "^29.0.0",
|
||||
"@types/node": "^18.7.16",
|
||||
"@vercel/ncc": "^0.34.0",
|
||||
"jest": "^29.0.3",
|
||||
"ts-jest": "^29.0.0",
|
||||
"typescript": "^4.8.3"
|
||||
}
|
||||
}
|
||||
|
||||
86
src/edits.ts
86
src/edits.ts
@@ -1,25 +1,23 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as fs from "fs";
|
||||
import {readFileSync} from "fs";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
import {google} from 'googleapis';
|
||||
import {androidpublisher_v3} from "googleapis";
|
||||
import * as google from '@googleapis/androidpublisher';
|
||||
import { androidpublisher_v3 } from "@googleapis/androidpublisher";
|
||||
|
||||
import AndroidPublisher = androidpublisher_v3.Androidpublisher;
|
||||
import AppEdit = androidpublisher_v3.Schema$AppEdit;
|
||||
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 {JWT} from "google-auth-library/build/src/auth/jwtclient";
|
||||
import {UserRefreshClient} from "google-auth-library/build/src/auth/refreshclient";
|
||||
import {readLocalizedReleaseNotes} from "./whatsnew";
|
||||
import { Compute } from "google-auth-library/build/src/auth/computeclient";
|
||||
import { JSONClient } from "google-auth-library/build/src/auth/googleauth"
|
||||
import { readLocalizedReleaseNotes } from "./whatsnew";
|
||||
|
||||
const androidPublisher: AndroidPublisher = google.androidpublisher('v3');
|
||||
|
||||
export interface EditOptions {
|
||||
auth: Compute | JWT | UserRefreshClient;
|
||||
auth: Compute | JSONClient;
|
||||
applicationId: string;
|
||||
track: string;
|
||||
inAppUpdatePriority: number;
|
||||
@@ -27,6 +25,9 @@ export interface EditOptions {
|
||||
whatsNewDir?: string;
|
||||
mappingFile?: string;
|
||||
name?: string;
|
||||
status?: string;
|
||||
changesNotSentForReview?: boolean;
|
||||
existingEditId?: string;
|
||||
}
|
||||
|
||||
export async function uploadToPlayStore(options: EditOptions, releaseFiles: string[]): Promise<string | undefined> {
|
||||
@@ -50,14 +51,14 @@ export async function uploadToPlayStore(options: EditOptions, releaseFiles: stri
|
||||
} else {
|
||||
// Create a new Edit
|
||||
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,
|
||||
packageName: options.applicationId
|
||||
});
|
||||
})).data.id
|
||||
|
||||
// Validate the given track
|
||||
core.info(`Validating track '${options.track}'`)
|
||||
await validateSelectedTrack(appEdit.data, options).catch(reason => {
|
||||
await validateSelectedTrack(appEditId!, options).catch(reason => {
|
||||
core.setFailed(reason);
|
||||
return Promise.reject(reason);
|
||||
});
|
||||
@@ -66,7 +67,7 @@ export async function uploadToPlayStore(options: EditOptions, releaseFiles: stri
|
||||
const versionCodes = new Array<number>();
|
||||
for (const releaseFile of releaseFiles) {
|
||||
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);
|
||||
return Promise.reject(reason);
|
||||
});
|
||||
@@ -76,15 +77,16 @@ export async function uploadToPlayStore(options: EditOptions, releaseFiles: stri
|
||||
|
||||
// Add the uploaded artifacts to the Edit 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}`);
|
||||
|
||||
// Commit the pending Edit
|
||||
core.info(`Committing the Edit`)
|
||||
const res = await androidPublisher.edits.commit({
|
||||
auth: options.auth,
|
||||
editId: appEdit.data.id!,
|
||||
packageName: options.applicationId
|
||||
editId: appEditId!,
|
||||
packageName: options.applicationId,
|
||||
changesNotSentForReview: options.changesNotSentForReview
|
||||
});
|
||||
|
||||
// Simple check to see whether commit was successful
|
||||
@@ -116,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')) {
|
||||
const apk = await uploadApk(appEdit, options, releaseFile);
|
||||
await uploadMappingFile(appEdit, apk.versionCode!, options);
|
||||
const apk = await uploadApk(appEditId, options, releaseFile);
|
||||
await uploadMappingFile(appEditId, apk.versionCode!, options);
|
||||
return Promise.resolve(apk.versionCode);
|
||||
} else if (releaseFile.endsWith('.aab')) {
|
||||
const bundle = await uploadBundle(appEdit, options, releaseFile);
|
||||
await uploadMappingFile(appEdit, bundle.versionCode!, options);
|
||||
const bundle = await uploadBundle(appEditId, options, releaseFile);
|
||||
await uploadMappingFile(appEditId, bundle.versionCode!, options);
|
||||
return Promise.resolve(bundle.versionCode);
|
||||
} else {
|
||||
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({
|
||||
auth: options.auth,
|
||||
editId: appEdit.id!,
|
||||
editId: appEditId,
|
||||
packageName: options.applicationId
|
||||
});
|
||||
const allTracks = res.data.tracks;
|
||||
@@ -142,19 +144,21 @@ async function validateSelectedTrack(appEdit: AppEdit, options: EditOptions): Pr
|
||||
}
|
||||
}
|
||||
|
||||
async function addReleasesToTrack(appEdit: AppEdit, options: EditOptions, versionCodes: number[]): Promise<Track> {
|
||||
let status: string;
|
||||
if (options.userFraction != undefined) {
|
||||
status = 'inProgress';
|
||||
} else {
|
||||
status = 'completed';
|
||||
async function addReleasesToTrack(appEditId: string, options: EditOptions, versionCodes: number[]): Promise<Track> {
|
||||
let status: string | undefined = options.status;
|
||||
if (!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})`);
|
||||
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
|
||||
.update({
|
||||
auth: options.auth,
|
||||
editId: appEdit.id!,
|
||||
editId: appEditId,
|
||||
packageName: options.applicationId,
|
||||
track: options.track,
|
||||
requestBody: {
|
||||
@@ -175,15 +179,15 @@ async function addReleasesToTrack(appEdit: AppEdit, options: EditOptions, versio
|
||||
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) {
|
||||
const mapping = readFileSync(options.mappingFile, 'utf-8');
|
||||
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({
|
||||
auth: options.auth,
|
||||
packageName: options.applicationId,
|
||||
editId: appEdit.id!,
|
||||
editId: appEditId,
|
||||
apkVersionCode: versionCode,
|
||||
deobfuscationFileType: 'proguard',
|
||||
media: {
|
||||
@@ -225,13 +229,13 @@ async function internalSharingUploadBundle(options: EditOptions, bundleReleaseFi
|
||||
return res.data;
|
||||
}
|
||||
|
||||
async function uploadApk(appEdit: AppEdit, options: EditOptions, apkReleaseFile: string): Promise<Apk> {
|
||||
core.debug(`[${appEdit.id}, packageName=${options.applicationId}]: Uploading APK @ ${apkReleaseFile}`);
|
||||
async function uploadApk(appEditId: string, options: EditOptions, apkReleaseFile: string): Promise<Apk> {
|
||||
core.debug(`[${appEditId}, packageName=${options.applicationId}]: Uploading APK @ ${apkReleaseFile}`);
|
||||
|
||||
const res = await androidPublisher.edits.apks.upload({
|
||||
auth: options.auth,
|
||||
packageName: options.applicationId,
|
||||
editId: appEdit.id!,
|
||||
editId: appEditId,
|
||||
media: {
|
||||
mimeType: 'application/vnd.android.package-archive',
|
||||
body: fs.createReadStream(apkReleaseFile)
|
||||
@@ -241,12 +245,12 @@ async function uploadApk(appEdit: AppEdit, options: EditOptions, apkReleaseFile:
|
||||
return res.data
|
||||
}
|
||||
|
||||
async function uploadBundle(appEdit: AppEdit, options: EditOptions, bundleReleaseFile: string): Promise<Bundle> {
|
||||
core.debug(`[${appEdit.id}, packageName=${options.applicationId}]: Uploading App Bundle @ ${bundleReleaseFile}`);
|
||||
async function uploadBundle(appEditId: string, options: EditOptions, bundleReleaseFile: string): Promise<Bundle> {
|
||||
core.debug(`[${appEditId}, packageName=${options.applicationId}]: Uploading App Bundle @ ${bundleReleaseFile}`);
|
||||
const res = await androidPublisher.edits.bundles.upload({
|
||||
auth: options.auth,
|
||||
packageName: options.applicationId,
|
||||
editId: appEdit.id!,
|
||||
editId: appEditId,
|
||||
media: {
|
||||
mimeType: 'application/octet-stream',
|
||||
body: fs.createReadStream(bundleReleaseFile)
|
||||
@@ -254,4 +258,4 @@ async function uploadBundle(appEdit: AppEdit, options: EditOptions, bundleReleas
|
||||
});
|
||||
|
||||
return res.data
|
||||
}
|
||||
}
|
||||
|
||||
20
src/main.ts
20
src/main.ts
@@ -1,8 +1,8 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as fs from "fs";
|
||||
import fg from "fast-glob";
|
||||
import { uploadToPlayStore } from "./edits";
|
||||
const {google} = require('googleapis');
|
||||
import {uploadToPlayStore} from "./edits";
|
||||
import * as google from '@googleapis/androidpublisher';
|
||||
|
||||
const auth = new google.auth.GoogleAuth({
|
||||
scopes: ['https://www.googleapis.com/auth/androidpublisher']
|
||||
@@ -21,8 +21,11 @@ async function run() {
|
||||
const track = core.getInput('track', { required: true });
|
||||
const inAppUpdatePriority = core.getInput('inAppUpdatePriority', { required: false });
|
||||
const userFraction = core.getInput('userFraction', { required: false });
|
||||
const status = core.getInput('status', { required: false });
|
||||
const whatsNewDir = core.getInput('whatsNewDirectory', { required: false });
|
||||
const mappingFile = core.getInput('mappingFile', { required: false });
|
||||
const changesNotSentForReview = core.getInput('changesNotSentForReview', { required: false }) == 'true';
|
||||
const existingEditId = core.getInput('existingEditId')
|
||||
|
||||
// Validate that we have a service account json in some format
|
||||
if (!serviceAccountJson && !serviceAccountJsonRaw) {
|
||||
@@ -112,14 +115,23 @@ async function run() {
|
||||
track: track,
|
||||
inAppUpdatePriority: inAppUpdatePriorityInt || 0,
|
||||
userFraction: userFractionFloat,
|
||||
status: status,
|
||||
whatsNewDir: whatsNewDir,
|
||||
mappingFile: mappingFile,
|
||||
name: releaseName
|
||||
name: releaseName,
|
||||
changesNotSentForReview: changesNotSentForReview,
|
||||
existingEditId: existingEditId
|
||||
}, validatedReleaseFiles);
|
||||
|
||||
console.log(`Finished uploading to the Play Store: ${result}`)
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
core.setFailed(error.message)
|
||||
} finally {
|
||||
if (core.getInput('serviceAccountJsonPlainText', { required: false})) {
|
||||
// Cleanup our auth file that we created.
|
||||
core.debug('Cleaning up service account json file');
|
||||
fs.unlinkSync('./serviceAccountJson.json');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,15 @@ import * as core from '@actions/core';
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import {readFileSync} from "fs";
|
||||
import {androidpublisher_v3} from "googleapis";
|
||||
import {androidpublisher_v3} from "@googleapis/androidpublisher";
|
||||
import LocalizedText = androidpublisher_v3.Schema$LocalizedText;
|
||||
|
||||
export async function readLocalizedReleaseNotes(whatsNewDir: string | undefined): Promise<LocalizedText[] | undefined> {
|
||||
core.debug(`Executing readLocalizedReleaseNotes`);
|
||||
if (whatsNewDir != undefined && whatsNewDir.length > 0) {
|
||||
const releaseNotes = fs.readdirSync(whatsNewDir)
|
||||
.filter(value => /whatsnew-.*-.{2,3}\b/.test(value));
|
||||
const pattern = /whatsnew-(?<local>.*-.*)/;
|
||||
.filter(value => /whatsnew-((.*-.*)|(.*))\b/.test(value));
|
||||
const pattern = /whatsnew-(?<local>(.*-.*)|(.*))/;
|
||||
|
||||
let localizedReleaseNotes: LocalizedText[] = [];
|
||||
|
||||
@@ -17,7 +18,7 @@ export async function readLocalizedReleaseNotes(whatsNewDir: string | undefined)
|
||||
releaseNotes.forEach(value => {
|
||||
const matches = value.match(pattern);
|
||||
core.debug(`Matches for ${value} = ${matches}`);
|
||||
if (matches != undefined && matches.length == 2) {
|
||||
if (matches != undefined && matches.length == 4) {
|
||||
const lang = matches[1];
|
||||
const filePath = path.join(whatsNewDir, value);
|
||||
const content = readFileSync(filePath, 'utf-8');
|
||||
|
||||
Reference in New Issue
Block a user