Implement feature toggles for beta commands

- Added feature toggles for beta commands in the package.json file.
- Implemented dynamic registration of commands based on feature toggles.
- Added error messages for users trying to access beta commands without the correct permissions.
- Updated the version of the devchat-ask package based on the feature toggles.
- Created a new featureToggle handler to manage feature toggles.
This commit is contained in:
bobo.yang 2023-08-24 10:45:51 +08:00
parent fa9bb41fd6
commit 9c16bced23
10 changed files with 159 additions and 27 deletions

View File

@ -2,7 +2,7 @@
"name": "devchat",
"displayName": "DevChat",
"description": "Write prompts, not code",
"version": "0.1.17",
"version": "0.1.22",
"icon": "assets/devchat.png",
"publisher": "merico",
"engines": {
@ -165,6 +165,11 @@
"type": "string",
"default": ".+\\.js$, .+\\.ts$, .+\\.jsx$, .+\\.tsx$, .+\\.java$, .+\\.py$, .+\\.go$, .+\\.rb$, .+\\.php$, .+\\.cpp$, .+\\.c$, .+\\.cs$, .+\\.swift$, .+\\.rs$, .+\\.sh$, .+\\.bash$, .+\\.zsh$, .+\\.m$, .+\\.mm$, .+\\.h$, .+\\.hpp$, .+\\.hh$, .+\\.html$, .+\\.htm$, .+\\.xhtml$, .+\\.xml$, .+\\.css$, .+\\.scss$, .+\\.sass$, .+\\.less$, .+\\.json$, .+\\.yaml$, .+\\.yml$, .+\\.toml$, .+\\.ini$, .+\\.md$, .+\\.markdown$, .+\\.txt$, .+\\.csv$, .+\\.sql$, .+\\.sqlite$, .+\\.db$, .+\\.hql$, .+\\.psql$, .+\\.pgsql$, .+\\.plpgsql$",
"description": "Comma-separated list of regular expressions for supported file types for analysis."
},
"DevChat.betaInvitationCode": {
"type": "string",
"default": "",
"description": "The invitation code for beta testing."
}
}
},

View File

