Skip to content

Commit 822ae40

Browse files
committed
Merge Commit '22b7d86': Add support for auto-updating git extensions (google-gemini#8511)
2 parents 30348b0 + 22b7d86 commit 822ae40

File tree

20 files changed

+1359
-540
lines changed

20 files changed

+1359
-540
lines changed

packages/cli/src/commands/extensions/install.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import { describe, it, expect, type MockInstance } from 'vitest';
7+
import { describe, it, expect, vi, type MockInstance } from 'vitest';
88
import { handleInstall, installCommand } from './install.js';
99
import yargs from 'yargs';
1010

@@ -32,6 +32,15 @@ describe('extensions install command', () => {
3232
validationParser.parse('install some-url --path /some/path'),
3333
).toThrow('Arguments source and path are mutually exclusive');
3434
});
35+
36+
it('should fail if both auto update and local path are provided', () => {
37+
const validationParser = yargs([]).command(installCommand).fail(false);
38+
expect(() =>
39+
validationParser.parse(
40+
'install some-url --path /some/path --auto-update',
41+
),
42+
).toThrow('Arguments path and auto-update are mutually exclusive');
43+
});
3544
});
3645

3746
describe('handleInstall', () => {

packages/cli/src/commands/extensions/install.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ interface InstallArgs {
1515
source?: string;
1616
path?: string;
1717
ref?: string;
18+
autoUpdate?: boolean;
1819
}
1920

2021
export async function handleInstall(args: InstallArgs) {
@@ -33,6 +34,7 @@ export async function handleInstall(args: InstallArgs) {
3334
source,
3435
type: 'git',
3536
ref: args.ref,
37+
autoUpdate: args.autoUpdate,
3638
};
3739
} else {
3840
throw new Error(
@@ -43,6 +45,7 @@ export async function handleInstall(args: InstallArgs) {
4345
installMetadata = {
4446
source: args.path,
4547
type: 'local',
48+
autoUpdate: args.autoUpdate,
4649
};
4750
} else {
4851
// This should not be reached due to the yargs check.
@@ -76,8 +79,13 @@ export const installCommand: CommandModule = {
7679
describe: t('commands.extensions.install.ref_description', 'The git ref to install from.'),
7780
type: 'string',
7881
})
82+
.option('auto-update', {
83+
describe: 'Enable auto-update for this extension.',
84+
type: 'boolean',
85+
})
7986
.conflicts('source', 'path')
8087
.conflicts('path', 'ref')
88+
.conflicts('path', 'auto-update')
8189
.check((argv) => {
8290
if (!argv.source && !argv.path) {
8391
throw new Error(
@@ -91,6 +99,7 @@ export const installCommand: CommandModule = {
9199
source: argv['source'] as string | undefined,
92100
path: argv['path'] as string | undefined,
93101
ref: argv['ref'] as string | undefined,
102+
autoUpdate: argv['auto-update'] as boolean | undefined,
94103
});
95104
},
96105
};

packages/cli/src/commands/extensions/update.ts

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@
66

77
import type { CommandModule } from 'yargs';
88
import {
9-
updateExtensionByName,
10-
updateAllUpdatableExtensions,
11-
type ExtensionUpdateInfo,
129
loadExtensions,
1310
annotateActiveExtensions,
14-
checkForAllExtensionUpdates,
1511
} from '../../config/extension.js';
1612
import { t } from '@thacio/auditaria-cli-core';
13+
import {
14+
updateAllUpdatableExtensions,
15+
type ExtensionUpdateInfo,
16+
checkForAllExtensionUpdates,
17+
updateExtension,
18+
} from '../../config/extensions/update.js';
19+
import { checkForExtensionUpdate } from '../../config/extensions/github.js';
1720
import { getErrorMessage } from '../../utils/errors.js';
21+
import { ExtensionUpdateState } from '../../ui/state/extensions.js';
1822

1923
interface UpdateArgs {
2024
name?: string;
@@ -46,7 +50,7 @@ export async function handleUpdate(args: UpdateArgs) {
4650
let updateInfos = await updateAllUpdatableExtensions(
4751
workingDir,
4852
extensions,
49-
await checkForAllExtensionUpdates(extensions, (_) => {}),
53+
await checkForAllExtensionUpdates(extensions, new Map(), (_) => {}),
5054
() => {},
5155
);
5256
updateInfos = updateInfos.filter(
@@ -68,13 +72,50 @@ export async function handleUpdate(args: UpdateArgs) {
6872
}
6973
if (args.name)
7074
try {
75+
const extension = extensions.find(
76+
(extension) => extension.name === args.name,
77+
);
78+
if (!extension) {
79+
console.log(
80+
t(
81+
'commands.extensions.update.extension_not_found',
82+
`Extension "${args.name}" not found.`,
83+
{ name: args.name },
84+
),
85+
);
86+
return;
87+
}
88+
let updateState: ExtensionUpdateState | undefined;
89+
if (!extension.installMetadata) {
90+
console.log(
91+
t(
92+
'commands.extensions.update.missing_install_metadata',
93+
`Unable to install extension "${args.name}" due to missing install metadata`,
94+
{ name: args.name },
95+
),
96+
);
97+
return;
98+
}
99+
await checkForExtensionUpdate(extension, (newState) => {
100+
updateState = newState;
101+
});
102+
if (updateState !== ExtensionUpdateState.UPDATE_AVAILABLE) {
103+
console.log(
104+
t(
105+
'commands.extensions.update.already_up_to_date',
106+
`Extension "${args.name}" is already up to date.`,
107+
{ name: args.name },
108+
),
109+
);
110+
return;
111+
}
71112
// TODO(chrstnb): we should list extensions if the requested extension is not installed.
72-
const updatedExtensionInfo = await updateExtensionByName(
73-
args.name,
113+
const updatedExtensionInfo = (await updateExtension(
114+
extension,
74115
workingDir,
75-
extensions,
116+
updateState,
76117
() => {},
77-
);
118+
))!;
78119
if (
79120
updatedExtensionInfo.originalVersion !==
80121
updatedExtensionInfo.updatedVersion
@@ -84,7 +125,7 @@ export async function handleUpdate(args: UpdateArgs) {
84125
console.log(
85126
t(
86127
'commands.extensions.update.already_up_to_date',
87-
`Extension "${args.name}" already up to date.`,
128+
`Extension "${args.name}" is already up to date.`,
88129
{ name: args.name },
89130
),
90131
);

0 commit comments

Comments
 (0)