ota-manager 1.0.6 → 1.0.13

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.
@@ -0,0 +1,130 @@
1
+ import { execSync } from 'child_process';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import OTA_CONFIG from './ota-config.js';
5
+
6
+ const rootDir = process.cwd();
7
+ const ENV_PATH = path.join(rootDir, '.env');
8
+ const MAIN_MANIFEST_PATH = path.join(rootDir, 'src', 'data', 'update-data.json');
9
+ const OTA_RELEASES_DIR = path.join(rootDir, 'ota-releases');
10
+
11
+ let githubPat = '';
12
+ let gitlabPat = '';
13
+ if (fs.existsSync(ENV_PATH)) {
14
+ const envContent = fs.readFileSync(ENV_PATH, 'utf-8');
15
+ envContent.split('\n').forEach(line => {
16
+ const match = line.match(/^\s*([\w.-]+)\s*=\s*(.*)?\s*$/);
17
+ if (match) {
18
+ if (match[1] === 'GITHUB_DEV_PAT') githubPat = match[2].replace(/['"]/g, '').trim();
19
+ if (match[1] === 'GITLAB_DEV_PAT') gitlabPat = match[2].replace(/['"]/g, '').trim();
20
+ }
21
+ });
22
+ }
23
+
24
+ function getRawBaseUrl(repoUrl, strategy, branch = 'main') {
25
+ let base = repoUrl.replace(/\/$/, '');
26
+ if (strategy === 'github') {
27
+ return base.replace('github.com', 'raw.githubusercontent.com') + `/${branch}`;
28
+ } else if (strategy === 'gitlab') {
29
+ return base + `/-/raw/${branch}`;
30
+ }
31
+ return base;
32
+ }
33
+
34
+ async function rollbackOTA() {
35
+ const targetVersion = process.argv[2];
36
+ const argChannel = process.argv[3] || 'training';
37
+
38
+ if (!targetVersion) {
39
+ console.log('āŒ Error: Mohon tentukan versi tujuan rollback (e.g., npx ota-manager rollback 0.2.0.11 training)');
40
+ process.exit(1);
41
+ }
42
+
43
+ console.log(`\nšŸ”„ --- MEMULAI PROSES ROLLBACK OTA (${argChannel.toUpperCase()}) ---`);
44
+ console.log(`šŸŽÆ Target Versi : v${targetVersion}`);
45
+ console.log(`šŸ”¹ Strategy : ${OTA_CONFIG.strategy.toUpperCase()}`);
46
+
47
+ try {
48
+ const config = OTA_CONFIG[OTA_CONFIG.strategy];
49
+ if (!config || !config.repo) {
50
+ throw new Error(`Repository not configured for strategy "${OTA_CONFIG.strategy}".`);
51
+ }
52
+
53
+ const channelConfig = config.channels?.[argChannel];
54
+ const activeBranch = channelConfig?.branch || config.branch || 'main';
55
+
56
+ console.log(`šŸ“‚ Menyiapkan repositori OTA jarak jauh (Branch: ${activeBranch})...`);
57
+ if (fs.existsSync(OTA_RELEASES_DIR)) {
58
+ fs.rmSync(OTA_RELEASES_DIR, { recursive: true, force: true });
59
+ }
60
+
61
+ const pat = OTA_CONFIG.strategy === 'gitlab' ? gitlabPat : githubPat;
62
+ const cloneRepo = config.repo.endsWith('.git') ? config.repo : config.repo + '.git';
63
+ const authRepo = cloneRepo.replace('https://', `https://${pat}@`);
64
+
65
+ execSync(`git clone --branch ${activeBranch} ${authRepo} "${OTA_RELEASES_DIR}"`, { stdio: 'inherit' });
66
+
67
+ const zipFileName = `v${targetVersion.replace(/\./g, '_')}.zip`;
68
+ const zipFilePath = path.join(OTA_RELEASES_DIR, zipFileName);
69
+
70
+ console.log(`šŸ” Memeriksa ketersediaan bungkusan fisik: ${zipFileName} di server...`);
71
+ if (!fs.existsSync(zipFilePath)) {
72
+ console.log(`\nāŒ ERROR FATAL: File bungkusan ${zipFileName} TIDAK DITEMUKAN di repositori jarak jauh!`);
73
+ console.log(`šŸ’” Rollback dibatalkan karena versi tujuan tidak memiliki bungkusan fisik yang valid.`);
74
+ process.exit(1);
75
+ }
76
+ console.log(`āœ… File bungkusan fisik ${zipFileName} tersedia dan valid!`);
77
+
78
+ const manifestFileName = argChannel === 'training' ? 'manifest-training.json' : 'manifest.json';
79
+ const manifestPath = path.join(OTA_RELEASES_DIR, manifestFileName);
80
+
81
+ const rawBaseUrl = getRawBaseUrl(config.repo, OTA_CONFIG.strategy, activeBranch);
82
+ const activeOtaUrl = `${rawBaseUrl}/${manifestFileName}`;
83
+ const activeZipUrl = `${rawBaseUrl}/${zipFileName}`;
84
+
85
+ console.log(`šŸ“ Memperbarui ${manifestFileName} jarak jauh ke versi ${targetVersion}...`);
86
+ const manifest = {
87
+ version: targetVersion,
88
+ url: activeZipUrl
89
+ };
90
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
91
+
92
+ console.log(`šŸ“ Memperbarui .env lokal ke versi ${targetVersion}...`);
93
+ if (fs.existsSync(ENV_PATH)) {
94
+ let envContent = fs.readFileSync(ENV_PATH, 'utf-8');
95
+ envContent = envContent.replace(/PUBLIC_APP_VERSION_ANDROID=.*/, `PUBLIC_APP_VERSION_ANDROID=${targetVersion}`);
96
+ envContent = envContent.replace(/PUBLIC_APP_VERSION_IOS=.*/, `PUBLIC_APP_VERSION_IOS=${targetVersion}`);
97
+ if (envContent.includes('PUBLIC_APP_VERSION=')) {
98
+ envContent = envContent.replace(/PUBLIC_APP_VERSION=.*/, `PUBLIC_APP_VERSION=${targetVersion}`);
99
+ }
100
+ if (envContent.includes('PUBLIC_OTA_UPDATE_URL=')) {
101
+ envContent = envContent.replace(/PUBLIC_OTA_UPDATE_URL=.*/, `PUBLIC_OTA_UPDATE_URL=${activeOtaUrl}`);
102
+ } else {
103
+ envContent += `\nPUBLIC_OTA_UPDATE_URL=${activeOtaUrl}`;
104
+ }
105
+ fs.writeFileSync(ENV_PATH, envContent);
106
+ }
107
+
108
+ if (fs.existsSync(MAIN_MANIFEST_PATH)) {
109
+ const updateManifest = JSON.parse(fs.readFileSync(MAIN_MANIFEST_PATH, 'utf8'));
110
+ updateManifest.version = targetVersion;
111
+ fs.writeFileSync(MAIN_MANIFEST_PATH, JSON.stringify(updateManifest, null, 2));
112
+ }
113
+
114
+ console.log(`šŸ“¤ Mengirim manifest rollback ke repositori OTA jarak jauh...`);
115
+ execSync('git add .', { cwd: OTA_RELEASES_DIR, stdio: 'ignore' });
116
+ execSync(`git commit -m "chore: rollback OTA manifest to v${targetVersion} (${argChannel})"`, { cwd: OTA_RELEASES_DIR, stdio: 'ignore' });
117
+ execSync(`git push origin ${activeBranch}`, { cwd: OTA_RELEASES_DIR, stdio: 'inherit' });
118
+
119
+ console.log(`\nšŸŽ‰ ROLLBACK SUKSES BERHASIL DIEKSEKUSI!`);
120
+ console.log(`šŸ“„ Versi Aktif Sekarang : v${targetVersion}`);
121
+ console.log(`šŸ”— Channel : ${argChannel}`);
122
+ console.log(`šŸ’” Capgo di HP pengguna akan langsung mendeteksi manifest baru dan melakukan downgrade otomatis ke v${targetVersion}.\n`);
123
+
124
+ } catch (e) {
125
+ console.error(`\nāŒ Rollback Gagal: ${e.message}\n`);
126
+ process.exit(1);
127
+ }
128
+ }
129
+
130
+ rollbackOTA();
@@ -25,10 +25,32 @@ async function checkStatus() {
25
25
  return;
26
26
  }
27
27
 
28
- // 1. Get Local Version
28
+ // 1. Get Local Version & Native Status
29
29
  const envContent = fs.readFileSync(ENV_PATH, 'utf-8');
30
- const localVersion = envContent.match(/PUBLIC_APP_VERSION_ANDROID=(.*)/)?.[1]?.trim().replace(/^"|"$/g, '');
31
- console.log(`šŸ’» Local Version (.env) : v${localVersion || 'unknown'}`);
30
+ const localVersion = envContent.match(/PUBLIC_APP_VERSION_ANDROID=(.*)/)?.[1]?.trim().replace(/^"|"$/g, '') || envContent.match(/PUBLIC_APP_VERSION=(.*)/)?.[1]?.trim().replace(/^"|"$/g, '');
31
+ const appId = envContent.match(/PUBLIC_APP_ID=(.*)/)?.[1]?.trim().replace(/^"|"$/g, '') || 'com.cyclopedia.one_eighty_run';
32
+ console.log(`šŸ’» Local Version (.env) : ${localVersion || 'unknown'}`);
33
+ console.log(`šŸ“¦ App ID / Package Name : ${appId}`);
34
+
35
+ // Cek Android versionCode
36
+ const buildGradlePath = path.join(rootDir, 'android', 'app', 'build.gradle');
37
+ let androidVersionCode = 'unknown';
38
+ if (fs.existsSync(buildGradlePath)) {
39
+ const bgContent = fs.readFileSync(buildGradlePath, 'utf8');
40
+ const match = bgContent.match(/versionCode\s+(\d+)/);
41
+ if (match) androidVersionCode = match[1];
42
+ }
43
+ console.log(`šŸ¤– Android versionCode : ${androidVersionCode}`);
44
+
45
+ // Cek iOS CURRENT_PROJECT_VERSION
46
+ const pbxprojPath = path.join(rootDir, 'ios', 'App', 'App.xcodeproj', 'project.pbxproj');
47
+ let iosProjectVersion = 'unknown';
48
+ if (fs.existsSync(pbxprojPath)) {
49
+ const pbxContent = fs.readFileSync(pbxprojPath, 'utf8');
50
+ const match = pbxContent.match(/CURRENT_PROJECT_VERSION = (\d+);/);
51
+ if (match) iosProjectVersion = match[1];
52
+ }
53
+ console.log(`šŸ iOS Project Version : ${iosProjectVersion}`);
32
54
 
33
55
  // 2. Get Remote Version based on Config
34
56
  const strategy = OTA_CONFIG.strategy;
@@ -83,18 +105,21 @@ async function checkStatus() {
83
105
  }
84
106
 
85
107
  const remoteData = JSON.parse(result);
86
- const remoteVersion = remoteData.version;
108
+ const remoteVersion = remoteData.version || remoteData[channel]?.version;
87
109
 
88
- if (remoteVersion === localVersion) {
89
- console.log(`āœ… Status: UP TO DATE (v${remoteVersion})`);
110
+ if (!remoteVersion) {
111
+ console.log(`✨ Status: CHANNEL "${channel.toUpperCase()}" KOSONG (Belum ada rilis)`);
112
+ console.log(`šŸ’” Jalankan 'npm run ota-manager deploy ${channel}' untuk merilis versi perdana ke channel ini!`);
113
+ } else if (remoteVersion === localVersion) {
114
+ console.log(`āœ… Status: UP TO DATE (${remoteVersion})`);
90
115
  } else {
91
116
  console.log(`āš ļø Status: OUTDATED!`);
92
- console.log(`šŸ“” Remote Version : v${remoteVersion}`);
93
- console.log(`šŸ’” Run 'npx ota-updates ${channel}' to update.`);
117
+ console.log(`šŸ“” Remote Version : ${remoteVersion}`);
118
+ console.log(`šŸ’” Run 'npm run ota-updates ${channel}' to update.`);
94
119
  }
95
120
  } catch (e) {
96
121
  if (e.message === 'File not found') {
97
- console.log(`✨ Status: NEW INFRASTRUCTURE (v0.0.0)`);
122
+ console.log(`✨ Status: NEW INFRASTRUCTURE (0.0.0)`);
98
123
  console.log(`šŸ’” No releases found on server. Ready for initial deployment!`);
99
124
  } else {
100
125
  console.log(`āŒ Could not fetch remote manifest.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ota-manager",
3
- "version": "1.0.6",
3
+ "version": "1.0.13",
4
4
  "description": "Multi-provider OTA update manager for Astro and static web projects.",
5
5
  "type": "module",
6
6
  "main": "index.js",