first commit

This commit is contained in:
Ваше Имя 2025-03-02 18:51:02 +05:00
commit fd4731129a
10 changed files with 617 additions and 0 deletions

24
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,24 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}"
},
{
"name": "Attach to Running Go Program",
"type": "go",
"request": "attach",
"mode": "remote",
"remotePath": "${workspaceFolder}",
"port": 40000,
"host": "127.0.0.1"
}
]
}

2
Readme.md Normal file
View File

@ -0,0 +1,2 @@
# Debug
```dlv debug --headless --listen=:40000 --api-version=2```

8
go.mod Normal file
View File

@ -0,0 +1,8 @@
module hello
go 1.23.5
require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

5
go.sum Normal file
View File

@ -0,0 +1,5 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

BIN
hello Executable file

Binary file not shown.

342
main.go Normal file
View File

@ -0,0 +1,342 @@
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"gopkg.in/ini.v1"
"gopkg.in/yaml.v2"
)
// Variables описывает работу с .ini файлами
type Variables struct {
iniFileName string
config *ini.File
}
// NewVariables конструктор для работы с .ini файлами
func NewVariables(iniFileName string) *Variables {
config, err := ini.LooseLoad(iniFileName)
if err != nil {
log.Fatalf("Ошибка загрузки ini-файла: %v", err)
}
return &Variables{
iniFileName: iniFileName,
config: config,
}
}
// GetVar читает или запрашивает переменную из .ini
func (v *Variables) GetVar(sectionName, varName string, interactive bool) string {
section := v.config.Section(sectionName)
value := section.Key(varName).String()
if interactive || value == "" {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("Введите %s (%s): ", varName, value)
newValue, _ := reader.ReadString('\n')
newValue = strings.TrimSpace(newValue)
if newValue != "" {
section.Key(varName).SetValue(newValue)
v.config.SaveTo(v.iniFileName)
return newValue
}
}
if !interactive && value == "" {
fmt.Printf("Переменная %s не найдена", varName)
os.Exit(1)
}
return value
}
// MenuItem описывает структуру меню из YAML
type MenuItem struct {
Title string `yaml:"title"`
File string `yaml:"file,omitempty"`
Items map[int]MenuItem `yaml:"items,omitempty"`
Commands []string `yaml:"commands,omitempty"`
Catch []string `yaml:"catch,omitempty"`
Vars []string `yaml:"vars,omitempty"`
Env string `yaml:"env,omitempty"`
}
// Menu управляет отображением меню
type Menu struct {
items map[int]MenuItem
variables *Variables
baseDir string
}
// NewMenu инициализирует меню из YAML
func NewMenu(yamlFile string) *Menu {
currDir, _ := os.Getwd()
// baseDir := filepath.Join(currDir, "menu") // Базовая директория меню
baseDir := currDir
yamlPath := resolvePath(baseDir, yamlFile)
// Читаем YAML файл
file, err := os.Open(yamlPath)
if err != nil {
log.Fatalf("Ошибка чтения YAML файла %s: %v", yamlPath, err)
}
defer file.Close()
decoder := yaml.NewDecoder(file)
var menu struct {
Items map[int]MenuItem `yaml:"items"`
}
if err := decoder.Decode(&menu); err != nil {
log.Fatalf("Ошибка парсинга YAML: %v", err)
}
return &Menu{
items: menu.Items,
variables: NewVariables("env.ini"),
baseDir: baseDir,
}
}
// resolvePath корректно формирует путь к файлу
func resolvePath(baseDir, yamlFile string) string {
// Если путь абсолютный, используем его как есть
if filepath.IsAbs(yamlFile) {
return yamlFile
}
// Если путь уже содержит сегмент "menu", оставляем всё как есть
if strings.HasPrefix(baseDir, "menu"+string(os.PathSeparator)) {
return filepath.Join(baseDir, yamlFile)
}
// Обычно добавляем сегмент "menu" перед файлом
return filepath.Join(baseDir, string(os.PathSeparator)+"menu", yamlFile)
}
// ShowItems отображает главное меню
func (m *Menu) ShowItems() {
fmt.Println("0. Выход")
//Извлекаем ключи из мапы
keys := make([]int, 0, len(m.items))
for key := range m.items {
keys = append(keys, key)
}
//Сортируем ключи
sort.Ints(keys)
for _, key := range keys {
fmt.Printf("%s\n", m.items[key].Title)
}
}
// Loop запускает интерактивный цикл меню
func (m *Menu) Loop() {
reader := bufio.NewReader(os.Stdin)
for {
m.ShowItems()
fmt.Print("Выберите действие: ")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
if input == "0" {
break
}
if itemNum, err := strconv.Atoi(input); err == nil {
m.ChooseItem(itemNum)
} else {
fmt.Println("Неверный ввод")
}
}
}
// ChooseItem обрабатывает выбор элемента меню
func (m *Menu) ChooseItem(itemNum int) {
item, found := m.items[itemNum]
if !found {
fmt.Println("Элемент не найден")
return
}
if len(item.File) > 0 {
// Подгружаем дочернее меню
f := strings.TrimPrefix(item.File, string(os.PathSeparator))
filePath := resolvePath(m.baseDir, f)
subMenu := NewMenu(filePath)
subMenu.Loop()
} else if len(item.Items) > 0 {
subMenu := &Menu{items: item.Items, variables: m.variables, baseDir: m.baseDir}
subMenu.Loop()
} else if len(item.Commands) > 0 {
vars := m.getVars(item, true)
if !m.runCommands(item, vars) {
m.catch(item, vars)
}
}
}
func (m *Menu) ExecItem(itemNum int) *Menu {
item, found := m.items[itemNum]
if !found {
fmt.Println("Элемент не найден")
return nil
}
if len(item.File) > 0 {
// Подгружаем дочернее меню
f := strings.TrimPrefix(item.File, string(os.PathSeparator))
filePath := resolvePath(m.baseDir, f)
subMenu := NewMenu(filePath)
return subMenu
} else if len(item.Items) > 0 {
subMenu := &Menu{items: item.Items, variables: m.variables, baseDir: m.baseDir}
return subMenu
} else if len(item.Commands) > 0 {
vars := m.getVars(item, false)
if !m.runCommands(item, vars) {
m.catch(item, vars)
}
}
return nil
}
// getVars получает переменные из .ini
func (m *Menu) getVars(item MenuItem, interactive bool) map[string]string {
vars := make(map[string]string)
vars["currentDir"], _ = os.Getwd()
for _, varName := range item.Vars {
vars[varName] = m.variables.GetVar(item.Env, varName, interactive)
}
return vars
}
// runCommands выполняет команды
func (m *Menu) runCommands(item MenuItem, vars map[string]string) bool {
for _, cmd := range item.Commands {
// Здесь можно вызвать реальное выполнение, используя os/exec
re := regexp.MustCompile(`%\(([a-zA-Z0-9]*)\)s`)
command := re.ReplaceAllStringFunc(cmd, func(match string) string {
// Извлекаем ключ внутри %( и )s
key := re.FindStringSubmatch(match)[1]
// Проверяем, есть ли значение для ключа в map
if val, exists := vars[key]; exists {
return val // Заменяем найденной строкой
}
// Если ключа нет в map, оставляем как есть
return match
})
fmt.Printf("Выполнение: %s\n", command)
// Разделяем на части
parts := strings.Split(command, " ")
// Проверяем, является ли первая часть установкой переменной окружения
envs := []string{} // Хранилище для переменных окружения
for len(parts) > 0 && strings.Contains(parts[0], "=") {
envs = append(envs, parts[0]) // Добавляем переменную в массив env
parts = parts[1:] // Убираем первую часть
}
// Теперь `parts` содержит только саму команду и её аргументы
cmd := exec.Command(parts[0], parts[1:]...)
// Добавляем переменные окружения к текущим
cmd.Env = append(os.Environ(), envs...)
// Передаём текущее окружение
// cmd.Env = os.Environ()
cmd.Stdin = os.Stdin // Ввод
cmd.Stdout = os.Stdout // Вывод
cmd.Stderr = os.Stderr // Вывод ошибок
// Запускаем команду
if err := cmd.Run(); err != nil {
os.Stderr.WriteString("Ошибка выполнения: " + err.Error() + "\n")
}
}
return true
}
// catch выполняет обработку ошибок команд
func (m *Menu) catch(item MenuItem, vars map[string]string) {
for _, cmd := range item.Catch {
fmt.Printf("Обработка ошибки: %s\n", cmd)
// Здесь можно вызвать реальное выполнение, используя os/exec
re := regexp.MustCompile(`%\(([a-zA-Z0-9]*)\)s`)
command := re.ReplaceAllStringFunc(cmd, func(match string) string {
// Извлекаем ключ внутри %( и )s
key := re.FindStringSubmatch(match)[1]
// Проверяем, есть ли значение для ключа в map
if val, exists := vars[key]; exists {
return val // Заменяем найденной строкой
}
// Если ключа нет в map, оставляем как есть
return match
})
fmt.Printf("Выполнение: %s\n", command)
// Разделяем на части
parts := strings.Split(command, " ")
// Проверяем, является ли первая часть установкой переменной окружения
envs := []string{} // Хранилище для переменных окружения
for len(parts) > 0 && strings.Contains(parts[0], "=") {
envs = append(envs, parts[0]) // Добавляем переменную в массив env
parts = parts[1:] // Убираем первую часть
}
// Теперь `parts` содержит только саму команду и её аргументы
cmd := exec.Command(parts[0], parts[1:]...)
// Добавляем переменные окружения к текущим
cmd.Env = append(os.Environ(), envs...)
// Передаём текущее окружение
// cmd.Env = os.Environ()
cmd.Stdin = os.Stdin // Ввод
cmd.Stdout = os.Stdout // Вывод
cmd.Stderr = os.Stderr // Вывод ошибок
// Запускаем команду
if err := cmd.Run(); err != nil {
os.Stderr.WriteString("Ошибка выполнения: " + err.Error() + "\n")
}
}
}
func main() {
// Инициализация главного меню
menu := NewMenu("menu.yml") // Файл главного меню (menu/menu.yml)
// Создаем флаг -e/--exec с необязательным аргументом
execFlag := flag.String("exec", "", "Execution parameter")
flag.Parse() // Парсим аргументы
if *execFlag != "" { // Проверяем, задан ли флаг --exec
items := strings.Split(*execFlag, "-") // Разделяем строку по "-"
for _, item := range items { // Итерируемся по элементам
num, _ := strconv.Atoi(item)
menu = menu.ExecItem(num)
}
os.Exit(0) // Завершаем выполнение программы
}
menu.Loop()
}

