实现默认java项目的quickinput创建功能

This commit is contained in:
zhangyun 2022-11-07 10:44:14 +08:00
parent bf421117f2
commit fd1ea2cc5a
20 changed files with 4740 additions and 38 deletions

4169
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@
"contributes": {
"commands": [
{
"command": "project.createProject",
"command": "project.createProject",
"title": "%language.create-project%",
"category": "PROJECT",
"icon": {
@ -119,18 +119,21 @@
"@types/async-lock": "^1.1.3",
"@types/fs-extra": "^9.0.13",
"@types/md5": "^2.3.2",
"@types/semver": "^7.3.12",
"@types/xml2js": "^0.4.9",
"async-lock": "^1.3.0",
"autoprefixer": "^9",
"await-lock": "^2.2.2",
"core-js": "^3.6.5",
"fs-extra": "^10.0.0",
"generator-code": "^1.6.5",
"lodash": "^4.17.21",
"md5": "^2.2.1",
"xml2js": "^0.4.23",
"@types/semver": "^7.3.12"
"xml2js": "^0.4.23"
},
"devDependencies": {
"@types/glob": "^7.2.0",
"@types/lodash": "^4.14.187",
"@types/mocha": "^9.1.0",
"@types/node": "14.x",
"@types/vscode": "^1.54.0",
@ -140,11 +143,11 @@
"eslint": "^8.9.0",
"glob": "^7.2.0",
"mocha": "^9.2.1",
"semver": "^7.3.8",
"ts-loader": "^9.4.1",
"typescript": "^4.5.5",
"webpack": "^5.74.0",
"vsce": "^2.11.0",
"webpack-cli": "^4.10.0",
"semver": "^7.3.8"
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
}
}

6
src/common/Lock.ts Normal file
View File

@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import AwaitLock from "await-lock";
export const explorerLock: AwaitLock = new AwaitLock();

40
src/constants.ts Normal file
View File

@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export namespace Context {
export const EXTENSION_ACTIVATED: string = "java:projectManagerActivated";
export const LANGUAGE_SUPPORT_INSTALLED: string = "java:languageSupportInstalled";
export const NO_JAVA_PROJECT: string = "java:noJavaProjects";
export const WORKSPACE_CONTAINS_BUILD_FILES: string = "java:workspaceContainsBuildFiles";
export const RELOAD_PROJECT_ACTIVE: string = "java:reloadProjectActive";
export const SHOW_DEPRECATED_TASKS: string = "java:showDeprecatedTasks";
}
export namespace Explorer {
export enum ContextValueType {
WorkspaceFolder = "workspaceFolder",
Project = "project",
Container = "container",
PackageRoot = "packageRoot",
Package = "package",
Jar = "jar",
File = "file",
Type = "type",
Folder = "folder",
Symbol = "symbol",
}
export enum Mime {
JavaProjectExplorer = "application/vnd.code.tree.javaProjectExplorer",
TextUriList = "text/uri-list",
}
}
export namespace ExtensionName {
export const JAVA_LANGUAGE_SUPPORT: string = "redhat.java";
}
/**
* The files names for all the build files we support.
*/
export const buildFiles = ["pom.xml", "build.gradle", "settings.gradle", "build.gradle.kts", "settings.gradle.kts"];

33
src/contextManager.ts Normal file
View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { commands, Disposable, ExtensionContext } from "vscode";
class ContextManager implements Disposable {
private _context!: ExtensionContext;
private _contextValueMap!: Map<string, any>;
public initialize(context: ExtensionContext) {
this._context = context;
this._contextValueMap = new Map<string, any>();
}
public get context(): ExtensionContext {
return this._context;
}
public async setContextValue(key: string, value: any): Promise<void> {
this._contextValueMap.set(key, value);
await commands.executeCommand("setContext", key, value);
}
public getContextValue<T>(key: string): T | undefined {
return <T> this._contextValueMap.get(key);
}
public dispose(): void {
this._contextValueMap.clear();
}
}
export const contextManager: ContextManager = new ContextManager();

View File

@ -18,7 +18,7 @@ interface IProjectTypeMetadata {
createCommandArgs?: any[];
}
enum ProjectType {
export enum ProjectType {
NoBuildTool = "NoBuildTool",
Maven = "Maven",
Gradle = "Gradle",

51
src/java/jdtls.ts Normal file
View File

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { CancellationToken, commands } from "vscode";
import { Commands, executeJavaLanguageServerCommand } from "../commands";
// import { IClasspath } from "../tasks/buildArtifact/IStepMetadata";
// import { IMainClassInfo } from "../tasks/buildArtifact/ResolveMainClassExecutor";
import { INodeData } from "./nodeData";
export namespace Jdtls {
export async function getProjects(params: string): Promise<INodeData[]> {
return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_LIST, params) || [];
}
export async function getProjectUris(): Promise<string[]> {
return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.GET_ALL_PROJECTS) || [];
}
export async function refreshLibraries(params: string): Promise<boolean | undefined> {
return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_REFRESH_LIB_SERVER, params);
}
export async function getPackageData(params: { [key: string]: any }): Promise<INodeData[]> {
return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_GETPACKAGEDATA, params) || [];
}
export async function resolvePath(params: string): Promise<INodeData[]> {
return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_RESOLVEPATH, params) || [];
}
// export async function getMainClasses(params: string): Promise<IMainClassInfo[]> {
// return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_GETMAINCLASSES, params) || [];
// }
// export async function exportJar(mainClass: string, classpaths: IClasspath[],
// destination: string, terminalId: string, token: CancellationToken): Promise<boolean | undefined> {
// return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_GENERATEJAR,
// mainClass, classpaths, destination, terminalId, token);
// }
export enum CompileWorkspaceStatus {
Failed = 0,
Succeed = 1,
Witherror = 2,
Cancelled = 3,
}
export function resolveBuildFiles(): Promise<string[]> {
return <Promise<string[]>>executeJavaLanguageServerCommand(Commands.JAVA_RESOLVE_BUILD_FILES);
}
}

