optikit 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -2
- package/CLAUDE.md +239 -0
- package/CODE_QUALITY.md +398 -0
- package/ENHANCEMENTS.md +310 -0
- package/FEATURE_ENHANCEMENTS.md +435 -0
- package/README.md +46 -13
- package/SAFETY_FEATURES.md +396 -0
- package/USAGE.md +225 -41
- package/VERSION_MANAGEMENT.md +438 -0
- package/dist/cli.js +116 -7
- package/dist/commands/build/releases.js +57 -0
- package/dist/commands/buildReleases.js +48 -74
- package/dist/commands/clean/flutter.js +51 -0
- package/dist/commands/clean/ios.js +109 -0
- package/dist/commands/cleanProject.js +34 -4
- package/dist/commands/cleanProjectIos.js +17 -7
- package/dist/commands/config/init.js +54 -0
- package/dist/commands/config/rollback.js +161 -0
- package/dist/commands/generateModule.js +39 -11
- package/dist/commands/init.js +54 -0
- package/dist/commands/openProject.js +17 -0
- package/dist/commands/project/devices.js +188 -0
- package/dist/commands/project/generate.js +143 -0
- package/dist/commands/project/open.js +63 -0
- package/dist/commands/project/setup.js +46 -0
- package/dist/commands/rollback.js +161 -0
- package/dist/commands/setupVSCode.js +27 -21
- package/dist/commands/updateVersions.js +13 -1
- package/dist/commands/version/bump.js +161 -0
- package/dist/commands/version/update.js +91 -0
- package/dist/commands/version.js +161 -0
- package/dist/constants.js +131 -0
- package/dist/utils/backupHelpers.js +88 -0
- package/dist/utils/buildHelpers.js +55 -0
- package/dist/utils/commandHelpers.js +51 -0
- package/dist/utils/configHelpers.js +80 -0
- package/dist/utils/dryRunHelpers.js +103 -0
- package/dist/utils/fileHelpers.js +2 -1
- package/dist/utils/helpers/build.js +55 -0
- package/dist/utils/helpers/dryRun.js +103 -0
- package/dist/utils/helpers/file.js +24 -0
- package/dist/utils/helpers/string.js +3 -0
- package/dist/utils/helpers/version.js +80 -0
- package/dist/utils/services/backup.js +88 -0
- package/dist/utils/services/command.js +51 -0
- package/dist/utils/services/config.js +80 -0
- package/dist/utils/services/exec.js +132 -0
- package/dist/utils/services/logger.js +15 -0
- package/dist/utils/validationHelpers.js +101 -0
- package/dist/utils/validators/validation.js +101 -0
- package/dist/utils/versionHelpers.js +80 -0
- package/package.json +1 -1
- package/src/cli.ts +165 -7
- package/src/commands/build/releases.ts +79 -0
- package/src/commands/clean/flutter.ts +58 -0
- package/src/commands/{cleanProjectIos.ts → clean/ios.ts} +19 -10
- package/src/commands/config/init.ts +63 -0
- package/src/commands/config/rollback.ts +200 -0
- package/src/commands/project/devices.ts +246 -0
- package/src/commands/{generateModule.ts → project/generate.ts} +47 -17
- package/src/commands/{openProject.ts → project/open.ts} +26 -5
- package/src/commands/project/setup.ts +50 -0
- package/src/commands/version/bump.ts +202 -0
- package/src/commands/{updateVersions.ts → version/update.ts} +22 -6
- package/src/constants.ts +144 -0
- package/src/utils/helpers/build.ts +80 -0
- package/src/utils/helpers/dryRun.ts +124 -0
- package/src/utils/{fileHelpers.ts → helpers/file.ts} +3 -2
- package/src/utils/helpers/version.ts +109 -0
- package/src/utils/services/backup.ts +109 -0
- package/src/utils/services/command.ts +76 -0
- package/src/utils/services/config.ts +106 -0
- package/src/utils/{execHelpers.ts → services/exec.ts} +1 -1
- package/src/utils/validators/validation.ts +122 -0
- package/src/commands/buildReleases.ts +0 -102
- package/src/commands/cleanProject.ts +0 -25
- package/src/commands/setupVSCode.ts +0 -44
- /package/src/utils/{stringHelpers.ts → helpers/string.ts} +0 -0
- /package/src/utils/{loggerHelpers.ts → services/logger.ts} +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { execCommand } from "./execHelpers.js";
|
|
4
|
+
import { LoggerHelpers } from "./loggerHelpers.js";
|
|
5
|
+
export { validateFlutterProject, validateFlutterSdk, validateIosProject, validateAndroidProject, checkFileExists, };
|
|
6
|
+
/**
|
|
7
|
+
* Validates that the current directory is a Flutter project
|
|
8
|
+
* by checking for pubspec.yaml
|
|
9
|
+
*/
|
|
10
|
+
function validateFlutterProject() {
|
|
11
|
+
const pubspecPath = path.join(process.cwd(), "pubspec.yaml");
|
|
12
|
+
if (!fs.existsSync(pubspecPath)) {
|
|
13
|
+
LoggerHelpers.error("Not a Flutter project: pubspec.yaml not found.");
|
|
14
|
+
LoggerHelpers.info("Please run this command from the root of a Flutter project.");
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
// Check if pubspec.yaml contains Flutter SDK
|
|
18
|
+
const pubspecContent = fs.readFileSync(pubspecPath, "utf8");
|
|
19
|
+
if (!pubspecContent.includes("flutter:")) {
|
|
20
|
+
LoggerHelpers.error("Not a Flutter project: pubspec.yaml does not reference Flutter SDK.");
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validates that Flutter SDK is available (either via FVM or globally)
|
|
27
|
+
*/
|
|
28
|
+
async function validateFlutterSdk(useFvm = false) {
|
|
29
|
+
try {
|
|
30
|
+
if (useFvm) {
|
|
31
|
+
// Check if FVM directory exists
|
|
32
|
+
const fvmPath = path.join(process.cwd(), ".fvm", "flutter_sdk");
|
|
33
|
+
if (!fs.existsSync(fvmPath)) {
|
|
34
|
+
LoggerHelpers.error("FVM Flutter SDK not found at .fvm/flutter_sdk");
|
|
35
|
+
LoggerHelpers.info("Run 'fvm install' or use --disable-fvm flag.");
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
// Check if fvm command is available
|
|
39
|
+
await execCommand("fvm --version");
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Check if global Flutter is available
|
|
44
|
+
await execCommand("flutter --version");
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (useFvm) {
|
|
50
|
+
LoggerHelpers.error("FVM not found. Please install FVM or use --disable-fvm flag.");
|
|
51
|
+
LoggerHelpers.info("Install FVM: https://fvm.app/docs/getting_started/installation");
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
LoggerHelpers.error("Flutter SDK not found.");
|
|
55
|
+
LoggerHelpers.info("Install Flutter: https://flutter.dev/docs/get-started/install");
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Validates that the iOS project exists
|
|
62
|
+
*/
|
|
63
|
+
function validateIosProject() {
|
|
64
|
+
const iosPath = path.join(process.cwd(), "ios");
|
|
65
|
+
if (!fs.existsSync(iosPath)) {
|
|
66
|
+
LoggerHelpers.error("iOS project directory not found.");
|
|
67
|
+
LoggerHelpers.info("Run 'flutter create .' to add iOS support.");
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const xcodeProjPath = path.join(iosPath, "Runner.xcodeproj");
|
|
71
|
+
const xcworkspacePath = path.join(iosPath, "Runner.xcworkspace");
|
|
72
|
+
if (!fs.existsSync(xcodeProjPath) && !fs.existsSync(xcworkspacePath)) {
|
|
73
|
+
LoggerHelpers.error("No Xcode project or workspace found in ios/ directory.");
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Validates that the Android project exists
|
|
80
|
+
*/
|
|
81
|
+
function validateAndroidProject() {
|
|
82
|
+
const androidPath = path.join(process.cwd(), "android");
|
|
83
|
+
if (!fs.existsSync(androidPath)) {
|
|
84
|
+
LoggerHelpers.error("Android project directory not found.");
|
|
85
|
+
LoggerHelpers.info("Run 'flutter create .' to add Android support.");
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const buildGradlePath = path.join(androidPath, "build.gradle");
|
|
89
|
+
const buildGradleKtsPath = path.join(androidPath, "build.gradle.kts");
|
|
90
|
+
if (!fs.existsSync(buildGradlePath) && !fs.existsSync(buildGradleKtsPath)) {
|
|
91
|
+
LoggerHelpers.error("No build.gradle found in android/ directory.");
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Checks if a file exists at the given path
|
|
98
|
+
*/
|
|
99
|
+
function checkFileExists(filePath) {
|
|
100
|
+
return fs.existsSync(filePath);
|
|
101
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { execCommand } from "../services/exec.js";
|
|
4
|
+
import { LoggerHelpers } from "../services/logger.js";
|
|
5
|
+
export { validateFlutterProject, validateFlutterSdk, validateIosProject, validateAndroidProject, checkFileExists, };
|
|
6
|
+
/**
|
|
7
|
+
* Validates that the current directory is a Flutter project
|
|
8
|
+
* by checking for pubspec.yaml
|
|
9
|
+
*/
|
|
10
|
+
function validateFlutterProject() {
|
|
11
|
+
const pubspecPath = path.join(process.cwd(), "pubspec.yaml");
|
|
12
|
+
if (!fs.existsSync(pubspecPath)) {
|
|
13
|
+
LoggerHelpers.error("Not a Flutter project: pubspec.yaml not found.");
|
|
14
|
+
LoggerHelpers.info("Please run this command from the root of a Flutter project.");
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
// Check if pubspec.yaml contains Flutter SDK
|
|
18
|
+
const pubspecContent = fs.readFileSync(pubspecPath, "utf8");
|
|
19
|
+
if (!pubspecContent.includes("flutter:")) {
|
|
20
|
+
LoggerHelpers.error("Not a Flutter project: pubspec.yaml does not reference Flutter SDK.");
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validates that Flutter SDK is available (either via FVM or globally)
|
|
27
|
+
*/
|
|
28
|
+
async function validateFlutterSdk(useFvm = false) {
|
|
29
|
+
try {
|
|
30
|
+
if (useFvm) {
|
|
31
|
+
// Check if FVM directory exists
|
|
32
|
+
const fvmPath = path.join(process.cwd(), ".fvm", "flutter_sdk");
|
|
33
|
+
if (!fs.existsSync(fvmPath)) {
|
|
34
|
+
LoggerHelpers.error("FVM Flutter SDK not found at .fvm/flutter_sdk");
|
|
35
|
+
LoggerHelpers.info("Run 'fvm install' or use --disable-fvm flag.");
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
// Check if fvm command is available
|
|
39
|
+
await execCommand("fvm --version");
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Check if global Flutter is available
|
|
44
|
+
await execCommand("flutter --version");
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (useFvm) {
|
|
50
|
+
LoggerHelpers.error("FVM not found. Please install FVM or use --disable-fvm flag.");
|
|
51
|
+
LoggerHelpers.info("Install FVM: https://fvm.app/docs/getting_started/installation");
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
LoggerHelpers.error("Flutter SDK not found.");
|
|
55
|
+
LoggerHelpers.info("Install Flutter: https://flutter.dev/docs/get-started/install");
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Validates that the iOS project exists
|
|
62
|
+
*/
|
|
63
|
+
function validateIosProject() {
|
|
64
|
+
const iosPath = path.join(process.cwd(), "ios");
|
|
65
|
+
if (!fs.existsSync(iosPath)) {
|
|
66
|
+
LoggerHelpers.error("iOS project directory not found.");
|
|
67
|
+
LoggerHelpers.info("Run 'flutter create .' to add iOS support.");
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const xcodeProjPath = path.join(iosPath, "Runner.xcodeproj");
|
|
71
|
+
const xcworkspacePath = path.join(iosPath, "Runner.xcworkspace");
|
|
72
|
+
if (!fs.existsSync(xcodeProjPath) && !fs.existsSync(xcworkspacePath)) {
|
|
73
|
+
LoggerHelpers.error("No Xcode project or workspace found in ios/ directory.");
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Validates that the Android project exists
|
|
80
|
+
*/
|
|
81
|
+
function validateAndroidProject() {
|
|
82
|
+
const androidPath = path.join(process.cwd(), "android");
|
|
83
|
+
if (!fs.existsSync(androidPath)) {
|
|
84
|
+
LoggerHelpers.error("Android project directory not found.");
|
|
85
|
+
LoggerHelpers.info("Run 'flutter create .' to add Android support.");
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const buildGradlePath = path.join(androidPath, "build.gradle");
|
|
89
|
+
const buildGradleKtsPath = path.join(androidPath, "build.gradle.kts");
|
|
90
|
+
if (!fs.existsSync(buildGradlePath) && !fs.existsSync(buildGradleKtsPath)) {
|
|
91
|
+
LoggerHelpers.error("No build.gradle found in android/ directory.");
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Checks if a file exists at the given path
|
|
98
|
+
*/
|
|
99
|
+
function checkFileExists(filePath) {
|
|
100
|
+
return fs.existsSync(filePath);
|
|
101
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
export { parseVersion, incrementVersion, getCurrentVersion };
|
|
4
|
+
/**
|
|
5
|
+
* Parses a version string in format "X.Y.Z+B"
|
|
6
|
+
* @param versionString - Version string (e.g., "1.2.3+45")
|
|
7
|
+
* @returns Parsed version info
|
|
8
|
+
*/
|
|
9
|
+
function parseVersion(versionString) {
|
|
10
|
+
const match = versionString.match(/^(\d+)\.(\d+)\.(\d+)\+(\d+)$/);
|
|
11
|
+
if (!match) {
|
|
12
|
+
throw new Error(`Invalid version format: ${versionString}. Expected format: X.Y.Z+B (e.g., 1.2.3+45)`);
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
major: parseInt(match[1], 10),
|
|
16
|
+
minor: parseInt(match[2], 10),
|
|
17
|
+
patch: parseInt(match[3], 10),
|
|
18
|
+
buildNumber: parseInt(match[4], 10),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Increments version based on type
|
|
23
|
+
* @param current - Current version info
|
|
24
|
+
* @param type - Type of increment (major, minor, patch)
|
|
25
|
+
* @param resetIosBuildNumber - Whether to reset iOS build number to 1
|
|
26
|
+
* @returns New version info
|
|
27
|
+
*/
|
|
28
|
+
function incrementVersion(current, type, resetIosBuildNumber = false) {
|
|
29
|
+
const newVersion = { ...current };
|
|
30
|
+
switch (type) {
|
|
31
|
+
case 'major':
|
|
32
|
+
newVersion.major += 1;
|
|
33
|
+
newVersion.minor = 0;
|
|
34
|
+
newVersion.patch = 0;
|
|
35
|
+
newVersion.buildNumber += 1;
|
|
36
|
+
break;
|
|
37
|
+
case 'minor':
|
|
38
|
+
newVersion.minor += 1;
|
|
39
|
+
newVersion.patch = 0;
|
|
40
|
+
newVersion.buildNumber += 1;
|
|
41
|
+
break;
|
|
42
|
+
case 'patch':
|
|
43
|
+
newVersion.patch += 1;
|
|
44
|
+
newVersion.buildNumber += 1;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
return newVersion;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Gets current version from pubspec.yaml
|
|
51
|
+
* @returns Current version info
|
|
52
|
+
*/
|
|
53
|
+
function getCurrentVersion() {
|
|
54
|
+
const pubspecPath = path.join(process.cwd(), "pubspec.yaml");
|
|
55
|
+
if (!fs.existsSync(pubspecPath)) {
|
|
56
|
+
throw new Error("pubspec.yaml not found. Are you in a Flutter project?");
|
|
57
|
+
}
|
|
58
|
+
const pubspecContent = fs.readFileSync(pubspecPath, "utf8");
|
|
59
|
+
const versionMatch = pubspecContent.match(/version:\s*(\d+\.\d+\.\d+\+\d+)/);
|
|
60
|
+
if (!versionMatch) {
|
|
61
|
+
throw new Error("No version found in pubspec.yaml");
|
|
62
|
+
}
|
|
63
|
+
return parseVersion(versionMatch[1]);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Formats version info to string
|
|
67
|
+
* @param version - Version info
|
|
68
|
+
* @returns Formatted version string (e.g., "1.2.3+45")
|
|
69
|
+
*/
|
|
70
|
+
export function formatVersion(version) {
|
|
71
|
+
return `${version.major}.${version.minor}.${version.patch}+${version.buildNumber}`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Gets the next build number from current version
|
|
75
|
+
* @returns Next build number
|
|
76
|
+
*/
|
|
77
|
+
export function getNextBuildNumber() {
|
|
78
|
+
const current = getCurrentVersion();
|
|
79
|
+
return current.buildNumber + 1;
|
|
80
|
+
}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -4,20 +4,29 @@ import chalk from "chalk";
|
|
|
4
4
|
import boxen from "boxen";
|
|
5
5
|
import yargs from "yargs/yargs";
|
|
6
6
|
import { hideBin } from "yargs/helpers";
|
|
7
|
-
import { generateModule } from "./commands/
|
|
8
|
-
import { cleanProject } from "./commands/
|
|
9
|
-
import { cleanIosProject } from "./commands/
|
|
10
|
-
import { updateFlutterVersion } from "./commands/
|
|
7
|
+
import { generateModule } from "./commands/project/generate.js";
|
|
8
|
+
import { cleanProject } from "./commands/clean/flutter.js";
|
|
9
|
+
import { cleanIosProject } from "./commands/clean/ios.js";
|
|
10
|
+
import { updateFlutterVersion } from "./commands/version/update.js";
|
|
11
11
|
import {
|
|
12
12
|
buildFlutterApk,
|
|
13
13
|
buildFlutterBundle,
|
|
14
14
|
buildFlutterIos,
|
|
15
15
|
buildFlutterIpa,
|
|
16
|
-
} from "./commands/
|
|
16
|
+
} from "./commands/build/releases.js";
|
|
17
17
|
import { boxenOptions } from "./styles.js";
|
|
18
|
-
import { openIos, openAndroid } from "./commands/
|
|
18
|
+
import { openIos, openAndroid } from "./commands/project/open.js";
|
|
19
19
|
import { createRequire } from "module";
|
|
20
|
-
import { createVscodeSettings } from "./commands/
|
|
20
|
+
import { createVscodeSettings } from "./commands/project/setup.js";
|
|
21
|
+
import { initializeProject } from "./commands/config/init.js";
|
|
22
|
+
import { rollbackFiles, rollbackRestore } from "./commands/config/rollback.js";
|
|
23
|
+
import {
|
|
24
|
+
bumpVersion,
|
|
25
|
+
bumpIosBuildOnly,
|
|
26
|
+
bumpAndroidBuildOnly,
|
|
27
|
+
showCurrentVersion
|
|
28
|
+
} from "./commands/version/bump.js";
|
|
29
|
+
import { listDevices, runApp, runAppInteractive } from "./commands/project/devices.js";
|
|
21
30
|
const require = createRequire(import.meta.url);
|
|
22
31
|
const packageInfo: { version: string } = require("../package.json");
|
|
23
32
|
|
|
@@ -191,5 +200,154 @@ const options = yargs(hideBin(process.argv))
|
|
|
191
200
|
await createVscodeSettings();
|
|
192
201
|
}
|
|
193
202
|
)
|
|
203
|
+
.command(
|
|
204
|
+
"init",
|
|
205
|
+
"Initialize OptiKit configuration in the current project",
|
|
206
|
+
() => {},
|
|
207
|
+
async () => {
|
|
208
|
+
await initializeProject();
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
.command(
|
|
212
|
+
"rollback",
|
|
213
|
+
"List and restore files from OptiKit backups",
|
|
214
|
+
(yargs) => {
|
|
215
|
+
return yargs.option("restore", {
|
|
216
|
+
type: "number",
|
|
217
|
+
description: "Restore backup by index number",
|
|
218
|
+
demandOption: false,
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
async (argv) => {
|
|
222
|
+
const restoreIndex = argv.restore as number | undefined;
|
|
223
|
+
if (restoreIndex !== undefined) {
|
|
224
|
+
await rollbackRestore(restoreIndex);
|
|
225
|
+
} else {
|
|
226
|
+
await rollbackFiles();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
.command(
|
|
231
|
+
"version",
|
|
232
|
+
"Show current version information",
|
|
233
|
+
() => {},
|
|
234
|
+
async () => {
|
|
235
|
+
await showCurrentVersion();
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
.command(
|
|
239
|
+
"version bump <type>",
|
|
240
|
+
"Bump version (major, minor, or patch)",
|
|
241
|
+
(yargs) => {
|
|
242
|
+
return yargs.positional("type", {
|
|
243
|
+
describe: "Version bump type (major, minor, patch)",
|
|
244
|
+
type: "string",
|
|
245
|
+
choices: ["major", "minor", "patch"],
|
|
246
|
+
});
|
|
247
|
+
},
|
|
248
|
+
async (argv) => {
|
|
249
|
+
const type = argv.type as 'major' | 'minor' | 'patch';
|
|
250
|
+
await bumpVersion(type);
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
.command(
|
|
254
|
+
"version bump-ios",
|
|
255
|
+
"Increment iOS build number only (for TestFlight)",
|
|
256
|
+
() => {},
|
|
257
|
+
async () => {
|
|
258
|
+
await bumpIosBuildOnly();
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
.command(
|
|
262
|
+
"version bump-android",
|
|
263
|
+
"Increment Android build number only",
|
|
264
|
+
() => {},
|
|
265
|
+
async () => {
|
|
266
|
+
await bumpAndroidBuildOnly();
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
.command(
|
|
270
|
+
"devices",
|
|
271
|
+
"List all connected devices",
|
|
272
|
+
(yargs) => {
|
|
273
|
+
return yargs.option("disable-fvm", {
|
|
274
|
+
type: "boolean",
|
|
275
|
+
default: false,
|
|
276
|
+
description: "Run without FVM (use --disable-fvm to enable)",
|
|
277
|
+
});
|
|
278
|
+
},
|
|
279
|
+
async (argv) => {
|
|
280
|
+
const useFvm = !argv.disableFvm;
|
|
281
|
+
await listDevices(useFvm);
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
.command(
|
|
285
|
+
"run",
|
|
286
|
+
"Run Flutter app on connected device",
|
|
287
|
+
(yargs) => {
|
|
288
|
+
return yargs
|
|
289
|
+
.option("device", {
|
|
290
|
+
alias: "d",
|
|
291
|
+
type: "string",
|
|
292
|
+
description: "Specific device ID to run on",
|
|
293
|
+
})
|
|
294
|
+
.option("release", {
|
|
295
|
+
alias: "r",
|
|
296
|
+
type: "boolean",
|
|
297
|
+
default: false,
|
|
298
|
+
description: "Run in release mode",
|
|
299
|
+
})
|
|
300
|
+
.option("flavor", {
|
|
301
|
+
alias: "f",
|
|
302
|
+
type: "string",
|
|
303
|
+
description: "Build flavor to use",
|
|
304
|
+
})
|
|
305
|
+
.option("disable-fvm", {
|
|
306
|
+
type: "boolean",
|
|
307
|
+
default: false,
|
|
308
|
+
description: "Run without FVM (use --disable-fvm to enable)",
|
|
309
|
+
});
|
|
310
|
+
},
|
|
311
|
+
async (argv) => {
|
|
312
|
+
const useFvm = !argv.disableFvm;
|
|
313
|
+
await runApp({
|
|
314
|
+
device: argv.device as string | undefined,
|
|
315
|
+
release: argv.release as boolean,
|
|
316
|
+
flavor: argv.flavor as string | undefined,
|
|
317
|
+
useFvm,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
)
|
|
321
|
+
.command(
|
|
322
|
+
"run-select",
|
|
323
|
+
"Interactive device selection and run",
|
|
324
|
+
(yargs) => {
|
|
325
|
+
return yargs
|
|
326
|
+
.option("release", {
|
|
327
|
+
alias: "r",
|
|
328
|
+
type: "boolean",
|
|
329
|
+
default: false,
|
|
330
|
+
description: "Run in release mode",
|
|
331
|
+
})
|
|
332
|
+
.option("flavor", {
|
|
333
|
+
alias: "f",
|
|
334
|
+
type: "string",
|
|
335
|
+
description: "Build flavor to use",
|
|
336
|
+
})
|
|
337
|
+
.option("disable-fvm", {
|
|
338
|
+
type: "boolean",
|
|
339
|
+
default: false,
|
|
340
|
+
description: "Run without FVM (use --disable-fvm to enable)",
|
|
341
|
+
});
|
|
342
|
+
},
|
|
343
|
+
async (argv) => {
|
|
344
|
+
const useFvm = !argv.disableFvm;
|
|
345
|
+
await runAppInteractive({
|
|
346
|
+
release: argv.release as boolean,
|
|
347
|
+
flavor: argv.flavor as string | undefined,
|
|
348
|
+
useFvm,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
)
|
|
194
352
|
.version(version)
|
|
195
353
|
.help(true).argv;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { executeBuild } from "../../utils/helpers/build.js";
|
|
2
|
+
import { BUILD_CONFIGS } from "../../constants.js";
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
buildFlutterApk,
|
|
6
|
+
buildFlutterBundle,
|
|
7
|
+
buildFlutterIos,
|
|
8
|
+
buildFlutterIpa,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Builds Flutter APK with release configuration, obfuscation, and split debug info
|
|
13
|
+
* @param noFvm - Whether to disable FVM usage
|
|
14
|
+
*/
|
|
15
|
+
async function buildFlutterApk(noFvm: boolean) {
|
|
16
|
+
await executeBuild(
|
|
17
|
+
{
|
|
18
|
+
type: "APK",
|
|
19
|
+
command: "flutter build apk",
|
|
20
|
+
flags: [
|
|
21
|
+
...BUILD_CONFIGS.APK.flags,
|
|
22
|
+
`--split-debug-info=${BUILD_CONFIGS.APK.outputPath}`,
|
|
23
|
+
],
|
|
24
|
+
requireAndroid: true,
|
|
25
|
+
},
|
|
26
|
+
noFvm
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Builds Flutter App Bundle with release configuration, obfuscation, and split debug info
|
|
32
|
+
* @param noFvm - Whether to disable FVM usage
|
|
33
|
+
*/
|
|
34
|
+
async function buildFlutterBundle(noFvm: boolean) {
|
|
35
|
+
await executeBuild(
|
|
36
|
+
{
|
|
37
|
+
type: "Bundle",
|
|
38
|
+
command: "flutter build appbundle",
|
|
39
|
+
flags: [
|
|
40
|
+
...BUILD_CONFIGS.BUNDLE.flags,
|
|
41
|
+
`--split-debug-info=${BUILD_CONFIGS.BUNDLE.outputPath}`,
|
|
42
|
+
],
|
|
43
|
+
requireAndroid: true,
|
|
44
|
+
},
|
|
45
|
+
noFvm
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Builds Flutter iOS app with release configuration
|
|
51
|
+
* @param noFvm - Whether to disable FVM usage
|
|
52
|
+
*/
|
|
53
|
+
async function buildFlutterIos(noFvm: boolean) {
|
|
54
|
+
await executeBuild(
|
|
55
|
+
{
|
|
56
|
+
type: "iOS app",
|
|
57
|
+
command: "flutter build ios",
|
|
58
|
+
flags: [...BUILD_CONFIGS.IOS.flags],
|
|
59
|
+
requireIos: true,
|
|
60
|
+
},
|
|
61
|
+
noFvm
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Creates release IPA with updated build version number
|
|
67
|
+
* @param noFvm - Whether to disable FVM usage
|
|
68
|
+
*/
|
|
69
|
+
async function buildFlutterIpa(noFvm: boolean) {
|
|
70
|
+
await executeBuild(
|
|
71
|
+
{
|
|
72
|
+
type: "IPA",
|
|
73
|
+
command: "flutter build ipa",
|
|
74
|
+
flags: [...BUILD_CONFIGS.IPA.flags],
|
|
75
|
+
requireIos: true,
|
|
76
|
+
},
|
|
77
|
+
noFvm
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { execCommand } from "../../utils/services/exec.js";
|
|
4
|
+
import { LoggerHelpers } from "../../utils/services/logger.js";
|
|
5
|
+
import { validateFlutterProject, validateFlutterSdk } from "../../utils/validators/validation.js";
|
|
6
|
+
import { createBackup } from "../../utils/services/backup.js";
|
|
7
|
+
|
|
8
|
+
export { cleanProject };
|
|
9
|
+
|
|
10
|
+
async function cleanProject(noFvm: boolean) {
|
|
11
|
+
LoggerHelpers.info(
|
|
12
|
+
noFvm ? "Running clean without FVM..." : "Running clean with FVM..."
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
// Pre-flight validation
|
|
16
|
+
if (!validateFlutterProject()) {
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!(await validateFlutterSdk(!noFvm))) {
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// Step 1: Run flutter clean
|
|
26
|
+
const flutterCommand = noFvm ? "flutter clean" : "fvm flutter clean";
|
|
27
|
+
LoggerHelpers.info("Running Flutter clean...");
|
|
28
|
+
await execCommand(flutterCommand);
|
|
29
|
+
LoggerHelpers.success("Flutter clean completed.");
|
|
30
|
+
|
|
31
|
+
// Step 2: Remove pubspec.lock using Node.js fs (cross-platform)
|
|
32
|
+
const pubspecLockPath = path.join(process.cwd(), "pubspec.lock");
|
|
33
|
+
if (fs.existsSync(pubspecLockPath)) {
|
|
34
|
+
LoggerHelpers.info("Removing pubspec.lock...");
|
|
35
|
+
// Create backup before deletion
|
|
36
|
+
createBackup(pubspecLockPath);
|
|
37
|
+
fs.unlinkSync(pubspecLockPath);
|
|
38
|
+
LoggerHelpers.success("pubspec.lock removed.");
|
|
39
|
+
} else {
|
|
40
|
+
LoggerHelpers.info("pubspec.lock does not exist, skipping removal.");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Step 3: Run flutter pub get
|
|
44
|
+
const pubGetCommand = noFvm ? "flutter pub get" : "fvm flutter pub get";
|
|
45
|
+
LoggerHelpers.info("Running Flutter pub get...");
|
|
46
|
+
await execCommand(pubGetCommand);
|
|
47
|
+
LoggerHelpers.success("Flutter pub get completed.");
|
|
48
|
+
|
|
49
|
+
LoggerHelpers.success("Project cleaned successfully.");
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (error instanceof Error) {
|
|
52
|
+
LoggerHelpers.error(`Error during clean: ${error.message}`);
|
|
53
|
+
} else {
|
|
54
|
+
LoggerHelpers.error(`Error during clean: ${error}`);
|
|
55
|
+
}
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import { LoggerHelpers } from "
|
|
4
|
-
import { execInIos, execCommand, iosDirectory, execInIosWithRetry } from "
|
|
3
|
+
import { LoggerHelpers } from "../../utils/services/logger.js";
|
|
4
|
+
import { execInIos, execCommand, iosDirectory, execInIosWithRetry } from "../../utils/services/exec.js";
|
|
5
|
+
import { validateFlutterProject, validateIosProject } from "../../utils/validators/validation.js";
|
|
5
6
|
|
|
6
7
|
export { cleanIosProject };
|
|
7
8
|
|
|
@@ -61,21 +62,28 @@ async function ensureFlutterArtifactsExist() {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
async function cleanIosProject(cleanCache: boolean, repoUpdate: boolean) {
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
// Pre-flight validation
|
|
66
|
+
if (!validateFlutterProject()) {
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
70
|
+
if (!validateIosProject()) {
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
71
73
|
|
|
74
|
+
try {
|
|
72
75
|
LoggerHelpers.info("Running clean for iOS project...");
|
|
73
76
|
|
|
74
77
|
await ensureFlutterArtifactsExist();
|
|
75
78
|
|
|
76
79
|
LoggerHelpers.info("Removing Podfile.lock...");
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
const podfileLockPath = path.join(iosDirectory, "Podfile.lock");
|
|
81
|
+
if (fs.existsSync(podfileLockPath)) {
|
|
82
|
+
fs.unlinkSync(podfileLockPath);
|
|
83
|
+
LoggerHelpers.success("Removed Podfile.lock.");
|
|
84
|
+
} else {
|
|
85
|
+
LoggerHelpers.info("Podfile.lock does not exist, skipping removal.");
|
|
86
|
+
}
|
|
79
87
|
|
|
80
88
|
LoggerHelpers.info("Deintegrating pods...");
|
|
81
89
|
await execInIos("pod deintegrate");
|
|
@@ -116,5 +124,6 @@ async function cleanIosProject(cleanCache: boolean, repoUpdate: boolean) {
|
|
|
116
124
|
} else {
|
|
117
125
|
LoggerHelpers.error(`Unknown error: ${JSON.stringify(error)}`);
|
|
118
126
|
}
|
|
127
|
+
process.exit(1);
|
|
119
128
|
}
|
|
120
129
|
}
|