Skip to content

Commit

Permalink
Implemented updates to various functions and files to enhance version…
Browse files Browse the repository at this point in the history
… checking and auto-update capabilities.

- Modified the start function to use a different function for checking the latest version and added a new command for checking updates and performing auto-updates.
- Updated `cancelablePromise` function to include a default export and added a new variable `interval`.
- Modified `readline` function in `consoleHelpers.ts` to remove the `async` keyword.
- Modified `environment.ts` file to include additional logic for notifying and offering an update.
- Renamed `checkForLatestVersionSafeWithTimeout` to `checkForLatestVersionAndNotify` and modified it to return a `Promise` with an `UpdatePayload` object.
- Added `notifyUpdate`, `offerUpdate`, and `doAutoUpdate` functions.
- Updated `spawn.ts` file in the `src/lib` directory to include additional functions from the `child_process` module.
- Updated `spawn` function to include an additional `exec` function that executes a command and returns a promise.
  • Loading branch information
sethwebster committed Aug 2, 2023
1 parent 591d09f commit 958c286
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 19 deletions.
7 changes: 5 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import { configure } from './lib/configure.js';
import packageJson from './lib/packageJson.js';
import { createReleaseNotes } from './lib/releaseNotes.js';
import generate from './lib/generate.js';
import { checkForLatestVersionSafeWithTimeout } from './lib/environment.js';
import { checkForLatestVersionAndNotify, doAutoUpdate } from './lib/environment.js';

