2024-09-14 23:16:42 +02:00
|
|
|
import * as fs from 'fs';
|
2024-09-17 08:36:53 +02:00
|
|
|
import { createWriteStream } from 'fs';
|
2024-09-14 23:16:42 +02:00
|
|
|
import * as path from 'path';
|
2024-09-17 08:36:53 +02:00
|
|
|
import { pipeline } from 'stream/promises';
|
|
|
|
import axios from 'axios';
|
2024-09-14 23:16:42 +02:00
|
|
|
import { GitHubRelease, GitHubReleaseAsset } from '../utils/checkForUpdatesUtils';
|
|
|
|
|
|
|
|
export interface Context {
|
|
|
|
repo: string; // {owner}/{repo}
|
|
|
|
githubToken: string;
|
|
|
|
targetTag: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
const apiBaseUrl = 'https://api.github.com/repos/';
|
|
|
|
const defaultApiHeaders = (context: Context) => ({
|
2024-09-16 23:13:24 +02:00
|
|
|
'User-Agent': 'Joplin',
|
2024-09-14 23:16:42 +02:00
|
|
|
'Authorization': `token ${context.githubToken}`,
|
|
|
|
'X-GitHub-Api-Version': '2022-11-28',
|
|
|
|
'Accept': 'application/vnd.github+json',
|
|
|
|
});
|
|
|
|
|
|
|
|
export const getTargetRelease = async (context: Context, targetTag: string): Promise<GitHubRelease> => {
|
|
|
|
console.log('Fetching releases...');
|
|
|
|
|
|
|
|
// Note: We need to fetch all releases, not just /releases/tag/tag-name-here.
|
|
|
|
// The latter doesn't include draft releases.
|
|
|
|
|
|
|
|
const result = await fetch(`${apiBaseUrl}${context.repo}/releases`, {
|
|
|
|
method: 'GET',
|
|
|
|
headers: defaultApiHeaders(context),
|
|
|
|
});
|
|
|
|
|
|
|
|
const releases = await result.json();
|
|
|
|
if (!result.ok) {
|
|
|
|
throw new Error(`Error fetching release: ${JSON.stringify(releases)}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const release of releases) {
|
|
|
|
if (release.tag_name === targetTag) {
|
|
|
|
return release;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error(`No release with tag ${targetTag} found!`);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Download a file from Joplin Desktop releases
|
2024-09-16 23:13:24 +02:00
|
|
|
export const downloadFileFromGitHub = async (context: Context, asset: GitHubReleaseAsset, destinationDir: string) => {
|
2024-09-14 23:16:42 +02:00
|
|
|
const downloadPath = path.join(destinationDir, asset.name);
|
|
|
|
if (!fs.existsSync(destinationDir)) {
|
|
|
|
fs.mkdirSync(destinationDir);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* eslint-disable no-console */
|
2024-09-15 12:02:02 +02:00
|
|
|
console.log(`Downloading ${asset.name} from ${asset.url} to ${downloadPath}`);
|
2024-09-17 08:36:53 +02:00
|
|
|
try {
|
|
|
|
const response = await axios({
|
|
|
|
method: 'get',
|
|
|
|
url: asset.url,
|
|
|
|
responseType: 'stream',
|
|
|
|
headers: {
|
|
|
|
...defaultApiHeaders(context),
|
|
|
|
'Accept': 'application/octet-stream',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
if (response.status < 200 || response.status >= 300) {
|
|
|
|
throw new Error(`Failed to download file: Status Code ${response.status}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
await pipeline(response.data, createWriteStream(downloadPath));
|
|
|
|
console.log('Download successful!');
|
|
|
|
/* eslint-enable no-console */
|
|
|
|
return downloadPath;
|
|
|
|
} catch (error) {
|
|
|
|
throw new Error('Download not successful.');
|
|
|
|
}
|
2024-09-14 23:16:42 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
export const updateReleaseAsset = async (context: Context, assetUrl: string, newName: string) => {
|
|
|
|
console.log('Updating asset with URL', assetUrl, 'to have name, ', newName);
|
|
|
|
|
|
|
|
// See https://docs.github.com/en/rest/releases/assets?apiVersion=2022-11-28#update-a-release-asset
|
|
|
|
const result = await fetch(assetUrl, {
|
|
|
|
method: 'PATCH',
|
|
|
|
headers: defaultApiHeaders(context),
|
|
|
|
body: JSON.stringify({
|
|
|
|
name: newName,
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!result.ok) {
|
|
|
|
throw new Error(`Unable to update release asset: ${await result.text()}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export const uploadReleaseAsset = async (context: Context, release: GitHubRelease, filePath: string): Promise<void> => {
|
|
|
|
console.log(`Uploading file from ${filePath} to release ${release.tag_name}`);
|
|
|
|
|
|
|
|
const fileContent = fs.readFileSync(filePath);
|
|
|
|
const fileName = path.basename(filePath);
|
|
|
|
const uploadUrl = `https://uploads.github.com/repos/${context.repo}/releases/${release.id}/assets?name=${encodeURIComponent(fileName)}`;
|
|
|
|
|
|
|
|
const response = await fetch(uploadUrl, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: {
|
|
|
|
...defaultApiHeaders(context),
|
|
|
|
'Content-Type': 'application/octet-stream',
|
|
|
|
},
|
|
|
|
body: fileContent,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
throw new Error(`Failed to upload asset: ${await response.text()}`);
|
|
|
|
} else {
|
|
|
|
console.log(`${fileName} uploaded successfully.`);
|
|
|
|
}
|
|
|
|
};
|