From c6d6d70ea634c2f5eb0588eac89b74013a064b17 Mon Sep 17 00:00:00 2001 From: Aleksey Khorev <665525+khorevaa@users.noreply.github.com> Date: Mon, 22 Mar 2021 07:01:06 +0300 Subject: [PATCH] =?UTF-8?q?NEW:=20=D0=9D=D0=BE=D0=B2=D0=B0=D1=8F=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D0=B0=20get=20(#15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Новая команда get --- .github/workflows/releaser.yaml | 1 - .github/workflows/test.yml | 10 +- .gitignore | 6 +- .goreleaser.yaml | 1 + .sonarcloud.properties | 3 + README.md | 170 +- cmd/get.go | 377 +- downloader/client.go | 225 + downloader/downloader.go | 47 +- downloader/downloader_test.go | 53 +- downloader/filter.go | 495 ++ downloader/filter_test.go | 337 ++ downloader/html.go | 257 + downloader/html_test.go | 4212 +++++++++++++++++ downloader/oneDownloader.go | 387 ++ downloader/oneDownloader_test.go | 123 + go.mod | 10 +- go.sum | 95 +- logos.yaml | 3 +- main.go | 25 +- .../client/client_8_3_16_1876.deb64.tar.gz | Bin 0 -> 269 bytes unpacker/unpacker.go | 21 + unpacker/unpacker_test.go | 114 + 23 files changed, 6818 insertions(+), 154 deletions(-) create mode 100644 .sonarcloud.properties create mode 100644 downloader/client.go create mode 100644 downloader/filter.go create mode 100644 downloader/filter_test.go create mode 100644 downloader/html.go create mode 100644 downloader/html_test.go create mode 100644 downloader/oneDownloader.go create mode 100644 downloader/oneDownloader_test.go create mode 100644 unpacker/fixtures/linux/client/client_8_3_16_1876.deb64.tar.gz create mode 100644 unpacker/unpacker.go create mode 100644 unpacker/unpacker_test.go diff --git a/.github/workflows/releaser.yaml b/.github/workflows/releaser.yaml index 5bb801d..747bf6f 100644 --- a/.github/workflows/releaser.yaml +++ b/.github/workflows/releaser.yaml @@ -1,7 +1,6 @@ name: goreleaser on: - pull_request: push: tags: - '*' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2131c8e..2dbcb8b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,12 @@ jobs: - name: Setup modules run: | go mod tidy - - name: Run Test + - name: Run Test ubuntu-latest + if: runner.os == 'Linux' run: | - go test -race \ No newline at end of file + go test -coverprofile=coverage.out ./... + - name: Run Test windows-latest + if: runner.os == 'Windows' + run: | + go test -coverprofile=coverage.out ./... + shell: cmd diff --git a/.gitignore b/.gitignore index f5b336a..c3cefc5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *.exe -*.gz *.zip +*.deb oneget logs @@ -9,3 +9,7 @@ dist .idea *.logs +/platform83/ +*.d1c +pack + diff --git a/.goreleaser.yaml b/.goreleaser.yaml index ba15972..d16c869 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -29,6 +29,7 @@ changelog: exclude: - '^docs:' - '^test:' + - '^skip:' - Merge pull request - Merge branch dockers: diff --git a/.sonarcloud.properties b/.sonarcloud.properties new file mode 100644 index 0000000..83928a9 --- /dev/null +++ b/.sonarcloud.properties @@ -0,0 +1,3 @@ +# Path to tests +sonar.tests=./... +sonar.test.inclusions=**/*_test.go \ No newline at end of file diff --git a/README.md b/README.md index bb3ce7c..6756eb3 100644 --- a/README.md +++ b/README.md @@ -11,20 +11,179 @@ [![Powered By: GoReleaser](https://img.shields.io/badge/powered%20by-goreleaser-green.svg?style=for-the-badge)](https://github.com/goreleaser) [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=for-the-badge)](https://conventionalcommits.org) -### Команда `get` +### Команда `get` описание использования -Использование: +Команда получения релизов проектов с сайта 1С `https://releases.1c.ru/` +Быстрый запуск: ```shell export ONEC_USERNAME=user -export $ONEC_PASSWORD=password -oneget get --path ./tmp/dist/ --nick platform83 --version 8.3.18.1334 --filter="deb64_.*.tar.gz$" +export ONEC_PASSWORD=password +oneget get --path ./tmp/dist/ platform@8.3.18.1334 # or -oneget --user user --pwd password get --path ./tmp/dist/ --nick platform83 --version 8.3.18.1334 --filter="deb64_.*.tar.gz$" +oneget --user user --pwd password get --path ./tmp/dist/ platform83@8.3.18.1334 ``` +#### Описание формата аргумента `RELEASE` + +Шаблон формата `platform83[:filter.[filter]...]@8.3.18.1334[:filter]` +Где, + * `platform83` - имя проекта (обязательный) + * `[:filter.[filter]...]` - набор фильтров файлов + * `@` - разделитель между проектов и версией релиза + * `8.3.18.1334[:filter]` - описание версии релиза + +Минимальный использование указание только имени проекта. +Например, `platform83` - будет трактоваться как `platform83@latest` + +> Имя проекта - подсмотреть можно в адресе, ссылки имеют вид например https://releases.1c.ru/project/EnterpriseERP20 + +> Синонимы проектов для быстрого доступа: +> * `platform` -> `platform83` +> * `edt` -> `DevelopmentTools10` +> * `ring` -> `EnterpriseLicenseTools` +> * `executor` -> `Executor` +> * `pg` -> `AddCompPostgre` + +##### Набор фильтров +Список предопределенных фильтров для проектов: + * По ОС: + * `win`, `windows`- фильтр по MS Windows + * `mac` - фильтр по OS X + * `deb` - фильтр по DEB-based Linux-систем + * `rpm` - фильтр по RPM-based Linux-систем +* По разрядности OS: + * `x32` - фильтр по 32-bit (по умолчанию, если не указан фильтр разрядности) + * `x64` - фильтр по 64-bit + +> Важно! Для OSX флаг разрядности игнорируется + +**Пример использования:** +```shell + export ONEC_USERNAME=user + export ONEC_PASSWORD=password + # Т.к. не указана разрядность OS будет скачены дистрибутивы для x32 + # скачать файлы с фильтрацией по Windows + oneget get platform:win + # скачать файлы с фильтрацией по OSX + oneget get platform:mac + # скачать файлы с фильтрацией по DEB-based Linux-систем + oneget get platform:deb + # скачать файлы с фильтрацией по RPM-based Linux-систем + oneget get platform:rpm +``` +**Пример для x64:** +```shell + export ONEC_USERNAME=user + export ONEC_PASSWORD=password + + # Там где не указана разрядность OS будет скачены дистрибутивы для x32 + # скачать файлы с фильтрацией по Windows + oneget get platform:win.x64 + # скачать файлы с фильтрацией по OSX + # Важно для OSX флаг разрядности игнорируется + oneget get platform:mac + # скачать файлы с фильтрацией по DEB-based Linux-систем + oneget get platform:deb.x64 + # скачать файлы с фильтрацией по RPM-based Linux-систем двух разрядностей сразу + oneget get platform:rpm.x64 platform:rpm.x32 +``` +##### Специальные фильтры для проектов +**Для проекта platform (platform83)** + * `thin-client`, `thin` - фильтр для файлов тонкого клиента 1С. Предприятие + * `client` - фильтр для файлов клиента 1С. Предприятие + * `server` - фильтр для файлов сервера 1С. Предприятие + * `full` - фильтр для файлов "Технологическая платформа" (только для Windows) + +> Важно! Для OSX фильтр `server` игнорируется + +> Важно! Фильтр `full` игнорируется для всех других фильтров кроме `win` + +**Пример использования:** +```shell + export ONEC_USERNAME=user + export ONEC_PASSWORD=password + + # Там где не указана разрядность OS будет скачены дистрибутивы для x32 + # скачать файлы сервера для Windows + oneget get platform:win.server.x64 + # скачать файлы клиента для OSX + # Важно для OSX флаг разрядности игнорируется + oneget get platform:mac.client + # скачать файлы тонкого клиента для DEB-based Linux-систем + oneget get platform:deb.thin.x64 + # скачать файлы сервера для RPM-based Linux-систем + oneget get platform:rpm.server.x64 +``` + +**Для проекта edt (DevelopmentTools10)** + + * `jdk` - фильтр для файлов Bellsoft JDK + * `online` - фильтр для файлов онлайн установщика 1С:EDT + +> Важно. Для проекта `edt` игнорируются фильтры разрядности + +**Пример использования:** +```shell + export ONEC_USERNAME=user + export ONEC_PASSWORD=password + + # скачать файлы 1C:EDT для Windows + oneget get edt:win + # скачать файлы 1C:EDT для OSX + oneget get edt:mac + # скачать файлы 1C:EDT для Linux и Bellsoft JDK для DEB-based Linux-систем + oneget get edt:deb + # скачать файлы 1C:EDT для Linux и Bellsoft JDK для RPM-based Linux-систем + oneget get edt:rpm + + # скачать файлы онлайн установщика 1C:EDT для Windows + oneget get edt:win.online +``` +##### Описание формата версии релиза + +> В версии релиза может быть указан номер версии или специальные фильтры версии. + +> Если версия релиза пустая, то подставляется фильтр "latest" +> ("edt" -> "edt@latest") + +**Специальные фильтры версии релиза:** + + * `latest` - выбирает наиболее старшую версию релиза + * `latest:regexp` - фильтрует список версию по `regexp`, и берет наиболее старшую + * `from:date` - фильтрует список версий по дате, у которых дата релиза больше `date` где, `date` - формате 02.06.21 + * `from-v:version` - фильтрует список версий, которые старше версии релиза `version` где, `version` - формате номер версии + * `regexp` - фильтрует список по регулярному выражению указанному в `regexp` + +**Пример использования:** +```shell + export ONEC_USERNAME=user + export ONEC_PASSWORD=password + + # скачать файлы последней версию релиза 1C:EDT для Windows + oneget get edt:win@latest + # or / или + # oneget get edt:win + + # скачать файлы Платформы 1С. Предприятие для всех систем + # всех версии релизов, выпущенные начиная с даты 2020.01.01 + oneget platform@from:01.01.21 + + # скачать файлы Платформы 1С. Предприятие для DEB-based Linux-систем + # всех версии релизов, у которых версия старше чем 8.3.18.1363 + oneget platform:deb.x64@from-v:8.3.18.1363 + + + # скачать файлы сервера Платформы 1С. Предприятие для DEB-based Linux-систем + # последней выпущенной версии 8.3.16 + oneget platform:deb.server.x64@latest:8.3.16 + + # скачать файлы Платформы 1С. Предприятие для OSX + # всех версии релизов 8.3.16.x + oneget platform:mac@8.3.16 +``` ## Запуск в докере @@ -55,6 +214,7 @@ appenders: - name: FILE file_name: ./logs/oneget.log max_size: 100 + max_age: 10 encoder: json: loggers: diff --git a/cmd/get.go b/cmd/get.go index 28b6631..f6cac04 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -1,7 +1,15 @@ package cmd import ( + "fmt" "github.com/khorevaa/logos" + "github.com/v8platform/oneget/unpacker" + "go.uber.org/multierr" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" "time" "github.com/urfave/cli/v2" @@ -11,13 +19,16 @@ import ( var log = logos.New("github.com/v8platform/oneget").Sugar() type getCmd struct { - User string - Password string - BaseDir string - StartDate time.Time - Type string - Version string - Filter string + User string + Password string + BaseDir string + StartDate time.Time + Rename bool + Extract bool + ExtractDir string + Filter cli.StringSlice + + releases []string } func (c *getCmd) run(ctx *cli.Context) error { @@ -36,25 +47,59 @@ func (c *getCmd) run(ctx *cli.Context) error { logos.SetLevel("github.com/v8platform/oneget", logos.DebugLevel) } - downloaderConfig := dloader.Config{ - Login: c.User, - Password: c.Password, - BasePath: c.BaseDir, - StartDate: c.StartDate, - Nicks: map[string]bool{ - c.Type: true, - }, - VersionFilter: c.Version, - DistribFilter: c.Filter, + releases := getMapFromStrings(c.releases, "@", "latest") + filtersStr := getFilters(c.Filter.Value(), "=", "") + + var downloads []dloader.GetConfig + + for project, version := range releases { + + projectIdAlias := getProjectId(project) + + projectId := dloader.GetProjectIDByAlias(projectIdAlias) + projectFilters := compileFilters(filtersStr[projectId]...) + + if fileFilter, err := getProjectFilter(project); err != nil { + log.Errorf("error get project <%s> file filter: %s", projectIdAlias, err.Error()) + return fmt.Errorf("project <%s> %s", projectIdAlias, err.Error()) + } else if fileFilter != nil { + projectFilters = append(projectFilters, fileFilter) + } + + versionFilter, err := dloader.NewVersionFilter(projectId, version) + if err != nil { + return err + } + + downloads = append(downloads, dloader.GetConfig{ + BasePath: c.BaseDir, + Project: projectId, + Version: versionFilter, + Filters: projectFilters, + }) } - downloader := dloader.New(downloaderConfig) + var err error + dl := dloader.NewDownloader( + c.User, + c.Password, + ) - files, err := downloader.Get() + files, errGet := dl.Get(downloads...) + if errGet != nil { + err = multierr.Append(err, errGet) + } if err != nil { return err } - log.Infof("Downloaded <%d> files", len(files)) + log.Infof("Downloaded <%d> releases, files <%d>", len(downloads), len(files)) + + if c.Extract { + err := c.extractFiles(files) + if err != nil { + return err + } + } return nil } @@ -62,10 +107,94 @@ func (c *getCmd) run(ctx *cli.Context) error { func (c *getCmd) Cmd() *cli.Command { cmd := &cli.Command{ - Name: "get", - Usage: "Получение релиза сайта релизов 1С", + Name: "get", + Usage: "Получение релиза сайта релизов 1С", + ArgsUsage: "RELEASE...", + CustomHelpTemplate: cli.CommandHelpTemplate + `ARGUMENTS: + RELEASE - описание релиза в формате platform83[:filter.[filter]...]@8.3.18.1334 + ^ ^ ^ + имя проекта набор фильтров версия релиза + (см. ниже) (см. ниже) + + > Имя проекта - подсмотреть можно в адресе, ссылки имею вид например https://releases.1c.ru/project/EnterpriseERP20 + Синонимы проектов для быстрого доступа: + * platform - platform83 + * edt - DevelopmentTools10 + * ring - EnterpriseLicenseTools + * executor - Executor + * pg - AddCompPostgre + + > Набор фильтров - список предопределенных фильтров для проектов: + - По ОС: + * win, windows - фильтр по MS Windows + * mac - фильтр по OS X + * deb - фильтр по DEB-based Linux-систем + * rpm - фильтр по RPM-based Linux-систем + Например, platform:deb - будет скачаны файлы с фильтрацией по DEB-based Linux-систем + + - По разрядности OS: + * x32 - фильтр по 32-bit (по умолчанию, если не указан фильтр разрядности) + * x64 - фильтр по 64-bit + Например, platform:deb.x64 - будет скачаны файлы с фильтрацией по DEB-based Linux-систем и 64-bit + + - Для проекта platform (platform83) + * thin-client, thin - фильтр для файлов тонкого клиента 1С.Предприятие + * client - фильтр для файлов клиента 1С.Предприятие + * server - фильтр для файлов сервера 1С.Предприятие + * full - фильтр для файлов "Технологическая платформа" (только для Windows) + Например, platform:deb.server.x64 - будет скачаны файлы сервера с фильтрацией по DEB-based Linux-систем и 64-bit + + - Для проекта edt (DevelopmentTools10) + * jdk - фильтр для файлов Bellsoft JDK + * online - фильтр для файлов онлайн установщика 1С:EDT + + Например, edt:deb - будет скачаны файлы: + - Дистрибутив для оффлайн установки 1C:EDT для ОС Linux 64 бит + - Bellsoft JDK Full (64-bit) для DEB-based Linux-систем + + > Версии релиза: + В версии релиза может быть указан номер версии или специальные фильтры версии. + Если версия релиза пустая то подставляется фильтр "latest" ( "edt" воспринимается как "edt@latest" + + Специальные фильтры версии релиза: + * latest - выбирает наиболее старшую версию релиза + * latest:[regexp] - фильтрует список версию по , и берет наиболее старшую + * from: - фильтрует список версий по дате, у которых дата релиза больше + где, date - формате 02.06.21 + * from-v: - фильтрует список версий, которые старше версии релиза + где, version - формате номер версии + * - фильтрует список по регулярному выражению указанному в + + Примеры: + 1. "platform@from:01.01.21" - будут загружена все версии релизов, выпущенные начиная с даты 2020.01.01 + 2. "platform@from-v:8.3.16" - будут загружена все версии релизов, у которых версия старше чем 8.3.16 + 3. "platform@latest:8.3.16" - будут загружена последняя версия релиза 8.3.16 + 4. "platform@8.3.16" - будут загружена все версии релизов 8.3.16 + + > Пример полного использования: + Загрузка дистрибутивов платформа 1С.Предприятие последней версии 8.3.18 и 1C:EDT версии 2020.6.2 для OS Windows + - oneget get platform:win.x64@latest:8.3.18 edt:win@2020.6.2 + + Загрузка дистрибутивов платформа 1С.Предприятие последней версии 8.3.18 и 1C:EDT версии 2020.6.2 для OS X (Mac OS) + - oneget get platform:mac.x64@latest:8.3.18 edt:mac@2020.6.2 + +`, Flags: c.Flags(), Action: c.run, + Before: func(ctx *cli.Context) error { + + if !ctx.Args().Present() { + err := cli.ShowSubcommandHelp(ctx) + if err != nil { + return err + } + return fmt.Errorf("WRONG USAGE: Requires a RELEASE argument") + } + + c.releases = ctx.Args().Slice() + + return nil + }, } return cmd @@ -74,34 +203,15 @@ func (c *getCmd) Cmd() *cli.Command { func (c *getCmd) Flags() []cli.Flag { return []cli.Flag{ - &cli.StringFlag{ - Destination: &c.Type, - EnvVars: []string{"ONEGET_NICKS"}, - Name: "nick", - Usage: `Имена приложений (например \"platform83 или EnterpriseERP20\"), - подсмотреть можно в адресе, ссылки имею вид например https://releases.1c.ru/project/EnterpriseERP20`, - Required: true, - }, - &cli.StringFlag{ - Destination: &c.Version, - EnvVars: []string{"ONEGET_NICKS_VERSION"}, - Name: "version", - Usage: "Фильтр версий по номеру", - Required: true, - }, - &cli.TimestampFlag{ - DefaultText: time.Now().Format("2006-01-02"), - Layout: "2006-01-02", - EnvVars: []string{"ONEGET_START_DATE"}, - Name: "start-date", - Usage: "Фильтр версий по номеру", - }, - &cli.StringFlag{ + &cli.StringSliceFlag{ Destination: &c.Filter, - EnvVars: []string{"ONEGET_NICKS_FILTER"}, - Aliases: []string{"filter"}, - Name: "distrib-filter", - Usage: "Дополнительный фильтр пакетов (регулярное выражение)", + EnvVars: []string{"ONEGET_FILTER"}, + Aliases: []string{"F"}, + Name: "filter", + Usage: `Дополнительный фильтр пакетов (регулярное выражение) + Задается для каждого типа релиза отдельно. + Например, edt=".*JDK.*" +`, }, &cli.StringFlag{ Destination: &c.BaseDir, @@ -111,5 +221,174 @@ func (c *getCmd) Flags() []cli.Flag { DefaultText: "./downloads", Usage: "Путь к каталогу выгрузки", }, + &cli.BoolFlag{ + Name: "extract", + Destination: &c.Extract, + Aliases: []string{"E"}, + EnvVars: []string{"ONEGET_EXTRACT"}, + Value: false, + Usage: "Распаковывать дистрибутив (только для файлов tar.gz)", + }, + &cli.StringFlag{ + Name: "extract-path", + Destination: &c.ExtractDir, + EnvVars: []string{"ONEGET_EXTRACT_PATH"}, + Value: "", + Usage: "Каталог распаковки дистрибутива", + }, + &cli.BoolFlag{ + Name: "rename", + Aliases: []string{"R"}, + Destination: &c.Rename, + EnvVars: []string{"ONEGET_EXTRACT_RENAME"}, + Value: false, + Usage: `Переименовывать дистрибутивы при распаковке. + Примеры: + 1c-enterprise-8.3.18.1334-client_8.3.18-1334_amd64.deb -> client-8.3.18.1334.deb + 1c-enterprise83-server_8.3.16-1876_amd64.deb -> server_8.3.16-1876.deb`, + }, + } +} + +func (c *getCmd) extractFiles(files []string) error { + + log.Infof("Extracting ", len(files)) + + var mErr error + for _, file := range files { + if strings.ToLower(filepath.Ext(file)) == ".tar.gz" { + continue + } + + extractDir := file + "_extract" + + if len(c.ExtractDir) > 0 { + _, filename := filepath.Split(file) + extractDir = filepath.Join(c.ExtractDir, filename+"_extract") + } + + err := unpacker.Extract(file, extractDir) + if err != nil { + log.Errorf(err.Error()) + multierr.Append(mErr, err) + continue + } + + if c.Rename { + err := renameFiles(extractDir) + if err != nil { + multierr.Append(mErr, err) + continue + } + + } + } + + return mErr +} + +func renameFiles(dir string) error { + files, err := ioutil.ReadDir(dir) + if err != nil { + log.Errorf("Error find files in dir <%s> to rename: %s", dir, err.Error()) + return err + } + for _, file := range files { + if file.IsDir() { + continue + } + oldName := file.Name() + newName := unpacker.GetAliasesDistrib(oldName) + err := os.Rename( + filepath.Join(dir, oldName), + filepath.Join(dir, newName)) + if err != nil { + log.Errorf("Error rename file <%s> to <%s>: %s", oldName, newName, err.Error()) + continue + } + + } + return nil +} + +func getMapFromStrings(arr []string, sep string, defValue string) map[string]string { + + result := make(map[string]string) + + for _, str := range arr { + + values := strings.SplitN(str, sep, 2) + + key := values[0] + value := defValue + + if len(values) == 2 { + value = values[1] + } + + result[key] = value + + } + + return result +} + +func getFilters(arr []string, sep string, defValue string) map[string][]string { + + result := make(map[string][]string) + + for _, str := range arr { + + values := strings.SplitN(str, sep, 2) + + key := values[0] + value := defValue + + if len(values) == 2 { + value = values[1] + } + + if result[key] == nil { + result[key] = []string{} + } + + result[key] = append(result[key], value) + + } + + return result +} + +func getProjectId(project string) string { + + values := strings.SplitN(project, ":", 2) + + key := values[0] + + return key + +} + +func compileFilters(filters ...string) []dloader.FileFilter { + var result []dloader.FileFilter + for _, filter := range filters { + + compile := regexp.MustCompile(filter) + + result = append(result, compile) + } + + return result +} + +func getProjectFilter(project string) (dloader.FileFilter, error) { + + values := strings.SplitN(project, ":", 2) + + key := values[0] + + if len(values) == 2 { + return dloader.NewFileFilter(dloader.GetProjectIDByAlias(key), values[1]) } + return nil, nil } diff --git a/downloader/client.go b/downloader/client.go new file mode 100644 index 0000000..cd7c7cc --- /dev/null +++ b/downloader/client.go @@ -0,0 +1,225 @@ +package downloader + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/cookiejar" + "strings" + "sync" +) + +func NewClient(loginUrl string, baseUrl string, login string, password string) (*Client, error) { + + cj, _ := cookiejar.New(nil) + + c := &Client{ + login: login, + password: password, + loginUrl: loginUrl, + baseUrl: baseUrl, + cookie: cj, + } + + url, err := c.getAuthTicketURL(baseUrl) + if err != nil { + return nil, err + } + + loginResp, err := c.Get(url) + if err != nil { + return nil, err + } + defer loginResp.Body.Close() + + return c, nil +} + +type Client struct { + cookie *cookiejar.Jar + login string + password string + loginUrl string + baseUrl string +} + +func (c *Client) getAuthTicketURL(url string) (string, error) { + + type loginParams struct { + Login string `json:"login"` + Password string `json:"password"` + ServiceNick string `json:"serviceNick"` + } + + type ticket struct { + Ticket string `json:"ticket"` + } + + ticketUrl := c.loginUrl + "/rest/public/ticket/get" + postBody, err := json.Marshal( + loginParams{c.login, c.password, url}) + if err != nil { + return "", err + } + + buf := bytes.NewBuffer(postBody) + defer put(buf) + req, err := http.NewRequest("POST", ticketUrl, buf) + + if err != nil { + return "", err + } + + req.SetBasicAuth(c.login, c.password) + req.Header.Set("Content-Type", "application/json") + resp, err := c.doRequest(req) + + if err != nil { + return "", err + } + + defer resp.Body.Close() + + switch resp.StatusCode { + case http.StatusOK: + + var ticketData ticket + err := bodyToJSON(resp.Body, &ticketData) + if err != nil { + return "", err + } + + return fmt.Sprintf(loginURL+"/ticket/auth?token=%s", ticketData.Ticket), nil + + default: + + type ErrorRespond struct { + Timestamp string `json:"timestamp"` + Status int `json:"status"` + Error string `json:"error"` + Exception string `json:"exception"` + Message string `json:"message"` + Path string `json:"path"` + } + + var errData ErrorRespond + + err := bodyToJSON(resp.Body, &errData) + if err != nil { + return "", err + } + + return "", fmt.Errorf("%s: %s", errData.Error, errData.Message) + } +} + +func (c *Client) Get(getUrl string) (*http.Response, error) { + + // для URL вида /total/ + if strings.HasPrefix(getUrl, "/") { + getUrl = c.baseUrl + getUrl + } + + req, err := http.NewRequest("GET", getUrl, nil) + + if err != nil { + return nil, err + } + + req.SetBasicAuth(c.login, c.password) + + resp, err := c.doRequest(req) + + if err != nil { + return nil, err + } + + switch resp.StatusCode { + case http.StatusUnauthorized: + log.Debugf("Re-authorized with ticket url: %s", getUrl) + url, err := c.getAuthTicketURL(getUrl) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", url, nil) + + if err != nil { + return nil, err + } + + req.SetBasicAuth(c.login, c.password) + + return c.doRequest(req) + + case http.StatusBadRequest, http.StatusNotFound: + + return nil, fmt.Errorf("respose CODE:%d ERR:%s", + resp.StatusCode, readBodyMustString(resp.Body)) + case http.StatusOK: + return resp, nil + default: + return resp, fmt.Errorf("unknown respose CODE: <%d>", resp.StatusCode) + } + +} + +func (c *Client) client() *http.Client { + + return &http.Client{ + Jar: c.cookie, + } +} + +func (c *Client) doRequest(req *http.Request) (*http.Response, error) { + + return c.client().Do(req) + +} + +func bodyToJSON(body io.ReadCloser, into interface{}) error { + b, err := readBody(body) + if err != nil { + return err + } + + return json.Unmarshal(b, into) +} + +func readBody(body io.ReadCloser) ([]byte, error) { + buf := get() + defer put(buf) + _, err := io.Copy(buf, body) + if err != nil { + return nil, err + } + + return buf.Bytes(), err +} + +func readBodyMustString(body io.ReadCloser) string { + buf, err := readBody(body) + if err != nil { + log.Errorf("must read body err: %s", err.Error()) + return "" + } + + return string(buf) +} + +var pool = sync.Pool{ + New: func() interface{} { + return &bytes.Buffer{} + }, +} + +func get() *bytes.Buffer { + return pool.Get().(*bytes.Buffer) +} + +func put(buf *bytes.Buffer) { + buf.Reset() + pool.Put(buf) +} diff --git a/downloader/downloader.go b/downloader/downloader.go index e9286a6..efaf86d 100644 --- a/downloader/downloader.go +++ b/downloader/downloader.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "github.com/khorevaa/logos" + "github.com/v8platform/oneget/unpacker" "golang.org/x/net/html" "io" "io/ioutil" @@ -33,9 +34,10 @@ var semaMaxConnections = make(chan struct{}, 10) var log = logos.New("github.com/v8platform/oneget/downloader").Sugar() type FileToDownload struct { - url string - path string - name string + url []string + basePath string + path string + name string } type Config struct { @@ -46,8 +48,12 @@ type Config struct { Nicks map[string]bool VersionFilter string DistribFilter string + Extract bool + ExtractPath string + Rename bool } + type Downloader struct { Config httpClient *http.Client @@ -286,7 +292,7 @@ func (dr *Downloader) addFileToChannel(u, href string) { fileName, filePath, err := dr.fileNameFromUrl(u) if err == nil { fileToDownload := FileToDownload{ - url: href, + url: []string{href}, path: filePath, name: fileName, } @@ -348,7 +354,7 @@ func (dr *Downloader) downloadFile(fileToDownload *FileToDownload) (os.FileInfo, log.Debugf("Getting a file from url: %s", fileToDownload.url) acquireSemaConnections() - resp, err := dr.httpClient.Get(fileToDownload.url) + resp, err := dr.httpClient.Get(fileToDownload.url[0]) if err != nil { return nil, err } @@ -365,8 +371,11 @@ func (dr *Downloader) downloadFile(fileToDownload *FileToDownload) (os.FileInfo, if err != nil { return nil, err } + f.Close() + log.Debugf("End of receiving file by url: %s", fileToDownload.url) + log.Debugf("File saved to: %s", fileName) log.Debugf("End of receiving file by url: %s", fileToDownload.url) log.Debugf("File saved to: %s", fileName) @@ -375,6 +384,32 @@ func (dr *Downloader) downloadFile(fileToDownload *FileToDownload) (os.FileInfo, return nil, err } + if dr.Extract { + extractDir := dr.ExtractPath + unpacker.Extract(fileName, extractDir) + + if dr.Rename { + files, err := ioutil.ReadDir(extractDir) + if err != nil { + return nil, err + } + for _, file := range files { + if file.IsDir() { + continue + } + oldName := file.Name() + newName := unpacker.GetAliasesDistrib(oldName) + err := os.Rename( + filepath.Join(extractDir, oldName), + filepath.Join(extractDir, newName)) + if err != nil { + return nil, err + } + } + + } + } + fileInfo, err := os.Stat(fileName) if err != nil { return nil, err @@ -383,7 +418,7 @@ func (dr *Downloader) downloadFile(fileToDownload *FileToDownload) (os.FileInfo, return fileInfo, nil } else if err != nil { - + log.Debugf("Error download files: %s", err) } return nil, nil diff --git a/downloader/downloader_test.go b/downloader/downloader_test.go index a8eb3be..10c2c4b 100644 --- a/downloader/downloader_test.go +++ b/downloader/downloader_test.go @@ -1,11 +1,8 @@ package downloader import ( - "bytes" "fmt" - "io" "io/ioutil" - "log" "net/http" "net/http/httptest" "net/url" @@ -15,35 +12,27 @@ import ( "time" ) -func init() { - SetLogOutput(bytes.NewBuffer(nil)) -} - -func SetLogOutput(out io.Writer) { - logOutput = out -} - func TestNewDownloader(t *testing.T) { startDate, err := time.Parse("02.01.2006", "20.01.2020") if err != nil { - log.Fatal(err) + t.Error(err) } - conf := Downloader{ - Login: "user", - Password: "user", - StartDate: startDate, + conf := Config{ + Login: "user", + Password: "user", + StartDate: startDate, } - New(&conf) + New(conf) } -func TestLoginIncorrect(t *testing.T) { - conf := Downloader{ - Login: "user", - Password: "user", - StartDate: time.Now(), +func TestLoginIncorrect(t *testing.T) { + conf := Config{ + Login: "user", + Password: "user", + StartDate: time.Now(), } - downldr := New(&conf) + downldr := New(conf) _, err := downldr.Get() if !(strings.Contains(err.Error(), "Incorrect login or password") || @@ -58,11 +47,11 @@ func TestGetPlatform_8_3_18_1334_linux(t *testing.T) { dir, err := ioutil.TempDir("", "oneget") if err != nil { - log.Fatal(err) + t.Fatal(err) } defer os.RemoveAll(dir) - conf := Downloader{ + conf := Config{ Login: "user", Password: "user", Nicks: nicks, @@ -83,11 +72,11 @@ func TestGetPlatform_8_3_18_1334_windows(t *testing.T) { dir, err := ioutil.TempDir("", "oneget") if err != nil { - log.Fatal(err) + t.Fatal(err) } defer os.RemoveAll(dir) - conf := Downloader{ + conf := Config{ Login: "user", Password: "user", Nicks: nicks, @@ -101,7 +90,7 @@ func TestGetPlatform_8_3_18_1334_windows(t *testing.T) { } } -func GetPlatform(t *testing.T, conf Downloader) []os.FileInfo { +func GetPlatform(t *testing.T, conf Config) []os.FileInfo { handler := getHandler() @@ -116,7 +105,7 @@ func GetPlatform(t *testing.T, conf Downloader) []os.FileInfo { defer func() { releasesURL = releasesURL_bak; loginURL = loginURL_bak }() - downldr := New(&conf) + downldr := New(conf) files, err := downldr.Get() if err != nil { t.Error(err) @@ -153,7 +142,7 @@ func getHandler() func(w http.ResponseWriter, r *http.Request) { } else if r.URL.Path == "/releases/version_files" { query, err := url.ParseQuery(r.URL.RawQuery) if err != nil { - log.Fatal(err) + log.Fatal(err.Error()) } nick := query.Get("nick") @@ -182,8 +171,8 @@ func getHandler() func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "File received!") } else if strings.Contains(r.RequestURI, "/public/file/get/id") { fmt.Fprintln(w, "Distribution received!") - }else { + } else { println("debug") } } -} \ No newline at end of file +} diff --git a/downloader/filter.go b/downloader/filter.go new file mode 100644 index 0000000..ddf28d7 --- /dev/null +++ b/downloader/filter.go @@ -0,0 +1,495 @@ +package downloader + +import ( + "fmt" + "github.com/xelaj/go-dry" + "regexp" + "sort" + "strings" + "time" +) + +const ( + Platform83Project = "Platform83" + EDTProject = "DevelopmentTools10" +) +const ( + x64re = "(?smU)(?:64-bit|64 бит).*" + rpmre = "(?smU)(?:RPM|ОС Linux|для Linux$|tar.bz2).*" + debre = "(?smU)(?:DEB|ОС Linux|для Linux$|tar.bz2).*" + windowsre = "(?smU)(?:Windows|ОС Windows|zip).*" + osxre = "(?smU)(?:OS X|macOS|MacOS|ОС macOS).*" + + clientre = "(?smU)Клиент" + serverre = "(?smU)(?:Cервер|Сервер)" + thinre = "(?smU)Тонкий клиент" + fullwindowsre = "(?smU)Технологическая платформа" +) + +/* + + Специальные фильтры для платформы и ряда других приложение + + platform:thin.mac@latest + platform:full.win.x64@latest + platform:win.x64@latest + platform:server.deb.x64@latest + + +*/ + +var ( + ProjectAliases = map[string]string{ + "platform": Platform83Project, + "edt": EDTProject, + "ring": "EnterpriseLicenseTools", + "executor": "Executor", + "pg": "AddCompPostgre", + } + + shortFilters = map[string]string{ + "mac": osxre, + "windows": windowsre, + "win": windowsre, + "deb": debre, + "rpm": rpmre, + "x64": x64re, + "client": clientre, + "server": serverre, + "thin-client": thinre, + "thin": thinre, + "online": "edt.online", + "jdk": "edt.jdk", + "full": fullwindowsre, + } + + x64Regexp = regexp.MustCompile(x64re) +) + +type FileFilter interface { + MatchString(source string) bool +} + +type VersionFilter interface { + Filter(source []*ProjectVersionInfo) (result []*ProjectVersionInfo) +} + +type PlatformMatchFilter struct { + filters []*regexp.Regexp + x64bitMatch bool + x64bitRegexp *regexp.Regexp +} + +func GetProjectIDByAlias(alias string) string { + + if val, ok := ProjectAliases[alias]; ok { + return val + } + + return alias + +} + +func NewFileFilter(project string, filter string) (FileFilter, error) { + + switch project { + case Platform83Project: + return newPlatformMatchFilter(filter) + case EDTProject: + return newEdtFilter(filter) + default: + return nil, fmt.Errorf("unknown filter builder for project <%s>", project) + } +} + +func NewFileFilterMust(project string, filter string) FileFilter { + + newFilter, err := NewFileFilter(project, filter) + if err != nil { + log.Fatal(err.Error()) + return nil + } + + return newFilter +} + +func newPlatformMatchFilter(filter string) (*PlatformMatchFilter, error) { + + m := &PlatformMatchFilter{ + x64bitMatch: false, + x64bitRegexp: x64Regexp, + } + + filters := strings.Split(filter, ".") + filters = removeFromFilter(filters, "x32") + + if ok := dry.StringInSlice("x64", filters); ok { + filters = removeFromFilter(filters, "x64") + m.x64bitMatch = true + } + + // Для Windows если стоит только фильтр по нему и другого нет, то установим для платформы скачиваем полного дистрибутива + if ok := dry.StringInSlice("win", filters) || dry.StringInSlice("windows", filters); ok && len(filters) == 1 { + filters = append(filters, "full") + } + + if ok := dry.StringInSlice("mac", filters); ok { + filters = removeFromFilter(filters, "server") + m.x64bitMatch = false + } + + err := m.build(filters) + + return m, err +} + +func (m *PlatformMatchFilter) MatchString(source string) bool { + + if m.x64bitRegexp.MatchString(source) != m.x64bitMatch { + return false + } + + for _, filter := range m.filters { + + if ok := filter.MatchString(source); !ok { + return false + } + + } + + return true +} + +func (m *PlatformMatchFilter) build(filters []string) error { + + for _, filter := range filters { + + val, ok := shortFilters[filter] + + if !ok { + return fmt.Errorf("unknown <%s> filter", filter) + } + + m.filters = append(m.filters, regexp.MustCompile(val)) + + } + + return nil +} + +func removeFromFilter(s []string, r string) []string { + for i, v := range s { + if v == r { + return append(s[:i], s[i+1:]...) + } + } + return s +} + +type EdtMatchFilter struct { + filters []*regexp.Regexp + matchOffline bool + matchOfflineRe []*regexp.Regexp + distrRe []*regexp.Regexp +} + +func newEdtFilter(filter string) (*EdtMatchFilter, error) { + m := &EdtMatchFilter{ + matchOffline: true, + matchOfflineRe: []*regexp.Regexp{ + regexp.MustCompile(".*оффлайн установки.*"), + regexp.MustCompile(".*1c_edt_distr_offline.*"), + }, + distrRe: []*regexp.Regexp{ + regexp.MustCompile(".*1C:EDT*"), + regexp.MustCompile(".*1c_edt_distr.*"), + }, + } + + filters := strings.Split(filter, ".") + filters = removeFromFilter(filters, "x32") + filters = removeFromFilter(filters, "x64") + if ok := dry.StringInSlice("online", filters); ok { + m.matchOffline = false + filters = removeFromFilter(filters, "online") + } + + if ok := dry.StringInSlice("jdk", filters); ok { + m.filters = append(m.filters, regexp.MustCompile(".*JDK.*")) + filters = removeFromFilter(filters, "jdk") + } + + err := m.build(filters) + if err != nil { + return nil, err + } + + return m, err +} + +func (m *EdtMatchFilter) build(filters []string) error { + + for _, filter := range filters { + + val, ok := shortFilters[filter] + + if !ok { + return fmt.Errorf("unknown <%s> filter", filter) + } + + m.filters = append(m.filters, regexp.MustCompile(val)) + + } + + return nil +} + +func (m *EdtMatchFilter) isDistrFile(source string) bool { + for _, filter := range m.distrRe { + + if ok := filter.MatchString(source); ok { + return true + } + + } + + return false +} + +func (m *EdtMatchFilter) isOfflineDistrFile(source string) bool { + for _, filter := range m.matchOfflineRe { + + if ok := filter.MatchString(source); ok { + return true + } + + } + + return false +} + +func (m *EdtMatchFilter) MatchString(source string) bool { + + for _, filter := range m.filters { + + if ok := filter.MatchString(source); !ok { + return false + } + + } + + if m.isDistrFile(source) { + + offlineDistr := m.isOfflineDistrFile(source) + + if m.matchOffline && offlineDistr { + return true + } + + if m.matchOffline && !offlineDistr { + return false + } + + } + + return true +} + +func NewVersionFilter(_ string, filter string) (VersionFilter, error) { + + switch { + case strings.HasPrefix(filter, "from:"): + return newVersionDateFilter(filter) + case strings.HasPrefix(filter, "from-v:"): + return newVersionFromFilter(filter) + case strings.HasPrefix(filter, "latest"): + return newLatestVersionFilter(filter) + default: + return newVersionFilter(filter) + } +} + +func NewVersionFilterMust(project string, filter string) VersionFilter { + + newFilter, err := NewVersionFilter(project, filter) + if err != nil { + log.Fatal(err.Error()) + return nil + } + + return newFilter +} + +type versionFilter struct { + filter *regexp.Regexp +} + +func (m *versionFilter) Filter(source []*ProjectVersionInfo) (result []*ProjectVersionInfo) { + + eachVersion(source, func(versionInfo *ProjectVersionInfo) { + if m.filter.MatchString(versionInfo.Name) { + result = append(result, versionInfo) + } + }) + + return +} + +func eachVersion(source []*ProjectVersionInfo, fn func(versionInfo *ProjectVersionInfo)) { + for _, info := range source { + fn(info) + } +} + +func newVersionFilter(filter string) (*versionFilter, error) { + + return &versionFilter{ + regexp.MustCompile(filter), + }, nil + +} + +type VersionDateFilter struct { + date time.Time +} + +func (m *VersionDateFilter) Filter(source []*ProjectVersionInfo) (result []*ProjectVersionInfo) { + + eachVersion(source, func(versionInfo *ProjectVersionInfo) { + if versionInfo.PublishDate.After(m.date) { + result = append(result, versionInfo) + } + }) + return +} + +func newVersionDateFilter(filter string) (*VersionDateFilter, error) { + + values := strings.SplitN(filter, ":", 2) + + if len(values) == 1 { + return nil, fmt.Errorf("error date filter: no contain date value") + } + + data, err := time.Parse("02.01.06", values[1]) + if err != nil { + return nil, fmt.Errorf("error parse date: %s", err.Error()) + } + + return &VersionDateFilter{ + data, + }, nil + +} + +type VersionFromFilter struct { + version string +} + +func (m *VersionFromFilter) Filter(source []*ProjectVersionInfo) (result []*ProjectVersionInfo) { + + eachVersion(source, func(versionInfo *ProjectVersionInfo) { + if compareVersion(versionInfo.Name, m.version) >= 0 { + result = append(result, versionInfo) + } + }) + return +} + +func newVersionFromFilter(filter string) (*VersionFromFilter, error) { + + values := strings.SplitN(filter, ":", 2) + + if len(values) == 1 { + return nil, fmt.Errorf("error from-v filter: no contain version value") + } + + // TODO Сделать проверку по формату версии + + return &VersionFromFilter{ + values[1], + }, nil + +} + +type LatestVersionFilter struct { + filter *regexp.Regexp +} + +func (m *LatestVersionFilter) Filter(source []*ProjectVersionInfo) (latest []*ProjectVersionInfo) { + + if m.filter != nil { + var result []*ProjectVersionInfo + eachVersion(source, func(versionInfo *ProjectVersionInfo) { + if m.filter.MatchString(versionInfo.Name) { + result = append(result, versionInfo) + } + }) + source = result + } + + if len(source) == 0 { + return + } + + sort.Slice(source, func(i, j int) bool { + return compareVersion(source[i].Name, source[j].Name) > 0 + }) + + latest = append(latest, source[0]) + + return +} + +func compareVersion(v1, v2 string) int { + + compV1 := strings.Split(v1, ".") + compV2 := strings.Split(v2, ".") + + maxLen := len(compV1) + + if len(compV1) < len(compV2) { + maxLen = len(compV2) + } + + for i := len(compV1); i < maxLen; i++ { + compV1 = append(compV1, "0") + } + for i := len(compV2); i < maxLen; i++ { + compV2 = append(compV2, "0") + } + + for i := 0; i < maxLen; i++ { + + if dry.StringToInt(compV1[i]) > dry.StringToInt(compV2[i]) { + return 1 + } + + if dry.StringToInt(compV1[i]) < dry.StringToInt(compV2[i]) { + return -1 + } + } + + return 0 +} + +func newLatestVersionFilter(filter string) (*LatestVersionFilter, error) { + + var filterRe *regexp.Regexp + + values := strings.SplitN(filter, ":", 2) + + if len(values) == 2 { + var err error + filterRe, err = regexp.Compile(values[1]) + + if err != nil { + return nil, fmt.Errorf("error latest filter: %s", err.Error()) + } + } + + return &LatestVersionFilter{ + filterRe, + }, nil + +} diff --git a/downloader/filter_test.go b/downloader/filter_test.go new file mode 100644 index 0000000..21e30f4 --- /dev/null +++ b/downloader/filter_test.go @@ -0,0 +1,337 @@ +package downloader + +import ( + "fmt" + "reflect" + "regexp" + "strings" + "testing" +) + +func TestMatchFilter_Match(t *testing.T) { + + type args struct { + filters string + project string + } + + type want struct { + source string + want bool + } + + tests := []struct { + filters string + project string + source string + want bool + }{ + { + "server.x64.win", + Platform83Project, + "Cервер 1С:Предприятия (64-bit) для Windows", + true, + }, { + "server.x64.win", + Platform83Project, + `Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятия для Windows +Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятия для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия для macOS +Технологическая платформа 1С:Предприятия для Windows +Технологическая платформа 1С:Предприятия (64-bit) для Windows +Cервер 1С:Предприятия для DEB-based Linux-систем +Клиент 1С:Предприятия для macOS +Cервер 1С:Предприятия для RPM-based Linux-систем +Cервер 1С:Предприятия (64-bit) для DEB-based Linux-систем +Cервер 1С:Предприятия (64-bit) для RPM-based Linux-систем +Cервер 1С:Предприятия для Windows +`, + false, + }, { + "server.x32.win", + Platform83Project, + "Cервер 1С:Предприятия для Windows", + true, + }, { + "server.x32.win", + Platform83Project, + `Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятия для Windows +Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятия для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия для macOS +Технологическая платформа 1С:Предприятия для Windows +Технологическая платформа 1С:Предприятия (64-bit) для Windows +Cервер 1С:Предприятия для DEB-based Linux-систем +Клиент 1С:Предприятия для macOS +Cервер 1С:Предприятия для RPM-based Linux-систем +Cервер 1С:Предприятия (64-bit) для DEB-based Linux-систем +Cервер 1С:Предприятия (64-bit) для RPM-based Linux-систем +`, + false, + }, { + "server.win", + Platform83Project, + "Cервер 1С:Предприятия для Windows", + true, + }, { + "server.win", + Platform83Project, + `Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятия для Windows +Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятия для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия для macOS +Технологическая платформа 1С:Предприятия для Windows +Технологическая платформа 1С:Предприятия (64-bit) для Windows +Cервер 1С:Предприятия для DEB-based Linux-систем +Клиент 1С:Предприятия для macOS +Cервер 1С:Предприятия для RPM-based Linux-систем +Cервер 1С:Предприятия (64-bit) для DEB-based Linux-систем +Cервер 1С:Предприятия (64-bit) для RPM-based Linux-систем +`, + false, + }, { + "thin.win", + Platform83Project, + "Тонкий клиент 1С:Предприятия для Windows", + true, + }, { + "thin.win", + Platform83Project, + `Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятия для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия для macOS +Технологическая платформа 1С:Предприятия для Windows +Технологическая платформа 1С:Предприятия (64-bit) для Windows +Cервер 1С:Предприятия для DEB-based Linux-систем +Клиент 1С:Предприятия для macOS +Cервер 1С:Предприятия для RPM-based Linux-систем +Cервер 1С:Предприятия (64-bit) для DEB-based Linux-систем +Cервер 1С:Предприятия (64-bit) для RPM-based Linux-систем +Cервер 1С:Предприятия для Windows +`, + false, + }, { + "win", + Platform83Project, + `Технологическая платформа 1С:Предприятия для Windows`, + true, + }, { + "win.x64", + Platform83Project, + `Технологическая платформа 1С:Предприятия (64-bit) для Windows`, + true, + }, { + "win", + Platform83Project, + `Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятия для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия для macOS +Тонкий клиент 1С:Предприятия для Windows +Cервер 1С:Предприятия для Windows +Технологическая платформа 1С:Предприятия (64-bit) для Windows +Cервер 1С:Предприятия для DEB-based Linux-систем +Клиент 1С:Предприятия для macOS +Cервер 1С:Предприятия для RPM-based Linux-систем +Cервер 1С:Предприятия (64-bit) для DEB-based Linux-систем +Cервер 1С:Предприятия (64-bit) для RPM-based Linux-систем +`, + false, + }, { + "win.full", + Platform83Project, + `Технологическая платформа 1С:Предприятия для Windows`, + true, + }, { + "win.full.x64", + Platform83Project, + `Технологическая платформа 1С:Предприятия (64-bit) для Windows`, + true, + }, { + "win.full", + Platform83Project, + `Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятие (64-bit) для Windows +Тонкий клиент 1С:Предприятия для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для DEB-based Linux-систем +Тонкий клиент 1С:Предприятия (64-bit) для RPM-based Linux-систем +Тонкий клиент 1С:Предприятия для macOS +Технологическая платформа 1С:Предприятия (64-bit) для Windows +Cервер 1С:Предприятия для DEB-based Linux-систем +Клиент 1С:Предприятия для macOS +Cервер 1С:Предприятия для RPM-based Linux-систем +Cервер 1С:Предприятия (64-bit) для DEB-based Linux-систем +Cервер 1С:Предприятия (64-bit) для RPM-based Linux-систем +`, + false, + }, + } + for _, tt := range tests { + t.Run(tt.filters, func(t *testing.T) { + m, err := NewFileFilter(tt.project, tt.filters) + + if err != nil { + t.Fatal(err) + } + + cases := strings.Split(tt.source, "\n") + for _, testCase := range cases { + if got := m.MatchString(testCase); got != tt.want { + t.Errorf("Match() = %v, want %v, source: %s", got, tt.want, testCase) + } + } + + }) + } +} + +func TestLatestVersionFilter_Filter(t *testing.T) { + + tests := []struct { + name string + filter *regexp.Regexp + versions []*ProjectVersionInfo + wantLatest []*ProjectVersionInfo + }{ + { + "simple", + regexp.MustCompile("8.3.16"), + []*ProjectVersionInfo{ + { + Name: "8.3.16.1324", + }, { + Name: "8.3.16.965", + }, { + Name: "8.3.17.1324", + }, + }, + []*ProjectVersionInfo{ + { + Name: "8.3.16.1324", + }, + }, + }, { + "no filter", + nil, + []*ProjectVersionInfo{ + { + Name: "8.3.16.1324", + }, { + Name: "8.3.16.965", + }, { + Name: "8.3.17.1589", + }, + }, + []*ProjectVersionInfo{ + { + Name: "8.3.17.1589", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &LatestVersionFilter{ + filter: tt.filter, + } + if gotLatest := m.Filter(tt.versions); !reflect.DeepEqual(gotLatest, tt.wantLatest) { + t.Errorf("Filter() = %v, want %v", gotLatest[0], tt.wantLatest[0]) + } + }) + } +} + +func Test_compareVersion(t *testing.T) { + type args struct { + } + tests := []struct { + v1 string + v2 string + want int + }{ + {"8.3.10.1877", "8.3.9.2016", 1}, + {"8.3.10.1877", "", 1}, + {"08.03.010.01877", "8.3.9.2016", 1}, + {"8.3.9.2016", "8.3.10.1877", -1}, + {"8.3", "8.3.9.2016", -1}, + {"08.03.09.0002016", "8.3.9.2016", 0}, + // two values + {"1.2", "1.1", 1}, + {"1.10", "1.9", 1}, + {"1.10.1", "1.10", 1}, + {"1.2", "", 1}, + {"1.1", "1.2", -1}, + {"1.5", "1.5.0", 0}, + // edt format + {"2020.2", "2020.1", 1}, + {"2020.2.1", "2020.2.0", 1}, + {"2020.3", "1.16.0.363", 1}, + {"2020.6", "2021.1", -1}, + {"2020.6.2", "2020.6.3", -1}, + {"2020.2.0", "2020.2", 0}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%s vs %s", tt.v1, tt.v2), func(t *testing.T) { + if got := compareVersion(tt.v1, tt.v2); got != tt.want { + t.Errorf("compareVersion() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestVersionFromFilter_Filter(t *testing.T) { + + tests := []struct { + name string + filter string + versions []string + wantResult []string + }{ + {"no results", "8.3.18", []string{"8.3.16.1564"}, []string{}}, + {"all results", "8.3.16", []string{"8.3.16.1564", "8.3.16.965"}, []string{"8.3.16.1564", "8.3.16.965"}}, + {"only 8.3", "8.3", []string{"8.3.16.1564", "8.3.16.965", "8.2.8"}, []string{"8.3.16.1564", "8.3.16.965"}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + m := &VersionFromFilter{ + version: tt.filter, + } + + var versions = []*ProjectVersionInfo{} + for _, version := range tt.versions { + versions = append(versions, &ProjectVersionInfo{Name: version}) + } + + var wantResult = []*ProjectVersionInfo{} + for _, version := range tt.wantResult { + wantResult = append(wantResult, &ProjectVersionInfo{Name: version}) + } + + if gotResult := m.Filter(versions); !(len(gotResult) == 0 && len(wantResult) == 0) && !reflect.DeepEqual(gotResult, wantResult) { + t.Errorf("Filter() = %v, want %v", gotResult, wantResult) + } + }) + } +} diff --git a/downloader/html.go b/downloader/html.go new file mode 100644 index 0000000..aa7fdec --- /dev/null +++ b/downloader/html.go @@ -0,0 +1,257 @@ +package downloader + +import ( + "fmt" + "github.com/PuerkitoBio/goquery" + "io" + "strings" + "time" +) + +type HtmlParser struct { + HtmlParserConfig +} + +type HtmlParserConfig struct { + ReleaseTableSelector string // [id$='actualTable'] + ProjectTableSelector string // [id$='versionsTable'] + ProjectReleaseSelector string // ".files-container .formLine a" + ReleaseFilesSelector string // ".downloadDist a" + +} + +var defaultConfig = HtmlParserConfig{ + + ReleaseTableSelector: "[id$='actualTable']", + ProjectTableSelector: "[id$='versionsTable']", + ProjectReleaseSelector: ".files-container .formLine a", + ReleaseFilesSelector: ".downloadDist a", +} + +func NewHtmlParser(config ...HtmlParserConfig) (*HtmlParser, error) { + + cfg := defaultConfig + + if len(config) == 1 { + cfg = config[0] + } + + return &HtmlParser{ + cfg, + }, nil +} + +func (p *HtmlParser) ParseTotalReleases(body io.Reader) (rows []ProjectInfo, err error) { + + doc, err := goquery.NewDocumentFromReader(body) + if err != nil { + return nil, err + } + + var tableHtml *goquery.Selection + + doc.Find(p.ReleaseTableSelector).Each(func(i int, html *goquery.Selection) { + tableHtml = html + return + }) + + if tableHtml == nil { + return + } + + return parseReleasesTable(tableHtml), nil + +} + +func (p *HtmlParser) ParseProjectReleases(body io.Reader) (rows []*ProjectVersionInfo, err error) { + + doc, err := goquery.NewDocumentFromReader(body) + if err != nil { + return nil, err + } + + var tableHtml *goquery.Selection + + doc.Find(p.ProjectTableSelector).Each(func(i int, html *goquery.Selection) { + tableHtml = html + return + }) + + if tableHtml == nil { + return nil, fmt.Errorf("not found html tag by selector: <%s>", p.ProjectTableSelector) + } + + return parseProjectTable(tableHtml), nil + +} + +func (p *HtmlParser) ParseProjectRelease(body io.Reader) (rows []ReleaseFileInfo, err error) { + + doc, err := goquery.NewDocumentFromReader(body) + if err != nil { + return nil, err + } + + doc.Find(p.ProjectReleaseSelector).Each(func(i int, releaseFileHtml *goquery.Selection) { + name := strings.TrimSpace(releaseFileHtml.Text()) + url := releaseFileHtml.AttrOr("href", "") + if strings.HasPrefix(url, "/version_file") { + rows = append(rows, ReleaseFileInfo{ + name: name, + url: url, + }) + } + + }) + + return + +} + +func (p *HtmlParser) ParseReleaseFiles(body io.Reader) (rows []string, err error) { + + doc, err := goquery.NewDocumentFromReader(body) + if err != nil { + return nil, err + } + + doc.Find(p.ReleaseFilesSelector).Each(func(i int, html *goquery.Selection) { + ref, ok := html.Attr("href") + if !ok { + return + } + rows = append(rows, ref) + }) + + return + +} + +type ReleaseFileInfo struct { + name string + url string +} + +type ProjectInfo struct { + ID string + Group string + GroupID string + Name string + Url string + VersionsInfo []*ProjectVersionInfo + TestVersionsInfo []*ProjectVersionInfo +} + +type ProjectVersionInfo struct { + Url string + Name string + PublishDate time.Time +} + +func parseReleasesTable(s *goquery.Selection) (rows []ProjectInfo) { + + s.Find("[group]").Each(func(i int, groupInfo *goquery.Selection) { + groupId, _ := groupInfo.Attr("group") + groupName := groupInfo.Find(".group-name").Text() + + log.Debugf("Group <%s> - <%s>", groupId, groupName) + + s.Find(fmt.Sprintf("[parent-group$='%s']", groupId)).Each(func(_ int, releaseRow *goquery.Selection) { + + info := ProjectInfo{ + Group: groupName, + GroupID: groupId, + } + + info.Name = strings.TrimSpace(releaseRow.Find(".nameColumn").Text()) + info.Url, _ = releaseRow.Find(".nameColumn a").Attr("href") + info.ID = strings.TrimLeft(info.Url, "/project/") + + releaseRow.Find("td").Each(func(i int, rowHtml *goquery.Selection) { + + switch i { + case 1: // .versionColumn .actualVersionColumn + rowHtml.Find("a").Each(func(i int, releaseHtml *goquery.Selection) { + releaseInfo := &ProjectVersionInfo{ + Url: releaseHtml.AttrOr("href", ""), + Name: strings.TrimSpace(releaseHtml.Text()), + } + info.VersionsInfo = append(info.VersionsInfo, releaseInfo) + }) + case 2: // .releaseDate + rowHtml.Find("span").Each(func(i int, releaseHtml *goquery.Selection) { + if len(info.VersionsInfo) > i { + textDate := strings.TrimSpace(releaseHtml.Text()) + publishDate, err := parseReleaseDate(textDate) + if err != nil { + log.Errorf("Error parse <%s> for %s", textDate, info.VersionsInfo[i].Name) + } + info.VersionsInfo[i].PublishDate = publishDate + } + }) + case 6: // .versionColumn + rowHtml.Find("a").Each(func(i int, releaseHtml *goquery.Selection) { + releaseInfo := &ProjectVersionInfo{ + Url: releaseHtml.AttrOr("href", ""), + Name: strings.TrimSpace(releaseHtml.Text()), + } + info.TestVersionsInfo = append(info.TestVersionsInfo, releaseInfo) + }) + case 7: // .publicationDate + rowHtml.Find("span").Each(func(i int, releaseHtml *goquery.Selection) { + if len(info.TestVersionsInfo) > i { + textDate := strings.TrimSpace(releaseHtml.Text()) + publishDate, err := parseReleaseDate(textDate) + if err != nil { + log.Errorf("Error parse <%s> for %s", textDate, info.TestVersionsInfo[i].Name) + } + info.TestVersionsInfo[i].PublishDate = publishDate + } + }) + } + + }) + + rows = append(rows, info) + + }) + + }) + + return + +} + +func parseProjectTable(s *goquery.Selection) (rows []*ProjectVersionInfo) { + + s.Find("tr").Each(func(i int, releaseRow *goquery.Selection) { + + if releaseRow.Parent().Is("thead") { + return + } + + releaseInfo := &ProjectVersionInfo{} + versionColumn := releaseRow.Find(".versionColumn a") + releaseInfo.Name = strings.TrimSpace(versionColumn.Text()) + releaseInfo.Url = versionColumn.AttrOr("href", "") + + textDate := strings.TrimSpace(releaseRow.Find(".dateColumn").Text()) + var err error + if releaseInfo.PublishDate, err = parseReleaseDate(textDate); err != nil { + log.Errorf("Error parse <%s> for %s <%s>", textDate, releaseInfo.Name, releaseInfo.Url) + } + + rows = append(rows, releaseInfo) + + }) + + return + +} + +func parseReleaseDate(textDate string) (time.Time, error) { + + date, err := time.Parse("02.01.06", textDate) + return date, err + +} diff --git a/downloader/html_test.go b/downloader/html_test.go new file mode 100644 index 0000000..7f46e1a --- /dev/null +++ b/downloader/html_test.go @@ -0,0 +1,4212 @@ +package downloader + +import ( + "github.com/stretchr/testify/assert" + "strings" + "testing" +) + +func TestHtmlParser_ParseTotalReleases(t *testing.T) { + body := strings.NewReader(releasesTotal) + parser, err := NewHtmlParser() + + if err != nil { + t.Fatal(err) + } + + releases, err := parser.ParseTotalReleases(body) + assert.NoError(t, err) + assert.Lenf(t, releases, 69, "releases count must be equal") + +} + +func TestHtmlParser_ParseProjectReleases(t *testing.T) { + body := strings.NewReader(projectReleases) + parser, err := NewHtmlParser() + + if err != nil { + t.Fatal(err) + } + + releases, err := parser.ParseProjectReleases(body) + if err != nil { + return + } + + assert.NoError(t, err) + assert.Lenf(t, releases, 165, "releases count must be equal") + +} + +func TestHtmlParser_ParseProjectRelease(t *testing.T) { + body := strings.NewReader(projectReleaseHtml) + parser, err := NewHtmlParser() + + if err != nil { + t.Fatal(err) + } + + releaseFiles, err := parser.ParseProjectRelease(body) + if err != nil { + return + } + + assert.NoError(t, err) + assert.Lenf(t, releaseFiles, 24, "release files count must be equal") + +} + +func TestHtmlParser_ParseReleaseFiles(t *testing.T) { + body := strings.NewReader(releaseFiles) + parser, err := NewHtmlParser() + + if err != nil { + t.Fatal(err) + } + + fileUrls, err := parser.ParseReleaseFiles(body) + if err != nil { + return + } + + assert.NoError(t, err) + assert.Lenf(t, fileUrls, 3, "file urls count must be equal") + +} + +var releaseFiles = ` + + + + + + + + + + + + +
+ +
+ +
+` +var projectReleaseHtml = ` +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+` +var projectReleases = ` +
+
+ Технологическая платформа 8.3 +
+
+

+

Сервис публикации ошибок

+

Доступ к публикуемым ошибкам технологической платформы "1С:Предприятие 8": каталог, поиск, сравнение версий, email-подписка.

+
+
+ Обращаем ваше внимание, что с мая 2020 года фирма "1С" перешла на бесконтактную форму сопровождения, в связи с чем был прекращен выпуск дисков ИТС.
Подробнее см. информационное письмо № 27079 от 17.04.2020 https://1c.ru/news/info.jsp?id=27079. +
+
+
+ Обновления +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Номер версии + + Дата выхода + + Диск 1С:ИТС + + Обновление версии +
+ + 8.3.18.1363 + + + 16.03.21 + + +
+ + 8.3.18.1334 + + + 15.02.21 + + +
+ + 8.3.17.1989 + + + 14.02.21 + + +
+ + 8.3.18.1289 + + + 12.01.21 + + +
+ + 8.3.16.1876 + + + 11.01.21 + + +
+ + 8.3.18.1208 + + + 19.11.20 + + +
+ + 8.3.17.1851 + + + 19.11.20 + + +
+ + 8.3.16.1814 + + + 19.11.20 + + +
+ + 8.3.18.1201 + + + 16.11.20 + + +
+ + 8.3.16.1810 + + + 15.11.20 + + +
+ + 8.3.17.1846 + + + 15.11.20 + + +
+ + 8.3.17.1823 + + + 09.11.20 + + +
+ + 8.3.16.1791 + + + 09.11.20 + + +
+ + 8.3.15.2107 + + + 09.11.20 + + +
+ + 8.3.18.1128 + + + 12.10.20 + + +
+ + 8.3.17.1549 + + + 07.07.20 + + +
+ + 8.3.16.1659 + + + 06.07.20 + + +
+ + 8.3.17.1496 + + + 04.06.20 + + +
+ + 8.3.15.1985 + + + 03.06.20 + + +
+ + 8.3.16.1502 + + + 03.06.20 + + +
+ + 8.3.17.1386 + + + 23.04.20 + + +
+ + 8.3.15.1958 + + + 22.04.20 + + +
+ + 8.3.14.2095 + + + 22.04.20 + + +
+ + 8.3.16.1359 + + + 22.04.20 + + +
+ + 8.3.16.1296 + + + 31.03.20 + + +
+ + 8.3.16.1224 + + + 26.02.20 + + +
+ + 8.3.16.1148 + + + 20.01.20 + + +
+ + 8.3.15.1869 + + + 19.01.20 + + +
+ + 8.3.16.1063 + + + 20.11.19 + + +
+ + 8.3.15.1830 + + + 19.11.19 + + +
+ + 8.3.15.1778 + + + 19.11.19 + + +
+ + 8.3.14.1993 + + + 19.11.19 + + +
+ + 8.3.16.1030 + + + 07.11.19 + + +
+ + 8.3.15.1747 + + + 05.11.19 + + +
+ + 8.3.14.1976 + + + 04.11.19 + + +
+ + 8.3.15.1700 + + + 10.10.19 + + +
+ + 8.3.15.1656 + + + 17.09.19 + + +
+ + 8.3.14.1944 + + + 16.09.19 + + +
+ + 8.3.15.1565 + + + 14.08.19 + + +
+ + 8.3.13.1926 + + + 13.08.19 + + +
+ + 8.3.15.1534 + + + 24.07.19 + + +
+ + 8.3.15.1489 + + + 26.06.19 + + +
+ + 8.3.14.1854 + + + 25.06.19 + + +
+ + 8.3.12.1924 + + + 25.06.19 + + +
+ + 8.3.14.1779 + + + 22.05.19 + + +
+ + 8.3.14.1694 + + + 17.04.19 + + +
+ + 8.3.13.1865 + + + 16.04.19 + + +
+ + 8.3.14.1630 + + + 06.03.19 + + +
+ + 8.3.13.1809 + + + 06.03.19 + + +
+ + 8.3.12.1855 + + + 04.03.19 + + +
+ + 8.3.14.1565 + + + 31.01.19 + + +
+ + 8.3.13.1690 + + + 14.01.19 + + +
+ + 8.3.13.1644 + + + 28.11.18 + + +
+ + 8.3.12.1790 + + + 27.11.18 + + +
+ + 8.3.13.1513 + + + 25.09.18 + + +
+ + 8.3.12.1714 + + + 24.09.18 + + +
+ + 8.3.12.1685 + + + 24.09.18 + + +
+ + 8.3.12.1616 + + + 12.09.18 + + +
+ + 8.3.12.1595 + + + 14.08.18 + + +
+ + 8.3.12.1567 + + + 31.07.18 + + +
+ + 8.3.12.1529 + + + 27.06.18 + + +
+ + 8.3.12.1469 + + + 04.06.18 + + +
+ + 8.3.12.1440 + + + 14.05.18 + + +
+ + 8.3.11.3133 + + + 13.05.18 + + +
+ + 8.3.10.2772 + + + 13.05.18 + + +
+ + 8.3.12.1412 + + + 10.04.18 + + +
+ + 8.3.11.3034 + + + 09.02.18 + + +
+ + 8.3.10.2753 + + + 08.02.18 + + +
+ + 8.3.11.2954 + + + 23.01.18 + + +
+ + 8.3.11.2924 + + + 09.01.18 + + +
+ + 8.3.11.2899 + + + 12.12.17 + + +
+ + 8.3.10.2699 + + + 12.12.17 + + +
+ + 8.3.11.2867 + + + 21.11.17 + + +
+ + 8.3.10.2667 + + + 14.11.17 + + +
+ + 8.3.10.2650 + + + 31.10.17 + + +
+ + 8.3.10.2639 + + + 20.10.17 + + +
+ + 8.3.10.2580 + + + 22.09.17 + + +
+ + 8.3.10.2561 + + + 11.08.17 + + +
+ + 8.3.9.2309 + + + 11.08.17 + + +
+ + 8.3.8.2442 + + + 11.08.17 + + +
+ + 8.3.10.2505 + + + 21.07.17 + + +
+ + 8.3.10.2466 + + + 05.07.17 + + +
+ + 8.3.10.2375 + + + 30.06.17 + + +
+ + 8.3.10.2299 + + + 05.06.17 + + +
+ + 8.3.10.2252 + + + 27.04.17 + + +
+ + 8.3.9.2233 + + + 05.04.17 + + +
+ + 8.3.9.2170 + + + 03.02.17 + + +
+ + 8.3.9.2033 + + + 19.12.16 + + +
+ + 8.3.8.2322 + + + 18.12.16 + + +
+ + 8.3.9.1850 + + + 02.11.16 + + +
+ + 8.3.8.2197 + + + 02.11.16 + + +
+ + 8.3.9.1818 + + + 30.09.16 + + +
+ + 8.3.8.2167 + + + 28.09.16 + + +
+ + 8.3.8.2137 + + + 28.09.16 + + +
+ + 8.3.8.2088 + + + 16.09.16 + + +
+ + 8.3.8.2054 + + + 31.08.16 + + +
+ + 8.3.8.2027 + + + 17.08.16 + + +
+ + 8.3.8.1964 + + + 03.08.16 + + +
+ + 8.3.8.1933 + + + 22.07.16 + + +
+ + 8.3.8.1861 + + + 14.07.16 + + +
+ + 8.3.8.1784 + + + 22.06.16 + + +
+ + 8.3.8.1747 + + + 22.06.16 + + +
+ + 8.3.8.1675 + + + 20.05.16 + + +
+ + 8.3.8.1652 + + + 20.04.16 + + +
+ + 8.3.7.2027 + + + 14.04.16 + + +
+ + 8.3.6.2530 + + + 14.04.16 + + +
+ + 8.3.7.2008 + + + 25.03.16 + + +
+ + 8.3.6.2524 + + + 25.03.16 + + +
+ + 8.3.7.1993 + + + 23.03.16 + + +
+ + 8.3.7.1970 + + + 18.03.16 + + +
+ + 8.3.7.1949 + + + 04.03.16 + + +
+ + 8.3.7.1917 + + + 10.02.16 + + +
+ + 8.3.7.1901 + + + 04.02.16 + + +
+ + 8.3.7.1873 + + + 28.01.16 + + +
+ + 8.3.7.1860 + + + 19.01.16 + + +
+ + 8.3.7.1845 + + + 30.12.15 + + +
+ + 8.3.7.1831 + + + 25.12.15 + + +
+ + 8.3.7.1805 + + + 21.12.15 + + +
+ + 8.3.7.1790 + + + 10.12.15 + + +
+ + 8.3.6.2449 + + + 09.12.15 + + +
+ + 8.3.7.1776 + + + 30.11.15 + + +
+ + 8.3.6.2421 + + + 30.11.15 + + +
+ + 8.3.7.1759 + + + 19.11.15 + + +
+ + 8.3.6.2390 + + + 03.11.15 + + +
+ + 8.3.6.2363 + + + 22.10.15 + + +
+ + 8.3.6.2332 + + + 02.10.15 + + +
+ + 8.3.6.2299 + + + 11.09.15 + + +
+ + 8.3.6.2237 + + + 25.08.15 + + +
+ + 8.3.6.2152 + + + 23.07.15 + + +
+ + 8.3.5.1625 + + + 23.07.15 + + +
+ + 8.3.6.2100 + + + 08.07.15 + + +
+ + 8.3.5.1596 + + + 07.07.15 + + +
+ + 8.3.6.2076 + + + 26.06.15 + + +
+ + 8.3.6.2041 + + + 03.06.15 + + +
+ + 8.3.6.2014 + + + 21.05.15 + + +
+ + 8.3.6.1999 + + + 15.05.15 + + +
+ + 8.3.5.1570 + + + 15.05.15 + + +
+ + 8.3.6.1977 + + + 29.04.15 + + +
+ + 8.3.5.1517 + + + 23.03.15 + + +
+ + 8.3.5.1486 + + + 13.03.15 + + +
+ + 8.3.5.1482 + + + 05.03.15 + + +
+ + 8.3.5.1460 + + + 13.02.15 + + +
+ + 8.3.5.1443 + + + 30.01.15 + + +
+ + 8.3.5.1428 + + + 28.01.15 + + +
+ + 8.3.5.1383 + + + 12.12.14 + + +
+ + 8.3.5.1248 + + + 31.10.14 + + +
+ + 8.3.5.1231 + + + 21.10.14 + + +
+ + 8.3.5.1186 + + + 03.10.14 + + +
+ + 8.3.5.1146 + + + 23.09.14 + + +
+ + 8.3.5.1119 + + + 08.08.14 + + Сентябрь 2014 + +
+ + 8.3.5.1098 + + + 01.08.14 + + +
+ + 8.3.5.1088 + + + 25.07.14 + + +
+ + 8.3.5.1068 + + + 10.07.14 + + Август 2014 + +
+ + 8.3.4.496 + + + 17.06.14 + + Июль 2014 + +
+ + 8.3.4.482 + + + 29.04.14 + + Июнь 2014 + +
+ + 8.3.4.465 + + + 04.04.14 + + Май 2014 + +
+ + 8.3.4.437 + + + 28.02.14 + + Апрель 2014 + +
+ + 8.3.4.408 + + + 31.01.14 + + Март 2014 + +
+ + 8.3.4.389 + + + 30.12.13 + + Февраль 2014 + +
+ + 8.3.4.365 + + + 04.12.13 + + Январь 2014 + +
+ + 8.3.3.721 + + + 06.09.13 + + октябрь 2013 + +
+ + 8.3.3.715 + + + 16.08.13 + + сентябрь 2013 + +
+ + 8.3.3.687 + + + 18.07.13 + + +
+ + 8.3.3.658 + + + 21.06.13 + + август 2013 + +
+ + 8.3.3.641 + + + 29.05.13 + + июль 2013 + +
+
+ +
+
+ Версии для тестирования +
+

+ 29.01.21 опубликована версия 8.3.19.900, предназначена для тестирования +

+

+ Предварительные тестовые релизы конфигураций предоставляются партнерам фирмы "1С" и пользователям системы программ 1С:Предприятие для тестирования, предварительного ознакомления с новыми возможностями конфигураций, исправлениями ошибок, для апробации работы новых релизов на реальных данных.
Использование предварительного релиза для автоматизации реальных задач предприятия может выполняться только в отдельных случаях по решению пользователя, совместно с партнером, поддерживающим внедрение +

+
+
+` +var releasesTotal = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
НазваниеАктуальная версия + Планируемая версия + +
+ Указанные сроки являются рабочим планом выпуска (ориентировочным) и могут быть изменены. Не следует указывать приведенные сроки в договорах и других юридически значимых документах. Также не следует использовать данные сроки в каких-либо других формах обязательств (устных, письменных и др.). +
+
Версия для ознакомления
Номер версииДата выходаНомер версииОриентировочная дата выходаДата обновления плановых данныхНомер версииДата публикации
+ + Технологические дистрибутивы +
+ 1C:Enterprise Development Tools + + 2020.6.2 + + + 10.02.21 + + Не определена + 2021.1 RC2 +
+ 2021.1 RC1 +
+
+ + 09.03.21 +
+ + 26.02.21 +
+
+ 1C:Исполнитель. Бета-версия + Не определена + 2020.2.4.6 +
+ 2020.2.3.7 +
+ 2020.2.2.31 +
+ 2020.2.1.16 +
+ 2020.2.0.547 +
+
+ + 17.02.21 +
+ + 27.11.20 +
+ + 26.10.20 +
+ + 10.08.20 +
+ + 19.06.20 +
+
+ 1С:Конвертация данных 2.0 + + 2.1.8.2 + + + 11.06.14 + + Не определенаНе определена
+ 1С:Конвертация данных 3 + + 3.0.5.3 + + + 27.04.17 + + Не определена + 3.1.1.3 +
+
+ + 31.12.20 +
+
+ 1С:Переводчик, редакция 2.1 + + 2.1.15.1 + + + 28.04.17 + + Не определенаНе определена
+ 1С:Печать штрихкодов (ActiveX) + + 8.0.16.4 + + + 31.03.14 + + Не определенаНе определена
+ 1С:Сервер взаимодействия + + 9.0.33 + + + 09.02.21 + + Не определена + 10.0.37 +
+
+ + 09.02.21 +
+
+ 1С:Сканер штрихкода (COM) + + 8.1.7.9 + + + 12.04.16 + + Не определенаНе определена
+ 1С:Сценарное тестирование 8 + + 3.0.24.2 + + + 19.02.21 + + Не определенаНе определена
+ 1С:Тестировщик + + 1.0.1.2 + + + 12.02.21 + + Не определенаНе определена
+ IBM DB2 Express-C + + v9.7 FP6 + + + 05.10.12 + + Не определенаНе определена
+ PostgreSQL + + 12.5-6.1C + + + 17.02.21 + + Не определена + 12.6-1.1C +
+ 11.11-1.1C +
+ 10.16-1.1C +
+
+ + 15.03.21 +
+ + 15.03.21 +
+ + 15.03.21 +
+
+ Автоматизированная проверка конфигураций + + 1.2.6.20 + + + 29.12.20 + + Не определенаНе определена
+ Драйвер аппаратных лицензий платформы 1С:Предприятия (Sentinel HASP) + + 7.63 + + + 14.05.18 + + Не определенаНе определена
+ Корпоративный инструментальный пакет 8 + + 2.1.7.11 + + + 05.11.20 + + Не определена + 2.1.8.2 +
+
+ + 27.01.21 +
+
+ Менеджер лицензий аппаратной защиты NetHASP + + 8.31 + + + 01.01.07 + + Не определенаНе определена
+ Мобильная платформа 1С:Предприятия + + 8.3.18.60 + + + 19.02.21 + + Не определенаНе определена
+ Обработки обслуживания торгового оборудования - для Технологической платформы 8.2 + Не определенаНе определена
+ Технологическая платформа 8.0 + + 8.0.18.2 + + + 19.12.06 + + Не определенаНе определена
+ Технологическая платформа 8.1 + + 8.1.15.14 + + + 30.10.09 + + Не определенаНе определена
+ Технологическая платформа 8.2 + + 8.2.19.130 + + + 13.02.15 + + Не определенаНе определена
+ Технологическая платформа 8.3 + + 8.3.18.1334 + + + 15.02.21 + + Не определена + 8.3.18.1363 +
+ 8.3.19.900 +
+
+ + 06.03.21 +
+ + 29.01.21 +
+
+ Утилита администрирования конфигураций и информационных баз 1С:Предприятия 8 + Не определена + 8.2.13.219 +
+
+ + 06.04.11 +
+
+ Утилита лицензирования 1С:Предприятия (1C:Enterprise License Tools) + + 0.15.0.2 + + + 16.11.20 + + Не определенаНе определена
+ Фабрика отчетов + + 1.0.1.1 + + + 26.04.12 + + Не определенаНе определена
+ + Стандартные библиотеки +
+ 1С:Библиотека интеграции с 1С:Документооборотом + + 1.1.17.2 + + + 26.08.20 + + Не определенаНе определена
+ 1С:Библиотека интеграции с МДЛП + + 1.2.3.5 + + + 17.02.21 + + Не определенаНе определена
+ 1С:Библиотека интернет-поддержки пользователей, редакция 2.1 + + 2.1.9.18 + + + 08.02.18 + + Не определенаНе определена
+ 1С:Библиотека интернет-поддержки пользователей, редакция 2.2 + + 2.2.3.19 + + + 29.01.19 + + Не определенаНе определена
+ 1С:Библиотека интернет-поддержки пользователей, редакция 2.3 + + 2.3.4.18 + + + 19.06.20 + + Не определенаНе определена
+ 1С:Библиотека интернет-поддержки пользователей, редакция 2.4 + + 2.4.2.63 + + + 15.02.21 + + Не определенаНе определена
+ 1С:Библиотека интернет-поддержки пользователей, редакция 2.5 + + 2.5.1.31 + + + 12.03.21 + + Не определенаНе определена
+ 1С:Библиотека подключаемого оборудования для мобильных приложений + + 2.13.7.0 + + + 11.02.21 + + Не определенаНе определена
+ 1С:Библиотека подключаемого оборудования, редакция 2.1 + + 2.1.4.14 + + + 20.01.21 + + Не определенаНе определена
+ 1С:Библиотека подключаемого оборудования, редакция 3.0 + + 3.0.2 +
+
3.0.1 +
+
+
+ + 02.09.19 +
+ + 11.03.19 +
+
+ + 28.06.19 +
+ + 20.11.18 +
+
Не определена
+ 1С:Библиотека стандартных подсистем 8.2 + + 2.0.1.19 + + + 26.07.12 + + Не определенаНе определена
+ 1С:Библиотека стандартных подсистем 8.2, редакция 2.1 + + 2.1.9.2 + + + 06.08.14 + + Не определенаНе определена
+ 1С:Библиотека стандартных подсистем, редакция 2.2 + + 2.2.5.36 + + + 14.07.15 + + Не определенаНе определена
+ 1С:Библиотека стандартных подсистем, редакция 2.3 + + 2.3.7.10 + + + 02.04.18 + + Не определенаНе определена
+ 1С:Библиотека стандартных подсистем, редакция 2.4 + + 2.4.6.241 + + + 21.12.18 + + Не определенаНе определена
+ 1С:Библиотека стандартных подсистем, редакция 3.0 + + 3.0.3.341 + + + 04.07.20 + + Не определенаНе определена
+ 1С:Библиотека стандартных подсистем, редакция 3.1 + + 3.1.4.174 + + + 05.03.21 + + Не определенаНе определена
+ 1С:Библиотека технологии сервиса, редакция 1.1 + + 1.1.4.5 + + + 21.10.19 + + Не определенаНе определена
+ 1С:Библиотека технологии сервиса, редакция 1.2 + + 1.2.2.35 + + + 30.07.20 + + Не определенаНе определена
+ 1С:Библиотека технологии сервиса, редакция 2.0 + + 2.0.4.39 + + + 10.03.21 + + Не определенаНе определена
+ 1С:Библиотека электронных документов, редакция 1.1 + + 1.1.28.35 + + + 12.03.21 + + Не определенаНе определена
+ 1С:Библиотека электронных документов, редакция 1.2 + + 1.2.7.11 + + + 07.08.15 + + Не определенаНе определена
+ 1С:Библиотека электронных документов, редакция 1.3 + + 1.3.11.125 + + + 15.02.19 + + Не определенаНе определена
+ 1С:Библиотека электронных документов, редакция 1.4 + + 1.4.1.75 + + + 05.02.19 + + Не определенаНе определена
+ 1С:Библиотека электронных документов, редакция 1.5 + + 1.5.1.93 + + + 09.08.19 + + Не определенаНе определена
+ 1С:Библиотека электронных документов, редакция 1.6 + + 1.6.4.117 + + + 24.07.20 + + Не определенаНе определена
+ 1С:Библиотека электронных документов, редакция 1.7 + + 1.7.2.109 + + + 11.03.21 + + Не определенаНе определена
+ 1С:Библиотека электронных документов, редакция 1.8 + + 1.8.1.50 + + + 11.03.21 + + Не определенаНе определена
+ 1С:Библиотека электронных документов, редакция 1.9 + + 1.9.1.26 + + + 11.03.21 + + Не определенаНе определена
+ + Типовые конфигурации фирмы "1С" для России +
+ 1С:275ФЗ + + 1.0.2.12 + + + 19.05.16 + + Не определенаНе определена
+ 1С:Кладовщик. Версия для разработчиков + + 1.0.18.1 + + + 29.04.20 + + Не определенаНе определена
+ 1С:Кладовщик для ИТС + + 1.0.18 + + + 29.04.20 + + Не определенаНе определена
+ 1С:Клиент ЭДО 8, редакция 1.0 + + 1.0.3.8 + + + 29.05.15 + + Не определенаНе определена
+ 1С:Мобильная касса (для разработчиков) + + 3.8.15.0 + + + 27.01.21 + + Не определена + 3.1.5.0 +
+ 3.0.22.0 +
+
+ + 25.12.19 +
+ + 26.09.19 +
+
+ 1С:Управление холдингом 1.3 + + 1.3.12.1 + + + 08.10.18 + + Не определенаНе определена
+ 1С:Управление холдингом 3.0 + + 3.0.18.2 + + + 09.02.21 + + Не определенаНе определена
+ 1С:Управление холдингом 3.1 + + 3.1.7.2 + + + 11.02.21 + + Не определенаНе определена
+ Бухгалтерия предприятия КОРП, редакция 2.0 + + 2.0.66.135 + + + 12.03.21 + + Не определенаНе определена
+ Бухгалтерия предприятия КОРП, редакция 3.0 + + 3.0.89.51 + + + 12.03.21 + + + 3.0.91 +
+
3.0.90 +
+
+
+ + Апрель 2021 +
+ + Март 2021 +
+
+ + 22.03.21 +
+ + 22.03.21 +
+
Не определена
+ Бухгалтерия предприятия, редакция 2.0 + + 2.0.66.135 + + + 12.03.21 + + Не определенаНе определена
+ Бухгалтерия предприятия, редакция 3.0 + + 3.0.89.51 + + + 12.03.21 + + + 3.0.91 +
+
3.0.90 +
+
+
+ + Апрель 2021 +
+ + Март 2021 +
+
+ + 22.03.21 +
+ + 22.02.21 +
+
Не определена
+ Зарплата и управление персоналом КОРП, редакция 2.5 + + 2.5.159.4 + + + 05.03.21 + + + 2.5. +
+
+
+ + 1 кв. 2021 +
+
+ + 05.03.21 +
+
Не определена
+ Зарплата и управление персоналом КОРП, редакция 3 + + 3.1.14.436 + + + 04.03.21 + + Не определенаНе определена
+ + Отраслевые решения +
+ 1С:СЛК + + 3.0.24.9152 + + + 27.01.21 + + Не определенаНе определена
+` diff --git a/downloader/oneDownloader.go b/downloader/oneDownloader.go new file mode 100644 index 0000000..fca9781 --- /dev/null +++ b/downloader/oneDownloader.go @@ -0,0 +1,387 @@ +package downloader + +import ( + "fmt" + "go.uber.org/multierr" + "io" + "net/http" + "net/http/cookiejar" + "net/url" + "os" + "path/filepath" + "strings" + "sync" +) + +type GetConfig struct { + BasePath string + Project string + Version VersionFilter + Filters []FileFilter +} + +type OnegetDownloader struct { + Login, Password string + + cookie *cookiejar.Jar + + client *Client + + urlCh chan *FileToDownload + wg *sync.WaitGroup + + parser *HtmlParser +} + +func NewDownloader(login, password string) *OnegetDownloader { + + cj, _ := cookiejar.New(nil) + parser, _ := NewHtmlParser() + + return &OnegetDownloader{ + cookie: cj, + Login: login, + Password: password, + wg: &sync.WaitGroup{}, + parser: parser, + } + +} + +func (dr *OnegetDownloader) Get(config ...GetConfig) ([]string, error) { + + client, err := NewClient(loginURL, releasesURL, dr.Login, dr.Password) + if err != nil { + return nil, err + } + + dr.client = client + + files := make([]string, 0) + + downloadCh := make(chan *FileToDownload, 100) + + for _, getConfig := range config { + err := dr.getFiles(getConfig, downloadCh) + if err != nil { + return nil, err + } + } + + go func() { + dr.wg.Wait() + close(downloadCh) + }() + + limit := make(chan struct{}, 10) + mu := &sync.Mutex{} + for fileToDownload := range downloadCh { + go func(file *FileToDownload) { + + limit <- struct{}{} + + filename, err := dr.downloadFile(file) + if err != nil { + dr.wg.Done() + log.Errorf(err.Error()) + } + if len(filename) > 0 { + mu.Lock() + files = append(files, filename) + mu.Unlock() + } + dr.wg.Done() + + <-limit + + }(fileToDownload) + + } + + downloadCh = nil + + return files, nil + +} + +func (dr *OnegetDownloader) getFiles(config GetConfig, downloadCh chan *FileToDownload) error { + + releases, err := dr.getProjectReleases(config) + if err != nil { + return err + } + + for _, release := range releases { + dr.wg.Add(1) + go func(info *ProjectVersionInfo, cfg GetConfig) { + _ = dr.getReleaseFiles(info, config, downloadCh) + dr.wg.Done() + }(release, config) + } + + return nil +} + +func (dr *OnegetDownloader) getReleaseFiles(release *ProjectVersionInfo, config GetConfig, downloadCh chan *FileToDownload) error { + + client := dr.client + resp, err := client.Get(releasesURL + release.Url) + + if err != nil { + return err + } + + defer resp.Body.Close() + + releaseFiles, err := dr.parser.ParseProjectRelease(resp.Body) + if err != nil { + return err + } + files := filterReleaseFiles(releaseFiles, config.Filters) + + var merr error + + for _, file := range files { + + err := dr.addFileToChannel(file.url, config, downloadCh) + if err != nil { + log.Errorf("Error get file from <%s>: %s", err.Error()) + multierr.Append(merr, err) + } + + } + + return merr + +} + +func (dr *OnegetDownloader) getProjectReleases(config GetConfig) ([]*ProjectVersionInfo, error) { + + resp, err := dr.client.Get(projectHrefPrefix + config.Project) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + releases, err := dr.parser.ParseProjectReleases(resp.Body) + if err != nil { + return nil, fmt.Errorf("error parse project <%s> releases: %s, html: <%s>", + config.Project, err.Error(), readBodyMustString(resp.Body)) + } + + return filterProjectVersionInfo(releases, config.Version), nil + +} +func filterReleaseFiles(list []ReleaseFileInfo, filters []FileFilter) (filteredList []ReleaseFileInfo) { + + if len(filters) == 0 || len(list) == 0 { + return list + } + + matchInfo := func(i ReleaseFileInfo) bool { + + for _, filter := range filters { + + matchName := filter.MatchString(i.name) + matchUrl := filter.MatchString(i.url) + + if matchName || matchUrl { + return true + } + + } + + return false + } + + for _, info := range list { + + if matchInfo(info) { + filteredList = append(filteredList, info) + } + + } + + return + +} + +func filterProjectVersionInfo(list []*ProjectVersionInfo, filter VersionFilter) (filteredList []*ProjectVersionInfo) { + + if len(list) == 0 { + return list + } + + return filter.Filter(list) + +} + +func (dr *OnegetDownloader) getClient() *http.Client { + return &http.Client{ + Jar: dr.cookie, + Transport: nil, + } +} + +func (dr *OnegetDownloader) addFileToChannel(href string, config GetConfig, downloadCh chan *FileToDownload) (err error) { + + downloadHref := []string{releasesURL + href} + + if !directLink(href) { + downloadHref, err = dr.getDownloadFileLinks(href, config) + if err != nil { + return err + } + } + + fileName, filePath, err := dr.fileNameFromUrl(href) + if err != nil { + return err + } + + if len(downloadHref) == 0 { + return nil + } + + dr.wg.Add(1) + + log.Debugf("Add to download: %s", href) + downloadCh <- &FileToDownload{ + url: downloadHref, + path: filePath, + name: fileName, + basePath: config.BasePath, + } + + return nil +} + +func directLink(href string) bool { + return strings.HasSuffix(href, "txt") || + strings.HasSuffix(href, "pdf") || + strings.HasSuffix(href, "html") || + strings.HasSuffix(href, "htm") || + (strings.HasSuffix(href, "zip") && + strings.Contains(href, "path=ro\\")) +} + +func (dr *OnegetDownloader) fileNameFromUrl(rawUrl string) (string, string, error) { + + fileName := strings.Builder{} + filePath := strings.Builder{} + + parsedUrl, err := url.Parse(rawUrl) + if err != nil { + return "", "", err + } + + query, err := url.ParseQuery(parsedUrl.RawQuery) + if err != nil { + return "", "", err + } + + path := strings.Split(query.Get("path"), "\\") + fileName.WriteString(path[len(path)-1]) + + nick := query.Get("nick") + ver := query.Get("ver") + + filePath.WriteString(nick) + filePath.WriteRune(os.PathSeparator) + filePath.WriteString(ver) + filePath.WriteRune(os.PathSeparator) + + return fileName.String(), filePath.String(), nil +} + +func (dr *OnegetDownloader) downloadFile(fileToDownload *FileToDownload) (string, error) { + + workDir := filepath.Join(fileToDownload.basePath, strings.ToLower(fileToDownload.path)) + fileName := filepath.Join(workDir, fileToDownload.name) + _, err := os.Stat(fileName) + if os.IsExist(err) { + return fileName, nil + } + if os.IsNotExist(err) { + + if _, err := os.Stat(workDir); os.IsNotExist(err) { + err = os.MkdirAll(filepath.Join(workDir), 0777) + if err != nil { + return "", err + } + // https://wenzr.wordpress.com/2018/03/27/go-file-permissions-on-unix/ + _ = os.Chmod(workDir, 0777) + } + + log.Infof("Getting a file: %s", fileToDownload.name) + + downloadUrl := fileToDownload.url[0] + + log.Debugf("Getting a file from url: %s", downloadUrl) + client := dr.client + resp, err := client.Get(downloadUrl) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if err := SaveToFile(resp.Body, fileName+tempFileSuffix); err != nil { + log.Debugf("File <%s> saved err: %s", fileName, err.Error()) + return "", err + } + + log.Debugf("File saved to: %s", fileName) + + err = os.Rename(fileName+tempFileSuffix, fileName) + if err != nil { + return "", err + } + + return fileName, nil + + } + + return "", nil + +} + +func SaveToFile(reader io.ReadCloser, fileName string) error { + fd, err := os.Create(fileName) + if err != nil { + return err + } + + defer reader.Close() + defer fd.Close() + + if _, err = io.Copy(fd, reader); err != nil && err != io.EOF { + return err + } + + return nil +} + +func (dr *OnegetDownloader) handleError(err error) { + if err == nil { + return + } + log.Error(err.Error()) +} + +func (dr *OnegetDownloader) getDownloadFileLinks(href string, _ GetConfig) ([]string, error) { + + client := dr.client + resp, err := client.Get(href) + if err != nil { + return []string{}, err + } + defer resp.Body.Close() + + fileLinks, err := dr.parser.ParseReleaseFiles(resp.Body) + if err != nil { + return nil, err + } + + return fileLinks, nil + +} diff --git a/downloader/oneDownloader_test.go b/downloader/oneDownloader_test.go new file mode 100644 index 0000000..a28a011 --- /dev/null +++ b/downloader/oneDownloader_test.go @@ -0,0 +1,123 @@ +package downloader + +import ( + "reflect" + "regexp" + "testing" +) + +func Test_filterReleaseFiles(t *testing.T) { + type args struct { + list []ReleaseFileInfo + filters []FileFilter + } + tests := []struct { + name string + args args + wantFilteredList int + }{ + { + "Platform83Project with addin_8_3_18_1363", + args{ + list: []ReleaseFileInfo{ + { + "Тонкий клиент 1С:Предприятия для DEB-based Linux-систем", + "/version_file?nick=Platform83&ver=8.3.18.1363&path=Platform%5C8_3_18_1363%5Cthin.client_8_3_18_1363.deb32.tar.gz", + }, + { + "Тонкий клиент 1С:Предприятия для RPM-based Linux-систем", + "/version_file?nick=Platform83&ver=8.3.18.1363&path=Platform%5C8_3_18_1363%5Cthin.client_8_3_18_1363.rpm32.tar.gz", + }, { + "Технология внешних компонент", + "/version_file?nick=Platform83&ver=8.3.18.1363&path=Platform%5C8_3_18_1363%5Caddin_8_3_18_1363.zip", + }, + }, + filters: []FileFilter{ + regexp.MustCompile(".*deb32.tar.gz"), + regexp.MustCompile("Технология внешних компонент"), + NewFileFilterMust(Platform83Project, "thin.deb"), + }, + }, + 2, + }, { + "DevelopmentTools10 with Bellsoft JDK", + args{ + list: []ReleaseFileInfo{ + { + "Дистрибутив 1C:EDT для ОС Windows 64 бит", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6_2%5C1c_edt_distr_2020.6.2_8_windows_x86_64.zip", + }, + { + "Дистрибутив для оффлайн установки 1C:EDT для ОС Linux 64 бит", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6_2%5C1c_edt_distr_offline_2020.6.2_8_linux_x86_64.tar.gz", + }, { + "Bellsoft JDK Full (64-bit) для Windows", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6%5Cbellsoft_jdk11.0.9_12_windows_amd64_full.msi", + }, { + "Дистрибутив для оффлайн установки 1C:EDT для ОС Windows 64 бит", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6_2%5C1c_edt_distr_offline_2020.6.2_8_windows_x86_64.zip", + }, + }, + filters: []FileFilter{ + NewFileFilterMust("DevelopmentTools10", "deb"), + }, + }, + 1, + }, { + "DevelopmentTools10 with Bellsoft JDK", + args{ + list: []ReleaseFileInfo{ + { + "Дистрибутив для оффлайн установки 1C:EDT для ОС Linux 64 бит", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6_2%5C1c_edt_distr_offline_2020.6.2_8_linux_x86_64.tar.gz", + }, + { + "Дистрибутив 1C:EDT для ОС Linux 64 бит", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6_2%5C1c_edt_distr_2020.6.2_8_linux_x86_64.tar.gz", + }, { + "Bellsoft JDK Full (64-bit) для DEB-based Linux-систем", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6%5Cbellsoft_jdk11.0.9_12_linux_amd64_full.deb", + }, { + "Bellsoft JDK Full (64-bit) для RPM-based Linux-систем", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6%5Cbellsoft_jdk11.0.9_12_linux_amd64_full.rpm", + }, + }, + filters: []FileFilter{ + NewFileFilterMust("DevelopmentTools10", "deb"), + }, + }, + 2, + }, { + "DevelopmentTools10 only Bellsoft JDK", + args{ + list: []ReleaseFileInfo{ + { + "Дистрибутив для оффлайн установки 1C:EDT для ОС Linux 64 бит", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6_2%5C1c_edt_distr_offline_2020.6.2_8_linux_x86_64.tar.gz", + }, + { + "Дистрибутив 1C:EDT для ОС Linux 64 бит", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6_2%5C1c_edt_distr_2020.6.2_8_linux_x86_64.tar.gz", + }, { + "Bellsoft JDK Full (64-bit) для DEB-based Linux-систем", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6%5Cbellsoft_jdk11.0.9_12_linux_amd64_full.deb", + }, { + "Bellsoft JDK Full (64-bit) для RPM-based Linux-систем", + "/version_file?nick=DevelopmentTools10&ver=2020.6.2&path=DevelopmentTools%5C2020_6%5Cbellsoft_jdk11.0.9_12_linux_amd64_full.rpm", + }, + }, + filters: []FileFilter{ + NewFileFilterMust("DevelopmentTools10", "deb.jdk.x64"), + }, + }, + 1, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotFilteredList := filterReleaseFiles(tt.args.list, tt.args.filters); !reflect.DeepEqual(len(gotFilteredList), tt.wantFilteredList) { + t.Errorf("filterReleaseFiles() = %v, want %v", len(gotFilteredList), tt.wantFilteredList) + } + }) + } +} diff --git a/go.mod b/go.mod index 082f84f..4a5b91f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,15 @@ module github.com/v8platform/oneget go 1.16 require ( - github.com/khorevaa/logos v0.9.7 + github.com/PuerkitoBio/goquery v1.6.1 + github.com/khorevaa/logos v0.9.8 + github.com/kr/pretty v0.2.0 // indirect + github.com/mholt/archiver/v3 v3.5.0 + github.com/stretchr/testify v1.7.0 github.com/urfave/cli/v2 v2.3.0 + github.com/xelaj/go-dry v0.0.0-20210221174141-040b89cd6129 + go.uber.org/multierr v1.5.0 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c // indirect ) diff --git a/go.sum b/go.sum index 5dc2bf8..c683a2e 100644 --- a/go.sum +++ b/go.sum @@ -1,82 +1,73 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= -github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= -github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= -github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= -github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/PuerkitoBio/goquery v1.6.1 h1:FgjbQZKl5HTmcn4sKBgvx8vv63nhyhIpv7lJpFGCWpk= +github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= +github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= +github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= +github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/elastic/go-ucfg v0.8.3 h1:leywnFjzr2QneZZWhE6uWd+QN/UpP0sdJRHYyuFvkeo= github.com/elastic/go-ucfg v0.8.3/go.mod h1:iaiY0NBIYeasNgycLyTvhJftQlQEUO2hpF+FX0JKxzo= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= -github.com/khorevaa/logos v0.9.7 h1:nIm0kjMwn9RM5BFvSh5sPpjBM6RDX1y9RxLwtUwAfR8= -github.com/khorevaa/logos v0.9.7/go.mod h1:a94j5c2u9ffo0Be2HT+qGvVRna1FnvG7JX8IipZlxwo= +github.com/khorevaa/logos v0.9.8 h1:ZBRnxOJ5ORnELHH/Rarm1OQoIUHhae4WdHBwI4kgwZE= +github.com/khorevaa/logos v0.9.8/go.mod h1:wLXbpwEXx1Je5HTf7EwgbJntj2kGPvvvAEbxqWlOZNA= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I= +github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A= +github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/phuslu/log v1.0.61/go.mod h1:kzJN3LRifrepxThMjufQwS7S35yFAB+jAV1qgA7eBW4= +github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE= +github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc= +github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= +github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= +github.com/pierrec/lz4/v4 v4.0.3 h1:vNQKSVZNYUEAvRY9FaUXAF1XPbSOHJtDTiP41kzDz2E= +github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= -github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= -github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= -github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= -github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= -github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= -github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4= +github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/xelaj/go-dry v0.0.0-20210221174141-040b89cd6129 h1:gIfFpTIQWxoFTJ3tevR7Jn/NUJ/RKGd8VCylpWdyIcE= +github.com/xelaj/go-dry v0.0.0-20210221174141-040b89cd6129/go.mod h1:T4HQGApNb61ngPeYXsZXVNy3whgx7tN3eVhdTD2j2Zs= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= @@ -86,36 +77,33 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -125,11 +113,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/logos.yaml b/logos.yaml index 3a391d2..563f494 100644 --- a/logos.yaml +++ b/logos.yaml @@ -9,6 +9,7 @@ appenders: - name: FILE file_name: ./logs/oneget.log max_size: 100 + max_age: 10 encoder: json: loggers: @@ -21,4 +22,4 @@ loggers: appender_refs: - CONSOLE - FILE - level: info \ No newline at end of file + level: debug \ No newline at end of file diff --git a/main.go b/main.go index 9e93b57..c8631c9 100644 --- a/main.go +++ b/main.go @@ -52,7 +52,7 @@ func setFlags() []cli.Flag { }, &cli.StringFlag{ Name: "path", - DefaultText: "./downloads", + DefaultText: "downloads", Usage: "Путь к каталогу выгрузки", }, &cli.BoolFlag{ @@ -66,6 +66,26 @@ func setFlags() []cli.Flag { Value: "oneget.logs", Usage: "Файл лога загрузки", }, + &cli.BoolFlag{ + Name: "extract", + Value: false, + Usage: "Распаковывать дистрибутив", + }, + &cli.StringFlag{ + Name: "extractPath", + DefaultText: "pack", + Value: "pack", + Usage: "Каталог распаковки дистрибутива", + }, + &cli.BoolFlag{ + Name: "rename", + Aliases: []string{"sl"}, + Value: false, + Usage: `Переименовывать дистрибутивы при распаковке. + Примеры: + 1c-enterprise-8.3.18.1334-client_8.3.18-1334_amd64.deb -> client-8.3.18.1334.deb + 1c-enterprise83-server_8.3.16-1876_amd64.deb -> server_8.3.16-1876.deb`, + }, } } @@ -84,6 +104,9 @@ func main() { Nicks: Nicks(strings.ToLower(c.String("nicks"))), VersionFilter: c.String("version-filter"), DistribFilter: c.String("distrib-filter"), + Extract: c.Bool("extract"), + ExtractPath: c.String("extractPath"), + Rename: c.Bool("rename"), } debug := c.Bool("debug") diff --git a/unpacker/fixtures/linux/client/client_8_3_16_1876.deb64.tar.gz b/unpacker/fixtures/linux/client/client_8_3_16_1876.deb64.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..dc95945fbd8722ce603257280d77aa7bd550202c GIT binary patch literal 269 zcmb2|=3oE==C{{kyP6F|SRd#d<;^JmT>LoCDVtfjBqbpIfAP{SbEm%wY3lh>qc12F zXk&Tg*`;N9J*8VFS_H}Nuv#v>yZMxRx4bBz5ZF}@|cApk_eJbpTs^sfOA3vYT+m-rE zvis*}+xqk8GxqIqz;?F2_QYDf9p$wb-vG+dv5RheE(Ly|NmzL^+5a# RCjSWA*Lkga%%H)*007n2hUEYN literal 0 HcmV?d00001 diff --git a/unpacker/unpacker.go b/unpacker/unpacker.go new file mode 100644 index 0000000..b264c20 --- /dev/null +++ b/unpacker/unpacker.go @@ -0,0 +1,21 @@ +package unpacker + +import ( + "github.com/mholt/archiver/v3" + "regexp" +) + +var re = regexp.MustCompile(`^1c-enterprise[\d]*-[\d\.\d\.\d*\.\d*]*-*([a-z-]*)_([\d\.\d\.\d*\.\d*-]*)_(amd64)\.([a-z]*)$`) + +func Extract(filename string, destinatin string) error { + err := archiver.Unarchive(filename, destinatin) + if err != nil { + return err + } + return nil +} + +func GetAliasesDistrib(fileName string) string { + resultFileName := re.ReplaceAllString(fileName, `$1-$2.$4`) + return resultFileName +} diff --git a/unpacker/unpacker_test.go b/unpacker/unpacker_test.go new file mode 100644 index 0000000..e9f81df --- /dev/null +++ b/unpacker/unpacker_test.go @@ -0,0 +1,114 @@ +package unpacker + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "io/ioutil" + "log" + "os" + "path/filepath" + "regexp" + "testing" +) + +func TestUnpackTarGz(t *testing.T) { + tempDir, err := getTempDir() + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(tempDir) + + if err != nil { + log.Println(err) + } + path := filepath.Join("fixtures","linux", "client") + files, err := ioutil.ReadDir(path) + if err != nil { + log.Fatal(err) + } + + for _, file := range files { + fileName := file.Name() + Extract(filepath.Join( "fixtures","linux", "client", fileName), tempDir) + + fileGZ := fileName[:len(fileName) - len(filepath.Ext(fileName))] + dirName := fileGZ[:len(fileGZ) - len(filepath.Ext(fileGZ))] + + files, err := ioutil.ReadDir(filepath.Join(tempDir, dirName)) + if err != nil { + log.Fatal(err) + } + + assert.Equal(t, len(files), 4, "Распаковка успешно завершена") + } +} + +func TestNameAliases_8_3_16(t *testing.T) { + distrNames := []string{ + "1c-enterprise83-client_8.3.16-1876_amd64.deb", + "1c-enterprise83-client-nls_8.3.16-1876_amd64.deb", + "1c-enterprise83-thin-client_8.3.16-1876_amd64.deb", + "1c-enterprise83-thin-client-nls_8.3.16-1876_amd64.deb", + "1c-enterprise83-common_8.3.16-1876_amd64.deb", + "1c-enterprise83-common-nls_8.3.16-1876_amd64.deb", + "1c-enterprise83-crs_8.3.16-1876_amd64.deb", + "1c-enterprise83-server_8.3.16-1876_amd64.deb", + "1c-enterprise83-server-nls_8.3.16-1876_amd64.deb", + "1c-enterprise83-ws_8.3.16-1876_amd64.deb", + "1c-enterprise83-ws-nls_8.3.16-1876_amd64.deb", + } + checkAliases(t, distrNames) +} + +func TestNameAliases_8_3_18_1334(t *testing.T) { + distrNames := [] string{ + "1c-enterprise-8.3.18.1334-client_8.3.18-1334_amd64.deb", + "1c-enterprise-8.3.18.1334-client-nls_8.3.18-1334_amd64.deb", + "1c-enterprise-8.3.18.1334-thin-client_8.3.18-1334_amd64.deb", + "1c-enterprise-8.3.18.1334-thin-client-nls_8.3.18-1334_amd64.deb", + "1c-enterprise-8.3.18.1363-common_8.3.18-1363_amd64.deb", + "1c-enterprise-8.3.18.1363-common-nls_8.3.18-1363_amd64.deb", + "1c-enterprise-8.3.18.1363-crs_8.3.18-1363_amd64.deb", + "1c-enterprise-8.3.18.1363-server_8.3.18-1363_amd64.deb", + "1c-enterprise-8.3.18.1363-server-nls_8.3.18-1363_amd64.deb", + "1c-enterprise-8.3.18.1363-ws_8.3.18-1363_amd64.deb", + "1c-enterprise-8.3.18.1363-ws-nls_8.3.18-1363_amd64.deb", + } + + checkAliases(t, distrNames) +} + +func checkAliases(t *testing.T, distrNames []string) { + + for _, distrName := range distrNames { + result := GetAliasesDistrib(distrName) + fmt.Println(result) + regexp := regexp.MustCompile(`^(.*)-([\d\.\d\.\d*\.\d*]*-[\d]*).(.*)$`) + find := regexp.ReplaceAllString(result, `$1-VERSION.$3`) + assert.Contains(t, getExpectedName(), find) + } +} + +func getExpectedName() map[string]string { + return map[string]string{ + "client-VERSION.deb": "", // "1c-enterprise83-client_8.3.16-1876_amd64.deb" + "client-nls-VERSION.deb": "", // "1c-enterprise83-client-nls_8.3.16-1876_amd64.deb" + "common-VERSION.deb": "", // "1c-enterprise83-common_8.3.16-1876_amd64.deb" + "crs-VERSION.deb": "", // "1c-enterprise83-crs_8.3.16-1876_amd64.deb" + "common-nls-VERSION.deb": "", // "1c-enterprise83-common-nls_8.3.16-1876_amd64.deb" + "server-VERSION.deb": "", // "1c-enterprise83-server_8.3.16-1876_amd64.deb" + "server-nls-VERSION.deb": "", // "1c-enterprise83-server-nls_8.3.16-1876_amd64.deb" + "thin-client-nls-VERSION.deb": "", // "1c-enterprise83-thin-client-nls_8.3.16-1876_amd64.deb" + "thin-client-VERSION.deb": "", // "1c-enterprise83-thin-client_8.3.16-1876_amd64.deb" + "ws-nls-VERSION.deb": "", // "1c-enterprise83-ws-nls_8.3.16-1876_amd64.deb" + "ws-VERSION.deb": "", // "1c-enterprise83-ws_8.3.16-1876_amd64.deb" + } +} + +func getTempDir() (string, error) { + dir, err := ioutil.TempDir("", "oneget") + if err != nil { + log.Fatal(err) + } + return dir, nil +} \ No newline at end of file