@ -1,3 +1,4 @@
import { FT } from "../util/feature_flags/feature_toggles";
import CustomCommands from "./customCommand";
export interface Command {
@ -17,15 +18,17 @@ class CommandManager {
public static getInstance(): CommandManager {
if (!CommandManager.instance) {
CommandManager.instance = new CommandManager();
CommandManager.instance.registerCommand({
name: 'ask-code',
pattern: 'ask-code',
description: 'ask code',
args: 0,
handler: async (commandName: string, userInput: string) => {
return '';
}
});
if (FT("ask-code")) {
CommandManager.instance.registerCommand({
name: 'ask-code',
pattern: 'ask-code',
description: 'ask code',
args: 0,
handler: async (commandName: string, userInput: string) => {
return '';
}
});
}
}
return CommandManager.instance;

View File

@ -17,6 +17,8 @@ import { installAskCode as installAskCodeFun } from '../util/python_installer/in
import { ProgressBar } from '../util/progressBar';
import path from 'path';
import { MessageHandler } from '../handler/messageHandler';
import { FT } from '../util/feature_flags/feature_toggles';
import { getPackageVersion } from '../util/python_installer/pip_package_version';
let indexProcess: CommandRun | null = null;
let summaryIndexProcess: CommandRun | null = null;
@ -238,17 +240,37 @@ export function TestDevChatCommand(context: vscode.ExtensionContext) {
export function registerAskCodeIndexStartCommand(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('DevChat.AskCodeIndexStart', async () => {
if (!FT("ask-code")) {
UiUtilWrapper.showErrorMessage("This command is a beta version command and has not been released yet.");
return;
}
const progressBar = new ProgressBar();
progressBar.init();
progressBar.update("Index source code files for ask codebase ...", 0);
const config = getConfig();
const pythonVirtualEnv = config.pythonVirtualEnv;
let pythonVirtualEnv: any = config.pythonVirtualEnv;
const supportedFileTypes = config.supportedFileTypes;
updateIndexingStatus("started");
if (pythonVirtualEnv) {
// check whether pythonVirtualEnv is stisfy the requirement version
const devchatAskVersion = getPackageVersion(pythonVirtualEnv, "devchat-ask");
let requireAskVersion = "0.0.8";
if (FT("ask-code-summary")) {
requireAskVersion = "0.0.10";
}
if (!devchatAskVersion || devchatAskVersion < requireAskVersion) {
logger.channel()?.info(`The version of devchat-ask is ${devchatAskVersion}`);
pythonVirtualEnv = undefined;
}
}
if (!pythonVirtualEnv) {
progressBar.update("Install devchat-ask package ...", 0);
await installAskCode(supportedFileTypes, progressBar, indexCode);
@ -345,6 +367,11 @@ async function indexCode(pythonVirtualEnv, supportedFileTypes, progressBar: any)
export function registerAskCodeIndexStopCommand(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('DevChat.AskCodeIndexStop', async () => {
if (!FT("ask-code")) {
UiUtilWrapper.showErrorMessage("This command is a beta version command and has not been released yet.");
return;
}
if (indexProcess) {
indexProcess.stop();
indexProcess = null;
@ -355,7 +382,12 @@ export function registerAskCodeIndexStopCommand(context: vscode.ExtensionContext
export function registerAskCodeSummaryIndexStartCommand(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('DevChat.AskCodeSummaryIndexStart', async () => {
const progressBar = new ProgressBar();
if (!FT("ask-code-summary")) {
UiUtilWrapper.showErrorMessage("This command is a beta version command and has not been released yet.");
return;
}
const progressBar = new ProgressBar();
progressBar.init();
progressBar.update("Index source code files for ask codebase summary...", 0);
@ -438,9 +470,12 @@ async function indexCodeSummary(pythonVirtualEnv, supportedFileTypes, progressBa
export function registerAskCodeSummaryIndexStopCommand(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('DevChat.AskCodeIndexSummaryStop', async () => {
// 在这里实现停止索引的功能
// 你可能需要检查summaryIndexProcess变量是否为null如果不为null那么停止索引进程
if (summaryIndexProcess) {
if (!FT("ask-code-summary")) {
UiUtilWrapper.showErrorMessage("This command is a beta version command and has not been released yet.");
return;
}
if (summaryIndexProcess) {
summaryIndexProcess.stop();
summaryIndexProcess = null;
}
@ -450,7 +485,12 @@ export function registerAskCodeSummaryIndexStopCommand(context: vscode.Extension
export function registerAddSummaryContextCommand(context: vscode.ExtensionContext) {
const callback = async (uri: { fsPath: any; }) => {
if (!await ensureChatPanel(context)) {
if (!FT("ask-code-summary")) {
UiUtilWrapper.showErrorMessage("This command is a beta version command and has not been released yet.");
return;
}
if (!await ensureChatPanel(context)) {
return;
}

View File

@ -30,6 +30,7 @@ import { LoggerChannelVscode } from './util/logger_vscode';
import { createStatusBarItem, createAskCodeStatusBarItem } from './panel/statusBarView';
import { UiUtilWrapper } from './util/uiUtil';
import { UiUtilVscode } from './util/uiUtil_vscode';
import { FT } from './util/feature_flags/feature_toggles';
function activate(context: vscode.ExtensionContext) {
@ -52,7 +53,9 @@ function activate(context: vscode.ExtensionContext) {
registerStatusBarItemClickCommand(context);
createStatusBarItem(context);
createAskCodeStatusBarItem(context);
if (FT("ask-code")) {
createAskCodeStatusBarItem(context);
}
regTopicDeleteCommand(context);
regAddTopicCommand(context);
@ -62,10 +65,12 @@ function activate(context: vscode.ExtensionContext) {
regApplyDiffResultCommand(context);
regPythonPathCommand(context);
registerAskCodeIndexStartCommand(context);
registerAskCodeIndexStopCommand(context);
registerAskCodeSummaryIndexStartCommand(context);
registerAskCodeSummaryIndexStopCommand(context);
registerAskCodeIndexStartCommand(context);
registerAskCodeIndexStopCommand(context);
registerAskCodeSummaryIndexStartCommand(context);
registerAskCodeSummaryIndexStopCommand(context);
registerAddSummaryContextCommand(context);
}
exports.activate = activate;

View File

@ -0,0 +1,23 @@
/*
check whether some feature is enabled
*/
import * as vscode from 'vscode';
import { regInMessage, regOutMessage } from '../util/reg_messages';
import { logger } from '../util/logger';
import { FT, FTs } from '../util/feature_flags/feature_toggles';
import { MessageHandler } from './messageHandler';
regInMessage({command: 'featureToggle', feature: 'feature name'});
regOutMessage({command: 'featureToggle', feature: 'feature name', enabled: true});
export async function featureToggle(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
const enabled = FT(message.feature);
MessageHandler.sendMessage(panel, {command: 'featureToggle', feature: message.feature, enabled: enabled});
}
regInMessage({command: 'featureToggles'});
regOutMessage({command: 'featureToggles', features: {'feature name': true}});
export async function featureToggles(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
const featureTaggles = FTs();
MessageHandler.sendMessage(panel, {command: 'featureToggles', features: featureTaggles});
}

View File

@ -17,6 +17,7 @@ import { regActionList } from './regActionList';
import { applyAction } from './applyAction';
import { doCommand } from './doCommand';
import { getSetting, updateSetting } from './updateConfig';
import { featureToggle, featureToggles } from './featureToggle';
// According to the context menu selected by the user, add the corresponding context file
@ -85,4 +86,6 @@ messageHandler.registerHandler('askCode', askCode);
messageHandler.registerHandler('doCommand', doCommand);
messageHandler.registerHandler('updateSetting', updateSetting);
messageHandler.registerHandler('getSetting', getSetting);
messageHandler.registerHandler('getSetting', getSetting);
messageHandler.registerHandler('featureToggle', featureToggle);
messageHandler.registerHandler('featureToggles', featureToggles);

View File

@ -11,6 +11,7 @@ import { ApiKeyManager } from '../util/apiKey';
import { regeneration, sendMessage as sendMessageX } from './sendMessage';
import { codeFileApply } from './codeFileApply';
import { applyAction } from './applyAction';
import { FT } from '../util/feature_flags/feature_toggles';
let autox = false;
@ -53,9 +54,11 @@ export class MessageHandler {
autox = true;
}
// if "/ask-code" in message.text, then call devchat-ask to get result
if (message.text.indexOf('/ask-code') !== -1) {
message.command = 'askCode';
message.text = message.text.replace('/ask-code', '');
if (FT("ask-code")) {
if (message.text.indexOf('/ask-code') !== -1) {
message.command = 'askCode';
message.text = message.text.replace('/ask-code', '');
}
}
}

View File

@ -0,0 +1,27 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
const featureTogglesJson = `
{
"ask-code-summary": false,
"ask-code": true,
"ask-code-dfs": false
}`;
const featureToggles = JSON.parse(featureTogglesJson);
export function FT(feature: string): boolean {
const betaInvitationCode = vscode.workspace.getConfiguration('DevChat').get<string>('betaInvitationCode');
const expectedInvitationCode = 'WELCOMEADDTODEVCHAT';
return betaInvitationCode === expectedInvitationCode || featureToggles[feature] === true;
}
export function FTs(): any {
// visited features
let newFeatureToggles = {};
for (const feature in featureToggles) {
newFeatureToggles[feature] = FT(feature);
}
return newFeatureToggles;
}

View File

@ -0,0 +1,18 @@
总结如下:
在VSCode插件开发中我们讨论了如何实现内测功能的激活控制。主要有以下几种方式
1. **使用特定的配置项**:在插件的配置项中添加一个特定的字段,例如`enableBetaFeatures`,根据这个配置项的值来决定是否启用内测功能。
2. **使用特定的命令**:在插件中添加一个特定的命令,例如`activateBetaFeatures`,只有当用户执行了这个命令,才启用内测功能。
3. **使用许可证**:在插件中添加一个许可证验证功能,只有当用户输入了有效的许可证,才启用内测功能。
4. **使用特定的版本**:在插件的版本号中添加一个特定的标识,例如`1.0.0-beta`,只有当插件的版本号包含了这个标识,才启用内测功能。
为了避免在内测结束后需要修改代码我们推荐使用特性开关Feature Toggles的方式来管理内测功能。可以创建一个特性开关的配置文件例如`feature-toggles.json`,在这个文件中定义哪些特性是开启的,哪些特性是关闭的。在插件代码中,读取这个配置文件,根据特性开关的值来决定是否启用某个特性。
对于`package.json`文件中定义的命令,我们可以在插件的激活函数(`activate`函数)中动态地注册或注销命令来实现这个功能。首先,在`package.json`文件中定义所有可能需要的命令,包括内测命令。然后,在`activate`函数中,根据特性开关的值来决定是否注册内测命令。
以上就是我们对于VSCode插件开发中内测功能激活控制的讨论总结。

View File

@ -2,7 +2,8 @@
Install DevChat with python=3.11.4
*/
import { logger } from "../logger";
import { FT } from "../feature_flags/feature_toggles";
import { logger } from "../logger";
import { appInstall } from "./app_install"
@ -12,7 +13,11 @@
export async function installAskCode(): Promise<string> {
try {
logger.channel()?.info(`start installing AskCode with python=3.11.4 ...`);
const pythonCommand = await appInstall('devchat-ask', '3.11.4');
let devchatAskVersion = 'devchat-ask>=0.0.8';
if (FT("ask-code-summary")) {
devchatAskVersion = 'devchat-ask>=0.0.10';
}
const pythonCommand = await appInstall(devchatAskVersion, '3.11.4');
if (!pythonCommand) {
logger.channel()?.error(`failed to install devchat-ask with python=3.11.4`);
logger.channel()?.show();