108
menu/develop/menu.yml Executable file
View File

@ -0,0 +1,108 @@
items:
1:
title: '1. Build'
env: 'dev'
commands:
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_dev.yml build --no-cache'
vars:
- appName
2:
title: '2. InitDB'
env: 'dev'
commands:
- 'docker run -v %(currentDir)s/data/mysql:/var/lib/mysql -v %(currentDir)s/dumps:/home/dumps -v %(currentDir)s/conf/scripts/mysql:/home/scripts -it %(appName)s_db sh /home/scripts/initDB.sh'
vars:
- appName
3:
title: '3. Deploy and Up'
env: 'dev'
commands:
- 'mkdir -p data/mysql'
- 'mkdir -p app'
- 'docker run -v %(currentDir)s/data/mysql:/var/lib/mysql -v %(currentDir)s/dumps:/home/dumps -v %(currentDir)s/conf/scripts/mysql:/home/scripts -it %(appName)s_db sh /home/scripts/initDB.sh'
- 'sh conf/scripts/deploy/dev.sh'
- 'docker network create -d bridge --subnet 192.168.0.0/24 --gateway 192.168.0.1 dockernet'
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_dev.yml up --force-recreate -d'
- 'docker exec -it %(appName)s_workspace_1 sh /home/scripts/deploy_dev.sh'
vars:
- appName
4:
title: '4. Up'
env: 'dev'
commands:
- 'docker network create -d bridge --subnet 192.168.0.0/24 --gateway 192.168.0.1 dockernet'
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_dev.yml up --force-recreate -d'
vars:
- appName
5:
title: '5. Stop'
env: 'dev'
commands:
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_dev.yml stop'
- 'sleep 3'
- 'docker network rm dockernet'
vars:
- appName
6:
title: '6. Remove'
env: 'dev'
commands:
- 'docker rm %(appName)s_php_1 --force'
- 'docker rm %(appName)s_workspace_1 --force'
- 'docker rm %(appName)s_db_1 --force'
- 'docker rm %(appName)s_nginx_1 --force'
- 'docker rm %(appName)s_nodejs_1 --force'
- 'docker rm %(appName)s_python_1 --force'
- 'docker rm %(appName)s_ftp_1 --force'
- 'docker rm %(appName)s_elastic_1 --force'
- 'docker rmi %(appName)s_php --force'
- 'docker rmi %(appName)s_workspace --force'
- 'docker rmi %(appName)s_db --force'
- 'docker rmi %(appName)s_nginx --force'
- 'docker rmi %(appName)s_nodejs --force'
- 'docker rmi %(appName)s_python --force'
- 'docker rmi %(appName)s_ftp --force'
- 'docker rmi %(appName)s_elastic --force'
vars:
- appName
7:
title: '7. In php workspace'
env: 'dev'
commands:
- 'docker exec -it %(appName)s_workspace_1 sh'
vars:
- appName
8:
title: '8. In nodejs workspace'
env: 'dev'
commands:
- 'docker exec -it %(appName)s_nodejs_1 /bin/bash'
vars:
- appName
9:
title: '9. Make Dump'
env: 'dev'
commands:
- 'docker exec -it %(appName)s_db_1 sh /home/scripts/makeDump.sh dump.sql'
vars:
- appName
10:
title: '10. SSHFS'
items:
1:
title: '1. Mount'
env: 'dev'
commands:
- 'sshfs %(user)s@%(ip)s:%(remotePath)s %(currentDir)s/server'
vars:
- appName
- user
- ip
- remotePath
2:
title: '2. Umount'
env: 'dev'
commands:
- 'sudo umount -l %(currentDir)s/server'
vars:
- appName

