Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[eas-build-job] add channel to Job #24

Merged
merged 28 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions packages/build-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@
"fs-extra": "^9.0.0",
"node-forge": "^0.9.1",
"nullthrows": "^1.1.1",
"plist": "^3.0.1",
"uuid": "^3.3.3",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uuid wasn't being used.

"xml2js": "^0.4.23"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced the manual xml2js usage in packages/build-tools/src/android/releaseChannel.ts with calls to methods in @expo/config-plugins.AndroidConfig.Manifest.* in packages/build-tools/src/android/expoUpdates.ts

"plist": "^3.0.1"
},
"devDependencies": {
"@types/fs-extra": "^9.0.1",
Expand Down
3 changes: 3 additions & 0 deletions packages/build-tools/src/__mocks__/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ import { fs } from 'memfs';
// `temp-dir` dependency of `tempy` is using `fs.realpathSync('/tmp')`
// on import to verify existence of tmp directory
fs.mkdirSync('/tmp');
if (process.env.TMPDIR) {
fs.mkdirSync(process.env.TMPDIR, { recursive: true });
}
dsokal marked this conversation as resolved.
Show resolved Hide resolved

module.exports = fs;
148 changes: 148 additions & 0 deletions packages/build-tools/src/android/__tests__/expoUpdates.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import path from 'path';

import fs from 'fs-extra';
import { AndroidConfig } from '@expo/config-plugins';

import {
AndroidMetadataName,
getAndroidManifestDirectory,
androidGetNativelyDefinedReleaseChannelAsync,
androidSetChannelNativelyAsync,
androidSetReleaseChannelNativelyAsync,
} from '../expoUpdates';

jest.mock('fs');

const channel = 'main';
const noMetadataAndroidManifest = `
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.expo.mycoolapp">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>

</manifest>
`;
describe(androidSetReleaseChannelNativelyAsync, () => {
test('sets the release channel', async () => {
const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-');
fs.ensureDirSync(reactNativeProjectDirectory);
const releaseChannel = 'default';
const ctx = {
reactNativeProjectDirectory,
job: { releaseChannel },
logger: { info: () => {} },
};

const manifestDirectory = getAndroidManifestDirectory(reactNativeProjectDirectory);
const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml');

fs.ensureDirSync(manifestDirectory);
fs.writeFileSync(manifestPath, Buffer.from(noMetadataAndroidManifest));
const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath);
expect(
AndroidConfig.Manifest.getMainApplicationMetaDataValue(androidManifest, 'releaseChannel')
).toBe(null);

await androidSetReleaseChannelNativelyAsync(ctx as any);

const newAndroidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath);
expect(
AndroidConfig.Manifest.getMainApplicationMetaDataValue(
newAndroidManifest,
AndroidMetadataName.RELEASE_CHANNEL
)
).toBe(releaseChannel);
});
});
describe(androidSetChannelNativelyAsync, () => {
it('sets the channel', async () => {
const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-');
fs.ensureDirSync(reactNativeProjectDirectory);
const ctx = {
reactNativeProjectDirectory,
job: { updates: { channel } },
logger: { info: () => {} },
};

const manifestDirectory = getAndroidManifestDirectory(reactNativeProjectDirectory);
const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml');

fs.ensureDirSync(manifestDirectory);
fs.writeFileSync(manifestPath, noMetadataAndroidManifest);

const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath);
expect(
AndroidConfig.Manifest.getMainApplicationMetaDataValue(
androidManifest,
AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY
)
).toBe(null);

await androidSetChannelNativelyAsync(ctx as any);

const newAndroidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath);
const newValue = AndroidConfig.Manifest.getMainApplicationMetaDataValue(
newAndroidManifest,
AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY
);
expect(newValue).toBeDefined();
expect(JSON.parse(newValue!)).toEqual({ 'expo-channel-name': channel });
});
});
describe(androidGetNativelyDefinedReleaseChannelAsync, () => {
it('gets the natively defined release channel', async () => {
const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-');
fs.ensureDirSync(reactNativeProjectDirectory);
const releaseChannel = 'default';
const ctx = {
reactNativeProjectDirectory,
logger: { info: () => {} },
};

const releaseChannelInAndroidManifest = `
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.expo.mycoolapp">
<uses-permission android:name="android.permission.INTERNET"/>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme">
<activity android:name=".MainActivity" android:launchMode="singleTask" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
<meta-data android:name="expo.modules.updates.EXPO_RELEASE_CHANNEL" android:value="default"/>
</application>
</manifest>`;
const manifestDirectory = getAndroidManifestDirectory(reactNativeProjectDirectory);
const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml');

fs.ensureDirSync(manifestDirectory);
fs.writeFileSync(manifestPath, releaseChannelInAndroidManifest);

const nativelyDefinedReleaseChannel = await androidGetNativelyDefinedReleaseChannelAsync(
ctx as any
);
expect(nativelyDefinedReleaseChannel).toBe(releaseChannel);
});
});
89 changes: 89 additions & 0 deletions packages/build-tools/src/android/expoUpdates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import path from 'path';
import assert from 'assert';

