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

fix/defaultTargets_updating #1782

Merged
merged 11 commits into from
Nov 26, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,17 @@ export default createTask({
copyFileSync(oldGlobalConfigPath, paths.workspace.config);
} else {
logInfo(`${paths.workspace.dir}/${RnvFileName.renative} file missing! Creating one for you...`);
writeFileSync(paths.workspace.config, '{}');
const defaultWorkspaceCnf = {
sdks: {
ANDROID_SDK: '/Users/<USER>/Library/Android/sdk',
ANDROID_NDK: '/Users/<USER>/Library/Android/sdk/ndk-bundle',
IOS_SDK: 'No need. Just install Xcode',
TIZEN_SDK: '/Users/<USER>/tizen-studio',
WEBOS_SDK: '/Users/<USER>/Library/webOS_TV_SDK',
KAIOS_SDK: '/Applications/Kaiosrt.app',
},
};
writeFileSync(paths.workspace.config, JSON.stringify(defaultWorkspaceCnf, null, 2));
}
}

Expand Down
6 changes: 5 additions & 1 deletion packages/sdk-android/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import {
} from './deviceManager';
import { ANDROID_COLORS, ANDROID_STRINGS, ANDROID_STYLES, CLI_ANDROID_ADB } from './constants';
import { runReactNativeAndroid, packageReactNativeAndroid, generateEnvVarsFile } from '@rnv/sdk-react-native';
import { getEntryFile } from '@rnv/sdk-utils';
import { getEntryFile, updateDefaultTargets } from '@rnv/sdk-utils';
import { Context, getContext } from './getContext';