10
menu/menu.yml Executable file
View File

@ -0,0 +1,10 @@
items:
1:
title: '1. Develop'
file: '/develop/menu.yml'
2:
title: '2. Test'
file: '/test/menu.yml'
3:
title: '3. Production'
file: '/prod/menu.yml'

59
menu/prod/menu.yml Executable file
View File

@ -0,0 +1,59 @@
items:
1:
title: '1. Build'
env: 'test'
commands:
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_prod.yml build --no-cache'
vars:
- appName
2:
title: '2. InitDB'
env: 'test'
commands:
- 'docker run -v %(currentDir)s/data/mysql:/var/lib/mysql -v %(currentDir)s/dumps:/home/dumps -v %(currentDir)s/conf/scripts/mysql:/home/scripts -it %(appName)s_db sh /home/scripts/initDB.sh'
vars:
- appName
3:
title: '3. Deploy and Up'
env: 'test'
commands:
- 'mkdir -p data/mysql'
- 'mkdir -p app'
- 'sh conf/scripts/deploy/test.sh'
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_prod.yml up --force-recreate -d'
vars:
- appName
4:
title: '4. Up'
env: 'test'
commands:
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_prod.yml up --force-recreate -d'
vars:
- appName
5:
title: '5. Stop'
env: 'test'
commands:
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_prod.yml stop'
- 'sleep 3'
- 'docker network rm dockernet'
vars:
- appName
6:
title: '6. Remove'
env: 'test'
commands:
- 'docker rm %(appName)s_php_1 --force'
- 'docker rm %(appName)s_db_1 --force'
- 'docker rm %(appName)s_nginx_1 --force'
- 'docker rm %(appName)s_ftp_1 --force'
- 'docker rm %(appName)s_elastic_1 --force'
- 'docker rm %(appName)s_redis_1 --force'
- 'docker rmi %(appName)s_php --force'
- 'docker rmi %(appName)s_db --force'
- 'docker rmi %(appName)s_nginx --force'
- 'docker rm %(appName)s_ftp --force'
- 'docker rm %(appName)s_elastic --force'
- 'docker rm %(appName)s_redis --force'
vars:
- appName

