import fs from 'fs' import path from 'path' import { execSync } from 'child_process' import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3' import dotenv from 'dotenv' dotenv.config({ quiet: true }) const DIST_DIR = path.resolve('dist') const BUCKET = 'dist' const client = new S3Client({ region: 'us-east-1', endpoint: 'https://s3.takerofnotes.com', forcePathStyle: true, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }, }) const getContentType = (file) => { if (file.endsWith('.yml')) return 'text/yaml' if (file.endsWith('.json')) return 'application/json' if (file.endsWith('.AppImage')) return 'application/octet-stream' if (file.endsWith('.deb')) return 'application/vnd.debian.binary-package' if (file.endsWith('.exe')) return 'application/vnd.microsoft.portable-executable' if (file.endsWith('.dmg')) return 'application/x-apple-diskimage' return 'application/octet-stream' } const getCurrentTag = () => { try { const tag = execSync('git describe --tags --exact-match 2>/dev/null', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], }).trim() return tag } catch { return null } } const isGitTagDirty = () => { try { const status = execSync('git status --porcelain', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], }).trim() return status.length > 0 } catch { return true } } const uploadFile = async (filePath, platform, version) => { const fileStream = fs.createReadStream(filePath) const fileName = path.basename(filePath) const key = `${platform}/${version}/${fileName}` console.log(`Uploading ${fileName} → ${key}`) await client.send( new PutObjectCommand({ Bucket: BUCKET, Key: key, Body: fileStream, ContentType: getContentType(fileName), }), ) } const uploadFileToRoot = async (filePath) => { const fileStream = fs.createReadStream(filePath) const fileName = path.basename(filePath) const key = `${fileName}` console.log(`Uploading ${fileName} → ${key} (latest)`) await client.send( new PutObjectCommand({ Bucket: BUCKET, Key: key, Body: fileStream, ContentType: getContentType(fileName), }), ) } const main = async () => { const platform = process.argv[2] const version = process.argv[3] if (!platform || !version) { console.error('Usage: node uploadArtifacts.js ') console.error('Example: node uploadArtifacts.js win 1.0.0') process.exit(1) } // const currentTag = getCurrentTag() // if (!currentTag) { // console.log( // 'No git tag found. Skipping upload (artifacts are only uploaded on tagged commits).', // ) // return // } // if (currentTag !== version && !currentTag.startsWith(version)) { // console.log( // `Git tag (${currentTag}) does not match version (${version}). Skipping upload.`, // ) // return // } // if (isGitTagDirty()) { // console.log('Git working directory is dirty. Skipping upload.') // return // } console.log( `Uploading artifacts for ${platform} v${version} (tag: ${currentTag})`, ) if (!fs.existsSync(DIST_DIR)) { throw new Error('dist directory not found. Run build first.') } const files = fs.readdirSync(DIST_DIR) const uploadTargets = files.filter((file) => { return ( file.endsWith('.yml') || file.endsWith('.AppImage') || file.endsWith('.deb') || file.endsWith('.exe') || file.endsWith('.dmg') ) }) if (uploadTargets.length === 0) { console.log('No artifacts found to upload.') return } for (const file of uploadTargets) { const fullPath = path.join(DIST_DIR, file) // Keep latest.yml up to date for platform if (file.startsWith('latest') && file.endsWith('.yml')) { await uploadFileToRoot(fullPath) } else { await uploadFile(fullPath, platform, version) } } console.log('Upload complete.') } main().catch((err) => { console.error(err) process.exit(1) })