Skip to content

Commit

Permalink
Lots of refactoring, cleanup and attempts to get 3.5 to perform well,…
Browse files Browse the repository at this point in the history
… plus version comparison updates

- adds previous commit messages to the createReleaseNotes
  • Loading branch information
sethwebster committed Aug 7, 2023
1 parent 6e6235d commit 6fe9bad
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 131 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sethwebster/ava-commit",
"version": "0.0.25",
"version": "0.0.26",
"description": "",
"main": "./src/index.ts",
"type": "module",
Expand Down Expand Up @@ -37,7 +37,8 @@
"commander": "^11.0.0",
"compare-versions": "^6.0.0",
"figlet": "^1.6.0",
"langchain": "^0.0.120"
"langchain": "^0.0.120",
"package-json-type": "^1.0.3"
},
"devDependencies": {
"@types/chalk-animation": "^1.6.1",
Expand Down
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ async function start() {
Logger.setVerbose(verbose);
await checkForLatestVersionAndNotify();
const program = new Command();
program.version(packageJson.packageVersion(), "-V,--version", MessagesForCurrentLanguage.messages["display-version-information"])
program.version(packageJson.packageVersion() ?? "0.0.0", "-V,--version", MessagesForCurrentLanguage.messages["display-version-information"])
.description(MessagesForCurrentLanguage.messages.description)
.name('ava-commit')
.addCommand(new Command("update").description(MessagesForCurrentLanguage.messages["update-command-description"])
Expand Down
6 changes: 6 additions & 0 deletions src/lib/consoleUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export function getConsoleSize() {
return {
columns: process.stdout.columns,
rows: process.stdout.rows
};
}
15 changes: 11 additions & 4 deletions src/lib/environment.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import fs from 'fs';
import packageJson from './packageJson.js';
import chalk from 'chalk';
import boxen, { Options } from 'boxen';
import { compareVersions } from 'compare-versions';
Expand All @@ -8,6 +7,7 @@ import { promiseWithTimeout } from './promiseWithTimeout.js';
import MessagesForCurrentLanguage from './messages.js';
import { input } from '@inquirer/prompts';
import Logger from './logger.js';
import packageJson from './packageJson.js';

export function makeAvaHomePath() {
return `${process.env.HOME}/.ava-commit`;
Expand Down Expand Up @@ -47,21 +47,28 @@ async function checkForLatestVersionSafeWithTimeout(timeout: number): Promise<Up
} catch (e) {
// Swallow the error
Logger.verbose("Error checking for latest version: ", e);
return { currentVersion: packageJson.packageVersion(), latestNpmVersion: packageJson.packageVersion(), updateAvailable: false };
return { currentVersion: (packageJson.packageVersion() ?? "0.0.0"), latestNpmVersion: packageJson.packageVersion() ?? "0.0.0", updateAvailable: false };
}
}

async function checkForLatestVersion(): Promise<UpdatePayload> {
const currentVersion = packageJson.packageVersion();
const currentVersion = packageJson.packageVersion() ?? "0.0.0";
const latestNpmVersion = await fetchLatestNpmVersion();
const versionComparison = compareVersions(currentVersion, latestNpmVersion);
return { currentVersion, latestNpmVersion, updateAvailable: versionComparison === -1 };
}

export async function fetchLatestNpmVersion() {
const response = await fetch(`https://registry.npmjs.org/@sethwebster/ava-commit/latest`);
Logger.verbose("Fetching latest npm version...")
const packageJsonData = await packageJson.loadPackageJson();
const { name } = packageJsonData;
if (!name) {
throw new Error("Failed to fetch latest npm version. No name in package.json");
}
const response = await fetch(`https://registry.npmjs.org/${name}/latest`);
const json = await response.json();
const latestVersion = json.version;
Logger.verbose(`Latest npmjs.org version for ${name} is ${latestVersion}`)
return latestVersion;
}

Expand Down
6 changes: 6 additions & 0 deletions src/lib/formatMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export function formatMessage(template: string, values: Record<string, string>): string {
return Object.entries(values).reduce(
(message, [key, value]) => message.replaceAll(`{${key}}`, value),
template
);
}
34 changes: 26 additions & 8 deletions src/lib/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import git from "./git.js";
import Logger from "./logger.js";
import MessagesForCurrentLanguage, { convertAnswerToDefault } from "./messages.js";
import { ChoicesType, GenerateOptions, GenerateStatusWithContext } from "../types.js";
import { getConsoleSize } from "./consoleUtils.js";

export default async function generate(options: GenerateOptions) {
Logger.verbose("Generate is starting...")
Expand Down Expand Up @@ -112,25 +113,38 @@ async function handleCombine(context: GenerateStatusWithContext, options: Genera
const { commitMessages, diffs, openAIApiKey } = context;
const { length } = options;
const choices = commitMessages.map((s, i) => ({ name: `${i + 1}. ${s}`, value: i + 1 }));
let numbers: number[] = [];
let numbers: (number | string)[] = [];
const { rows } = getConsoleSize();
const totalLinesInCommitMessages = commitMessages.map(s => s.split("\n").length).reduce((a, b) => a + b, 0);
const pageSize = Math.min(rows - 2, Math.max(20, totalLinesInCommitMessages));

if (choices.length > 2) {
numbers = await checkbox<number>({
numbers = await checkbox<number | string>({
message: MessagesForCurrentLanguage.prompts["combine-summaries-selection"].text,
choices: [
{ name: "0. Back", value: 0 },
{ name: "-. Back", value: 0 },
{ name: "A. All", value: "A" },
...choices,
],
pageSize: 20
pageSize
});
} else {
numbers = [1, 2]
}
if (numbers.length === 0 || numbers[0] === 0) {
return { ...context, status: "continue" };
}
const combined = numbers.map(n => commitMessages[n - 1]);
const resummarized = await combineSummaries({ openAIApiKey, summaries: combined, maxLength: length });
console.log(MessagesForCurrentLanguage.messages["summaries-combined-confirmation"] + "\n", resummarized);

let combined: string[] = [];
const allSelected = !!(numbers.find(val => val === "A"))
if (allSelected) {
combined = commitMessages;
} else {
const numbersFixed = numbers.map(n => parseInt(n.toString()));
combined = numbersFixed.map(n => commitMessages[n - 1]);
}
const resummarized = await combineSummaries({ openAIApiKey, summaries: combined, maxLength: length, verbose: options.verbose });
// console.log(MessagesForCurrentLanguage.messages["summaries-combined-confirmation"] + "\n", resummarized);
return await getUserResponseToMessages({ status: "continue", diffs, summaries: combined, commitMessages: [resummarized], openAIApiKey }, options);
}

Expand Down Expand Up @@ -164,16 +178,20 @@ async function summariesRouter(answer: string, context: GenerateStatusWithContex
async function getUserResponseToMessages(statusWithContext: GenerateStatusWithContext, options: GenerateOptions): Promise<GenerateStatusWithContext> {
const { commitMessages } = statusWithContext;
let status: GenerateStatusWithContext = { ...statusWithContext, status: "continue" }
const totalLinesInCommitMessages = commitMessages.map(s => s.split("\n").length).reduce((a, b) => a + b, 0);
const { rows } = getConsoleSize();
while (status.status === "continue") {
const choices = await generateChoices(commitMessages);
const key = choices.length > 1 ? "accept-summary-selection" : "accept-summary-single";
const pageSize = Math.min(rows - 2, Math.max(20, totalLinesInCommitMessages));

const promptOptions = {
message: MessagesForCurrentLanguage.prompts[key].text,
choices: [
{ name: "<", value: "n" },
...choices,
],
pageSize: 20,
pageSize,
}
const userAnswer = await select<string | number>(promptOptions);

Expand Down
16 changes: 16 additions & 0 deletions src/lib/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ async function diff(options?: DiffOptions) {
});
}

async function log({ baseCompare, compare }: Omit<DiffOptions, "staged">) {
try {
const logResult = spawn("git", ["log", "--pretty=%B", baseCompare ?? "", compare ?? "HEAD"]);
const lines = (logResult ?? "").split("\n").filter(l => l.trim().length > 0);
return lines;
} catch (e) {
if (e instanceof Error) {
if (e.message.includes("ambiguous argument")) {
Logger.verbose("git.log - No commits found")
return [];
}
}
}
}

async function fetch({ all }: { all?: boolean } = {}) {
return new Promise((resolve, reject) => {
try {
Expand Down Expand Up @@ -125,6 +140,7 @@ const git = {
fetch,
push,
tags,
log
}

export default git;
15 changes: 15 additions & 0 deletions src/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ const Messages: Messages = {
"option-push-description": "Push to remote after commit",
"update-package-version": "The version in your package.json is the same or lower than the latest tagged version. Please update your package.json version to match the latest tag.",
"select-version-update-type": "Select version update type:",
"latest-tagged-is-greater-than-local-but-npm-is-newer": "Local package.json version {packageJsonVersion} is older than the latest tagged version {latestTaggedGitVersion} and NPM version {latestNpmVersion}.\nDo you have the latest changes?",
"npm-is-newer-than-local": "NPM version {latestNpmVersion} is newer than the local version {packageJsonVersion}.\nDo you have the latest changes?",

// Version update types
"major": "Major",
"minor": "Minor",
Expand Down Expand Up @@ -290,6 +293,8 @@ const Messages: Messages = {
"major": "Mayor",
"minor": "Menor",
"patch": "Parche",
"latest-tagged-is-greater-than-local-but-npm-is-newer": "La versión local de package.json {packageJsonVersion} es más antigua que la última versión etiquetada {latestTaggedGitVersion} y la versión de NPM {latestNpmVersion}.\n¿Tiene los últimos cambios?",
"npm-is-newer-than-local": "La versión de NPM {latestNpmVersion} es más reciente que la versión local {packageJsonVersion}.\n¿Tiene los últimos cambios?",
},
errors: {
"no-diff": "No hay cambios para hacer commit",
Expand Down Expand Up @@ -390,6 +395,8 @@ const Messages: Messages = {
"major": "Majeur",
"minor": "Mineur",
"patch": "Patch",
"latest-tagged-is-greater-than-local-but-npm-is-newer": "La version locale de package.json {packageJsonVersion} est plus ancienne que la dernière version étiquetée {latestTaggedGitVersion} et la version de NPM {latestNpmVersion}.\nAvez-vous les derniers changements ?",
"npm-is-newer-than-local": "La version de NPM {latestNpmVersion} est plus récente que la version locale {packageJsonVersion}.\nAvez-vous les derniers changements ?",
},
errors: {
"no-diff": "Aucun changement à commiter",
Expand Down Expand Up @@ -507,6 +514,8 @@ const Messages: Messages = {
"major": "Maggiore",
"minor": "Minore",
"patch": "Patch",
"latest-tagged-is-greater-than-local-but-npm-is-newer": "La versione locale di package.json {packageJsonVersion} è più vecchia dell'ultima versione taggata {latestTaggedGitVersion} e la versione di NPM {latestNpmVersion}.\nHai gli ultimi cambiamenti?",
"npm-is-newer-than-local": "La versione di NPM {latestNpmVersion} è più recente della versione locale {packageJsonVersion}.\nHai gli ultimi cambiamenti?",
},
errors: {
"no-diff": "Nessuna modifica da commitare",
Expand Down Expand Up @@ -611,6 +620,8 @@ const Messages: Messages = {
"major": "メジャー",
"minor": "マイナー",
"patch": "パッチ",
"latest-tagged-is-greater-than-local-but-npm-is-newer": "package.jsonのローカルバージョン{packageJsonVersion}は、最新のタグバージョン{latestTaggedGitVersion}とNPMバージョン{latestNpmVersion}よりも古いです。\n最新の変更を持っていますか?",
"npm-is-newer-than-local": "NPMバージョン{latestNpmVersion}は、ローカルバージョン{packageJsonVersion}よりも新しいです。\n最新の変更を持っていますか?",
},
"errors": {
"no-diff": "コミットする変更がありません",
Expand Down Expand Up @@ -803,6 +814,8 @@ const Messages: Messages = {
"major": "Основной",
"minor": "Минорный",
"patch": "Патч",
"latest-tagged-is-greater-than-local-but-npm-is-newer": "Локальная версия package.json {packageJsonVersion} старше, чем последняя тегированная версия {latestTaggedGitVersion} и версия NPM {latestNpmVersion}.\nУ вас последние изменения?",
"npm-is-newer-than-local": "Версия NPM {latestNpmVersion} новее, чем локальная версия {packageJsonVersion}.\nУ вас последние изменения?",
},
"errors": {
"no-diff": "Нет изменений для коммита",
Expand Down Expand Up @@ -950,6 +963,8 @@ const Messages: Messages = {
"major": "Основний",
"minor": "Мінорний",
"patch": "Патч",
"latest-tagged-is-greater-than-local-but-npm-is-newer": "Локальна версія package.json {packageJsonVersion} старша за останню помічену версію {latestTaggedGitVersion} та версію NPM {latestNpmVersion}.\nУ вас останні зміни?",
"npm-is-newer-than-local": "Версія NPM {latestNpmVersion} новіша за локальну версію {packageJsonVersion}.\nУ вас останні зміни?",
},
"errors": {
"no-diff": "Немає змін для коміту"
Expand Down
9 changes: 5 additions & 4 deletions src/lib/packageJson.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import fs from 'fs';
import * as url from 'url';
import {IPackageJson} from 'package-json-type'

function loadPackageJsonFromProject() {
function loadPackageJsonFromProject(): IPackageJson {
const __dirname = process.env.PWD;
const packageJson = fs.readFileSync(`${__dirname}/package.json`);
return JSON.parse(packageJson.toString());
}

function loadPackageJson() {
function loadPackageJson(): IPackageJson {
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
const packageJson = fs.readFileSync(`${__dirname}../../package.json`);
return JSON.parse(packageJson.toString());
}

function packageVersion() {
function packageVersion(): IPackageJson["version"] {
const packageJson = loadPackageJson();
return packageJson.version;
}

function packageVersionFromProject() {
function packageVersionFromProject(): Required<IPackageJson["version"]> {
const packageJson = loadPackageJsonFromProject();
return packageJson.version;
}
Expand Down
37 changes: 12 additions & 25 deletions src/lib/promptTemplates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@ import { PromptTemplate } from "langchain/prompts";

const combineSummaries = new PromptTemplate({
inputVariables: ["summaries"],
template: `Combine the following summaries into a single summary.
It should have a first line (no more than 50 chars) overall summary followed by bullets that expand on the summary.
Example Output:
Makes a change to the UI allowing the user to cancel running jobs
- refactored JobsList.tsx to use the new API
- added a new button to the UI
- added a new API endpoint to cancel jobs
- something else in the diffs
- etc.
Do not remove any important information:
template: `
-- instructions --
You are to combine a list of other summaries, into a single summary, while keeping all important information.
Your response MUST:
1. Contain a first line that is 50 or fewer characters.
2. Be followed by between 5 and 10 bulleted lines which give more detail on the summary.
-- summaries --
{summaries}
Expand Down Expand Up @@ -59,14 +55,7 @@ const reSummarizeDiff = new PromptTemplate({
Focus on WHY the change was made, not WHAT the change was. Use your context of the code for the WHY, not general knowledge.
Note: lines that start with a + were added, lines that start with a - were removed. Only use the + and - lines for the substance of the summary, while using the lines around for context.
Example:
Makes a change to the UI allowing the user to cancel running jobs
- refactored JobsList.tsx to use the new API
- added a new button to the UI
- added a new API endpoint to cancel jobs
-- diff --
{diff}
Expand Down Expand Up @@ -223,20 +212,18 @@ const summarizeSummaries = new PromptTemplate({
});

const releaseNotes = new PromptTemplate({
inputVariables: ["summaries", "numberOfDiffs", "latest"],
inputVariables: ["summaries", "numberOfDiffs", "previous", "latest"],
template: `These are summaries of {numberOfDiffs} diffs between product releases.
-- instructions --
Purpose:
Create awesome, exciting release notes of the change between the last release ({latest}) and now. Use GitHub flavored markdown.
Create awesome, exciting release notes of the change between the last release ({previous}) and now ({latest}). Use GitHub flavored markdown.
Focus on WHY the change was made, not WHAT the change was.
Special Note: If functionality has changed, but the version in the package.json hasn't changed, return a header on the options: [CHECK PACKAGE VERSION]
-- input content --
{summaries}
{previousCommitMessages}
-- example output --
# Release notes for <new version>!
Expand Down
Loading

0 comments on commit 6fe9bad

Please sign in to comment.