optikit 1.1.1 → 1.2.1
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 +39 -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 +264 -41
- package/VERSION_MANAGEMENT.md +438 -0
- package/dist/cli.js +127 -8
- 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 +163 -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 +191 -8
- 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/project/open.ts +193 -0
- 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/openProject.ts +0 -51
- 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,193 @@
|
|
|
1
|
+
import { LoggerHelpers } from "../../utils/services/logger.js";
|
|
2
|
+
import { execCommand } from "../../utils/services/exec.js";
|
|
3
|
+
import { platform } from "os";
|
|
4
|
+
import { validateFlutterProject, validateIosProject, validateAndroidProject } from "../../utils/validators/validation.js";
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
|
|
8
|
+
export { openIos, openAndroid, openIpaOutput, openBundleOutput, openApkOutput };
|
|
9
|
+
|
|
10
|
+
async function openIos() {
|
|
11
|
+
LoggerHelpers.info("Opening the iOS project in Xcode...");
|
|
12
|
+
|
|
13
|
+
// Pre-flight validation
|
|
14
|
+
if (!validateFlutterProject()) {
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!validateIosProject()) {
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const command = "open ios/Runner.xcworkspace";
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
await execCommand(command);
|
|
26
|
+
LoggerHelpers.success("Xcode opened successfully.");
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error instanceof Error) {
|
|
29
|
+
LoggerHelpers.error(`Error while opening Xcode: ${error.message}`);
|
|
30
|
+
} else {
|
|
31
|
+
LoggerHelpers.error(`Error while opening Xcode: ${error}`);
|
|
32
|
+
}
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function openAndroid() {
|
|
38
|
+
LoggerHelpers.info("Opening the Android project in Android Studio...");
|
|
39
|
+
|
|
40
|
+
// Pre-flight validation
|
|
41
|
+
if (!validateFlutterProject()) {
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!validateAndroidProject()) {
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const osPlatform = platform();
|
|
50
|
+
let command;
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if (osPlatform === "win32") {
|
|
54
|
+
command = "start android";
|
|
55
|
+
} else if (osPlatform === "darwin") {
|
|
56
|
+
command = "open -a 'Android Studio' android";
|
|
57
|
+
} else {
|
|
58
|
+
command = "xdg-open android";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
await execCommand(command);
|
|
63
|
+
LoggerHelpers.success("Android Studio opened successfully.");
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (error instanceof Error) {
|
|
66
|
+
LoggerHelpers.error(
|
|
67
|
+
`Error while opening Android Studio: ${error.message}`
|
|
68
|
+
);
|
|
69
|
+
} else {
|
|
70
|
+
LoggerHelpers.error(`Error while opening Android Studio: ${error}`);
|
|
71
|
+
}
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Opens the IPA build output directory in Finder (macOS) or File Explorer (Windows/Linux)
|
|
78
|
+
*/
|
|
79
|
+
async function openIpaOutput(): Promise<void> {
|
|
80
|
+
if (!validateFlutterProject()) {
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const ipaPath = path.join(process.cwd(), "build", "ios", "ipa");
|
|
85
|
+
|
|
86
|
+
if (!fs.existsSync(ipaPath)) {
|
|
87
|
+
LoggerHelpers.warning(`IPA output directory not found: ${ipaPath}`);
|
|
88
|
+
LoggerHelpers.info("Build an IPA first using: optikit flutter-build-ipa");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const openCommand = getOpenCommand();
|
|
94
|
+
await execCommand(`${openCommand} "${ipaPath}"`);
|
|
95
|
+
LoggerHelpers.success(`Opened IPA output directory`);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (error instanceof Error) {
|
|
98
|
+
LoggerHelpers.error(`Error opening IPA directory: ${error.message}`);
|
|
99
|
+
} else {
|
|
100
|
+
LoggerHelpers.error(`Error opening IPA directory: ${error}`);
|
|
101
|
+
}
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Opens the Android App Bundle output directory in Finder (macOS) or File Explorer (Windows/Linux)
|
|
108
|
+
*/
|
|
109
|
+
async function openBundleOutput(): Promise<void> {
|
|
110
|
+
if (!validateFlutterProject()) {
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const bundlePath = path.join(
|
|
115
|
+
process.cwd(),
|
|
116
|
+
"build",
|
|
117
|
+
"app",
|
|
118
|
+
"outputs",
|
|
119
|
+
"bundle",
|
|
120
|
+
"release"
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (!fs.existsSync(bundlePath)) {
|
|
124
|
+
LoggerHelpers.warning(`Bundle output directory not found: ${bundlePath}`);
|
|
125
|
+
LoggerHelpers.info("Build a bundle first using: optikit flutter-build-bundle");
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const openCommand = getOpenCommand();
|
|
131
|
+
await execCommand(`${openCommand} "${bundlePath}"`);
|
|
132
|
+
LoggerHelpers.success(`Opened Bundle output directory`);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (error instanceof Error) {
|
|
135
|
+
LoggerHelpers.error(`Error opening Bundle directory: ${error.message}`);
|
|
136
|
+
} else {
|
|
137
|
+
LoggerHelpers.error(`Error opening Bundle directory: ${error}`);
|
|
138
|
+
}
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Opens the APK build output directory in Finder (macOS) or File Explorer (Windows/Linux)
|
|
145
|
+
*/
|
|
146
|
+
async function openApkOutput(): Promise<void> {
|
|
147
|
+
if (!validateFlutterProject()) {
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const apkPath = path.join(
|
|
152
|
+
process.cwd(),
|
|
153
|
+
"build",
|
|
154
|
+
"app",
|
|
155
|
+
"outputs",
|
|
156
|
+
"flutter-apk"
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (!fs.existsSync(apkPath)) {
|
|
160
|
+
LoggerHelpers.warning(`APK output directory not found: ${apkPath}`);
|
|
161
|
+
LoggerHelpers.info("Build an APK first using: optikit flutter-build-apk");
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const openCommand = getOpenCommand();
|
|
167
|
+
await execCommand(`${openCommand} "${apkPath}"`);
|
|
168
|
+
LoggerHelpers.success(`Opened APK output directory`);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (error instanceof Error) {
|
|
171
|
+
LoggerHelpers.error(`Error opening APK directory: ${error.message}`);
|
|
172
|
+
} else {
|
|
173
|
+
LoggerHelpers.error(`Error opening APK directory: ${error}`);
|
|
174
|
+
}
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Returns the appropriate command to open a directory based on the platform
|
|
181
|
+
*/
|
|
182
|
+
function getOpenCommand(): string {
|
|
183
|
+
const osPlatform = platform();
|
|
184
|
+
|
|
185
|
+
switch (osPlatform) {
|
|
186
|
+
case "darwin": // macOS
|
|
187
|
+
return "open";
|
|
188
|
+
case "win32": // Windows
|
|
189
|
+
return "start";
|
|
190
|
+
default: // Linux and others
|
|
191
|
+
return "xdg-open";
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { LoggerHelpers } from "../../utils/services/logger.js";
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
createVscodeSettings,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates a .vscode directory (if it doesn't exist) and writes a settings.json file
|
|
11
|
+
* with recommended Flutter configuration. The Flutter SDK path is set to ".fvm/flutter_sdk".
|
|
12
|
+
*/
|
|
13
|
+
async function createVscodeSettings(){
|
|
14
|
+
try {
|
|
15
|
+
const vscodeDir = path.join(process.cwd(), ".vscode");
|
|
16
|
+
const settingsPath = path.join(vscodeDir, "settings.json");
|
|
17
|
+
|
|
18
|
+
// Create the .vscode folder using Node.js fs (cross-platform)
|
|
19
|
+
if (!fs.existsSync(vscodeDir)) {
|
|
20
|
+
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
21
|
+
LoggerHelpers.success("Created .vscode directory.");
|
|
22
|
+
} else {
|
|
23
|
+
LoggerHelpers.info(".vscode directory already exists.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Define the settings object
|
|
27
|
+
const settings = {
|
|
28
|
+
"dart.flutterSdkPath": ".fvm/flutter_sdk",
|
|
29
|
+
"editor.formatOnSave": true,
|
|
30
|
+
"dart.previewFlutterUiGuides": true,
|
|
31
|
+
"files.exclude": {
|
|
32
|
+
"**/.git": true,
|
|
33
|
+
"**/.DS_Store": true,
|
|
34
|
+
"**/node_modules": true,
|
|
35
|
+
"**/build": true
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Write settings.json using Node.js fs
|
|
40
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf8");
|
|
41
|
+
LoggerHelpers.success("Created .vscode/settings.json with Flutter configuration.");
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error instanceof Error) {
|
|
44
|
+
LoggerHelpers.error(`Error while creating VSCode settings: ${error.message}`);
|
|
45
|
+
} else {
|
|
46
|
+
LoggerHelpers.error(`Error while creating VSCode settings: ${error}`);
|
|
47
|
+
}
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { validateFlutterProject } from "../../utils/validators/validation.js";
|
|
2
|
+
import { LoggerHelpers } from "../../utils/services/logger.js";
|
|
3
|
+
import {
|
|
4
|
+
getCurrentVersion,
|
|
5
|
+
incrementVersion,
|
|
6
|
+
formatVersion,
|
|
7
|
+
getNextBuildNumber
|
|
8
|
+
} from "../../utils/helpers/version.js";
|
|
9
|
+
import { updateFlutterVersion } from "./update.js";
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
bumpVersion,
|
|
14
|
+
bumpIosBuildOnly,
|
|
15
|
+
bumpAndroidBuildOnly,
|
|
16
|
+
showCurrentVersion
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Bumps version with semantic versioning (major, minor, patch)
|
|
21
|
+
* Android build number increments with version
|
|
22
|
+
* iOS build number resets to 1 on version change
|
|
23
|
+
*
|
|
24
|
+
* @param type - Type of version bump (major, minor, patch)
|
|
25
|
+
*/
|
|
26
|
+
async function bumpVersion(
|
|
27
|
+
type: 'major' | 'minor' | 'patch'
|
|
28
|
+
): Promise<void> {
|
|
29
|
+
// Pre-flight validation
|
|
30
|
+
if (!validateFlutterProject()) {
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const current = getCurrentVersion();
|
|
36
|
+
const newVersion = incrementVersion(current, type);
|
|
37
|
+
|
|
38
|
+
LoggerHelpers.info(`Current version: ${formatVersion(current)}`);
|
|
39
|
+
LoggerHelpers.info(`Bumping ${type} version...`);
|
|
40
|
+
|
|
41
|
+
console.log(chalk.cyan("\nVersion changes:"));
|
|
42
|
+
console.log(chalk.gray(" Old:"), chalk.white(formatVersion(current)));
|
|
43
|
+
console.log(chalk.gray(" New:"), chalk.green.bold(formatVersion(newVersion)));
|
|
44
|
+
|
|
45
|
+
console.log(chalk.cyan("\nBuild number strategy:"));
|
|
46
|
+
console.log(chalk.gray(" Android:"), chalk.white(`${current.buildNumber} → ${newVersion.buildNumber} (incremented)`));
|
|
47
|
+
console.log(chalk.gray(" iOS:"), chalk.white(`${current.buildNumber} → 1 (reset for new version)`));
|
|
48
|
+
console.log();
|
|
49
|
+
|
|
50
|
+
// Update with new version
|
|
51
|
+
// Android uses the incremented build number from version
|
|
52
|
+
// iOS gets reset to 1 for new version releases
|
|
53
|
+
await updateFlutterVersion(
|
|
54
|
+
`${newVersion.major}.${newVersion.minor}.${newVersion.patch}`,
|
|
55
|
+
newVersion.buildNumber.toString(),
|
|
56
|
+
"1" // iOS always starts at 1 for new versions
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
LoggerHelpers.success(`Version bumped to ${formatVersion(newVersion)}`);
|
|
60
|
+
console.log(chalk.gray("\nAndroid build:"), chalk.white(newVersion.buildNumber));
|
|
61
|
+
console.log(chalk.gray("iOS build:"), chalk.white("1"));
|
|
62
|
+
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error instanceof Error) {
|
|
65
|
+
LoggerHelpers.error(`Error bumping version: ${error.message}`);
|
|
66
|
+
} else {
|
|
67
|
+
LoggerHelpers.error(`Error bumping version: ${error}`);
|
|
68
|
+
}
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Increments ONLY iOS build number (for TestFlight builds)
|
|
75
|
+
* Keeps version and Android build number unchanged
|
|
76
|
+
* Perfect for uploading new iOS builds without changing app version
|
|
77
|
+
*
|
|
78
|
+
* Example: 1.0.2+45 (iOS: 45) → 1.0.2+45 (iOS: 46)
|
|
79
|
+
*/
|
|
80
|
+
async function bumpIosBuildOnly(): Promise<void> {
|
|
81
|
+
// Pre-flight validation
|
|
82
|
+
if (!validateFlutterProject()) {
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const current = getCurrentVersion();
|
|
88
|
+
const currentVersionString = `${current.major}.${current.minor}.${current.patch}`;
|
|
89
|
+
|
|
90
|
+
// iOS build number increments from current Android build number
|
|
91
|
+
const nextIosBuild = current.buildNumber + 1;
|
|
92
|
+
|
|
93
|
+
LoggerHelpers.info(`Current version: ${formatVersion(current)}`);
|
|
94
|
+
LoggerHelpers.info("Incrementing iOS build number only (for TestFlight)...");
|
|
95
|
+
|
|
96
|
+
console.log(chalk.cyan("\nBuild number changes:"));
|
|
97
|
+
console.log(chalk.gray(" Version:"), chalk.white(`${currentVersionString} (unchanged)`));
|
|
98
|
+
console.log(chalk.gray(" Android:"), chalk.white(`${current.buildNumber} (unchanged)`));
|
|
99
|
+
console.log(chalk.gray(" iOS:"), chalk.white(`${current.buildNumber} → ${nextIosBuild}`), chalk.green("(incremented)"));
|
|
100
|
+
console.log();
|
|
101
|
+
|
|
102
|
+
// Update only iOS build number
|
|
103
|
+
await updateFlutterVersion(
|
|
104
|
+
currentVersionString,
|
|
105
|
+
"", // Empty string means don't update Android
|
|
106
|
+
nextIosBuild.toString()
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
LoggerHelpers.success(`iOS build number incremented to ${nextIosBuild}`);
|
|
110
|
+
console.log(chalk.gray("\nResult:"), chalk.white(`${currentVersionString}+${current.buildNumber} (iOS: ${nextIosBuild})`));
|
|
111
|
+
console.log(chalk.gray("Use this for:"), chalk.white("TestFlight uploads without version changes"));
|
|
112
|
+
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (error instanceof Error) {
|
|
115
|
+
LoggerHelpers.error(`Error incrementing iOS build: ${error.message}`);
|
|
116
|
+
} else {
|
|
117
|
+
LoggerHelpers.error(`Error incrementing iOS build: ${error}`);
|
|
118
|
+
}
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Increments ONLY Android build number
|
|
125
|
+
* Keeps version and iOS build number unchanged
|
|
126
|
+
*/
|
|
127
|
+
async function bumpAndroidBuildOnly(): Promise<void> {
|
|
128
|
+
// Pre-flight validation
|
|
129
|
+
if (!validateFlutterProject()) {
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const current = getCurrentVersion();
|
|
135
|
+
const currentVersionString = `${current.major}.${current.minor}.${current.patch}`;
|
|
136
|
+
const nextAndroidBuild = current.buildNumber + 1;
|
|
137
|
+
|
|
138
|
+
LoggerHelpers.info(`Current version: ${formatVersion(current)}`);
|
|
139
|
+
LoggerHelpers.info("Incrementing Android build number only...");
|
|
140
|
+
|
|
141
|
+
console.log(chalk.cyan("\nBuild number changes:"));
|
|
142
|
+
console.log(chalk.gray(" Version:"), chalk.white(`${currentVersionString} (unchanged)`));
|
|
143
|
+
console.log(chalk.gray(" Android:"), chalk.white(`${current.buildNumber} → ${nextAndroidBuild}`), chalk.green("(incremented)"));
|
|
144
|
+
console.log(chalk.gray(" iOS:"), chalk.white("(unchanged)"));
|
|
145
|
+
console.log();
|
|
146
|
+
|
|
147
|
+
// Update only Android build number
|
|
148
|
+
await updateFlutterVersion(
|
|
149
|
+
currentVersionString,
|
|
150
|
+
nextAndroidBuild.toString(),
|
|
151
|
+
"" // Empty string means don't update iOS
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
LoggerHelpers.success(`Android build number incremented to ${nextAndroidBuild}`);
|
|
155
|
+
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if (error instanceof Error) {
|
|
158
|
+
LoggerHelpers.error(`Error incrementing Android build: ${error.message}`);
|
|
159
|
+
} else {
|
|
160
|
+
LoggerHelpers.error(`Error incrementing Android build: ${error}`);
|
|
161
|
+
}
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Shows current version information
|
|
168
|
+
*/
|
|
169
|
+
async function showCurrentVersion(): Promise<void> {
|
|
170
|
+
try {
|
|
171
|
+
const current = getCurrentVersion();
|
|
172
|
+
const versionString = formatVersion(current);
|
|
173
|
+
|
|
174
|
+
console.log(chalk.bold("\n📱 Current Version Information\n"));
|
|
175
|
+
console.log(chalk.cyan("Version:"), chalk.white.bold(versionString));
|
|
176
|
+
console.log(chalk.gray(" Major:"), chalk.white(current.major));
|
|
177
|
+
console.log(chalk.gray(" Minor:"), chalk.white(current.minor));
|
|
178
|
+
console.log(chalk.gray(" Patch:"), chalk.white(current.patch));
|
|
179
|
+
console.log(chalk.gray(" Build:"), chalk.white(current.buildNumber));
|
|
180
|
+
console.log();
|
|
181
|
+
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (error instanceof Error) {
|
|
184
|
+
LoggerHelpers.error(`Error reading version: ${error.message}`);
|
|
185
|
+
} else {
|
|
186
|
+
LoggerHelpers.error(`Error reading version: ${error}`);
|
|
187
|
+
}
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Helper to get next iOS build number
|
|
194
|
+
* In the future, this could read from Info.plist or project.pbxproj
|
|
195
|
+
* For now, we'll use a simple increment
|
|
196
|
+
*/
|
|
197
|
+
async function getNextIosBuildNumber(): Promise<number> {
|
|
198
|
+
// TODO: Read actual iOS build number from Info.plist or project.pbxproj
|
|
199
|
+
// For now, we'll just increment based on timestamp or simple counter
|
|
200
|
+
const current = getCurrentVersion();
|
|
201
|
+
return current.buildNumber + 1;
|
|
202
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { execInIos } from "
|
|
4
|
-
import { LoggerHelpers } from "
|
|
3
|
+
import { execInIos } from "../../utils/services/exec.js";
|
|
4
|
+
import { LoggerHelpers } from "../../utils/services/logger.js";
|
|
5
|
+
import { validateFlutterProject } from "../../utils/validators/validation.js";
|
|
6
|
+
import { createBackup } from "../../utils/services/backup.js";
|
|
5
7
|
|
|
6
8
|
const currentDir = process.cwd();
|
|
7
9
|
|
|
@@ -12,6 +14,11 @@ async function updateFlutterVersion(
|
|
|
12
14
|
build: string,
|
|
13
15
|
iosBuild: string
|
|
14
16
|
) {
|
|
17
|
+
// Pre-flight validation
|
|
18
|
+
if (!validateFlutterProject()) {
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
try {
|
|
16
23
|
// Always update the version since it is required.
|
|
17
24
|
LoggerHelpers.info(`Starting update for version ${version}...`);
|
|
@@ -43,7 +50,7 @@ async function updateFlutterVersion(
|
|
|
43
50
|
error instanceof Error ? error.message : String(error)
|
|
44
51
|
}`
|
|
45
52
|
);
|
|
46
|
-
|
|
53
|
+
process.exit(1);
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
56
|
|
|
@@ -55,6 +62,9 @@ async function updatePubspecVersionAndBuild(version: string, build: string) {
|
|
|
55
62
|
throw new Error(`pubspec.yaml not found at ${pubspecPath}`);
|
|
56
63
|
}
|
|
57
64
|
|
|
65
|
+
// Create backup before modification
|
|
66
|
+
createBackup(pubspecPath);
|
|
67
|
+
|
|
58
68
|
const pubspecContent = fs.readFileSync(pubspecPath, "utf8");
|
|
59
69
|
|
|
60
70
|
const updatedPubspec = pubspecContent.replace(
|
|
@@ -80,13 +90,16 @@ async function updatePubspecVersionAndBuild(version: string, build: string) {
|
|
|
80
90
|
async function updateIosVersionAndBuild(version: string, iosBuild: string) {
|
|
81
91
|
try {
|
|
82
92
|
const currentDir = process.cwd();
|
|
83
|
-
|
|
93
|
+
|
|
84
94
|
const projectPbxProjPath = path.join(currentDir, "ios/Runner.xcodeproj/project.pbxproj");
|
|
85
|
-
|
|
95
|
+
|
|
86
96
|
if (!fs.existsSync(projectPbxProjPath)) {
|
|
87
97
|
throw new Error(`project.pbxproj not found at ${projectPbxProjPath}`);
|
|
88
98
|
}
|
|
89
|
-
|
|
99
|
+
|
|
100
|
+
// Create backup before modification
|
|
101
|
+
createBackup(projectPbxProjPath);
|
|
102
|
+
|
|
90
103
|
let projectContent = fs.readFileSync(projectPbxProjPath, 'utf8');
|
|
91
104
|
|
|
92
105
|
projectContent = projectContent
|
|
@@ -109,6 +122,9 @@ async function updateIosVersionAndBuild(version: string, iosBuild: string) {
|
|
|
109
122
|
throw new Error(`Info.plist not found at ${infoPlistPath}`);
|
|
110
123
|
}
|
|
111
124
|
|
|
125
|
+
// Create backup before modification
|
|
126
|
+
createBackup(infoPlistPath);
|
|
127
|
+
|
|
112
128
|
const infoPlistContent = fs.readFileSync(infoPlistPath, "utf8");
|
|
113
129
|
|
|
114
130
|
const updatedPlist = infoPlistContent
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application-wide constants
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Build configurations
|
|
6
|
+
export const BUILD_CONFIGS = {
|
|
7
|
+
APK: {
|
|
8
|
+
outputPath: "build/app/outputs/symbols",
|
|
9
|
+
flags: ["--release", "--obfuscate"],
|
|
10
|
+
},
|
|
11
|
+
BUNDLE: {
|
|
12
|
+
outputPath: "build/app/outputs/symbols",
|
|
13
|
+
flags: ["--release", "--obfuscate"],
|
|
14
|
+
},
|
|
15
|
+
IOS: {
|
|
16
|
+
flags: ["--release"],
|
|
17
|
+
},
|
|
18
|
+
IPA: {
|
|
19
|
+
flags: ["--release"],
|
|
20
|
+
},
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
23
|
+
// Project structure
|
|
24
|
+
export const PROJECT_PATHS = {
|
|
25
|
+
PUBSPEC: "pubspec.yaml",
|
|
26
|
+
PUBSPEC_LOCK: "pubspec.lock",
|
|
27
|
+
IOS_DIR: "ios",
|
|
28
|
+
ANDROID_DIR: "android",
|
|
29
|
+
LIB_DIR: "lib",
|
|
30
|
+
MODULE_DIR: "lib/module",
|
|
31
|
+
IOS_RUNNER_PROJ: "ios/Runner.xcodeproj",
|
|
32
|
+
IOS_RUNNER_WORKSPACE: "ios/Runner.xcworkspace",
|
|
33
|
+
IOS_PROJECT_PBXPROJ: "ios/Runner.xcodeproj/project.pbxproj",
|
|
34
|
+
IOS_INFO_PLIST: "ios/Runner/Info.plist",
|
|
35
|
+
IOS_PODFILE_LOCK: "ios/Podfile.lock",
|
|
36
|
+
ANDROID_BUILD_GRADLE: "android/build.gradle",
|
|
37
|
+
ANDROID_BUILD_GRADLE_KTS: "android/build.gradle.kts",
|
|
38
|
+
FVM_FLUTTER_SDK: ".fvm/flutter_sdk",
|
|
39
|
+
VSCODE_DIR: ".vscode",
|
|
40
|
+
VSCODE_SETTINGS: ".vscode/settings.json",
|
|
41
|
+
} as const;
|
|
42
|
+
|
|
43
|
+
// Backup configuration
|
|
44
|
+
export const BACKUP_CONFIG = {
|
|
45
|
+
DIR_NAME: ".optikit-backup",
|
|
46
|
+
RETENTION_COUNT: 5,
|
|
47
|
+
} as const;
|
|
48
|
+
|
|
49
|
+
// Module generation
|
|
50
|
+
export const MODULE_STRUCTURE = {
|
|
51
|
+
DIRECTORIES: ["bloc", "event", "state", "screen", "import", "factory"],
|
|
52
|
+
NAME_PATTERN: /^[a-z0-9_]+$/,
|
|
53
|
+
} as const;
|
|
54
|
+
|
|
55
|
+
// Flutter commands
|
|
56
|
+
export const FLUTTER_COMMANDS = {
|
|
57
|
+
CLEAN: "flutter clean",
|
|
58
|
+
PUB_GET: "flutter pub get",
|
|
59
|
+
BUILD_APK: "flutter build apk",
|
|
60
|
+
BUILD_BUNDLE: "flutter build appbundle",
|
|
61
|
+
BUILD_IOS: "flutter build ios",
|
|
62
|
+
BUILD_IPA: "flutter build ipa",
|
|
63
|
+
PRECACHE_IOS: "flutter precache --ios",
|
|
64
|
+
VERSION: "flutter --version",
|
|
65
|
+
} as const;
|
|
66
|
+
|
|
67
|
+
// FVM commands
|
|
68
|
+
export const FVM_COMMANDS = {
|
|
69
|
+
CLEAN: "fvm flutter clean",
|
|
70
|
+
PUB_GET: "fvm flutter pub get",
|
|
71
|
+
BUILD_APK: "fvm flutter build apk",
|
|
72
|
+
BUILD_BUNDLE: "fvm flutter build appbundle",
|
|
73
|
+
BUILD_IOS: "fvm flutter build ios",
|
|
74
|
+
BUILD_IPA: "fvm flutter build ipa",
|
|
75
|
+
PRECACHE_IOS: "fvm flutter precache --ios",
|
|
76
|
+
VERSION: "fvm --version",
|
|
77
|
+
} as const;
|
|
78
|
+
|
|
79
|
+
// iOS commands
|
|
80
|
+
export const IOS_COMMANDS = {
|
|
81
|
+
POD_DEINTEGRATE: "pod deintegrate",
|
|
82
|
+
POD_INSTALL: "pod install",
|
|
83
|
+
POD_UPDATE: "pod update",
|
|
84
|
+
POD_REPO_UPDATE: "pod repo update",
|
|
85
|
+
POD_CACHE_CLEAN: "pod cache clean --all",
|
|
86
|
+
} as const;
|
|
87
|
+
|
|
88
|
+
// IDE commands
|
|
89
|
+
export const IDE_COMMANDS = {
|
|
90
|
+
XCODE: "open ios/Runner.xcworkspace",
|
|
91
|
+
ANDROID_STUDIO: {
|
|
92
|
+
DARWIN: "open -a 'Android Studio' android",
|
|
93
|
+
WIN32: "start android",
|
|
94
|
+
LINUX: "xdg-open android",
|
|
95
|
+
},
|
|
96
|
+
} as const;
|
|
97
|
+
|
|
98
|
+
// VSCode settings template
|
|
99
|
+
export const VSCODE_SETTINGS_TEMPLATE = {
|
|
100
|
+
"dart.flutterSdkPath": ".fvm/flutter_sdk",
|
|
101
|
+
"editor.formatOnSave": true,
|
|
102
|
+
"dart.previewFlutterUiGuides": true,
|
|
103
|
+
"files.exclude": {
|
|
104
|
+
"**/.git": true,
|
|
105
|
+
"**/.DS_Store": true,
|
|
106
|
+
"**/node_modules": true,
|
|
107
|
+
"**/build": true,
|
|
108
|
+
},
|
|
109
|
+
} as const;
|
|
110
|
+
|
|
111
|
+
// Retry configuration
|
|
112
|
+
export const RETRY_CONFIG = {
|
|
113
|
+
DEFAULT_ATTEMPTS: 3,
|
|
114
|
+
DEFAULT_DELAY_MS: 10000,
|
|
115
|
+
IOS_TIMEOUT_MS: 600000,
|
|
116
|
+
} as const;
|
|
117
|
+
|
|
118
|
+
// Help URLs
|
|
119
|
+
export const HELP_URLS = {
|
|
120
|
+
FLUTTER_INSTALL: "https://flutter.dev/docs/get-started/install",
|
|
121
|
+
FVM_INSTALL: "https://fvm.app/docs/getting_started/installation",
|
|
122
|
+
} as const;
|
|
123
|
+
|
|
124
|
+
// Error messages
|
|
125
|
+
export const ERROR_MESSAGES = {
|
|
126
|
+
NOT_FLUTTER_PROJECT: "Not a Flutter project: pubspec.yaml not found.",
|
|
127
|
+
NO_FLUTTER_REFERENCE: "Not a Flutter project: pubspec.yaml does not reference Flutter SDK.",
|
|
128
|
+
FVM_NOT_FOUND: "FVM Flutter SDK not found at .fvm/flutter_sdk",
|
|
129
|
+
FLUTTER_NOT_FOUND: "Flutter SDK not found.",
|
|
130
|
+
IOS_PROJECT_NOT_FOUND: "iOS project directory not found.",
|
|
131
|
+
ANDROID_PROJECT_NOT_FOUND: "Android project directory not found.",
|
|
132
|
+
NO_XCODE_PROJECT: "No Xcode project or workspace found in ios/ directory.",
|
|
133
|
+
NO_BUILD_GRADLE: "No build.gradle found in android/ directory.",
|
|
134
|
+
MODULE_NAME_EMPTY: "Module name cannot be empty.",
|
|
135
|
+
MODULE_NAME_INVALID: "Module name must contain only lowercase letters, numbers, and underscores.",
|
|
136
|
+
} as const;
|
|
137
|
+
|
|
138
|
+
// Info messages
|
|
139
|
+
export const INFO_MESSAGES = {
|
|
140
|
+
RUN_FROM_PROJECT_ROOT: "Please run this command from the root of a Flutter project.",
|
|
141
|
+
ADD_IOS_SUPPORT: "Run 'flutter create .' to add iOS support.",
|
|
142
|
+
ADD_ANDROID_SUPPORT: "Run 'flutter create .' to add Android support.",
|
|
143
|
+
INSTALL_FVM_OR_DISABLE: "Run 'fvm install' or use --disable-fvm flag.",
|
|
144
|
+
} as const;
|