@siranjeevan/releaseflow 1.0.0 → 1.1.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/MANUAL.md ADDED
@@ -0,0 +1,71 @@
1
+ # 📖 ReleaseFlow Official User Manual
2
+
3
+ Welcome to **ReleaseFlow**, the ultimate automation tool for Flutter developers. This guide will help you master the release process.
4
+
5
+ ---
6
+
7
+ ## 🛠️ 1. Setup Phase
8
+ Before pushing your first update, you must link your project to Firebase. Use the `config` subcommand to manage settings.
9
+
10
+ ### 📝 Configuration Actions:
11
+ - **`releaseflow config set`**: Interactive menu to configure the current directory or select a subfolder.
12
+ - **`releaseflow config edit`**: Allows updating the Service Account or Bucket name with pre-filled defaults.
13
+ - **`releaseflow config show`**: Displays the path of the current `releaseflow.config.json` and its settings.
14
+ - **`releaseflow config remove`**: Deletes the `releaseflow.config.json` file for the current project.
15
+
16
+ ### 🔑 What you need:
17
+ - **Firebase Console**: Go to **Project Settings > Service Accounts > Select Node.js**.
18
+ - **Service Account**: Generate and download your Private Key JSON file.
19
+ - **Storage Bucket**: Copy the bucket name without `gs://` (from Storage > Files).
20
+
21
+ ---
22
+
23
+ ## 🚀 2. Releasing an Update
24
+ The `release` command is your main workspace tool. It handles EVERYTHING in one go.
25
+
26
+ - **Run**: `releaseflow release`
27
+ - **What happens**:
28
+ 1. **Version Check**: Compares `pubspec.yaml` with Firebase versions.
29
+ 2. **Smart Suggestion**: If the version already exists, it suggests a patch bump.
30
+ 3. **Automated Build**: Executes `flutter build apk --split-per-abi --target-platform android-arm64`.
31
+ 4. **Optimized Upload**: Pushes the ~15MB APK to Firebase Storage.
32
+ 5. **Cloud Update**: Updates Firestore so your users instantly see the new version.
33
+
34
+ ---
35
+
36
+ ## ⏪ 3. Rolling Back
37
+ If you find a mistake in your latest version, you can go back quickly.
38
+
39
+ - **Run**: `releaseflow rollback`
40
+ - **What happens**:
41
+ - Displays a list of your most recent uploaded versions.
42
+ - Selecting one instantly points the "Live Version" to that stable build.
43
+ - **Storage Management**: The CLI automatically keeps only the 3 latest versions in your Firebase Storage to save space.
44
+
45
+ ---
46
+
47
+ ## 📱 4. Flutter Integration Checklist
48
+ To make the "Force Update" work, your app needs to talk to Firestore.
49
+
50
+ ### 🔗 Next Steps for your App:
51
+ 1. **Manual UI**: Build a "Force Update" screen or dialog in your Flutter code.
52
+ 2. **Firestore**: The CLI manages the `app_config/version` document for you.
53
+ 3. **Security**: Set Firestore rules to allow your app to READ `app_config/version`.
54
+ 4. **Logic**: Use `package_info_plus` to compare your app version with Firestore's `latest_version`.
55
+
56
+ ### ✅ Verification Checklist:
57
+ - **a.** Run `releaseflow release` and set "Force Update" to TRUE.
58
+ - **b.** Open your old app version: It should now show your Force Update screen.
59
+ - **c.** Run `releaseflow rollback`: Your app should return to normal.
60
+
61
+ ---
62
+
63
+ ## 🆘 5. Help Center
64
+ If you are stuck or encounter an error, use these resources:
65
+ - **`releaseflow prompt`**: See your integration guide instantly.
66
+ - **`releaseflow --help`**: View quick usage commands.
67
+ - **GitHub**: https://github.com/siranjeevan/releaseflow-cli
68
+
69
+ ---
70
+
71
+ © 2026 ReleaseFlow | Designed for Professional Flutter Developers 🚀
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # 🚀 ReleaseFlow CLI
2
+
3
+ **The Professional Flutter Release Automator.**
4
+ Effortlessly build, version, and deploy Flutter APKs to Firebase with a single command. Designed for developers who value speed, consistency, and a clean release history.
5
+
6
+ ---
7
+
8
+ ## 📦 Installation
9
+
10
+ Install globally via NPM to access the `releaseflow` command anywhere:
11
+ ```bash
12
+ npm install -g @siranjeevan/releaseflow
13
+ ```
14
+
15
+ ---
16
+
17
+ ## 🎮 Command Overview
18
+
19
+ | Command | Description |
20
+ | :--- | :--- |
21
+ | `releaseflow release` | **All-in-One**: Builds optimized APK, increments version, and uploads to Firebase. |
22
+ | `releaseflow rollback`| **Safety Net**: Instantly revert the live app to a previous stable version. |
23
+ | `releaseflow config set` | **Setup**: Link a folder/project to its Firebase credentials. |
24
+ | `releaseflow prompt` | **Guide**: Show the Flutter integration checklist and next steps. |
25
+ | `releaseflow manual` | **Full Help**: Open the detailed user manual in your terminal. |
26
+
27
+ ---
28
+
29
+ ## ✨ Why ReleaseFlow?
30
+
31
+ * **Project-Aware**: Automatically detects and uses unique Firebase settings for each of your apps.
32
+ * **Smart Versioning**: Reads/increments `pubspec.yaml` and prevents duplicate version releases.
33
+ * **Optimized Builds**: Automatically builds `arm64` APKs to keep your file size small (< 15MB).
34
+ * **Auto-Cleanup**: Keeps your Firebase Storage tidy by only maintaining the 3 most recent APKs.
35
+ * **Force Update Support**: Toggle mandatory updates for any version directly from the CLI.
36
+
37
+ ---
38
+
39
+ ## 🚀 Quick Start (3 Steps)
40
+
41
+ ### 1. Link Your Project
42
+ Go to your Flutter project root and run:
43
+ ```bash
44
+ releaseflow config set
45
+ ```
46
+ Follow the prompts to select your project folder and provide your Firebase Service Account JSON.
47
+
48
+ ### 2. Prepare Your Flutter App
49
+ Ensure your app is set up to read from your Firestore `app_config/version` document. Run `releaseflow prompt` to see the code requirements.
50
+
51
+ ### 3. Launch Your Release
52
+ When you're ready to deploy, just run:
53
+ ```bash
54
+ releaseflow release
55
+ ```
56
+ The CLI will build, version, and upload your APK. Your app is now LIVE!
57
+
58
+ ---
59
+
60
+ ## 🛠️ Requirements
61
+ - **Flutter SDK**: Must be installed and available in your PATH.
62
+ - **Node.js**: Version 16 or higher.
63
+ - **Firebase Project**: A project with Firestore and Storage enabled.
64
+
65
+ ---
66
+
67
+ Created with ❤️ by [Jeevith](https://github.com/siranjeevan)
68
+ [GitHub Repository](https://github.com/siranjeevan/releaseflow-cli) | [Report a Bug](https://github.com/siranjeevan/releaseflow-cli/issues)
package/firebase.js CHANGED
@@ -4,9 +4,33 @@ const path = require('path');
4
4
 
5
5
  let db, storage;
6
6
 
7
- const CONFIG_PATH = path.join(__dirname, 'config.json');
7
+ const GLOBAL_CONFIG_PATH = path.join(__dirname, 'config.json');
8
+
9
+ function getConfigPath() {
10
+ const cwd = process.cwd();
11
+
12
+ // 1. Current Directory
13
+ const p1 = path.join(cwd, 'releaseflow.config.json');
14
+ // 2. flutter_app folder (if in project root)
15
+ const p2 = path.join(cwd, 'flutter_app', 'releaseflow.config.json');
16
+ // 3. Parent Directory (if inside flutter_app/lib or similar)
17
+ const p3 = path.join(cwd, '..', 'releaseflow.config.json');
18
+
19
+ if (fs.existsSync(p1)) return p1;
20
+ if (fs.existsSync(p2)) return p2;
21
+ if (fs.existsSync(p3)) return p3;
22
+
23
+ // Fallback to absolute legacy path if it exists
24
+ if (fs.existsSync(GLOBAL_CONFIG_PATH)) return GLOBAL_CONFIG_PATH;
25
+
26
+ // Default to project root for NEW configurations
27
+ return p1;
28
+ }
29
+
30
+ const CONFIG_PATH = getConfigPath();
8
31
 
9
32
  function isConfigured() {
33
+ const CONFIG_PATH = getConfigPath();
10
34
  if (!fs.existsSync(CONFIG_PATH)) return false;
11
35
  try {
12
36
  const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
@@ -18,9 +42,10 @@ function isConfigured() {
18
42
 
19
43
  function initFirebase() {
20
44
  if (!isConfigured()) {
21
- throw new Error('Firebase is not configured. Run "myapp configure" first.');
45
+ throw new Error('Firebase is not configured. Run "releaseflow configure" first.');
22
46
  }
23
47
 
48
+ const CONFIG_PATH = getConfigPath();
24
49
  const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
25
50
  const serviceAccount = require(path.resolve(config.serviceAccountPath));
26
51
 
@@ -43,4 +68,4 @@ async function getLiveVersion() {
43
68
  return doc.exists ? doc.data().latest_version : '0.0.0';
44
69
  }
45
70
 
46
- module.exports = { initFirebase, isConfigured, CONFIG_PATH, getLiveVersion };
71
+ module.exports = { initFirebase, isConfigured, getConfigPath, getLiveVersion, GLOBAL_CONFIG_PATH };
package/index.js CHANGED
@@ -12,15 +12,29 @@ const {
12
12
  listAvailableVersions,
13
13
  rollbackToVersion
14
14
  } = require('./uploader');
15
- const { isConfigured, CONFIG_PATH, getLiveVersion } = require('./firebase');
15
+ const { isConfigured, getConfigPath, getLiveVersion, GLOBAL_CONFIG_PATH } = require('./firebase');
16
16
 
17
17
  const yargs = require('yargs/yargs');
18
18
  const { hideBin } = require('yargs/helpers');
19
+ const axios = require('axios');
20
+ const pkg = require('./package.json');
21
+
22
+ async function checkForUpdates() {
23
+ try {
24
+ const response = await axios.get(`https://registry.npmjs.org/${pkg.name}/latest`, { timeout: 1000 });
25
+ const latestVersion = response.data.version;
26
+ if (semver.gt(latestVersion, pkg.version)) {
27
+ console.log('\x1b[33m%s\x1b[0m', `\n--- UPDATE AVAILABLE: ${latestVersion} ---`);
28
+ console.log(`Run 'npm install -g ${pkg.name}' to update.\n`);
29
+ }
30
+ } catch (e) {}
31
+ }
19
32
 
20
33
  async function main() {
34
+ await checkForUpdates();
21
35
  yargs(hideBin(process.argv))
22
36
  .command('release', 'Build, Detect and Upload in ONE command!', {}, async () => {
23
- console.log('\x1b[36m%s\x1b[0m', '\n🚀 --- ReleaseFlow All-in-One Generator --- 🚀');
37
+ console.log('\x1b[36m%s\x1b[0m', '\n--- ReleaseFlow All-in-One Generator ---');
24
38
  if (!isConfigured()) {
25
39
  console.log('\x1b[33mFirebase is not configured yet.\x1b[0m');
26
40
  await runConfigureFlow();
@@ -28,55 +42,187 @@ async function main() {
28
42
  await runFullReleaseFlow();
29
43
  })
30
44
  .command('rollback', 'Rollback to a previous APK version', {}, async () => {
31
- console.log('\x1b[36m%s\x1b[0m', '\n--- Rollback to Previous Version ---');
45
+ console.log('\x1b[36m%s\x1b[0m', '\n--- Rollback to Previous Version ---');
32
46
  if (!isConfigured()) {
33
47
  await runConfigureFlow();
34
48
  }
35
49
  await runRollbackFlow();
36
50
  })
37
- .command('configure', 'Set up Firebase credentials', {}, async () => {
51
+ .command('config <action>', 'Manage Firebase credentials (set, remove, edit, show)', (yargs) => {
52
+ return yargs.positional('action', {
53
+ describe: 'Action to perform',
54
+ choices: ['set', 'remove', 'edit', 'show']
55
+ });
56
+ }, async (argv) => {
57
+ switch (argv.action) {
58
+ case 'set':
59
+ case 'edit':
60
+ console.log('\x1b[36m%s\x1b[0m', `\n--- ${argv.action === 'edit' ? 'Edit' : 'Configure'} Firebase Credentials ---`);
61
+ await runConfigureFlow();
62
+ runPromptFlow();
63
+ break;
64
+ case 'remove':
65
+ await runRemoveConfig();
66
+ break;
67
+ case 'show':
68
+ runShowConfig();
69
+ break;
70
+ }
71
+ })
72
+ .command('configure', 'Alias for config set', {}, async () => {
38
73
  console.log('\x1b[36m%s\x1b[0m', '\n--- Configure Firebase Credentials ---');
39
74
  await runConfigureFlow();
75
+ runPromptFlow();
76
+ })
77
+ .command('manual', 'Open the detailed user manual', {}, async () => {
78
+ const manualPath = path.resolve(__dirname, 'MANUAL.md');
79
+ if (fs.existsSync(manualPath)) {
80
+ console.log('\n' + fs.readFileSync(manualPath, 'utf8'));
81
+ } else {
82
+ console.log('\x1b[31mManual file not found. Please check GitHub.\x1b[0m');
83
+ }
84
+ })
85
+ .command('prompt', 'Show the integration guide and next steps', {}, async () => {
86
+ runPromptFlow();
40
87
  })
41
- .demandCommand(1, 'Please specify a command (e.g., release, rollback, configure)')
88
+ .demandCommand(1, 'Please specify a valid command.')
89
+ .recommendCommands()
90
+ .strict()
91
+ .showHelpOnFail(true, 'Type --help for further assistance.')
92
+ .epilogue(`
93
+ 📊 DASHBOARD SUMMARY:
94
+ --------------------------------------------------
95
+ 1. CONFIG: 'releaseflow config set' (Setup credentials)
96
+ 2. RELEASE: 'releaseflow release' (Build + Upload)
97
+ 3. ROLLBACK: 'releaseflow rollback' (Quick Revert)
98
+ 4. HELP: 'releaseflow prompt' (Integration checklist)
99
+
100
+ Full Documentation: 'releaseflow manual'
101
+ --------------------------------------------------
102
+ `)
42
103
  .help()
43
104
  .argv;
44
105
  }
45
106
 
46
- const PUBSPEC_PATH = path.resolve(__dirname, '../flutter_app/pubspec.yaml');
107
+ // Global path detection for pubspec.yaml
108
+ function getProjectPaths() {
109
+ const cwd = process.cwd();
110
+
111
+ // Option 1: Current directory (user is inside flutter_app)
112
+ const p1 = path.join(cwd, 'pubspec.yaml');
113
+ // Option 2: flutter_app subfolder (user is in project root)
114
+ const p2 = path.join(cwd, 'flutter_app', 'pubspec.yaml');
115
+
116
+ if (fs.existsSync(p1)) return { root: cwd, pubspec: p1 };
117
+ if (fs.existsSync(p2)) return { root: path.join(cwd, 'flutter_app'), pubspec: p2 };
118
+
119
+ throw new Error('Could not find pubspec.yaml. Please run this command inside your Flutter project folder.');
120
+ }
47
121
 
48
122
  function getPubspecVersion() {
49
123
  try {
50
- if (fs.existsSync(PUBSPEC_PATH)) {
51
- const doc = yaml.load(fs.readFileSync(PUBSPEC_PATH, 'utf8'));
52
- return doc.version.split('+')[0];
53
- }
124
+ const { pubspec } = getProjectPaths();
125
+ const doc = yaml.load(fs.readFileSync(pubspec, 'utf8'));
126
+ return doc.version.split('+')[0];
54
127
  } catch (e) {
55
- console.error('Warning: Could not read version from pubspec.yaml');
128
+ console.error('\x1b[31mError: ' + e.message + '\x1b[0m');
129
+ process.exit(1);
56
130
  }
57
- return null;
58
131
  }
59
132
 
60
133
  function updatePubspecVersion(newVersion) {
61
134
  try {
62
- const rawPubspec = fs.readFileSync(PUBSPEC_PATH, 'utf8');
135
+ const { pubspec } = getProjectPaths();
136
+ const rawPubspec = fs.readFileSync(pubspec, 'utf8');
63
137
  const updatedPubspec = rawPubspec.replace(/^version: ([\d\.]+)(.*)$/m, (match, v, build) => {
64
138
  return `version: ${newVersion}${build || '+1'}`;
65
139
  });
66
- fs.writeFileSync(PUBSPEC_PATH, updatedPubspec);
140
+ fs.writeFileSync(pubspec, updatedPubspec);
67
141
  return true;
68
142
  } catch (e) {
69
- console.error('Error updating pubspec.yaml: ' + e.message);
143
+ console.error('\x1b[31mError updating pubspec.yaml: ' + e.message + '\x1b[0m');
70
144
  return false;
71
145
  }
72
146
  }
73
147
 
74
148
  async function runConfigureFlow() {
149
+ const { choice } = await inquirer.prompt([
150
+ {
151
+ type: 'list',
152
+ name: 'choice',
153
+ message: 'Where would you like to save the configuration?',
154
+ choices: [
155
+ { name: `Current Project (${path.basename(process.cwd())})`, value: 'this' },
156
+ { name: 'Select a Subfolder/Project...', value: 'select' },
157
+ { name: 'Enter a Custom Path...', value: 'manual' }
158
+ ]
159
+ }
160
+ ]);
161
+
162
+ let configLocation = '';
163
+
164
+ switch (choice) {
165
+ case 'this':
166
+ configLocation = path.join(process.cwd(), 'releaseflow.config.json');
167
+ console.log(`\x1b[36mTargeting Current Project: ${path.basename(process.cwd())}\x1b[0m`);
168
+ break;
169
+
170
+ case 'select':
171
+ const subDirs = fs.readdirSync(process.cwd())
172
+ .filter(f => fs.statSync(path.join(process.cwd(), f)).isDirectory() && !f.startsWith('.'))
173
+ .map(f => ({ name: `${f}/`, value: path.join(process.cwd(), f) }));
174
+
175
+ if (subDirs.length === 0) {
176
+ console.log('\x1b[33mNo subfolders found. Reverting to Current Project.\x1b[0m');
177
+ configLocation = path.join(process.cwd(), 'releaseflow.config.json');
178
+ } else {
179
+ const { selectedDir } = await inquirer.prompt([{
180
+ type: 'list',
181
+ name: 'selectedDir',
182
+ message: 'Pick a project folder:',
183
+ choices: subDirs
184
+ }]);
185
+ configLocation = path.join(selectedDir, 'releaseflow.config.json');
186
+ console.log(`\x1b[36mTargeting Folder: ${path.basename(selectedDir)}\x1b[0m`);
187
+ }
188
+ break;
189
+
190
+ case 'manual':
191
+ const { manualPath } = await inquirer.prompt([{
192
+ type: 'input',
193
+ name: 'manualPath',
194
+ message: 'Enter the exact directory path:',
195
+ validate: (input) => {
196
+ const fullPath = path.resolve(process.cwd(), input);
197
+ return fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory() ? true : 'Please provide a valid directory path.';
198
+ }
199
+ }]);
200
+ configLocation = path.join(path.resolve(process.cwd(), manualPath), 'releaseflow.config.json');
201
+ console.log('\x1b[36mTargeting Custom Path\x1b[0m');
202
+ break;
203
+ }
204
+
205
+ let defaultConfig = {
206
+ serviceAccountPath: '',
207
+ storageBucket: 'your-project-id.firebasestorage.app'
208
+ };
209
+
210
+ // Try to find ANY existing config to use as a helpful default
211
+ const bestAvailableConfig = fs.existsSync(configLocation) ? configLocation : getConfigPath();
212
+
213
+ if (fs.existsSync(bestAvailableConfig)) {
214
+ try {
215
+ const existing = JSON.parse(fs.readFileSync(bestAvailableConfig, 'utf8'));
216
+ defaultConfig = { ...defaultConfig, ...existing };
217
+ } catch (e) {}
218
+ }
219
+
75
220
  const answers = await inquirer.prompt([
76
221
  {
77
222
  type: 'input',
78
223
  name: 'serviceAccountPath',
79
- message: 'Enter the path to your Firebase Service Account JSON file:',
224
+ message: 'Enter the path to your Firebase Service Account JSON file:\n (Settings > Service Accounts > Select Node.js > Generate new private key > Download and enter its file path)\n Path:',
225
+ default: defaultConfig.serviceAccountPath,
80
226
  validate: (input) => {
81
227
  const fullPath = path.resolve(process.cwd(), input);
82
228
  if (fs.existsSync(fullPath) && (input.endsWith('.json') || fs.statSync(fullPath).isFile())) {
@@ -88,8 +234,8 @@ async function runConfigureFlow() {
88
234
  {
89
235
  type: 'input',
90
236
  name: 'storageBucket',
91
- message: 'Enter your Firebase Storage Bucket name:',
92
- default: 'testing-dhwayam.firebasestorage.app'
237
+ message: 'Enter your Firebase Storage Bucket name:\n (Go to Firebase Console > Storage > Files > Copy the bucket name without gs://)\n Name:',
238
+ default: defaultConfig.storageBucket
93
239
  }
94
240
  ]);
95
241
 
@@ -97,8 +243,61 @@ async function runConfigureFlow() {
97
243
  serviceAccountPath: path.resolve(process.cwd(), answers.serviceAccountPath),
98
244
  storageBucket: answers.storageBucket
99
245
  };
100
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
101
- console.log('\x1b[32m✔ Configuration saved successfully!\x1b[0m\n');
246
+
247
+ fs.writeFileSync(configLocation, JSON.stringify(config, null, 2));
248
+ console.log('\x1b[32mConfiguration saved successfully!\x1b[0m\n');
249
+ console.log(`\x1b[34mSaved to: ${configLocation}\x1b[0m`);
250
+ }
251
+
252
+ function runPromptFlow() {
253
+ console.log('\n\x1b[36m--- NEXT STEPS FOR YOUR FLUTTER APP ---\x1b[0m');
254
+ console.log('1. Manual UI: You must build the Force Update screen in your Flutter code.');
255
+ console.log('2. Firestore: The CLI will manage "app_config/version" automatically.');
256
+ console.log('3. Security: Allow your users to READ "app_config/version" in Firebase Rules.');
257
+
258
+ console.log('\n\x1b[33m--- VERIFICATION CHECKLIST (TEST YOUR UI) ---\x1b[0m');
259
+ console.log('If you have built your UI, here is how to verify it works:');
260
+ console.log(' a. Run "releaseflow release" and set "Force Update" to TRUE.');
261
+ console.log(' b. Open your old app version: It should now show your Force Update screen.');
262
+ console.log(' c. Run "releaseflow rollback": Your app should return to normal.');
263
+
264
+ console.log('\n\x1b[35m--- READY TO RELEASE? ---\x1b[0m');
265
+ console.log('Run these commands to start:');
266
+ console.log(' releaseflow release : Build and Upload your first APK.');
267
+ console.log(' releaseflow config show : Verify your current project settings.');
268
+ console.log(' releaseflow manual : View full Flutter integration guide.\n');
269
+ }
270
+
271
+ async function runRemoveConfig() {
272
+ const CONFIG_PATH = getConfigPath();
273
+ if (fs.existsSync(CONFIG_PATH)) {
274
+ const { confirm } = await inquirer.prompt([{
275
+ type: 'confirm',
276
+ name: 'confirm',
277
+ message: 'Are you sure you want to remove your Firebase configuration?',
278
+ default: false
279
+ }]);
280
+
281
+ if (confirm) {
282
+ fs.unlinkSync(CONFIG_PATH);
283
+ console.log('\x1b[32mConfiguration removed successfully!\x1b[0m\n');
284
+ }
285
+ } else {
286
+ console.log('\x1b[33mNo configuration found to remove.\x1b[0m\n');
287
+ }
288
+ }
289
+
290
+ function runShowConfig() {
291
+ const CONFIG_PATH = getConfigPath();
292
+ if (fs.existsSync(CONFIG_PATH)) {
293
+ const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
294
+ console.log('\n\x1b[36m--- Current Configuration ---\x1b[0m');
295
+ console.log(`Config File: \x1b[34m${CONFIG_PATH}\x1b[0m`);
296
+ console.log(`Service Account: \x1b[33m${config.serviceAccountPath}\x1b[0m`);
297
+ console.log(`Storage Bucket: \x1b[33m${config.storageBucket}\x1b[0m\n`);
298
+ } else {
299
+ console.log('\x1b[33mNo configuration found.\x1b[0m\n');
300
+ }
102
301
  }
103
302
 
104
303
  async function runFullReleaseFlow() {
@@ -133,29 +332,30 @@ async function runFullReleaseFlow() {
133
332
  if (choice.action === 'auto') {
134
333
  localVersion = suggestedVersion;
135
334
  updatePubspecVersion(localVersion);
136
- console.log(`\x1b[32m✔ Updated pubspec.yaml to ${localVersion}\x1b[0m`);
335
+ console.log(`Updated pubspec.yaml to ${localVersion}`);
137
336
  }
138
337
  } else {
139
- console.log('\x1b[32m✔ New version detected! Proceeding...\x1b[0m\n');
338
+ console.log('New version detected! Proceeding...\n');
140
339
  }
141
340
 
142
- // Automated Build
143
- console.log('\n\x1b[35m🛠️ Building Optimized APK (arm64)... Please wait...\x1b[0m');
341
+
342
+ const { root } = getProjectPaths();
343
+ console.log('\x1b[35mBuilding Optimized APK (arm64)... Please wait...\x1b[0m');
144
344
 
145
345
  const build = spawn('flutter', ['build', 'apk', '--release', '--split-per-abi', '--target-platform', 'android-arm64'], {
146
- cwd: path.resolve(__dirname, '../flutter_app'),
346
+ cwd: root,
147
347
  stdio: 'inherit'
148
348
  });
149
349
 
150
350
  build.on('close', async (code) => {
151
351
  if (code !== 0) {
152
- console.error('\x1b[31m❌ Build failed!\x1b[0m');
352
+ console.error('\x1b[31mBuild failed!\x1b[0m');
153
353
  return;
154
354
  }
155
355
 
156
- console.log('\x1b[32m✔ Build Successful!\x1b[0m');
157
- const apkPath = '../flutter_app/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk';
158
- const fullApkPath = path.resolve(__dirname, apkPath);
356
+ console.log('\x1b[32mBuild Successful!\x1b[0m');
357
+ const apkPath = path.join(root, 'build/app/outputs/flutter-apk/app-arm64-v8a-release.apk');
358
+
159
359
 
160
360
  const answers = await inquirer.prompt([{
161
361
  type: 'confirm',
@@ -165,16 +365,16 @@ async function runFullReleaseFlow() {
165
365
  }]);
166
366
 
167
367
  try {
168
- console.log('\n\x1b[33m🚀 Starting Cloud Release Flow...\x1b[0m');
169
- const downloadUrl = await uploadApk(fullApkPath, localVersion);
368
+ console.log('\n\x1b[33mStarting Cloud Release Flow...\x1b[0m');
369
+ const downloadUrl = await uploadApk(apkPath, localVersion);
170
370
  await updateFirestore(localVersion, downloadUrl, answers.forceUpdate);
171
371
 
172
372
  console.log('\n\x1b[35mSummary:\x1b[0m');
173
373
  console.log(`Version: ${localVersion}`);
174
374
  console.log(`Force Update: ${answers.forceUpdate ? 'Yes' : 'No'}`);
175
- console.log('\n\x1b[32m🎉 Release Complete! Your app is now LIVE. 🎉\x1b[0m');
375
+ console.log('\n\x1b[32mRelease Complete! Your app is now LIVE.\x1b[0m');
176
376
  } catch (error) {
177
- console.error('\x1b[31m\n❌ Error: ' + error.message + '\x1b[0m');
377
+ console.error('\x1b[31m\nError: ' + error.message + '\x1b[0m');
178
378
  }
179
379
  });
180
380
  }
@@ -184,7 +384,7 @@ async function runRollbackFlow() {
184
384
  const versions = await listAvailableVersions();
185
385
 
186
386
  if (versions.length === 0) {
187
- console.log('\x1b[31m❌ No previous versions found.\x1b[0m');
387
+ console.log('\x1b[31mNo previous versions found.\x1b[0m');
188
388
  return;
189
389
  }
190
390
 
@@ -210,11 +410,11 @@ async function runRollbackFlow() {
210
410
  ]);
211
411
 
212
412
  try {
213
- console.log(`\n\x1b[33m⏪ Rolling back to version ${answers.version}...\x1b[0m`);
413
+ console.log(`\n\x1b[33mRolling back to version ${answers.version}...\x1b[0m`);
214
414
  await rollbackToVersion(answers.version, answers.forceUpdate);
215
- console.log('\n\x1b[32m✔ Success! The database has been rolled back.\x1b[0m');
415
+ console.log('\n\x1b[32mSuccess! The database has been rolled back.\x1b[0m');
216
416
  } catch (error) {
217
- console.error('\n\x1b[31m❌ Rollback failed: ' + error.message + '\x1b[0m');
417
+ console.error('\n\x1b[31mRollback failed: ' + error.message + '\x1b[0m');
218
418
  }
219
419
  }
220
420
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siranjeevan/releaseflow",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Automated Flutter Release Manager",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1 @@
1
+ {}