From 5ce79bd2b870fa280f42ace4ea2056791b7b9125 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 24 Aug 2022 14:17:31 +0100 Subject: [PATCH 1/6] nightly task to automatically PR and merge changes from main into release --- .github/workflows/nightly.yml | 31 ++++++ .gitignore | 3 +- tools/beta-release/app.js | 138 +++++++++++++++++++++++++++ tools/beta-release/package-lock.json | 13 +++ tools/beta-release/package.json | 8 ++ 5 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/nightly.yml create mode 100644 tools/beta-release/app.js create mode 100644 tools/beta-release/package-lock.json create mode 100644 tools/beta-release/package.json diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000..945a0de --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,31 @@ +name: Nightly + +on: + schedule: + - cron: '0 19 * * *' + +jobs: + check-develop-beta-changes: + name: Check if develop is ahead of beta release + runs-on: ubuntu-latest + + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-node@v3 + with: + node-version: + 16 + - run: npm ci + working-directory: ./tools/beta-release/ + + - uses: actions/github-script@v6 + with: + script: | + const { script } = await import('${{ github.workspace }}/tools/beta-release/app.js') + await script({github, context, core}) + diff --git a/.gitignore b/.gitignore index 3b76762..9257dec 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ .externalNativeBuild .cxx local.properties -/benchmark-out \ No newline at end of file +/benchmark-out +**/node_modules \ No newline at end of file diff --git a/tools/beta-release/app.js b/tools/beta-release/app.js new file mode 100644 index 0000000..b0ebf35 --- /dev/null +++ b/tools/beta-release/app.js @@ -0,0 +1,138 @@ +const config = { + owner: "ouchadam", + repo: "small-talk", + pathToVersionFile: "version.json", + rcBranchesFrom: "main", + rcMergesTo: "release", +} + +const rcBranchName = "release-candidate" + +export const script = async ({ github, context, core }) => { + console.log("script start") + if (await doesNotHaveInProgressRelease(github) && await isWorkingBranchAhead(github)) { + await startRelease(github) + } else { + console.log(`Release skipped due to being behind`) + } + return "" +} + +const isWorkingBranchAhead = async (github) => { + const result = await github.rest.repos.compareCommitsWithBasehead({ + owner: config.owner, + repo: config.repo, + basehead: `${config.rcMergesTo}...${config.rcBranchesFrom}`, + per_page: 1, + page: 1, + }) + return result.data.status === "ahead" +} + +const doesNotHaveInProgressRelease = async (github) => { + const releasePrs = await github.rest.pulls.list({ + owner: config.owner, + repo: config.repo, + state: "open", + base: config.rcMergesTo + }) + + const syncPrs = await github.rest.pulls.list({ + owner: config.owner, + repo: config.repo, + state: "open", + base: config.rcBranchesFrom, + head: `${config.owner}:${config.rcMergesTo}` + }) + + return releasePrs.data.length === 0 && syncPrs.data.length === 0 +} + +const startRelease = async (github) => { + console.log(`creating release candidate from head of ${config.rcBranchesFrom}`) + + await createBranch(github, "release-candidate", config.rcBranchesFrom) + await incrementVersionFile(github, rcBranchName) + + const createdPr = await github.rest.pulls.create({ + owner: config.owner, + repo: config.repo, + title: "[Auto] Release Candidate", + head: rcBranchName, + base: config.rcMergesTo, + body: "todo", + }) + + github.graphql( + ` + mutation ($pullRequestId: ID!, $mergeMethod: PullRequestMergeMethod!) { + enablePullRequestAutoMerge(input: { + pullRequestId: $pullRequestId, + mergeMethod: $mergeMethod + }) { + pullRequest { + autoMergeRequest { + enabledAt + enabledBy { + login + } + } + } + } + } + `, + { + pullRequestId: createdPr.data.node_id, + mergeMethod: "MERGE" + } + ) +} + +const createBranch = async (github, branchName, fromBranch) => { + const mainRef = await github.rest.git.getRef({ + owner: config.owner, + repo: config.repo, + ref: `heads/${fromBranch}`, + }) + + await github.rest.git.createRef({ + owner: config.owner, + repo: config.repo, + ref: `refs/heads/${branchName}`, + sha: mainRef.data.object.sha, + }) +} + +const incrementVersionFile = async (github, branchName) => { + const versionFile = await readVersionFile(github) + + const updatedVersionFile = { + ...versionFile.content, + code: versionFile.content.code + 1, + } + const encodedContentUpdate = Buffer.from(JSON.stringify(updatedVersionFile, null, 2)).toString('base64') + await github.rest.repos.createOrUpdateFileContents({ + owner: config.owner, + repo: config.repo, + content: encodedContentUpdate, + path: config.pathToVersionFile, + sha: versionFile.sha, + branch: branchName, + message: "updating version for release", + }) +} + +const readVersionFile = async (github) => { + const result = await github.rest.repos.getContent({ + owner: config.owner, + repo: config.repo, + path: config.pathToVersionFile, + ref: config.rcBranchesFrom, + }) + + const content = Buffer.from(result.data.content, result.data.encoding).toString() + return { + content: JSON.parse(content), + sha: result.data.sha, + } +} \ No newline at end of file diff --git a/tools/beta-release/package-lock.json b/tools/beta-release/package-lock.json new file mode 100644 index 0000000..358934c --- /dev/null +++ b/tools/beta-release/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "beta-release", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "beta-release", + "version": "1.0.0", + "license": "MIT" + } + } +} diff --git a/tools/beta-release/package.json b/tools/beta-release/package.json new file mode 100644 index 0000000..975ce6c --- /dev/null +++ b/tools/beta-release/package.json @@ -0,0 +1,8 @@ +{ + "name": "beta-release", + "version": "1.0.0", + "main": "app.js", + "license": "MIT", + "type": "module", + "private": true +} From cac4f8cedf49de96a491df0ba3d56ea2d436f48f Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 24 Aug 2022 15:15:26 +0100 Subject: [PATCH 2/6] ignoring node modules --- .github/workflows/nightly.yml | 4 ++-- .gitignore | 2 +- app/build.gradle | 7 +++++-- tools/beta-release/app.js | 7 ++++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 945a0de..8162c56 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -26,6 +26,6 @@ jobs: - uses: actions/github-script@v6 with: script: | - const { script } = await import('${{ github.workspace }}/tools/beta-release/app.js') - await script({github, context, core}) + const { startReleaseProcess } = await import('${{ github.workspace }}/tools/beta-release/app.js') + await startReleaseProcess({github, context, core}) diff --git a/.gitignore b/.gitignore index 9257dec..0f83f23 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ .cxx local.properties /benchmark-out -**/node_modules \ No newline at end of file +/node_modules \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 5ed4c0c..1e87c5c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,8 +46,11 @@ android { "proguard/serializationx.pro", "proguard/olm.pro" - // actual releases are signed with a different config - signingConfig = buildTypes.debug.signingConfig + if (project.hasProperty("unsigned")) { + // releases are signed externally + } else { + signingConfig = buildTypes.debug.signingConfig + } } } diff --git a/tools/beta-release/app.js b/tools/beta-release/app.js index b0ebf35..a430101 100644 --- a/tools/beta-release/app.js +++ b/tools/beta-release/app.js @@ -8,7 +8,7 @@ const config = { const rcBranchName = "release-candidate" -export const script = async ({ github, context, core }) => { +export const startReleaseProcess = async ({ github, context, core }) => { console.log("script start") if (await doesNotHaveInProgressRelease(github) && await isWorkingBranchAhead(github)) { await startRelease(github) @@ -18,6 +18,11 @@ export const script = async ({ github, context, core }) => { return "" } +export const publishRelease = async (github, artifact) => { + +} + + const isWorkingBranchAhead = async (github) => { const result = await github.rest.repos.compareCommitsWithBasehead({ owner: config.owner, From 81c8cb4927f5bf71c31769546c34310847c19ff4 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 24 Aug 2022 15:34:08 +0100 Subject: [PATCH 3/6] updating gitignore to ignore secrets and any nodemodules dir --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0f83f23..b55bcc1 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ .cxx local.properties /benchmark-out -/node_modules \ No newline at end of file +**/node_modules +.secrets \ No newline at end of file From e235598ff526b500aa2dc70c3a97d4d7cfe81898 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 24 Aug 2022 15:34:30 +0100 Subject: [PATCH 4/6] adding script to generate signed release bundle --- tools/generate-release.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100755 tools/generate-release.sh diff --git a/tools/generate-release.sh b/tools/generate-release.sh new file mode 100755 index 0000000..dc10bf0 --- /dev/null +++ b/tools/generate-release.sh @@ -0,0 +1,10 @@ +#! /bin/bash + +./gradlew clean bundleRelease -Punsigned --no-daemon --no-configuration-cache --no-build-cache +RELEASE_AAB=app/build/outputs/bundle/release/app-release.aab + +jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 \ + -keystore .secrets/upload-key.jks \ + -storepass $1 \ + $RELEASE_AAB \ + key0 \ No newline at end of file From 982476e7d6b5fa50b8d5849d34dc624120a3a561 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 24 Aug 2022 15:35:16 +0100 Subject: [PATCH 5/6] adding gh action for generating the signed bundle - doesn't upload anywhere yet... --- .github/workflows/release-candidate.yml | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/release-candidate.yml diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml new file mode 100644 index 0000000..db85502 --- /dev/null +++ b/.github/workflows/release-candidate.yml @@ -0,0 +1,43 @@ + + +# on pushes to release candidate +# create release apk +# create tag, draft github release, upload universal + + +name: Generate and publish Release Candidate + +on: + push: + branches: + - 'release' + +jobs: + publish-release-candidate: + name: Publish release candidate + runs-on: ubuntu-latest + + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + - uses: actions/setup-node@v3 + with: + node-version: + 16 + - run: npm ci + working-directory: ./tools/beta-release/ + + - run: | + mkdir .secrets && touch .secrets/upload-key.jks + echo ${{ secrets.UPLOAD_KEY }} | base64 -d >> .secrets/upload-key.jks + + - name: Assemble release variant + run: ./tools/generate-release.sh ${{ secrets.STORE_PASS }} + From 9f5beb532f67d176fc865f9ae039852ff4b776b6 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 30 Aug 2022 20:32:52 +0100 Subject: [PATCH 6/6] adds automatic beta track deploying and github release creation on merges to the release branch - each merge expects an updated version code --- .github/workflows/release-candidate.yml | 20 +- tools/beta-release/app.js | 15 +- tools/beta-release/package-lock.json | 756 +++++++++++++++++++++++- tools/beta-release/package.json | 5 +- tools/beta-release/release.js | 201 +++++++ tools/generate-release.sh | 9 +- version.json | 4 +- 7 files changed, 993 insertions(+), 17 deletions(-) create mode 100644 tools/beta-release/release.js diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml index db85502..7ee6440 100644 --- a/.github/workflows/release-candidate.yml +++ b/.github/workflows/release-candidate.yml @@ -1,10 +1,3 @@ - - -# on pushes to release candidate -# create release apk -# create tag, draft github release, upload universal - - name: Generate and publish Release Candidate on: @@ -35,9 +28,20 @@ jobs: working-directory: ./tools/beta-release/ - run: | - mkdir .secrets && touch .secrets/upload-key.jks + mkdir .secrets && touch .secrets/upload-key.jks && touch .secrets/service-account.json echo ${{ secrets.UPLOAD_KEY }} | base64 -d >> .secrets/upload-key.jks + echo ${{ secrets.SERVICE_ACCOUNT }} | base64 -d >> .secrets/service-account.json - name: Assemble release variant run: ./tools/generate-release.sh ${{ secrets.STORE_PASS }} + - uses: actions/github-script@v6 + with: + script: | + const { publishRelease } = await import('${{ github.workspace }}/tools/beta-release/app.js') + const artifacts = { + bundle: '${{ github.workspace }}/app/build/outputs/bundle/release/app-release.aab', + mapping: '${{ github.workspace }}/app/build/outputs/mapping/release/mapping.txt', + } + await publishRelease(github, artifacts) + diff --git a/tools/beta-release/app.js b/tools/beta-release/app.js index a430101..aeb790c 100644 --- a/tools/beta-release/app.js +++ b/tools/beta-release/app.js @@ -1,9 +1,12 @@ +import { release } from './release.js' + const config = { owner: "ouchadam", repo: "small-talk", pathToVersionFile: "version.json", rcBranchesFrom: "main", rcMergesTo: "release", + packageName: "app.dapk.st" } const rcBranchName = "release-candidate" @@ -18,11 +21,17 @@ export const startReleaseProcess = async ({ github, context, core }) => { return "" } -export const publishRelease = async (github, artifact) => { - +export const publishRelease = async (github, artifacts) => { + const versionFile = await readVersionFile(github) + await release( + github, + versionFile.content, + config.packageName, + artifacts, + config, + ).catch((error) => console.log(error)) } - const isWorkingBranchAhead = async (github) => { const result = await github.rest.repos.compareCommitsWithBasehead({ owner: config.owner, diff --git a/tools/beta-release/package-lock.json b/tools/beta-release/package-lock.json index 358934c..a35258b 100644 --- a/tools/beta-release/package-lock.json +++ b/tools/beta-release/package-lock.json @@ -7,7 +7,761 @@ "": { "name": "beta-release", "version": "1.0.0", - "license": "MIT" + "license": "MIT", + "dependencies": { + "@googleapis/androidpublisher": "^3.0.0" + } + }, + "node_modules/@googleapis/androidpublisher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@googleapis/androidpublisher/-/androidpublisher-3.0.0.tgz", + "integrity": "sha512-r4JfmLlcu/VI4hObZuQ8RW5OeWRFtOxqE9xU8C2GAp3GTu2ZcemEICu69xy/rchpzMNQY4lrr8WiqUG1LE1L5Q==", + "dependencies": { + "googleapis-common": "^5.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "engines": { + "node": ">=8" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bignumber.js": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz", + "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/fast-text-encoding": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.4.tgz", + "integrity": "sha512-x6lDDm/tBAzX9kmsPcZsNbvDs3Zey3+scsxaZElS8xWLgUMAg/oFLeewfUz0mu1CblHhhsu15jGkraldkFh8KQ==" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/gaxios": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.3.tgz", + "integrity": "sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==", + "dependencies": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.7" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gcp-metadata": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", + "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", + "dependencies": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/google-auth-library": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz", + "integrity": "sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==", + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/google-p12-pem": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.4.tgz", + "integrity": "sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg==", + "dependencies": { + "node-forge": "^1.3.1" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/googleapis-common": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-5.1.0.tgz", + "integrity": "sha512-RXrif+Gzhq1QAzfjxulbGvAY3FPj8zq/CYcvgjzDbaBNCD6bUl+86I7mUs4DKWHGruuK26ijjR/eDpWIDgNROA==", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^4.0.0", + "google-auth-library": "^7.14.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/gtoken": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz", + "integrity": "sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==", + "dependencies": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + }, + "dependencies": { + "@googleapis/androidpublisher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@googleapis/androidpublisher/-/androidpublisher-3.0.0.tgz", + "integrity": "sha512-r4JfmLlcu/VI4hObZuQ8RW5OeWRFtOxqE9xU8C2GAp3GTu2ZcemEICu69xy/rchpzMNQY4lrr8WiqUG1LE1L5Q==", + "requires": { + "googleapis-common": "^5.0.1" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bignumber.js": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz", + "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "fast-text-encoding": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.4.tgz", + "integrity": "sha512-x6lDDm/tBAzX9kmsPcZsNbvDs3Zey3+scsxaZElS8xWLgUMAg/oFLeewfUz0mu1CblHhhsu15jGkraldkFh8KQ==" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gaxios": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.3.tgz", + "integrity": "sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.7" + } + }, + "gcp-metadata": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", + "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", + "requires": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + } + }, + "get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "google-auth-library": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz", + "integrity": "sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + }, + "google-p12-pem": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.4.tgz", + "integrity": "sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg==", + "requires": { + "node-forge": "^1.3.1" + } + }, + "googleapis-common": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-5.1.0.tgz", + "integrity": "sha512-RXrif+Gzhq1QAzfjxulbGvAY3FPj8zq/CYcvgjzDbaBNCD6bUl+86I7mUs4DKWHGruuK26ijjR/eDpWIDgNROA==", + "requires": { + "extend": "^3.0.2", + "gaxios": "^4.0.0", + "google-auth-library": "^7.14.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^8.0.0" + } + }, + "gtoken": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz", + "integrity": "sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==", + "requires": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.1.3", + "jws": "^4.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/tools/beta-release/package.json b/tools/beta-release/package.json index 975ce6c..b36676e 100644 --- a/tools/beta-release/package.json +++ b/tools/beta-release/package.json @@ -4,5 +4,8 @@ "main": "app.js", "license": "MIT", "type": "module", - "private": true + "private": true, + "dependencies": { + "@googleapis/androidpublisher": "^3.0.0" + } } diff --git a/tools/beta-release/release.js b/tools/beta-release/release.js new file mode 100644 index 0000000..94789ce --- /dev/null +++ b/tools/beta-release/release.js @@ -0,0 +1,201 @@ +import * as google from '@googleapis/androidpublisher'; +import * as fs from "fs"; +import * as http from 'https'; +import * as url from 'url'; + +const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); + +const auth = new google.auth.GoogleAuth({ + keyFile: '.secrets/service-account.json', + scopes: ['https://www.googleapis.com/auth/androidpublisher'], +}) + +const androidPublisher = google.androidpublisher({ + version: 'v3', + auth: auth, +}) + +const universalApkPath = `${__dirname}/universal.apk` + +export const release = async (github, version, applicationId, artifacts, config) => { + const appEditId = await startPlayRelease(applicationId) + + console.log("Uploading bundle...") + await uploadBundle(appEditId, applicationId, artifacts.bundle) + + console.log("Uploading mapping...") + await uploadMappingFile(appEditId, version.code, applicationId, artifacts.mapping) + + console.log("Assign artifacts to beta release...") + await addReleaseToTrack(appEditId, version, applicationId) + + console.log("Commiting draft release...") + await androidPublisher.edits.commit({ + editId: appEditId, + packageName: applicationId, + }).catch((error) => Promise.reject(error.response.data)) + + console.log("Downloading generated universal apk...") + await dowloadSignedUniversalApk( + version, + applicationId, + await auth.getAccessToken(), + universalApkPath + ) + + const releaseResult = await github.rest.repos.createRelease({ + owner: config.owner, + repo: config.repo, + tag_name: version.name, + prerelease: true, + generate_release_notes: true, + }) + + console.log(releaseResult.data.id) + + await github.rest.repos.uploadReleaseAsset({ + owner: config.owner, + repo: config.repo, + release_id: releaseResult.data.id, + name: `universal-${version.name}.apk`, + data: fs.readFileSync(universalApkPath) + }) + + console.log("Promoting beta draft release to live...") + await promoteDraftToLive(applicationId) +} + +const startPlayRelease = async (applicationId) => { + const result = await androidPublisher.edits.insert({ + packageName: applicationId + }).catch((error) => Promise.reject(error.response.data)) + return result.data.id +} + +const uploadBundle = async (appEditId, applicationId, bundleReleaseFile) => { + const res = await androidPublisher.edits.bundles.upload({ + packageName: applicationId, + editId: appEditId, + media: { + mimeType: 'application/octet-stream', + body: fs.createReadStream(bundleReleaseFile) + } + }).catch((error) => Promise.reject(error.response.data)) + + return res.data +} + +const uploadMappingFile = async (appEditId, versionCode, applicationId, mappingFilePath) => { + await androidPublisher.edits.deobfuscationfiles.upload({ + packageName: applicationId, + editId: appEditId, + apkVersionCode: versionCode, + deobfuscationFileType: 'proguard', + media: { + mimeType: 'application/octet-stream', + body: fs.createReadStream(mappingFilePath) + } + }).catch((error) => Promise.reject(error.response.data)) +} + +const addReleaseToTrack = async (appEditId, version, applicationId) => { + const result = await androidPublisher.edits.tracks + .update({ + editId: appEditId, + packageName: applicationId, + track: "beta", + requestBody: { + track: "beta", + releases: [ + { + name: version.name, + status: "draft", + releaseNotes: { + language: "en-GB", + text: "Bug fixes and improvments - See https://github.com/ouchadam/small-talk/releases for more details", + }, + versionCodes: [version.code] + } + ] + } + }) + .catch((error) => Promise.reject(error.response.data)) + return result.data; +} + + +const dowloadSignedUniversalApk = async (version, applicationId, authToken, outputFile) => { + console.log("fetching universal apk") + + const apkRes = await androidPublisher.generatedapks.list({ + packageName: applicationId, + versionCode: version.code, + }) + + const apks = apkRes.data.generatedApks + + console.log(`found ${apks.length} apks`) + apks.forEach((apk) => { + console.log(apk) + }) + + const id = apks[0].generatedUniversalApk.downloadId + + console.log(`downloading: ${id}`) + + const downloadUrl = `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${applicationId}/generatedApks/${version.code}/downloads/${id}:download?alt=media` + const options = { + headers: { + "Authorization": `Bearer ${authToken}` + } + } + + await downloadToFile(downloadUrl, options, outputFile) +} + +const downloadToFile = async (url, options, outputFile) => { + return new Promise((resolve, error) => { + http.get(url, options, (response) => { + const file = fs.createWriteStream(outputFile) + response.pipe(file) + + file.on("finish", () => { + file.close() + resolve() + }) + + file.on("error", (cause) => { + error(cause) + }) + }).on("error", (cause) => { + error(cause) + }) + }) +} + +const promoteDraftToLive = async () => { + const fappEditId = await startPlayRelease(applicationId) + + await androidPublisher.edits.tracks + .update({ + editId: fappEditId, + packageName: applicationId, + track: "beta", + requestBody: { + track: "beta", + releases: [ + { + status: "completed", + } + ] + } + }) + .catch((error) => Promise.reject(error.response.data)) + + + await androidPublisher.edits.commit({ + editId: fappEditId, + packageName: applicationId, + }).catch((error) => Promise.reject(error.response.data)) +} + diff --git a/tools/generate-release.sh b/tools/generate-release.sh index dc10bf0..16ae11f 100755 --- a/tools/generate-release.sh +++ b/tools/generate-release.sh @@ -1,10 +1,15 @@ #! /bin/bash ./gradlew clean bundleRelease -Punsigned --no-daemon --no-configuration-cache --no-build-cache -RELEASE_AAB=app/build/outputs/bundle/release/app-release.aab +WORKING_DIR=app/build/outputs/bundle/release +RELEASE_AAB=$WORKING_DIR/app-release.aab + +cp $RELEASE_AAB $WORKING_DIR/app-release-unsigned.aab + +echo "signing $RELEASE_AAB" jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 \ -keystore .secrets/upload-key.jks \ -storepass $1 \ $RELEASE_AAB \ - key0 \ No newline at end of file + key0 diff --git a/version.json b/version.json index dacf613..a85baed 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { - "name": "0.0.1-alpha03", - "code": 4 + "name": "0.0.1-alpha04", + "code": 6 } \ No newline at end of file