34
src/java/nodeData.ts Normal file
View File

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export enum NodeKind {
Workspace = 1,
Project = 2,
Container = 3,
PackageRoot = 4,
Package = 5,
PrimaryType = 6,
Folder = 7,
File = 8,
}
export enum TypeKind {
Class = 1,
Interface = 2,
Enum = 3,
}
export interface INodeData {
displayName?: string;
name: string;
moduleName?: string;
path?: string;
/**
* returned from Java side using `IJavaElement.getHandlerIdentifier();`
*/
handlerIdentifier?: string;
uri?: string;
kind: NodeKind;
children?: any[];
metaData?: { [id: string]: any };
}

View File

@ -4,6 +4,16 @@ import * as os from "os";
import * as fse from "fs-extra";
import * as vscode from "vscode";
import { WebviewPanel } from "vscode";
import * as _ from "lodash";
import * as semver from "semver";
import { commands, Disposable, Extension, ExtensionContext, extensions, QuickPickItem, Uri, window, workspace } from "vscode";
// import { instrumentOperationAsVsCodeCommand, sendInfo } from "vscode-extension-telemetry-wrapper";
import { Commands } from "../commands";
import { Utility } from "../utility";
import { contextManager } from "../contextManager";
import { explorerLock } from "../common/Lock";
import { Context } from "../constants";
import { Jdtls } from "../java/jdtls";
//import { gContext, createProject } from "../extension";
import {
gContext,
@ -21,7 +31,7 @@ import * as utils from "../utils";
import { ViewManager } from "../common/viewManager";
import { BaseManager } from "../BaseManager";
import { debug } from "console";
import {ensureExtension, projectTypes} from "../installExternsionDeps";
import {ensureExtension, ProjectType} from "../installExternsionDeps";
export class javaManager extends BaseManager {
/**
@ -39,25 +49,105 @@ export class javaManager extends BaseManager {
console.log("message.data: ");
console.log(message.data);
let isCheck = await ensureExtension(message.data.displayName, message.data.metadata);
console.log(" isCheck:" + isCheck );
let err = "false";
let msg = "";
if (!isCheck) {
err = "true";
msg = localize("language.callExtensionError");
if(message.data.metadata.type === ProjectType.NoBuildTool){
await scaffoldSimpleProject(this.extensionContext);
}else{
console.log("executeCommand: "+ message.data.metadata.createCommandId);
vscode.commands.executeCommand(message.data.metadata.createCommandId);
let isCheck = await ensureExtension(message.data.displayName, message.data.metadata);
console.log(" isCheck:" + isCheck );
let err = "false";
let msg = "";
if (!isCheck) {
err = "true";
msg = localize("language.callExtensionError");
}else{
console.log("executeCommand: "+ message.data.metadata.createCommandId);
vscode.commands.executeCommand(message.data.metadata.createCommandId);
}
// 发送消息
viewPanel.webview.postMessage({
type: "CALL_EXTENSION",
error: err,
message: msg,
});
}
// 发送消息
viewPanel.webview.postMessage({
type: "CALL_EXTENSION",
error: err,
message: msg,
});
// vscode.window.showErrorMessage(msg);
return;
// }else if(message.type === "CREATE_JAVA"){
// await scaffoldSimpleProject(this.extensionContext);
}
};
}
// async function getRootNodes(): Promise<any> {
// try {
// await explorerLock.acquireAsync();
// const folders = workspace.workspaceFolders;
// let isNoJava =
// if (folders && folders.length) {
// if (folders.length > 1) {
// folders.forEach((folder) => rootItems.push(new WorkspaceNode({
// name: folder.name,
// uri: folder.uri.toString(),
// kind: NodeKind.Workspace,
// }, undefined)));
// this._rootItems = rootItems;
// } else {
// const result: INodeData[] = await Jdtls.getProjects(folders[0].uri.toString());
// result.forEach((project) => {
// rootItems.push(new ProjectNode(project, undefined));
// });
// this._rootItems = rootItems;
// }
// }
// contextManager.setContextValue(Context.NO_JAVA_PROJECT, _.isEmpty(rootItems));
// return rootItems;
// } finally {
// explorerLock.release();
// }
// }
async function scaffoldSimpleProject(context: ExtensionContext): Promise<void> {
const workspaceFolder = Utility.getDefaultWorkspaceFolder();
const location: Uri[] | undefined = await window.showOpenDialog({
defaultUri: workspaceFolder && workspaceFolder.uri,
canSelectFiles: false,
canSelectFolders: true,
openLabel: "Select the project location",
});
if (!location || !location.length) {
return;
}
const basePath: string = location[0].fsPath;
const projectName: string | undefined = await window.showInputBox({
prompt: "Input a Java project name",
ignoreFocusOut: true,
validateInput: async (name: string): Promise<string> => {
if (name && !name.match(/^[^*~/\\]+$/)) {
return "Please input a valid project name";
}
if (name && await fse.pathExists(path.join(basePath, name))) {
return "A project with this name already exists";
}
return "";
},
});
if (!projectName) {
return;
}
const projectRoot: string = path.join(basePath, projectName);
const templateRoot: string = path.join(context.extensionPath, "templates", "java", "default");
try {
await fse.ensureDir(projectRoot);
await fse.copy(templateRoot, projectRoot);
await fse.ensureDir(path.join(projectRoot, "lib"));
} catch (error: any) {
window.showErrorMessage(error.message);
return;
}
const openInNewWindow = workspace && !_.isEmpty(workspace.workspaceFolders);
await commands.executeCommand(Commands.VSCODE_OPEN_FOLDER, Uri.file(path.join(basePath, projectName)), openInNewWindow);
}

116
src/utility.ts Normal file
View File

@ -0,0 +1,116 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Uri, window, workspace, WorkspaceFolder } from "vscode";
// import { setUserError } from "vscode-extension-telemetry-wrapper";
import { INodeData } from "./java/nodeData";
// import { languageServerApiManager } from "./languageServerApi/languageServerApiManager";
export class Utility {
public static getDefaultWorkspaceFolder(): WorkspaceFolder | undefined {
if (workspace.workspaceFolders === undefined) {
return undefined;
}
if (workspace.workspaceFolders.length === 1) {
return workspace.workspaceFolders[0];
}
if (window.activeTextEditor) {
const activeWorkspaceFolder: WorkspaceFolder | undefined =
workspace.getWorkspaceFolder(window.activeTextEditor.document.uri);
return activeWorkspaceFolder;
}
return undefined;
}
// public static async isRevealable(uri: Uri): Promise<boolean> {
// if (!SUPPORTED_URI_SCHEMES.includes(uri.scheme)) {
// return false;
// }
// if (uri.scheme === "file" && !workspace.getWorkspaceFolder(uri)) {
// return false;
// }
// return languageServerApiManager.ready();
// }
}
export class EventCounter {
public static dict: {[key: string]: number} = {};
public static increase(event: string) {
const count = this.dict[event] ?? 0;
this.dict[event] = count + 1;
}
}
// export class UserError extends Error {
// public context: ITroubleshootingMessage;
// constructor(context: ITroubleshootingMessage) {
// super(context.message);
// this.context = context;
// setUserError(this);
// }
// }
interface IProperties {
[key: string]: string;
}
interface ILoggingMessage {
message: string;
type?: Type;
details?: IProperties;
}
interface ITroubleshootingMessage extends ILoggingMessage {
anchor?: string;
}
export enum Type {
EXCEPTION = "exception",
USAGEDATA = "usageData",
USAGEERROR = "usageError",
ACTIVATEEXTENSION = "activateExtension", // TODO: Activation belongs to usage data, remove this category.
}
const keywords: Set<string> = new Set([
"abstract", "default", "if", "private", "this", "boolean", "do", "implements", "protected", "throw", "break", "double", "import",
"public", "throws", "byte", "else", "instanceof", "return", "transient", "case", "extends", "int", "short", "try", "catch", "final",
"interface", "static", "void", "char", "finally", "long", "strictfp", "volatile", "class", "float", "native", "super", "while",
"const", "for", "new", "switch", "continue", "goto", "package", "synchronized", "true", "false", "null", "assert", "enum",
]);
const SUPPORTED_URI_SCHEMES: string[] = ["file", "jdt"];
export function isKeyword(identifier: string): boolean {
return keywords.has(identifier);
}
const identifierRegExp: RegExp = /^([a-zA-Z_$][a-zA-Z\d_$]*)$/;
export function isJavaIdentifier(identifier: string): boolean {
return identifierRegExp.test(identifier);
}
export function isTest(nodeData: INodeData | undefined): boolean {
if (!nodeData) {
return false;
}
if (nodeData.metaData?.test === "true") {
return true;
}
const mavenScope: string = nodeData.metaData?.["maven.scope"] || "";
if (mavenScope.toLocaleLowerCase().includes("test")) {
return true;
}
const gradleScope: string = nodeData.metaData?.gradle_scope || "";
if (gradleScope.toLocaleLowerCase().includes("test")) {
return true;
}
return false;
}

View File

@ -1,4 +1,7 @@
{
"java.project.sourcePath": "src",
"java.project.outputPath": "bin"
}
"java.project.sourcePaths": ["src"],
"java.project.outputPath": "bin",
"java.project.referencedLibraries": [
"lib/**/*.jar"
]
}