import fs from 'fs-extra';
import { AndroidConfig } from '@expo/config-plugins';

import { ManagedBuildContext, ManagedJob } from '../managed/context';
import { BuildContext } from '../context';
import { GenericJob } from '../utils/expoUpdates';
export enum AndroidMetadataName {
jkhales marked this conversation as resolved.
Show resolved Hide resolved
UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = 'expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY',
RELEASE_CHANNEL = 'expo.modules.updates.EXPO_RELEASE_CHANNEL',
}
export function getAndroidManifestDirectory(reactNativeProjectDirectory: string): string {
jkhales marked this conversation as resolved.
Show resolved Hide resolved
return path.join(reactNativeProjectDirectory, 'android', 'app', 'src', 'main');
}

export async function androidSetChannelNativelyAsync(
ctx: ManagedBuildContext<ManagedJob> | BuildContext<GenericJob>
): Promise<void> {
assert(ctx.job.updates?.channel, 'updates.channel must be defined');

const manifestPath = path.join(
getAndroidManifestDirectory(ctx.reactNativeProjectDirectory),
'AndroidManifest.xml'
);
jkhales marked this conversation as resolved.
Show resolved Hide resolved
if (!(await fs.pathExists(manifestPath))) {
throw new Error(`Couldn't find Android manifest at ${manifestPath}`);
}

const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath);
const mainApp = AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest);
const stringifiedUpdatesRequestHeaders = AndroidConfig.Manifest.getMainApplicationMetaDataValue(
androidManifest,
AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY
);
AndroidConfig.Manifest.addMetaDataItemToMainApplication(
mainApp,
AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY,
JSON.stringify({
...JSON.parse(stringifiedUpdatesRequestHeaders ?? '{}'),
'expo-channel-name': ctx.job.updates.channel,
}),
'value'
);
await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest);
}

export const androidSetReleaseChannelNativelyAsync = async (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

androidSetClasicChannelNativelyAsync or androidSetLegacyChannelNativelyAsync or sth simliair that mekes it more clear, distinction between realeasChannel and channel is often hard to spot.

When we switch to eas updates old key in eas.json will also have to be renamed this way to

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const androidSetReleaseChannelNativelyAsync = async (
export async function androidSetReleaseChannelNativelyAsync(

ctx: ManagedBuildContext<ManagedJob> | BuildContext<GenericJob>
): Promise<void> => {
assert(ctx.job.releaseChannel, 'releaseChannel must be defined');

const manifestPath = path.join(
getAndroidManifestDirectory(ctx.reactNativeProjectDirectory),
'AndroidManifest.xml'
);
if (!(await fs.pathExists(manifestPath))) {
throw new Error(`Couldn't find Android manifest at ${manifestPath}`);
}

const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath);
const mainApp = AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest);
AndroidConfig.Manifest.addMetaDataItemToMainApplication(
mainApp,
AndroidMetadataName.RELEASE_CHANNEL,
ctx.job.releaseChannel,
'value'
);
await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest);
};

