const fs = require('fs'); const path = require('path'); const https = require('https'); const { execSync } = require('child_process'); const GITLAB_PROJECT_PATH = 'vyla-entertainment%2Fhome'; const PROJECT_ID = process.env.CI_PROJECT_ID || GITLAB_PROJECT_PATH; const TOKEN = process.env.GITLAB_TOKEN || process.env.VYLA_GITLAB_TOKEN; if (!TOKEN) { console.error('Error: GITLAB_TOKEN or VYLA_GITLAB_TOKEN environment variable is required.'); process.exit(1); } function getPackageVersion() { const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')); return pkg.version || '1.0.0'; } function uploadFileToGitLabPackage(filePath, packageName, version, fileName) { return new Promise((resolve, reject) => { const stat = fs.statSync(filePath); const url = `https://gitlab.com/api/v4/projects/${PROJECT_ID}/packages/generic/${packageName}/${version}/${encodeURIComponent(fileName)}`; const urlObj = new URL(url); const req = https.request({ hostname: urlObj.hostname, port: 443, path: urlObj.pathname, method: 'PUT', headers: { 'PRIVATE-TOKEN': TOKEN, 'Content-Type': 'application/octet-stream', 'Content-Length': stat.size } }, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve(url); } else { reject(new Error(`Failed to upload ${fileName} (HTTP ${res.statusCode}): ${body}`)); } }); }); req.on('error', reject); const fileStream = fs.createReadStream(filePath); fileStream.on('error', reject); fileStream.pipe(req); }); } function createGitLabRelease(tag, releaseLinks) { return new Promise((resolve, reject) => { let currentRef = 'main'; try { currentRef = execSync('git rev-parse HEAD').toString().trim(); } catch (e) { } const postData = JSON.stringify({ name: `Vyla Home Release ${tag}`, tag_name: tag, ref: currentRef, description: `Automated local release ${tag}`, assets: { links: releaseLinks } }); const req = https.request({ hostname: 'gitlab.com', port: 443, path: `/api/v4/projects/${PROJECT_ID}/releases`, method: 'POST', headers: { 'PRIVATE-TOKEN': TOKEN, 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) } }, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { if (res.statusCode === 201 || res.statusCode === 200) { resolve(JSON.parse(body)); } else { reject(new Error(`Failed to create release (HTTP ${res.statusCode}): ${body}`)); } }); }); req.on('error', reject); req.write(postData); req.end(); }); } async function main() { const isFullBuild = process.argv.includes('--installer'); const customTag = process.argv.find(arg => arg.startsWith('--tag='))?.split('=')[1]; const version = customTag || `v${getPackageVersion()}`; console.log(`=== Building Vyla Home Release (${version}) ===`); console.log('[1/4] Building update payload and manifest...'); execSync('node build-pipeline.js', { stdio: 'inherit' }); const payloadPath = path.join(__dirname, 'update-payload.zip'); const manifestPath = path.join(__dirname, 'manifest.json'); if (!fs.existsSync(payloadPath) || !fs.existsSync(manifestPath)) { throw new Error('update-payload.zip or manifest.json not found.'); } let installerPath = null; let installerUploadName = null; if (isFullBuild) { console.log('[Optional] Compiling Windows Tauri installer locally...'); execSync('npm run build', { stdio: 'inherit' }); const nsisDir = path.join(__dirname, 'src-tauri/target/release/bundle/nsis'); if (fs.existsSync(nsisDir)) { const files = fs.readdirSync(nsisDir).filter(f => f.endsWith('.exe')); if (files.length > 0) { const originalName = files[0]; installerPath = path.join(nsisDir, originalName); installerUploadName = originalName.replace(/\s+/g, '-'); } } } console.log('[2/4] Uploading artifacts to GitLab Package Registry...'); console.log('Uploading update-payload.zip...'); const payloadUrl = await uploadFileToGitLabPackage(payloadPath, 'vyla-home', version, 'update-payload.zip'); console.log('Uploading manifest.json...'); const manifestUrl = await uploadFileToGitLabPackage(manifestPath, 'vyla-home', version, 'manifest.json'); const releaseLinks = [ { name: 'update-payload.zip', url: payloadUrl, link_type: 'other' }, { name: 'manifest.json', url: manifestUrl, link_type: 'other' } ]; if (installerPath && installerUploadName) { console.log(`Uploading installer ${installerUploadName}...`); const installerUrl = await uploadFileToGitLabPackage(installerPath, 'vyla-home', version, installerUploadName); releaseLinks.push({ name: installerUploadName, url: installerUrl, link_type: 'package' }); } console.log('[3/4] Publishing GitLab Release...'); const release = await createGitLabRelease(version, releaseLinks); console.log(`[4/4] Release successfully created: ${release.name} (${release.tag_name})`); } main().catch(err => { console.error('Release failed:', err.message); process.exit(1); });