59
menu/test/menu.yml Executable file
View File

@ -0,0 +1,59 @@
items:
1:
title: '1. Build'
env: 'test'
commands:
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_test.yml build --no-cache'
vars:
- appName
2:
title: '2. InitDB'
env: 'test'
commands:
- 'docker run -v %(currentDir)s/data/mysql:/var/lib/mysql -v %(currentDir)s/dumps:/home/dumps -v %(currentDir)s/conf/scripts/mysql:/home/scripts -it %(appName)s_db sh /home/scripts/initDB.sh'
vars:
- appName
3:
title: '3. Deploy and Up'
env: 'test'
commands:
- 'mkdir -p data/mysql'
- 'mkdir -p app'
- 'sh conf/scripts/deploy/test.sh'
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_test.yml up --force-recreate -d'
vars:
- appName
4:
title: '4. Up'
env: 'test'
commands:
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_test.yml up --force-recreate -d'
vars:
- appName
5:
title: '5. Stop'
env: 'test'
commands:
- 'docker-compose --project-name %(appName)s --file conf/docker/docker-compose_test.yml stop'
- 'sleep 3'
- 'docker network rm dockernet'
vars:
- appName
6:
title: '6. Remove'
env: 'test'
commands:
- 'docker rm %(appName)s_php_1 --force'
- 'docker rm %(appName)s_db_1 --force'
- 'docker rm %(appName)s_nginx_1 --force'
- 'docker rm %(appName)s_ftp_1 --force'
- 'docker rm %(appName)s_elastic_1 --force'
- 'docker rm %(appName)s_redis_1 --force'
- 'docker rmi %(appName)s_php --force'
- 'docker rmi %(appName)s_db --force'
- 'docker rmi %(appName)s_nginx --force'
- 'docker rm %(appName)s_ftp --force'
- 'docker rm %(appName)s_elastic --force'
- 'docker rm %(appName)s_redis --force'
vars:
- appName