View File

@ -0,0 +1,18 @@
## Getting Started
Welcome to the VS Code Java world. Here is a guideline to help you get started to write Java code in Visual Studio Code.
## Folder Structure
The workspace contains two folders by default, where:
- `src`: the folder to maintain sources
- `lib`: the folder to maintain dependencies
Meanwhile, the compiled output files will be generated in the `bin` folder by default.
> If you want to customize the folder structure, open `.vscode/settings.json` and update the related settings there.
## Dependency Management
The `JAVA PROJECTS` view allows you to manage your dependencies. More details can be found [here](https://github.com/microsoft/vscode-java-dependency#manage-dependencies).

View File

@ -1,8 +1,5 @@
public class main {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Hello World!");
}
public static void main(String[] args) throws Exception {
System.out.println("Hello, World!");
}
}

View File

@ -1,6 +1,6 @@
{
"projects": {
"java-maven": [
"java-maven-": [
[
{
"type": "button",
@ -34,10 +34,25 @@
}
]
],
"java": [
"java-more": [
[
{
"type": "button",
"displayName": "language.java-project-create",
"description": "Click to Work with source code directly without any build tools.",
"metadata": {
"type": "NoBuildTool",
"extensionId": "",
"extensionName": "",
"leastExtensionVersion": "",
"createCommandId": "",
"createCommandArgs": [
]
},
"category": "Java",
"icon": "add"
},
{
"displayName": "language.java-project-create",
"description": "Click to Work with source code directly without any build tools.",
"metadata": {

View File

@ -629,7 +629,7 @@
}
]
],
"key-java-project": [
"java": [
[
{
"type": "input",
@ -668,6 +668,116 @@
"desc": ""
}
]
],
"java-more": [
[
{
"type": "button",
"displayName": "language.java-project-create",
"description": "Click to Work with source code directly without any build tools.",
"metadata": {
"type": "NoBuildTool",
"extensionId": "",
"extensionName": "",
"leastExtensionVersion": "",
"createCommandId": "",
"createCommandArgs": [
]
},
"category": "Java",
"icon": "add"
},
{
"displayName": "language.java-project-create",
"description": "Click to Work with source code directly without any build tools.",
"metadata": {
"type": "NoBuildTool",
"extensionId": "vscjava.vscode-java-dependency",
"extensionName": "Default Java",
"leastExtensionVersion": "0.21.0",
"createCommandId": "java.project.create",
"createCommandArgs": [
]
},
"category": "Java",
"icon": "add"
},
{
"type": "button",
"displayName": "Maven",
"description": "create maven project",
"metadata": {
"type": "Maven",
"extensionId": "vscjava.vscode-maven",
"extensionName": "Maven for Java",
"createCommandId": "maven.archetype.generate"
}
},
{
"type": "button",
"displayName": "Gradle",
"description": "create Gradle project",
"metadata": {
"type": "Gradle",
"extensionId": "vscjava.vscode-gradle",
"extensionName": "Gradle for Java",
"leastExtensionVersion": "3.10.0",
"createCommandId": "gradle.createProject"
}
},
{
"type": "button",
"displayName": "Spring Boot",
"description": "create a new Spring Boot project",
"metadata": {
"type": "SpringBoot",
"extensionId": "vscjava.vscode-spring-initializr",
"extensionName": "Spring Initializr Java Support",
"createCommandId": "spring.initializr.createProject"
}
},
{
"type": "button",
"displayName": "Quarkus",
"description": "create a new Quarkus project",
"metadata": {
"type": "Quarkus",
"extensionId": "redhat.vscode-quarkus",
"extensionName": "Quarkus",
"createCommandId": "quarkusTools.createProject"
}
},
{
"type": "button",
"displayName": "MicroProfile",
"description": "create a new project by MicroProfile",
"metadata": {
"type": "MicroProfile",
"extensionId": "microprofile-community.mp-starter-vscode-ext",
"extensionName": "MicroProfile Starter",
"createCommandId": "extension.microProfileStarter"
}
},
{
"type": "button",
"displayName": "JavaFX",
"description": "create maven project from archetype",
"metadata": {
"type": "JavaFX",
"extensionId": "vscjava.vscode-maven",
"extensionName": "Maven for Java",
"leastExtensionVersion": "0.35.0",
"createCommandId": "maven.archetype.generate",
"createCommandArgs": [
{
"archetypeGroupId": "org.openjfx",
"archetypeArtifactId": "javafx-archetype-fxml",
"archetypeVersion": "RELEASE"
}
]
}
}
]
]
}
}

View File

@ -16,11 +16,16 @@
"value": "java",
"label": "language.java-project",
"isCallExtension": "true"
},{
"value": "java-more",
"label": "language.more-java-project",
"isCallExtension": "true"
},{
"value": "java-maven",
"label": "language.maven-java-project",
"isCallExtension": "true"
}]
}
]
}],

