@siranjeevan/releaseflow 1.0.1 โ†’ 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 CHANGED
@@ -1,55 +1,71 @@
1
1
  # ๐Ÿ“– ReleaseFlow Official User Manual
2
2
 
3
- Welcome to **ReleaseFlow**, the ultimate automation tool for Flutter developers. This guide will help you master the system.
3
+ Welcome to **ReleaseFlow**, the ultimate automation tool for Flutter developers. This guide will help you master the release process.
4
4
 
5
5
  ---
6
6
 
7
7
  ## ๐Ÿ› ๏ธ 1. Setup Phase
8
- Before pushing your first update, you must link your project to Firebase.
9
- - **Run**: `releaseflow configure`
10
- - **What you need**:
11
- 1. Your Firebase Service Account JSON file.
12
- 2. Your Storage Bucket Name (found in Firebase Storage Console).
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).
13
20
 
14
21
  ---
15
22
 
16
23
  ## ๐Ÿš€ 2. Releasing an Update
17
- This is the most common task. It builds the app and puts it in the cloud.
24
+ The `release` command is your main workspace tool. It handles EVERYTHING in one go.
25
+
18
26
  - **Run**: `releaseflow release`
19
27
  - **What happens**:
20
- - The CLI checks if your version is new (e.g., moves you from 1.0.4 to 1.0.5).
21
- - It builds a small, optimized APK (< 15MB).
22
- - It uploads the file and updates your users instantly.
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.
23
33
 
24
34
  ---
25
35
 
26
36
  ## โช 3. Rolling Back
27
- If you find a mistake in your latest version, you can go back.
37
+ If you find a mistake in your latest version, you can go back quickly.
38
+
28
39
  - **Run**: `releaseflow rollback`
29
40
  - **What happens**:
30
- - You see a list of your last 3 versions.
31
- - Pick one, and the database instantly points users back to that stable APK.
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.
32
44
 
33
45
  ---
34
46
 
35
- ## ๐Ÿงน 4. Storage Management
36
- **You don't need to do anything!** The CLI automatically keeps only the 3 latest versions in your Firebase Storage to save you space and money.
47
+ ## ๐Ÿ“ฑ 4. Flutter Integration Checklist
48
+ To make the "Force Update" work, your app needs to talk to Firestore.
37
49
 
38
- ---
39
-
40
- ## ๐Ÿ“ฑ 5. Phone Behavior
41
- - If a phone version is **different** from the live version, it will show the **Update Required** screen.
42
- - If they are the same, the user sees your **Home Screen**.
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`.
43
55
 
44
- ---
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.
45
60
 
46
61
  ---
47
62
 
48
- ## ๐Ÿ†˜ 6. Help Center
49
- If you are stuck or encounter an error, join the official support group:
50
- - **GitHub Issues**: https://github.com/siranjeevan/releaseflow-cli/issues
51
- - **Community Support**: Check the Readme on NPM for latest updates.
52
- - **Direct Help**: `releaseflow --help`
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
53
68
 
54
69
  ---
55
- ยฉ 2026 ReleaseFlow | Designed for Flutter Developers ๐Ÿš€
70
+
71
+ ยฉ 2026 ReleaseFlow | Designed for Professional Flutter Developers ๐Ÿš€
package/README.md CHANGED
@@ -1,45 +1,68 @@
1
1
  # ๐Ÿš€ ReleaseFlow CLI
2
2
 
3
- Automated Flutter Release Manager. Effortlessly build, version, and deploy Flutter APKs to Firebase with a single command.
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
+ ---
4
7
 
5
8
  ## ๐Ÿ“ฆ Installation
6
9
 
7
- Install globally via NPM:
10
+ Install globally via NPM to access the `releaseflow` command anywhere:
8
11
  ```bash
9
12
  npm install -g @siranjeevan/releaseflow
10
13
  ```
11
14
 
12
- ## ๐Ÿ› ๏ธ First-Time Setup
15
+ ---
16
+
17
+ ## ๐ŸŽฎ Command Overview
13
18
 
14
- 1. **Configure Firebase**:
15
- Run the configuration command to link your service account and storage bucket.
16
- - **Bucket Name**: Enter your storage bucket (e.g., `your-project-id.firebasestorage.app`).
17
- ```bash
18
- releaseflow configure
19
- ```
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. |
20
26
 
21
- 2. **Integrate Flutter**:
22
- Ensure your Flutter app uses the `version_service` to check for updates against your Firestore `app_config/version` document.
27
+ ---
28
+
29
+ ## โœจ Why ReleaseFlow?
23
30
 
24
- ## ๐Ÿš€ Usage
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
+ ---
25
38
 
26
- ### Full Release (Build + Upload + Clean)
27
- The most powerful command. It automatically detects your version, builds the optimized APK, and pushes it to Firebase.
39
+ ## ๐Ÿš€ Quick Start (3 Steps)
40
+
41
+ ### 1. Link Your Project
42
+ Go to your Flutter project root and run:
28
43
  ```bash
