trilium/scripts/migrate-releases.js

125 lines
3.6 KiB
JavaScript

// migrate-releases.js
const fetch = require('node-fetch').default;
const fs = require('fs');
const path = require('path');
const TOKEN = process.env.GITHUB_TOKEN;
const SOURCE_REPO = 'TriliumNext/Notes';
const DEST_REPO = 'TriliumNext/trilium';
if (!TOKEN) {
console.error('Error: Please set your GITHUB_TOKEN environment variable');
process.exit(1);
}
const headers = {
Authorization: `token ${TOKEN}`,
Accept: 'application/vnd.github.v3+json',
};
async function getReleases(repo) {
let releases = [];
let page = 1;
while (true) {
console.log("Got fetch", fetch);
const res = await fetch(
`https://api.github.com/repos/${repo}/releases?per_page=100&page=${page}`,
{ headers }
);
if (!res.ok) throw new Error(`Failed to get releases: ${res.status} ${res.statusText}`);
const data = await res.json();
if (data.length === 0) break;
releases = releases.concat(data);
page++;
}
return releases;
}
async function createRelease(repo, release) {
// Strip id, url etc. fields to prepare payload
const payload = {
tag_name: release.tag_name,
target_commitish: "main",
name: release.name,
body: release.body,
draft: release.draft,
prerelease: release.prerelease,
};
const res = await fetch(`https://api.github.com/repos/${repo}/releases`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
console.log(`POST to https://api.github.com/repos/${repo}/releases with payload:`, payload);
if (!res.ok) {
const text = await res.text();
throw new Error(`Failed to create release: ${res.status} ${res.statusText} - ${text}`);
}
return await res.json();
}
async function downloadAsset(assetUrl, filename) {
const res = await fetch(assetUrl, { headers: { ...headers, Accept: 'application/octet-stream' } });
if (!res.ok) throw new Error(`Failed to download asset: ${res.status} ${res.statusText}`);
const buffer = await res.buffer();
fs.writeFileSync(filename, buffer);
}
async function uploadAsset(uploadUrl, filepath) {
const filename = path.basename(filepath);
const stats = fs.statSync(filepath);
const res = await fetch(`${uploadUrl}?name=${encodeURIComponent(filename)}`, {
method: 'POST',
headers: {
Authorization: `token ${TOKEN}`,
'Content-Type': 'application/octet-stream',
'Content-Length': stats.size,
},
body: fs.createReadStream(filepath),
});
if (!res.ok) {
const text = await res.text();
throw new Error(`Failed to upload asset: ${res.status} ${res.statusText} - ${text}`);
}
return await res.json();
}
async function migrate() {
console.log(`Fetching releases from ${SOURCE_REPO}...`);
const releases = await getReleases(SOURCE_REPO);
console.log(`Found ${releases.length} releases.`);
releases.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
for (const release of releases) {
console.log(`Migrating release: ${release.name} (${release.tag_name})`);
const newRelease = await createRelease(DEST_REPO, release);
// Download and upload assets if any
for (const asset of release.assets) {
const tempFile = path.join(__dirname, asset.name);
console.log(`Downloading asset ${asset.name}...`);
await downloadAsset(asset.url, tempFile);
console.log(`Uploading asset ${asset.name}...`);
await uploadAsset(newRelease.upload_url.replace('{?name,label}', ''), tempFile);
fs.unlinkSync(tempFile); // Clean up temp file
}
}
console.log('Migration complete!');
}
migrate().catch((err) => {
console.error('Migration failed:', err);
process.exit(1);
});