export const androidGetNativelyDefinedReleaseChannelAsync = async (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const androidGetNativelyDefinedReleaseChannelAsync = async (
export async function androidGetNativelyDefinedReleaseChannelAsync(

ctx: ManagedBuildContext<ManagedJob> | BuildContext<GenericJob>
jkhales marked this conversation as resolved.
Show resolved Hide resolved
): Promise<string | undefined | null> => {
const manifestPath = path.join(
getAndroidManifestDirectory(ctx.reactNativeProjectDirectory),
'AndroidManifest.xml'
);
if (!(await fs.pathExists(manifestPath))) {
return;
}

const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath);
return AndroidConfig.Manifest.getMainApplicationMetaDataValue(
androidManifest,
AndroidMetadataName.RELEASE_CHANNEL
);
};
91 changes: 0 additions & 91 deletions packages/build-tools/src/android/releaseChannel.ts

This file was deleted.

7 changes: 3 additions & 4 deletions packages/build-tools/src/builders/androidGeneric.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { Android, BuildPhase } from '@expo/eas-build-job';
import { Android, BuildPhase, Platform } from '@expo/eas-build-job';

import { BuildContext } from '../context';
import { setup } from '../utils/project';
import { findBuildArtifacts } from '../utils/buildArtifacts';
import { Hook, runHookIfPresent } from '../utils/hooks';
import { getReleaseChannel, updateReleaseChannel } from '../android/releaseChannel';
import { restoreCredentials } from '../android/credentials';
import { runGradleCommand, ensureLFLineEndingsInGradlewScript } from '../android/gradle';
import { configureExpoUpdatesIfInstalled } from '../generic/expoUpdates';
import { configureExpoUpdatesIfInstalledAsync } from '../utils/expoUpdates';

export default async function androidGenericBuilder(
ctx: BuildContext<Android.GenericJob>
Expand All @@ -33,7 +32,7 @@ export default async function androidGenericBuilder(
}

await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => {
await configureExpoUpdatesIfInstalled(ctx, { getReleaseChannel, updateReleaseChannel });
await configureExpoUpdatesIfInstalledAsync(ctx, Platform.ANDROID);
});

await ctx.runBuildPhase(BuildPhase.RUN_GRADLEW, async () => {
Expand Down
7 changes: 3 additions & 4 deletions packages/build-tools/src/builders/androidManaged.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { AndroidConfig } from '@expo/config-plugins';
import { Android, BuildPhase } from '@expo/eas-build-job';
import { Android, BuildPhase, Platform } from '@expo/eas-build-job';

import { ManagedBuildContext } from '../managed/context';
import { configureExpoUpdatesIfInstalled } from '../managed/expoUpdates';
import { configureExpoUpdatesIfInstalledAsync } from '../utils/expoUpdates';
import { setup } from '../utils/project';
import { findSingleBuildArtifact } from '../utils/buildArtifacts';
import { Hook, runHookIfPresent } from '../utils/hooks';
import { updateReleaseChannel } from '../android/releaseChannel';
import { restoreCredentials } from '../android/credentials';
import { runGradleCommand } from '../android/gradle';

Expand All @@ -33,7 +32,7 @@ export default async function androidManagedBuilder(
});
}
await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => {
await configureExpoUpdatesIfInstalled(ctx, updateReleaseChannel);
await configureExpoUpdatesIfInstalledAsync(ctx, Platform.ANDROID);
jkhales marked this conversation as resolved.
Show resolved Hide resolved
});

await ctx.runBuildPhase(BuildPhase.RUN_GRADLEW, async () => {
Expand Down
Loading