async function start() {
await checkForLatestVersionSafeWithTimeout(600);
await checkForLatestVersionAndNotify();
const program = new Command();
program.version(packageJson.packageVersion())
.description("🤖 Use AI to write your commit messages")
.name("ava-commit")
.addCommand(new Command("update").description("Check for updates").action(() => {
doAutoUpdate();
}))
.addCommand(new Command("release-notes").description("Generates release notes based on what's changed since the most recent tag").action((options) => createReleaseNotes(options)))
.addCommand(new Command("configure").description("Configure the tool").action((options) => configure(options)))
.addCommand(new Command("generate").description("Generate a commit message")
Expand Down
16 changes: 11 additions & 5 deletions src/lib/cancelablePromise.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
async function cancelablePromise<T>(fn: (resolve: (value: T) => void, reject: (reason?: any) => void) => void, timeoutMs?: number): Promise<T> {
export default function cancelablePromise<T>(fn: (resolve: (value: T) => void, reject: (reason?: any) => void) => void, timeoutMs?: number): Promise<T> {
return new Promise<T>((resolve, reject) => {
let interval: NodeJS.Timeout;
if (timeoutMs) {
setTimeout(() => {
interval = setTimeout(() => {
reject("Promise timed out");
}, timeoutMs);
}
fn(resolve, reject);
const res = (value: T) => {
if (interval) {
clearTimeout(interval);
}
resolve(value);
}
fn(res, reject)
});
}
export default cancelablePromise;
}
2 changes: 1 addition & 1 deletion src/lib/consoleHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Readline from 'readline';
async function readline(question: string) {
function readline(question: string) {
return new Promise<string>((resolve, reject) => {
const rl = Readline.createInterface({
input: process.stdin,
Expand Down
95 changes: 85 additions & 10 deletions src/lib/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import fs from 'fs';
import packageJson from './packageJson.js';
import chalk from 'chalk';
import boxen, { Options } from 'boxen';
import cancelablePromise from './CancelablePromise.js';
import { compareVersions } from 'compare-versions';
import { exec, spawn } from './spawn.js';
import consoleHelpers from './consoleHelpers.js';
import cancelablePromise from './cancelablePromise.js';

export function makeAvaHomePath() {
return `${process.env.HOME}/.ava-commit`;
Expand All @@ -24,31 +26,46 @@ export function ensureAvaDiffCachePathExists() {
}
}

export async function checkForLatestVersionSafeWithTimeout(timeout: number) {
return cancelablePromise(checkForLatestVersionSafe, timeout).catch(e => {});
export async function checkForLatestVersionAndNotify() {
const { currentVersion, latestVersion, updateAvailable } = await checkForLatestVersionSafeWithTimeout(500);
if (!updateAvailable) return;
notifyUpdate({ currentVersion, latestVersion, updateAvailable });
if (updateAvailable) {
await offerUpdate();
}
}

export async function checkForLatestVersionSafe() {
async function checkForLatestVersionSafeWithTimeout(timeout: number): Promise<UpdatePayload> {
try {
await checkForLatestVersion();
const result = await cancelablePromise<UpdatePayload>((resolve) => checkForLatestVersion().then(resolve), timeout);
return result;
} catch (e) {
// Swallow the error
return { currentVersion: packageJson.packageVersion(), latestVersion: packageJson.packageVersion(), updateAvailable: false };
}
}

export async function checkForLatestVersion() {
//{"name":"@sethwebster/ava-commit","version":"0.0.10","description":"`ava-commit` is a command-line tool that uses ChatGPT to generate git commit messages automatically. It leverages the capabilities of AI to produce informative, human-like messages. This was created as a fun pet project while I couldn't sleep and has pro","main":"./src/index.ts","type":"module","repository":{"type":"git","url":"git+https://github.com/sethwebster/ava-commit.git"},"bin":{"ava-commit":"dist/index.js"},"scripts":{"dev":"tsc -w","build":"tsc","test":"echo \"Error: no test specified\" && exit 1"},"keywords":["ai","gpt","git"],"author":{"name":"Seth Webster"},"license":"ISC","dependencies":{"chalk":"^4.1.2","chalk-animation":"^2.0.3","commander":"^11.0.0","figlet":"^1.6.0","langchain":"^0.0.120"},"devDependencies":{"@types/chalk-animation":"^1.6.1","@types/node":"^20.4.5","typescript":"^5.1.6"},"gitHead":"4c4023ec80ec473171847085dd8e7a439122a1ca","bugs":{"url":"https://github.com/sethwebster/ava-commit/issues"},"homepage":"https://github.com/sethwebster/ava-commit#readme","_id":"@sethwebster/[email protected]","_nodeVersion":"18.17.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-UxV3n21qHsE+8Sl4664U7ikTTkhWBMgbl8X2QjoT1aETBkUVk2qozhzc//7XJSwfPWaq0bTJuwKH5AhMQlnnCQ==","shasum":"eb5bcb6f466af3042b053373b9ca495f73a13cb1","tarball":"https://registry.npmjs.org/@sethwebster/ava-commit/-/ava-commit-0.0.10.tgz","fileCount":49,"unpackedSize":131755,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEhIsDQmGthERFR/t5qPb4u7V3hhtSzwphGmrGshpje8AiEAoXcBNsCaHmr20QeSlYw18AwOioPLxBu9Hq9C3DqP6ng="}]},"_npmUser":{"name":"sethwebster","email":"[email protected]"},"directories":{},"maintainers":[{"name":"sethwebster","email":"[email protected]"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ava-commit_0.0.10_1690928641013_0.20577745860724161"},"_hasShrinkwrap":false}
//https://registry.npmjs.org/@sethwebster/ava-commit/latest
async function checkForLatestVersion(): Promise<UpdatePayload> {
const currentVersion = packageJson.packageVersion();
const response = await fetch(`https://registry.npmjs.org/@sethwebster/ava-commit/latest`);
const json = await response.json();
const latestVersion = json.version;
const versionComparison = compareVersions(currentVersion, latestVersion);
if (versionComparison === -1) {
return { currentVersion, latestVersion, updateAvailable: versionComparison === -1 };
}

interface UpdatePayload {
currentVersion: string;
latestVersion: string;
updateAvailable: boolean;
}

function notifyUpdate({ currentVersion, latestVersion, updateAvailable }: UpdatePayload) {
if (updateAvailable) {
// Draw a corner and a line
const options: Options = {
padding: 1,
margin:1,
margin: 1,
borderStyle: "round",
dimBorder: true,
title: "An update is available",
Expand All @@ -63,6 +80,64 @@ export async function checkForLatestVersion() {
}
}

async function offerUpdate() {
const userUpdateOfferAnswer = await consoleHelpers.readline("Would you like to update now? (Y/n) ");
const trimmed = userUpdateOfferAnswer.trim().toLowerCase();
if (trimmed === "y" || trimmed.length === 0) {
await doAutoUpdate();
}
}

export async function doAutoUpdate() {
const updated: { [key: string]: "updated" | "error" } = {};

try {
const npm = await exec("npm list -g");
if (npm && npm.length > 0 && npm.includes("@sethwebster/ava-commit")) {
const update = spawn("npm", ["i", "-g", "@sethwebster/ava-commit"]);
if (update && update.length > 0) {
updated["npm"] = "updated";
}
}
} catch (e) {
updated["npm"] = "error";
}

try {
const pnpm = await exec("pnpm list -g");
if (pnpm && pnpm.length > 0 && pnpm.includes("@sethwebster/ava-commit")) {
const update = spawn("pnpm", ["i", "-g", "@sethwebster/ava-commit"]);
if (update && update.length > 0) {
updated["pnpm"] = "updated";
}
}
} catch (e) {
updated["pnpm"] = "error";
}

try {
const yarn = await exec("yarn global list");
if (yarn && yarn.length > 0 && yarn.includes("@sethwebster/ava-commit")) {
const update = spawn("yarn", ["global", "add", "@sethwebster/ava-commit"]);
if (update && update.length > 0) {
updated["yarn"] = "updated";
}
}
} catch (e) {
updated["yarn"] = "error";
}

const updatedKeys = Object.keys(updated).filter(k => updated[k] === "updated");
if (updatedKeys.length > 0) {
console.log(`Updated the following package managers:`);
updatedKeys.forEach(k => console.log(chalk.green(k)));
console.log("Please run ava-commit again.");
process.exit(0);
}
}



export function makeAvaDiffCacheFilePath(hash: string) {
return `${makeAvaDiffCachePath()}/${hash}.json`;
}
Expand Down
19 changes: 18 additions & 1 deletion src/lib/spawn.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { spawnSync } from "child_process";
import { spawn as spawnBase, spawnSync, exec as execBase } from "child_process";

export function spawn(command: string, args: string[]) {
const result = spawnSync(command, args, {
Expand All @@ -19,4 +19,21 @@ export function spawn(command: string, args: string[]) {
if (result.stdout && result.stdout.length > 0) {
return result.stdout.toString();
}
}

export async function exec(command: string) {
return new Promise<string>((resolve, reject) => {
execBase(command, (error, stdout, stderr) => {
if (error) {
reject(error);
}
if (stderr && stderr.length > 0) {
reject(stderr);
}
if (stdout && stdout.length > 0) {
resolve(stdout);
}
resolve("Nothing")
});
});
}

0 comments on commit 958c286

Please sign in to comment.