Compare commits

..
Author SHA1 Message Date
Kirby Cofino f5191b2da6 [eas-cli] Add pagination to supported GQL queries 2022-07-05 18:39:13 -07:00
498 changed files with 3657 additions and 4392 deletions

No files matched your search

View File
File renamed without changes.
-146
View File
@@ -1,146 +0,0 @@
name: Issue Triage
on:
issues:
types: [labeled]
jobs:
needs-repro:
runs-on: ubuntu-20.04
if: "${{ contains(github.event.label.name, 'incomplete issue: missing or invalid repro') }}"
steps:
- uses: actions/github-script@v3
with:
github-token: ${{ secrets.EXPO_BOT_GITHUB_TOKEN }}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Hi there! It looks like your issue requires a minimal reproducible example, but it is invalid or absent. Please prepare such an example and share it in a new issue.
**The best way to get attention to your issue is to provide a clean and easy way for a developer to reproduce the issue on their own machine.** Please do not provide your entire project, or a project with more code than is necessary to reproduce the issue.
A side benefit of going through the process of narrowing down the minimal amount of code needed to reproduce the issue is that you may get lucky and discover that the bug is due to a mistake in your application code that you can quickly fix on your own.
### Resources
- ["How to create a Minimal, Reproducible Example"](https://stackoverflow.com/help/minimal-reproducible-example)
- ["How to narrow down the source of an error"](https://expo.fyi/manual-debugging)
### Common concerns
#### "I've only been able to reproduce it in private, proprietary code"
You may not have spent enough time narrowing down the root cause of the issue. Try out the techniques discussed in this [manual debugging guide](https://expo.fyi/manual-debugging) to learn how to isolate the problem from the rest of your codebase.
#### "I didn't have time to create one"
That's understandable, it can take some time to prepare. We ask that you hold off on filing an issue until you are able to fully complete the required fields in the issue template.
#### "You can reproduce it by yourself by creating a project and following these steps"
This is useful knowledge, but it's still valuable to have the resulting project that is produced from running the steps, where you have verified you can reproduce the issue.
`})
github.issues.update({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed'
})
needs-info:
runs-on: ubuntu-20.04
if: "${{ contains(github.event.label.name, 'incomplete issue: missing info') }}"
steps:
- uses: actions/github-script@v3
with:
github-token: ${{ secrets.EXPO_BOT_GITHUB_TOKEN }}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Hello! There isn't enough information provided in your issue description for us to be able to help you. The best way to get help is to provide information to enable other developers to understand the issue and reproduce it on their own machines.
Please create a new issue and fill out the entire issue template to the best of your ability. Refer to the following resources for more information on how to create a good issue report.
### Resources
- ["How do I ask a good question?"](https://stackoverflow.com/help/how-to-ask)
- ["How to create a Minimal, Reproducible Example"](https://stackoverflow.com/help/minimal-reproducible-example)
- ["How to narrow down the source of an error"](https://expo.fyi/manual-debugging)
`})
github.issues.update({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed'
})
issue-accepted:
runs-on: ubuntu-20.04
if: github.event.label.name == 'issue accepted'
steps:
- uses: actions/github-script@v3
with:
github-token: ${{ secrets.EXPO_BOT_GITHUB_TOKEN }}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Thank you for filing this issue!
This comment acknowledges we believe this may be a bug and there’s enough information to investigate it.
However, we can’t promise any sort of timeline for resolution. We prioritize issues based on severity, breadth of impact, and alignment with our roadmap. If you’d like to help move it more quickly, you can continue to investigate it more deeply and/or you can open a pull request that fixes the cause.
`})
question:
runs-on: ubuntu-20.04
if: "${{ contains(github.event.label.name, 'invalid issue: question') }}"
steps:
- uses: actions/github-script@v3
with:
github-token: ${{ secrets.EXPO_BOT_GITHUB_TOKEN }}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Hello! Our GitHub issues are reserved for bug reports.
If you have a question about Expo or related tools, please post on our forums at https://forums.expo.dev/ or join our Discord at https://chat.expo.dev/.
### Resources
- ["How do I ask a good question?"](https://stackoverflow.com/help/how-to-ask)
- ["Join the community"](https://docs.expo.dev/next-steps/community/)
`})
github.issues.update({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed'
})
feature-request:
runs-on: ubuntu-20.04
if: "${{ contains(github.event.label.name, 'invalid issue: feature request') }}"
steps:
- uses: actions/github-script@v3
with:
github-token: ${{ secrets.EXPO_BOT_GITHUB_TOKEN }}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Hello! Our GitHub issues are reserved for bug reports.
If you would like to request a feature, please post to https://expo.canny.io/feature-requests.
`})
github.issues.update({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed'
})
-26
View File
@@ -1,26 +0,0 @@
name: Test scripts
defaults:
run:
working-directory: scripts
on:
push:
branches: [main]
paths:
- 'scripts/**'
pull_request:
paths:
- 'scripts/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 18
- run: yarn install --frozen-lockfile --check-files
- run: yarn typecheck
-4
View File
@@ -2,7 +2,6 @@
packages/*/build
packages/eas-cli/dist
packages/eas-cli/tmp
scripts/build
# Code editors
.idea
@@ -19,6 +18,3 @@ node_modules
# Tests
coverage
# Copies of script for testing ESM imports
packages/*/scripts/testESMImports.ts
-2
View File
@@ -22,8 +22,6 @@ This is the log of notable changes to EAS CLI and related packages.
- Update validation rules for metadata info locales. ([#1174](https://github.com/expo/eas-cli/pull/1174) by [@byCedric](https://github.com/byCedric))
- Improve store configuration defaults and schema documentation. ([#1183](https://github.com/expo/eas-cli/pull/1183) by [@byCedric](https://github.com/byCedric))
- Improve store config categories configuration. ([#1187](https://github.com/expo/eas-cli/pull/1187) by [@byCedric](https://github.com/byCedric))
- Remove deprecated idfa and price tier from store config. ([#1193](https://github.com/expo/eas-cli/pull/1193) by [@byCedric](https://github.com/byCedric))
## [0.54.1](https://github.com/expo/eas-cli/releases/tag/v0.54.1) - 2022-06-15
-7
View File
@@ -1,7 +0,0 @@
module.exports = {
projects: [
require('./packages/eas-cli/jest.config.cjs'),
require('./packages/eas-json/jest.config.cjs'),
],
testPathIgnorePatterns: ['.*'],
};
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
projects: [
require('./packages/eas-cli/jest.config.js'),
require('./packages/eas-json/jest.config.js'),
],
testPathIgnorePatterns: ['.*'],
};
+1 -9
View File
@@ -1,15 +1,7 @@
module.exports = {
preset: 'ts-jest/presets/default-esm',
preset: 'ts-jest',
testEnvironment: 'node',
testRegex: '/__tests__/.*(test|spec)\\.[jt]sx?$',
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
collectCoverageFrom: ['<rootDir>/src/**/*.{ts,tsx,js,jsx}'],
globals: {
'ts-jest': {
useESM: true,
},
},
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
};
+3 -5
View File
@@ -1,6 +1,5 @@
{
"private": true,
"type": "module",
"workspaces": [
"packages/*"
],
@@ -14,9 +13,8 @@
"eas": "packages/eas-cli/bin/run",
"lint": "eslint .",
"release": "./scripts/bin/run update-local-plugin && lerna version --exact",
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
"clean": "lerna run clean && rm -rf node_modules coverage",
"test-esm-imports": "lerna run test-esm-imports"
"test": "jest",
"clean": "lerna run clean && rm -rf node_modules coverage"
},
"devDependencies": {
"@types/jest": "27.5.0",
@@ -31,7 +29,7 @@
"oclif": "3.0.1",
"prettier": "2.6.2",
"ts-jest": "28.0.2",
"typescript": "4.7.4"
"typescript": "4.6.4"
},
"volta": {
"node": "18.2.0",
@@ -1,4 +1,4 @@
const rootEslintrc = require('../../.eslintrc.cjs');
const rootEslintrc = require('../../.eslintrc.js');
module.exports = {
rules: {
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env node
require('@oclif/core')
.run()
.then(require('@oclif/core/flush'))
.catch(require('@oclif/core/handle'));
-7
View File
@@ -1,7 +0,0 @@
#!/usr/bin/env node
import { Errors, flush, run } from '@oclif/core';
run(undefined, import.meta.url)
.then(flush)
.catch(Errors.handle);
+23 -1
View File
@@ -10209,7 +10209,7 @@
"args": [
{
"name": "assetContentTypes",
"description": "max 600",
"description": "max 1400",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -10763,6 +10763,22 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "maxBuildTimeSeconds",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "metrics",
"description": null,
@@ -12574,6 +12590,12 @@
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "NORMAL_PLUS",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
@@ -1,5 +1,5 @@
module.exports = {
...require('../../jest/jest.config.cjs'),
...require('../../jest/jest.config.js'),
rootDir: __dirname,
roots: ['src', '__mocks__'],
globalSetup: './jest/global-setup.ts',
-2
View File
@@ -1,5 +1,3 @@
import { jest } from '@jest/globals';
/* eslint-disable no-console */
beforeAll(() => {
console.log = jest.fn();
+6 -15
View File
@@ -1,6 +1,5 @@
{
"name": "eas-cli",
"type": "module",
"description": "EAS command line tool",
"version": "0.54.1",
"author": "Expo <[email protected]>",
@@ -28,7 +27,7 @@
"@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.6.0",
"@expo/timeago.js": "1.0.0",
"@oclif/core": "1.9.5",
"@oclif/core": "1.9.0",
"@urql/core": "2.4.4",
"@urql/exchange-retry": "0.3.3",
"ajv": "^6.12.6",
@@ -82,7 +81,6 @@
"@graphql-codegen/introspection": "2.1.1",
"@graphql-codegen/typescript": "2.4.11",
"@graphql-codegen/typescript-operations": "2.4.0",
"@jest/globals": "28.1.2",
"@types/cli-progress": "3.9.2",
"@types/dateformat": "3.0.1",
"@types/envinfo": "7.8.1",
@@ -100,17 +98,15 @@
"axios": "0.27.2",
"eslint-plugin-graphql": "4.0.0",
"form-data": "4.0.0",
"globby": "13.1.2",
"memfs": "3.4.3",
"mockdate": "3.0.5",
"nock": "13.2.4",
"tempy": "0.7.0",
"ts-deepmerge": "2.0.1",
"ts-node": "10.8.2",
"typescript": "4.7.4"
"typescript": "4.6.4"
},
"engines": {
"node": ">=14.16"
"node": ">=14.0.0"
},
"files": [
"/bin",
@@ -200,14 +196,9 @@
"build": "tsc --project tsconfig.build.json",
"watch": "yarn build --watch --preserveWatchOutput",
"typecheck": "tsc",
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
"version": "yarn oclif readme && node scripts/patch-readme.cjs && git add README.md",
"test": "jest",
"version": "yarn oclif readme && node scripts/patch-readme && git add README.md",
"generate-graphql-code": "graphql-codegen --config graphql-codegen.yml",
"clean": "rm -rf dist build tmp node_modules yarn-error.log",
"test-esm-imports": "cp ../../scripts/src/testESMImports.ts scripts/ && ts-node-esm ./scripts/testESMImports.ts 'build/**/*.js'"
},
"volta": {
"node": "18.2.0",
"yarn": "1.22.18"
"clean": "rm -rf dist build tmp node_modules yarn-error.log"
}
}
+163 -140
View File
@@ -33,152 +33,96 @@
]
},
"AppleCategory": {
"type": "array",
"description": "App Store categories for the app, can add up to two categories. The first item in this list is the primary category, the second is the secondary and optional category. `STICKERS` and `GAMES` categories can add up to two additional subcategories, written as nested array.",
"markdownDescription": "App Store categories for the app, can add up to two categories. The first item in this list is the primary category, the second is the secondary and optional category. `STICKERS` and `GAMES` categories can add up to two additional subcategories, written as nested array. E.g.\n\n- `[\"SPORTS\"]`\n- `[\"SPORTS\", \"LIFESTYLE\"]`\n- `[[\"GAMES\", \"GAMES_CARD\"], \"ENTERTAINMENT\"]`\n- `[\"GAMES\", \"WEATHER\"]`",
"minItems": 1,
"maxItems": 2,
"uniqueItems": true,
"enum": [
"FOOD_AND_DRINK",
"BUSINESS",
"EDUCATION",
"SOCIAL_NETWORKING",
"BOOKS",
"SPORTS",
"FINANCE",
"REFERENCE",
"GRAPHICS_AND_DESIGN",
"DEVELOPER_TOOLS",
"HEALTH_AND_FITNESS",
"MUSIC",
"WEATHER",
"TRAVEL",
"ENTERTAINMENT",
"STICKERS",
"GAMES",
"LIFESTYLE",
"MEDICAL",
"MAGAZINES_AND_NEWSPAPERS",
"UTILITIES",
"SHOPPING",
"PRODUCTIVITY",
"NEWS",
"PHOTO_AND_VIDEO",
"NAVIGATION"
]
},
"AppleSubcategory": {
"enum": [
"STICKERS_PLACES_AND_OBJECTS",
"STICKERS_EMOJI_AND_EXPRESSIONS",
"STICKERS_CELEBRATIONS",
"STICKERS_CELEBRITIES",
"STICKERS_MOVIES_AND_TV",
"STICKERS_SPORTS_AND_ACTIVITIES",
"STICKERS_EATING_AND_DRINKING",
"STICKERS_CHARACTERS",
"STICKERS_ANIMALS",
"STICKERS_FASHION",
"STICKERS_ART",
"STICKERS_GAMING",
"STICKERS_KIDS_AND_FAMILY",
"STICKERS_PEOPLE",
"STICKERS_MUSIC",
"GAMES_SPORTS",
"GAMES_WORD",
"GAMES_MUSIC",
"GAMES_ADVENTURE",
"GAMES_ACTION",
"GAMES_ROLE_PLAYING",
"GAMES_CASUAL",
"GAMES_BOARD",
"GAMES_TRIVIA",
"GAMES_CARD",
"GAMES_PUZZLE",
"GAMES_CASINO",
"GAMES_STRATEGY",
"GAMES_SIMULATION",
"GAMES_RACING",
"GAMES_FAMILY"
]
},
"AppleIdfa": {
"description": "Identifier for Advertisers",
"type": "object",
"additionalProperties": false,
"defaultSnippets": [
{
"label": "Basic categories",
"description": "Add two simple categories",
"body": [
"$1",
"$2"
]
},
{
"label": "Category with subcategories",
"description": "Add a category containing subcategories",
"body": [
[
"${1|GAMES,STICKERS|}",
"$3",
"$4"
]
]
"body": {
"servesAds": false,
"attributesAppInstallationToPreviousAd": false,
"attributesActionWithPreviousAd": false,
"honorsLimitedAdTracking": false
}
}
],
"items": {
"oneOf": [
{
"$ref": "#/definitions/apple/AppleCategory/categories/root"
},
{
"$ref": "#/definitions/apple/AppleCategory/categories/parent"
},
{
"type": "array",
"description": "The GAMES category can have additional subcategories",
"minItems": 1,
"maxItems": 3,
"uniqueItems": true,
"items": [
{
"const": "GAMES"
},
{
"$ref": "#/definitions/apple/AppleCategory/categories/games"
},
{
"$ref": "#/definitions/apple/AppleCategory/categories/games"
}
]
},
{
"type": "array",
"description": "The STICKERS category can have additional subcategories",
"minItems": 1,
"maxItems": 3,
"uniqueItems": true,
"items": [
{
"const": "STICKERS"
},
{
"$ref": "#/definitions/apple/AppleCategory/categories/stickers"
},
{
"$ref": "#/definitions/apple/AppleCategory/categories/stickers"
}
]
}
]
},
"categories": {
"root": {
"enum": [
"BUSINESS",
"SOCIAL_NETWORKING",
"MEDICAL",
"FOOD_AND_DRINK",
"EDUCATION",
"BOOKS",
"SPORTS",
"FINANCE",
"REFERENCE",
"GRAPHICS_AND_DESIGN",
"DEVELOPER_TOOLS",
"HEALTH_AND_FITNESS",
"MUSIC",
"WEATHER",
"TRAVEL",
"ENTERTAINMENT",
"LIFESTYLE",
"MAGAZINES_AND_NEWSPAPERS",
"UTILITIES",
"SHOPPING",
"PRODUCTIVITY",
"NEWS",
"PHOTO_AND_VIDEO",
"NAVIGATION"
]
"properties": {
"servesAds": {
"type": "boolean"
},
"parent": {
"enum": [
"GAMES",
"STICKERS"
]
"attributesAppInstallationToPreviousAd": {
"type": "boolean"
},
"games": {
"enum": [
"GAMES_SPORTS",
"GAMES_WORD",
"GAMES_MUSIC",
"GAMES_ADVENTURE",
"GAMES_ACTION",
"GAMES_ROLE_PLAYING",
"GAMES_CASUAL",
"GAMES_BOARD",
"GAMES_TRIVIA",
"GAMES_CARD",
"GAMES_PUZZLE",
"GAMES_CASINO",
"GAMES_STRATEGY",
"GAMES_SIMULATION",
"GAMES_RACING",
"GAMES_FAMILY"
]
"attributesActionWithPreviousAd": {
"type": "boolean"
},
"stickers": {
"enum": [
"STICKERS_PLACES_AND_OBJECTS",
"STICKERS_EMOJI_AND_EXPRESSIONS",
"STICKERS_CELEBRATIONS",
"STICKERS_CELEBRITIES",
"STICKERS_MOVIES_AND_TV",
"STICKERS_SPORTS_AND_ACTIVITIES",
"STICKERS_EATING_AND_DRINKING",
"STICKERS_CHARACTERS",
"STICKERS_ANIMALS",
"STICKERS_FASHION",
"STICKERS_ART",
"STICKERS_GAMING",
"STICKERS_KIDS_AND_FAMILY",
"STICKERS_PEOPLE",
"STICKERS_MUSIC"
]
"honorsLimitedAdTracking": {
"type": "boolean"
}
}
},
@@ -610,7 +554,79 @@
"$ref": "#/definitions/apple/AppleAdvisory"
},
"categories": {
"$ref": "#/definitions/apple/AppleCategory"
"type": "array",
"maxItems": 2,
"description": "App Store categories for the app. Can add up to two categories. STICKERS and GAMES categories can add an additional subcategory.",
"items": {
"anyOf": [
{
"$ref": "#/definitions/apple/AppleCategory"
},
{
"type": "array",
"maxItems": 2,
"items": [
{
"enum": [
"GAMES"
]
},
{
"enum": [
"GAMES_SPORTS",
"GAMES_WORD",
"GAMES_MUSIC",
"GAMES_ADVENTURE",
"GAMES_ACTION",
"GAMES_ROLE_PLAYING",
"GAMES_CASUAL",
"GAMES_BOARD",
"GAMES_TRIVIA",
"GAMES_CARD",
"GAMES_PUZZLE",
"GAMES_CASINO",
"GAMES_STRATEGY",
"GAMES_SIMULATION",
"GAMES_RACING",
"GAMES_FAMILY"
]
}
],
"additionalItems": false
},
{
"type": "array",
"maxItems": 2,
"items": [
{
"enum": [
"STICKERS"
]
},
{
"enum": [
"STICKERS_PLACES_AND_OBJECTS",
"STICKERS_EMOJI_AND_EXPRESSIONS",
"STICKERS_CELEBRATIONS",
"STICKERS_CELEBRITIES",
"STICKERS_MOVIES_AND_TV",
"STICKERS_SPORTS_AND_ACTIVITIES",
"STICKERS_EATING_AND_DRINKING",
"STICKERS_CHARACTERS",
"STICKERS_ANIMALS",
"STICKERS_FASHION",
"STICKERS_ART",
"STICKERS_GAMING",
"STICKERS_KIDS_AND_FAMILY",
"STICKERS_PEOPLE",
"STICKERS_MUSIC"
]
}
],
"additionalItems": false
}
]
}
},
"copyright": {
"type": "string",
@@ -626,6 +642,9 @@
"storeInfo": "The name of the person or entity that owns the exclusive rights to your app, preceded by the year the rights were obtained (for example, \"2008 Acme Inc.\"). Do not provide a URL."
}
},
"idfa": {
"$ref": "#/definitions/apple/AppleIdfa"
},
"info": {
"description": "Localized core app info",
"type": "object",
@@ -967,6 +986,10 @@
"review": {
"$ref": "#/definitions/apple/AppleReview",
"description": "Info for the App Store reviewer"
},
"priceTier": {
"type": "number",
"description": "App price tier. 0 is Free."
}
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import assert from 'assert';
import nock from 'nock';
import { ApiV2Error, api, getExpoApiBaseUrl } from '../api.js';
import { ApiV2Error, api, getExpoApiBaseUrl } from '../api';
describe('apiClient', () => {
it('converts Expo APIv2 error to ApiV2Error', async () => {
@@ -1,4 +1,4 @@
import { Actor } from '../user/User.js';
import { Actor } from '../user/User';
interface MockProject {
projectRoot: string;
@@ -1,4 +1,4 @@
import { fixArchiveUrl } from '../uploads.js';
import { fixArchiveUrl } from '../uploads';
describe(fixArchiveUrl, () => {
it('fixes broken links', () => {
+1 -1
View File
@@ -1,4 +1,4 @@
import { Analytics, Event } from './events.js';
import { Analytics, Event } from './events';
export type TrackingContext = Record<string, string | number | boolean>;
+1 -1
View File
@@ -1,4 +1,4 @@
import { logEvent } from './rudderstackClient.js';
import { logEvent } from './rudderstackClient';
export type Event = BuildEvent | SubmissionEvent | MetadataEvent;
export enum SubmissionEvent {
@@ -1,12 +1,10 @@
import RudderAnalyticsModule from '@expo/rudder-sdk-node';
import RudderAnalytics from '@expo/rudder-sdk-node';
import os from 'os';
import { URL } from 'url';
import { v4 as uuidv4 } from 'uuid';
import UserSettings from '../user/UserSettings.js';
import { easCliVersion } from '../utils/easCli.js';
const RudderAnalytics = RudderAnalyticsModule.default;
import UserSettings from '../user/UserSettings';
import { easCliVersion } from '../utils/easCli';
const PLATFORM_TO_ANALYTICS_PLATFORM: { [platform: string]: string } = {
darwin: 'Mac',
@@ -14,7 +12,7 @@ const PLATFORM_TO_ANALYTICS_PLATFORM: { [platform: string]: string } = {
linux: 'Linux',
};
let rudderstackClient: RudderAnalyticsModule.default | null = null;
let rudderstackClient: RudderAnalytics | null = null;
let userIdentified = false;
let identifyData: {
userId: string;
+2 -2
View File
@@ -1,7 +1,7 @@
import { JSONValue } from '@expo/json-file';
import fetch, { RequestError, RequestInit } from './fetch.js';
import { getAccessToken, getSessionSecret } from './user/sessionStorage.js';
import fetch, { RequestError, RequestInit } from './fetch';
import { getAccessToken, getSessionSecret } from './user/sessionStorage';
export class ApiV2Error extends Error {
readonly name = 'ApiV2Error';
@@ -7,16 +7,13 @@ import { vol } from 'memfs';
import os from 'os';
import path from 'path';
import {
getAppBuildGradleAsync,
resolveConfigValue,
} from '../../../project/android/gradleUtils.js';
import { getAppBuildGradleAsync, resolveConfigValue } from '../../../project/android/gradleUtils';
import {
BumpStrategy,
bumpVersionAsync,
bumpVersionInAppJsonAsync,
maybeResolveVersionsAsync,
} from '../version.js';
} from '../version';
const fsReal = jest.requireActual('fs').promises as typeof fs;
jest.mock('fs');
+17 -17
View File
@@ -1,33 +1,33 @@
import { Android, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import AndroidCredentialsProvider, {
AndroidCredentials,
} from '../../credentials/android/AndroidCredentialsProvider.js';
import { BuildParamsInput } from '../../graphql/generated.js';
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation.js';
import Log from '../../log.js';
} from '../../credentials/android/AndroidCredentialsProvider';
import { BuildParamsInput } from '../../graphql/generated';
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation';
import Log from '../../log';
import {
ensureApplicationIdIsDefinedForManagedProjectAsync,
getApplicationIdAsync,
} from '../../project/android/applicationId.js';
import { resolveGradleBuildContextAsync } from '../../project/android/gradle.js';
import { toggleConfirmAsync } from '../../prompts.js';
import { findAccountByName } from '../../user/Account.js';
import { nullthrows } from '../../utils/nullthrows.js';
} from '../../project/android/applicationId';
import { resolveGradleBuildContextAsync } from '../../project/android/gradle';
import { toggleConfirmAsync } from '../../prompts';
import { findAccountByName } from '../../user/Account';
import {
BuildRequestSender,
CredentialsResult,
JobData,
prepareBuildRequestForPlatformAsync,
} from '../build.js';
import { AndroidBuildContext, BuildContext, CommonContext } from '../context.js';
import { transformMetadata } from '../graphql.js';
import { logCredentialsSource } from '../utils/credentials.js';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate.js';
import { transformJob } from './graphql.js';
import { prepareJobAsync } from './prepareJob.js';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration.js';
} from '../build';
import { AndroidBuildContext, BuildContext, CommonContext } from '../context';
import { transformMetadata } from '../graphql';
import { logCredentialsSource } from '../utils/credentials';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate';
import { transformJob } from './graphql';
import { prepareJobAsync } from './prepareJob';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
export async function createAndroidContextAsync(
ctx: CommonContext<Platform.ANDROID>
@@ -1,7 +1,7 @@
import { Android } from '@expo/eas-build-job';
import { AndroidBuildType, AndroidJobInput } from '../../graphql/generated.js';
import { transformProjectArchive, transformWorkflow } from '../graphql.js';
import { AndroidBuildType, AndroidJobInput } from '../../graphql/generated';
import { transformProjectArchive, transformWorkflow } from '../graphql';
export function transformJob(job: Android.Job): AndroidJobInput {
return {
@@ -3,11 +3,11 @@ import { BuildProfile } from '@expo/eas-json';
import path from 'path';
import slash from 'slash';
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider.js';
import { getUsername } from '../../project/projectUtils.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
import { getVcsClient } from '../../vcs/index.js';
import { BuildContext } from '../context.js';
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
import { getUsername } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
interface JobData {
projectArchive: ArchiveSource;
@@ -1,18 +1,16 @@
import { ExpoConfig } from '@expo/config';
import ConfigPlugins from '@expo/config-plugins';
import { AndroidConfig } from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import { AndroidVersionAutoIncrement, BuildProfile } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
import Log from '../../log.js';
import { isExpoUpdatesInstalled } from '../../project/projectUtils.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule.js';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version.js';
const { AndroidConfig } = ConfigPlugins;
import Log from '../../log';
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
export async function syncProjectConfigurationAsync({
projectDir,
@@ -1,21 +1,19 @@
import { ExpoConfig } from '@expo/config';
import ConfigPlugins from '@expo/config-plugins';
import { AndroidConfig } from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import Log from '../../log.js';
import Log from '../../log';
import {
getAppBuildGradleAsync,
parseGradleCommand,
resolveConfigValue,
} from '../../project/android/gradleUtils.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { updateAppJsonConfigAsync } from '../utils/appJson.js';
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version.js';
const { AndroidConfig } = ConfigPlugins;
} from '../../project/android/gradleUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { updateAppJsonConfigAsync } from '../utils/appJson';
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
export enum BumpStrategy {
APP_VERSION,
+20 -20
View File
@@ -3,10 +3,11 @@ import { CredentialsSource } from '@expo/eas-json';
import chalk from 'chalk';
import cliProgress from 'cli-progress';
import fs from 'fs-extra';
import nullthrows from 'nullthrows';
import { withAnalyticsAsync } from '../analytics/common.js';
import { BuildEvent } from '../analytics/events.js';
import { getExpoWebsiteBaseUrl } from '../api.js';
import { withAnalyticsAsync } from '../analytics/common';
import { BuildEvent } from '../analytics/events';
import { getExpoWebsiteBaseUrl } from '../api';
import {
AppPlatform,
BuildFragment,
@@ -14,27 +15,26 @@ import {
BuildPriority,
BuildStatus,
UploadSessionType,
} from '../graphql/generated.js';
import { BuildResult } from '../graphql/mutations/BuildMutation.js';
import { BuildQuery } from '../graphql/queries/BuildQuery.js';
import Log, { learnMore } from '../log.js';
import { Ora, ora } from '../ora.js';
} from '../graphql/generated';
import { BuildResult } from '../graphql/mutations/BuildMutation';
import { BuildQuery } from '../graphql/queries/BuildQuery';
import Log, { learnMore } from '../log';
import { Ora, ora } from '../ora';
import {
appPlatformDisplayNames,
appPlatformEmojis,
requestedPlatformDisplayNames,
} from '../platform.js';
import { uploadAsync } from '../uploads.js';
import { formatBytes } from '../utils/files.js';
import { nullthrows } from '../utils/nullthrows.js';
import { createProgressTracker } from '../utils/progress.js';
import { sleepAsync } from '../utils/promise.js';
import { getVcsClient } from '../vcs/index.js';
import { BuildContext } from './context.js';
import { runLocalBuildAsync } from './local.js';
import { collectMetadataAsync } from './metadata.js';
import { printDeprecationWarnings } from './utils/printBuildInfo.js';
import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository.js';
} from '../platform';
import { uploadAsync } from '../uploads';
import { formatBytes } from '../utils/files';
import { createProgressTracker } from '../utils/progress';
import { sleepAsync } from '../utils/promise';
import { getVcsClient } from '../vcs';
import { BuildContext } from './context';
import { runLocalBuildAsync } from './local';
import { collectMetadataAsync } from './metadata';
import { printDeprecationWarnings } from './utils/printBuildInfo';
import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository';
export interface CredentialsResult<Credentials> {
source: CredentialsSource.LOCAL | CredentialsSource.REMOTE;
+5 -5
View File
@@ -3,11 +3,11 @@ import { EasJson, EasJsonReader } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import Log from '../log.js';
import { resolveWorkflowAsync } from '../project/workflow.js';
import { easCliVersion } from '../utils/easCli.js';
import { getVcsClient } from '../vcs/index.js';
import { maybeBailOnRepoStatusAsync, reviewAndCommitChangesAsync } from './utils/repository.js';
import Log from '../log';
import { resolveWorkflowAsync } from '../project/workflow';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { maybeBailOnRepoStatusAsync, reviewAndCommitChangesAsync } from './utils/repository';
interface ConfigureParams {
projectDir: string;
+8 -8
View File
@@ -3,14 +3,14 @@ import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import type { XCBuildConfiguration } from 'xcode';
import { TrackingContext } from '../analytics/common.js';
import { CredentialsContext } from '../credentials/context.js';
import { Target } from '../credentials/ios/types.js';
import { BuildResourceClass } from '../graphql/generated.js';
import { GradleBuildContext } from '../project/android/gradle.js';
import { XcodeBuildContext } from '../project/ios/scheme.js';
import { Actor } from '../user/User.js';
import { LocalBuildOptions } from './local.js';
import { TrackingContext } from '../analytics/common';
import { CredentialsContext } from '../credentials/context';
import { Target } from '../credentials/ios/types';
import { BuildResourceClass } from '../graphql/generated';
import { GradleBuildContext } from '../project/android/gradle';
import { XcodeBuildContext } from '../project/ios/scheme';
import { Actor } from '../user/User';
import { LocalBuildOptions } from './local';
export type CommonContext<T extends Platform> = Omit<BuildContext<T>, 'android' | 'ios'>;
+14 -16
View File
@@ -1,24 +1,22 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import JsonFileModule from '@expo/json-file';
import JsonFile from '@expo/json-file';
import resolveFrom from 'resolve-from';
import { v4 as uuidv4 } from 'uuid';
import { TrackingContext } from '../analytics/common.js';
import { Analytics, BuildEvent } from '../analytics/events.js';
import { CredentialsContext } from '../credentials/context.js';
import { BuildResourceClass } from '../graphql/generated.js';
import { getExpoConfig } from '../project/expoConfig.js';
import { getProjectAccountName, getProjectIdAsync } from '../project/projectUtils.js';
import { resolveWorkflowAsync } from '../project/workflow.js';
import { findAccountByName } from '../user/Account.js';
import { ensureLoggedInAsync } from '../user/actions.js';
import { createAndroidContextAsync } from './android/build.js';
import { BuildContext, CommonContext } from './context.js';
import { createIosContextAsync } from './ios/build.js';
import { LocalBuildOptions } from './local.js';
const JsonFile = JsonFileModule.default;
import { TrackingContext } from '../analytics/common';
import { Analytics, BuildEvent } from '../analytics/events';
import { CredentialsContext } from '../credentials/context';
import { BuildResourceClass } from '../graphql/generated';
import { getExpoConfig } from '../project/expoConfig';
import { getProjectAccountName, getProjectIdAsync } from '../project/projectUtils';
import { resolveWorkflowAsync } from '../project/workflow';
import { findAccountByName } from '../user/Account';
import { ensureLoggedInAsync } from '../user/actions';
import { createAndroidContextAsync } from './android/build';
import { BuildContext, CommonContext } from './context';
import { createIosContextAsync } from './ios/build';
import { LocalBuildOptions } from './local';
export async function createBuildContextAsync<T extends Platform>({
buildProfileName,
+1 -1
View File
@@ -8,7 +8,7 @@ import {
DistributionType,
ProjectArchiveSourceInput,
ProjectArchiveSourceType,
} from '../graphql/generated.js';
} from '../graphql/generated';
export function transformProjectArchive(archiveSource: ArchiveSource): ProjectArchiveSourceInput {
if (archiveSource.type === ArchiveSourceType.S3) {
@@ -1,10 +1,10 @@
import { ExpoConfig } from '@expo/config';
import { InfoPlist } from '@expo/config-plugins';
import { IOSConfig } from '@expo/config-plugins';
import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import { readPlistAsync } from '../../../utils/plist.js';
import { readPlistAsync } from '../../../utils/plist';
import {
BumpStrategy,
bumpVersionAsync,
@@ -13,7 +13,7 @@ import {
getInfoPlistPath,
readBuildNumberAsync,
readShortVersionAsync,
} from '../version.js';
} from '../version';
jest.mock('fs');
@@ -62,7 +62,9 @@ describe(bumpVersionAsync, () => {
});
const appJSON = await fs.readJSON('/repo/app.json');
const infoPlist = (await readPlistAsync('/repo/ios/myproject/Info.plist')) as InfoPlist;
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info.plist'
)) as IOSConfig.InfoPlist;
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('2');
expect(appJSON.expo.version).toBe('1.0.0');
@@ -84,7 +86,9 @@ describe(bumpVersionAsync, () => {
});
const appJSON = await fs.readJSON('/repo/app.json');
const infoPlist = (await readPlistAsync('/repo/ios/myproject/Info2.plist')) as InfoPlist;
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info2.plist'
)) as IOSConfig.InfoPlist;
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('2');
expect(appJSON.expo.version).toBe('1.0.0');
@@ -104,7 +108,9 @@ describe(bumpVersionAsync, () => {
});
const appJSON = await fs.readJSON('/repo/app.json');
const infoPlist = (await readPlistAsync('/repo/ios/myproject/Info.plist')) as InfoPlist;
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info.plist'
)) as IOSConfig.InfoPlist;
expect(fakeExp.version).toBe('1.0.1');
expect(fakeExp.ios?.buildNumber).toBe('1');
expect(appJSON.expo.version).toBe('1.0.1');
@@ -124,7 +130,9 @@ describe(bumpVersionAsync, () => {
});
const appJSON = await fs.readJSON('/repo/app.json');
const infoPlist = (await readPlistAsync('/repo/ios/myproject/Info.plist')) as InfoPlist;
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info.plist'
)) as IOSConfig.InfoPlist;
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('1');
expect(appJSON.expo.version).toBe('1.0.0');
+15 -17
View File
@@ -1,23 +1,21 @@
import ConfigPlugins from '@expo/config-plugins';
import { IOSConfig } from '@expo/config-plugins';
import { Ios, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import type { XCBuildConfiguration } from 'xcode';
import { IosCredentials, Target } from '../../credentials/ios/types.js';
import { BuildParamsInput } from '../../graphql/generated.js';
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation.js';
import { ensureBundleIdentifierIsDefinedForManagedProjectAsync } from '../../project/ios/bundleIdentifier.js';
import { resolveXcodeBuildContextAsync } from '../../project/ios/scheme.js';
import { findApplicationTarget, resolveTargetsAsync } from '../../project/ios/target.js';
import { BuildRequestSender, JobData, prepareBuildRequestForPlatformAsync } from '../build.js';
import { BuildContext, CommonContext, IosBuildContext } from '../context.js';
import { transformMetadata } from '../graphql.js';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate.js';
import { ensureIosCredentialsAsync } from './credentials.js';
import { transformJob } from './graphql.js';
import { prepareJobAsync } from './prepareJob.js';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration.js';
const { IOSConfig } = ConfigPlugins;
import { IosCredentials, Target } from '../../credentials/ios/types';
import { BuildParamsInput } from '../../graphql/generated';
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation';
import { ensureBundleIdentifierIsDefinedForManagedProjectAsync } from '../../project/ios/bundleIdentifier';
import { resolveXcodeBuildContextAsync } from '../../project/ios/scheme';
import { findApplicationTarget, resolveTargetsAsync } from '../../project/ios/target';
import { BuildRequestSender, JobData, prepareBuildRequestForPlatformAsync } from '../build';
import { BuildContext, CommonContext, IosBuildContext } from '../context';
import { transformMetadata } from '../graphql';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate';
import { ensureIosCredentialsAsync } from './credentials';
import { transformJob } from './graphql';
import { prepareJobAsync } from './prepareJob';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
export async function createIosContextAsync(
ctx: CommonContext<Platform.IOS>
@@ -1,11 +1,11 @@
import { Platform } from '@expo/eas-build-job';
import IosCredentialsProvider from '../../credentials/ios/IosCredentialsProvider.js';
import { getAppFromContext } from '../../credentials/ios/actions/BuildCredentialsUtils.js';
import { IosCredentials, Target } from '../../credentials/ios/types.js';
import { CredentialsResult } from '../build.js';
import { BuildContext } from '../context.js';
import { logCredentialsSource } from '../utils/credentials.js';
import IosCredentialsProvider from '../../credentials/ios/IosCredentialsProvider';
import { getAppFromContext } from '../../credentials/ios/actions/BuildCredentialsUtils';
import { IosCredentials, Target } from '../../credentials/ios/types';
import { CredentialsResult } from '../build';
import { BuildContext } from '../context';
import { logCredentialsSource } from '../utils/credentials';
export async function ensureIosCredentialsAsync(
buildCtx: BuildContext<Platform.IOS>,
+3 -3
View File
@@ -1,8 +1,8 @@
import { Env, Ios } from '@expo/eas-build-job';
import nullthrows from 'nullthrows';
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated.js';
import { nullthrows } from '../../utils/nullthrows.js';
import { transformProjectArchive, transformWorkflow } from '../graphql.js';
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated';
import { transformProjectArchive, transformWorkflow } from '../graphql';
export function transformJob(job: Ios.Job): IosJobInput {
return {
+5 -5
View File
@@ -2,11 +2,11 @@ import { ArchiveSource, Ios, Job, Platform, sanitizeJob } from '@expo/eas-build-
import path from 'path';
import slash from 'slash';
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types.js';
import { getUsername } from '../../project/projectUtils.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
import { getVcsClient } from '../../vcs/index.js';
import { BuildContext } from '../context.js';
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
import { getUsername } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
interface JobData {
projectArchive: ArchiveSource;
@@ -3,10 +3,10 @@ import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile, IosVersionAutoIncrement } from '@expo/eas-json';
import type { XCBuildConfiguration } from 'xcode';
import { isExpoUpdatesInstalled } from '../../project/projectUtils.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule.js';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version.js';
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
export async function syncProjectConfigurationAsync({
projectDir,
+12 -14
View File
@@ -1,18 +1,16 @@
import { ExpoConfig, getConfigFilePaths } from '@expo/config';
import ConfigPlugins, { InfoPlist } from '@expo/config-plugins';
import { IOSConfig } from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import chalk from 'chalk';
import path from 'path';
import type { XCBuildConfiguration } from 'xcode';
import Log from '../../log.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { promptAsync } from '../../prompts.js';
import { readPlistAsync, writePlistAsync } from '../../utils/plist.js';
import { updateAppJsonConfigAsync } from '../utils/appJson.js';
import { bumpAppVersionAsync } from '../utils/version.js';
const { IOSConfig } = ConfigPlugins;
import Log from '../../log';
import { resolveWorkflowAsync } from '../../project/workflow';
import { promptAsync } from '../../prompts';
import { readPlistAsync, writePlistAsync } from '../../utils/plist';
import { updateAppJsonConfigAsync } from '../utils/appJson';
import { bumpAppVersionAsync } from '../utils/version';
export enum BumpStrategy {
APP_VERSION,
@@ -147,9 +145,9 @@ async function writeVersionsToInfoPlistAsync({
}: {
projectDir: string;
exp: ExpoConfig;
infoPlist: InfoPlist;
infoPlist: IOSConfig.InfoPlist;
buildSettings: XCBuildConfiguration['buildSettings'];
}): Promise<InfoPlist> {
}): Promise<IOSConfig.InfoPlist> {
let updatedInfoPlist = IOSConfig.Version.setVersion(exp, infoPlist);
updatedInfoPlist = IOSConfig.Version.setBuildNumber(exp, updatedInfoPlist);
await writeInfoPlistAsync({ projectDir, infoPlist: updatedInfoPlist, buildSettings });
@@ -178,9 +176,9 @@ export function getInfoPlistPath(
async function readInfoPlistAsync(
projectDir: string,
buildSettings: XCBuildConfiguration['buildSettings']
): Promise<InfoPlist> {
): Promise<IOSConfig.InfoPlist> {
const infoPlistPath = getInfoPlistPath(projectDir, buildSettings);
return ((await readPlistAsync(infoPlistPath)) ?? {}) as InfoPlist;
return ((await readPlistAsync(infoPlistPath)) ?? {}) as IOSConfig.InfoPlist;
}
async function writeInfoPlistAsync({
@@ -189,7 +187,7 @@ async function writeInfoPlistAsync({
buildSettings,
}: {
projectDir: string;
infoPlist: InfoPlist;
infoPlist: IOSConfig.InfoPlist;
buildSettings: XCBuildConfiguration['buildSettings'];
}): Promise<void> {
const infoPlistPath = getInfoPlistPath(projectDir, buildSettings);
+2 -2
View File
@@ -1,9 +1,9 @@
import { Job } from '@expo/eas-build-job';
import { default as spawnAsync } from '@expo/spawn-async';
import spawnAsync from '@expo/spawn-async';
import { ChildProcess } from 'child_process';
import semver from 'semver';
import { ora } from '../ora.js';
import { ora } from '../ora';
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
const PLUGIN_PACKAGE_VERSION = '0.0.85';
+11 -13
View File
@@ -1,27 +1,25 @@
import ConfigPlugins from '@expo/config-plugins';
import { Updates } from '@expo/config-plugins';
import { Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
import { IosEnterpriseProvisioning } from '@expo/eas-json';
import fs from 'fs-extra';
import resolveFrom from 'resolve-from';
import Log from '../log.js';
import { getUsername, isExpoUpdatesInstalled } from '../project/projectUtils.js';
import Log from '../log';
import { getUsername, isExpoUpdatesInstalled } from '../project/projectUtils';
import {
readChannelSafelyAsync as readAndroidChannelSafelyAsync,
readReleaseChannelSafelyAsync as readAndroidReleaseChannelSafelyAsync,
} from '../update/android/UpdatesModule.js';
} from '../update/android/UpdatesModule';
import {
readChannelSafelyAsync as readIosChannelSafelyAsync,
readReleaseChannelSafelyAsync as readIosReleaseChannelSafelyAsync,
} from '../update/ios/UpdatesModule.js';
import { ensureLoggedInAsync } from '../user/actions.js';
import { easCliVersion } from '../utils/easCli.js';
import { getVcsClient } from '../vcs/index.js';
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version.js';
import { BuildContext } from './context.js';
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version.js';
const { Updates } = ConfigPlugins;
} from '../update/ios/UpdatesModule';
import { ensureLoggedInAsync } from '../user/actions';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
import { BuildContext } from './context';
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
export async function collectMetadataAsync<T extends Platform>(
ctx: BuildContext<T>
+24 -24
View File
@@ -1,6 +1,7 @@
import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile, EasJsonReader, SubmitProfile } from '@expo/eas-json';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import {
AppPlatform,
@@ -8,38 +9,37 @@ import {
BuildResourceClass,
BuildStatus,
SubmissionFragment,
} from '../graphql/generated.js';
import { toAppPlatform, toPlatform } from '../graphql/types/AppPlatform.js';
import Log from '../log.js';
} from '../graphql/generated';
import { toAppPlatform, toPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
import {
RequestedPlatform,
appPlatformDisplayNames,
appPlatformEmojis,
toPlatforms,
} from '../platform.js';
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk.js';
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig.js';
import { createSubmissionContextAsync } from '../submit/context.js';
} from '../platform';
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
import { createSubmissionContextAsync } from '../submit/context';
import {
submitAsync,
waitToCompleteAsync as waitForSubmissionsToCompleteAsync,
} from '../submit/submit.js';
import { printSubmissionDetailsUrls } from '../submit/utils/urls.js';
import { printJsonOnlyOutput } from '../utils/json.js';
import { nullthrows } from '../utils/nullthrows.js';
import { ProfileData, getProfilesAsync } from '../utils/profiles.js';
import { getVcsClient } from '../vcs/index.js';
import { prepareAndroidBuildAsync } from './android/build.js';
import { BuildRequestSender, waitForBuildEndAsync } from './build.js';
import { ensureProjectConfiguredAsync } from './configure.js';
import { BuildContext } from './context.js';
import { createBuildContextAsync } from './createContext.js';
import { prepareIosBuildAsync } from './ios/build.js';
import { LocalBuildOptions } from './local.js';
import { UserInputResourceClass } from './types.js';
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from './utils/devClient.js';
import { printBuildResults, printLogsUrls } from './utils/printBuildInfo.js';
import { ensureRepoIsCleanAsync } from './utils/repository.js';
} from '../submit/submit';
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
import { printJsonOnlyOutput } from '../utils/json';
import { ProfileData, getProfilesAsync } from '../utils/profiles';
import { getVcsClient } from '../vcs';
import { prepareAndroidBuildAsync } from './android/build';
import { BuildRequestSender, waitForBuildEndAsync } from './build';
import { ensureProjectConfiguredAsync } from './configure';
import { BuildContext } from './context';
import { createBuildContextAsync } from './createContext';
import { prepareIosBuildAsync } from './ios/build';
import { LocalBuildOptions } from './local';
import { UserInputResourceClass } from './types';
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from './utils/devClient';
import { printBuildResults, printLogsUrls } from './utils/printBuildInfo';
import { ensureRepoIsCleanAsync } from './utils/repository';
let metroConfigValidated = false;
let sdkVersionChecked = false;
+1 -3
View File
@@ -1,9 +1,7 @@
import * as ExpoConfig from '@expo/config';
import JsonFileModule from '@expo/json-file';
import JsonFile from '@expo/json-file';
import assert from 'assert';
const JsonFile = JsonFileModule.default;
export async function updateAppJsonConfigAsync(
{
projectDir,
@@ -2,8 +2,8 @@ import { Platform } from '@expo/eas-build-job';
import { CredentialsSource } from '@expo/eas-json';
import chalk from 'chalk';
import Log from '../../log.js';
import { requestedPlatformDisplayNames } from '../../platform.js';
import Log from '../../log';
import { requestedPlatformDisplayNames } from '../../platform';
export function logCredentialsSource(
credentialsSource: CredentialsSource,
@@ -3,15 +3,15 @@ import { Errors } from '@oclif/core';
import chalk from 'chalk';
import resolveFrom from 'resolve-from';
import { toAppPlatform } from '../../graphql/types/AppPlatform.js';
import Log, { learnMore } from '../../log.js';
import { appPlatformDisplayNames } from '../../platform.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { confirmAsync } from '../../prompts.js';
import { expoCommandAsync } from '../../utils/expoCli.js';
import { ProfileData } from '../../utils/profiles.js';
import { getVcsClient } from '../../vcs/index.js';
import { reviewAndCommitChangesAsync } from './repository.js';
import { toAppPlatform } from '../../graphql/types/AppPlatform';
import Log, { learnMore } from '../../log';
import { appPlatformDisplayNames } from '../../platform';
import { resolveWorkflowAsync } from '../../project/workflow';
import { confirmAsync } from '../../prompts';
import { expoCommandAsync } from '../../utils/expoCli';
import { ProfileData } from '../../utils/profiles';
import { getVcsClient } from '../../vcs';
import { reviewAndCommitChangesAsync } from './repository';
export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
projectDir,
@@ -4,10 +4,10 @@ import {
AppPlatform,
BuildFragment,
BuildStatus as GraphQLBuildStatus,
} from '../../graphql/generated.js';
import { appPlatformDisplayNames } from '../../platform.js';
import formatFields from '../../utils/formatFields.js';
import { getBuildLogsUrl } from './url.js';
} from '../../graphql/generated';
import { appPlatformDisplayNames } from '../../platform';
import formatFields from '../../utils/formatFields';
import { getBuildLogsUrl } from './url';
export function formatGraphQLBuild(build: BuildFragment): string {
const actor = getActorName(build);
@@ -11,11 +11,11 @@ import {
DistributionType,
EasBuildDeprecationInfo,
EasBuildDeprecationInfoType,
} from '../../graphql/generated.js';
import Log, { learnMore } from '../../log.js';
import { appPlatformDisplayNames, appPlatformEmojis } from '../../platform.js';
import { printJsonOnlyOutput } from '../../utils/json.js';
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url.js';
} from '../../graphql/generated';
import Log, { learnMore } from '../../log';
import { appPlatformDisplayNames, appPlatformEmojis } from '../../platform';
import { printJsonOnlyOutput } from '../../utils/json';
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url';
export function printLogsUrls(builds: BuildFragment[]): void {
if (builds.length === 1) {
@@ -4,13 +4,13 @@ import path from 'path';
import tar from 'tar';
import { v4 as uuidv4 } from 'uuid';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { confirmAsync, promptAsync } from '../../prompts.js';
import { formatBytes } from '../../utils/files.js';
import { getTmpDirectory } from '../../utils/paths.js';
import { endTimer, formatMilliseconds, startTimer } from '../../utils/timer.js';
import { getVcsClient } from '../../vcs/index.js';
import Log from '../../log';
import { ora } from '../../ora';
import { confirmAsync, promptAsync } from '../../prompts';
import { formatBytes } from '../../utils/files';
import { getTmpDirectory } from '../../utils/paths';
import { endTimer, formatMilliseconds, startTimer } from '../../utils/timer';
import { getVcsClient } from '../../vcs';
export async function maybeBailOnRepoStatusAsync(): Promise<void> {
if (!(await getVcsClient().isCommitRequiredAsync())) {
+2 -2
View File
@@ -1,7 +1,7 @@
import assert from 'assert';
import { getExpoApiBaseUrl, getExpoWebsiteBaseUrl } from '../../api.js';
import { AppPlatform, BuildFragment } from '../../graphql/generated.js';
import { getExpoApiBaseUrl, getExpoWebsiteBaseUrl } from '../../api';
import { AppPlatform, BuildFragment } from '../../graphql/generated';
export function getProjectDashboardUrl(accountName: string, projectName: string): string {
return new URL(
+4 -4
View File
@@ -1,11 +1,11 @@
import { ExpoConfig, getConfigFilePaths } from '@expo/config';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import semver from 'semver';
import Log from '../../log.js';
import { promptAsync } from '../../prompts.js';
import { nullthrows } from '../../utils/nullthrows.js';
import { updateAppJsonConfigAsync } from './appJson.js';
import Log from '../../log';
import { promptAsync } from '../../prompts';
import { updateAppJsonConfigAsync } from './appJson';
export function ensureStaticConfigExists(projectDir: string): void {
const paths = getConfigFilePaths(projectDir);
+3 -3
View File
@@ -2,9 +2,9 @@ import { Platform, Workflow } from '@expo/eas-build-job';
import fs from 'fs-extra';
import path from 'path';
import Log, { learnMore } from '../log.js';
import { getVcsClient } from '../vcs/index.js';
import { CommonContext } from './context.js';
import Log, { learnMore } from '../log';
import { getVcsClient } from '../vcs';
import { CommonContext } from './context';
export function checkNodeEnvVariable(ctx: CommonContext<Platform>): void {
if (ctx.buildProfile.env?.NODE_ENV === 'production') {
@@ -7,13 +7,13 @@ import {
flushAsync as flushAnalyticsAsync,
initAsync as initAnalyticsAsync,
logEvent,
} from '../analytics/rudderstackClient.js';
import { findProjectRootAsync } from '../project/projectUtils.js';
import { getUserAsync } from '../user/User.js';
import { ensureLoggedInAsync } from '../user/actions.js';
import { easCliVersion } from '../utils/easCli.js';
import GitClient from '../vcs/clients/git.js';
import { setVcsClient } from '../vcs/index.js';
} from '../analytics/rudderstackClient';
import { findProjectRootAsync } from '../project/projectUtils';
import { getUserAsync } from '../user/User';
import { ensureLoggedInAsync } from '../user/actions';
import { easCliVersion } from '../utils/easCli';
import { setVcsClient } from '../vcs';
import GitClient from '../vcs/clients/git';
export default abstract class EasCommand extends Command {
/**
@@ -1,8 +1,8 @@
import { flushAsync, initAsync, logEvent } from '../../analytics/rudderstackClient.js';
import { jester as mockJester } from '../../credentials/__tests__/fixtures-constants.js';
import { getUserAsync } from '../../user/User.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
import EasCommand from '../EasCommand.js';
import { flushAsync, initAsync, logEvent } from '../../analytics/rudderstackClient';
import { jester as mockJester } from '../../credentials/__tests__/fixtures-constants';
import { getUserAsync } from '../../user/User';
import { ensureLoggedInAsync } from '../../user/actions';
import EasCommand from '../EasCommand';
jest.mock('../../user/User');
jest.mock('../../user/actions', () => ({ ensureLoggedInAsync: jest.fn() }));
@@ -1,6 +1,6 @@
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { showLoginPromptAsync } from '../../user/actions.js';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { showLoginPromptAsync } from '../../user/actions';
export default class AccountLogin extends EasCommand {
static description = 'log in with your Expo account';
@@ -1,6 +1,6 @@
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { logoutAsync } from '../../user/User.js';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { logoutAsync } from '../../user/User';
export default class AccountLogout extends EasCommand {
static description = 'log out';
@@ -1,8 +1,8 @@
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { getActorDisplayName, getUserAsync } from '../../user/User.js';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { getActorDisplayName, getUserAsync } from '../../user/User';
export default class AccountView extends EasCommand {
static description = 'show the username you are logged in as';
+3 -3
View File
@@ -1,6 +1,6 @@
import EasCommand from '../commandUtils/EasCommand.js';
import Log from '../log.js';
import UserSettings from '../user/UserSettings.js';
import EasCommand from '../commandUtils/EasCommand';
import Log from '../log';
import UserSettings from '../user/UserSettings';
export default class AnalyticsView extends EasCommand {
static description = 'display or change analytics settings';
+10 -10
View File
@@ -1,24 +1,24 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
CreateUpdateBranchForAppMutation,
CreateUpdateBranchForAppMutationVariables,
UpdateBranch,
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import { getVcsClient } from '../../vcs/index.js';
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { getVcsClient } from '../../vcs';
export async function createUpdateBranchOnAppAsync({
appId,
@@ -1,25 +1,25 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
DeleteUpdateBranchMutation,
DeleteUpdateBranchMutationVariables,
DeleteUpdateBranchResult,
GetBranchInfoQuery,
GetBranchInfoQueryVariables,
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { promptAsync, toggleConfirmAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
} from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
async function getBranchInfoAsync({
appId,
+57 -27
View File
@@ -2,38 +2,39 @@ import { Flags } from '@oclif/core';
import chalk from 'chalk';
import CliTable from 'cli-table3';
import { print } from 'graphql';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
BranchesByAppQuery,
BranchesByAppQueryVariables,
UpdateBranchFragment,
} from '../../graphql/generated.js';
import { UpdateBranchFragmentNode } from '../../graphql/types/UpdateBranch.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
} from '../../graphql/generated';
import { UpdateBranchFragmentNode } from '../../graphql/types/UpdateBranch';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { PaginatedQueryResponse, performPaginatedQueryAsync } from '../../utils/queries';
const BRANCHES_LIMIT = 10_000;
export const BRANCHES_LIMIT = 50;
export async function listBranchesAsync({
projectId,
}: {
projectId: string;
}): Promise<UpdateBranchFragment[]> {
appId,
limit = BRANCHES_LIMIT,
offset = 0,
}: BranchesByAppQueryVariables): Promise<UpdateBranchFragment[]> {
const data = await withErrorHandlingAsync(
graphqlClient
.query<BranchesByAppQuery, BranchesByAppQueryVariables>(
gql`
query BranchesByAppQuery($appId: String!, $limit: Int!) {
query BranchesByAppQuery($appId: String!, $limit: Int!, $offset: Int!) {
app {
byId(appId: $appId) {
id
updateBranches(offset: 0, limit: $limit) {
updateBranches(offset: $offset, limit: $limit) {
id
...UpdateBranchFragment
}
@@ -43,8 +44,9 @@ export async function listBranchesAsync({
${print(UpdateBranchFragmentNode)}
`,
{
appId: projectId,
limit: BRANCHES_LIMIT,
appId,
limit,
offset,
},
{ additionalTypenames: ['UpdateBranch'] }
)
@@ -73,9 +75,28 @@ export default class BranchList extends EasCommand {
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp);
const branches = await listBranchesAsync({ projectId });
await queryForPaginatedBranchesAsync(projectId, flags);
}
}
async function queryForPaginatedBranchesAsync(
projectId: string,
flags: { json: boolean } & { json: boolean | undefined }
): Promise<void> {
const queryAdditionalBranchesAsync = async (
pageSize: number,
offset: number
): Promise<PaginatedQueryResponse<UpdateBranchFragment>> => {
const branches = await listBranchesAsync({ appId: projectId, limit: pageSize, offset });
return {
queryResponse: branches,
queryResponseRawLength: branches.length,
};
};
const renderPageOfBranches = (currentPage: UpdateBranchFragment[]): void => {
if (flags.json) {
printJsonOnlyOutput(branches);
printJsonOnlyOutput(currentPage);
} else {
const table = new CliTable({
head: ['Branch', ...UPDATE_COLUMNS],
@@ -83,7 +104,7 @@ export default class BranchList extends EasCommand {
});
table.push(
...branches.map(branch => [
...currentPage.map(branch => [
branch.name,
formatUpdate(branch.updates[0]),
branch.updates[0]?.runtimeVersion ?? 'N/A',
@@ -96,11 +117,20 @@ export default class BranchList extends EasCommand {
);
Log.addNewLineIfNone();
Log.log(chalk.bold('Branches with their most recent update group:'));
Log.log(chalk.bold('Branches:'));
Log.addNewLineIfNone();
Log.log(table.toString());
if (branches.length >= BRANCHES_LIMIT) {
Log.warn(`Showing first ${BRANCHES_LIMIT} branches, some results might be omitted.`);
}
}
}
};
await performPaginatedQueryAsync({
pageSize: 50,
offset: 0,
queryToPerform: queryAdditionalBranchesAsync,
promptOptions: {
type: 'confirm',
title: 'Fetch next page of branches?',
renderListItems: renderPageOfBranches,
},
});
}
@@ -1,4 +1,4 @@
import EasCommand from '../../commandUtils/EasCommand.js';
import EasCommand from '../../commandUtils/EasCommand';
export default class BranchPublish extends EasCommand {
static description = 'deprecated, use "eas update"';
@@ -1,24 +1,24 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
EditUpdateBranchInput,
EditUpdateBranchMutation,
EditUpdateBranchMutationVariables,
UpdateBranch,
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
async function renameUpdateBranchOnAppAsync({
appId,
+90 -41
View File
@@ -2,16 +2,18 @@ import { Flags } from '@oclif/core';
import chalk from 'chalk';
import Table from 'cli-table3';
import EasCommand from '../../commandUtils/EasCommand.js';
import { UpdateQuery } from '../../graphql/queries/UpdateQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils.js';
import groupBy from '../../utils/expodash/groupBy.js';
import formatFields from '../../utils/formatFields.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import EasCommand from '../../commandUtils/EasCommand';
import { ViewBranchUpdatesQuery } from '../../graphql/generated';
import { UpdateQuery } from '../../graphql/queries/UpdateQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils';
import groupBy from '../../utils/expodash/groupBy';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { PaginatedQueryResponse, performPaginatedQueryAsync } from '../../utils/queries';
export default class BranchView extends EasCommand {
static description = 'view a branch';
@@ -57,50 +59,97 @@ export default class BranchView extends EasCommand {
}));
}
await queryForPaginatedBranchesAsync(projectId, name, { json: jsonFlag });
}
}
type BranchUpdateObject = Exclude<
ViewBranchUpdatesQuery['app']['byId']['updateBranchByName'],
null | undefined
>['updates'][0];
async function queryForPaginatedBranchesAsync(
projectId: string,
name: string,
flags: { json: boolean } & { json: boolean | undefined }
): Promise<void> {
const queryAdditionalBranchesAsync = async (
pageSize: number,
offset: number
): Promise<PaginatedQueryResponse<BranchUpdateObject>> => {
const { app } = await UpdateQuery.viewBranchAsync({
appId: projectId,
name,
limit: pageSize,
offset,
});
const UpdateBranch = app?.byId.updateBranchByName;
if (!UpdateBranch) {
throw new Error(`Could not find branch "${name}"`);
}
const updates = Object.values(groupBy(UpdateBranch.updates, u => u.group)).map(
group => group[0]
const updateGroups = Object.values(groupBy(UpdateBranch.updates, u => u.group)).map(
updateGroup => ({
...updateGroup[0],
platform: getPlatformsForGroup({
updates: updateGroup,
group: updateGroup[0].group,
}),
})
);
if (jsonFlag) {
printJsonOnlyOutput({ ...UpdateBranch, updates });
return {
queryResponse: updateGroups,
queryResponseRawLength: UpdateBranch.updates.length,
};
};
const renderPageOfBranches = (currentPage: BranchUpdateObject[]): void => {
if (flags.json) {
printJsonOnlyOutput(currentPage);
} else {
const groupTable = new Table({
head: UPDATE_COLUMNS,
wordWrap: true,
});
if (flags.json) {
printJsonOnlyOutput({ currentPage });
} else {
const groupTable = new Table({
head: UPDATE_COLUMNS,
wordWrap: true,
});
for (const update of updates) {
groupTable.push([
formatUpdate(update),
update.runtimeVersion,
update.group,
getPlatformsForGroup({
updates: UpdateBranch.updates,
group: update.group,
}),
]);
for (const update of currentPage) {
groupTable.push([
formatUpdate(update),
update.runtimeVersion,
update.group,
getPlatformsForGroup({
updates: currentPage,
group: update.group,
}),
]);
}
Log.addNewLineIfNone();
Log.log(chalk.bold('Branch:'));
Log.log(
formatFields([
{ label: 'Name', value: currentPage[0]['branch']['name'] },
{ label: 'ID', value: currentPage[0]['branch']['id'] },
])
);
Log.addNewLineIfNone();
Log.log(groupTable.toString());
}
Log.addNewLineIfNone();
Log.log(chalk.bold('Branch:'));
Log.log(
formatFields([
{ label: 'Name', value: UpdateBranch.name },
{ label: 'ID', value: UpdateBranch.id },
])
);
Log.addNewLineIfNone();
Log.log(chalk.bold('Recently published update groups:'));
Log.log(groupTable.toString());
}
}
};
await performPaginatedQueryAsync({
pageSize: 50,
offset: 0,
queryToPerform: queryAdditionalBranchesAsync,
promptOptions: {
type: 'confirm',
title: 'Fetch next page of branches?',
renderListItems: renderPageOfBranches,
},
});
}
+81 -54
View File
@@ -1,25 +1,27 @@
import chalk from 'chalk';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
Build,
BuildFragment,
BuildStatus,
CancelBuildMutation,
CancelBuildMutationVariables,
} from '../../graphql/generated.js';
import { BuildQuery } from '../../graphql/queries/BuildQuery.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { appPlatformEmojis } from '../../platform.js';
import { getExpoConfig } from '../../project/expoConfig.js';
} from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { appPlatformEmojis } from '../../platform';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { confirmAsync, selectAsync } from '../../prompts.js';
} from '../../project/projectUtils';
import { confirmAsync } from '../../prompts';
import { PaginatedQueryResponse, performPaginatedQueryAsync } from '../../utils/queries';
async function cancelBuildAsync(buildId: string): Promise<Pick<Build, 'id' | 'status'>> {
const data = await withErrorHandlingAsync(
@@ -59,48 +61,6 @@ function formatUnfinishedBuild(
return `${platform} Started at: ${startTime}, Status: ${status}, Id: ${build.id}`;
}
async function selectBuildToCancelAsync(
projectId: string,
projectFullName: string
): Promise<string | null> {
const spinner = ora().start('Fetching the uncompleted builds…');
let builds;
try {
const [newBuilds, inQueueBuilds, inProgressBuilds] = await Promise.all([
BuildQuery.allForAppAsync(projectId, { filter: { status: BuildStatus.New } }),
BuildQuery.allForAppAsync(projectId, { filter: { status: BuildStatus.InQueue } }),
BuildQuery.allForAppAsync(projectId, { filter: { status: BuildStatus.InProgress } }),
]);
spinner.stop();
builds = [...newBuilds, ...inQueueBuilds, ...inProgressBuilds];
} catch (error) {
spinner.fail(
`Something went wrong and we couldn't fetch the builds for the project ${projectFullName}.`
);
throw error;
}
if (builds.length === 0) {
Log.warn(`There aren't any uncompleted builds for the project ${projectFullName}.`);
return null;
} else if (builds.length === 1) {
Log.log('Found one build');
Log.log(formatUnfinishedBuild(builds[0]));
await confirmAsync({
message: 'Do you want to cancel it?',
});
return builds[0].id;
} else {
const buildId = await selectAsync<string>(
'Which build do you want to cancel?',
builds.map(build => ({
title: formatUnfinishedBuild(build),
value: build.id,
}))
);
return buildId;
}
}
async function ensureBuildExistsAsync(buildId: string): Promise<void> {
try {
await BuildQuery.byIdAsync(buildId);
@@ -126,7 +86,8 @@ export default class BuildCancel extends EasCommand {
await ensureBuildExistsAsync(buildIdFromArg);
}
const buildId = buildIdFromArg || (await selectBuildToCancelAsync(projectId, projectFullName));
const buildId =
buildIdFromArg || (await queryForBuildToCancelAsync(projectId, projectFullName));
if (!buildId) {
return;
}
@@ -146,3 +107,69 @@ export default class BuildCancel extends EasCommand {
}
}
}
async function queryForBuildToCancelAsync(
projectId: string,
projectFullName: string
): Promise<string | null> {
const queryToPerformAsync = async (
pageSize: number,
offset: number
): Promise<PaginatedQueryResponse<BuildFragment>> => {
const [newBuilds, inQueueBuilds, inProgressBuilds] = await Promise.all([
BuildQuery.allForAppAsync(projectId, {
limit: pageSize,
offset,
filter: { status: BuildStatus.New },
}),
BuildQuery.allForAppAsync(projectId, {
limit: pageSize,
offset,
filter: { status: BuildStatus.InQueue },
}),
BuildQuery.allForAppAsync(projectId, {
limit: pageSize,
offset,
filter: { status: BuildStatus.InProgress },
}),
]);
// this variable controls whether performPaginatedQueryAsync will query the next page.
// force another query if we have hit the query limit for any of these 3 queries.
const queryResponseLength =
newBuilds.length < pageSize &&
inQueueBuilds.length < pageSize &&
inProgressBuilds.length < pageSize
? pageSize - 1
: pageSize;
return {
queryResponse: [...newBuilds, ...inQueueBuilds, ...inProgressBuilds],
queryResponseRawLength: queryResponseLength,
};
};
const getIdentifierForQueryItem = (buildFragment: BuildFragment): string => buildFragment.id;
const selectedBuild = (
await performPaginatedQueryAsync({
pageSize: 50,
offset: 0,
queryToPerform: queryToPerformAsync,
promptOptions: {
type: 'select',
title: 'Which build would you like to cancel?',
getIdentifierForQueryItem,
createDisplayTextForSelectionPromptListItem: formatUnfinishedBuild,
},
})
).pop();
if (!selectedBuild) {
Log.warn(`There aren't any uncompleted builds for the project ${projectFullName}.`);
return null;
}
const userConfirmedCancellation = await confirmAsync({
message: `Are you sure you want to cancel ${formatUnfinishedBuild(selectedBuild)}?`,
});
return userConfirmedCancellation ? selectedBuild.id : null;
}
@@ -2,18 +2,18 @@ import { Platform, Workflow } from '@expo/eas-build-job';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { cleanUpOldEasBuildGradleScriptAsync } from '../../build/android/syncProjectConfiguration.js';
import { ensureProjectConfiguredAsync } from '../../build/configure.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log, { learnMore } from '../../log.js';
import { RequestedPlatform } from '../../platform.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, isExpoUpdatesInstalled } from '../../project/projectUtils.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { promptAsync } from '../../prompts.js';
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from '../../update/android/UpdatesModule.js';
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule.js';
import { getVcsClient } from '../../vcs/index.js';
import { cleanUpOldEasBuildGradleScriptAsync } from '../../build/android/syncProjectConfiguration';
import { ensureProjectConfiguredAsync } from '../../build/configure';
import EasCommand from '../../commandUtils/EasCommand';
import Log, { learnMore } from '../../log';
import { RequestedPlatform } from '../../platform';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, isExpoUpdatesInstalled } from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { promptAsync } from '../../prompts';
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
import { getVcsClient } from '../../vcs';
export default class BuildConfigure extends EasCommand {
static description = 'configure the project to support EAS Build';
+7 -7
View File
@@ -1,13 +1,13 @@
import { Errors, Flags } from '@oclif/core';
import path from 'path';
import { BuildFlags, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit.js';
import { UserInputResourceClass } from '../../build/types.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { RequestedPlatform, selectRequestedPlatformAsync } from '../../platform.js';
import { findProjectRootAsync } from '../../project/projectUtils.js';
import { enableJsonOutput } from '../../utils/json.js';
import { BuildFlags, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
import { UserInputResourceClass } from '../../build/types';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { RequestedPlatform, selectRequestedPlatformAsync } from '../../platform';
import { findProjectRootAsync } from '../../project/projectUtils';
import { enableJsonOutput } from '../../utils/json';
interface RawBuildFlags {
platform?: string;
@@ -4,14 +4,14 @@ import fs from 'fs-extra';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { RequestedPlatform } from '../../platform.js';
import { findProjectRootAsync } from '../../project/projectUtils.js';
import { getTmpDirectory } from '../../utils/paths.js';
import { getVcsClient } from '../../vcs/index.js';
import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { ora } from '../../ora';
import { RequestedPlatform } from '../../platform';
import { findProjectRootAsync } from '../../project/projectUtils';
import { getTmpDirectory } from '../../utils/paths';
import { getVcsClient } from '../../vcs';
enum InspectStage {
ARCHIVE = 'archive',
+84 -58
View File
@@ -1,25 +1,26 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { BuildDistributionType, BuildStatus } from '../../build/types.js';
import { formatGraphQLBuild } from '../../build/utils/formatBuild.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import { BuildDistributionType, BuildStatus } from '../../build/types';
import { formatGraphQLBuild } from '../../build/utils/formatBuild';
import EasCommand from '../../commandUtils/EasCommand';
import {
AppPlatform,
BuildFragment,
DistributionType,
BuildStatus as GraphQLBuildStatus,
} from '../../graphql/generated.js';
import { BuildQuery } from '../../graphql/queries/BuildQuery.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { RequestedPlatform } from '../../platform.js';
import { getExpoConfig } from '../../project/expoConfig.js';
} from '../../graphql/generated';
import { BuildQuery, BuildsQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { RequestedPlatform } from '../../platform';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
} from '../../project/projectUtils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { PaginatedQueryResponse, performPaginatedQueryAsync } from '../../utils/queries';
export default class BuildList extends EasCommand {
static description = 'list all builds for your project';
@@ -56,7 +57,6 @@ export default class BuildList extends EasCommand {
appIdentifier: Flags.string(),
buildProfile: Flags.string(),
gitCommitHash: Flags.string(),
limit: Flags.integer(),
};
async runAsync(): Promise<void> {
@@ -66,7 +66,6 @@ export default class BuildList extends EasCommand {
platform: requestedPlatform,
status: buildStatus,
distribution: buildDistribution,
limit = 10,
} = flags;
if (json) {
enableJsonOutput();
@@ -81,51 +80,24 @@ export default class BuildList extends EasCommand {
const projectId = await getProjectIdAsync(exp);
const projectName = await getProjectFullNameAsync(exp);
const spinner = ora().start('Fetching the build list for the project…');
try {
const builds = await BuildQuery.allForAppAsync(projectId, {
limit,
filter: {
platform,
status: graphqlBuildStatus,
distribution: graphqlBuildDistribution,
channel: flags.channel,
appVersion: flags.appVersion,
appBuildVersion: flags.appBuildVersion,
sdkVersion: flags.sdkVersion,
runtimeVersion: flags.runtimeVersion,
appIdentifier: flags.appIdentifier,
buildProfile: flags.buildProfile,
gitCommitHash: flags.gitCommitHash,
},
});
if (builds.length) {
if (platform || graphqlBuildStatus) {
spinner.succeed(
`Showing ${builds.length} matching builds for the project ${projectName}`
);
} else {
spinner.succeed(`Showing last ${builds.length} builds for the project ${projectName}`);
}
if (json) {
printJsonOnlyOutput(builds);
} else {
const list = builds
.map(build => formatGraphQLBuild(build))
.join(`\n\n${chalk.dim('———')}\n\n`);
Log.log(`\n${list}`);
}
} else {
spinner.fail(`Couldn't find any builds for the project ${projectName}`);
}
} catch (e) {
spinner.fail(`Something went wrong and we couldn't fetch the build list ${projectName}`);
throw e;
}
await queryForPaginatedBuildsAsync(
projectId,
projectName,
{
platform,
status: graphqlBuildStatus,
distribution: graphqlBuildDistribution,
channel: flags.channel,
appVersion: flags.appVersion,
appBuildVersion: flags.appBuildVersion,
sdkVersion: flags.sdkVersion,
runtimeVersion: flags.runtimeVersion,
appIdentifier: flags.appIdentifier,
buildProfile: flags.buildProfile,
gitCommitHash: flags.gitCommitHash,
},
flags
);
}
}
@@ -170,3 +142,57 @@ const toGraphQLBuildDistribution = (
return undefined;
}
};
async function queryForPaginatedBuildsAsync(
projectId: string,
projectName: string,
filter: BuildsQuery['filter'],
flags: { json: boolean } & { json: boolean | undefined }
): Promise<void> {
const queryAdditionalBuildsAsync = async (
pageSize: number,
offset: number
): Promise<PaginatedQueryResponse<BuildFragment>> => {
const builds = await BuildQuery.allForAppAsync(projectId, {
limit: pageSize,
offset,
filter,
});
return {
queryResponse: builds,
queryResponseRawLength: builds.length,
};
};
const renderPageOfBuilds = (currentPage: BuildFragment[]): void => {
if (flags.json) {
printJsonOnlyOutput(currentPage);
} else {
if (currentPage.length) {
const list = currentPage
.map(build => formatGraphQLBuild(build))
.join(`\n\n${chalk.dim('———')}\n\n`);
Log.addNewLineIfNone();
Log.withTick(chalk.bold(`Builds for ${projectName}:`));
Log.log(`\n${list}`);
Log.addNewLineIfNone();
} else {
Log.withCross(chalk.bold(`Couldn't find any builds for the project ${projectName}`));
Log.addNewLineIfNone();
}
}
};
await performPaginatedQueryAsync({
pageSize: 50,
offset: 0,
queryToPerform: queryAdditionalBuildsAsync,
promptOptions: {
type: 'confirm',
title: 'Fetch next page of builds?',
renderListItems: renderPageOfBuilds,
},
});
}
+9 -9
View File
@@ -1,18 +1,18 @@
import { Flags } from '@oclif/core';
import { formatGraphQLBuild } from '../../build/utils/formatBuild.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import { BuildFragment } from '../../graphql/generated.js';
import { BuildQuery } from '../../graphql/queries/BuildQuery.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { formatGraphQLBuild } from '../../build/utils/formatBuild';
import EasCommand from '../../commandUtils/EasCommand';
import { BuildFragment } from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
} from '../../project/projectUtils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export default class BuildView extends EasCommand {
static description = 'view a build for your project';
+12 -12
View File
@@ -1,25 +1,25 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
CreateUpdateChannelOnAppMutation,
CreateUpdateChannelOnAppMutationVariables,
} from '../../graphql/generated.js';
import { BranchQuery } from '../../graphql/queries/BranchQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
} from '../../graphql/generated';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import formatFields from '../../utils/formatFields.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import { createUpdateBranchOnAppAsync } from '../branch/create.js';
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { createUpdateBranchOnAppAsync } from '../branch/create';
export async function createUpdateChannelOnAppAsync({
appId,
@@ -1,24 +1,24 @@
import { Flags } from '@oclif/core';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
DeleteUpdateChannelMutation,
DeleteUpdateChannelMutationVariables,
DeleteUpdateChannelResult,
GetChannelInfoQuery,
GetChannelInfoQueryVariables,
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { promptAsync, toggleConfirmAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
} from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export default class ChannelDelete extends EasCommand {
static hidden = true;
+10 -10
View File
@@ -1,9 +1,9 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
GetChannelByNameToEditQuery,
GetChannelByNameToEditQueryVariables,
@@ -11,13 +11,13 @@ import {
UpdateChannel,
UpdateChannelBranchMappingMutation,
UpdateChannelBranchMappingMutationVariables,
} from '../../graphql/generated.js';
import { BranchQuery } from '../../graphql/queries/BranchQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
} from '../../graphql/generated';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
async function getChannelByNameForAppAsync({
appId,
+110 -29
View File
@@ -1,42 +1,53 @@
import { Flags } from '@oclif/core';
import { gql } from 'graphql-tag';
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
GetAllChannelsForAppQuery,
GetAllChannelsForAppQueryVariables,
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import formatFields from '../../utils/formatFields.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import { logChannelDetails } from './view.js';
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { PaginatedQueryResponse, performPaginatedQueryAsync } from '../../utils/queries';
import { BRANCHES_LIMIT } from '../branch/list';
import { logChannelDetails } from './view';
const CHANNEL_LIMIT = 10_000;
const CHANNEL_LIMIT = 50;
async function getAllUpdateChannelForAppAsync({
appId,
}: {
appId: string;
}): Promise<GetAllChannelsForAppQuery> {
channelLimit = CHANNEL_LIMIT,
channelOffset = 0,
branchLimit = BRANCHES_LIMIT,
branchOffset = 0,
}: GetAllChannelsForAppQueryVariables): Promise<GetAllChannelsForAppQuery> {
return await withErrorHandlingAsync(
graphqlClient
.query<GetAllChannelsForAppQuery, GetAllChannelsForAppQueryVariables>(
gql`
query GetAllChannelsForApp($appId: String!, $offset: Int!, $limit: Int!) {
query GetAllChannelsForApp(
$appId: String!
$channelOffset: Int!
$channelLimit: Int!
$branchLimit: Int!
$branchOffset: Int!
) {
app {
byId(appId: $appId) {
id
updateChannels(offset: $offset, limit: $limit) {
updateChannels(offset: $channelOffset, limit: $channelLimit) {
id
name
branchMapping
updateBranches(offset: 0, limit: $limit) {
updateBranches(offset: $branchOffset, limit: $branchLimit) {
id
name
updates(offset: 0, limit: 10) {
updates(offset: 0, limit: 1) {
id
group
message
@@ -59,7 +70,7 @@ async function getAllUpdateChannelForAppAsync({
}
}
`,
{ appId, offset: 0, limit: CHANNEL_LIMIT },
{ appId, channelOffset, channelLimit, branchLimit, branchOffset },
{ additionalTypenames: ['UpdateChannel', 'UpdateBranch', 'Update'] }
)
.toPromise()
@@ -88,18 +99,77 @@ export default class ChannelList extends EasCommand {
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp);
const getAllUpdateChannelForAppResult = await getAllUpdateChannelForAppAsync({
appId: projectId,
});
const channels = getAllUpdateChannelForAppResult.app?.byId.updateChannels;
if (!channels) {
throw new Error(`Could not find channels on project with id ${projectId}`);
}
await queryForChannelsAsync(projectId, jsonFlag);
}
}
type UpdateChannelObject = GetAllChannelsForAppQuery['app']['byId']['updateChannels'][0];
async function queryForChannelsAsync(projectId: string, jsonFlag: boolean): Promise<void> {
const queryToPerformAsync = async (
pageSize: number,
offset: number
): Promise<PaginatedQueryResponse<UpdateChannelObject>> => {
let queriedAllBranches = false;
let branchOffset = 0;
const branchLimit = 10;
const paginatedQuery: PaginatedQueryResponse<UpdateChannelObject> = {
queryResponse: [],
queryResponseRawLength: 0,
};
const branchesForChannels: Record<string, UpdateChannelObject['updateBranches']> = {};
// we need to paginate our paginated query. Each channel (N) can have M branches
// and we need to fetch all branches to populate our 'branch mapping' portion
// of the UI properly.
do {
const getAllUpdateChannelForAppResult = await getAllUpdateChannelForAppAsync({
appId: projectId,
channelLimit: pageSize,
channelOffset: offset,
branchLimit,
branchOffset,
});
const channels = getAllUpdateChannelForAppResult.app?.byId.updateChannels;
if (!channels) {
throw new Error(`Could not find channels on project with id ${projectId}`);
}
queriedAllBranches = true;
// save each array of branches using a record so we can lookup in O(1) later on
channels.forEach(channel => {
if (channel.updateBranches.length === branchLimit) {
queriedAllBranches = false;
}
branchesForChannels[channel.id] = [
...(branchesForChannels[channel.id] ? branchesForChannels[channel.id] : []),
...channel.updateBranches,
];
});
if (!paginatedQuery.queryResponse.length) {
paginatedQuery.queryResponse = channels;
} else {
paginatedQuery.queryResponse.forEach(channel => {
channel.updateBranches = branchesForChannels[channel.id];
});
}
paginatedQuery.queryResponseRawLength = channels.length;
branchOffset += branchLimit + 1;
} while (!queriedAllBranches);
return paginatedQuery;
};
const renderListItems = (currentPage: UpdateChannelObject[]): void => {
if (jsonFlag) {
printJsonOnlyOutput(channels);
printJsonOnlyOutput(currentPage);
} else {
for (const channel of channels) {
Log.addNewLineIfNone();
Log.log(chalk.bold('Channels:'));
Log.addNewLineIfNone();
for (const channel of currentPage) {
Log.addNewLineIfNone();
Log.log(
formatFields([
@@ -110,5 +180,16 @@ export default class ChannelList extends EasCommand {
logChannelDetails(channel);
}
}
}
};
await performPaginatedQueryAsync({
pageSize: 25, // lower, because we need to query all branches per channel O(N*M)
offset: 0,
queryToPerform: queryToPerformAsync,
promptOptions: {
type: 'confirm',
title: 'Fetch next page of channels?',
renderListItems,
},
});
}
@@ -1,21 +1,21 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand.js';
import { GetChannelByNameForAppQuery, UpdateBranch } from '../../graphql/generated.js';
import { BranchQuery } from '../../graphql/queries/BranchQuery.js';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import EasCommand from '../../commandUtils/EasCommand';
import { GetChannelByNameForAppQuery, UpdateBranch } from '../../graphql/generated';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { promptAsync, selectAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import { updateChannelBranchMappingAsync } from './edit.js';
import { BranchMapping, getBranchMapping } from './view.js';
} from '../../project/projectUtils';
import { promptAsync, selectAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { updateChannelBranchMappingAsync } from './edit';
import { BranchMapping, getBranchMapping } from './view';
async function promptForRolloutPercentAsync({
promptMessage,
@@ -351,6 +351,8 @@ export default class ChannelRollout extends EasCommand {
const channel = await ChannelQuery.getUpdateChannelByNameForAppAsync({
appId: projectId,
channelName: channelName!,
offset: 0,
limit: 50,
});
if (!channel) {
throw new Error(
+12 -10
View File
@@ -3,20 +3,20 @@ import assert from 'assert';
import chalk from 'chalk';
import Table from 'cli-table3';
import EasCommand from '../../commandUtils/EasCommand.js';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import EasCommand from '../../commandUtils/EasCommand';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import {
FormatUpdateParameter,
UPDATE_COLUMNS,
formatUpdate,
getPlatformsForGroup,
} from '../../update/utils.js';
import formatFields from '../../utils/formatFields.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
} from '../../update/utils';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export type BranchMapping = {
version: number;
@@ -111,7 +111,7 @@ export function logChannelDetails(channel: {
}
const { branchId } = branchMapping.data[index];
const branch = channel.updateBranches.filter(branch => branch.id === branchId)[0];
const branch = channel.updateBranches.find(branch => branch.id === branchId);
if (!branch) {
throw new Error('Branch mapping is pointing at a missing branch.');
}
@@ -184,6 +184,8 @@ export default class ChannelView extends EasCommand {
const channel = await ChannelQuery.getUpdateChannelByNameForAppAsync({
appId: projectId,
channelName,
offset: 0,
limit: 50,
});
if (!channel) {
throw new Error(`Could not find a channel with name: ${channelName}`);
+7 -7
View File
@@ -4,13 +4,13 @@ import { EasJsonReader } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand from '../commandUtils/EasCommand.js';
import { toAppPlatform } from '../graphql/types/AppPlatform.js';
import Log from '../log.js';
import { appPlatformEmojis } from '../platform.js';
import { getExpoConfig } from '../project/expoConfig.js';
import { findProjectRootAsync } from '../project/projectUtils.js';
import { selectAsync } from '../prompts.js';
import EasCommand from '../commandUtils/EasCommand';
import { toAppPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
import { appPlatformEmojis } from '../platform';
import { getExpoConfig } from '../project/expoConfig';
import { findProjectRootAsync } from '../project/projectUtils';
import { selectAsync } from '../prompts';
export default class Config extends EasCommand {
static description = 'display project configuration (app.json + eas.json)';
+2 -2
View File
@@ -1,5 +1,5 @@
import EasCommand from '../commandUtils/EasCommand.js';
import { SelectPlatform } from '../credentials/manager/SelectPlatform.js';
import EasCommand from '../commandUtils/EasCommand';
import { SelectPlatform } from '../credentials/manager/SelectPlatform';
export default class Credentials extends EasCommand {
static description = 'manage credentials';
@@ -1,8 +1,8 @@
import EasCommand from '../../commandUtils/EasCommand.js';
import AppStoreApi from '../../credentials/ios/appstore/AppStoreApi.js';
import { createContextAsync } from '../../devices/context.js';
import DeviceManager from '../../devices/manager.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
import EasCommand from '../../commandUtils/EasCommand';
import AppStoreApi from '../../credentials/ios/appstore/AppStoreApi';
import { createContextAsync } from '../../devices/context';
import DeviceManager from '../../devices/manager';
import { ensureLoggedInAsync } from '../../user/actions';
export default class DeviceCreate extends EasCommand {
static description = 'register new Apple Devices to use for internal distribution';
+14 -19
View File
@@ -1,29 +1,24 @@
import AppleUtils from '@expo/apple-utils';
import { Device, DeviceStatus } from '@expo/apple-utils';
import { Flags } from '@oclif/core';
import assert from 'assert';
import EasCommand from '../../commandUtils/EasCommand.js';
import { chooseDevicesToDeleteAsync } from '../../credentials/ios/actions/DeviceUtils.js';
import { AppleDeviceMutation } from '../../credentials/ios/api/graphql/mutations/AppleDeviceMutation.js';
import EasCommand from '../../commandUtils/EasCommand';
import { chooseDevicesToDeleteAsync } from '../../credentials/ios/actions/DeviceUtils';
import { AppleDeviceMutation } from '../../credentials/ios/api/graphql/mutations/AppleDeviceMutation';
import {
AppleDeviceQuery,
AppleDeviceQueryResult,
AppleDevicesByTeamIdentifierQueryResult,
} from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery.js';
import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleTeamQuery.js';
import {
authenticateAsync,
getRequestContext,
} from '../../credentials/ios/appstore/authenticate.js';
import formatDevice from '../../devices/utils/formatDevice.js';
import { AppleDevice, Maybe } from '../../graphql/generated.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils.js';
import { promptAsync, toggleConfirmAsync } from '../../prompts.js';
const { Device, DeviceStatus } = AppleUtils;
} from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleTeamQuery';
import { authenticateAsync, getRequestContext } from '../../credentials/ios/appstore/authenticate';
import formatDevice from '../../devices/utils/formatDevice';
import { AppleDevice, Maybe } from '../../graphql/generated';
import Log from '../../log';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
export default class DeviceDelete extends EasCommand {
static description = 'remove a registered device from your account';
+9 -9
View File
@@ -2,15 +2,15 @@ import { Flags } from '@oclif/core';
import assert from 'assert';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand.js';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery.js';
import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleTeamQuery.js';
import formatDevice from '../../devices/utils/formatDevice.js';
import Log from '../../log.js';
import { Ora, ora } from '../../ora.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import EasCommand from '../../commandUtils/EasCommand';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleTeamQuery';
import formatDevice from '../../devices/utils/formatDevice';
import Log from '../../log';
import { Ora, ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
export default class BuildList extends EasCommand {
static description = 'list all registered devices for your account';
+7 -7
View File
@@ -1,10 +1,10 @@
import EasCommand from '../../commandUtils/EasCommand.js';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery.js';
import formatDevice from '../../devices/utils/formatDevice.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils.js';
import EasCommand from '../../commandUtils/EasCommand';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
import formatDevice from '../../devices/utils/formatDevice';
import Log from '../../log';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils';
export default class DeviceView extends EasCommand {
static description = 'view a device for your project';
+5 -5
View File
@@ -1,11 +1,11 @@
import { Platform } from '@expo/eas-build-job';
import envinfo from 'envinfo';
import EasCommand from '../commandUtils/EasCommand.js';
import Log from '../log.js';
import { findProjectRootAsync } from '../project/projectUtils.js';
import { resolveWorkflowAsync } from '../project/workflow.js';
import { easCliVersion } from '../utils/easCli.js';
import EasCommand from '../commandUtils/EasCommand';
import Log from '../log';
import { findProjectRootAsync } from '../project/projectUtils';
import { resolveWorkflowAsync } from '../project/workflow';
import { easCliVersion } from '../utils/easCli';
export default class Diagnostics extends EasCommand {
static description = 'display environment info';
@@ -3,15 +3,15 @@ import { Flags } from '@oclif/core';
import chalk from 'chalk';
import path from 'path';
import { ensureProjectConfiguredAsync } from '../../build/configure.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import { CredentialsContext } from '../../credentials/context.js';
import Log, { learnMore } from '../../log.js';
import { createMetadataContextAsync } from '../../metadata/context.js';
import { downloadMetadataAsync } from '../../metadata/download.js';
import { handleMetadataError } from '../../metadata/errors.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
import { ensureProjectConfiguredAsync } from '../../build/configure';
import EasCommand from '../../commandUtils/EasCommand';
import { CredentialsContext } from '../../credentials/context';
import Log, { learnMore } from '../../log';
import { createMetadataContextAsync } from '../../metadata/context';
import { downloadMetadataAsync } from '../../metadata/download';
import { handleMetadataError } from '../../metadata/errors';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
export default class MetadataPull extends EasCommand {
static description = 'generate the local store configuration from the app stores';
@@ -1,15 +1,15 @@
import { getConfig } from '@expo/config';
import { Flags } from '@oclif/core';
import { ensureProjectConfiguredAsync } from '../../build/configure.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import { CredentialsContext } from '../../credentials/context.js';
import Log, { learnMore } from '../../log.js';
import { createMetadataContextAsync } from '../../metadata/context.js';
import { handleMetadataError } from '../../metadata/errors.js';
import { uploadMetadataAsync } from '../../metadata/upload.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
import { ensureProjectConfiguredAsync } from '../../build/configure';
import EasCommand from '../../commandUtils/EasCommand';
import { CredentialsContext } from '../../credentials/context';
import Log, { learnMore } from '../../log';
import { createMetadataContextAsync } from '../../metadata/context';
import { handleMetadataError } from '../../metadata/errors';
import { uploadMetadataAsync } from '../../metadata/upload';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
export default class MetadataPush extends EasCommand {
static description = 'sync the local store configuration to the app stores';
@@ -1,12 +1,12 @@
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import { AppInfoQuery, AppInfoQueryVariables } from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import formatFields from '../../utils/formatFields.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import { AppInfoQuery, AppInfoQueryVariables } from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import formatFields from '../../utils/formatFields';
async function projectInfoByIdAsync(appId: string): Promise<AppInfoQuery> {
const data = await withErrorHandlingAsync(
@@ -1,9 +1,9 @@
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, setProjectIdAsync } from '../../project/projectUtils.js';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, setProjectIdAsync } from '../../project/projectUtils';
export default class ProjectInit extends EasCommand {
static description = 'create or link an EAS project';
+10 -10
View File
@@ -1,23 +1,23 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand.js';
import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation.js';
import EasCommand from '../../commandUtils/EasCommand';
import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation';
import {
EnvironmentSecretScope,
EnvironmentSecretsQuery,
} from '../../graphql/queries/EnvironmentSecretsQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
} from '../../graphql/queries/EnvironmentSecretsQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectAccountNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import { findAccountByName } from '../../user/Account.js';
import { getActorDisplayName } from '../../user/User.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { findAccountByName } from '../../user/Account';
import { getActorDisplayName } from '../../user/User';
import { ensureLoggedInAsync } from '../../user/actions';
export default class EnvironmentSecretCreate extends EasCommand {
static description = 'create an environment secret on the current project or owner account';
@@ -1,20 +1,20 @@
import { Flags } from '@oclif/core';
import EasCommand from '../../commandUtils/EasCommand.js';
import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation.js';
import EasCommand from '../../commandUtils/EasCommand';
import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation';
import {
EnvironmentSecretScope,
EnvironmentSecretWithScope,
EnvironmentSecretsQuery,
} from '../../graphql/queries/EnvironmentSecretsQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
} from '../../graphql/queries/EnvironmentSecretsQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectAccountNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
import { promptAsync, toggleConfirmAsync } from '../../prompts.js';
} from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
export default class EnvironmentSecretDelete extends EasCommand {
static description = 'delete an environment secret by ID';
+5 -5
View File
@@ -2,15 +2,15 @@ import chalk from 'chalk';
import Table from 'cli-table3';
import dateFormat from 'dateformat';
import EasCommand from '../../commandUtils/EasCommand.js';
import { EnvironmentSecretsQuery } from '../../graphql/queries/EnvironmentSecretsQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import EasCommand from '../../commandUtils/EasCommand';
import { EnvironmentSecretsQuery } from '../../graphql/queries/EnvironmentSecretsQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectAccountNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils.js';
} from '../../project/projectUtils';
export default class EnvironmentSecretList extends EasCommand {
static description = 'list environment secrets available for your current app';
+11 -11
View File
@@ -2,23 +2,23 @@ import { EasJsonReader } from '@expo/eas-json';
import { Errors, Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand from '../commandUtils/EasCommand.js';
import { SubmissionFragment } from '../graphql/generated.js';
import { toAppPlatform } from '../graphql/types/AppPlatform.js';
import Log from '../log.js';
import EasCommand from '../commandUtils/EasCommand';
import { SubmissionFragment } from '../graphql/generated';
import { toAppPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
import {
RequestedPlatform,
appPlatformDisplayNames,
appPlatformEmojis,
selectRequestedPlatformAsync,
toPlatforms,
} from '../platform.js';
import { getExpoConfig } from '../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../project/projectUtils.js';
import { SubmitArchiveFlags, createSubmissionContextAsync } from '../submit/context.js';
import { submitAsync, waitToCompleteAsync } from '../submit/submit.js';
import { printSubmissionDetailsUrls } from '../submit/utils/urls.js';
import { getProfilesAsync } from '../utils/profiles.js';
} from '../platform';
import { getExpoConfig } from '../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../project/projectUtils';
import { SubmitArchiveFlags, createSubmissionContextAsync } from '../submit/context';
import { submitAsync, waitToCompleteAsync } from '../submit/submit';
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
import { getProfilesAsync } from '../utils/profiles';
interface RawCommandFlags {
platform?: string;
@@ -4,21 +4,21 @@ import { Flags } from '@oclif/core';
import assert from 'assert';
import chalk from 'chalk';
import { getEASUpdateURL } from '../../api.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import { AppPlatform } from '../../graphql/generated.js';
import Log, { learnMore } from '../../log.js';
import { RequestedPlatform, appPlatformDisplayNames } from '../../platform.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { getEASUpdateURL } from '../../api';
import EasCommand from '../../commandUtils/EasCommand';
import { AppPlatform } from '../../graphql/generated';
import Log, { learnMore } from '../../log';
import { RequestedPlatform, appPlatformDisplayNames } from '../../platform';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectIdAsync,
installExpoUpdatesAsync,
isExpoUpdatesInstalledOrAvailable,
} from '../../project/projectUtils.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from '../../update/android/UpdatesModule.js';
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule.js';
} from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
const DEFAULT_MANAGED_RUNTIME_VERSION = { policy: 'sdkVersion' } as const;
const DEFAULT_BARE_RUNTIME_VERSION = '1.0.0' as const;
@@ -1,16 +1,16 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
DeleteUpdateGroupMutation,
UpdateMutationDeleteUpdateGroupArgs,
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { confirmAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
} from '../../graphql/generated';
import Log from '../../log';
import { confirmAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
async function deleteUpdateGroupAsync({
group,
+140 -141
View File
@@ -1,67 +1,66 @@
import { ExpoConfig } from '@expo/config';
import ConfigPlugins from '@expo/config-plugins';
import { Updates } from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import { Errors, Flags } from '@oclif/core';
import assert from 'assert';
import chalk from 'chalk';
import dateFormat from 'dateformat';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import nullthrows from 'nullthrows';
import { getEASUpdateURL } from '../../api.js';
import { getUpdateGroupUrl } from '../../build/utils/url.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import fetch from '../../fetch.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import { getEASUpdateURL } from '../../api';
import { getUpdateGroupUrl } from '../../build/utils/url';
import EasCommand from '../../commandUtils/EasCommand';
import fetch from '../../fetch';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
GetUpdateGroupAsyncQuery,
PublishUpdateGroupInput,
Robot,
RootQueryUpdatesByGroupArgs,
Update,
UpdateBranchFragment,
UpdateInfoGroup,
UpdatePublishMutation,
User,
ViewBranchUpdatesQuery,
} from '../../graphql/generated.js';
import { PublishMutation } from '../../graphql/mutations/PublishMutation.js';
import { UpdateQuery } from '../../graphql/queries/UpdateQuery.js';
import Log, { link } from '../../log.js';
import { ora } from '../../ora.js';
import { getExpoConfig } from '../../project/expoConfig.js';
} from '../../graphql/generated';
import { PublishMutation } from '../../graphql/mutations/PublishMutation';
import { UpdateQuery } from '../../graphql/queries/UpdateQuery';
import Log, { link } from '../../log';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectAccountName,
getProjectIdAsync,
installExpoUpdatesAsync,
isExpoUpdatesInstalledOrAvailable,
} from '../../project/projectUtils.js';
} from '../../project/projectUtils';
import {
PublishPlatform,
buildBundlesAsync,
buildUnsortedUpdateInfoGroupAsync,
collectAssetsAsync,
uploadAssetsAsync,
} from '../../project/publish.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { confirmAsync, promptAsync, selectAsync } from '../../prompts.js';
import { formatUpdate } from '../../update/utils.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
} from '../../project/publish';
import { resolveWorkflowAsync } from '../../project/workflow';
import { confirmAsync, promptAsync } from '../../prompts';
import { formatUpdate } from '../../update/utils';
import { ensureLoggedInAsync } from '../../user/actions';
import {
checkManifestBodyAgainstUpdateInfoGroup,
getCodeSigningInfoAsync,
getManifestBodyAsync,
signManifestBody,
} from '../../utils/code-signing.js';
import uniqBy from '../../utils/expodash/uniqBy.js';
import formatFields from '../../utils/formatFields.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import { nullthrows } from '../../utils/nullthrows.js';
import { getVcsClient } from '../../vcs/index.js';
import { createUpdateBranchOnAppAsync } from '../branch/create.js';
import { listBranchesAsync } from '../branch/list.js';
import { createUpdateChannelOnAppAsync } from '../channel/create.js';
const { Updates } = ConfigPlugins;
} from '../../utils/code-signing';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { PaginatedQueryResponse, performPaginatedQueryAsync } from '../../utils/queries';
import { getVcsClient } from '../../vcs';
import { createUpdateBranchOnAppAsync } from '../branch/create';
import { listBranchesAsync } from '../branch/list';
import { createUpdateChannelOnAppAsync } from '../channel/create';
export const defaultPublishPlatforms: PublishPlatform[] = ['android', 'ios'];
export type PublishPlatformFlag = PublishPlatform | 'all';
@@ -130,8 +129,8 @@ export async function ensureBranchExistsAsync({
}: {
appId: string;
name: string;
limit?: number;
offset?: number;
limit: number;
offset: number;
}): Promise<{
id: string;
updates: Exclude<
@@ -284,40 +283,16 @@ export default class UpdatePublish extends EasCommand {
}
if (!branchName) {
const validationMessage = 'Branch name may not be empty.';
if (nonInteractive) {
throw new Error('Must supply --branch or use --auto when in non-interactive mode');
}
const validationMessage = 'Branch name may not be empty.';
if (jsonFlag) {
throw new Error(validationMessage);
}
const branches = await listBranchesAsync({ projectId });
if (branches.length === 0) {
({ name: branchName } = await promptAsync({
type: 'text',
name: 'name',
message: 'No branches found. Creating a new one. Please name the new branch:',
initial:
(await getVcsClient().getBranchNameAsync()) ||
`branch-${Math.random().toString(36).substr(2, 4)}`,
validate: value => (value ? true : validationMessage),
}));
} else {
branchName = await selectAsync<string>(
'Which branch would you like to publish on?',
branches.map(branch => {
return {
title: `${branch.name} ${chalk.grey(
`- current update: ${formatUpdate(branch.updates[0])}`
)}`,
value: branch.name,
};
})
);
}
assert(branchName, 'Branch name must be specified.');
branchName = await getBranchNameToPublishOnAsync(projectId, validationMessage);
}
let unsortedUpdateInfoGroups: UpdateInfoGroup = {};
@@ -343,12 +318,7 @@ export default class UpdatePublish extends EasCommand {
throw new Error('You must specify the update group to republish.');
}
updatesToRepublish = await getUpdatesToRepublishInteractiveAsync(
projectId,
branchName,
platformFlag,
50
);
updatesToRepublish = await getUpdatesToRepublishAsync(projectId, branchName, platformFlag);
}
const updatesToRepublishFilteredByPlatform = updatesToRepublish.filter(
// Only republish to the specified platforms
@@ -413,7 +383,6 @@ export default class UpdatePublish extends EasCommand {
if (nonInteractive) {
throw new Error('Must supply --message or use --auto when in non-interactive mode');
}
const validationMessage = 'publish message may not be empty.';
if (jsonFlag) {
throw new Error(validationMessage);
@@ -472,6 +441,7 @@ export default class UpdatePublish extends EasCommand {
appId: projectId,
name: branchName,
limit: 0,
offset: 0,
});
// Sort the updates into different groups based on their platform specific runtime versions
@@ -601,71 +571,52 @@ export default class UpdatePublish extends EasCommand {
}
}
export async function getUpdatesToRepublishInteractiveAsync(
export async function getUpdatesToRepublishAsync(
projectId: string,
branchName: string,
platformFlag: string,
pageSize: number,
offset: number = 0,
cumulativeUpdates: Exclude<
Exclude<ViewBranchUpdatesQuery['app'], null | undefined>['byId']['updateBranchByName'],
null | undefined
>['updates'] = []
): Promise<
Exclude<
Exclude<ViewBranchUpdatesQuery['app'], null | undefined>['byId']['updateBranchByName'],
null | undefined
>['updates']
> {
const fetchMoreValue = '_fetchMore';
platformFlag: string
): Promise<AppUpdateObject[]> {
const queryUpdatesForBranchAsync = async (
pageSize: number,
offset: number
): Promise<PaginatedQueryResponse<AppUpdateObject>> => {
const { updates } = await ensureBranchExistsAsync({
appId: projectId,
name: branchName!,
limit: pageSize,
offset,
});
if (!updates.length) {
throw new Error(
`There are no updates on branch "${branchName}" published for the platform(s) ${platformFlag}. Did you mean to publish a new update instead?`
);
}
const relevantUpdates =
platformFlag === 'all'
? updates
: updates.filter(update => {
// Only show groups that have updates on the specified platform(s).
return update.platform === platformFlag;
});
return {
queryResponse: relevantUpdates,
queryResponseRawLength: updates.length,
};
};
const { updates } = await ensureBranchExistsAsync({
appId: projectId,
name: branchName,
limit: pageSize + 1, // fetch an extra item so we know if there are additional pages
offset,
const getUpdateIdentifier = (update: AppUpdateObject): string => update.group;
return await performPaginatedQueryAsync({
pageSize: 50,
offset: 0,
queryToPerform: queryUpdatesForBranchAsync,
promptOptions: {
type: 'select',
title: 'Which update would you like to republish?',
createDisplayTextForSelectionPromptListItem: formatUpdateTitle,
getIdentifierForQueryItem: getUpdateIdentifier,
},
});
cumulativeUpdates = [
...cumulativeUpdates,
// drop that extra item used for pagination from our render logic
...updates.slice(0, updates.length - 1),
];
const cumulativeUpdatesForTargetPlatforms =
platformFlag === 'all'
? cumulativeUpdates
: cumulativeUpdates.filter(update => {
// Only show groups that have updates on the specified platform(s).
return update.platform === platformFlag;
});
const updateGroups = uniqBy(cumulativeUpdatesForTargetPlatforms, u => u.group).map(update => ({
title: formatUpdateTitle(update),
value: update.group,
}));
if (!updateGroups.length) {
throw new Error(
`There are no updates on branch "${branchName}" published for the platform(s) ${platformFlag}. Did you mean to publish a new update instead?`
);
}
if (updates.length > pageSize) {
updateGroups.push({ title: 'Next page...', value: fetchMoreValue });
}
const selectedUpdateGroup = await selectAsync<string>(
'Which update would you like to republish?',
updateGroups
);
if (selectedUpdateGroup === fetchMoreValue) {
return await getUpdatesToRepublishInteractiveAsync(
projectId,
branchName,
platformFlag,
pageSize,
offset + pageSize,
cumulativeUpdates
);
}
return cumulativeUpdates.filter(update => update.group === selectedUpdateGroup);
}
async function getRuntimeVersionObjectAsync(
@@ -698,12 +649,24 @@ async function getRuntimeVersionObjectAsync(
);
}
function formatUpdateTitle(
update: Exclude<
Exclude<ViewBranchUpdatesQuery['app'], null | undefined>['byId']['updateBranchByName'],
null | undefined
>['updates'][number]
): string {
async function checkEASUpdateURLIsSetAsync(exp: ExpoConfig): Promise<void> {
const configuredURL = exp.updates?.url;
const projectId = await getProjectIdAsync(exp);
const expectedURL = getEASUpdateURL(projectId);
if (configuredURL !== expectedURL) {
throw new Error(
`The update URL is incorrectly configured for EAS Update. Please set updates.url to ${expectedURL} in your app.json.`
);
}
}
export type AppUpdateObject = Exclude<
Exclude<ViewBranchUpdatesQuery['app'], null | undefined>['byId']['updateBranchByName'],
null | undefined
>['updates'][0];
export function formatUpdateTitle(update: AppUpdateObject): string {
const { message, createdAt, actor, runtimeVersion } = update;
let actorName: string;
@@ -726,16 +689,52 @@ function formatUpdateTitle(
)} by ${actorName}, runtimeVersion: ${runtimeVersion}] ${message}`;
}
async function checkEASUpdateURLIsSetAsync(exp: ExpoConfig): Promise<void> {
const configuredURL = exp.updates?.url;
const projectId = await getProjectIdAsync(exp);
const expectedURL = getEASUpdateURL(projectId);
async function getBranchNameToPublishOnAsync(
projectId: string,
validationMessage: string
): Promise<string> {
const queryForBanchesToPublishOnAsync = async (
pageSize: number,
offset: number
): Promise<PaginatedQueryResponse<UpdateBranchFragment>> => {
const branches = await listBranchesAsync({ appId: projectId, limit: pageSize, offset });
if (branches.length === 0) {
const { name: newBranchName } = await promptAsync({
type: 'text',
name: 'name',
message: 'No branches found. Creating a new one. Please name the new branch:',
initial:
(await getVcsClient().getBranchNameAsync()) ||
`branch-${Math.random().toString(36).substring(2, 7)}`,
validate: value => (value ? true : validationMessage),
});
branches.push({
id: '_defaultId',
name: newBranchName,
updates: [],
});
}
return { queryResponse: branches, queryResponseRawLength: branches.length };
};
const formatBranchTitle = (branch: UpdateBranchFragment): string =>
`${branch.name} ${chalk.grey(`- current update: ${formatUpdate(branch.updates[0])}`)}`;
const getBranchIdentifier = (branch: UpdateBranchFragment): string => branch.id;
if (configuredURL !== expectedURL) {
throw new Error(
`The update URL is incorrectly configured for EAS Update. Please set updates.url to ${expectedURL} in your app.json.`
);
}
const branches = await performPaginatedQueryAsync({
pageSize: 50,
offset: 0,
queryToPerform: queryForBanchesToPublishOnAsync,
promptOptions: {
type: 'select',
createDisplayTextForSelectionPromptListItem: formatBranchTitle,
getIdentifierForQueryItem: getBranchIdentifier,
title: 'Which branch would you like to publish on?',
},
});
const branchName = branches?.pop()?.name;
assert(branchName, validationMessage);
return branchName;
}
export const truncatePublishUpdateMessage = (originalMessage: string): string => {
+115 -44
View File
@@ -3,17 +3,18 @@ import assert from 'assert';
import chalk from 'chalk';
import CliTable from 'cli-table3';
import EasCommand from '../../commandUtils/EasCommand.js';
import { ViewAllUpdatesQuery } from '../../graphql/generated.js';
import { UpdateQuery } from '../../graphql/queries/UpdateQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import { FormatUpdateParameter, UPDATE_COLUMNS, formatUpdate } from '../../update/utils.js';
import groupBy from '../../utils/expodash/groupBy.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import { getVcsClient } from '../../vcs/index.js';
import EasCommand from '../../commandUtils/EasCommand';
import { ViewAllUpdatesQuery, ViewBranchUpdatesQuery } from '../../graphql/generated';
import { UpdateQuery } from '../../graphql/queries/UpdateQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { FormatUpdateParameter, UPDATE_COLUMNS, formatUpdate } from '../../update/utils';
import groupBy from '../../utils/expodash/groupBy';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { PaginatedQueryResponse, performPaginatedQueryAsync } from '../../utils/queries';
import { getVcsClient } from '../../vcs';
type UpdateGroupDescription = FormatUpdateParameter & {
branch: string;
@@ -53,12 +54,8 @@ export default class BranchView extends EasCommand {
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp);
let updateGroupDescriptions: UpdateGroupDescription[];
if (all) {
const branchesAndUpdates = await UpdateQuery.viewAllAsync({ appId: projectId });
updateGroupDescriptions = getUpdateGroupDescriptions(
branchesAndUpdates.app.byId.updateBranches
);
await fetchAllUpdatesForAppAsync(projectId, jsonFlag);
} else {
let branchInteractive: string | undefined;
if (!branchFlag) {
@@ -77,46 +74,120 @@ export default class BranchView extends EasCommand {
const branch = branchFlag ?? branchInteractive;
assert(branch, 'Branch name may not be empty.');
const branchesAndUpdates = await UpdateQuery.viewBranchAsync({
appId: projectId,
name: branch,
});
const UpdateBranch = branchesAndUpdates.app?.byId.updateBranchByName;
if (!UpdateBranch) {
throw new Error(`Could not find branch "${branch}"`);
}
updateGroupDescriptions = getUpdateGroupDescriptions([UpdateBranch]);
fetchAllUpdatesForBranchAsync(projectId, branch, jsonFlag);
}
}
}
type BranchUpdateObject = Exclude<
ViewBranchUpdatesQuery['app']['byId']['updateBranchByName'],
null | undefined
>['updates'][0];
type AppUpdateObject = ViewAllUpdatesQuery['app']['byId']['updates'][0];
async function fetchAllUpdatesForBranchAsync(
projectId: string,
branchName: string,
jsonFlag: boolean
): Promise<void> {
const queryToPerformAsync = async (
pageSize: number,
offset: number
): Promise<PaginatedQueryResponse<BranchUpdateObject>> => {
const branchesAndUpdates = await UpdateQuery.viewBranchAsync({
appId: projectId,
name: branchName,
limit: pageSize,
offset,
});
const UpdateBranch = branchesAndUpdates.app?.byId.updateBranchByName;
if (!UpdateBranch) {
throw new Error(`Could not find branch "${branchName}"`);
}
return {
queryResponse: UpdateBranch.updates,
queryResponseRawLength: UpdateBranch.updates.length,
};
};
const renderListItems = (currentPage: BranchUpdateObject[]): void => {
const updateGroupDescriptions = getUpdateGroupDescriptions(currentPage);
if (jsonFlag) {
printJsonOnlyOutput(updateGroupDescriptions);
} else {
logAsTable(updateGroupDescriptions);
}
}
};
await performPaginatedQueryAsync({
pageSize: 50,
offset: 0,
queryToPerform: queryToPerformAsync,
promptOptions: {
type: 'confirm',
title: 'View next page of updates?',
renderListItems,
},
});
}
async function fetchAllUpdatesForAppAsync(projectId: string, jsonFlag: boolean): Promise<void> {
const queryToPerformAsync = async (
pageSize: number,
offset: number
): Promise<PaginatedQueryResponse<AppUpdateObject>> => {
const viewAllUpdates = await UpdateQuery.viewAllAsync({
appId: projectId,
limit: pageSize,
offset,
});
const { updates } = viewAllUpdates.app.byId;
return {
queryResponse: updates,
queryResponseRawLength: updates.length,
};
};
const renderListItems = (currentPage: AppUpdateObject[]): void => {
const updateGroupDescriptions = getUpdateGroupDescriptions(currentPage);
if (jsonFlag) {
printJsonOnlyOutput(updateGroupDescriptions);
} else {
logAsTable(updateGroupDescriptions);
}
};
await performPaginatedQueryAsync({
pageSize: 50,
offset: 0,
queryToPerform: queryToPerformAsync,
promptOptions: {
type: 'confirm',
title: 'View next page of updates?',
renderListItems,
},
});
}
function getUpdateGroupDescriptions(
branchesAndUpdates: ViewAllUpdatesQuery['app']['byId']['updateBranches']
updates: (BranchUpdateObject | AppUpdateObject)[]
): UpdateGroupDescription[] {
const flattenedBranchesAndUpdates = branchesAndUpdates.flatMap(branch =>
branch.updates.map(update => {
return { branch: branch.name, ...update };
return Object.values(groupBy(updates, update => update.group))
.map(updateGroup => {
return {
platforms: updateGroup
.map(update => update.platform)
.sort()
.join(', '),
...updateGroup[0],
branch: updateGroup[0].branch.name,
} as UpdateGroupDescription;
})
);
const updateGroupDescriptions = Object.values(
groupBy(flattenedBranchesAndUpdates, update => update.group)
).map(updateGroup => {
const platforms = updateGroup
.map(update => update.platform)
.sort()
.join(', ');
return { ...updateGroup[0], platforms };
});
updateGroupDescriptions.sort(
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
);
return updateGroupDescriptions;
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
}
function logAsTable(updateGroupDescriptions: UpdateGroupDescription[]): void {
+7 -7
View File
@@ -1,13 +1,13 @@
import { Flags } from '@oclif/core';
import Table from 'cli-table3';
import { gql } from 'graphql-tag';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import { UpdatesByGroupQuery, UpdatesByGroupQueryVariables } from '../../graphql/generated.js';
import Log from '../../log.js';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import { UpdatesByGroupQuery, UpdatesByGroupQueryVariables } from '../../graphql/generated';
import Log from '../../log';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export async function viewUpdateAsync({
groupId,
@@ -1,12 +1,12 @@
import { Flags } from '@oclif/core';
import EasCommand from '../../commandUtils/EasCommand.js';
import { WebhookType } from '../../graphql/generated.js';
import { WebhookMutation } from '../../graphql/mutations/WebhookMutation.js';
import { ora } from '../../ora.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { prepareInputParamsAsync } from '../../webhooks/input.js';
import EasCommand from '../../commandUtils/EasCommand';
import { WebhookType } from '../../graphql/generated';
import { WebhookMutation } from '../../graphql/mutations/WebhookMutation';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { prepareInputParamsAsync } from '../../webhooks/input';
export default class WebhookCreate extends EasCommand {
static description = 'create a webhook';
Loaded 100 of 498 files, more files were not shown because too many files have changed in this diff. Show more