From a51ed9ead96da767a8cf09243599548775a42f29 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Tue, 23 May 2023 10:09:08 +0200 Subject: [PATCH 01/51] Add branding to action.yml --- action.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/action.yml b/action.yml index 7fa727e..903aeb0 100644 --- a/action.yml +++ b/action.yml @@ -37,3 +37,6 @@ inputs: runs: using: 'node16' main: 'dist/index.js' +branding: + icon: 'upload-cloud' + color: 'blue' From 34ebbbad88525e0bfcad096f5f99da48cc251271 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Tue, 23 May 2023 10:10:51 +0200 Subject: [PATCH 02/51] Update action.yml --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 903aeb0..98f0ba3 100644 --- a/action.yml +++ b/action.yml @@ -1,5 +1,5 @@ name: 'Maestro Cloud Upload Action' -description: 'Upload app to Maestro Cloud for analysis' +description: 'Upload your app to Maestro Cloud to run your Flows in CI' inputs: api-url: description: 'Alternative mobile.dev API url' From 276e91298b059b5508fd38b7cf82c0ebffe41eb7 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Tue, 23 May 2023 10:20:01 +0200 Subject: [PATCH 03/51] Update release script to also update major version tag --- _release.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_release.sh b/_release.sh index 2a3cc68..0c6f84f 100755 --- a/_release.sh +++ b/_release.sh @@ -3,6 +3,7 @@ set -e VERSION=$1 +MAJOR_VERSION=$(echo "$version" | cut -d '.' -f 1) if [[ -z "$VERSION" ]]; then echo "Usage: npm run release " @@ -16,3 +17,6 @@ git commit --allow-empty -m "Version ${VERSION}" git tag -a "v${VERSION}" -m "Version ${VERSION}" git push git push --tags +# update major version tag +git tag -fa "v${MAJOR_VERSION}" -m "Update v${MAJOR_VERSION} tag" +git push origin "v${MAJOR_VERSION}" --force From d22f98513893cebe1ba6fb35aa457fc1ebde0f8e Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Tue, 23 May 2023 10:20:32 +0200 Subject: [PATCH 04/51] Version 1.3.2 --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f02b0d2..6bb7833 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -47,7 +47,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -57,7 +57,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -69,7 +69,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -83,7 +83,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -101,7 +101,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -113,7 +113,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -125,7 +125,7 @@ If you don't want the action to wait until the Upload has been completed as is t If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -141,7 +141,7 @@ You can use Maestro (Tags)[https://maestro.mobile.dev/cli/tags] to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip From ec93f16167fc75e666c509e804ecefedf775bdd4 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Tue, 23 May 2023 10:21:46 +0200 Subject: [PATCH 05/51] Fix release script --- _release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_release.sh b/_release.sh index 0c6f84f..1ad4c7b 100755 --- a/_release.sh +++ b/_release.sh @@ -3,7 +3,7 @@ set -e VERSION=$1 -MAJOR_VERSION=$(echo "$version" | cut -d '.' -f 1) +MAJOR_VERSION=$(echo "$VERSION" | cut -d '.' -f 1) if [[ -z "$VERSION" ]]; then echo "Usage: npm run release " From dfbb66d71275a7ddc914992991d4116772734acb Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Tue, 23 May 2023 10:21:55 +0200 Subject: [PATCH 06/51] Version 1.3.2 From 006235e6db9d4a2d0251252479f8274c36d62454 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Tue, 23 May 2023 10:25:26 +0200 Subject: [PATCH 07/51] Update release notes --- RELEASING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 774782b..2761e0a 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,4 +1,5 @@ # Releasing -* Run `npm run release ` where `` is in the form `major.minor.patch`. -* That's it! The command above will build and push the appropriate artifacts and tags to deploy the action. +- Run `npm run release ` where `` is in the form `major.minor.patch`. +- Publish a new release named ` release` [here](https://github.com/mobile-dev-inc/action-maestro-cloud/releases/new) to the Actions marketplace. Tip: use the `Generate release notes` feature to get a great changelog filled out automatically. +- That's it! The command above will build and push the appropriate artifacts and tags to deploy the action. From 27e266a4e1fc5c19969385bc6a299e3a596373fb Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Tue, 23 May 2023 10:31:19 +0200 Subject: [PATCH 08/51] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 6bb7833..f704e80 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,17 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). +# Using the action + +Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. + +```yaml +- uses: mobile-dev-inc/action-maestro-cloud@v1 + with: + api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + app-file: +``` + # Triggers Trigger this action on (1) pushes to your main branch and (2) pull requests opened against your main branch: From 38406deb6c978b2df738146fc3b783a19e918fcc Mon Sep 17 00:00:00 2001 From: Iain Smith Date: Wed, 24 May 2023 13:50:48 +0100 Subject: [PATCH 09/51] Update action.yml to set name as not required (#13) This PR sets the required property on the `name` parameter to false in the `action.yml` I have the VSCode [Github Actions Extension](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-github-actions) installed, it shows that I need to add the parameter 'name' but this is not true. image From checking the [params.ts](https://github.com/mobile-dev-inc/action-maestro-cloud/blob/main/params.ts#LL103C1-L103C1) file I think we can set required to false in the `action.yml`. --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 98f0ba3..69161ab 100644 --- a/action.yml +++ b/action.yml @@ -9,7 +9,7 @@ inputs: required: true name: description: 'Name of the version of the uploaded app' - required: true + required: false app-file: description: 'The app file to upload to Maestro Cloud' required: true From 8b368a350db07fdfdd0024961f6cfe6d31bf5b77 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Wed, 24 May 2023 14:51:39 +0200 Subject: [PATCH 10/51] Version 1.3.3 --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f704e80..ca7f3d0 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -136,7 +136,7 @@ If you don't want the action to wait until the Upload has been completed as is t If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -152,7 +152,7 @@ You can use Maestro (Tags)[https://maestro.mobile.dev/cli/tags] to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip From 46145706b8d5762efd593e4a8f5fbe5532f4cec6 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Mon, 12 Jun 2023 12:42:28 +0200 Subject: [PATCH 11/51] Add support for iOS version config (#15) --- ApiClient.ts | 1 + action.yml | 37 ++++++++++++++++++++----------------- index.ts | 2 ++ params.ts | 23 ++++++++++++++++++++++- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/ApiClient.ts b/ApiClient.ts index cee6570..8c237c7 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -19,6 +19,7 @@ export type UploadRequest = { env?: { [key: string]: string }, agent: string, androidApiLevel?: number, + iOSVersion?: string, includeTags: string[], excludeTags: string[], } diff --git a/action.yml b/action.yml index 69161ab..0efb386 100644 --- a/action.yml +++ b/action.yml @@ -1,42 +1,45 @@ -name: 'Maestro Cloud Upload Action' -description: 'Upload your app to Maestro Cloud to run your Flows in CI' +name: "Maestro Cloud Upload Action" +description: "Upload your app to Maestro Cloud to run your Flows in CI" inputs: api-url: - description: 'Alternative mobile.dev API url' + description: "Alternative mobile.dev API url" required: false api-key: - description: 'mobile.dev API key' + description: "mobile.dev API key" required: true name: - description: 'Name of the version of the uploaded app' + description: "Name of the version of the uploaded app" required: false app-file: - description: 'The app file to upload to Maestro Cloud' + description: "The app file to upload to Maestro Cloud" required: true mapping-file: - description: 'The Proguard mapping file to upload to Maestro Cloud' + description: "The Proguard mapping file to upload to Maestro Cloud" required: false workspace: - description: 'Path to a folder that contains Maestro flows (.mobiledev by default)' + description: "Path to a folder that contains Maestro flows (.mobiledev by default)" required: false env: - description: 'Set of key=value entries to pass as an input to Maestro flows' + description: "Set of key=value entries to pass as an input to Maestro flows" required: false async: - description: 'Whether to run Upload in async fashion and not block until completed' + description: "Whether to run Upload in async fashion and not block until completed" required: false android-api-level: - description: 'Android API level to run your flow against' + description: "Android API level to run your flow against" + required: false + ios-version: + description: "iOS version to run your flow against" required: false include-tags: - description: 'List of tags that will remove flows that does not have the provided tags' + description: "List of tags that will remove flows that does not have the provided tags" required: false exclude-tags: - description: 'List of tags that will remove flows containing the provided tags' + description: "List of tags that will remove flows containing the provided tags" required: false runs: - using: 'node16' - main: 'dist/index.js' + using: "node16" + main: "dist/index.js" branding: - icon: 'upload-cloud' - color: 'blue' + icon: "upload-cloud" + color: "blue" diff --git a/index.ts b/index.ts index 41cca58..f50181a 100644 --- a/index.ts +++ b/index.ts @@ -58,6 +58,7 @@ const run = async () => { env, async, androidApiLevel, + iOSVersion, includeTags, excludeTags } = await getParameters() @@ -84,6 +85,7 @@ const run = async () => { env: env, agent: 'github', androidApiLevel: androidApiLevel, + iOSVersion, includeTags: includeTags, excludeTags: excludeTags, } diff --git a/params.ts b/params.ts index 4673377..4a60c1d 100644 --- a/params.ts +++ b/params.ts @@ -18,6 +18,7 @@ export type Params = { env?: { [key: string]: string }, async?: boolean, androidApiLevel?: number, + iOSVersion?: string, includeTags: string[], excludeTags: string[], } @@ -108,6 +109,7 @@ export async function getParameters(): Promise { const mappingFile = mappingFileInput && validateMappingFile(mappingFileInput) const async = core.getInput('async', { required: false }) === 'true' const androidApiLevelString = core.getInput('android-api-level', { required: false }) + const iOSVersion = core.getInput('ios-version', { required: false }) const includeTags = parseTags(core.getInput('include-tags', { required: false })) const excludeTags = parseTags(core.getInput('exclude-tags', { required: false })) @@ -133,5 +135,24 @@ export async function getParameters(): Promise { const repoName = getRepoName() const pullRequestId = getPullRequestId() const androidApiLevel = getAndroidApiLevel(androidApiLevelString) - return { apiUrl, name, apiKey, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, includeTags, excludeTags } + + return { + apiUrl, + name, + apiKey, + appFilePath, + mappingFile, + workspaceFolder, + branchName, + commitSha, + repoOwner, + repoName, + pullRequestId, + env, + async, + androidApiLevel, + iOSVersion, + includeTags, + excludeTags + } } \ No newline at end of file From 30d670df46997cc012434c88b48f427e899d622e Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Mon, 12 Jun 2023 12:47:12 +0200 Subject: [PATCH 12/51] Update README.md --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index ca7f3d0..b745397 100644 --- a/README.md +++ b/README.md @@ -159,3 +159,17 @@ You can either pass a single value, or comma-separated (`,`) values. include-tags: dev, pull-request exclude-tags: excludeTag ``` + +# Specifying Android API Level + +You can specify what Android API level to use when running in Maestro Cloud using the `android-api-level` parameter. + +The default API level is 30. + +```yaml +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 + with: + api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + app-file: app.apk + android-api-level: 29 +``` From 0bc8258607df0d817078c20122b0a92db367f2aa Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Tue, 13 Jun 2023 14:26:46 +0200 Subject: [PATCH 13/51] Update README (#16) --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index b745397..3b461a1 100644 --- a/README.md +++ b/README.md @@ -173,3 +173,17 @@ The default API level is 30. app-file: app.apk android-api-level: 29 ``` + +# Specifying iOS version + +You can specify what **major** iOS Version to use when running in Maestro Cloud using the `ios-version` parameter. + +The default iOS version is 15. + +```yaml +- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 + with: + api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + app-file: app.zip + ios-version: 16 +``` From 8cf8aef1a3d0e003a08af6c4a1756161d72c6c47 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Tue, 13 Jun 2023 14:27:56 +0200 Subject: [PATCH 14/51] Version 1.4.0 --- README.md | 24 ++++++++++++------------ dist/index.js | 24 ++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 3b461a1..3aac696 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -136,7 +136,7 @@ If you don't want the action to wait until the Upload has been completed as is t If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -152,7 +152,7 @@ You can use Maestro (Tags)[https://maestro.mobile.dev/cli/tags] to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -167,7 +167,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -181,7 +181,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.3.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip diff --git a/dist/index.js b/dist/index.js index 92e2ab7..3a1d1c8 100644 --- a/dist/index.js +++ b/dist/index.js @@ -47018,7 +47018,7 @@ const getConsoleUrl = (uploadId, teamId, appId) => { }; exports.getConsoleUrl = getConsoleUrl; const run = () => __awaiter(void 0, void 0, void 0, function* () { - const { apiKey, apiUrl, name, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, includeTags, excludeTags } = yield (0, params_1.getParameters)(); + const { apiKey, apiUrl, name, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, iOSVersion, includeTags, excludeTags } = yield (0, params_1.getParameters)(); const appFile = yield (0, app_file_1.validateAppFile)(yield (0, archive_utils_1.zipIfFolder)(appFilePath)); if (!knownAppTypes.includes(appFile.type)) { throw new Error(`Unsupported app file type: ${appFile.type}`); @@ -47036,6 +47036,7 @@ const run = () => __awaiter(void 0, void 0, void 0, function* () { env: env, agent: 'github', androidApiLevel: androidApiLevel, + iOSVersion, includeTags: includeTags, excludeTags: excludeTags, }; @@ -47207,6 +47208,7 @@ function getParameters() { const mappingFile = mappingFileInput && (0, app_file_1.validateMappingFile)(mappingFileInput); const async = core.getInput('async', { required: false }) === 'true'; const androidApiLevelString = core.getInput('android-api-level', { required: false }); + const iOSVersion = core.getInput('ios-version', { required: false }); const includeTags = parseTags(core.getInput('include-tags', { required: false })); const excludeTags = parseTags(core.getInput('exclude-tags', { required: false })); var env = {}; @@ -47228,7 +47230,25 @@ function getParameters() { const repoName = getRepoName(); const pullRequestId = getPullRequestId(); const androidApiLevel = getAndroidApiLevel(androidApiLevelString); - return { apiUrl, name, apiKey, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, includeTags, excludeTags }; + return { + apiUrl, + name, + apiKey, + appFilePath, + mappingFile, + workspaceFolder, + branchName, + commitSha, + repoOwner, + repoName, + pullRequestId, + env, + async, + androidApiLevel, + iOSVersion, + includeTags, + excludeTags + }; }); } exports.getParameters = getParameters; From 24212737582cc2a351df0f80c70fcc60665e1528 Mon Sep 17 00:00:00 2001 From: Felipe Volpone Date: Tue, 13 Jun 2023 14:57:31 -0300 Subject: [PATCH 15/51] Changing IOS versions specified in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3aac696..ea0693d 100644 --- a/README.md +++ b/README.md @@ -178,12 +178,12 @@ The default API level is 30. You can specify what **major** iOS Version to use when running in Maestro Cloud using the `ios-version` parameter. -The default iOS version is 15. +The default iOS version is 15.5. ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - ios-version: 16 + ios-version: 16.2 ``` From 24cdd07e567afd9afd14252ba85addceacf9feb4 Mon Sep 17 00:00:00 2001 From: Felipe Volpone Date: Tue, 13 Jun 2023 16:37:30 -0300 Subject: [PATCH 16/51] Revert "Changing IOS versions specified in README" This reverts commit 24212737582cc2a351df0f80c70fcc60665e1528. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ea0693d..3aac696 100644 --- a/README.md +++ b/README.md @@ -178,12 +178,12 @@ The default API level is 30. You can specify what **major** iOS Version to use when running in Maestro Cloud using the `ios-version` parameter. -The default iOS version is 15.5. +The default iOS version is 15. ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - ios-version: 16.2 + ios-version: 16 ``` From dca94e8c5b94b5737bf248e9277a8fb3be7f9f9e Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Mon, 19 Jun 2023 13:41:45 +0200 Subject: [PATCH 17/51] Fix bug causing rejected uploads when ios-version is not set (#17) --- ApiClient.ts | 2 +- index.ts | 2 +- params.ts | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ApiClient.ts b/ApiClient.ts index 8c237c7..c523855 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -19,7 +19,7 @@ export type UploadRequest = { env?: { [key: string]: string }, agent: string, androidApiLevel?: number, - iOSVersion?: string, + iOSVersion?: number, includeTags: string[], excludeTags: string[], } diff --git a/index.ts b/index.ts index f50181a..4806cb5 100644 --- a/index.ts +++ b/index.ts @@ -85,7 +85,7 @@ const run = async () => { env: env, agent: 'github', androidApiLevel: androidApiLevel, - iOSVersion, + iOSVersion: iOSVersion, includeTags: includeTags, excludeTags: excludeTags, } diff --git a/params.ts b/params.ts index 4a60c1d..45f85a5 100644 --- a/params.ts +++ b/params.ts @@ -18,7 +18,7 @@ export type Params = { env?: { [key: string]: string }, async?: boolean, androidApiLevel?: number, - iOSVersion?: string, + iOSVersion?: number, includeTags: string[], excludeTags: string[], } @@ -83,6 +83,10 @@ function getAndroidApiLevel(apiLevel?: string): number | undefined { return apiLevel ? +apiLevel : undefined } +function getIOSVersion(iosVersion?: string): number | undefined { + return iosVersion ? +iosVersion : undefined +} + function parseTags(tags?: string): string[] { if (tags === undefined || tags === '') return [] @@ -109,7 +113,7 @@ export async function getParameters(): Promise { const mappingFile = mappingFileInput && validateMappingFile(mappingFileInput) const async = core.getInput('async', { required: false }) === 'true' const androidApiLevelString = core.getInput('android-api-level', { required: false }) - const iOSVersion = core.getInput('ios-version', { required: false }) + const iOSVersionString = core.getInput('ios-version', { required: false }) const includeTags = parseTags(core.getInput('include-tags', { required: false })) const excludeTags = parseTags(core.getInput('exclude-tags', { required: false })) @@ -135,6 +139,7 @@ export async function getParameters(): Promise { const repoName = getRepoName() const pullRequestId = getPullRequestId() const androidApiLevel = getAndroidApiLevel(androidApiLevelString) + const iOSVersion = getIOSVersion(iOSVersionString) return { apiUrl, From 2458fd509d6e632b59927fb17a94eef625fa18bd Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Mon, 19 Jun 2023 13:42:52 +0200 Subject: [PATCH 18/51] Version 1.4.1 --- README.md | 24 ++++++++++++------------ dist/index.js | 8 ++++++-- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 3aac696..32a3710 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -136,7 +136,7 @@ If you don't want the action to wait until the Upload has been completed as is t If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -152,7 +152,7 @@ You can use Maestro (Tags)[https://maestro.mobile.dev/cli/tags] to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -167,7 +167,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -181,7 +181,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip diff --git a/dist/index.js b/dist/index.js index 3a1d1c8..aa102fc 100644 --- a/dist/index.js +++ b/dist/index.js @@ -47036,7 +47036,7 @@ const run = () => __awaiter(void 0, void 0, void 0, function* () { env: env, agent: 'github', androidApiLevel: androidApiLevel, - iOSVersion, + iOSVersion: iOSVersion, includeTags: includeTags, excludeTags: excludeTags, }; @@ -47185,6 +47185,9 @@ function getInferredName() { function getAndroidApiLevel(apiLevel) { return apiLevel ? +apiLevel : undefined; } +function getIOSVersion(iosVersion) { + return iosVersion ? +iosVersion : undefined; +} function parseTags(tags) { if (tags === undefined || tags === '') return []; @@ -47208,7 +47211,7 @@ function getParameters() { const mappingFile = mappingFileInput && (0, app_file_1.validateMappingFile)(mappingFileInput); const async = core.getInput('async', { required: false }) === 'true'; const androidApiLevelString = core.getInput('android-api-level', { required: false }); - const iOSVersion = core.getInput('ios-version', { required: false }); + const iOSVersionString = core.getInput('ios-version', { required: false }); const includeTags = parseTags(core.getInput('include-tags', { required: false })); const excludeTags = parseTags(core.getInput('exclude-tags', { required: false })); var env = {}; @@ -47230,6 +47233,7 @@ function getParameters() { const repoName = getRepoName(); const pullRequestId = getPullRequestId(); const androidApiLevel = getAndroidApiLevel(androidApiLevelString); + const iOSVersion = getIOSVersion(iOSVersionString); return { apiUrl, name, From 54742c38c8646eed8595954d1d46a42f97b22811 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Thu, 31 Aug 2023 10:26:02 +0200 Subject: [PATCH 19/51] Output console URL (#20) Fixes #18 --- index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/index.ts b/index.ts index 4806cb5..3ed1dc1 100644 --- a/index.ts +++ b/index.ts @@ -98,6 +98,7 @@ const run = async () => { ) const consoleUrl = getConsoleUrl(uploadId, teamId, appId) info(`Visit the web console for more details about the upload: ${consoleUrl}\n`) + core.setOutput('MAESTRO_CLOUD_CONSOLE_URL', consoleUrl) !async && new StatusPoller(client, uploadId, consoleUrl).startPolling() } From 2bc194f669d913796d628f48d240a0075df78c82 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Thu, 31 Aug 2023 10:26:40 +0200 Subject: [PATCH 20/51] Show errors and better cancellation messages in StatusPoller (#19) --- ApiClient.ts | 13 +++++++++++-- StatusPoller.ts | 27 ++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/ApiClient.ts b/ApiClient.ts index c523855..1331226 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -35,9 +35,18 @@ export class UploadStatusError { constructor(public status: number, public text: string) { } } +export enum CancellationReason { + BENCHMARK_DEPENDENCY_FAILED = 'BENCHMARK_DEPENDENCY_FAILED', + INFRA_ERROR = 'INFRA_ERROR', + OVERLAPPING_BENCHMARK = 'OVERLAPPING_BENCHMARK', + TIMEOUT = 'TIMEOUT', +} + export type Flow = { name: string, - status: BenchmarkStatus + status: BenchmarkStatus, + errors?: string[], + cancellationReason?: CancellationReason } export type UploadStatusResponse = { @@ -99,7 +108,7 @@ export default class ApiClient { async getUploadStatus( uploadId: string, ): Promise { - const res = await fetch(`${this.apiUrl}/v2/upload/${uploadId}/status`, { + const res = await fetch(`${this.apiUrl}/v2/upload/${uploadId}/status?includeErrors=true`, { method: 'GET', headers: { 'Authorization': `Bearer ${this.apiKey}`, diff --git a/StatusPoller.ts b/StatusPoller.ts index 2fb3937..5a047f0 100644 --- a/StatusPoller.ts +++ b/StatusPoller.ts @@ -1,5 +1,5 @@ import * as core from '@actions/core' -import ApiClient, { BenchmarkStatus, Flow, UploadStatusError } from "./ApiClient"; +import ApiClient, { BenchmarkStatus, CancellationReason, Flow, UploadStatusError } from "./ApiClient"; import { canceled, err, info, success, warning } from './log'; const WAIT_TIMEOUT_MS = 1000 * 60 * 30 // 30 minutes @@ -8,15 +8,36 @@ const TERMINAL_STATUSES = new Set([BenchmarkStatus.SUCCESS, BenchmarkStatus.ERRO const isCompleted = (flow: Flow): boolean => TERMINAL_STATUSES.has(flow.status) +const getCanceledStatusMessage = (reason?: CancellationReason): string => { + switch (reason) { + case CancellationReason.BENCHMARK_DEPENDENCY_FAILED: + case CancellationReason.OVERLAPPING_BENCHMARK: + return 'Skipped'; + + case CancellationReason.TIMEOUT: + return 'Timeout'; + + case CancellationReason.INFRA_ERROR: + default: + return 'Canceled'; + } +} + +const renderError = (errors?: string[]): string => { + if (!errors || errors.length === 0) return ''; + + return ` (${errors[0]})`; +} + const printFlowResult = (flow: Flow): void => { if (flow.status === BenchmarkStatus.SUCCESS) { success(`[Passed] ${flow.name}`) } else if (flow.status === BenchmarkStatus.ERROR) { - err(`[Failed] ${flow.name}`) + err(`[Failed] ${flow.name}${renderError(flow.errors)}`) } else if (flow.status === BenchmarkStatus.WARNING) { warning(`[Warning] ${flow.name}`) } else if (flow.status === BenchmarkStatus.CANCELED) { - canceled(`[Canceled] ${flow.name}`) + canceled(`[${getCanceledStatusMessage(flow.cancellationReason)}] ${flow.name}`) } } From b56b3d9f8cdb97ebc20b168086d1e69827fca49a Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Thu, 31 Aug 2023 10:33:36 +0200 Subject: [PATCH 21/51] Store upload status and flow results in output (#21) Fixes #14 --- README.md | 8 ++++++++ StatusPoller.ts | 3 +++ 2 files changed, 11 insertions(+) diff --git a/README.md b/README.md index 32a3710..a3968c8 100644 --- a/README.md +++ b/README.md @@ -187,3 +187,11 @@ The default iOS version is 15. app-file: app.zip ios-version: 16 ``` + +# Accessing output + +The following output variables are set by the action and you can access them as part of `GITHUB_OUTPUT`. + +- `MAESTRO_CLOUD_CONSOLE_URL` - link to the Maestro Cloud console +- `MAESTRO_CLOUD_UPLOAD_STATUS` - status of the Upload (available only in `async` mode) +- `MAESTRO_CLOUD_FLOW_RESULTS` - list of Flows and their results (available only in `async` mode) diff --git a/StatusPoller.ts b/StatusPoller.ts index 5a047f0..8027753 100644 --- a/StatusPoller.ts +++ b/StatusPoller.ts @@ -110,6 +110,9 @@ export default class StatusPoller { info(`==== View details in the console ====\n`) info(`${this.consoleUrl}`) + core.setOutput('MAESTRO_CLOUD_UPLOAD_STATUS', status) + core.setOutput('MAESTRO_CLOUD_FLOW_RESULTS', flows) + if (status === BenchmarkStatus.ERROR) { const resultStr = getFailedFlowsCountStr(flows) console.log('') From 3c896c7f4a5ec2595bf19ca6ed9fd916de194ec3 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Thu, 31 Aug 2023 10:43:56 +0200 Subject: [PATCH 22/51] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a3968c8..7c6f2da 100644 --- a/README.md +++ b/README.md @@ -193,5 +193,5 @@ The default iOS version is 15. The following output variables are set by the action and you can access them as part of `GITHUB_OUTPUT`. - `MAESTRO_CLOUD_CONSOLE_URL` - link to the Maestro Cloud console -- `MAESTRO_CLOUD_UPLOAD_STATUS` - status of the Upload (available only in `async` mode) -- `MAESTRO_CLOUD_FLOW_RESULTS` - list of Flows and their results (available only in `async` mode) +- `MAESTRO_CLOUD_UPLOAD_STATUS` - status of the Upload (not available in `async` mode) +- `MAESTRO_CLOUD_FLOW_RESULTS` - list of Flows and their results (not available in `async` mode) From d701602d9642651cbcbf1acb8a39c08763e83c54 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Thu, 31 Aug 2023 12:46:35 +0200 Subject: [PATCH 23/51] Update README --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c6f2da..accf4b1 100644 --- a/README.md +++ b/README.md @@ -190,8 +190,25 @@ The default iOS version is 15. # Accessing output -The following output variables are set by the action and you can access them as part of `GITHUB_OUTPUT`. +The following output variables are set by the action: - `MAESTRO_CLOUD_CONSOLE_URL` - link to the Maestro Cloud console - `MAESTRO_CLOUD_UPLOAD_STATUS` - status of the Upload (not available in `async` mode) - `MAESTRO_CLOUD_FLOW_RESULTS` - list of Flows and their results (not available in `async` mode) + +In order to access these variables you can use the following approach: + +```yaml +- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 + with: + api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + app-file: + # ... any other parameters + +- name: Access Outputs + if: always() + run: | + echo "Console URL: ${{ steps.upload.outputs.MAESTRO_CLOUD_CONSOLE_URL }}" + echo "Flow Results: ${{ steps.upload.outputs.MAESTRO_CLOUD_FLOW_RESULTS }}" + echo "Upload Status: ${{ steps.upload.outputs.MAESTRO_CLOUD_UPLOAD_STATUS }}" +``` From baa70d5c94ef2d6960b2156b28c5271c090ca069 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Thu, 31 Aug 2023 12:46:50 +0200 Subject: [PATCH 24/51] Version 1.5.0 --- README.md | 26 +++++++++++++------------- dist/index.js | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index accf4b1..605de98 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -136,7 +136,7 @@ If you don't want the action to wait until the Upload has been completed as is t If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -152,7 +152,7 @@ You can use Maestro (Tags)[https://maestro.mobile.dev/cli/tags] to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -167,7 +167,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -181,7 +181,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -199,7 +199,7 @@ The following output variables are set by the action: In order to access these variables you can use the following approach: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: diff --git a/dist/index.js b/dist/index.js index aa102fc..f44b0c8 100644 --- a/dist/index.js +++ b/dist/index.js @@ -46538,7 +46538,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.UploadStatusError = exports.BenchmarkStatus = void 0; +exports.CancellationReason = exports.UploadStatusError = exports.BenchmarkStatus = void 0; const node_fetch_1 = __importStar(__nccwpck_require__(4429)); var BenchmarkStatus; (function (BenchmarkStatus) { @@ -46556,6 +46556,13 @@ class UploadStatusError { } } exports.UploadStatusError = UploadStatusError; +var CancellationReason; +(function (CancellationReason) { + CancellationReason["BENCHMARK_DEPENDENCY_FAILED"] = "BENCHMARK_DEPENDENCY_FAILED"; + CancellationReason["INFRA_ERROR"] = "INFRA_ERROR"; + CancellationReason["OVERLAPPING_BENCHMARK"] = "OVERLAPPING_BENCHMARK"; + CancellationReason["TIMEOUT"] = "TIMEOUT"; +})(CancellationReason = exports.CancellationReason || (exports.CancellationReason = {})); class ApiClient { constructor(apiKey, apiUrl) { this.apiKey = apiKey; @@ -46588,7 +46595,7 @@ class ApiClient { } getUploadStatus(uploadId) { return __awaiter(this, void 0, void 0, function* () { - const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/v2/upload/${uploadId}/status`, { + const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/v2/upload/${uploadId}/status?includeErrors=true`, { method: 'GET', headers: { 'Authorization': `Bearer ${this.apiKey}`, @@ -46656,18 +46663,35 @@ const WAIT_TIMEOUT_MS = 1000 * 60 * 30; // 30 minutes const INTERVAL_MS = 10000; // 10 seconds const TERMINAL_STATUSES = new Set([ApiClient_1.BenchmarkStatus.SUCCESS, ApiClient_1.BenchmarkStatus.ERROR, ApiClient_1.BenchmarkStatus.WARNING, ApiClient_1.BenchmarkStatus.CANCELED]); const isCompleted = (flow) => TERMINAL_STATUSES.has(flow.status); +const getCanceledStatusMessage = (reason) => { + switch (reason) { + case ApiClient_1.CancellationReason.BENCHMARK_DEPENDENCY_FAILED: + case ApiClient_1.CancellationReason.OVERLAPPING_BENCHMARK: + return 'Skipped'; + case ApiClient_1.CancellationReason.TIMEOUT: + return 'Timeout'; + case ApiClient_1.CancellationReason.INFRA_ERROR: + default: + return 'Canceled'; + } +}; +const renderError = (errors) => { + if (!errors || errors.length === 0) + return ''; + return ` (${errors[0]})`; +}; const printFlowResult = (flow) => { if (flow.status === ApiClient_1.BenchmarkStatus.SUCCESS) { (0, log_1.success)(`[Passed] ${flow.name}`); } else if (flow.status === ApiClient_1.BenchmarkStatus.ERROR) { - (0, log_1.err)(`[Failed] ${flow.name}`); + (0, log_1.err)(`[Failed] ${flow.name}${renderError(flow.errors)}`); } else if (flow.status === ApiClient_1.BenchmarkStatus.WARNING) { (0, log_1.warning)(`[Warning] ${flow.name}`); } else if (flow.status === ApiClient_1.BenchmarkStatus.CANCELED) { - (0, log_1.canceled)(`[Canceled] ${flow.name}`); + (0, log_1.canceled)(`[${getCanceledStatusMessage(flow.cancellationReason)}] ${flow.name}`); } }; const flowWord = (count) => count === 1 ? 'Flow' : 'Flows'; @@ -46727,6 +46751,8 @@ class StatusPoller { console.log(''); (0, log_1.info)(`==== View details in the console ====\n`); (0, log_1.info)(`${this.consoleUrl}`); + core.setOutput('MAESTRO_CLOUD_UPLOAD_STATUS', status); + core.setOutput('MAESTRO_CLOUD_FLOW_RESULTS', flows); if (status === ApiClient_1.BenchmarkStatus.ERROR) { const resultStr = getFailedFlowsCountStr(flows); console.log(''); @@ -47043,6 +47069,7 @@ const run = () => __awaiter(void 0, void 0, void 0, function* () { const { uploadId, teamId, targetId: appId } = yield client.uploadRequest(request, appFile.path, workspaceZip, mappingFile && (yield (0, archive_utils_1.zipIfFolder)(mappingFile))); const consoleUrl = (0, exports.getConsoleUrl)(uploadId, teamId, appId); (0, log_1.info)(`Visit the web console for more details about the upload: ${consoleUrl}\n`); + core.setOutput('MAESTRO_CLOUD_CONSOLE_URL', consoleUrl); !async && new StatusPoller_1.default(client, uploadId, consoleUrl).startPolling(); }); run().catch(e => { From 1e1571a448e85b46b2398a98ddef062532c0ac0a Mon Sep 17 00:00:00 2001 From: Igor Lema <48068670+igorsmotto@users.noreply.github.com> Date: Fri, 6 Oct 2023 10:40:42 -0300 Subject: [PATCH 25/51] feat: add app binary id to input and output (#23) --- ApiClient.ts | 16 ++++++++++------ README.md | 22 +++++++++++++++++++++- action.yml | 5 ++++- index.ts | 22 ++++++++++++++-------- params.ts | 45 ++++++++++++++++++++++++++------------------- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/ApiClient.ts b/ApiClient.ts index 1331226..0d8a8f1 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -22,13 +22,15 @@ export type UploadRequest = { iOSVersion?: number, includeTags: string[], excludeTags: string[], + appBinaryId?: string } // irrelevant data has been factored out from this model export type UploadResponse = { uploadId: string, teamId: string, - targetId: string + targetId: string, + appBinaryId: string } export class UploadStatusError { @@ -65,17 +67,19 @@ export default class ApiClient { async uploadRequest( request: UploadRequest, - appFile: string, + appFile: string | null, workspaceZip: string | null, mappingFile: string | null, ): Promise { const formData = new FormData() formData.set('request', JSON.stringify(request)) - formData.set( - 'app_binary', - fileFromSync(appFile) - ) + if (appFile) { + formData.set( + 'app_binary', + fileFromSync(appFile) + ) + } if (workspaceZip) { formData.set( diff --git a/README.md b/README.md index 605de98..1885b5b 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,23 @@ The default iOS version is 15. ios-version: 16 ``` +# Using an already uploaded App + +You can use an already uploaded App binary in Maestro Cloud using the `app-binary-id` parameter. + +```yaml + - id: upload + uses: igorsmotto/action-maestro-cloud@v0.8 + with: + api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + app-file: app.zip + + - uses: igorsmotto/action-maestro-cloud@v0.8 + with: + api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} +``` + # Accessing output The following output variables are set by the action: @@ -195,11 +212,13 @@ The following output variables are set by the action: - `MAESTRO_CLOUD_CONSOLE_URL` - link to the Maestro Cloud console - `MAESTRO_CLOUD_UPLOAD_STATUS` - status of the Upload (not available in `async` mode) - `MAESTRO_CLOUD_FLOW_RESULTS` - list of Flows and their results (not available in `async` mode) +- `MAESTRO_CLOUD_APP_BINARY_ID` - id of the binary uploaded In order to access these variables you can use the following approach: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- id: upload + uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -211,4 +230,5 @@ In order to access these variables you can use the following approach: echo "Console URL: ${{ steps.upload.outputs.MAESTRO_CLOUD_CONSOLE_URL }}" echo "Flow Results: ${{ steps.upload.outputs.MAESTRO_CLOUD_FLOW_RESULTS }}" echo "Upload Status: ${{ steps.upload.outputs.MAESTRO_CLOUD_UPLOAD_STATUS }}" + echo "App Binary ID:: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }}" ``` diff --git a/action.yml b/action.yml index 0efb386..01cdb84 100644 --- a/action.yml +++ b/action.yml @@ -12,7 +12,10 @@ inputs: required: false app-file: description: "The app file to upload to Maestro Cloud" - required: true + required: false + app-binary-id: + description: "The app binary already uploaded to Maestro Cloud" + required: false mapping-file: description: "The Proguard mapping file to upload to Maestro Cloud" required: false diff --git a/index.ts b/index.ts index 3ed1dc1..edd2f17 100644 --- a/index.ts +++ b/index.ts @@ -60,14 +60,18 @@ const run = async () => { androidApiLevel, iOSVersion, includeTags, - excludeTags + excludeTags, + appBinaryId } = await getParameters() - const appFile = await validateAppFile( - await zipIfFolder(appFilePath) - ); - if (!knownAppTypes.includes(appFile.type)) { - throw new Error(`Unsupported app file type: ${appFile.type}`) + let appFile = null + if (appFilePath !== "") { + appFile = await validateAppFile( + await zipIfFolder(appFilePath) + ); + if (!knownAppTypes.includes(appFile.type)) { + throw new Error(`Unsupported app file type: ${appFile.type}`) + } } const workspaceZip = await createWorkspaceZip(workspaceFolder) @@ -88,17 +92,19 @@ const run = async () => { iOSVersion: iOSVersion, includeTags: includeTags, excludeTags: excludeTags, + appBinaryId: appBinaryId || undefined, } - const { uploadId, teamId, targetId: appId } = await client.uploadRequest( + const { uploadId, teamId, targetId: appId, appBinaryId: uploadedBinaryId } = await client.uploadRequest( request, - appFile.path, + appFile && appFile.path, workspaceZip, mappingFile && await zipIfFolder(mappingFile), ) const consoleUrl = getConsoleUrl(uploadId, teamId, appId) info(`Visit the web console for more details about the upload: ${consoleUrl}\n`) core.setOutput('MAESTRO_CLOUD_CONSOLE_URL', consoleUrl) + core.setOutput('MAESTRO_CLOUD_APP_BINARY_ID', uploadedBinaryId) !async && new StatusPoller(client, uploadId, consoleUrl).startPolling() } diff --git a/params.ts b/params.ts index 45f85a5..0ebe57f 100644 --- a/params.ts +++ b/params.ts @@ -21,6 +21,7 @@ export type Params = { iOSVersion?: number, includeTags: string[], excludeTags: string[], + appBinaryId: string, } function getBranchName(): string { @@ -107,7 +108,6 @@ export async function getParameters(): Promise { const apiUrl = core.getInput('api-url', { required: false }) || 'https://api.mobile.dev' const name = core.getInput('name', { required: false }) || getInferredName() const apiKey = core.getInput('api-key', { required: true }) - const appFilePath = core.getInput('app-file', { required: true }) const mappingFileInput = core.getInput('mapping-file', { required: false }) const workspaceFolder = core.getInput('workspace', { required: false }) const mappingFile = mappingFileInput && validateMappingFile(mappingFileInput) @@ -117,6 +117,12 @@ export async function getParameters(): Promise { const includeTags = parseTags(core.getInput('include-tags', { required: false })) const excludeTags = parseTags(core.getInput('exclude-tags', { required: false })) + const appFilePath = core.getInput('app-file', { required: false }) + const appBinaryId = core.getInput('app-binary-id', { required: false }) + if (!(appFilePath !== "") !== (appBinaryId !== "")) { + throw new Error("Either app-file or app-binary-id must be used") + } + var env: { [key: string]: string } = {} env = core.getMultilineInput('env', { required: false }) .map(it => { @@ -140,24 +146,25 @@ export async function getParameters(): Promise { const pullRequestId = getPullRequestId() const androidApiLevel = getAndroidApiLevel(androidApiLevelString) const iOSVersion = getIOSVersion(iOSVersionString) - - return { - apiUrl, - name, - apiKey, - appFilePath, - mappingFile, - workspaceFolder, - branchName, + + return { + apiUrl, + name, + apiKey, + appFilePath, + mappingFile, + workspaceFolder, + branchName, commitSha, - repoOwner, - repoName, - pullRequestId, - env, - async, - androidApiLevel, - iOSVersion, - includeTags, - excludeTags + repoOwner, + repoName, + pullRequestId, + env, + async, + androidApiLevel, + iOSVersion, + includeTags, + excludeTags, + appBinaryId } } \ No newline at end of file From 80f9291c8fe0979499da0c78834a4542485eb720 Mon Sep 17 00:00:00 2001 From: Igor Lema <48068670+igorsmotto@users.noreply.github.com> Date: Fri, 6 Oct 2023 10:59:50 -0300 Subject: [PATCH 26/51] Release: Version 1.6.0 (#24) --- README.md | 30 +++++++++++++++--------------- dist/index.js | 28 ++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 1885b5b..646f68e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -136,7 +136,7 @@ If you don't want the action to wait until the Upload has been completed as is t If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -152,7 +152,7 @@ You can use Maestro (Tags)[https://maestro.mobile.dev/cli/tags] to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -167,7 +167,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -181,7 +181,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -194,12 +194,12 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar ```yaml - id: upload - uses: igorsmotto/action-maestro-cloud@v0.8 + uses: igorsmotto/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - - uses: igorsmotto/action-maestro-cloud@v0.8 + - uses: igorsmotto/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} @@ -218,7 +218,7 @@ In order to access these variables you can use the following approach: ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.5.0 + uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: diff --git a/dist/index.js b/dist/index.js index f44b0c8..5463747 100644 --- a/dist/index.js +++ b/dist/index.js @@ -46572,7 +46572,9 @@ class ApiClient { return __awaiter(this, void 0, void 0, function* () { const formData = new node_fetch_1.FormData(); formData.set('request', JSON.stringify(request)); - formData.set('app_binary', (0, node_fetch_1.fileFromSync)(appFile)); + if (appFile) { + formData.set('app_binary', (0, node_fetch_1.fileFromSync)(appFile)); + } if (workspaceZip) { formData.set('workspace', (0, node_fetch_1.fileFromSync)(workspaceZip)); } @@ -47044,10 +47046,13 @@ const getConsoleUrl = (uploadId, teamId, appId) => { }; exports.getConsoleUrl = getConsoleUrl; const run = () => __awaiter(void 0, void 0, void 0, function* () { - const { apiKey, apiUrl, name, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, iOSVersion, includeTags, excludeTags } = yield (0, params_1.getParameters)(); - const appFile = yield (0, app_file_1.validateAppFile)(yield (0, archive_utils_1.zipIfFolder)(appFilePath)); - if (!knownAppTypes.includes(appFile.type)) { - throw new Error(`Unsupported app file type: ${appFile.type}`); + const { apiKey, apiUrl, name, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, iOSVersion, includeTags, excludeTags, appBinaryId } = yield (0, params_1.getParameters)(); + let appFile = null; + if (appFilePath !== "") { + appFile = yield (0, app_file_1.validateAppFile)(yield (0, archive_utils_1.zipIfFolder)(appFilePath)); + if (!knownAppTypes.includes(appFile.type)) { + throw new Error(`Unsupported app file type: ${appFile.type}`); + } } const workspaceZip = yield createWorkspaceZip(workspaceFolder); const client = new ApiClient_1.default(apiKey, apiUrl); @@ -47065,11 +47070,13 @@ const run = () => __awaiter(void 0, void 0, void 0, function* () { iOSVersion: iOSVersion, includeTags: includeTags, excludeTags: excludeTags, + appBinaryId: appBinaryId || undefined, }; - const { uploadId, teamId, targetId: appId } = yield client.uploadRequest(request, appFile.path, workspaceZip, mappingFile && (yield (0, archive_utils_1.zipIfFolder)(mappingFile))); + const { uploadId, teamId, targetId: appId, appBinaryId: uploadedBinaryId } = yield client.uploadRequest(request, appFile && appFile.path, workspaceZip, mappingFile && (yield (0, archive_utils_1.zipIfFolder)(mappingFile))); const consoleUrl = (0, exports.getConsoleUrl)(uploadId, teamId, appId); (0, log_1.info)(`Visit the web console for more details about the upload: ${consoleUrl}\n`); core.setOutput('MAESTRO_CLOUD_CONSOLE_URL', consoleUrl); + core.setOutput('MAESTRO_CLOUD_APP_BINARY_ID', uploadedBinaryId); !async && new StatusPoller_1.default(client, uploadId, consoleUrl).startPolling(); }); run().catch(e => { @@ -47232,7 +47239,6 @@ function getParameters() { const apiUrl = core.getInput('api-url', { required: false }) || 'https://api.mobile.dev'; const name = core.getInput('name', { required: false }) || getInferredName(); const apiKey = core.getInput('api-key', { required: true }); - const appFilePath = core.getInput('app-file', { required: true }); const mappingFileInput = core.getInput('mapping-file', { required: false }); const workspaceFolder = core.getInput('workspace', { required: false }); const mappingFile = mappingFileInput && (0, app_file_1.validateMappingFile)(mappingFileInput); @@ -47241,6 +47247,11 @@ function getParameters() { const iOSVersionString = core.getInput('ios-version', { required: false }); const includeTags = parseTags(core.getInput('include-tags', { required: false })); const excludeTags = parseTags(core.getInput('exclude-tags', { required: false })); + const appFilePath = core.getInput('app-file', { required: false }); + const appBinaryId = core.getInput('app-binary-id', { required: false }); + if (!(appFilePath !== "") !== (appBinaryId !== "")) { + throw new Error("Either app-file or app-binary-id must be used"); + } var env = {}; env = core.getMultilineInput('env', { required: false }) .map(it => { @@ -47278,7 +47289,8 @@ function getParameters() { androidApiLevel, iOSVersion, includeTags, - excludeTags + excludeTags, + appBinaryId }; }); } From 06ef0c49f7bcb2ff9aa0328ac5646daef66d562c Mon Sep 17 00:00:00 2001 From: artem888 Date: Fri, 20 Oct 2023 17:09:35 +0200 Subject: [PATCH 27/51] feat: add support for device locale (#26) --- ApiClient.ts | 3 ++- README.md | 17 +++++++++++++++-- action.yml | 3 +++ index.ts | 4 +++- params.ts | 6 +++++- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/ApiClient.ts b/ApiClient.ts index 0d8a8f1..4512b22 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -22,7 +22,8 @@ export type UploadRequest = { iOSVersion?: number, includeTags: string[], excludeTags: string[], - appBinaryId?: string + appBinaryId?: string, + deviceLocale?: string, } // irrelevant data has been factored out from this model diff --git a/README.md b/README.md index 646f68e..ef203d7 100644 --- a/README.md +++ b/README.md @@ -194,17 +194,30 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar ```yaml - id: upload - uses: igorsmotto/action-maestro-cloud@v1.6.0 + uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - - uses: igorsmotto/action-maestro-cloud@v1.6.0 + - uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} ``` +# Configuring the locale for the device where the flows will be executed + +To switch the device locale on a remote device from a default one (en_US) `device-locale` parameter should be used. The value is a combination of lowercase ISO-639-1 code and uppercase ISO-3166-1 code, i.e. "de_DE" for Germany. + +```yaml +- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 + with: + api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + app-file: app.zip + device-locale: de_DE + +``` + # Accessing output The following output variables are set by the action: diff --git a/action.yml b/action.yml index 01cdb84..0c2d5c9 100644 --- a/action.yml +++ b/action.yml @@ -34,6 +34,9 @@ inputs: ios-version: description: "iOS version to run your flow against" required: false + device-locale: + description: "Device locale to run your flow against" + required: false include-tags: description: "List of tags that will remove flows that does not have the provided tags" required: false diff --git a/index.ts b/index.ts index edd2f17..ee75889 100644 --- a/index.ts +++ b/index.ts @@ -61,7 +61,8 @@ const run = async () => { iOSVersion, includeTags, excludeTags, - appBinaryId + appBinaryId, + deviceLocale, } = await getParameters() let appFile = null @@ -93,6 +94,7 @@ const run = async () => { includeTags: includeTags, excludeTags: excludeTags, appBinaryId: appBinaryId || undefined, + deviceLocale: deviceLocale || undefined, } const { uploadId, teamId, targetId: appId, appBinaryId: uploadedBinaryId } = await client.uploadRequest( diff --git a/params.ts b/params.ts index 0ebe57f..c5bceb9 100644 --- a/params.ts +++ b/params.ts @@ -22,6 +22,7 @@ export type Params = { includeTags: string[], excludeTags: string[], appBinaryId: string, + deviceLocale?: string, } function getBranchName(): string { @@ -123,6 +124,8 @@ export async function getParameters(): Promise { throw new Error("Either app-file or app-binary-id must be used") } + const deviceLocale = core.getInput('device-locale', { required: false }) + var env: { [key: string]: string } = {} env = core.getMultilineInput('env', { required: false }) .map(it => { @@ -165,6 +168,7 @@ export async function getParameters(): Promise { iOSVersion, includeTags, excludeTags, - appBinaryId + appBinaryId, + deviceLocale, } } \ No newline at end of file From ec9d3f5c89258a6b209c372cddd1711388b1f5fe Mon Sep 17 00:00:00 2001 From: artem888 Date: Fri, 10 Nov 2023 14:47:38 +0100 Subject: [PATCH 28/51] Release: Version 1.7.0 (#27) --- README.md | 32 ++++++++++++++++---------------- dist/index.js | 7 +++++-- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ef203d7..abcb3da 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -136,7 +136,7 @@ If you don't want the action to wait until the Upload has been completed as is t If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -152,7 +152,7 @@ You can use Maestro (Tags)[https://maestro.mobile.dev/cli/tags] to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -167,7 +167,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -181,7 +181,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -194,12 +194,12 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 + uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - - uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 + - uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} @@ -210,7 +210,7 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar To switch the device locale on a remote device from a default one (en_US) `device-locale` parameter should be used. The value is a combination of lowercase ISO-639-1 code and uppercase ISO-3166-1 code, i.e. "de_DE" for Germany. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -231,7 +231,7 @@ In order to access these variables you can use the following approach: ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 + uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: diff --git a/dist/index.js b/dist/index.js index 5463747..d4bb85a 100644 --- a/dist/index.js +++ b/dist/index.js @@ -47046,7 +47046,7 @@ const getConsoleUrl = (uploadId, teamId, appId) => { }; exports.getConsoleUrl = getConsoleUrl; const run = () => __awaiter(void 0, void 0, void 0, function* () { - const { apiKey, apiUrl, name, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, iOSVersion, includeTags, excludeTags, appBinaryId } = yield (0, params_1.getParameters)(); + const { apiKey, apiUrl, name, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, iOSVersion, includeTags, excludeTags, appBinaryId, deviceLocale, } = yield (0, params_1.getParameters)(); let appFile = null; if (appFilePath !== "") { appFile = yield (0, app_file_1.validateAppFile)(yield (0, archive_utils_1.zipIfFolder)(appFilePath)); @@ -47071,6 +47071,7 @@ const run = () => __awaiter(void 0, void 0, void 0, function* () { includeTags: includeTags, excludeTags: excludeTags, appBinaryId: appBinaryId || undefined, + deviceLocale: deviceLocale || undefined, }; const { uploadId, teamId, targetId: appId, appBinaryId: uploadedBinaryId } = yield client.uploadRequest(request, appFile && appFile.path, workspaceZip, mappingFile && (yield (0, archive_utils_1.zipIfFolder)(mappingFile))); const consoleUrl = (0, exports.getConsoleUrl)(uploadId, teamId, appId); @@ -47252,6 +47253,7 @@ function getParameters() { if (!(appFilePath !== "") !== (appBinaryId !== "")) { throw new Error("Either app-file or app-binary-id must be used"); } + const deviceLocale = core.getInput('device-locale', { required: false }); var env = {}; env = core.getMultilineInput('env', { required: false }) .map(it => { @@ -47290,7 +47292,8 @@ function getParameters() { iOSVersion, includeTags, excludeTags, - appBinaryId + appBinaryId, + deviceLocale, }; }); } From 4499213137baf4f0d8817e0e86e46d7e0dc2cb02 Mon Sep 17 00:00:00 2001 From: Igor Lema <48068670+igorsmotto@users.noreply.github.com> Date: Wed, 15 Nov 2023 13:24:30 -0300 Subject: [PATCH 29/51] feat: add timeout parameter (#30) This PR allows the timeout of the action to be controlled, just like we have today in `maestro cloud` CLI --- README.md | 10 ++++++++++ StatusPoller.ts | 22 ++++++++++++++-------- action.yml | 3 +++ index.ts | 3 ++- params.ts | 8 ++++++++ 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index abcb3da..05597a1 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,16 @@ If you don't want the action to wait until the Upload has been completed as is t async: true ``` +Alternatively, you might want to still wait for the action but would like to configure the timeout period, set `timeout` argument to a number of minutes: + +```yaml +- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 + with: + api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + app-file: app.zip + timeout: 90 # Wait for 90 minutes +``` + # Adding environment variables If you want to pass environment variables along with your upload, add a multiline `env` argument: diff --git a/StatusPoller.ts b/StatusPoller.ts index 8027753..a9f9937 100644 --- a/StatusPoller.ts +++ b/StatusPoller.ts @@ -16,10 +16,10 @@ const getCanceledStatusMessage = (reason?: CancellationReason): string => { case CancellationReason.TIMEOUT: return 'Timeout'; - + case CancellationReason.INFRA_ERROR: default: - return 'Canceled'; + return 'Canceled'; } } @@ -70,11 +70,12 @@ const printUploadResult = (status: BenchmarkStatus, flows: Flow[]) => { export default class StatusPoller { timeout: NodeJS.Timeout | undefined completedFlows: { [flowName: string]: string } = {} + stopped: Boolean = false constructor( private client: ApiClient, private uploadId: string, - private consoleUrl: string + private consoleUrl: string, ) { } markFailed(msg: string) { @@ -83,7 +84,7 @@ export default class StatusPoller { onError(errMsg: string, error?: any) { let msg = `${errMsg}` - if (!!error) msg += ` - receveied error ${error}` + if (!!error) msg += ` - received error ${error}` msg += `. View the Upload in the console for more information: ${this.consoleUrl}` this.markFailed(msg) } @@ -92,6 +93,10 @@ export default class StatusPoller { sleep: number, prevErrorCount: number = 0 ) { + if (this.stopped) { + return + } + try { const { completed, status, flows } = await this.client.getUploadStatus(this.uploadId) for (const flow of flows.filter(isCompleted)) { @@ -142,17 +147,18 @@ export default class StatusPoller { } } - registerTimeout() { + registerTimeout(timeoutInMinutes?: number) { this.timeout = setTimeout(() => { warning(`Timed out waiting for Upload to complete. View the Upload in the console for more information: ${this.consoleUrl}`) - }, WAIT_TIMEOUT_MS) + this.stopped = true + }, timeoutInMinutes ? (timeoutInMinutes * 60 * 1000) : WAIT_TIMEOUT_MS) } teardown() { this.timeout && clearTimeout(this.timeout) } - startPolling() { + startPolling(timeout?: number) { try { this.poll(INTERVAL_MS) info('Waiting for analyses to complete...\n') @@ -160,6 +166,6 @@ export default class StatusPoller { this.markFailed(err instanceof Error ? err.message : `${err} `) } - this.registerTimeout() + this.registerTimeout(timeout) } } \ No newline at end of file diff --git a/action.yml b/action.yml index 0c2d5c9..3d8b34d 100644 --- a/action.yml +++ b/action.yml @@ -43,6 +43,9 @@ inputs: exclude-tags: description: "List of tags that will remove flows containing the provided tags" required: false + timeout: + description: "Minutes to timeout while waiting for results" + required: false runs: using: "node16" main: "dist/index.js" diff --git a/index.ts b/index.ts index ee75889..82ff16b 100644 --- a/index.ts +++ b/index.ts @@ -63,6 +63,7 @@ const run = async () => { excludeTags, appBinaryId, deviceLocale, + timeout, } = await getParameters() let appFile = null @@ -108,7 +109,7 @@ const run = async () => { core.setOutput('MAESTRO_CLOUD_CONSOLE_URL', consoleUrl) core.setOutput('MAESTRO_CLOUD_APP_BINARY_ID', uploadedBinaryId) - !async && new StatusPoller(client, uploadId, consoleUrl).startPolling() + !async && new StatusPoller(client, uploadId, consoleUrl).startPolling(timeout) } run().catch(e => { diff --git a/params.ts b/params.ts index c5bceb9..edf55d5 100644 --- a/params.ts +++ b/params.ts @@ -23,6 +23,7 @@ export type Params = { excludeTags: string[], appBinaryId: string, deviceLocale?: string, + timeout?: number, } function getBranchName(): string { @@ -89,6 +90,10 @@ function getIOSVersion(iosVersion?: string): number | undefined { return iosVersion ? +iosVersion : undefined } +function getTimeout(timeout?: string): number | undefined { + return timeout ? +timeout : undefined +} + function parseTags(tags?: string): string[] { if (tags === undefined || tags === '') return [] @@ -125,6 +130,7 @@ export async function getParameters(): Promise { } const deviceLocale = core.getInput('device-locale', { required: false }) + const timeoutString = core.getInput('timeout', { required: false }) var env: { [key: string]: string } = {} env = core.getMultilineInput('env', { required: false }) @@ -149,6 +155,7 @@ export async function getParameters(): Promise { const pullRequestId = getPullRequestId() const androidApiLevel = getAndroidApiLevel(androidApiLevelString) const iOSVersion = getIOSVersion(iOSVersionString) + const timeout = getTimeout(timeoutString) return { apiUrl, @@ -170,5 +177,6 @@ export async function getParameters(): Promise { excludeTags, appBinaryId, deviceLocale, + timeout, } } \ No newline at end of file From 796e192e95f56166ef9a0bb5b77e614121b052af Mon Sep 17 00:00:00 2001 From: Igor Lema <48068670+igorsmotto@users.noreply.github.com> Date: Wed, 15 Nov 2023 13:27:23 -0300 Subject: [PATCH 30/51] Release: Version 1.8.0 (#31) Adds timeout argument --- README.md | 34 +++++++++++++++++----------------- dist/index.js | 25 ++++++++++++++++++------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 05597a1..c18e69c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -134,7 +134,7 @@ If you don't want the action to wait until the Upload has been completed as is t Alternatively, you might want to still wait for the action but would like to configure the timeout period, set `timeout` argument to a number of minutes: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -146,7 +146,7 @@ Alternatively, you might want to still wait for the action but would like to con If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -162,7 +162,7 @@ You can use Maestro (Tags)[https://maestro.mobile.dev/cli/tags] to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -177,7 +177,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -191,7 +191,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -204,12 +204,12 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 + uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - - uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 + - uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} @@ -220,7 +220,7 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar To switch the device locale on a remote device from a default one (en_US) `device-locale` parameter should be used. The value is a combination of lowercase ISO-639-1 code and uppercase ISO-3166-1 code, i.e. "de_DE" for Germany. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -241,7 +241,7 @@ In order to access these variables you can use the following approach: ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 + uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: diff --git a/dist/index.js b/dist/index.js index d4bb85a..1c57023 100644 --- a/dist/index.js +++ b/dist/index.js @@ -46725,6 +46725,7 @@ class StatusPoller { this.uploadId = uploadId; this.consoleUrl = consoleUrl; this.completedFlows = {}; + this.stopped = false; } markFailed(msg) { core.setFailed(msg); @@ -46732,12 +46733,15 @@ class StatusPoller { onError(errMsg, error) { let msg = `${errMsg}`; if (!!error) - msg += ` - receveied error ${error}`; + msg += ` - received error ${error}`; msg += `. View the Upload in the console for more information: ${this.consoleUrl}`; this.markFailed(msg); } poll(sleep, prevErrorCount = 0) { return __awaiter(this, void 0, void 0, function* () { + if (this.stopped) { + return; + } try { const { completed, status, flows } = yield this.client.getUploadStatus(this.uploadId); for (const flow of flows.filter(isCompleted)) { @@ -46790,15 +46794,16 @@ class StatusPoller { } }); } - registerTimeout() { + registerTimeout(timeoutInMinutes) { this.timeout = setTimeout(() => { (0, log_1.warning)(`Timed out waiting for Upload to complete. View the Upload in the console for more information: ${this.consoleUrl}`); - }, WAIT_TIMEOUT_MS); + this.stopped = true; + }, timeoutInMinutes ? (timeoutInMinutes * 60 * 1000) : WAIT_TIMEOUT_MS); } teardown() { this.timeout && clearTimeout(this.timeout); } - startPolling() { + startPolling(timeout) { try { this.poll(INTERVAL_MS); (0, log_1.info)('Waiting for analyses to complete...\n'); @@ -46806,7 +46811,7 @@ class StatusPoller { catch (err) { this.markFailed(err instanceof Error ? err.message : `${err} `); } - this.registerTimeout(); + this.registerTimeout(timeout); } } exports["default"] = StatusPoller; @@ -47046,7 +47051,7 @@ const getConsoleUrl = (uploadId, teamId, appId) => { }; exports.getConsoleUrl = getConsoleUrl; const run = () => __awaiter(void 0, void 0, void 0, function* () { - const { apiKey, apiUrl, name, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, iOSVersion, includeTags, excludeTags, appBinaryId, deviceLocale, } = yield (0, params_1.getParameters)(); + const { apiKey, apiUrl, name, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, iOSVersion, includeTags, excludeTags, appBinaryId, deviceLocale, timeout, } = yield (0, params_1.getParameters)(); let appFile = null; if (appFilePath !== "") { appFile = yield (0, app_file_1.validateAppFile)(yield (0, archive_utils_1.zipIfFolder)(appFilePath)); @@ -47078,7 +47083,7 @@ const run = () => __awaiter(void 0, void 0, void 0, function* () { (0, log_1.info)(`Visit the web console for more details about the upload: ${consoleUrl}\n`); core.setOutput('MAESTRO_CLOUD_CONSOLE_URL', consoleUrl); core.setOutput('MAESTRO_CLOUD_APP_BINARY_ID', uploadedBinaryId); - !async && new StatusPoller_1.default(client, uploadId, consoleUrl).startPolling(); + !async && new StatusPoller_1.default(client, uploadId, consoleUrl).startPolling(timeout); }); run().catch(e => { core.setFailed(`Error running Maestro Cloud Upload Action: ${e.message}`); @@ -47223,6 +47228,9 @@ function getAndroidApiLevel(apiLevel) { function getIOSVersion(iosVersion) { return iosVersion ? +iosVersion : undefined; } +function getTimeout(timeout) { + return timeout ? +timeout : undefined; +} function parseTags(tags) { if (tags === undefined || tags === '') return []; @@ -47254,6 +47262,7 @@ function getParameters() { throw new Error("Either app-file or app-binary-id must be used"); } const deviceLocale = core.getInput('device-locale', { required: false }); + const timeoutString = core.getInput('timeout', { required: false }); var env = {}; env = core.getMultilineInput('env', { required: false }) .map(it => { @@ -47274,6 +47283,7 @@ function getParameters() { const pullRequestId = getPullRequestId(); const androidApiLevel = getAndroidApiLevel(androidApiLevelString); const iOSVersion = getIOSVersion(iOSVersionString); + const timeout = getTimeout(timeoutString); return { apiUrl, name, @@ -47294,6 +47304,7 @@ function getParameters() { excludeTags, appBinaryId, deviceLocale, + timeout, }; }); } From fa6929dd33a23307bf4a68cf7a21b1a7d7df63f8 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Mon, 27 Nov 2023 08:35:04 +0100 Subject: [PATCH 31/51] Fix broken link in README (#32) And add another useful link. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c18e69c..8e1097c 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ If you want to pass environment variables along with your upload, add a multilin # Using tags -You can use Maestro (Tags)[https://maestro.mobile.dev/cli/tags] to filter which Flows to send to Maestro Cloud: +You can use Maestro [Tags](https://maestro.mobile.dev/cli/tags) to filter which Flows to send to Maestro Cloud: You can either pass a single value, or comma-separated (`,`) values. @@ -174,7 +174,7 @@ You can either pass a single value, or comma-separated (`,`) values. You can specify what Android API level to use when running in Maestro Cloud using the `android-api-level` parameter. -The default API level is 30. +The default API level is 30. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available Android emulator API levels. ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 @@ -188,7 +188,7 @@ The default API level is 30. You can specify what **major** iOS Version to use when running in Maestro Cloud using the `ios-version` parameter. -The default iOS version is 15. +The default iOS version is 15. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available iOS simulator versions. ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 From 94fbd1167b8885b0be8fe9b1ad2185f0164019b3 Mon Sep 17 00:00:00 2001 From: Andrew <35735666+meatnordrink@users.noreply.github.com> Date: Thu, 18 Jan 2024 06:01:23 -0500 Subject: [PATCH 32/51] Update README with output variable types (#33) The Maestro GH action returns variables that contain information about the results of the run which can be very useful in CI. These variables are noted in the README, but little information is given as to what those variables will actually contain. This means devs need to run fake CI runs, which take time and money, to see the type of output they're going to get. This PR simply clarifies what type the output variables will be, for those types which are not simply strings, hopefully saving devs time in debugging GH Actions workflows. --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 8e1097c..e46f2ee 100644 --- a/README.md +++ b/README.md @@ -255,3 +255,25 @@ In order to access these variables you can use the following approach: echo "Upload Status: ${{ steps.upload.outputs.MAESTRO_CLOUD_UPLOAD_STATUS }}" echo "App Binary ID:: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }}" ``` + +## Output types + +- `MAESTRO_CLOUD_UPLOAD_STATUS` + + Any of the following values: + ``` + PENDING + RUNNING + SUCCESS + ERROR + CANCELED + WARNING + ``` + +- `MAESTRO_CLOUD_FLOW_RESULTS` + + An array of objects with at least `name`, `status`, and `errors` fields. + ```json + [{"name":"my-first-flow","status":"SUCCESS","errors":[]},{"name":"my-second-flow","status":"SUCCESS","errors":[]},{"name":"my-cancelled-flow","status":"CANCELED","errors":[],"cancellationReason":"INFRA_ERROR"}] + ``` + From 36c06a35c1a8e0c1eb2a3e8292d6478590a90261 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Fri, 16 Feb 2024 10:41:03 +0100 Subject: [PATCH 33/51] chore: bump to node 20 (#36) Fixes #35 --- _release.sh | 2 +- action.yml | 2 +- package.json | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/_release.sh b/_release.sh index 1ad4c7b..403e9ed 100755 --- a/_release.sh +++ b/_release.sh @@ -10,7 +10,7 @@ if [[ -z "$VERSION" ]]; then exit 1 fi -ncc build index.ts +npm run build sed -i.bkp "s/action-maestro-cloud@v.*/action-maestro-cloud@v${VERSION}/g" README.md git add -A git commit --allow-empty -m "Version ${VERSION}" diff --git a/action.yml b/action.yml index 3d8b34d..4409093 100644 --- a/action.yml +++ b/action.yml @@ -47,7 +47,7 @@ inputs: description: "Minutes to timeout while waiting for results" required: false runs: - using: "node16" + using: "node20" main: "dist/index.js" branding: icon: "upload-cloud" diff --git a/package.json b/package.json index 07560a2..cb81298 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "", "main": "index.js", "scripts": { + "build": "ncc build index.ts", "release": "./_release.sh" }, "repository": { @@ -33,4 +34,4 @@ "node-fetch": "^3.2.10", "node-stream-zip": "^1.15.0" } -} +} \ No newline at end of file From c974d6dd20ca0a4162b8cb0e00895bd043ad376f Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Fri, 16 Feb 2024 10:41:19 +0100 Subject: [PATCH 34/51] Version 1.8.1 --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index e46f2ee..65bb8c2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -134,7 +134,7 @@ If you don't want the action to wait until the Upload has been completed as is t Alternatively, you might want to still wait for the action but would like to configure the timeout period, set `timeout` argument to a number of minutes: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -146,7 +146,7 @@ Alternatively, you might want to still wait for the action but would like to con If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -162,7 +162,7 @@ You can use Maestro [Tags](https://maestro.mobile.dev/cli/tags) to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -177,7 +177,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available Android emulator API levels. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -191,7 +191,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available iOS simulator versions. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -204,12 +204,12 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 + uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - - uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 + - uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} @@ -220,7 +220,7 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar To switch the device locale on a remote device from a default one (en_US) `device-locale` parameter should be used. The value is a combination of lowercase ISO-639-1 code and uppercase ISO-3166-1 code, i.e. "de_DE" for Germany. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 +- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -241,7 +241,7 @@ In order to access these variables you can use the following approach: ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 + uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: From 398c36732fde9e8dfb114c7eddd12c2f2782c2ce Mon Sep 17 00:00:00 2001 From: Dominic Letz Date: Fri, 7 Jun 2024 09:36:33 +0200 Subject: [PATCH 35/51] Update README.md - fix double colon (#38) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 65bb8c2..4e6cf51 100644 --- a/README.md +++ b/README.md @@ -253,7 +253,7 @@ In order to access these variables you can use the following approach: echo "Console URL: ${{ steps.upload.outputs.MAESTRO_CLOUD_CONSOLE_URL }}" echo "Flow Results: ${{ steps.upload.outputs.MAESTRO_CLOUD_FLOW_RESULTS }}" echo "Upload Status: ${{ steps.upload.outputs.MAESTRO_CLOUD_UPLOAD_STATUS }}" - echo "App Binary ID:: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }}" + echo "App Binary ID: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }}" ``` ## Output types From ae2da373f4c40dc531181927aaa564beade59772 Mon Sep 17 00:00:00 2001 From: Proksh Luthra <35415752+proksh@users.noreply.github.com> Date: Fri, 30 Aug 2024 16:44:52 +0530 Subject: [PATCH 36/51] Added robin actions (#42) --- ApiClient.ts | 196 ++++++++++++++++++++++++++++++++---------------- dist/index.js | 203 +++++++++++++++++++++++++++++++++++--------------- index.ts | 157 +++++++++++++++++++++++++------------- params.ts | 93 ++++++++++++++--------- 4 files changed, 436 insertions(+), 213 deletions(-) diff --git a/ApiClient.ts b/ApiClient.ts index 4512b22..856d7ef 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -1,4 +1,4 @@ -import fetch, { FetchError, fileFromSync, FormData } from 'node-fetch'; +import fetch, { fileFromSync, FormData } from 'node-fetch' export enum BenchmarkStatus { PENDING = 'PENDING', @@ -9,33 +9,57 @@ export enum BenchmarkStatus { WARNING = 'WARNING', } -export type UploadRequest = { +export type CloudUploadRequest = { benchmarkName?: string repoOwner?: string repoName?: string pullRequestId?: string - branch?: string, - commitSha?: string, - env?: { [key: string]: string }, - agent: string, - androidApiLevel?: number, - iOSVersion?: number, - includeTags: string[], - excludeTags: string[], - appBinaryId?: string, - deviceLocale?: string, + branch?: string + commitSha?: string + env?: { [key: string]: string } + agent: string + androidApiLevel?: number + iOSVersion?: number + includeTags: string[] + excludeTags: string[] + appBinaryId?: string + deviceLocale?: string +} + +export type RobinUploadRequest = { + projectId: string + repoOwner?: string + repoName?: string + pullRequestId?: string + branch?: string + commitSha?: string + env?: { [key: string]: string } + agent: string + androidApiLevel?: number + iOSVersion?: number + includeTags: string[] + excludeTags: string[] + appBinaryId?: string + deviceLocale?: string } // irrelevant data has been factored out from this model export type UploadResponse = { - uploadId: string, - teamId: string, - targetId: string, + uploadId: string + teamId: string + targetId: string + appBinaryId: string +} + +export type RobinUploadResponse = { + uploadId: string + orgId: string + appId: string appBinaryId: string } export class UploadStatusError { - constructor(public status: number, public text: string) { } + constructor(public status: number, public text: string) {} } export enum CancellationReason { @@ -46,89 +70,133 @@ export enum CancellationReason { } export type Flow = { - name: string, - status: BenchmarkStatus, - errors?: string[], + name: string + status: BenchmarkStatus + errors?: string[] cancellationReason?: CancellationReason } export type UploadStatusResponse = { - uploadId: string, - status: BenchmarkStatus, - completed: boolean, + uploadId: string + status: BenchmarkStatus + completed: boolean flows: Flow[] } export default class ApiClient { - constructor( private apiKey: string, - private apiUrl: string - ) { } + private apiUrl: string, + private projectId?: string + ) {} - async uploadRequest( - request: UploadRequest, + async cloudUploadRequest( + request: CloudUploadRequest, appFile: string | null, workspaceZip: string | null, - mappingFile: string | null, + mappingFile: string | null ): Promise { const formData = new FormData() - - formData.set('request', JSON.stringify(request)) if (appFile) { - formData.set( - 'app_binary', - fileFromSync(appFile) - ) + formData.set('app_binary', fileFromSync(appFile)) } - if (workspaceZip) { - formData.set( - 'workspace', - fileFromSync(workspaceZip) - ); + formData.set('workspace', fileFromSync(workspaceZip)) } - if (mappingFile) { - formData.set( - 'mapping', - fileFromSync(mappingFile) - ) + formData.set('mapping', fileFromSync(mappingFile)) } - + formData.set('request', JSON.stringify(request)) const res = await fetch(`${this.apiUrl}/v2/upload`, { method: 'POST', headers: { - 'Authorization': `Bearer ${this.apiKey}`, + Authorization: `Bearer ${this.apiKey}`, }, - body: formData - }); + body: formData, + }) if (!res.ok) { - const body = await res.text(); - throw new Error(`Request to ${res.url} failed (${res.status}): ${body}`); + const body = await res.text() + throw new Error(`Request to ${res.url} failed (${res.status}): ${body}`) } - return await res.json() as UploadResponse; + return (await res.json()) as UploadResponse } - async getUploadStatus( - uploadId: string, - ): Promise { - const res = await fetch(`${this.apiUrl}/v2/upload/${uploadId}/status?includeErrors=true`, { - method: 'GET', + async robinUploadRequest( + request: RobinUploadRequest, + appFile: string | null, + workspaceZip: string | null, + mappingFile: string | null + ): Promise { + const formData = new FormData() + + if (appFile) { + formData.set('app_binary', fileFromSync(appFile)) + } + if (workspaceZip) { + formData.set('workspace', fileFromSync(workspaceZip)) + } + if (mappingFile) { + formData.set('mapping', fileFromSync(mappingFile)) + } + formData.set('request', JSON.stringify(request)) + + const res = await fetch(`${this.apiUrl}/runMaestroTest`, { + method: 'POST', headers: { - 'Authorization': `Bearer ${this.apiKey}`, + Authorization: `Bearer ${this.apiKey}`, }, - }); + body: formData, + }) if (!res.ok) { - const body = await res.text(); - throw new Error(`Request to ${res.url} failed (${res.status}): ${body}`); + const body = await res.text() + throw new Error(`Request to ${res.url} failed (${res.status}): ${body}`) } + return (await res.json()) as RobinUploadResponse + } - if (res.status >= 400) { - const text = await res.text(); - Promise.reject(new UploadStatusError(res.status, text)); + async getUploadStatus(uploadId: string): Promise { + // If Project Id exist - Hit robin + if (!!this.projectId) { + const res = await fetch(`${this.apiUrl}/upload/${uploadId}`, { + method: 'GET', + headers: { + Authorization: `Bearer ${this.apiKey}`, + }, + }) + if (!res.ok) { + const body = await res.text() + throw new Error(`Request to ${res.url} failed (${res.status}): ${body}`) + } + + if (res.status >= 400) { + const text = await res.text() + Promise.reject(new UploadStatusError(res.status, text)) + } + + return (await res.json()) as UploadStatusResponse } + // Else if no project id - Hit Cloud + else { + const res = await fetch( + `${this.apiUrl}/v2/upload/${uploadId}/status?includeErrors=true`, + { + method: 'GET', + headers: { + Authorization: `Bearer ${this.apiKey}`, + }, + } + ) + if (!res.ok) { + const body = await res.text() + throw new Error(`Request to ${res.url} failed (${res.status}): ${body}`) + } - return await res.json() as UploadStatusResponse; + if (res.status >= 400) { + const text = await res.text() + Promise.reject(new UploadStatusError(res.status, text)) + } + + return (await res.json()) as UploadStatusResponse + } } } diff --git a/dist/index.js b/dist/index.js index 1c57023..dd40b67 100644 --- a/dist/index.js +++ b/dist/index.js @@ -46564,14 +46564,14 @@ var CancellationReason; CancellationReason["TIMEOUT"] = "TIMEOUT"; })(CancellationReason = exports.CancellationReason || (exports.CancellationReason = {})); class ApiClient { - constructor(apiKey, apiUrl) { + constructor(apiKey, apiUrl, projectId) { this.apiKey = apiKey; this.apiUrl = apiUrl; + this.projectId = projectId; } - uploadRequest(request, appFile, workspaceZip, mappingFile) { + cloudUploadRequest(request, appFile, workspaceZip, mappingFile) { return __awaiter(this, void 0, void 0, function* () { const formData = new node_fetch_1.FormData(); - formData.set('request', JSON.stringify(request)); if (appFile) { formData.set('app_binary', (0, node_fetch_1.fileFromSync)(appFile)); } @@ -46581,37 +46581,86 @@ class ApiClient { if (mappingFile) { formData.set('mapping', (0, node_fetch_1.fileFromSync)(mappingFile)); } + formData.set('request', JSON.stringify(request)); const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/v2/upload`, { method: 'POST', headers: { - 'Authorization': `Bearer ${this.apiKey}`, + Authorization: `Bearer ${this.apiKey}`, }, - body: formData + body: formData, }); if (!res.ok) { const body = yield res.text(); throw new Error(`Request to ${res.url} failed (${res.status}): ${body}`); } - return yield res.json(); + return (yield res.json()); }); } - getUploadStatus(uploadId) { + robinUploadRequest(request, appFile, workspaceZip, mappingFile) { return __awaiter(this, void 0, void 0, function* () { - const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/v2/upload/${uploadId}/status?includeErrors=true`, { - method: 'GET', + const formData = new node_fetch_1.FormData(); + if (appFile) { + formData.set('app_binary', (0, node_fetch_1.fileFromSync)(appFile)); + } + if (workspaceZip) { + formData.set('workspace', (0, node_fetch_1.fileFromSync)(workspaceZip)); + } + if (mappingFile) { + formData.set('mapping', (0, node_fetch_1.fileFromSync)(mappingFile)); + } + formData.set('request', JSON.stringify(request)); + const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/runMaestroTest`, { + method: 'POST', headers: { - 'Authorization': `Bearer ${this.apiKey}`, + Authorization: `Bearer ${this.apiKey}`, }, + body: formData, }); if (!res.ok) { const body = yield res.text(); throw new Error(`Request to ${res.url} failed (${res.status}): ${body}`); } - if (res.status >= 400) { - const text = yield res.text(); - Promise.reject(new UploadStatusError(res.status, text)); + return (yield res.json()); + }); + } + getUploadStatus(uploadId) { + return __awaiter(this, void 0, void 0, function* () { + // If Project Id exist - Hit robin + if (!!this.projectId) { + const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/upload/${uploadId}`, { + method: 'GET', + headers: { + Authorization: `Bearer ${this.apiKey}`, + }, + }); + if (!res.ok) { + const body = yield res.text(); + throw new Error(`Request to ${res.url} failed (${res.status}): ${body}`); + } + if (res.status >= 400) { + const text = yield res.text(); + Promise.reject(new UploadStatusError(res.status, text)); + } + return (yield res.json()); + } + // Else if no project id - Hit Cloud + else { + const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/v2/upload/${uploadId}/status?includeErrors=true`, { + method: 'GET', + headers: { + Authorization: `Bearer ${this.apiKey}`, + }, + }); + if (!res.ok) { + const body = yield res.text(); + throw new Error(`Request to ${res.url} failed (${res.status}): ${body}`); + } + if (res.status >= 400) { + const text = yield res.text(); + Promise.reject(new UploadStatusError(res.status, text)); + } + return (yield res.json()); } - return yield res.json(); }); } } @@ -47007,7 +47056,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getConsoleUrl = void 0; const core = __importStar(__nccwpck_require__(2186)); const ApiClient_1 = __importDefault(__nccwpck_require__(9494)); const app_file_1 = __nccwpck_require__(9617); @@ -47019,7 +47067,7 @@ const log_1 = __nccwpck_require__(3826); const knownAppTypes = ['ANDROID_APK', 'IOS_BUNDLE']; const listFilesInDirectory = () => { const files = (0, fs_1.readdirSync)('.', { withFileTypes: true }); - console.log("Directory contents:"); + console.log('Directory contents:'); for (const f of files) { console.log(f.isDirectory() ? `${f.name}/` : f.name); } @@ -47029,15 +47077,15 @@ const createWorkspaceZip = (workspaceFolder) => __awaiter(void 0, void 0, void 0 if (resolvedWorkspaceFolder === null || (workspaceFolder === null || workspaceFolder === void 0 ? void 0 : workspaceFolder.length) === 0) { if ((0, fs_1.existsSync)('.maestro')) { resolvedWorkspaceFolder = '.maestro'; - (0, log_1.info)("Packaging .maestro folder"); + (0, log_1.info)('Packaging .maestro folder'); } else if ((0, fs_1.existsSync)('.mobiledev')) { resolvedWorkspaceFolder = '.mobiledev'; - (0, log_1.info)("Packaging .mobiledev folder"); + (0, log_1.info)('Packaging .mobiledev folder'); } else { listFilesInDirectory(); - throw new Error("Default workspace directory does not exist: .maestro/"); + throw new Error('Default workspace directory does not exist: .maestro/'); } } else if (!(0, fs_1.existsSync)(resolvedWorkspaceFolder)) { @@ -47046,46 +47094,76 @@ const createWorkspaceZip = (workspaceFolder) => __awaiter(void 0, void 0, void 0 yield (0, archive_utils_1.zipFolder)(resolvedWorkspaceFolder, 'workspace.zip'); return 'workspace.zip'; }); -const getConsoleUrl = (uploadId, teamId, appId) => { - return `https://console.mobile.dev/uploads/${uploadId}?teamId=${teamId}&appId=${appId}`; -}; -exports.getConsoleUrl = getConsoleUrl; const run = () => __awaiter(void 0, void 0, void 0, function* () { - const { apiKey, apiUrl, name, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, iOSVersion, includeTags, excludeTags, appBinaryId, deviceLocale, timeout, } = yield (0, params_1.getParameters)(); + const { apiKey, apiUrl, name, appFilePath, mappingFile, workspaceFolder, branchName, commitSha, repoOwner, repoName, pullRequestId, env, async, androidApiLevel, iOSVersion, includeTags, excludeTags, appBinaryId, deviceLocale, timeout, projectId, } = yield (0, params_1.getParameters)(); let appFile = null; - if (appFilePath !== "") { + if (appFilePath !== '') { appFile = yield (0, app_file_1.validateAppFile)(yield (0, archive_utils_1.zipIfFolder)(appFilePath)); if (!knownAppTypes.includes(appFile.type)) { throw new Error(`Unsupported app file type: ${appFile.type}`); } } const workspaceZip = yield createWorkspaceZip(workspaceFolder); - const client = new ApiClient_1.default(apiKey, apiUrl); - (0, log_1.info)("Uploading to Maestro Cloud"); - const request = { - benchmarkName: name, - branch: branchName, - commitSha: commitSha, - repoOwner: repoOwner, - repoName: repoName, - pullRequestId: pullRequestId, - env: env, - agent: 'github', - androidApiLevel: androidApiLevel, - iOSVersion: iOSVersion, - includeTags: includeTags, - excludeTags: excludeTags, - appBinaryId: appBinaryId || undefined, - deviceLocale: deviceLocale || undefined, - }; - const { uploadId, teamId, targetId: appId, appBinaryId: uploadedBinaryId } = yield client.uploadRequest(request, appFile && appFile.path, workspaceZip, mappingFile && (yield (0, archive_utils_1.zipIfFolder)(mappingFile))); - const consoleUrl = (0, exports.getConsoleUrl)(uploadId, teamId, appId); - (0, log_1.info)(`Visit the web console for more details about the upload: ${consoleUrl}\n`); - core.setOutput('MAESTRO_CLOUD_CONSOLE_URL', consoleUrl); - core.setOutput('MAESTRO_CLOUD_APP_BINARY_ID', uploadedBinaryId); - !async && new StatusPoller_1.default(client, uploadId, consoleUrl).startPolling(timeout); + const client = new ApiClient_1.default(apiKey, apiUrl, projectId); + if (!!projectId) { + /** + * If project Exist - Its Robin + */ + (0, log_1.info)('Uploading to Robin Basic'); + const request = { + projectId: projectId, + repoOwner: repoOwner, + repoName: repoName, + agent: 'github', + branch: branchName, + commitSha: commitSha, + pullRequestId: pullRequestId, + env: env, + androidApiLevel: androidApiLevel, + iOSVersion: iOSVersion, + includeTags: includeTags, + excludeTags: excludeTags, + appBinaryId: appBinaryId || undefined, + deviceLocale: deviceLocale || undefined, + }; + const { uploadId, orgId, appId, appBinaryId: appBinaryIdResponse, } = yield client.robinUploadRequest(request, appFile && appFile.path, workspaceZip, mappingFile && (yield (0, archive_utils_1.zipIfFolder)(mappingFile))); + const consoleUrl = `https://copilot.mobile.dev/project/${projectId}/maestro-test/app/${appId}/upload/${uploadId}`; + core.setOutput('ROBIN_CONSOLE_URL', consoleUrl); + core.setOutput('ROBIN_APP_BINARY_ID', appBinaryIdResponse); + !async && + new StatusPoller_1.default(client, uploadId, consoleUrl).startPolling(timeout); + } + else { + /** + * If project Exist - Its Cloud + */ + (0, log_1.info)('Uploading to Maestro Cloud'); + const request = { + benchmarkName: name, + repoOwner: repoOwner, + repoName: repoName, + agent: 'github', + branch: branchName, + commitSha: commitSha, + pullRequestId: pullRequestId, + env: env, + androidApiLevel: androidApiLevel, + iOSVersion: iOSVersion, + includeTags: includeTags, + excludeTags: excludeTags, + appBinaryId: appBinaryId || undefined, + deviceLocale: deviceLocale || undefined, + }; + const { uploadId, teamId, appBinaryId: appBinaryIdResponse, } = yield client.cloudUploadRequest(request, appFile && appFile.path, workspaceZip, mappingFile && (yield (0, archive_utils_1.zipIfFolder)(mappingFile))); + const consoleUrl = `https://console.mobile.dev/uploads/${uploadId}?teamId=${teamId}&appId=${appBinaryIdResponse}`; + (0, log_1.info)(`Visit the web console for more details about the upload: ${consoleUrl}\n`); + core.setOutput('MAESTRO_CLOUD_CONSOLE_URL', consoleUrl); + core.setOutput('MAESTRO_CLOUD_APP_BINARY_ID', appBinaryId); + !async && + new StatusPoller_1.default(client, uploadId, consoleUrl).startPolling(timeout); + } }); -run().catch(e => { +run().catch((e) => { core.setFailed(`Error running Maestro Cloud Upload Action: ${e.message}`); }); @@ -47235,37 +47313,43 @@ function parseTags(tags) { if (tags === undefined || tags === '') return []; if (tags.includes(',')) { - const arrayTags = tags.split(',') - .map(it => it.trim()); + const arrayTags = tags.split(',').map((it) => it.trim()); if (!Array.isArray(arrayTags)) - throw new Error("tags must be an Array."); + throw new Error('tags must be an Array.'); return arrayTags; } return [tags]; } function getParameters() { return __awaiter(this, void 0, void 0, function* () { - const apiUrl = core.getInput('api-url', { required: false }) || 'https://api.mobile.dev'; + const projectId = core.getInput('project-id', { required: false }) || undefined; + const apiUrl = core.getInput('api-url', { required: false }) || + (projectId + ? `https://api.copilot.mobile.dev/v2/project/${projectId}` + : 'https://api.mobile.dev'); const name = core.getInput('name', { required: false }) || getInferredName(); const apiKey = core.getInput('api-key', { required: true }); const mappingFileInput = core.getInput('mapping-file', { required: false }); const workspaceFolder = core.getInput('workspace', { required: false }); const mappingFile = mappingFileInput && (0, app_file_1.validateMappingFile)(mappingFileInput); const async = core.getInput('async', { required: false }) === 'true'; - const androidApiLevelString = core.getInput('android-api-level', { required: false }); + const androidApiLevelString = core.getInput('android-api-level', { + required: false, + }); const iOSVersionString = core.getInput('ios-version', { required: false }); const includeTags = parseTags(core.getInput('include-tags', { required: false })); const excludeTags = parseTags(core.getInput('exclude-tags', { required: false })); const appFilePath = core.getInput('app-file', { required: false }); const appBinaryId = core.getInput('app-binary-id', { required: false }); - if (!(appFilePath !== "") !== (appBinaryId !== "")) { - throw new Error("Either app-file or app-binary-id must be used"); + if (!(appFilePath !== '') !== (appBinaryId !== '')) { + throw new Error('Either app-file or app-binary-id must be used'); } const deviceLocale = core.getInput('device-locale', { required: false }); const timeoutString = core.getInput('timeout', { required: false }); var env = {}; - env = core.getMultilineInput('env', { required: false }) - .map(it => { + env = core + .getMultilineInput('env', { required: false }) + .map((it) => { const parts = it.split('='); if (parts.length < 2) { throw new Error(`Invalid env parameter: ${it}`); @@ -47305,6 +47389,7 @@ function getParameters() { appBinaryId, deviceLocale, timeout, + projectId, }; }); } diff --git a/index.ts b/index.ts index 82ff16b..4519169 100644 --- a/index.ts +++ b/index.ts @@ -1,47 +1,52 @@ import * as core from '@actions/core' -import ApiClient, { UploadRequest } from './ApiClient' -import { validateAppFile } from './app_file'; -import { zipFolder, zipIfFolder } from './archive_utils'; -import { getParameters } from './params'; -import { existsSync, readdirSync } from 'fs'; -import StatusPoller from './StatusPoller'; -import { info } from './log'; +import ApiClient, { + CloudUploadRequest, + UploadResponse, + RobinUploadRequest, + RobinUploadResponse, +} from './ApiClient' +import { validateAppFile } from './app_file' +import { zipFolder, zipIfFolder } from './archive_utils' +import { getParameters } from './params' +import { existsSync, readdirSync } from 'fs' +import StatusPoller from './StatusPoller' +import { info } from './log' const knownAppTypes = ['ANDROID_APK', 'IOS_BUNDLE'] const listFilesInDirectory = () => { const files = readdirSync('.', { withFileTypes: true }) - console.log("Directory contents:") + console.log('Directory contents:') for (const f of files) { console.log(f.isDirectory() ? `${f.name}/` : f.name) } } -const createWorkspaceZip = async (workspaceFolder: string | null): Promise => { +const createWorkspaceZip = async ( + workspaceFolder: string | null +): Promise => { let resolvedWorkspaceFolder = workspaceFolder if (resolvedWorkspaceFolder === null || workspaceFolder?.length === 0) { if (existsSync('.maestro')) { resolvedWorkspaceFolder = '.maestro' - info("Packaging .maestro folder") + info('Packaging .maestro folder') } else if (existsSync('.mobiledev')) { resolvedWorkspaceFolder = '.mobiledev' - info("Packaging .mobiledev folder") + info('Packaging .mobiledev folder') } else { listFilesInDirectory() - throw new Error("Default workspace directory does not exist: .maestro/") + throw new Error('Default workspace directory does not exist: .maestro/') } } else if (!existsSync(resolvedWorkspaceFolder)) { - throw new Error(`Workspace directory does not exist: ${resolvedWorkspaceFolder}`) + throw new Error( + `Workspace directory does not exist: ${resolvedWorkspaceFolder}` + ) } - await zipFolder(resolvedWorkspaceFolder, 'workspace.zip'); + await zipFolder(resolvedWorkspaceFolder, 'workspace.zip') return 'workspace.zip' } -export const getConsoleUrl = (uploadId: string, teamId: string, appId: string): string => { - return `https://console.mobile.dev/uploads/${uploadId}?teamId=${teamId}&appId=${appId}` -} - const run = async () => { const { apiKey, @@ -64,13 +69,12 @@ const run = async () => { appBinaryId, deviceLocale, timeout, + projectId, } = await getParameters() let appFile = null - if (appFilePath !== "") { - appFile = await validateAppFile( - await zipIfFolder(appFilePath) - ); + if (appFilePath !== '') { + appFile = await validateAppFile(await zipIfFolder(appFilePath)) if (!knownAppTypes.includes(appFile.type)) { throw new Error(`Unsupported app file type: ${appFile.type}`) } @@ -78,40 +82,87 @@ const run = async () => { const workspaceZip = await createWorkspaceZip(workspaceFolder) - const client = new ApiClient(apiKey, apiUrl) + const client = new ApiClient(apiKey, apiUrl, projectId) - info("Uploading to Maestro Cloud") - const request: UploadRequest = { - benchmarkName: name, - branch: branchName, - commitSha: commitSha, - repoOwner: repoOwner, - repoName: repoName, - pullRequestId: pullRequestId, - env: env, - agent: 'github', - androidApiLevel: androidApiLevel, - iOSVersion: iOSVersion, - includeTags: includeTags, - excludeTags: excludeTags, - appBinaryId: appBinaryId || undefined, - deviceLocale: deviceLocale || undefined, + if (!!projectId) { + /** + * If project Exist - Its Robin + */ + info('Uploading to Robin Basic') + const request: RobinUploadRequest = { + projectId: projectId, + repoOwner: repoOwner, + repoName: repoName, + agent: 'github', + branch: branchName, + commitSha: commitSha, + pullRequestId: pullRequestId, + env: env, + androidApiLevel: androidApiLevel, + iOSVersion: iOSVersion, + includeTags: includeTags, + excludeTags: excludeTags, + appBinaryId: appBinaryId || undefined, + deviceLocale: deviceLocale || undefined, + } + const { + uploadId, + orgId, + appId, + appBinaryId: appBinaryIdResponse, + }: RobinUploadResponse = await client.robinUploadRequest( + request, + appFile && appFile.path, + workspaceZip, + mappingFile && (await zipIfFolder(mappingFile)) + ) + const consoleUrl = `https://copilot.mobile.dev/project/${projectId}/maestro-test/app/${appId}/upload/${uploadId}` + core.setOutput('ROBIN_CONSOLE_URL', consoleUrl) + core.setOutput('ROBIN_APP_BINARY_ID', appBinaryIdResponse) + !async && + new StatusPoller(client, uploadId, consoleUrl).startPolling(timeout) + } else { + /** + * If project Exist - Its Cloud + */ + info('Uploading to Maestro Cloud') + const request: CloudUploadRequest = { + benchmarkName: name, + repoOwner: repoOwner, + repoName: repoName, + agent: 'github', + branch: branchName, + commitSha: commitSha, + pullRequestId: pullRequestId, + env: env, + androidApiLevel: androidApiLevel, + iOSVersion: iOSVersion, + includeTags: includeTags, + excludeTags: excludeTags, + appBinaryId: appBinaryId || undefined, + deviceLocale: deviceLocale || undefined, + } + const { + uploadId, + teamId, + appBinaryId: appBinaryIdResponse, + }: UploadResponse = await client.cloudUploadRequest( + request, + appFile && appFile.path, + workspaceZip, + mappingFile && (await zipIfFolder(mappingFile)) + ) + const consoleUrl = `https://console.mobile.dev/uploads/${uploadId}?teamId=${teamId}&appId=${appBinaryIdResponse}` + info( + `Visit the web console for more details about the upload: ${consoleUrl}\n` + ) + core.setOutput('MAESTRO_CLOUD_CONSOLE_URL', consoleUrl) + core.setOutput('MAESTRO_CLOUD_APP_BINARY_ID', appBinaryId) + !async && + new StatusPoller(client, uploadId, consoleUrl).startPolling(timeout) } - - const { uploadId, teamId, targetId: appId, appBinaryId: uploadedBinaryId } = await client.uploadRequest( - request, - appFile && appFile.path, - workspaceZip, - mappingFile && await zipIfFolder(mappingFile), - ) - const consoleUrl = getConsoleUrl(uploadId, teamId, appId) - info(`Visit the web console for more details about the upload: ${consoleUrl}\n`) - core.setOutput('MAESTRO_CLOUD_CONSOLE_URL', consoleUrl) - core.setOutput('MAESTRO_CLOUD_APP_BINARY_ID', uploadedBinaryId) - - !async && new StatusPoller(client, uploadId, consoleUrl).startPolling(timeout) } -run().catch(e => { +run().catch((e) => { core.setFailed(`Error running Maestro Cloud Upload Action: ${e.message}`) }) diff --git a/params.ts b/params.ts index edf55d5..03ccb83 100644 --- a/params.ts +++ b/params.ts @@ -1,44 +1,51 @@ -import * as github from '@actions/github'; -import * as core from '@actions/core'; -import { AppFile, validateMappingFile } from './app_file'; +import * as github from '@actions/github' +import * as core from '@actions/core' +import { AppFile, validateMappingFile } from './app_file' import { PushEvent } from '@octokit/webhooks-definitions/schema' export type Params = { - apiKey: string, - apiUrl: string, - name: string, - appFilePath: string, - mappingFile: string | null, - workspaceFolder: string | null, + projectId?: string + apiKey: string + apiUrl: string + name: string + appFilePath: string + mappingFile: string | null + workspaceFolder: string | null branchName: string commitSha?: string repoName: string repoOwner: string - pullRequestId?: string, - env?: { [key: string]: string }, - async?: boolean, - androidApiLevel?: number, - iOSVersion?: number, - includeTags: string[], - excludeTags: string[], - appBinaryId: string, - deviceLocale?: string, - timeout?: number, + pullRequestId?: string + env?: { [key: string]: string } + async?: boolean + androidApiLevel?: number + iOSVersion?: number + includeTags: string[] + excludeTags: string[] + appBinaryId: string + deviceLocale?: string + timeout?: number } function getBranchName(): string { const pullRequest = github.context.payload.pull_request if (pullRequest) { - const branchName = pullRequest?.head?.ref; + const branchName = pullRequest?.head?.ref if (!branchName) { - throw new Error(`Unable find pull request ref: ${JSON.stringify(pullRequest, undefined, 2)}`) + throw new Error( + `Unable find pull request ref: ${JSON.stringify( + pullRequest, + undefined, + 2 + )}` + ) } return branchName } const regex = /refs\/(heads|tags)\/(.*)/ const ref = github.context.ref - let result = regex.exec(ref); + let result = regex.exec(ref) if (!result || result.length < 3) { throw new Error(`Failed to parse GitHub ref: ${ref}`) } @@ -71,11 +78,11 @@ function getPullRequestTitle(): string | undefined { function getInferredName(): string { const pullRequestTitle = getPullRequestTitle() - if (pullRequestTitle) return pullRequestTitle; + if (pullRequestTitle) return pullRequestTitle if (github.context.eventName === 'push') { const pushPayload = github.context.payload as PushEvent - const commitMessage = pushPayload.head_commit?.message; + const commitMessage = pushPayload.head_commit?.message if (commitMessage) return commitMessage } @@ -98,11 +105,9 @@ function parseTags(tags?: string): string[] { if (tags === undefined || tags === '') return [] if (tags.includes(',')) { - const arrayTags = tags.split(',') - .map(it => it.trim()) + const arrayTags = tags.split(',').map((it) => it.trim()) - if (!Array.isArray(arrayTags)) - throw new Error("tags must be an Array.") + if (!Array.isArray(arrayTags)) throw new Error('tags must be an Array.') return arrayTags } @@ -111,30 +116,43 @@ function parseTags(tags?: string): string[] { } export async function getParameters(): Promise { - const apiUrl = core.getInput('api-url', { required: false }) || 'https://api.mobile.dev' + const projectId = + core.getInput('project-id', { required: false }) || undefined + const apiUrl = + core.getInput('api-url', { required: false }) || + (projectId + ? `https://api.copilot.mobile.dev/v2/project/${projectId}` + : 'https://api.mobile.dev') const name = core.getInput('name', { required: false }) || getInferredName() const apiKey = core.getInput('api-key', { required: true }) const mappingFileInput = core.getInput('mapping-file', { required: false }) const workspaceFolder = core.getInput('workspace', { required: false }) const mappingFile = mappingFileInput && validateMappingFile(mappingFileInput) const async = core.getInput('async', { required: false }) === 'true' - const androidApiLevelString = core.getInput('android-api-level', { required: false }) + const androidApiLevelString = core.getInput('android-api-level', { + required: false, + }) const iOSVersionString = core.getInput('ios-version', { required: false }) - const includeTags = parseTags(core.getInput('include-tags', { required: false })) - const excludeTags = parseTags(core.getInput('exclude-tags', { required: false })) + const includeTags = parseTags( + core.getInput('include-tags', { required: false }) + ) + const excludeTags = parseTags( + core.getInput('exclude-tags', { required: false }) + ) const appFilePath = core.getInput('app-file', { required: false }) const appBinaryId = core.getInput('app-binary-id', { required: false }) - if (!(appFilePath !== "") !== (appBinaryId !== "")) { - throw new Error("Either app-file or app-binary-id must be used") + if (!(appFilePath !== '') !== (appBinaryId !== '')) { + throw new Error('Either app-file or app-binary-id must be used') } const deviceLocale = core.getInput('device-locale', { required: false }) const timeoutString = core.getInput('timeout', { required: false }) var env: { [key: string]: string } = {} - env = core.getMultilineInput('env', { required: false }) - .map(it => { + env = core + .getMultilineInput('env', { required: false }) + .map((it) => { const parts = it.split('=') if (parts.length < 2) { @@ -178,5 +196,6 @@ export async function getParameters(): Promise { appBinaryId, deviceLocale, timeout, + projectId, } -} \ No newline at end of file +} From 2c449e82eb0833c9f93b728083d42ed29ea4f280 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Mon, 2 Sep 2024 15:01:05 +0200 Subject: [PATCH 37/51] fix: add project-id to action.yml (#43) --- action.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/action.yml b/action.yml index 4409093..cc55820 100644 --- a/action.yml +++ b/action.yml @@ -46,6 +46,9 @@ inputs: timeout: description: "Minutes to timeout while waiting for results" required: false + project-id: + description: "Robin project ID" + required: false runs: using: "node20" main: "dist/index.js" From f4573b6b46203639b0d62333a1a46bef96197f8f Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Mon, 2 Sep 2024 15:03:30 +0200 Subject: [PATCH 38/51] Version 1.9.1 --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 4e6cf51..019d0e7 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -134,7 +134,7 @@ If you don't want the action to wait until the Upload has been completed as is t Alternatively, you might want to still wait for the action but would like to configure the timeout period, set `timeout` argument to a number of minutes: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -146,7 +146,7 @@ Alternatively, you might want to still wait for the action but would like to con If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -162,7 +162,7 @@ You can use Maestro [Tags](https://maestro.mobile.dev/cli/tags) to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -177,7 +177,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available Android emulator API levels. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -191,7 +191,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available iOS simulator versions. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -204,12 +204,12 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 + uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - - uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 + - uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} @@ -220,7 +220,7 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar To switch the device locale on a remote device from a default one (en_US) `device-locale` parameter should be used. The value is a combination of lowercase ISO-639-1 code and uppercase ISO-3166-1 code, i.e. "de_DE" for Germany. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -241,7 +241,7 @@ In order to access these variables you can use the following approach: ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 + uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: From e011061954298927323b68ad941691de8483c76a Mon Sep 17 00:00:00 2001 From: Leland Takamine Date: Wed, 9 Oct 2024 14:59:14 -0700 Subject: [PATCH 39/51] [fix] Support setting upload name in Robin --- ApiClient.ts | 1 + index.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ApiClient.ts b/ApiClient.ts index 856d7ef..1b60a8d 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -27,6 +27,7 @@ export type CloudUploadRequest = { } export type RobinUploadRequest = { + benchmarkName?: string projectId: string repoOwner?: string repoName?: string diff --git a/index.ts b/index.ts index 4519169..e2a9a91 100644 --- a/index.ts +++ b/index.ts @@ -88,8 +88,9 @@ const run = async () => { /** * If project Exist - Its Robin */ - info('Uploading to Robin Basic') + info('Uploading to Robin') const request: RobinUploadRequest = { + benchmarkName: name, projectId: projectId, repoOwner: repoOwner, repoName: repoName, From 5da5c6a998937513ed2ebdb6b2163d60b40b16e1 Mon Sep 17 00:00:00 2001 From: Leland Takamine Date: Wed, 9 Oct 2024 14:59:59 -0700 Subject: [PATCH 40/51] Version 1.9.1 --- dist/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dist/index.js b/dist/index.js index dd40b67..f38a259 100644 --- a/dist/index.js +++ b/dist/index.js @@ -47109,8 +47109,9 @@ const run = () => __awaiter(void 0, void 0, void 0, function* () { /** * If project Exist - Its Robin */ - (0, log_1.info)('Uploading to Robin Basic'); + (0, log_1.info)('Uploading to Robin'); const request = { + benchmarkName: name, projectId: projectId, repoOwner: repoOwner, repoName: repoName, From f96a607e1b32622d185a3535ced5e2c79e4deabd Mon Sep 17 00:00:00 2001 From: Leland Takamine Date: Wed, 9 Oct 2024 15:00:04 -0700 Subject: [PATCH 41/51] Version 1.9.2 --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 019d0e7..1b74e16 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -134,7 +134,7 @@ If you don't want the action to wait until the Upload has been completed as is t Alternatively, you might want to still wait for the action but would like to configure the timeout period, set `timeout` argument to a number of minutes: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -146,7 +146,7 @@ Alternatively, you might want to still wait for the action but would like to con If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -162,7 +162,7 @@ You can use Maestro [Tags](https://maestro.mobile.dev/cli/tags) to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -177,7 +177,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available Android emulator API levels. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -191,7 +191,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available iOS simulator versions. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -204,12 +204,12 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 + uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - - uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 + - uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} @@ -220,7 +220,7 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar To switch the device locale on a remote device from a default one (en_US) `device-locale` parameter should be used. The value is a combination of lowercase ISO-639-1 code and uppercase ISO-3166-1 code, i.e. "de_DE" for Germany. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -241,7 +241,7 @@ In order to access these variables you can use the following approach: ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 + uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: From 0c230a92e0c2c02bfaf28eadd7c04c2f463a36ab Mon Sep 17 00:00:00 2001 From: Amanjeet Singh Date: Wed, 16 Oct 2024 18:10:06 +0530 Subject: [PATCH 42/51] fix: add stopped status (#44) --- ApiClient.ts | 1 + README.md | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/ApiClient.ts b/ApiClient.ts index 1b60a8d..25b0355 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -7,6 +7,7 @@ export enum BenchmarkStatus { ERROR = 'ERROR', CANCELED = 'CANCELED', WARNING = 'WARNING', + STOPPED = 'STOPPED' } export type CloudUploadRequest = { diff --git a/README.md b/README.md index 1b74e16..b8ae540 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -134,7 +134,7 @@ If you don't want the action to wait until the Upload has been completed as is t Alternatively, you might want to still wait for the action but would like to configure the timeout period, set `timeout` argument to a number of minutes: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -146,7 +146,7 @@ Alternatively, you might want to still wait for the action but would like to con If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -162,7 +162,7 @@ You can use Maestro [Tags](https://maestro.mobile.dev/cli/tags) to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -177,7 +177,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available Android emulator API levels. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -191,7 +191,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available iOS simulator versions. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -204,12 +204,12 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 + uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - - uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 + - uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} @@ -220,7 +220,7 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar To switch the device locale on a remote device from a default one (en_US) `device-locale` parameter should be used. The value is a combination of lowercase ISO-639-1 code and uppercase ISO-3166-1 code, i.e. "de_DE" for Germany. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -241,7 +241,7 @@ In order to access these variables you can use the following approach: ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 + uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: From 9efcddc4820254fb90bd036150a535fe84958f0b Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Wed, 6 Nov 2024 15:56:36 +0100 Subject: [PATCH 43/51] Version 1.9.3 --- dist/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/index.js b/dist/index.js index f38a259..3202f94 100644 --- a/dist/index.js +++ b/dist/index.js @@ -46548,6 +46548,7 @@ var BenchmarkStatus; BenchmarkStatus["ERROR"] = "ERROR"; BenchmarkStatus["CANCELED"] = "CANCELED"; BenchmarkStatus["WARNING"] = "WARNING"; + BenchmarkStatus["STOPPED"] = "STOPPED"; })(BenchmarkStatus = exports.BenchmarkStatus || (exports.BenchmarkStatus = {})); class UploadStatusError { constructor(status, text) { From 63464428af1af2c4526b165dcb3f9910c8fe087d Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Wed, 6 Nov 2024 17:28:03 +0100 Subject: [PATCH 44/51] chore: update to new robin domain (#45) --- index.ts | 2 +- params.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.ts b/index.ts index e2a9a91..a810bf8 100644 --- a/index.ts +++ b/index.ts @@ -117,7 +117,7 @@ const run = async () => { workspaceZip, mappingFile && (await zipIfFolder(mappingFile)) ) - const consoleUrl = `https://copilot.mobile.dev/project/${projectId}/maestro-test/app/${appId}/upload/${uploadId}` + const consoleUrl = `https://app.robintest.com/project/${projectId}/maestro-test/app/${appId}/upload/${uploadId}` core.setOutput('ROBIN_CONSOLE_URL', consoleUrl) core.setOutput('ROBIN_APP_BINARY_ID', appBinaryIdResponse) !async && diff --git a/params.ts b/params.ts index 03ccb83..164de90 100644 --- a/params.ts +++ b/params.ts @@ -121,7 +121,7 @@ export async function getParameters(): Promise { const apiUrl = core.getInput('api-url', { required: false }) || (projectId - ? `https://api.copilot.mobile.dev/v2/project/${projectId}` + ? `https://api.app.robintest.com/v2/project/${projectId}` : 'https://api.mobile.dev') const name = core.getInput('name', { required: false }) || getInferredName() const apiKey = core.getInput('api-key', { required: true }) From 6696ae0552676a41dfcd15d3953bce613cb27963 Mon Sep 17 00:00:00 2001 From: Axel Niklasson Date: Wed, 6 Nov 2024 17:28:23 +0100 Subject: [PATCH 45/51] Version 1.9.4 --- README.md | 34 +++++++++++++++++----------------- dist/index.js | 4 ++-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index b8ae540..e3b5194 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: @@ -45,7 +45,7 @@ jobs: # Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/debug/app-debug.apk @@ -58,7 +58,7 @@ jobs: Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app/build/outputs/apk/release/app-release.apk @@ -68,7 +68,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: # iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -80,7 +80,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: .app @@ -94,7 +94,7 @@ Include the Proguard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -112,7 +112,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -124,7 +124,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -134,7 +134,7 @@ If you don't want the action to wait until the Upload has been completed as is t Alternatively, you might want to still wait for the action but would like to configure the timeout period, set `timeout` argument to a number of minutes: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -146,7 +146,7 @@ Alternatively, you might want to still wait for the action but would like to con If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -162,7 +162,7 @@ You can use Maestro [Tags](https://maestro.mobile.dev/cli/tags) to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -177,7 +177,7 @@ You can specify what Android API level to use when running in Maestro Cloud usin The default API level is 30. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available Android emulator API levels. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.apk @@ -191,7 +191,7 @@ You can specify what **major** iOS Version to use when running in Maestro Cloud The default iOS version is 15. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available iOS simulator versions. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -204,12 +204,12 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 + uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip - - uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 + - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} @@ -220,7 +220,7 @@ You can use an already uploaded App binary in Maestro Cloud using the `app-binar To switch the device locale on a remote device from a default one (en_US) `device-locale` parameter should be used. The value is a combination of lowercase ISO-639-1 code and uppercase ISO-3166-1 code, i.e. "de_DE" for Germany. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: app.zip @@ -241,7 +241,7 @@ In order to access these variables you can use the following approach: ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.9.3 + uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: diff --git a/dist/index.js b/dist/index.js index 3202f94..23ad2f9 100644 --- a/dist/index.js +++ b/dist/index.js @@ -47129,7 +47129,7 @@ const run = () => __awaiter(void 0, void 0, void 0, function* () { deviceLocale: deviceLocale || undefined, }; const { uploadId, orgId, appId, appBinaryId: appBinaryIdResponse, } = yield client.robinUploadRequest(request, appFile && appFile.path, workspaceZip, mappingFile && (yield (0, archive_utils_1.zipIfFolder)(mappingFile))); - const consoleUrl = `https://copilot.mobile.dev/project/${projectId}/maestro-test/app/${appId}/upload/${uploadId}`; + const consoleUrl = `https://app.robintest.com/project/${projectId}/maestro-test/app/${appId}/upload/${uploadId}`; core.setOutput('ROBIN_CONSOLE_URL', consoleUrl); core.setOutput('ROBIN_APP_BINARY_ID', appBinaryIdResponse); !async && @@ -47327,7 +47327,7 @@ function getParameters() { const projectId = core.getInput('project-id', { required: false }) || undefined; const apiUrl = core.getInput('api-url', { required: false }) || (projectId - ? `https://api.copilot.mobile.dev/v2/project/${projectId}` + ? `https://api.app.robintest.com/v2/project/${projectId}` : 'https://api.mobile.dev'); const name = core.getInput('name', { required: false }) || getInferredName(); const apiKey = core.getInput('api-key', { required: true }); From d7e2fb70ee3a40ab6cf17340ac4c63090be05633 Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Fri, 8 Nov 2024 16:09:51 +0000 Subject: [PATCH 46/51] Add Robin to README (#46) * Lots more Robin, with a few Maestro Cloud caveats * Added a canonical list of inputs at the top * Fixed some formatting that the linter was complaining about --- README.md | 137 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 91 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index e3b5194..200d84d 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,43 @@ # Maestro Cloud Action -Run your Flows on [Maestro Cloud](https://cloud.mobile.dev). +Run your Flows on [Robin](https://www.robintest.com/) or [Maestro Cloud](https://cloud.mobile.dev). -# Using the action +## Using the action Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' # replace this with your actual project id app-file: ``` -# Triggers +### Maestro Cloud vs Robin + +The action can be used with both Robin and Maestro Cloud. Robin is the successor to Maestro Cloud, which is due to sunset on December 31st 2024. Robin is a drop-in replacement for Maestro Cloud, and is the enterprise-grade hosted Maestro execution platform built by the same team that builds Maestro. + +## Inputs + +| Key | Required | Description | +|---------------------|--------------------------|-------------------------------------------------------------------------------- | +| `api-key` | Yes | Your Robin or Maestro Cloud API key | +| `android-api-level` | No | The Android API level to use when running the Flows | +| `app-file` | Yes (or `app-binary-id`) | Path to the app file to upload. | +| `app-binary-id` | Yes (or `app-file`) | The ID of a previously uploaded app-file. | +| `async` | No | Whether to start the flow and exit the action (defaults to `false`) | +| `env` | No | Environment variables to pass to the run | +| `exclude-tags` | No | Comma-separated list of tags to exclude from the run | +| `include-tags` | No | Comma-separated list of tags to include in the run | +| `ios-version` | No | The iOS version to use when running the Flows | +| `mapping-file` | No | Path to the ProGuard map (Android) or dSYM (iOS) | +| `project-id` | Yes (for Robin) | Which project to run the tests against | +| `name` | No | Friendly name of the run | +| `timeout` | No | How long to wait for the run to complete when not async (defaults to 30 minutes)| +| `workspace` | No | Path to the workspace directory containing the Flows (defaults to `.maestro`) | + +## Triggers Trigger this action on (1) pushes to your main branch and (2) pull requests opened against your main branch: @@ -35,42 +59,47 @@ on: branches: [master] jobs: upload-to-mobile-dev: - name: Run Flows on Maestro Cloud + name: Run Flows on Robin steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # Checkout PR HEAD ``` -# Android +For more information on triggering workflows, check out [GitHub's documentation](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows). + +## Android ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app/build/outputs/apk/debug/app-debug.apk ``` `app-file` should point to an x86 compatible APK file, either directly to the file or a glob pattern matching the file name. When using a pattern, the first matched file will be used. -### Proguard Deobfuscation +### ProGuard Deobfuscation -Include the Proguard mapping file to deobfuscate Android performance traces: +Include the ProGuard mapping file to deobfuscate Android performance traces: ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app/build/outputs/apk/release/app-release.apk mapping-file: app/build/outputs/mapping/release/mapping.txt ``` -# iOS +## iOS ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: .app mapping-file: .app.dSYM ``` @@ -82,26 +111,28 @@ Include the Proguard mapping file to deobfuscate Android performance traces: ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: .app mapping-file: .app.dSYM ``` `mapping-file` should point to generated .dSYM file (unique per build). more info [here](https://developer.apple.com/documentation/xcode/building-your-app-to-include-debugging-information). -# Custom workspace location +## Custom workspace location By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app.zip workspace: myFlows/ ``` -# Custom name +## Custom name A name will automatically be provided according to the following order: @@ -114,19 +145,21 @@ If you want to override this behaviour and specify your own name, you can do so ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app.zip name: My Upload ``` -# Run in async mode +## Run in async mode If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app.zip async: true ``` @@ -136,106 +169,116 @@ Alternatively, you might want to still wait for the action but would like to con ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app.zip timeout: 90 # Wait for 90 minutes ``` -# Adding environment variables +## Adding environment variables If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app.zip env: | USERNAME= PASSWORD= ``` -# Using tags +## Using tags -You can use Maestro [Tags](https://maestro.mobile.dev/cli/tags) to filter which Flows to send to Maestro Cloud: +You can use Maestro [Tags](https://maestro.mobile.dev/cli/tags) to filter which Flows to send: You can either pass a single value, or comma-separated (`,`) values. ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app.zip include-tags: dev, pull-request exclude-tags: excludeTag ``` -# Specifying Android API Level +## Specifying Android API Level -You can specify what Android API level to use when running in Maestro Cloud using the `android-api-level` parameter. +You can specify which Android API level to use when running using the `android-api-level` parameter. -The default API level is 30. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available Android emulator API levels. +On Robin, the default API level is 33 (Android 13). [Refer to Robin docs](https://docs.robintest.com/maestro/reference/device-configuration/configuring-os-version) for available Android emulator API levels. On Maestro Cloud, the default API level is 30 (Android 11) - docs [here](https://cloud.mobile.dev/reference/device-configuration). ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app.apk android-api-level: 29 ``` -# Specifying iOS version +## Specifying iOS version -You can specify what **major** iOS Version to use when running in Maestro Cloud using the `ios-version` parameter. +You can specify which **major** iOS Version to use when running in Robin using the `ios-version` parameter. -The default iOS version is 15. [Refer to Maestro Cloud docs](https://cloud.mobile.dev/reference/device-configuration) for available iOS simulator versions. +On Robin, the default iOS version is 16. [Refer to Robin docs](https://docs.robintest.com/maestro/reference/device-configuration/configuring-os-version) for available iOS simulator versions. On Maestro Cloud, the default iOS version is 15 - docs [here](https://cloud.mobile.dev/reference/device-configuration). ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app.zip ios-version: 16 ``` -# Using an already uploaded App +## Using an already uploaded App -You can use an already uploaded App binary in Maestro Cloud using the `app-binary-id` parameter. +You can use an already uploaded binary in Robin using the `app-binary-id` parameter. ```yaml - id: upload uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app.zip - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-binary-id: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }} ``` -# Configuring the locale for the device where the flows will be executed +## Configuring the locale for the device where the flows will be executed To switch the device locale on a remote device from a default one (en_US) `device-locale` parameter should be used. The value is a combination of lowercase ISO-639-1 code and uppercase ISO-3166-1 code, i.e. "de_DE" for Germany. ```yaml - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: app.zip device-locale: de_DE ``` -# Accessing output +## Outputs The following output variables are set by the action: -- `MAESTRO_CLOUD_CONSOLE_URL` - link to the Maestro Cloud console +- `MAESTRO_CLOUD_CONSOLE_URL` - link to the Maestro Cloud console (if using Maestro Cloud) +- `ROBIN_CONSOLE_URL` - link to the Robin console (if using Robin) - `MAESTRO_CLOUD_UPLOAD_STATUS` - status of the Upload (not available in `async` mode) - `MAESTRO_CLOUD_FLOW_RESULTS` - list of Flows and their results (not available in `async` mode) -- `MAESTRO_CLOUD_APP_BINARY_ID` - id of the binary uploaded +- `MAESTRO_CLOUD_APP_BINARY_ID` - id of the binary uploaded (if using Maestro Cloud) +- `ROBIN_APP_BINARY_ID` - id of the binary uploaded (if using Robin) In order to access these variables you can use the following approach: @@ -243,7 +286,8 @@ In order to access these variables you can use the following approach: - id: upload uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 with: - api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + api-key: ${{ secrets.ROBIN_API_KEY }} + project-id: 'proj_01example0example1example2' app-file: # ... any other parameters @@ -256,12 +300,13 @@ In order to access these variables you can use the following approach: echo "App Binary ID: ${{ steps.upload.outputs.MAESTRO_CLOUD_APP_BINARY_ID }}" ``` -## Output types +### Output types - `MAESTRO_CLOUD_UPLOAD_STATUS` Any of the following values: - ``` + + ```plaintext PENDING RUNNING SUCCESS @@ -273,7 +318,7 @@ In order to access these variables you can use the following approach: - `MAESTRO_CLOUD_FLOW_RESULTS` An array of objects with at least `name`, `status`, and `errors` fields. + ```json [{"name":"my-first-flow","status":"SUCCESS","errors":[]},{"name":"my-second-flow","status":"SUCCESS","errors":[]},{"name":"my-cancelled-flow","status":"CANCELED","errors":[],"cancellationReason":"INFRA_ERROR"}] ``` - From 2a5ce0abd7562ef3301029adb7032ce8c74d2677 Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Fri, 8 Nov 2024 17:31:26 +0000 Subject: [PATCH 47/51] Use existing API URL (#47) Whilst DNS doesn't resolve the new name, revert the API endpoint change from #45 --- params.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params.ts b/params.ts index 164de90..03ccb83 100644 --- a/params.ts +++ b/params.ts @@ -121,7 +121,7 @@ export async function getParameters(): Promise { const apiUrl = core.getInput('api-url', { required: false }) || (projectId - ? `https://api.app.robintest.com/v2/project/${projectId}` + ? `https://api.copilot.mobile.dev/v2/project/${projectId}` : 'https://api.mobile.dev') const name = core.getInput('name', { required: false }) || getInferredName() const apiKey = core.getInput('api-key', { required: true }) From aa773c5b308aa8e4ea4912b6c13000b575e22749 Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Fri, 8 Nov 2024 17:38:53 +0000 Subject: [PATCH 48/51] Version 1.9.6 --- README.md | 34 +++++++++++++++++----------------- dist/index.js | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 200d84d..408155e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Run your Flows on [Robin](https://www.robintest.com/) or [Maestro Cloud](https:/ Add the following to your workflow. Note that you can use the `v1` tag if you want to keep using the latest version of the action, which will automatically resolve to all `v1.minor.patch` versions as they get published. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' # replace this with your actual project id @@ -71,7 +71,7 @@ For more information on triggering workflows, check out [GitHub's documentation] ## Android ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -85,7 +85,7 @@ For more information on triggering workflows, check out [GitHub's documentation] Include the ProGuard mapping file to deobfuscate Android performance traces: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -96,7 +96,7 @@ Include the ProGuard mapping file to deobfuscate Android performance traces: ## iOS ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -109,7 +109,7 @@ Include the ProGuard mapping file to deobfuscate Android performance traces: ### .dSYM file ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -124,7 +124,7 @@ Include the ProGuard mapping file to deobfuscate Android performance traces: By default, the action is looking for a `.maestro` folder with Maestro flows in the root directory of the project. If you would like to customize this behaviour, you can override it with a `workspace` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -143,7 +143,7 @@ A name will automatically be provided according to the following order: If you want to override this behaviour and specify your own name, you can do so by setting the `name` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -156,7 +156,7 @@ If you want to override this behaviour and specify your own name, you can do so If you don't want the action to wait until the Upload has been completed as is the default behaviour, set the `async` argument to `true`: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -167,7 +167,7 @@ If you don't want the action to wait until the Upload has been completed as is t Alternatively, you might want to still wait for the action but would like to configure the timeout period, set `timeout` argument to a number of minutes: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -180,7 +180,7 @@ Alternatively, you might want to still wait for the action but would like to con If you want to pass environment variables along with your upload, add a multiline `env` argument: ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -197,7 +197,7 @@ You can use Maestro [Tags](https://maestro.mobile.dev/cli/tags) to filter which You can either pass a single value, or comma-separated (`,`) values. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -213,7 +213,7 @@ You can specify which Android API level to use when running using the `android-a On Robin, the default API level is 33 (Android 13). [Refer to Robin docs](https://docs.robintest.com/maestro/reference/device-configuration/configuring-os-version) for available Android emulator API levels. On Maestro Cloud, the default API level is 30 (Android 11) - docs [here](https://cloud.mobile.dev/reference/device-configuration). ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -228,7 +228,7 @@ You can specify which **major** iOS Version to use when running in Robin using t On Robin, the default iOS version is 16. [Refer to Robin docs](https://docs.robintest.com/maestro/reference/device-configuration/configuring-os-version) for available iOS simulator versions. On Maestro Cloud, the default iOS version is 15 - docs [here](https://cloud.mobile.dev/reference/device-configuration). ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -242,13 +242,13 @@ You can use an already uploaded binary in Robin using the `app-binary-id` parame ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 + uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' app-file: app.zip - - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 + - uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -260,7 +260,7 @@ You can use an already uploaded binary in Robin using the `app-binary-id` parame To switch the device locale on a remote device from a default one (en_US) `device-locale` parameter should be used. The value is a combination of lowercase ISO-639-1 code and uppercase ISO-3166-1 code, i.e. "de_DE" for Germany. ```yaml -- uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 +- uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' @@ -284,7 +284,7 @@ In order to access these variables you can use the following approach: ```yaml - id: upload - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 + uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.ROBIN_API_KEY }} project-id: 'proj_01example0example1example2' diff --git a/dist/index.js b/dist/index.js index 23ad2f9..1a7e4dc 100644 --- a/dist/index.js +++ b/dist/index.js @@ -47327,7 +47327,7 @@ function getParameters() { const projectId = core.getInput('project-id', { required: false }) || undefined; const apiUrl = core.getInput('api-url', { required: false }) || (projectId - ? `https://api.app.robintest.com/v2/project/${projectId}` + ? `https://api.copilot.mobile.dev/v2/project/${projectId}` : 'https://api.mobile.dev'); const name = core.getInput('name', { required: false }) || getInferredName(); const apiKey = core.getInput('api-key', { required: true }); From b53ea3aa492cb48d008f92c229b933dde23d2f93 Mon Sep 17 00:00:00 2001 From: Proksh Luthra <35415752+proksh@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:35:33 +0530 Subject: [PATCH 49/51] feat:added new run status PREPARING & INSTALLING (#49) --- .prettierrc | 9 +++++++++ ApiClient.ts | 2 ++ README.md | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..b035259 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "singleQuote": true, + "semi": true, + "useTabs": false, + "tabWidth": 2, + "importOrderSeparation": true, + "importOrderSortSpecifiers": true, + "printWidth": 120 +} diff --git a/ApiClient.ts b/ApiClient.ts index 25b0355..25c4878 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -2,6 +2,8 @@ import fetch, { fileFromSync, FormData } from 'node-fetch' export enum BenchmarkStatus { PENDING = 'PENDING', + PREPARING = 'PREPARING', + INSTALLING = 'INSTALLING', RUNNING = 'RUNNING', SUCCESS = 'SUCCESS', ERROR = 'ERROR', diff --git a/README.md b/README.md index 408155e..bc30f0a 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,8 @@ In order to access these variables you can use the following approach: ```plaintext PENDING + PREPARING + INSTALLING RUNNING SUCCESS ERROR From c220d1d538ea0637c31aaa8bf6de6c2a80667725 Mon Sep 17 00:00:00 2001 From: Proksh Luthra <35415752+proksh@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:29:13 +0530 Subject: [PATCH 50/51] Separated out status for upload and flows (#50) --- .prettierrc | 5 +++-- ApiClient.ts | 36 ++++++++++++++++++++++++------------ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/.prettierrc b/.prettierrc index b035259..8ac1c07 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,9 +1,10 @@ { "singleQuote": true, - "semi": true, + "semi": false, "useTabs": false, + "trailingComma": "none", "tabWidth": 2, "importOrderSeparation": true, "importOrderSortSpecifiers": true, - "printWidth": 120 + "printWidth": 80 } diff --git a/ApiClient.ts b/ApiClient.ts index 25c4878..dbc1214 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -1,6 +1,18 @@ import fetch, { fileFromSync, FormData } from 'node-fetch' -export enum BenchmarkStatus { +export enum FlowStatus { + PENDING = 'PENDING', + PREPARING = 'PREPARING', + INSTALLING = 'INSTALLING', + RUNNING = 'RUNNING', + SUCCESS = 'SUCCESS', + ERROR = 'ERROR', + CANCELED = 'CANCELED', + WARNING = 'WARNING', + STOPPED = 'STOPPED' +} + +export enum UploadStatus { PENDING = 'PENDING', PREPARING = 'PREPARING', INSTALLING = 'INSTALLING', @@ -70,19 +82,19 @@ export enum CancellationReason { BENCHMARK_DEPENDENCY_FAILED = 'BENCHMARK_DEPENDENCY_FAILED', INFRA_ERROR = 'INFRA_ERROR', OVERLAPPING_BENCHMARK = 'OVERLAPPING_BENCHMARK', - TIMEOUT = 'TIMEOUT', + TIMEOUT = 'TIMEOUT' } export type Flow = { name: string - status: BenchmarkStatus + status: FlowStatus errors?: string[] cancellationReason?: CancellationReason } export type UploadStatusResponse = { uploadId: string - status: BenchmarkStatus + status: UploadStatus completed: boolean flows: Flow[] } @@ -114,9 +126,9 @@ export default class ApiClient { const res = await fetch(`${this.apiUrl}/v2/upload`, { method: 'POST', headers: { - Authorization: `Bearer ${this.apiKey}`, + Authorization: `Bearer ${this.apiKey}` }, - body: formData, + body: formData }) if (!res.ok) { const body = await res.text() @@ -147,9 +159,9 @@ export default class ApiClient { const res = await fetch(`${this.apiUrl}/runMaestroTest`, { method: 'POST', headers: { - Authorization: `Bearer ${this.apiKey}`, + Authorization: `Bearer ${this.apiKey}` }, - body: formData, + body: formData }) if (!res.ok) { const body = await res.text() @@ -164,8 +176,8 @@ export default class ApiClient { const res = await fetch(`${this.apiUrl}/upload/${uploadId}`, { method: 'GET', headers: { - Authorization: `Bearer ${this.apiKey}`, - }, + Authorization: `Bearer ${this.apiKey}` + } }) if (!res.ok) { const body = await res.text() @@ -186,8 +198,8 @@ export default class ApiClient { { method: 'GET', headers: { - Authorization: `Bearer ${this.apiKey}`, - }, + Authorization: `Bearer ${this.apiKey}` + } } ) if (!res.ok) { From ab98a98369e514c15ed80313851ff5294394938c Mon Sep 17 00:00:00 2001 From: Proksh Luthra <35415752+proksh@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:53:29 +0530 Subject: [PATCH 51/51] Feat/added new run status (#51) --- ApiClient.ts | 6 ++-- StatusPoller.ts | 64 +++++++++++++++++++++--------------------- dist/index.js | 74 ++++++++++++++++++++++++++++--------------------- 3 files changed, 76 insertions(+), 68 deletions(-) diff --git a/ApiClient.ts b/ApiClient.ts index dbc1214..c96ad10 100644 --- a/ApiClient.ts +++ b/ApiClient.ts @@ -1,6 +1,6 @@ import fetch, { fileFromSync, FormData } from 'node-fetch' -export enum FlowStatus { +export enum RunStatus { PENDING = 'PENDING', PREPARING = 'PREPARING', INSTALLING = 'INSTALLING', @@ -14,8 +14,6 @@ export enum FlowStatus { export enum UploadStatus { PENDING = 'PENDING', - PREPARING = 'PREPARING', - INSTALLING = 'INSTALLING', RUNNING = 'RUNNING', SUCCESS = 'SUCCESS', ERROR = 'ERROR', @@ -87,7 +85,7 @@ export enum CancellationReason { export type Flow = { name: string - status: FlowStatus + status: RunStatus errors?: string[] cancellationReason?: CancellationReason } diff --git a/StatusPoller.ts b/StatusPoller.ts index a9f9937..c3ea182 100644 --- a/StatusPoller.ts +++ b/StatusPoller.ts @@ -1,10 +1,10 @@ import * as core from '@actions/core' -import ApiClient, { BenchmarkStatus, CancellationReason, Flow, UploadStatusError } from "./ApiClient"; -import { canceled, err, info, success, warning } from './log'; +import ApiClient, { RunStatus, UploadStatus, CancellationReason, Flow, UploadStatusError } from './ApiClient' +import { canceled, err, info, success, warning } from './log' const WAIT_TIMEOUT_MS = 1000 * 60 * 30 // 30 minutes const INTERVAL_MS = 10000 // 10 seconds -const TERMINAL_STATUSES = new Set([BenchmarkStatus.SUCCESS, BenchmarkStatus.ERROR, BenchmarkStatus.WARNING, BenchmarkStatus.CANCELED]) +const TERMINAL_STATUSES = new Set([RunStatus.SUCCESS, RunStatus.ERROR, RunStatus.WARNING, RunStatus.CANCELED]) const isCompleted = (flow: Flow): boolean => TERMINAL_STATUSES.has(flow.status) @@ -12,48 +12,48 @@ const getCanceledStatusMessage = (reason?: CancellationReason): string => { switch (reason) { case CancellationReason.BENCHMARK_DEPENDENCY_FAILED: case CancellationReason.OVERLAPPING_BENCHMARK: - return 'Skipped'; + return 'Skipped' case CancellationReason.TIMEOUT: - return 'Timeout'; + return 'Timeout' case CancellationReason.INFRA_ERROR: default: - return 'Canceled'; + return 'Canceled' } } const renderError = (errors?: string[]): string => { - if (!errors || errors.length === 0) return ''; + if (!errors || errors.length === 0) return '' - return ` (${errors[0]})`; + return ` (${errors[0]})` } const printFlowResult = (flow: Flow): void => { - if (flow.status === BenchmarkStatus.SUCCESS) { + if (flow.status === RunStatus.SUCCESS) { success(`[Passed] ${flow.name}`) - } else if (flow.status === BenchmarkStatus.ERROR) { + } else if (flow.status === RunStatus.ERROR) { err(`[Failed] ${flow.name}${renderError(flow.errors)}`) - } else if (flow.status === BenchmarkStatus.WARNING) { + } else if (flow.status === RunStatus.WARNING) { warning(`[Warning] ${flow.name}`) - } else if (flow.status === BenchmarkStatus.CANCELED) { + } else if (flow.status === RunStatus.CANCELED) { canceled(`[${getCanceledStatusMessage(flow.cancellationReason)}] ${flow.name}`) } } -const flowWord = (count: number): string => count === 1 ? 'Flow' : 'Flows' +const flowWord = (count: number): string => (count === 1 ? 'Flow' : 'Flows') const getFailedFlowsCountStr = (flows: Flow[]): string => { - const failedFlows = flows.filter(flow => flow.status === BenchmarkStatus.ERROR) + const failedFlows = flows.filter((flow) => flow.status === RunStatus.ERROR) return `${failedFlows.length}/${flows.length} ${flowWord(flows.length)} Failed` } -const printUploadResult = (status: BenchmarkStatus, flows: Flow[]) => { - if (status === BenchmarkStatus.ERROR) { +const printUploadResult = (status: UploadStatus, flows: Flow[]) => { + if (status === UploadStatus.ERROR) { err(getFailedFlowsCountStr(flows)) } else { - const passedFlows = flows.filter(flow => flow.status === BenchmarkStatus.SUCCESS || flow.status === BenchmarkStatus.WARNING) - const canceledFlows = flows.filter(flow => flow.status === BenchmarkStatus.CANCELED) + const passedFlows = flows.filter((flow) => flow.status === RunStatus.SUCCESS || flow.status === RunStatus.WARNING) + const canceledFlows = flows.filter((flow) => flow.status === RunStatus.CANCELED) if (passedFlows.length > 0) { success(`${passedFlows.length}/${flows.length} ${flowWord(flows.length)} Passed`) @@ -72,11 +72,7 @@ export default class StatusPoller { completedFlows: { [flowName: string]: string } = {} stopped: Boolean = false - constructor( - private client: ApiClient, - private uploadId: string, - private consoleUrl: string, - ) { } + constructor(private client: ApiClient, private uploadId: string, private consoleUrl: string) {} markFailed(msg: string) { core.setFailed(msg) @@ -89,10 +85,7 @@ export default class StatusPoller { this.markFailed(msg) } - async poll( - sleep: number, - prevErrorCount: number = 0 - ) { + async poll(sleep: number, prevErrorCount: number = 0) { if (this.stopped) { return } @@ -118,7 +111,7 @@ export default class StatusPoller { core.setOutput('MAESTRO_CLOUD_UPLOAD_STATUS', status) core.setOutput('MAESTRO_CLOUD_FLOW_RESULTS', flows) - if (status === BenchmarkStatus.ERROR) { + if (status === UploadStatus.ERROR) { const resultStr = getFailedFlowsCountStr(flows) console.log('') this.markFailed(resultStr) @@ -148,10 +141,15 @@ export default class StatusPoller { } registerTimeout(timeoutInMinutes?: number) { - this.timeout = setTimeout(() => { - warning(`Timed out waiting for Upload to complete. View the Upload in the console for more information: ${this.consoleUrl}`) - this.stopped = true - }, timeoutInMinutes ? (timeoutInMinutes * 60 * 1000) : WAIT_TIMEOUT_MS) + this.timeout = setTimeout( + () => { + warning( + `Timed out waiting for Upload to complete. View the Upload in the console for more information: ${this.consoleUrl}` + ) + this.stopped = true + }, + timeoutInMinutes ? timeoutInMinutes * 60 * 1000 : WAIT_TIMEOUT_MS + ) } teardown() { @@ -168,4 +166,4 @@ export default class StatusPoller { this.registerTimeout(timeout) } -} \ No newline at end of file +} diff --git a/dist/index.js b/dist/index.js index 1a7e4dc..963c2a2 100644 --- a/dist/index.js +++ b/dist/index.js @@ -46538,18 +46538,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.CancellationReason = exports.UploadStatusError = exports.BenchmarkStatus = void 0; +exports.CancellationReason = exports.UploadStatusError = exports.UploadStatus = exports.RunStatus = void 0; const node_fetch_1 = __importStar(__nccwpck_require__(4429)); -var BenchmarkStatus; -(function (BenchmarkStatus) { - BenchmarkStatus["PENDING"] = "PENDING"; - BenchmarkStatus["RUNNING"] = "RUNNING"; - BenchmarkStatus["SUCCESS"] = "SUCCESS"; - BenchmarkStatus["ERROR"] = "ERROR"; - BenchmarkStatus["CANCELED"] = "CANCELED"; - BenchmarkStatus["WARNING"] = "WARNING"; - BenchmarkStatus["STOPPED"] = "STOPPED"; -})(BenchmarkStatus = exports.BenchmarkStatus || (exports.BenchmarkStatus = {})); +var RunStatus; +(function (RunStatus) { + RunStatus["PENDING"] = "PENDING"; + RunStatus["PREPARING"] = "PREPARING"; + RunStatus["INSTALLING"] = "INSTALLING"; + RunStatus["RUNNING"] = "RUNNING"; + RunStatus["SUCCESS"] = "SUCCESS"; + RunStatus["ERROR"] = "ERROR"; + RunStatus["CANCELED"] = "CANCELED"; + RunStatus["WARNING"] = "WARNING"; + RunStatus["STOPPED"] = "STOPPED"; +})(RunStatus = exports.RunStatus || (exports.RunStatus = {})); +var UploadStatus; +(function (UploadStatus) { + UploadStatus["PENDING"] = "PENDING"; + UploadStatus["RUNNING"] = "RUNNING"; + UploadStatus["SUCCESS"] = "SUCCESS"; + UploadStatus["ERROR"] = "ERROR"; + UploadStatus["CANCELED"] = "CANCELED"; + UploadStatus["WARNING"] = "WARNING"; + UploadStatus["STOPPED"] = "STOPPED"; +})(UploadStatus = exports.UploadStatus || (exports.UploadStatus = {})); class UploadStatusError { constructor(status, text) { this.status = status; @@ -46586,9 +46598,9 @@ class ApiClient { const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/v2/upload`, { method: 'POST', headers: { - Authorization: `Bearer ${this.apiKey}`, + Authorization: `Bearer ${this.apiKey}` }, - body: formData, + body: formData }); if (!res.ok) { const body = yield res.text(); @@ -46613,9 +46625,9 @@ class ApiClient { const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/runMaestroTest`, { method: 'POST', headers: { - Authorization: `Bearer ${this.apiKey}`, + Authorization: `Bearer ${this.apiKey}` }, - body: formData, + body: formData }); if (!res.ok) { const body = yield res.text(); @@ -46631,8 +46643,8 @@ class ApiClient { const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/upload/${uploadId}`, { method: 'GET', headers: { - Authorization: `Bearer ${this.apiKey}`, - }, + Authorization: `Bearer ${this.apiKey}` + } }); if (!res.ok) { const body = yield res.text(); @@ -46649,8 +46661,8 @@ class ApiClient { const res = yield (0, node_fetch_1.default)(`${this.apiUrl}/v2/upload/${uploadId}/status?includeErrors=true`, { method: 'GET', headers: { - Authorization: `Bearer ${this.apiKey}`, - }, + Authorization: `Bearer ${this.apiKey}` + } }); if (!res.ok) { const body = yield res.text(); @@ -46713,7 +46725,7 @@ const ApiClient_1 = __nccwpck_require__(9494); const log_1 = __nccwpck_require__(3826); const WAIT_TIMEOUT_MS = 1000 * 60 * 30; // 30 minutes const INTERVAL_MS = 10000; // 10 seconds -const TERMINAL_STATUSES = new Set([ApiClient_1.BenchmarkStatus.SUCCESS, ApiClient_1.BenchmarkStatus.ERROR, ApiClient_1.BenchmarkStatus.WARNING, ApiClient_1.BenchmarkStatus.CANCELED]); +const TERMINAL_STATUSES = new Set([ApiClient_1.RunStatus.SUCCESS, ApiClient_1.RunStatus.ERROR, ApiClient_1.RunStatus.WARNING, ApiClient_1.RunStatus.CANCELED]); const isCompleted = (flow) => TERMINAL_STATUSES.has(flow.status); const getCanceledStatusMessage = (reason) => { switch (reason) { @@ -46733,31 +46745,31 @@ const renderError = (errors) => { return ` (${errors[0]})`; }; const printFlowResult = (flow) => { - if (flow.status === ApiClient_1.BenchmarkStatus.SUCCESS) { + if (flow.status === ApiClient_1.RunStatus.SUCCESS) { (0, log_1.success)(`[Passed] ${flow.name}`); } - else if (flow.status === ApiClient_1.BenchmarkStatus.ERROR) { + else if (flow.status === ApiClient_1.RunStatus.ERROR) { (0, log_1.err)(`[Failed] ${flow.name}${renderError(flow.errors)}`); } - else if (flow.status === ApiClient_1.BenchmarkStatus.WARNING) { + else if (flow.status === ApiClient_1.RunStatus.WARNING) { (0, log_1.warning)(`[Warning] ${flow.name}`); } - else if (flow.status === ApiClient_1.BenchmarkStatus.CANCELED) { + else if (flow.status === ApiClient_1.RunStatus.CANCELED) { (0, log_1.canceled)(`[${getCanceledStatusMessage(flow.cancellationReason)}] ${flow.name}`); } }; -const flowWord = (count) => count === 1 ? 'Flow' : 'Flows'; +const flowWord = (count) => (count === 1 ? 'Flow' : 'Flows'); const getFailedFlowsCountStr = (flows) => { - const failedFlows = flows.filter(flow => flow.status === ApiClient_1.BenchmarkStatus.ERROR); + const failedFlows = flows.filter((flow) => flow.status === ApiClient_1.RunStatus.ERROR); return `${failedFlows.length}/${flows.length} ${flowWord(flows.length)} Failed`; }; const printUploadResult = (status, flows) => { - if (status === ApiClient_1.BenchmarkStatus.ERROR) { + if (status === ApiClient_1.UploadStatus.ERROR) { (0, log_1.err)(getFailedFlowsCountStr(flows)); } else { - const passedFlows = flows.filter(flow => flow.status === ApiClient_1.BenchmarkStatus.SUCCESS || flow.status === ApiClient_1.BenchmarkStatus.WARNING); - const canceledFlows = flows.filter(flow => flow.status === ApiClient_1.BenchmarkStatus.CANCELED); + const passedFlows = flows.filter((flow) => flow.status === ApiClient_1.RunStatus.SUCCESS || flow.status === ApiClient_1.RunStatus.WARNING); + const canceledFlows = flows.filter((flow) => flow.status === ApiClient_1.RunStatus.CANCELED); if (passedFlows.length > 0) { (0, log_1.success)(`${passedFlows.length}/${flows.length} ${flowWord(flows.length)} Passed`); if (canceledFlows.length > 0) { @@ -46809,7 +46821,7 @@ class StatusPoller { (0, log_1.info)(`${this.consoleUrl}`); core.setOutput('MAESTRO_CLOUD_UPLOAD_STATUS', status); core.setOutput('MAESTRO_CLOUD_FLOW_RESULTS', flows); - if (status === ApiClient_1.BenchmarkStatus.ERROR) { + if (status === ApiClient_1.UploadStatus.ERROR) { const resultStr = getFailedFlowsCountStr(flows); console.log(''); this.markFailed(resultStr); @@ -46848,7 +46860,7 @@ class StatusPoller { this.timeout = setTimeout(() => { (0, log_1.warning)(`Timed out waiting for Upload to complete. View the Upload in the console for more information: ${this.consoleUrl}`); this.stopped = true; - }, timeoutInMinutes ? (timeoutInMinutes * 60 * 1000) : WAIT_TIMEOUT_MS); + }, timeoutInMinutes ? timeoutInMinutes * 60 * 1000 : WAIT_TIMEOUT_MS); } teardown() { this.timeout && clearTimeout(this.timeout);