View File

@ -42,6 +42,7 @@
"java-project": "Java project",
"jdk-path": "JDK path",
"out-path": "output path",
"more-java-project": "More java project",
"maven-java-project": "Maven java project",
"web-java-project": "Java web project",
"c-project": "C project",

View File

@ -42,6 +42,7 @@
"java-project": "Java 项目",
"jdk-path": "JDK 目录",
"out-path": "输出目录",
"more-java-project": "更多工具创建Java项目",
"maven-java-project": "从Maven创建Java项目",
"web-java-project": "Java web 项目",
"c-project": "C 项目",

View File

@ -80,7 +80,7 @@ export default defineComponent({
projectKey: keys["KEY-C"],
projectItem: undefined,
callExtensionKeys: [
"java",
"java-more",
"java-maven",
"java-web"
]

View File

@ -275,6 +275,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
"@types/lodash@^4.14.187":
version "4.14.187"
resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.187.tgz#122ff0a7192115b4c1a19444ab4482caa77e2c9d"
integrity sha512-MrO/xLXCaUgZy3y96C/iOsaIqZSeupyTImKClHunL5GrmaiII2VwvWmLBu2hwa0Kp0sV19CsyjtrTc/Fx8rg/A==
"@types/md5@^2.3.2":
version "2.3.2"
resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.2.tgz#529bb3f8a7e9e9f621094eb76a443f585d882528"
@ -682,6 +687,11 @@ autoprefixer@^9:
postcss "^7.0.32"
postcss-value-parser "^4.1.0"
await-lock@^2.2.2:
version "2.2.2"
resolved "https://registry.npmmirror.com/await-lock/-/await-lock-2.2.2.tgz#a95a9b269bfd2f69d22b17a321686f551152bcef"
integrity sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==
azure-devops-node-api@^11.0.1:
version "11.2.0"
resolved "https://registry.npmmirror.com/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz#bf04edbef60313117a0507415eed4790a420ad6b"
@ -1988,7 +1998,7 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@^4.17.11:
lodash@^4.17.11, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==