29
- releaseflow release
44
+ releaseflow config set
30
45
  ```
46
+ Follow the prompts to select your project folder and provide your Firebase Service Account JSON.
31
47
 
32
- ### Rollback
33
- Instantly move the live version back to a previous stable build.
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:
34
53
  ```bash
35
- releaseflow rollback
54
+ releaseflow release
36
55
  ```
56
+ The CLI will build, version, and upload your APK. Your app is now LIVE!
37
57
 
38
- ## โœจ Key Features
39
- - **Smart Versioning**: Automatically reads/increments `pubspec.yaml`.
40
- - **Storage Cleanup**: Only keeps the 3 most recent APKs to save cloud space.
41
- - **Optimized Builds**: Automatically builds for `arm64` to reduce APK size (< 15MB).
42
- - **History Aware**: Never allows duplicate version releases.
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.
43
64
 
44
65
  ---
45
- Created by [Jeevith](https://github.com/siranjeevan)
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,15 +42,37 @@ 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();
40
76
  })
41
77
  .command('manual', 'Open the detailed user manual', {}, async () => {
42
78
  const manualPath = path.resolve(__dirname, 'MANUAL.md');
@@ -46,18 +82,22 @@ async function main() {
46
82
  console.log('\x1b[31mManual file not found. Please check GitHub.\x1b[0m');
47
83
  }
48
84
  })
49
- .demandCommand(1, 'โŒ Please specify a valid command.')
85
+ .command('prompt', 'Show the integration guide and next steps', {}, async () => {
86
+ runPromptFlow();
87
+ })
88
+ .demandCommand(1, 'Please specify a valid command.')
50
89
  .recommendCommands()
51
90
  .strict()
52
- .showHelpOnFail(true, 'Type --help for the user manual.')
91
+ .showHelpOnFail(true, 'Type --help for further assistance.')
53
92
  .epilogue(`
54
- ๐Ÿ“– USER MANUAL:
93
+ ๐Ÿ“Š DASHBOARD SUMMARY:
55
94
  --------------------------------------------------
56
- 1. CONFIGURE: Run 'releaseflow configure' first to set up your Firebase keys.
57
- 2. RELEASE: Run 'releaseflow release' to build and push your app.
58
- 3. ROLLBACK: Run 'releaseflow rollback' to move back to an older version.
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)
59
99
 
60
- Support: https://github.com/siranjeevan/releaseflow-cli
100
+ Full Documentation: 'releaseflow manual'
61
101
  --------------------------------------------------