export const packageAndroid = async () => {
Expand Down Expand Up @@ -120,6 +120,10 @@ export const getAndroidDeviceToRunOn = async () => {
choices,
});
if (chosenTarget) {
// update defaultTarget
if (!target) {
await updateDefaultTargets(c, chosenTarget);
}
const dev = activeDevices.find((d) => d.name === chosenTarget);
if (dev) return dev;

Expand Down
28 changes: 15 additions & 13 deletions packages/sdk-apple/src/__tests__/runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { inquirerPrompt, getContext, createRnvContext, logSuccess } from '@rnv/c
import type { PromptParams } from '@rnv/core';
import { getIosDeviceToRunOn } from '../runner';
import { getAppleDevices } from '../deviceManager';
import { updateDefaultTargets } from '@rnv/sdk-utils';

const simJson = [
{
Expand Down Expand Up @@ -33,6 +34,7 @@ const devicesJson = [
];

jest.mock('@rnv/core');
jest.mock('@rnv/sdk-utils');
jest.mock('../deviceManager');
jest.mock('chalk', () => ({
bold: {
Expand Down Expand Up @@ -97,22 +99,17 @@ describe('getIosDeviceToRunOn', () => {
[name as string]: true,
};
}

if (type === 'list') {
// Testing the addition of global/project value should be handled in another UT
if (choices?.includes("Don't update")) {
const choiceIndex = choices.findIndex((c) => c === "Don't update");
return {
[name as string]:
(choices![choiceIndex] as { name: string; value: any }).value || choices![choiceIndex],
};
}
// By default first value returned (aka the first simulator from the list in this case)
return {
[name as string]: (choices![0] as { name: string; value: any }).value || choices![0],
};
}
});
jest.mocked(updateDefaultTargets).mockImplementation(async (ctx, currentTarget) => {
if (!ctx.platform) return;
ctx.runtime.target = currentTarget;
});
// WHEN
const deviceArgs = await getIosDeviceToRunOn(ctx);
//THEN
Expand All @@ -139,14 +136,19 @@ describe('getIosDeviceToRunOn', () => {
ctx.files.workspace.config = {};
ctx.runtime.target = 'iPhone 14';
jest.mocked(getAppleDevices).mockResolvedValueOnce(simJson);
jest.mocked(inquirerPrompt).mockImplementation(async ({ type, name, choices }: PromptParams) => {
if (type === 'list' && choices?.includes('Update global default target for platform ios')) {
return { [name as string]: 'Update global default target for platform ios' };
}
jest.mocked(inquirerPrompt).mockImplementation(async ({ name, choices }: PromptParams) => {
return {
[name as string]: (choices![0] as { name: string; value: any }).value || choices![0],
};
});
jest.mocked(updateDefaultTargets).mockImplementation(async (ctx, currentTarget) => {
if (!ctx.platform) return;
const configGlobal = ctx.files.workspace.config || {};
configGlobal.defaultTargets = configGlobal.defaultTargets || {};
configGlobal.defaultTargets[ctx.platform] = currentTarget;
ctx.files.workspace.config = configGlobal;
ctx.runtime.target = currentTarget;
});
// WHEN
const deviceArgs = await getIosDeviceToRunOn(ctx);
// THEN
Expand Down
40 changes: 2 additions & 38 deletions packages/sdk-apple/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import {
import { registerDevice } from './fastlane';
import { Context, getContext } from './getContext';
import { parsePrivacyManifest } from './privacyManifestParser';
import { getAppId } from '@rnv/sdk-utils';
import { getAppId, updateDefaultTargets } from '@rnv/sdk-utils';

export const packageBundleForXcode = () => {
return packageReactNativeIOS();
Expand Down Expand Up @@ -176,43 +176,7 @@ export const getIosDeviceToRunOn = async (c: Context) => {
})),
});
desiredSim = currentTarget;
const localOverridden = !!c.files.project.configLocal?.defaultTargets?.[c.platform];

const actionLocalUpdate = `Update ${chalk().green('project')} default target for platform ${c.platform}`;
const actionGlobalUpdate = `Update ${chalk().green('global')}${
localOverridden ? ` and ${chalk().green('project')}` : ''
} default target for platform ${c.platform}`;
const actionNoUpdate = "Don't update";

const { chosenAction } = await inquirerPrompt({
message: 'What to do next?',
type: 'list',
name: 'chosenAction',
choices: [actionLocalUpdate, actionGlobalUpdate, actionNoUpdate],
warningMessage: `Your default target for platform ${c.platform} is set to ${c.runtime.target}.`,
});

c.runtime.target = currentTarget.name;

if (chosenAction === actionLocalUpdate || (chosenAction === actionGlobalUpdate && localOverridden)) {
const configLocal = c.files.project.configLocal || {};
if (!configLocal.defaultTargets) configLocal.defaultTargets = {};
configLocal.defaultTargets[c.platform] = currentTarget.name;

c.files.project.configLocal = configLocal;
writeFileSync(c.paths.project.configLocal, configLocal);
}

if (chosenAction === actionGlobalUpdate) {
const configGlobal = c.files.workspace.config;
if (configGlobal) {
if (!configGlobal.defaultTargets) configGlobal.defaultTargets = {};
configGlobal.defaultTargets[c.platform] = currentTarget.name;

c.files.workspace.config = configGlobal;
writeFileSync(c.paths.workspace.config, configGlobal);
}
}
await updateDefaultTargets(c, currentTarget.name);
}
if (!desiredSim?.isDevice) {
const target = c.runtime.target?.replace(/(\s+)/g, '\\$1');
Expand Down
29 changes: 21 additions & 8 deletions packages/sdk-tizen/src/deviceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import { CLI_SDB_TIZEN, CLI_TIZEN, CLI_TIZEN_EMULATOR } from './constants';

import { TizenDevice, TizenSecurityConfig } from './types';
import { updateDefaultTargets } from '@rnv/sdk-utils';

const xml2js = require('xml2js');

Expand Down Expand Up @@ -72,7 +73,11 @@
return {};
};

export const launchTizenTarget = async (name: string | true, hideDevices?: boolean): Promise<boolean> => {
export const launchTizenTarget = async (
name: string | true,
hideDevices?: boolean,
updateDefault = false
): Promise<boolean> => {
const c = getContext();
logDefault(`launchTizenTarget:${name}`);
if (name === true) {
Expand Down Expand Up @@ -105,7 +110,10 @@
: 'which emulator or device would you like to launch?',
choices,
});

if (chosenEmulator) {
// update defaultTarget in .rnv/renative.json
await updateDefaultTargets(c, chosenEmulator);
}
name = chosenEmulator;
}
if (name && typeof name === 'string') {
Expand All @@ -118,11 +126,16 @@
return new Promise(() => logInfo('Device is launched.'));
}
try {
await executeAsync(
`${c.cli[CLI_TIZEN_EMULATOR]} launch --name ${name}`,
ExecOptionsPresets.SPINNER_FULL_ERROR_SUMMARY
);
return true;
if (updateDefault) {
await runTizenSimOrDevice();
return true;
} else {
await executeAsync(
`${c.cli[CLI_TIZEN_EMULATOR]} launch --name ${name}`,

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string concatenation which depends on
library input
is later used in a
shell command
.

Copilot Autofix AI about 1 month ago

To fix the problem, we should avoid constructing the shell command using string concatenation with untrusted input. Instead, we can use the child_process.execFile method, which allows us to pass arguments as an array, thus avoiding shell interpretation of the input.

  • Replace the use of executeAsync with execFile from the child_process module.
  • Ensure that the name parameter is passed as an argument in the array format to execFile.
Suggested changeset 1
packages/sdk-tizen/src/deviceManager.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/sdk-tizen/src/deviceManager.ts b/packages/sdk-tizen/src/deviceManager.ts
--- a/packages/sdk-tizen/src/deviceManager.ts
+++ b/packages/sdk-tizen/src/deviceManager.ts
@@ -139,6 +139,12 @@
             } else {
-                await executeAsync(
-                    `${c.cli[CLI_TIZEN_EMULATOR]} launch --name ${name}`,
-                    ExecOptionsPresets.SPINNER_FULL_ERROR_SUMMARY
-                );
+                const { execFile } = require('child_process');
+                await new Promise((resolve, reject) => {
+                    execFile(c.cli[CLI_TIZEN_EMULATOR], ['launch', '--name', name], ExecOptionsPresets.SPINNER_FULL_ERROR_SUMMARY, (error, stdout, stderr) => {
+                        if (error) {
+                            reject(error);
+                        } else {
+                            resolve(stdout);
+                        }
+                    });
+                });
                 return true;
EOF
@@ -139,6 +139,12 @@
} else {
await executeAsync(
`${c.cli[CLI_TIZEN_EMULATOR]} launch --name ${name}`,
ExecOptionsPresets.SPINNER_FULL_ERROR_SUMMARY
);
const { execFile } = require('child_process');
await new Promise((resolve, reject) => {
execFile(c.cli[CLI_TIZEN_EMULATOR], ['launch', '--name', name], ExecOptionsPresets.SPINNER_FULL_ERROR_SUMMARY, (error, stdout, stderr) => {
if (error) {
reject(error);
} else {
resolve(stdout);
}
});
});
return true;
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
ExecOptionsPresets.SPINNER_FULL_ERROR_SUMMARY
);
return true;
}
} catch (e) {
if (typeof e === 'string') {
if (e.includes(ERROR_MSG.UNKNOWN_VM)) {
Expand Down Expand Up @@ -428,7 +441,7 @@
if (!tId) return Promise.reject(`Tizen platform requires "id" filed in platforms.tizen`);
const askForEmulator = async () => {
if (!target) {
launchTizenTarget(true);
launchTizenTarget(true, undefined, true);
return;
}
const { startEmulator } = await inquirerPrompt({
Expand Down
45 changes: 44 additions & 1 deletion packages/sdk-utils/src/target.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getContext, inquirerPrompt } from '@rnv/core';
import { RnvContext, chalk, getContext, inquirerPrompt, writeFileSync } from '@rnv/core';

export const getTargetWithOptionalPrompt = async () => {
const ctx = getContext();
Expand Down Expand Up @@ -31,3 +31,46 @@ export const getTargetWithOptionalPrompt = async () => {
}
return target;
};

export const updateDefaultTargets = async (c: RnvContext, currentTarget: string) => {
if (!c.platform) return;
const localOverridden = !!c.files.project.configLocal?.defaultTargets?.[c.platform];
const defaultTarget = c.runtime.target;
const actionLocalUpdate = `Update ${chalk().green('project')} default target for platform ${c.platform}`;
const actionGlobalUpdate = `Update ${chalk().green('global')}${
localOverridden ? ` and ${chalk().green('project')}` : ''
} default target for platform ${c.platform}`;
const actionNoUpdate = "Don't update";

const { chosenAction } = await inquirerPrompt({
message: 'What to do next?',
type: 'list',
name: 'chosenAction',
choices: [actionLocalUpdate, actionGlobalUpdate, actionNoUpdate],
warningMessage: `Your default target for platform ${c.platform} is ${
!defaultTarget ? 'not defined' : `set to ${defaultTarget}`
}. `,
});

c.runtime.target = currentTarget;

if (chosenAction === actionLocalUpdate || (chosenAction === actionGlobalUpdate && localOverridden)) {
const configLocal = c.files.project.configLocal || {};
if (!configLocal.defaultTargets) configLocal.defaultTargets = {};
configLocal.defaultTargets[c.platform] = currentTarget;

c.files.project.configLocal = configLocal;
writeFileSync(c.paths.project.configLocal, JSON.stringify(configLocal, null, 2));
}

if (chosenAction === actionGlobalUpdate) {
const configGlobal = c.files.workspace.config;
if (configGlobal) {
if (!configGlobal.defaultTargets) configGlobal.defaultTargets = {};
configGlobal.defaultTargets[c.platform] = currentTarget;

c.files.workspace.config = configGlobal;
writeFileSync(c.paths.workspace.config, JSON.stringify(configGlobal, null, 2));
}
}
};
30 changes: 2 additions & 28 deletions packages/sdk-webos/src/deviceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
getAppFolder,
getContext,
logError,
writeFileSync,
} from '@rnv/core';
import { WebosDevice } from './types';
import {
Expand All @@ -33,7 +32,7 @@ import {
CLI_WEBOS_ARES_DEVICE_INFO,
} from './constants';
import semver from 'semver';
import { isUrlLocalhost } from '@rnv/sdk-utils';
import { isUrlLocalhost, updateDefaultTargets } from '@rnv/sdk-utils';

export const launchWebOSimulator = async (target: string | boolean) => {
logTask('launchWebOSimulator', `${target}`);
Expand Down Expand Up @@ -175,7 +174,7 @@ const launchAppOnSimulator = async (c: RnvContext, appPath: string) => {
selectedSimulator = availableEmulatorVersions[0];
logInfo(`Found simulator ${chalk().bold.white(selectedSimulator)} in ${simulatorDirPath}`);
}
await _updateDefaultTargets(c, selectedSimulator);
await updateDefaultTargets(c, selectedSimulator);
}

const regex = /\d+(\.\d+)?/g;
Expand All @@ -194,31 +193,6 @@ const launchAppOnSimulator = async (c: RnvContext, appPath: string) => {
);
};

const _updateDefaultTargets = async (c: RnvContext, selectedSimulator: string) => {
const defaultTarget = c.runtime.target;
const { confirm } = await inquirerPrompt({
type: 'confirm',
name: 'confirm',
message: `Your default target for platform ${c.platform} is ${
!defaultTarget ? 'not defined' : `set to ${defaultTarget}`
}. Do you want to ${!defaultTarget ? 'set' : `update `} it to ${selectedSimulator} `,
});
if (!confirm) return;

const workspaceConfig = c.files.workspace.config;

if (workspaceConfig && c.platform) {
if (!workspaceConfig.defaultTargets) workspaceConfig.defaultTargets = {};

workspaceConfig.defaultTargets[c.platform] = selectedSimulator;

c.files.workspace.config = workspaceConfig;
writeFileSync(c.paths.workspace.config, workspaceConfig);
}
logInfo(
`Your default target for platform ${c.platform} has been updated successfully in ${c.paths.workspace.config}`
);
};
// Used for actual devices
const installAndLaunchApp = async (target: string, appPath: string, tId: string) => {
try {
Expand Down
Loading