Compare commits

...
Author SHA1 Message Date
Douglas Lowder 517773c3c6 Remove fallbackToCacheTimeout 2023-02-10 13:00:25 -08:00
Douglas Lowder d887c69fb4 Update CHANGELOG.md 2023-02-09 16:20:47 -08:00
Douglas Lowder 5a128a2dbe update CHANGELOG.md 2023-02-10 00:19:26 +00:00
Douglas Lowder e1148ff5a2 [eas-cli] Add EAS update properties that were removed from templates 2023-02-09 16:08:23 -08:00
Will Schurman 317d2fcbb6 [eas-cli] Warn when project ID but no owner specified and mismatch logged in user (#1667) 2023-02-06 10:39:49 -08:00
Will Schurman dd61b4bb70 [eas-cli] Fix suggested application identifier to match owning account name (#1670) 2023-02-06 10:32:22 -08:00
Jon Samp 80fbf7cf62 Revert "[eas-cli] use appVersion instead of sdkVersion (#1588)" (#1669)
* Revert "[eas-cli] use appVersion instead of sdkVersion (#1588)"

This reverts commit 9ef8483882.

* co main changelog

* update CHANGELOG.md
2023-02-06 11:23:39 -06:00
Expo CI 26b6a17160 update CHANGELOG.md 2023-02-05 20:27:34 +00:00
Brent Vatne 7f4e2eef95 v3.5.2 2023-02-05 12:20:17 -08:00
Brent Vatne 88b683655c [eas-cli] Update subdomain to developer-mdn (#1673) 2023-02-05 12:19:31 -08:00
Will Schurman f3aa3d98d6 [eas-cli] Update test certificates (#1671) 2023-02-03 20:20:59 -08:00
dependabot[bot] f852b9329d Bump http-cache-semantics from 4.1.0 to 4.1.1 (#1668)
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-03 15:50:33 +01:00
Expo CI a2340531b0 update CHANGELOG.md 2023-02-01 12:56:13 +00:00
29 changed files with 427 additions and 1424 deletions

No files matched your search

+18 -2
View File
@@ -8,12 +8,28 @@ This is the log of notable changes to EAS CLI and related packages.
### 🎉 New features
- Use `sdkVersion` as default runtime version policy when running `eas update:configure`. ([#1669](https://github.com/expo/eas-cli/pull/1669) by [@jonsamp](https://github.com/jonsamp))
- Warn when project ID but no owner specified and mismatch logged in user. ([#1667](https://github.com/expo/eas-cli/pull/1667) by [@wschurman](https://github.com/wschurman))
### 🐛 Bug fixes
- Fix suggested application identifier to match owning account name. ([#1670](https://github.com/expo/eas-cli/pull/1670) by [@wschurman](https://github.com/wschurman))
- Modify update:configure to add possibly missing EAS update config properties. ([#1686](https://github.com/expo/eas-cli/pull/1686) by [@douglowder](https://github.com/douglowder))
### 🧹 Chores
## [3.5.2](https://github.com/expo/eas-cli/releases/tag/v3.5.2) - 2023-02-05
### 🐛 Bug fixes
- Use the new developer-mdn.apple.com subdomain instead of developer.apple.com. ([#1673](https://github.com/expo/eas-cli/pull/1673) by [@brentvatne](https://github.com/brentvatne))
## [3.5.1](https://github.com/expo/eas-cli/releases/tag/v3.5.1) - 2023-02-01
### 🐛 Bug fixes
- Fix the issue with the `eas.json` envs being not available when resolving dynamic config. ([#1666](https://github.com/expo/eas-cli/pull/1666) by [@szdziedzic](https://github.com/szdziedzic))
### 🧹 Chores
## [3.5.0](https://github.com/expo/eas-cli/releases/tag/v3.5.0) - 2023-01-31
### 🎉 New features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "3.5.1",
"version": "3.5.2",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -1,14 +1,14 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "3.5.1",
"version": "3.5.2",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
},
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/apple-utils": "0.0.0-alpha.34",
"@expo/apple-utils": "0.0.0-alpha.37",
"@expo/code-signing-certificates": "0.0.5",
"@expo/config": "7.0.3",
"@expo/config-plugins": "5.0.4",
+2 -2
View File
@@ -437,8 +437,8 @@
"$ref": "#/definitions/apple/AppleKidsAge"
}
],
"description": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n@see https://developer.apple.com/news/?id=091202019a",
"markdownDescription": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n\n[Learn more](https://developer.apple.com/news/?id=091202019a)"
"description": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n@see https://developer-mdn.apple.com/news/?id=091202019a",
"markdownDescription": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n\n[Learn more](https://developer-mdn.apple.com/news/?id=091202019a)"
},
"seventeenPlus": {
"type": "boolean",
+1 -1
View File
@@ -62,7 +62,7 @@ This means that it will most likely produce an AAB and you will not be able to i
const gradleContext = await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile);
if (ctx.workflow === Workflow.MANAGED) {
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp, ctx.user);
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx);
}
const applicationId = await getApplicationIdAsync(ctx.projectDir, ctx.exp, gradleContext);
+1 -1
View File
@@ -27,7 +27,7 @@ export async function createIosContextAsync(
const { buildProfile } = ctx;
if (ctx.workflow === Workflow.MANAGED) {
await ensureBundleIdentifierIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp, ctx.user);
await ensureBundleIdentifierIsDefinedForManagedProjectAsync(ctx);
}
checkNodeEnvVariable(ctx);
@@ -15,7 +15,6 @@ jest.mock('fs');
jest.mock('../../../../graphql/queries/AppQuery');
jest.mock('../../contextUtils/findProjectDirAndVerifyProjectSetupAsync');
jest.mock('../../../../user/User');
jest.mock('../../../../ora', () => ({
ora: () => ({
start: () => ({ succeed: () => {}, fail: () => {} }),
@@ -107,7 +106,31 @@ describe(getProjectIdAsync, () => {
{ nonInteractive: false }
)
).rejects.toThrow(
`Project config: Project identified by "extra.eas.projectId" (notnotbrent) is not owned by owner specified in the "owner" field (wat). ${learnMore(
`Project config: Owner of project identified by "extra.eas.projectId" (notnotbrent) does not match owner specified in the "owner" field (wat). ${learnMore(
'https://expo.fyi/eas-project-id'
)}`
);
});
it('throws when the owner is not specified and is different than logged in user', async () => {
jest.mocked(getConfig).mockReturnValue({
exp: { name: 'test', slug: 'test', extra: { eas: { projectId: '1234' } } },
} as any);
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
fullName: '@totallybrent/test',
slug: 'test',
ownerAccount: { name: 'totallybrent' } as any,
});
await expect(
getProjectIdAsync(
sessionManager,
{ name: 'test', slug: 'test', extra: { eas: { projectId: '1234' } } },
{ nonInteractive: false }
)
).rejects.toThrow(
`Project config: Owner of project identified by "extra.eas.projectId" (totallybrent) does not match the logged in user (notnotbrent) and the "owner" field is not specified. To ensure all libraries work correctly, "owner": "totallybrent" should be added to the project config, which can be done automatically by re-running "eas init". ${learnMore(
'https://expo.fyi/eas-project-id'
)}`
);
@@ -10,7 +10,7 @@ import { getExpoConfig } from '../../../project/expoConfig';
import { fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync } from '../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
import { toAppPrivacy } from '../../../project/projectUtils';
import SessionManager from '../../../user/SessionManager';
import { Actor } from '../../../user/User';
import { Actor, getActorUsername } from '../../../user/User';
import { createGraphqlClient } from './createGraphqlClient';
import { findProjectRootAsync } from './findProjectDirAndVerifyProjectSetupAsync';
@@ -93,9 +93,22 @@ export async function getProjectIdAsync(
const appForProjectId = await AppQuery.byIdAsync(graphqlClient, localProjectId);
if (exp.owner && exp.owner !== appForProjectId.ownerAccount.name) {
throw new Error(
`Project config: Project identified by "extra.eas.projectId" (${
`Project config: Owner of project identified by "extra.eas.projectId" (${
appForProjectId.ownerAccount.name
}) is not owned by owner specified in the "owner" field (${exp.owner}). ${learnMore(
}) does not match owner specified in the "owner" field (${exp.owner}). ${learnMore(
'https://expo.fyi/eas-project-id'
)}`
);
}
const actorUsername = getActorUsername(actor);
if (!exp.owner && appForProjectId.ownerAccount.name !== actorUsername) {
throw new Error(
`Project config: Owner of project identified by "extra.eas.projectId" (${
appForProjectId.ownerAccount.name
}) does not match the logged in user (${actorUsername}) and the "owner" field is not specified. To ensure all libraries work correctly, "owner": "${
appForProjectId.ownerAccount.name
}" should be added to the project config, which can be done automatically by re-running "eas init". ${learnMore(
'https://expo.fyi/eas-project-id'
)}`
);
@@ -46,7 +46,7 @@ export default class BuildVersionSetView extends EasCommand {
public async runAsync(): Promise<void> {
const { flags } = await this.parse(BuildVersionSetView);
const {
loggedIn: { actor, graphqlClient },
loggedIn: { graphqlClient },
getDynamicProjectConfigAsync,
projectDir,
} = await this.getContextAsync(BuildVersionSetView, {
@@ -67,13 +67,14 @@ export default class BuildVersionSetView extends EasCommand {
validateAppConfigForRemoteVersionSource(exp, platform);
const applicationIdentifier = await getApplicationIdentifierAsync(
const applicationIdentifier = await getApplicationIdentifierAsync({
graphqlClient,
projectDir,
projectId,
exp,
profile,
buildProfile: profile,
platform,
actor
);
});
const remoteVersions = await AppVersionQuery.latestVersionAsync(
graphqlClient,
projectId,
@@ -63,7 +63,7 @@ export default class BuildVersionSyncView extends EasCommand {
public async runAsync(): Promise<void> {
const { flags } = await this.parse(BuildVersionSyncView);
const {
loggedIn: { actor, graphqlClient },
loggedIn: { graphqlClient },
getDynamicProjectConfigAsync,
projectDir,
} = await this.getContextAsync(BuildVersionSyncView, {
@@ -89,13 +89,14 @@ export default class BuildVersionSyncView extends EasCommand {
validateAppConfigForRemoteVersionSource(exp, profileInfo.platform);
const platformDisplayName = appPlatformDisplayNames[toAppPlatform(profileInfo.platform)];
const applicationIdentifier = await getApplicationIdentifierAsync(
const applicationIdentifier = await getApplicationIdentifierAsync({
graphqlClient,
projectDir,
projectId,
exp,
profileInfo.profile,
profileInfo.platform,
actor
);
buildProfile: profileInfo.profile,
platform: profileInfo.platform,
});
const remoteVersions = await AppVersionQuery.latestVersionAsync(
graphqlClient,
projectId,
@@ -344,12 +344,38 @@ describe(ProjectInit.name, () => {
describe('when it is already configured', () => {
beforeEach(() => {
mockTestProject({ configuredProjectId: '1234', configuredOwner: jester.accounts[0].name });
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
});
it('does not configure', async () => {
await new ProjectInit([], commandOptions).run();
expect(saveProjectIdToAppConfigAsync).not.toHaveBeenCalled();
});
it('checks owner and slug consistency', async () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-124',
fullName: '@jester2/testing-124',
ownerAccount: jester2.accounts[0],
});
jest.mocked(confirmAsync).mockResolvedValue(true);
jest.mocked(modifyConfigAsync).mockResolvedValue({ type: 'success', config: {} as any });
await new ProjectInit([], commandOptions).run();
expect(confirmAsync).toHaveBeenCalled();
expect(modifyConfigAsync).toHaveBeenCalledTimes(2);
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { owner: 'jester2' });
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { slug: 'testing-124' });
});
});
describe('when it is not yet configured', () => {
@@ -365,6 +391,14 @@ describe(ProjectInit.name, () => {
it('prompts for confirmation to link', async () => {
jest.mocked(confirmAsync).mockResolvedValue(true);
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
await new ProjectInit([], commandOptions).run();
expect(saveProjectIdToAppConfigAsync).toHaveBeenCalledWith('/test-project', '123456');
expect(confirmAsync).toHaveBeenCalledTimes(1);
@@ -384,6 +418,14 @@ describe(ProjectInit.name, () => {
it('asks to create it', async () => {
jest.mocked(confirmAsync).mockResolvedValue(true);
jest.mocked(AppMutation.createAppAsync).mockResolvedValue('0129');
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '0129',
slug: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
await new ProjectInit([], commandOptions).run();
expect(saveProjectIdToAppConfigAsync).toHaveBeenCalledWith('/test-project', '0129');
expect(confirmAsync).toHaveBeenCalledTimes(1);
+19 -12
View File
@@ -194,7 +194,6 @@ export default class ProjectInit extends EasCommand {
}
private static async initializeWithExplicitIDAsync(
graphqlClient: ExpoGraphqlClient,
projectId: string,
projectDir: string,
{ force, nonInteractive }: InitializeMethodOptions
@@ -203,17 +202,13 @@ export default class ProjectInit extends EasCommand {
force,
nonInteractive,
});
await this.ensureOwnerSlugConsistencyAsync(graphqlClient, projectId, projectDir, {
force,
nonInteractive,
});
}
private static async initializeWithInteractiveSelectionAsync(
graphqlClient: ExpoGraphqlClient,
actor: Actor,
projectDir: string
): Promise<void> {
): Promise<string> {
const exp = getExpoConfig(projectDir);
const existingProjectId = exp.extra?.eas?.projectId;
@@ -223,7 +218,7 @@ export default class ProjectInit extends EasCommand {
existingProjectId
)}). To re-configure, remove the "extra.eas.projectId" field from your app config.`
);
return;
return existingProjectId;
}
const allAccounts = actor.accounts;
@@ -287,7 +282,7 @@ export default class ProjectInit extends EasCommand {
}
await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, existingProjectIdOnServer);
return;
return existingProjectIdOnServer;
}
if (!accountNamesWhereUserHasSufficientPermissionsToCreateApp.has(accountName)) {
@@ -323,24 +318,36 @@ export default class ProjectInit extends EasCommand {
}
await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, createdProjectId);
return createdProjectId;
}
async runAsync(): Promise<void> {
const {
flags: { id, force, 'non-interactive': nonInteractive },
flags: { id: idArgument, force, 'non-interactive': nonInteractive },
} = await this.parse(ProjectInit);
const {
loggedIn: { actor, graphqlClient },
projectDir,
} = await this.getContextAsync(ProjectInit, { nonInteractive });
if (id) {
await ProjectInit.initializeWithExplicitIDAsync(graphqlClient, id, projectDir, {
let idForConsistency: string;
if (idArgument) {
await ProjectInit.initializeWithExplicitIDAsync(idArgument, projectDir, {
force,
nonInteractive,
});
idForConsistency = idArgument;
} else {
await ProjectInit.initializeWithInteractiveSelectionAsync(graphqlClient, actor, projectDir);
idForConsistency = await ProjectInit.initializeWithInteractiveSelectionAsync(
graphqlClient,
actor,
projectDir
);
}
await ProjectInit.ensureOwnerSlugConsistencyAsync(graphqlClient, idForConsistency, projectDir, {
force,
nonInteractive,
});
}
}
@@ -348,7 +348,7 @@ describe(syncCapabilitiesForEntitlementsAsync, () => {
'com.apple.developer.healthkit': true,
})
).rejects.toThrowError(
`https://developer.apple.com/account/resources/identifiers/bundleId/edit/XXX333`
`https://developer-mdn.apple.com/account/resources/identifiers/bundleId/edit/XXX333`
);
});
@@ -5,7 +5,7 @@ import { formatContractMessage } from '../contractMessages';
const expiredMembership = {
subject: 'Developer Program Membership Expired',
message:
"Your membership has expired, and your apps have been removed from the App Store until you renew your membership. To renew, a user with the Account Holder role must <a href='http://developer.apple.com/membercenter/index.action' target='_blank'>sign in</a> and renew the membership on the Apple Developer website. <a href='https://developer.apple.com/support/renewal/' target='_blank'>Learn More</a>.",
"Your membership has expired, and your apps have been removed from the App Store until you renew your membership. To renew, a user with the Account Holder role must <a href='http://developer-mdn.apple.com/membercenter/index.action' target='_blank'>sign in</a> and renew the membership on the Apple Developer website. <a href='https://developer-mdn.apple.com/support/renewal/' target='_blank'>Learn More</a>.",
};
const paymentReturned = {
subject: 'Payment Returned',
@@ -15,12 +15,12 @@ const paymentReturned = {
const licenseAgreementUpdated = {
subject: 'Apple Developer Program License Agreement Updated',
message:
'The updated Apple Developer Program License Agreement needs to be reviewed. In order to update your existing apps and submit new apps to the App Store, the Account Holder must review and accept the updated agreement by signing in to their <a href="https://developer.apple.com/account" target="_blank">account</a> on the Apple Developer website.',
'The updated Apple Developer Program License Agreement needs to be reviewed. In order to update your existing apps and submit new apps to the App Store, the Account Holder must review and accept the updated agreement by signing in to their <a href="https://developer-mdn.apple.com/account" target="_blank">account</a> on the Apple Developer website.',
};
const paidLicenseAgreementUpdated = {
subject: '',
message:
'<b>Review the updated Paid Applications Schedule.</b><br />In order to update your existing apps, create new in-app purchases, and submit new apps to the App Store, the user with the Legal role (Account Holder) must review and accept the Paid Applications Schedule (Schedule 2 to the Apple Developer Program License Agreement) in the Agreements, Tax, and Banking module.<br /><br /> To accept this agreement, they must have already accepted the latest version of the Apple Developer Program License Agreement in their <a href="http://developer.apple.com/membercenter/index.action">account on the developer website<a/>.<br />',
'<b>Review the updated Paid Applications Schedule.</b><br />In order to update your existing apps, create new in-app purchases, and submit new apps to the App Store, the user with the Legal role (Account Holder) must review and accept the Paid Applications Schedule (Schedule 2 to the Apple Developer Program License Agreement) in the Agreements, Tax, and Banking module.<br /><br /> To accept this agreement, they must have already accepted the latest version of the Apple Developer Program License Agreement in their <a href="http://developer-mdn.apple.com/membercenter/index.action">account on the developer website<a/>.<br />',
};
describe(formatContractMessage, () => {
@@ -111,7 +111,7 @@ export async function syncCapabilitiesForEntitlementsAsync(
inspect(modifiedRequest, { depth: null, colors: true })
);
throw new Error(
`Unexpected error occurred while attempting to update capabilities for app "${bundleId.attributes.identifier}".\nCapabilities can be modified manually in the Apple developer console at https://developer.apple.com/account/resources/identifiers/bundleId/edit/${bundleId.id}.\nAuto capability syncing can be disabled with the environment variable \`EXPO_NO_CAPABILITY_SYNC=1\`.\n${error.message}`
`Unexpected error occurred while attempting to update capabilities for app "${bundleId.attributes.identifier}".\nCapabilities can be modified manually in the Apple developer console at https://developer-mdn.apple.com/account/resources/identifiers/bundleId/edit/${bundleId.id}.\nAuto capability syncing can be disabled with the environment variable \`EXPO_NO_CAPABILITY_SYNC=1\`.\n${error.message}`
);
}
}
@@ -266,7 +266,7 @@ type CapabilityClassifier = {
// NOTE(Bacon): From manually toggling values in Xcode and checking the git diff and network requests.
// Last Updated: July 22nd, 2021
// https://developer.apple.com/documentation/bundleresources/entitlements
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements
export const CapabilityMapping: CapabilityClassifier[] = [
{
name: 'HomeKit',
@@ -413,7 +413,7 @@ export const CapabilityMapping: CapabilityClassifier[] = [
getOptions: getBooleanOptions,
},
{
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_default-data-protection
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_default-data-protection
name: 'Data Protection',
entitlement: 'com.apple.developer.default-data-protection',
capability: CapabilityType.DATA_PROTECTION,
@@ -446,7 +446,7 @@ export const CapabilityMapping: CapabilityClassifier[] = [
getOptions: getBooleanOptions,
},
{
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_networkextension
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_networkextension
name: 'Network Extensions',
entitlement: 'com.apple.developer.networking.networkextension',
capability: CapabilityType.NETWORK_EXTENSIONS,
@@ -465,7 +465,7 @@ export const CapabilityMapping: CapabilityClassifier[] = [
getOptions: getDefinedOptions,
},
{
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_nfc_readersession_formats
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_nfc_readersession_formats
name: 'NFC Tag Reading',
entitlement: 'com.apple.developer.nfc.readersession.formats',
capability: CapabilityType.NFC_TAG_READING,
@@ -482,7 +482,7 @@ export const CapabilityMapping: CapabilityClassifier[] = [
getOptions: getDefinedOptions,
},
{
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_vpn_api
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_vpn_api
name: 'Push Notifications',
// com.apple.developer.aps-environment
entitlement: 'aps-environment',
@@ -515,8 +515,8 @@ export const CapabilityMapping: CapabilityClassifier[] = [
},
{
// Not in Xcode
// https://developer.apple.com/documentation/devicecheck/preparing_to_use_the_app_attest_service
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_devicecheck_appattest-environment
// https://developer-mdn.apple.com/documentation/devicecheck/preparing_to_use_the_app_attest_service
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_devicecheck_appattest-environment
name: 'App Attest',
entitlement: 'com.apple.developer.devicecheck.appattest-environment',
capability: CapabilityType.APP_ATTEST,
@@ -725,10 +725,10 @@ export const CapabilityMapping: CapabilityClassifier[] = [
// it's not clear if it needs to be updated.
// "Contact Notes" requires the user to ask Apple in a form:
// https://developer.apple.com/contact/request/contact-note-field
// com.apple.developer.contacts.notes: https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_contacts_notes/
// https://developer-mdn.apple.com/contact/request/contact-note-field
// com.apple.developer.contacts.notes: https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_contacts_notes/
// "Exposure Notification" requires the user to ask Apple in a form:
// https://developer.apple.com/contact/request/exposure-notification-entitlement
// com.apple.developer.exposure-notification: https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_exposure-notification/
// https://developer-mdn.apple.com/contact/request/exposure-notification-entitlement
// com.apple.developer.exposure-notification: https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_exposure-notification/
];
@@ -5,7 +5,7 @@ const KEYWORD_CHARACTER_LIMIT = 100;
/**
* Keywords are limited to 100 characters when converted to a comma separated string.
* @see https://developer.apple.com/app-store/search/
* @see https://developer-mdn.apple.com/app-store/search/
*/
export const infoKeywordLength: IssueRule = {
id: 'apple.info.keyword.length',
+1 -1
View File
@@ -26,7 +26,7 @@ export interface AppleRelease {
export interface AppleInfo {
title: string;
subtitle?: string;
/** Does not effect ASO https://developer.apple.com/app-store/product-page/ */
/** Does not effect ASO https://developer-mdn.apple.com/app-store/product-page/ */
promoText?: string;
description?: string;
keywords?: string[];
@@ -1,8 +1,11 @@
import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import { instance, mock } from 'ts-mockito';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
import { jester, jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { promptAsync } from '../../../prompts';
import {
ensureApplicationIdIsDefinedForManagedProjectAsync,
@@ -11,6 +14,7 @@ import {
jest.mock('fs');
jest.mock('../../../prompts');
jest.mock('../../../graphql/queries/AppQuery');
jest.mock('../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) }));
beforeEach(async () => {
@@ -103,6 +107,8 @@ describe(getApplicationIdAsync, () => {
describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
describe('managed project + android.package missing in app config', () => {
it('throws an error if using app.config.js', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
vol.fromJSON(
{
'app.config.js': 'module.exports = { blah: {} };',
@@ -110,10 +116,23 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
'/app'
);
await expect(
ensureApplicationIdIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
ensureApplicationIdIsDefinedForManagedProjectAsync({
graphqlClient,
projectDir: '/app',
projectId: '',
exp: {} as any,
})
).rejects.toThrowError(/we can't update this file programmatically/);
});
it('prompts for the Android package if using app.json', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
vol.fromJSON(
{
'app.json': '{ "expo": {} }',
@@ -126,11 +145,26 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
}));
await expect(
ensureApplicationIdIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
ensureApplicationIdIsDefinedForManagedProjectAsync({
graphqlClient,
projectDir: '/app',
projectId: '',
exp: {} as any,
})
).resolves.toBe('com.expo.notdominik');
expect(promptAsync).toHaveBeenCalled();
});
it('puts the Android package in app.json', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
vol.fromJSON(
{
'app.json': '{ "expo": {} }',
@@ -143,7 +177,12 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
}));
await expect(
ensureApplicationIdIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
ensureApplicationIdIsDefinedForManagedProjectAsync({
graphqlClient,
projectDir: '/app',
projectId: '',
exp: {} as any,
})
).resolves.toBe('com.expo.notdominik');
const appJson = JSON.parse(await fs.readFile('/app/app.json', 'utf-8'));
expect(appJson).toMatchObject({
@@ -7,22 +7,31 @@ import fs from 'fs-extra';
import nullthrows from 'nullthrows';
import { readAppJson } from '../../build/utils/appJson';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import env from '../../env';
import Log, { learnMore } from '../../log';
import { getProjectConfigDescription, getUsername } from '../../project/projectUtils';
import {
getOwnerAccountForProjectIdAsync,
getProjectConfigDescription,
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { Actor } from '../../user/User';
import { resolveWorkflowAsync } from '../workflow';
import { GradleBuildContext } from './gradle';
import * as gradleUtils from './gradleUtils';
export const INVALID_APPLICATION_ID_MESSAGE = `Invalid format of Android applicationId. Only alphanumeric characters, '.' and '_' are allowed, and each '.' must be followed by a letter.`;
export async function ensureApplicationIdIsDefinedForManagedProjectAsync(
projectDir: string,
exp: ExpoConfig,
actor: Actor
): Promise<string> {
export async function ensureApplicationIdIsDefinedForManagedProjectAsync({
graphqlClient,
projectDir,
projectId,
exp,
}: {
graphqlClient: ExpoGraphqlClient;
projectDir: string;
projectId: string;
exp: ExpoConfig;
}): Promise<string> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
assert(workflow === Workflow.MANAGED, 'This function should be called only for managed projects');
@@ -31,7 +40,7 @@ export async function ensureApplicationIdIsDefinedForManagedProjectAsync(
moduleName: gradleUtils.DEFAULT_MODULE_NAME,
});
} catch {
return await configureApplicationIdAsync(projectDir, exp, actor);
return await configureApplicationIdAsync({ graphqlClient, projectDir, projectId, exp });
}
}
@@ -110,11 +119,17 @@ export async function getApplicationIdAsync(
}
}
async function configureApplicationIdAsync(
projectDir: string,
exp: ExpoConfig,
actor: Actor
): Promise<string> {
async function configureApplicationIdAsync({
graphqlClient,
projectDir,
projectId,
exp,
}: {
graphqlClient: ExpoGraphqlClient;
projectDir: string;
projectId: string;
exp: ExpoConfig;
}): Promise<string> {
const paths = getConfigFilePaths(projectDir);
// we can't automatically update app.config.js
if (paths.dynamicConfigPath) {
@@ -132,7 +147,11 @@ async function configureApplicationIdAsync(
)}`
);
const suggestedAndroidApplicationId = await getSuggestedApplicationIdAsync(exp, actor);
const suggestedAndroidApplicationId = await getSuggestedApplicationIdAsync(
graphqlClient,
exp,
projectId
);
const { packageName } = await promptAsync({
name: 'packageName',
type: 'text',
@@ -174,8 +193,9 @@ export function warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject(
}
async function getSuggestedApplicationIdAsync(
graphqlClient: ExpoGraphqlClient,
exp: ExpoConfig,
actor: Actor
projectId: string
): Promise<string | undefined> {
// Attempt to use the ios bundle id first since it's convenient to have them aligned.
const maybeBundleId = IOSConfig.BundleIdentifier.getBundleIdentifier(exp);
@@ -183,9 +203,9 @@ async function getSuggestedApplicationIdAsync(
return maybeBundleId;
} else {
// the only callsite is heavily interactive
const username = getUsername(exp, actor);
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
// It's common to use dashes in your node project name, strip them from the suggested package name.
const possibleId = `com.${username}.${exp.slug}`.split('-').join('');
const possibleId = `com.${account.name}.${exp.slug}`.split('-').join('');
if (isApplicationIdValid(possibleId)) {
return possibleId;
}
@@ -2,7 +2,7 @@ import { ExpoConfig } from '@expo/config';
import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import { Actor } from '../user/User';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import {
ensureApplicationIdIsDefinedForManagedProjectAsync,
getApplicationIdAsync,
@@ -16,19 +16,32 @@ import { resolveXcodeBuildContextAsync } from './ios/scheme';
import { findApplicationTarget, resolveTargetsAsync } from './ios/target';
import { resolveWorkflowAsync } from './workflow';
export async function getApplicationIdentifierAsync(
projectDir: string,
exp: ExpoConfig,
buildProfile: BuildProfile,
platform: Platform,
actor: Actor
): Promise<string> {
export async function getApplicationIdentifierAsync({
graphqlClient,
projectDir,
projectId,
exp,
buildProfile,
platform,
}: {
graphqlClient: ExpoGraphqlClient;
projectDir: string;
projectId: string;
exp: ExpoConfig;
buildProfile: BuildProfile;
platform: Platform;
}): Promise<string> {
if (platform === Platform.ANDROID) {
const profile = buildProfile as BuildProfile<Platform.ANDROID>;
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
if (workflow === Workflow.MANAGED) {
return await ensureApplicationIdIsDefinedForManagedProjectAsync(projectDir, exp, actor);
return await ensureApplicationIdIsDefinedForManagedProjectAsync({
graphqlClient,
projectDir,
projectId,
exp,
});
}
const gradleContext = await resolveGradleBuildContextAsync(projectDir, profile);
@@ -37,7 +50,12 @@ export async function getApplicationIdentifierAsync(
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
const profile = buildProfile as BuildProfile<Platform.IOS>;
if (workflow === Workflow.MANAGED) {
return await ensureBundleIdentifierIsDefinedForManagedProjectAsync(projectDir, exp, actor);
return await ensureBundleIdentifierIsDefinedForManagedProjectAsync({
graphqlClient,
projectDir,
projectId,
exp,
});
}
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
@@ -2,8 +2,11 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import path from 'path';
import { instance, mock } from 'ts-mockito';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
import { jester, jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { promptAsync } from '../../../prompts';
import {
ensureBundleIdentifierIsDefinedForManagedProjectAsync,
@@ -13,6 +16,7 @@ import {
jest.mock('fs');
jest.mock('../../../prompts');
jest.mock('../../../graphql/queries/AppQuery');
jest.mock('../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) }));
beforeEach(async () => {
@@ -86,6 +90,7 @@ describe(getBundleIdentifierAsync, () => {
describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
describe('managed project + ios.bundleIdentifier missing in app config', () => {
it('throws an error if using app.config.js', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
vol.fromJSON(
{
'app.config.js': 'module.exports = { blah: {} };',
@@ -93,10 +98,23 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
'/app'
);
await expect(
ensureBundleIdentifierIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
ensureBundleIdentifierIsDefinedForManagedProjectAsync({
graphqlClient,
projectDir: '/app',
projectId: '1234',
exp: {} as any,
})
).rejects.toThrowError(/we can't update this file programmatically/);
});
it('prompts for the bundle identifier if using app.json', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
vol.fromJSON(
{
'app.json': '{ "expo": {} }',
@@ -109,11 +127,24 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
}));
await expect(
ensureBundleIdentifierIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
ensureBundleIdentifierIsDefinedForManagedProjectAsync({
graphqlClient,
projectDir: '/app',
projectId: '1234',
exp: {} as any,
})
).resolves.toBe('com.expo.notdominik');
expect(promptAsync).toHaveBeenCalled();
});
it('puts the bundle identifier in app.json', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
vol.fromJSON(
{
'app.json': '{ "expo": {} }',
@@ -126,7 +157,12 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
}));
await expect(
ensureBundleIdentifierIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
ensureBundleIdentifierIsDefinedForManagedProjectAsync({
graphqlClient,
projectDir: '/app',
projectId: '1234',
exp: {} as any,
})
).resolves.toBe('com.expo.notdominik');
const appJson = JSON.parse(await fs.readFile('/app/app.json', 'utf-8'));
expect(appJson).toMatchObject({
@@ -6,26 +6,37 @@ import chalk from 'chalk';
import fs from 'fs-extra';
import { readAppJson } from '../../build/utils/appJson';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import Log, { learnMore } from '../../log';
import { promptAsync } from '../../prompts';
import { Actor } from '../../user/User';
import { getProjectConfigDescription, getUsername } from '../projectUtils';
import { getOwnerAccountForProjectIdAsync, getProjectConfigDescription } from '../projectUtils';
import { resolveWorkflowAsync } from '../workflow';
export const INVALID_BUNDLE_IDENTIFIER_MESSAGE = `Invalid format of iOS bundle identifier. Only alphanumeric characters, '.' and '-' are allowed, and each '.' must be followed by a letter.`;
export async function ensureBundleIdentifierIsDefinedForManagedProjectAsync(
projectDir: string,
exp: ExpoConfig,
actor: Actor
): Promise<string> {
export async function ensureBundleIdentifierIsDefinedForManagedProjectAsync({
graphqlClient,
projectDir,
projectId,
exp,
}: {
graphqlClient: ExpoGraphqlClient;
projectDir: string;
projectId: string;
exp: ExpoConfig;
}): Promise<string> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
assert(workflow === Workflow.MANAGED, 'This function should be called only for managed projects');
try {
return await getBundleIdentifierAsync(projectDir, exp);
} catch {
return await configureBundleIdentifierAsync(projectDir, exp, actor);
return await configureBundleIdentifierAsync({
graphqlClient,
projectDir,
exp,
projectId,
});
}
}
@@ -100,11 +111,17 @@ export async function getBundleIdentifierAsync(
}
}
async function configureBundleIdentifierAsync(
projectDir: string,
exp: ExpoConfig,
actor: Actor
): Promise<string> {
async function configureBundleIdentifierAsync({
graphqlClient,
projectDir,
projectId,
exp,
}: {
graphqlClient: ExpoGraphqlClient;
projectDir: string;
projectId: string;
exp: ExpoConfig;
}): Promise<string> {
const paths = getConfigFilePaths(projectDir);
// we can't automatically update app.config.js
if (paths.dynamicConfigPath) {
@@ -122,7 +139,11 @@ async function configureBundleIdentifierAsync(
)}`
);
const suggestedBundleIdentifier = await getSuggestedBundleIdentifierAsync(exp, actor);
const suggestedBundleIdentifier = await getSuggestedBundleIdentifierAsync(
graphqlClient,
exp,
projectId
);
const { bundleIdentifier } = await promptAsync({
name: 'bundleIdentifier',
type: 'text',
@@ -169,8 +190,9 @@ export function isWildcardBundleIdentifier(bundleIdentifier: string): boolean {
}
async function getSuggestedBundleIdentifierAsync(
graphqlClient: ExpoGraphqlClient,
exp: ExpoConfig,
actor: Actor
projectId: string
): Promise<string | undefined> {
// Attempt to use the android package name first since it's convenient to have them aligned.
const maybeAndroidPackage = AndroidConfig.Package.getPackage(exp);
@@ -178,9 +200,9 @@ async function getSuggestedBundleIdentifierAsync(
return maybeAndroidPackage;
} else {
// the only callsite is heavily interactive
const username = getUsername(exp, actor);
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
// It's common to use dashes in your node project name, strip them from the suggested package name.
const possibleId = `com.${username}.${exp.slug}`.split('-').join('');
const possibleId = `com.${account.name}.${exp.slug}`.split('-').join('');
if (isBundleIdentifierValid(possibleId)) {
return possibleId;
}
+1 -1
View File
@@ -264,7 +264,7 @@ export function getApplePlatformFromSdkRoot(target: Target): ApplePlatform | nul
* Get Apple Platform from the Xcode TARGETED_DEVICE_FAMILY where possible.
*
* References:
* https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html
* https://developer-mdn.apple.com/library/archive/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html
* https://stackoverflow.com/questions/39677524/xcode-8-how-to-change-targeted-device-family#comment100316573_39677659
*
* @returns - Apple Platform when known, defaults to null when unknown
+16 -2
View File
@@ -18,7 +18,7 @@ import { resolveWorkflowPerPlatformAsync } from '../project/workflow';
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from './android/UpdatesModule';
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from './ios/UpdatesModule';
export const DEFAULT_MANAGED_RUNTIME_VERSION = { policy: 'appVersion' } as const;
export const DEFAULT_MANAGED_RUNTIME_VERSION = { policy: 'sdkVersion' } as const;
export const DEFAULT_BARE_RUNTIME_VERSION = '1.0.0' as const;
function getDefaultRuntimeVersion(workflow: Workflow): NonNullable<ExpoConfig['runtimeVersion']> {
@@ -61,7 +61,11 @@ function replaceUndefinedObjectValues(
function mergeExpoConfig(exp: ExpoConfig, modifyExp: Partial<ExpoConfig>): Partial<ExpoConfig> {
return {
runtimeVersion: modifyExp.runtimeVersion ?? exp.runtimeVersion,
updates: { ...exp.updates, ...modifyExp.updates },
assetBundlePatterns: modifyExp.assetBundlePatterns ?? exp.assetBundlePatterns,
updates: {
...exp.updates,
url: modifyExp?.updates?.url || exp.updates?.url,
},
android: {
...exp.android,
...modifyExp.android,
@@ -99,6 +103,10 @@ async function ensureEASUpdatesIsConfiguredInExpoConfigAsync({
modifyConfig.updates = { url: getEASUpdateURL(projectId) };
}
if (!exp.assetBundlePatterns) {
modifyConfig.assetBundlePatterns = ['*/**'];
}
let androidRuntimeVersion = exp.android?.runtimeVersion ?? exp.runtimeVersion;
let iosRuntimeVersion = exp.ios?.runtimeVersion ?? exp.runtimeVersion;
@@ -185,6 +193,12 @@ function logEasUpdatesAutoConfig({
} with "${JSON.stringify(modifyConfig.ios?.runtimeVersion ?? modifyConfig.runtimeVersion)}"`
);
}
if (modifyConfig?.assetBundlePatterns) {
Log.withTick(
`Configured assetBundlePatterns to ${JSON.stringify(modifyConfig.assetBundlePatterns)}`
);
}
}
function warnEASUpdatesManualConfig({
+15 -5
View File
@@ -7,15 +7,25 @@ export type Actor = NonNullable<CurrentUserQuery['meActor']>;
* This should be used whenever the "current user" needs to be displayed.
* The display name CANNOT be used as project owner.
*/
export function getActorDisplayName(user?: Actor): string {
switch (user?.__typename) {
export function getActorDisplayName(actor?: Actor): string {
switch (actor?.__typename) {
case 'User':
return user.username;
return actor.username;
case 'Robot':
return user.firstName ? `${user.firstName} (robot)` : 'robot';
return actor.firstName ? `${actor.firstName} (robot)` : 'robot';
case 'SSOUser':
return user.username ? `${user.username} (sso user)` : 'sso user';
return actor.username ? `${actor.username} (sso user)` : 'sso user';
default:
return 'anonymous';
}
}
export function getActorUsername(actor?: Actor): string | null {
switch (actor?.__typename) {
case 'User':
case 'SSOUser':
return actor.username;
default:
return null;
}
}
@@ -1,17 +1,18 @@
-----BEGIN CERTIFICATE-----
MIICyzCCAbOgAwIBAgIJWVXVdVgS9u+DMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV
BAMTBFRlc3QwHhcNMjIwMjA0MDEzODU2WhcNMjMwMjA0MDEzODU2WjAPMQ0wCwYD
VQQDEwRUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp1ILmaSl
1pUVFgt/nLtzoaGuBW4OPRFN5uU2rYPow3uy4NfULKiir4AWxGETcm4gFIOG3RIb
DJys6TAJJLnFEFnzpV+qaNeqaMqnXm4OBspx/WS2rSSGIwr7raKy4yrpPYJB1A2D
5fZ6oFoWROyjbHJvPddu0PqbbEWSM3PzdZYOKfF13ofzFOPlsTnlo12QWgksxrcT
wgeX7cjHsXaUc+I8U9DRR+9SgKhejWdFNPGcMkEc5BIXwtkTau+8+DwQzXz775Wl
hf0eJwfnNQeleblbLQIp8i+RmfIjPSt8W/iTSN64iBROpOvnOeJ9gKX922QDZoqW
z9o4RTNCPfyYBQIDAQABoyowKDAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAww
CgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAHTyG2f/CReXVrS3zZjpgnje
Vn2/XYevnaHNWYIxjlUVGaASE/k6qWkM832f0gaZwJYXsll2YxJa5Jcdqa1yq8QA
Ay5YBq8Vv6K6iGTeZ5wTQc3xNys7zcS95eNq7yhDDjbLDbSDdZ8T/1hZqiha2DG5
itLGfBH+HywW2G9njLgIzNcmKx3d76y3jlVXqug1/AVFK9in+r/0t0TYlk+opqZb
H7cjJSefHusnq2vvvR6gbkZzyzSYP5FMlj/RjRCSJ+VHNwCn3fS+KDlJgVOk0Cxc
7n52UGazeFwH3mem89DN+Bhw/uyU0IcIaDLykb6PsTPy2tjHsLpK5kpvfrp2m0o=
-----END CERTIFICATE-----
MIICzTCCAbWgAwIBAgIJWEL03IR99LhMMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV
BAMTBHRlc3QwIBcNMjIwNDEyMTcyODIzWhgPMjEyMjA0MTIxNzI4MjNaMA8xDTAL
BgNVBAMTBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBS7bz
ELiUKOB8QNh0e0Oit7j7vk7ZICdyvBv+AB7PYbgImTvJSZ3CJTfmDYOt57ENd+ke
V/RnD9b8u55ijroJBoR8QASVVsEYmgA1cv9owUYkIDxsAjxV9JZp3UgNeRmQ7IKI
1J7myNnUQ3RIclSZkJO7ViHGSwTMXBanaO/OwjY6SM6Eca89XMcpOc/0xCBidFkO
qn/jnIXk3uPAoLjPMQcX535+AUayevwSr6YqPV0zOGOYJy26n401eH3mu/UZveWy
6qp7VumcBmz8AkvTJDgfSv04Pk3gpi4d8q4ABrtM46qWlw6UMMh5R6/i6Ou/8526
xThOm3qEpEGXyZ2VAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8E
DDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEATeE0FXXheYjw77pjy6sZ
b6POytJdYLawMt7Chn11zP1TLx0LA1R01uH/ld0tVQQyt1XqG9AE3as1VCUYklPA
zmwFSOxcSMiAXobEa5NcJ4S490YTjYmHI8fkz6TS+eeLkIr7eZMbpe6ck9V2d1e3
0xHshYRc3NceNOKB018QYxbdxuTbV3a6f4yTlCSxKuyYfA5KDMoXaPAKE7Cwddrz
1dbQpSI6djWknFjhTh7Bu/zpGWEx7wXTZlx9nIhKrinVowvyUkTaaCfuzdCmZOzS
AJsADnJN0LEKDI4q0kDwl1v6U4cJ5Ru7VQv3xho03LPEA/fFKHK6OITDznspiXRs
IA==
-----END CERTIFICATE-----
@@ -1,27 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAp1ILmaSl1pUVFgt/nLtzoaGuBW4OPRFN5uU2rYPow3uy4NfU
LKiir4AWxGETcm4gFIOG3RIbDJys6TAJJLnFEFnzpV+qaNeqaMqnXm4OBspx/WS2
rSSGIwr7raKy4yrpPYJB1A2D5fZ6oFoWROyjbHJvPddu0PqbbEWSM3PzdZYOKfF1
3ofzFOPlsTnlo12QWgksxrcTwgeX7cjHsXaUc+I8U9DRR+9SgKhejWdFNPGcMkEc
5BIXwtkTau+8+DwQzXz775Wlhf0eJwfnNQeleblbLQIp8i+RmfIjPSt8W/iTSN64
iBROpOvnOeJ9gKX922QDZoqWz9o4RTNCPfyYBQIDAQABAoIBAEKMXVTEqbkJHpPg
Cud5nuoAdkhul3cudL+LFw44UtG9V04aSadhgyMuXN/KhIOUzWmbTn6K/vsrLZKp
qllTEdAJFuEFha+hZ4O6ZosmVqnYxzGzZvzCdB9n9OYAugmkPZRbRHdk0LscJ3Wz
nvvW6sDtWtVL5CV2J28O1LFmQsTXPtEE5aQ5KX3Ix0OW1HBdEMOXRDFFDXhf7AKz
nlvl3WRsXLyHYBfKs0pQHy8Gi+zl2OgKZDc9elMdU0Mx0jTJUUDtPSgNAsmdKO26
qmkhbGd/kM1+6h+zngY3m48yD9elDxR3WUIYE2x5tT9fv3T6/xn1QfMz/gtToplx
qY1DegECgYEA3bGUq91QMIH5rxrr2BBQpniXOTtOPAdvRcRh0/EsYhHcuL+uJEtG
W9o9MqrREhXMMeD9Y8RorL+qefJjGq+GRwnPHlDjr6NkGTZfDKUn4IFH1xMbspET
Nen5BsY5inUrQ/LSg1+HiVsSDyIJzGVcGMTOh9qC53M/zIGdiEwHgUECgYEAwTZ3
AVieag5ETYahGdverxCRKlJ0MFJq3VKEdt6EcZHfeY1gmi17vGZ39q6djmfTT9Il
QVP4/mxU2/mg/UgRC+rwiAZs3qlhOrtkrGd9qPOIeZ8PJkoNlvhM2WC2rGjTfXsb
2IKtDEVXetwGgm3SXqpjycI5Xj6/9VlHAmCc4cUCgYAv9dT2AWDxvYyopyhSi+UG
vpvok73vGqSl8UBAu7IgXUDk7wLbczV7dZE7vtyQDwsn10a6KKmEhcp5q0hpY4On
JqYaJuG7A5wKIEsbzzb7SLyj+MxLKzt+tGldX9De9U4w2v1T0nzd6EfV4kVAZMUx
zpHnrgwXykUJFxlffSM6gQKBgQCMxKXHsU0Zb/OLmD7fnDWNzsA02YYVfralMW2Z
PV25cNIkuUBclC7GgNF+RJI+Ip7uVOkXw5pxo3PgIOuOHWduC2nbcPL49ucD52vd
wDjpUyVnlt9uwh1MlPNInRH6YxVTItKS2AJEInEt7ghAFstidTnm0T8Czy0EEFuP
+9vREQKBgQDIQxJ5oPIOEibJqn34z6qm5EmUyme/evvHxtQTaVuQi5CrzD5+qw1C
jOTHmlJQQsb8b1g9wk7n+JbqWYhkRiAE6dwqvp/zDz1VfR90WbAPYQ8ZiXZ07D2P
8P6ZXR3ZyDchuT6oUBXz5WtHUWOzXRJgPbXnFhrkIU8PkZYiPs/4Bw==
-----END RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAwUu28xC4lCjgfEDYdHtDore4+75O2SAncrwb/gAez2G4CJk7
yUmdwiU35g2DreexDXfpHlf0Zw/W/LueYo66CQaEfEAElVbBGJoANXL/aMFGJCA8
bAI8VfSWad1IDXkZkOyCiNSe5sjZ1EN0SHJUmZCTu1YhxksEzFwWp2jvzsI2OkjO
hHGvPVzHKTnP9MQgYnRZDqp/45yF5N7jwKC4zzEHF+d+fgFGsnr8Eq+mKj1dMzhj
mCctup+NNXh95rv1Gb3lsuqqe1bpnAZs/AJL0yQ4H0r9OD5N4KYuHfKuAAa7TOOq
lpcOlDDIeUev4ujrv/OdusU4Tpt6hKRBl8mdlQIDAQABAoIBAEVijWCA/xDH/5T6
nfhqCuRM+Mz9CkasYRyxY4bwuh6NIEeN4cUmdMetHnypGzyAr7B6+6ZVwjiAmhaM
rpUIUMVOnp4PSNXml1fiZ/LHveD3h9sN60KGJuxf6OJFeUjE0KWSEGVXlVaYgIDO
Wd5rk+yv1ifoCiWo5icJY2RiqbpYouveqb/InkQU+irZEwCviNvN1ZyJKqquNgcG
FaY7iVd3WlHRealF1OfKHCBvSah9H5UBFWRBWUds2/xNzpCTAAGsmXdG6O9wPuEQ
IhJqnSkTiQw49vyOH708VQnBfz/MILV7uTYqQ0Tep3+xkFFy4BSUIb6fE2zJegs5
80MxIh0CgYEA4yf8cBcYidTAeoDbl2UXasDHAmNKeR4IxKAR3AJOiETIKDv4vAMP
oBvHnUnnUQa5LhihrVXCbVwGqwATDka73K8sW/TkCE7OmuLDpZYvU/XQRWI74rka
A/+nbD26NlwnM/a8mJNhbCVns/+PlLq1rK/5WlINcRNVk8PZ1UpG+nsCgYEA2dcM
1TBZs1iqa+9AQ6nco5MWbsp3roq3sM06UneK0EGffWXPcmQkBZI9Jg+p5P2o/H13
cYvkyKGmnL8qRZViXWEa/NI60kphQmFNGr+J/3PlKCOqZhlyqGhCFN9KLumJL17P
XUuzVAa14UsfIFCiv7R33D02o/xw89u5TN+3ky8CgYEAhmkzszPHXk9YcWRsC2JS
/+UAHQaZm59M+uPojXGD/JgOg9gwrzd0eH3XmNeRG13KF1+V01YKjOFGRMrve6QZ
J1Uz/1mh0NSo5fdGY2XBrYGnclbVLqvum+0bqS3BUMcon1PhdrrGi9J6UYTn9c6h
D4S1HF+u+njBS4U5ET85Tp8CgYEAno0bVf+/Cf467BxTFeyIHrZr2W+b9HoagKCf
Fm7TpghdYRO9DXE9lqB9yToVWgoV+NAJI6fCeRTPA79PsR2tXnHTBris/2oLqBjR
2eoXMsrTu4dZ+r4C6fgYQMDUaZiotMW5ABqdB0drEfNvUEHgcs+TfcVAA9M1EwiV
shIStxkCgYBnxccKAfXywLjhVUCa50TsCrL8ToQ7WmWjU1LqN2iO/htFoxeIx4xP
N7YauagBhyuypcq7SV3bKsdxlCzKi0cMNEonuRJb1Ejo3iU+4F7rzqsltGTjLtpB
pgp5HP8sALzFZlWWif6W1TjzNJ87MIphr/VPcitRc9n4hWcbmxDvZA==
-----END RSA PRIVATE KEY-----
Generated
+7 -7
View File
@@ -1324,10 +1324,10 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@expo/[email protected]4":
version "0.0.0-alpha.34"
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-0.0.0-alpha.34.tgz#b36547de894e69a73290b8e43a8ef48ffe0ce240"
integrity sha512-GRHN1xhI3Yjb76aFVLhxE7o/Pdx8D78Gm2HaRHxdFRSSdYK5lPw2/1y1/GhDH7DoXqqGwo2uQSevd6ywlj657g==
"@expo/[email protected]7":
version "0.0.0-alpha.37"
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-0.0.0-alpha.37.tgz#e85dea5c4ada8f1e8e26ce9670fb6d38c0092299"
integrity sha512-/v7adya/u6GDBj7py+HsnKmA11VwG34z4/7peJdMfu8M3LZ2G14dsaxEqdndrCAuEyJ6gQ88sggjcjPjzQXdmg==
"@expo/bunyan@^4.0.0":
version "4.0.0"
@@ -8077,9 +8077,9 @@ html-escaper@^2.0.0:
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
version "4.1.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
http-call@^5.2.2:
version "5.3.0"