/ utils / addUpdate.ts
addUpdate.ts
  1  /**
  2   * @file adds the new version to the `updates.json`
  3   *
  4   * Usage:
  5   *
  6   * 1. add the current version:
  7   *      pnpm add-update --file /path/to/coub-addons-0.1.26-firefox.xpi
  8   *
  9   * 2. add a specific version:
 10   *    - using file:
 11   *        pnpm add-update --version 0.1.26 --file /path/to/coub-addons-0.1.26-firefox.xpi
 12   *
 13   *    - using precomputed hash:
 14   *        pnpm add-update --version 0.1.26 --sha256 sha256_hash_of_xpi
 15   */
 16  
 17  import { createHash } from 'node:crypto';
 18  import { createReadStream } from 'node:fs';
 19  import fs from 'node:fs/promises';
 20  import path from 'node:path';
 21  import { buffer } from 'node:stream/consumers';
 22  import { fileURLToPath } from 'node:url';
 23  import { parseArgs } from 'node:util';
 24  import { codeToANSI } from '@shikijs/cli';
 25  import { execa } from 'execa';
 26  
 27  import updates from '../docs/updates.json' with { type: 'json' };
 28  import pkg from '../package.json' with { type: 'json' };
 29  import { geckoManifest } from '../wxt.config';
 30  
 31  const ROOT_PATH = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
 32  const UPDATES_FILE = path.join(ROOT_PATH, 'docs', 'updates.json');
 33  
 34  const {
 35  	values: { version: argsVersion, file, sha256 },
 36  } = parseArgs({
 37  	options: {
 38  		version: {
 39  			type: 'string',
 40  			short: 'v',
 41  		},
 42  		file: {
 43  			type: 'string',
 44  			short: 'f',
 45  		},
 46  		sha256: {
 47  			type: 'string',
 48  		},
 49  	},
 50  });
 51  
 52  const version = argsVersion || pkg.version;
 53  
 54  if (!version) {
 55  	throw new RangeError(`\`--version\` is required, got \`${JSON.stringify(version)}\``);
 56  }
 57  
 58  let newUpdateHash: string;
 59  
 60  if (sha256) {
 61  	if (!argsVersion) {
 62  		throw new Error('`--sha256` can only be used with `--version`');
 63  	}
 64  
 65  	newUpdateHash = sha256;
 66  } else {
 67  	if (!file) {
 68  		throw new RangeError(`path to the XPI \`--file\` is required, got \`${JSON.stringify(file)}\``);
 69  	}
 70  
 71  	try {
 72  		newUpdateHash = (await buffer(createReadStream(file).pipe(createHash('sha256')))).toString(
 73  			'hex',
 74  		);
 75  	} catch (err) {
 76  		console.error('Failed to hash', file, err);
 77  		process.exit(1);
 78  	}
 79  }
 80  
 81  interface Update {
 82  	version: string;
 83  	update_link: string;
 84  	update_info_url: string;
 85  	update_hash: string;
 86  	applications: {
 87  		gecko: {
 88  			strict_min_version: string;
 89  		};
 90  	};
 91  }
 92  
 93  const newUpdate: Update = {
 94  	version,
 95  	update_link: `https://github.com/hikiko4ern/coub-addons/releases/download/v${version}/coub-addons-${version}-firefox.xpi`,
 96  	update_info_url: `https://coub-addons.doggo.moe/release-notes/${version}.html`,
 97  	update_hash: `sha256:${newUpdateHash}`,
 98  	applications: {
 99  		gecko: {
100  			strict_min_version: geckoManifest.strict_min_version,
101  		},
102  	},
103  };
104  
105  const oldExtUpdates = updates.addons[process.env.VITE_GECKO_ID as keyof typeof updates.addons];
106  const sameVersionOldUpdate = oldExtUpdates.updates.findIndex(u => u.version === version);
107  
108  const newUpdates: typeof updates = {
109  	...updates,
110  	addons: {
111  		...updates.addons,
112  		[process.env.VITE_GECKO_ID]: {
113  			...oldExtUpdates,
114  			updates: oldExtUpdates.updates.toSpliced(
115  				sameVersionOldUpdate === -1 ? oldExtUpdates.updates.length : sameVersionOldUpdate,
116  				sameVersionOldUpdate === -1 ? 0 : 1,
117  				newUpdate,
118  			),
119  		},
120  	},
121  };
122  
123  console.log('Writing to', path.relative(ROOT_PATH, UPDATES_FILE));
124  console.log();
125  
126  if (sameVersionOldUpdate !== -1) {
127  	console.warn('Replacing old update');
128  	console.warn(
129  		await codeToANSI(
130  			JSON.stringify(oldExtUpdates.updates[sameVersionOldUpdate], null, 2),
131  			'json',
132  			'ayu-dark',
133  		),
134  	);
135  	console.log();
136  }
137  
138  console.log('New update:');
139  console.log(await codeToANSI(JSON.stringify(newUpdate, null, 2), 'json', 'ayu-dark'));
140  
141  await fs.writeFile(UPDATES_FILE, JSON.stringify(newUpdates, null, 2), { encoding: 'utf8' });
142  
143  await execa({
144  	stdout: 'inherit',
145  	stderr: 'inherit',
146  })`pnpm dprint fmt ${UPDATES_FILE}`;