diff --git a/package.json b/package.json index f8d5d0e..4e4a966 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "start": "node server.js", "build:web": "node -e \"const fs = require('fs'); fs.rmSync('www', { recursive: true, force: true }); fs.mkdirSync('www'); ['css', 'js', 'assets'].forEach(d => fs.cpSync(d, 'www/' + d, { recursive: true })); fs.copyFileSync('index.html', 'www/index.html');\"", "dev": "node scripts/build-apk.mjs", - "release": "npm run build:web && cap sync android && cd android && gradlew.bat assembleRelease && cd .. && node -e \"require('fs').copyFileSync('android/app/build/outputs/apk/release/app-release-unsigned.apk', 'Vyla-TV-release.apk')\"", + "release": "node scripts/release.mjs", "android": "node scripts/build-android.mjs && npx cap sync android" }, "dependencies": { diff --git a/scripts/release.mjs b/scripts/release.mjs new file mode 100644 index 0000000..fae3110 --- /dev/null +++ b/scripts/release.mjs @@ -0,0 +1,81 @@ +import { spawnSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; + +const root = process.cwd(); +const win = process.platform === 'win32'; +const androidDir = path.join(root, 'android'); + +const run = (cmd, args, cwd = root) => { + const r = spawnSync(cmd, args, { cwd, stdio: 'inherit', shell: win }); + if (r.status !== 0) process.exit(r.status || 1); +}; + +const sdk = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT; +const lp = path.join(androidDir, 'local.properties'); +if (!fs.existsSync(lp)) { + if (!sdk) { + console.error('Set ANDROID_HOME environment variable.'); + process.exit(1); + } + fs.writeFileSync(lp, 'sdk.dir=' + sdk.replace(/\\/g, '\\\\') + '\n'); +} + +const ghCheck = spawnSync('gh', ['--version'], { shell: win, stdio: 'ignore' }); +if (ghCheck.status !== 0) { + console.error('GitHub CLI (gh) is not installed or not in PATH.'); + process.exit(1); +} + +const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')); +const version = pkg.version || '1.0.0'; +const tag = `v${version}`; + +run('node', ['scripts/build-android.mjs']); +run('npx', ['cap', 'sync', 'android']); +run(win ? 'gradlew.bat' : './gradlew', ['assembleRelease'], androidDir); + +const relDir = path.join(androidDir, 'app/build/outputs/apk/release'); +const apkCandidates = [ + path.join(relDir, 'app-release.apk'), + path.join(relDir, 'app-release-unsigned.apk'), + path.join(androidDir, 'app/build/outputs/apk/debug/app-debug.apk') +]; + +const sourceApk = apkCandidates.find(p => fs.existsSync(p)); +if (!sourceApk) { + console.error('Build succeeded but no output APK was found.'); + process.exit(1); +} + +const distDir = path.join(root, 'release'); +fs.rmSync(distDir, { recursive: true, force: true }); +fs.mkdirSync(distDir, { recursive: true }); + +const targetApkName = `Vyla-TV_${version}.apk`; +const targetApk = path.join(distDir, targetApkName); +fs.copyFileSync(sourceApk, targetApk); + +const manifest = { + name: 'Vyla TV', + version, + package: targetApkName, + releaseDate: new Date().toISOString() +}; +const manifestPath = path.join(distDir, 'manifest.json'); +fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); + +spawnSync('git', ['tag', '-a', tag, '-m', `Release ${tag}`], { shell: win, stdio: 'inherit' }); +spawnSync('git', ['push', 'origin', tag], { shell: win, stdio: 'inherit' }); + +run('gh', [ + 'release', + 'create', + tag, + targetApk, + manifestPath, + '--title', + `Vyla TV Release ${tag}`, + '--notes', + `Automated Release ${tag}` +]); \ No newline at end of file