62
102
  `)
63
103
  .help()
@@ -76,7 +116,7 @@ function getProjectPaths() {
76
116
  if (fs.existsSync(p1)) return { root: cwd, pubspec: p1 };
77
117
  if (fs.existsSync(p2)) return { root: path.join(cwd, 'flutter_app'), pubspec: p2 };
78
118
 
79
- throw new Error('โŒ Could not find pubspec.yaml. Please run this command inside your Flutter project folder.');
119
+ throw new Error('Could not find pubspec.yaml. Please run this command inside your Flutter project folder.');
80
120
  }
81
121
 
82
122
  function getPubspecVersion() {
@@ -106,11 +146,83 @@ function updatePubspecVersion(newVersion) {
106
146
  }
107
147
 
108
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
+
109
220
  const answers = await inquirer.prompt([
110
221
  {
111
222
  type: 'input',
112
223
  name: 'serviceAccountPath',
113
- 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,
114
226
  validate: (input) => {
115
227
  const fullPath = path.resolve(process.cwd(), input);
116
228
  if (fs.existsSync(fullPath) && (input.endsWith('.json') || fs.statSync(fullPath).isFile())) {
@@ -122,8 +234,8 @@ async function runConfigureFlow() {
122
234
  {
123
235
  type: 'input',
124
236
  name: 'storageBucket',
125
- message: 'Enter your Firebase Storage Bucket name:',
126
- default: 'your-project-id.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
127
239
  }
128
240
  ]);
129
241
 
@@ -131,8 +243,61 @@ async function runConfigureFlow() {
131
243
  serviceAccountPath: path.resolve(process.cwd(), answers.serviceAccountPath),
132
244
  storageBucket: answers.storageBucket
133
245
  };
134
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
135
- 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
+ }
136
301
  }
137
302
 
138
303
  async function runFullReleaseFlow() {
@@ -167,15 +332,15 @@ async function runFullReleaseFlow() {
167
332
  if (choice.action === 'auto') {
168
333
  localVersion = suggestedVersion;
169
334
  updatePubspecVersion(localVersion);
170
- console.log(`\x1b[32mโœ” Updated pubspec.yaml to ${localVersion}\x1b[0m`);
335
+ console.log(`Updated pubspec.yaml to ${localVersion}`);
171
336
  }
172
337
  } else {
173
- console.log('\x1b[32mโœ” New version detected! Proceeding...\x1b[0m\n');
338
+ console.log('New version detected! Proceeding...\n');
174
339
  }
175
340
 
176
- // Automated Build
341
+
177
342
  const { root } = getProjectPaths();
178
- console.log('\x1b[35m๐Ÿ› ๏ธ Building Optimized APK (arm64)... Please wait...\x1b[0m');
343
+ console.log('\x1b[35mBuilding Optimized APK (arm64)... Please wait...\x1b[0m');
179
344
 
180
345
  const build = spawn('flutter', ['build', 'apk', '--release', '--split-per-abi', '--target-platform', 'android-arm64'], {
181
346
  cwd: root,
@@ -184,13 +349,14 @@ async function runFullReleaseFlow() {
184
349
 
185
350
  build.on('close', async (code) => {
186
351
  if (code !== 0) {
187
- console.error('\x1b[31mโŒ Build failed!\x1b[0m');
352
+ console.error('\x1b[31mBuild failed!\x1b[0m');
188
353
  return;
189
354
  }
190
355
 
191
- console.log('\x1b[32mโœ” Build Successful!\x1b[0m');
356
+ console.log('\x1b[32mBuild Successful!\x1b[0m');
192
357
  const apkPath = path.join(root, 'build/app/outputs/flutter-apk/app-arm64-v8a-release.apk');
193
358
 
359
+
194
360
  const answers = await inquirer.prompt([{
195
361
  type: 'confirm',
196
362
  name: 'forceUpdate',
@@ -199,16 +365,16 @@ async function runFullReleaseFlow() {
199
365
  }]);
200
366
 
201
367
  try {
202
- console.log('\n\x1b[33m๐Ÿš€ Starting Cloud Release Flow...\x1b[0m');
368
+ console.log('\n\x1b[33mStarting Cloud Release Flow...\x1b[0m');
203
369
  const downloadUrl = await uploadApk(apkPath, localVersion);
204
370
  await updateFirestore(localVersion, downloadUrl, answers.forceUpdate);
205
371
 
206
372
  console.log('\n\x1b[35mSummary:\x1b[0m');
207
373
  console.log(`Version: ${localVersion}`);
208
374
  console.log(`Force Update: ${answers.forceUpdate ? 'Yes' : 'No'}`);
209
- 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');
210
376
  } catch (error) {
211
- console.error('\x1b[31m\nโŒ Error: ' + error.message + '\x1b[0m');
377
+ console.error('\x1b[31m\nError: ' + error.message + '\x1b[0m');
212
378
  }
213
379
  });
214
380
  }
@@ -218,7 +384,7 @@ async function runRollbackFlow() {
218
384
  const versions = await listAvailableVersions();
219
385
 
220
386
  if (versions.length === 0) {
221
- console.log('\x1b[31mโŒ No previous versions found.\x1b[0m');
387
+ console.log('\x1b[31mNo previous versions found.\x1b[0m');
222
388
  return;
223
389
  }
224
390
 
@@ -244,11 +410,11 @@ async function runRollbackFlow() {
244
410
  ]);
245
411
 
246
412
  try {
247
- 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`);
248
414
  await rollbackToVersion(answers.version, answers.forceUpdate);
249
- 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');
250
416
  } catch (error) {
251
- console.error('\n\x1b[31mโŒ Rollback failed: ' + error.message + '\x1b[0m');
417
+ console.error('\n\x1b[31mRollback failed: ' + error.message + '\x1b[0m');
252
418
  }
253
419
  }
254
420
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siranjeevan/releaseflow",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Automated Flutter Release Manager",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1 @@
1
+ {}