bgipfs
Version:
BuidlGuidl IPFS CLI
156 lines (155 loc) • 6.43 kB
JavaScript
import { Flags } from '@oclif/core';
import { execa } from 'execa';
import { promises as fs } from 'node:fs';
import path from 'node:path';
import { BaseCommand } from '../../../base-command.js';
import { checkRunningContainers, getContainerVersions } from '../../../lib/system.js';
import Restart from '../restart/index.js';
import Start from '../start/index.js';
export default class Update extends BaseCommand {
static description = 'Update IPFS and IPFS Cluster to their latest versions';
static examples = [
'bgipfs cluster update',
'bgipfs cluster update --no-backup',
'bgipfs cluster update --backup-dir ./my-backup',
];
static flags = {
'backup-dir': Flags.string({
description: 'Directory to store backup (defaults to ./backup_YYYYMMDD_HHMMSS)',
}),
force: Flags.boolean({
char: 'f',
default: false,
description: 'Force update: skip confirmation prompts',
}),
'no-backup': Flags.boolean({
default: false,
description: 'Skip creating a backup before updating',
}),
};
async run() {
const { flags } = await this.parse(Update);
try {
// Check if services are running
const running = await checkRunningContainers();
const isRunning = running.length > 0;
// Get current versions if services are running
let currentVersions;
if (isRunning) {
try {
currentVersions = await getContainerVersions();
this.logInfo(`Current versions:
IPFS: ${currentVersions.ipfs}
IPFS Cluster: ${currentVersions.cluster}`);
}
catch (error) {
this.logError(`Failed to get current versions: ${error.message}`);
}
}
// Confirm update
if (!flags.force) {
const shouldUpdate = await this.confirm('Are you sure you want to update? This will require restarting the IPFS cluster.');
if (!shouldUpdate) {
this.logInfo('Update cancelled');
return;
}
}
// Handle backup
if (!flags['no-backup']) {
const backupDir = flags['backup-dir'] || `backup_${new Date().toISOString().replaceAll(/[.:]/g, '').slice(0, 15)}`;
if (flags.force) {
await this.createBackup(backupDir);
}
else {
const shouldBackup = await this.confirm(`Would you like to create a backup before updating? (Will be stored in ${backupDir})`);
if (shouldBackup) {
await this.createBackup(backupDir);
}
else {
this.logInfo('Skipping backup');
}
}
}
// Pull latest images first
this.logInfo('Pulling latest images...');
await execa('docker', ['compose', 'pull']);
// Check if versions changed
if (isRunning && !(await this.checkVersions(currentVersions))) {
return;
}
// Restart or start services
if (isRunning) {
this.logInfo('Restarting services with new versions...');
await Restart.run(['--force']);
}
else {
this.logInfo('Starting services with new versions...');
await Start.run([]);
}
this.logSuccess('IPFS cluster updated successfully');
if (!flags['no-backup'] && !flags.force) {
this.logInfo(`A backup was created in: ${flags['backup-dir'] || 'backup_*'}`);
}
}
catch (error) {
this.logError(`Update failed: ${error.message}`);
if (!flags['no-backup'] && !flags.force) {
this.logInfo('A backup was created before the update attempt');
}
}
}
async checkVersions(currentVersions) {
try {
const newVersions = await getContainerVersions();
if (currentVersions) {
const ipfsUpdated = newVersions.ipfs !== currentVersions.ipfs;
const clusterUpdated = newVersions.cluster !== currentVersions.cluster;
if (!ipfsUpdated && !clusterUpdated) {
this.logInfo('No updates available - already running latest versions');
return false;
}
this.logInfo(`New versions available:
IPFS: ${currentVersions.ipfs} -> ${newVersions.ipfs}
IPFS Cluster: ${currentVersions.cluster} -> ${newVersions.cluster}`);
}
return true;
}
catch (error) {
this.logError(`Failed to check new versions: ${error.message}`);
return true; // Continue with update if version check fails
}
}
async createBackup(backupDir) {
// Check if backup directory exists
try {
await fs.access(backupDir);
this.logError(`Backup directory ${backupDir} already exists`);
return;
}
catch {
// Directory doesn't exist, which is what we want
}
this.logInfo('Creating backup before update...');
await fs.mkdir(backupDir, { recursive: true });
// List of files and directories to backup
const itemsToBackup = [
{ dest: 'ipfs', src: 'data/ipfs' },
{ dest: 'ipfs-cluster', src: 'data/ipfs-cluster' },
{ dest: 'ipfs.config.json', src: 'ipfs.config.json' },
{ dest: 'service.json', src: 'service.json' },
{ dest: 'identity.json', src: 'identity.json' },
{ dest: 'auth', src: 'auth' },
];
// Copy each item
await Promise.all(itemsToBackup.map(async (item) => {
try {
this.logInfo(`Backing up ${item.src}...`);
await fs.cp(item.src, path.join(backupDir, item.dest), { recursive: true });
this.logSuccess(`Successfully backed up ${item.src}`);
}
catch (error) {
this.logError(`Failed to backup ${item.src}: ${error.message}`);
}
}));
}
}