# Установка, настройка и сборка на Windows

Здесь описана установка готового Windows-дистрибутива GSF CLI, настройка проекта и сборка `applib` и `appsrc`. Основная
оболочка в примерах - **PowerShell**. Блоки CMD отмечены отдельно.

Можно работать через мастер с IntelliJ IDEA или загрузить `config.json` и выполнять сборку без диалогов. Для закрытых
источников подготовка доступа одинакова в обоих случаях и выполняется до добавления проекта.

## Что потребуется

Используйте JDK **21**, SBT **1.x** начиная с 1.8.2 и Git for Windows. В примерах устанавливается SBT 1.10.7. Готовый
Windows-дистрибутив GSF CLI включает Python; отдельно создавать venv для него не нужно.

Подготовьте URL конфигурационного Git-проекта, оканчивающийся на `.git`, существующую ветку и имя проекта в GSF CLI.
Ниже имя - `main`, каталог CLI - `C:\programs\gsf-cli`.

Исходники и артефакты могут требовать разный доступ. Проверьте каждый используемый Git-хост, источник GlobalServer и
репозитории SBT:

- При работе с публичными источниками зависимости скачиваются из интернета.
- Внутренний прокси загружает недостающие зависимости из внешних источников и кеширует их.
- В изолированном контуре все зависимости заранее размещаются во внутренних репозиториях. Туда же должны быть доставлены
  установочные файлы.

Для чистой машины нужны не только библиотеки проекта, но и SBT, Scala, плагины и все их транзитивные зависимости.
Приватный Git-репозиторий сам по себе не означает, что весь контур закрыт от интернета.

## Установка

### Пользователь и рабочие каталоги

Войдите в Windows под пользователем, который будет собирать проект. Его `%USERPROFILE%` определяет расположение SBT
credentials. Чтобы открыть этот каталог, вставьте `%USERPROFILE%` в адресную строку Проводника. У служебной учётной
записи будет другой профиль и другие файлы доступа.

В Проводнике создайте папку `C:\programs`, а внутри неё — `tmp`, `builds` и `gsf-cli`.

Если запись в `C:\programs` запрещена, администратор должен создать эти каталоги и выдать пользователю сборки право
изменения. После этого продолжайте под пользователем сборки. Для SBT интерактивный конфигуратор ожидает путь
`C:\programs\sbt`.

### Git, JDK и IDEA

Установите Git for Windows и JDK 21. В поиске Windows найдите «переменные среды» и откройте изменение переменных
текущего пользователя. Создайте `JAVA_HOME` со значением `C:\Program Files\Java\jdk-21`, заменив путь на фактический.
В пользовательскую переменную `Path` добавьте каталог `bin` этого JDK. Остальные записи оставьте на месте.

Завершите сеанс Windows и войдите снова, чтобы консоли и ярлыки получили новые значения. Откройте PowerShell и
проверьте:

```powershell
git --version
& "$env:JAVA_HOME\bin\java.exe" -version
& "$env:JAVA_HOME\bin\javac.exe" -version
```

Обе команды Java должны показывать версию 21. Если работаете через мастер и IDEA, установите IntelliJ IDEA с плагином
Scala. Мастер ищет JDK в `C:\Program Files\Java`, а IDEA - в `C:\Program Files\JetBrains`; при выборе укажите
фактические каталоги.

Для сборки через `config.json` IDEA не требуется.

### Сертификаты перед загрузкой

Если адрес загрузки или Git-сервер использует корпоративный CA, настройте доверие до обращения к нему. Получите корневой
и промежуточные сертификаты у администратора. Корневой CA устанавливается в доверенные корневые центры Windows,
промежуточные - в хранилище промежуточных центров для пользователя/машины, где работает сборка.

Git for Windows может использовать отдельный набор CA. Проверьте выбранный TLS backend:

```powershell
git config --show-origin --get http.sslBackend
```

При `schannel` проверяется хранилище Windows. При `openssl` укажите полный PEM-bundle для Git:

```powershell
git config --global http.sslCAInfo 'C:/programs/certs/company-ca-bundle.pem'
```

Если backend явно не задан, проверьте настройку установленного Git for Windows. Отсутствие этой записи само по себе не
означает ошибку.

Не отключайте проверку Git-сертификатов. Настройки Java и Python для загрузки зависимостей приведены ниже, после
установки компонентов.

### Дистрибутив GSF CLI и SBT

Скачайте [дистрибутив GSF CLI для Windows](https://repo.global-system.ru/artifactory/common/ru/bitec/gsf-cli-windows/LATEST/gsf-cli-windows-LATEST.zip)
и [SBT 1.10.7](https://github.com/sbt/sbt/releases/download/v1.10.7/sbt-1.10.7.zip) в `C:\programs\tmp`.
В закрытой сети доставьте эти архивы заранее.

Распакуйте GSF CLI в `C:\programs\gsf-cli`, а SBT — в `C:\programs\sbt`. Если архив содержит ещё одну корневую папку,
перенесите её содержимое так, чтобы файлы лежали по указанным ниже путям. При обновлении сохраните `workspace` и
мастер-ключ.

В Проводнике проверьте, что существуют `C:\programs\gsf-cli\python\python.exe`,
`C:\programs\gsf-cli\manage.cmd`, папка `C:\programs\gsf-cli\links` и
`C:\programs\sbt\bin\sbt-launch.jar`. Добавьте `C:\programs\sbt\bin` в пользовательскую переменную `Path`, как ранее
добавляли JDK. После выхода и повторного входа в Windows откройте PowerShell:

```powershell
Set-Location 'C:\programs\gsf-cli'
.\python\python.exe --version
.\manage.cmd version
```

`manage.cmd version` должен вывести версию GSF CLI. Вызовы `.cmd` используют Python из
поставки. `bin\initvenv.cmd` для этого маршрута запускать не нужно.

## Сертификаты Java и Python

Если TLS-сертификаты всех источников уже доверенные, пропустите раздел.

### Java truststore

В PowerShell с правами записи в JDK задайте тот же `JAVA_HOME`, который будет использоваться сборкой, и импортируйте CA:

```powershell
$env:JAVA_HOME = 'C:\Program Files\Java\jdk-21'
& "$env:JAVA_HOME\bin\keytool.exe" -import -trustcacerts `
  -keystore "$env:JAVA_HOME\lib\security\cacerts" `
  -storepass changeit -alias company-ca `
  -file 'C:\programs\certs\company-ca.crt'

& "$env:JAVA_HOME\bin\keytool.exe" -list `
  -keystore "$env:JAVA_HOME\lib\security\cacerts" `
  -storepass changeit | Select-String 'company-ca'
```

`company-ca` - уникальный alias, `changeit` - стандартный пароль `cacerts`, если его не меняли. Повторите импорт для
других необходимых CA с отдельными alias. После административного шага вернитесь в PowerShell пользователя сборки.

### Requests CA bundle

GSF CLI загружает часть артефактов через Python Requests. Эта библиотека не читает Java `cacerts`. Если нужных CA нет в
доступном Requests наборе, поместите PEM-bundle, например, в `C:\programs\certs\company-ca-bundle.pem`. Убедитесь, что
пользователь сборки может прочитать файл. В переменных среды текущего пользователя создайте `REQUESTS_CA_BUNDLE` со
значением `C:\programs\certs\company-ca-bundle.pem`.

Bundle заменяет стандартный набор Requests и должен включать все необходимые корневые и промежуточные CA. Если вместо
PEM-файла используется каталог сертификатов, предварительно подготовьте его командой
`openssl rehash <path-to-ca-directory>`.

После настройки завершите сеанс Windows и войдите снова. Для запуска от служебной учётной записи задайте эту переменную
в окружении службы или скрипте сборки: переменные вашего пользователя туда не переносятся.

## Мастер-ключ и доступ к репозиториям

### Регистрация ключа

В PowerShell пользователя сборки:

```powershell
Set-Location 'C:\programs\gsf-cli'
.\config.cmd register_private_key -c 'C:\programs'
if ($LASTEXITCODE -ne 0) { throw 'Не удалось зарегистрировать мастер-ключ' }
```

`-c` принимает существующий каталог, а не путь к файлу. GSF CLI добавляет имя `.gsf-cli.priv`, создаёт ключ либо читает
существующий и сохраняет путь в `workspace\store.json`.
Проверьте в Проводнике, что файл `C:\programs\.gsf-cli.priv` существует.

Если в этой установке ключ уже зарегистрирован и загружен, повторная команда не переносит и не заменяет его. Не удаляйте
ключ: он нужен для расшифровки сохранённых паролей.

Через свойства файлов → **Безопасность** проверьте доступ к `C:\programs\.gsf-cli.priv` и
`C:\programs\gsf-cli\workspace\store.json`. Пользователь сборки должен читать и изменять их; посторонним пользователям
доступ не нужен. В Windows GSF CLI не устанавливает NTFS ACL автоматически, в отличие от прав `600` на Linux.

### Учётные данные Git и артефактов

Для приватного Git сохраните логин и токен до первого добавления проекта. Следующий блок читает секрет без отображения
на экране и передаёт его в stdin:

```powershell
$GsfUrl = Read-Host 'Базовый URL Git-сервера'
$GsfLogin = Read-Host 'Git login'
$GsfSecret = Read-Host 'Git token' -AsSecureString
$GsfSecretPtr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($GsfSecret)
try {
    [Runtime.InteropServices.Marshal]::PtrToStringBSTR($GsfSecretPtr) |
        .\credential_manager.cmd set --url $GsfUrl --login $GsfLogin --password-stdin
    if ($LASTEXITCODE -ne 0) { throw 'Не удалось сохранить учётные данные' }
}
finally {
    [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($GsfSecretPtr)
    Remove-Variable GsfSecret, GsfSecretPtr, GsfUrl, GsfLogin
}
```

Базовый URL для `https://git.example.org/group/project.git` - `https://git.example.org`. Сохранённая запись подходит
всем вложенным путям с теми же протоколом, хостом и портом. Для другого Git-хоста повторите блок. Учётной записи нужны
права чтения конфигурационного проекта, всех Git-модулей и SBT-плагина, если он получен из Git.

Если источник GlobalServer или других артефактов требует авторизацию, повторите тот же блок с базовым адресом менеджера
репозиториев и его логином/токеном. Для публичных артефактов без авторизации запись не требуется.

В интерактивном режиме публичный Git позволяет работать без сохранённой записи. При отсутствии записи стандартный запрос
Git остаётся доступен.

```{attention}
В headless-режиме текущий код требует Git credentials до обращения к серверу, даже если URL публичный. Добавьте действующую запись для каждого Git-хоста. Без неё анонимная headless-сборка не начнётся. Git не будет спрашивать пароль через терминал.
```

Если для URL есть запись в хранилище GSF CLI, он автоматически подключает свой Git helper. Для этого вызова Git
остальные helpers отключаются. Не добавляйте секреты в `bin\credential_manager_git.cmd`. После смены токена снова
выполните `set` с тем же URL.

Проверьте записи:

```powershell
.\credential_manager.cmd show
```

Пароли отображаются как `********`. `show --reveal` раскрывает их; не используйте этот параметр в автоматической сборке
и логах.

### Когда нужен credential.helper store

GSF CLI не выполняет `git config --global credential.helper store`. Если вы сохранили данные через
`credential_manager.cmd set`, вручную включать `store` для вызовов Git из GSF CLI не нужно.

Если обычный Git каждый раз спрашивает логин и пароль, можно включить их сохранение. В интерактивном режиме без
подходящей записи в хранилище GSF CLI также вызывает обычный Git, и тот использует свои настройки. В PowerShell
пользователя сборки выполните:

```powershell
git config --global credential.helper store
```

Повторите Git-операцию и введите логин и токен. Команда только включает helper; сами данные сохраняются после успешной
авторизации. `store` хранит их на диске открытым текстом, обычно в файле `.git-credentials` в домашнем каталоге
пользователя Git. Подробнее — [документация git-credential-store](https://git-scm.com/docs/git-credential-store).

Настройка `store` не создаёт запись в GSF CLI и не заменяет её в headless. Если CLI сообщает «Не заданы учетные данные»,
используйте `credential_manager.cmd set`, как показано выше.

## Закрытые репозитории SBT

Для публичных источников без авторизации и без принудительного прокси этот раздел можно пропустить. Для закрытых
репозиториев обязательны файл `repositories`, **обе копии** `.credentials`, переменная `SBT_CREDENTIALS` и **три
параметра JVM**.

### Файл repositories

Откройте `%USERPROFILE%` через адресную строку Проводника. Создайте в нём папки `.sbt` и `.ivy2`, если их ещё нет.
Включите показ расширений имён файлов, чтобы случайно не оставить `.txt`.

Откройте Блокнот и сохраните в папке `.sbt` файл `repositories` с таким содержимым, заменив адреса своими. В диалоге
сохранения выберите тип «Все файлы» и кодировку UTF-8 без BOM:

```text
[repositories]
local
repository-1: https://repository.example.org/path/to/repository-1/
repository-2: https://repository.example.org/path/to/repository-2/
```

Имя файла - `repositories`, без `.txt`. `local` обозначает локальный Ivy-репозиторий `%USERPROFILE%\.ivy2\local`.
Остальные имена произвольные и уникальные. Количество строк соответствует используемым источникам; групповой URL
допустим, если за ним доступны все нужные артефакты.

Если в контуре используется HTTP:

```text
repository-http: http://repository.example.org/path/to/repository/, allowInsecureProtocol
```

Этот параметр разрешает HTTP, но не отключает проверку HTTPS. Для корпоративного HTTPS нужен сертификат в Java
truststore.

Launcher читает файл до прикладного `build.sbt`. Внутренний репозиторий должен отдавать `org.scala-sbt:sbt`, Scala,
плагины и их зависимости. Формат Ivy, если он требуется, задаётся дополнительным шаблоном пути из конфигурации вашего
репозитория. Описание форматов - в [Proxy Repositories](https://www.scala-sbt.org/1.x/docs/Proxy-Repositories.html).

### Credentials для SBT и Ivy

В Блокноте создайте файл `.credentials` в папке `%USERPROFILE%\.sbt`. При сохранении выберите тип «Все файлы» и
кодировку UTF-8 без BOM.
Содержимое:

```properties
realm=repository-realm
host=repository.example.org
user=build-user
password=very-secret-password
```

Укажите действующий realm, hostname и учётную запись. Этот пример предполагает один hostname, realm и один набор
credentials для перечисленных SBT-репозиториев.

Сохраните файл без расширения `.txt`. В Проводнике скопируйте его в `%USERPROFILE%\.ivy2`, сохранив имя `.credentials`.
Проверьте обе папки: в каждой должен лежать свой файл `.credentials` с одинаковым содержимым. Эта копия обязательна.

Основным остаётся `%USERPROFILE%\.sbt\.credentials`. Копия в `.ivy2` нужна компонентам сборки, использующим путь Ivy.
Оба файла содержат секрет открытым текстом; ограничьте к ним доступ средствами Windows. После смены токена обновите обе
копии.

### Все три параметра JVM

Откройте переменные среды текущего пользователя Windows. Создайте `SBT_CREDENTIALS` и укажите полный путь к основному
файлу, например `C:\Users\build-user\.sbt\.credentials`. Сам путь в значении этой переменной запишите без кавычек.

Затем создайте переменную `JAVA_TOOL_OPTIONS` со следующим значением в одну строку. Замените `C:\Users\build-user`
на путь к профилю пользователя сборки:

```text
-Dsbt.boot.credentials="C:\Users\build-user\.sbt\.credentials" -Dsbt.repository.config="C:\Users\build-user\.sbt\repositories" -Dsbt.override.build.repos=true
```

Если `JAVA_TOOL_OPTIONS` уже существует, сохраните её остальные параметры. Эти три добавьте через пробел; если они
уже есть, обновите значения, не создавая дубликатов.

`sbt.boot.credentials` передаёт файл доступа launcher, `sbt.repository.config` задаёт список репозиториев,
`sbt.override.build.repos=true` заставляет использовать именно этот список. Кавычки вокруг путей сохраняют их целиком,
если имя профиля пользователя содержит пробелы.

Настройте переменные до первой команды GSF CLI или SBT. Скрипты GSF CLI могут автоматически
заполнить только `SBT_CREDENTIALS`, если основной файл существует; три параметра JVM они за вас не задают.

Git helper и `credential_manager.cmd` не заменяют SBT credentials. Одна запись в `workspace\store.json` не обеспечивает
авторизацию launcher.

### Запуск из ярлыков и новой консоли

После изменения переменных пользователя завершите сеанс Windows и войдите снова. Теперь новые консоли и ярлыки,
открытые из Проводника, получат настроенное окружение.

Переменные `$env:...`, заданные командой в PowerShell, действуют только в этой консоли и её дочерних процессах.
Настройка PowerShell-профиля тоже не передаёт значения ярлыкам из Проводника. Для служебной учётной записи задавайте
переменные в процессе запуска сборки; пример приведён ниже, в разделе «Повторный запуск без оператора».

## Подготовка проекта

Проверьте источники в конфигурационном Git-проекте до первого запуска: GlobalServer, SBT-плагин, модули и
`project/repositories/default.yaml` должны быть доступны из вашей сети. В изолированном контуре они должны указывать на
внутренние источники.

### Без IDEA: config.json и headless

Создайте в Блокноте файл `config.json` в папке `C:\programs\builds`. При сохранении выберите тип «Все файлы», чтобы
получилось имя `config.json`, а не `config.json.txt`. Кодировка — UTF-8 без BOM:

```json
{
  "sbt_home": "C:\\programs\\sbt",
  "concurrent_module_updates": 1,
  "projects": [
    {
      "name": "main",
      "project_source": "https://git.example.org/group/configuration-project.git",
      "project_branch": "main",
      "jdk_home": "C:\\Program Files\\Java\\jdk-21",
      "build_system": "sbt",
      "publish_type": "SNAPSHOT"
    }
  ]
}
```

Замените Git URL, ветку и путь JDK своими значениями. В JSON обратная косая черта записывается как `\\`.
`concurrent_module_updates: 1` включает последовательную загрузку модулей; без настройки допускается до 20 обновлений
одновременно.

```{attention}
`load_config` удаляет уже зарегистрированные проекты, отсутствующие в файле, вместе с исходниками, дистрибутивом, ярлыками, окружением IDEA и кешем. Включите в `projects` все проекты, которые должны остаться в этой установке.
```

```powershell
Set-Location 'C:\programs\gsf-cli'
.\python\python.exe -m json.tool 'C:\programs\builds\config.json' > $null
if ($LASTEXITCODE -ne 0) { throw 'Ошибка JSON' }
.\config.cmd enable_headless
if ($LASTEXITCODE -ne 0) { throw 'Не удалось включить headless' }
.\config.cmd load_config -f 'C:\programs\builds\config.json'
if ($LASTEXITCODE -ne 0) { throw 'Не удалось загрузить конфигурацию' }
```

Команда регистрирует настройки, не скачивая исходники и не активируя проект. Для дальнейших команд используйте
`-p main`.

Headless сохраняется для всей установки и запрещает диалоги. Чтобы вернуть интерактивный режим:

```powershell
.\config.cmd disable_headless
```

Можно загрузить `config.json` и затем отключить headless, если нужны запросы пользователя без подготовки IDEA.
Последовательность сборки останется той же.

### Через мастер и IDEA

Для этого варианта `config.json` не нужен. Используйте скрипты из `C:\programs\gsf-cli\links` готового дистрибутива.
Закройте IDEA в общем окружении. Если ранее включали headless, отключите его в PowerShell:

```powershell
Set-Location 'C:\programs\gsf-cli'
.\config.cmd disable_headless
```

Откройте в Проводнике `C:\programs\gsf-cli\links` и запустите двойным щелчком `add_project.cmd`.
Для закрытых источников к этому моменту уже должны быть настроены файлы доступа и переменные пользователя Windows.

Укажите имя проекта, URL, ветку, систему сборки `sbt`, JDK 21 и вариант портов. SBT должен находиться в
`C:\programs\sbt`. Мастер предложит подготовить проект, запросит путь IDEA и недостающие настройки.

При импорте дождитесь окончания загрузки проекта в IDEA, закройте её и продолжите конфигурацию. Подтвердите активацию
проекта, чтобы следующие ярлыки работали с ним. Позже выбрать активный проект можно через `links\activate_project.cmd`.
Когда подготовка отложена, позже используйте
`manage.cmd -p <project_name> prepare_project`.

После подготовки появятся исходники в `workspace\sources\<project_name>\application`, сервер в
`workspace\dists\<project_name>\Global3se` и ярлыки в `workspace\links\<project_name>`.

Для повседневной работы запускайте из Проводника общие ярлыки в `C:\programs\gsf-cli\links`:

| Скрипт                              | Действие                              |
|-------------------------------------|---------------------------------------|
| `active_project_refresh.cmd`        | Обновить проект и зависимости         |
| `active_project_start_idea.cmd`     | Открыть IDEA для активного проекта    |
| `active_project_sbt.cmd`            | Открыть консоль SBT активного проекта |
| `active_project_configure_idea.cmd` | Обновить конфигурацию IDEA            |

Открывайте IDEA через `active_project_start_idea.cmd`: он задаёт окружение проекта перед запуском среды.
В общем окружении допускается одна IDEA. Если нужны несколько проектов одновременно, запустите `start_sep_idea.cmd`
из той же папки и выберите проект в диалоге.

Если Git Credential Manager выдаёт:

```text
fatal: Unencrypted HTTP is not supported for GitHub. Ensure the repository remote URL is using HTTPS.
```

для хоста `extgit.global-system.ru` в исходной инструкции используется настройка:

```powershell
git config --global credential.extgit.global-system.ru.provider generic
```

После этого повторите добавление проекта. Это настройка конкретного Git-хоста, не способ исправления TLS-сертификата.

## Сборка applib и appsrc

При работе через мастер и IDEA сначала выберите нужный проект через `C:\programs\gsf-cli\links\activate_project.cmd`.
Затем запустите `active_project_refresh.cmd` из той же папки и дождитесь окончания обновления.

Для маршрута через `config.json` выполните обновление в PowerShell пользователя сборки:

```powershell
Set-Location 'C:\programs\gsf-cli'
.\manage.cmd -p main refresh
if ($LASTEXITCODE -ne 0) {
    .\manage.cmd -p main refresh
    if ($LASTEXITCODE -ne 0) { throw 'Обновление зависимостей не выполнено' }
}
```

Один повтор `refresh` сохранён из исходного сценария с чистыми кешами: первый запуск может загрузить часть зависимостей
перед ошибкой. При повторной ошибке сборка останавливается. Ошибки прав, сертификатов и отсутствующих артефактов нужно
исправить отдельно. Если работаете через ярлык, для одного повтора запустите `active_project_refresh.cmd` ещё раз.

После успешного обновления соберите JAR-файлы. Общего ярлыка для `build` в поставке нет, поэтому в обоих вариантах
используется команда PowerShell:

```powershell
Set-Location 'C:\programs\gsf-cli'
.\manage.cmd -p main build --build-appsrc --skip-publication
if ($LASTEXITCODE -ne 0) { throw 'Сборка завершилась ошибкой' }
```

Результат находится в `C:\programs\gsf-cli\workspace\sources\main\application\build\publish`:

```text
applib\
  *.jar
  metadata.yaml
appsrc\
  *-sources.jar
```

Откройте этот каталог в Проводнике. Убедитесь, что в `applib` есть JAR-файлы, а в `appsrc` — файлы `*-sources.jar`.
Проверьте и вложенные папки.

`--build-appsrc` поддерживается для SBT. Он запускает `publishSrcFolder` после `publishLibFolder`, выполняет чистую
сборку и проверяет, что оба вида JAR сформированы.

Для другого корня результата:

```powershell
.\manage.cmd -p main build --build-appsrc --skip-publication `
  --publish-path 'C:\programs\builds\publish\main'
if ($LASTEXITCODE -ne 0) { throw 'Сборка завершилась ошибкой' }
```

Каталоги `applib` и `appsrc` создаются внутри указанного корня. Для нескольких проектов используйте отдельные вызовы с
`-p` и отдельные пути. Относительный путь считается от `application`.

`--skip-publication` отключает удалённую публикацию, сохраняя локальные каталоги. Без этого флага сборка вызывает
публикацию, включая `sbt publish`. `--publish-path` не меняет адрес Maven-репозитория. При включённой публикации
исходников Maven получает отдельные `*-sources.jar`, а не весь `appsrc` одним артефактом.

ZIP-архивы GSF CLI не создаёт. Если нужны `applib.zip` и `appsrc.zip`, упакуйте результат на следующем шаге или
используйте nscli.

### Повторный запуск без оператора

Подготовленные файлы, ключ и записи credentials должны быть доступны пользователю, который запускает сборку. В каждом
новом процессе заново задайте переменные. Ниже вариант для закрытых SBT-репозиториев:

```powershell
$ErrorActionPreference = 'Stop'
$env:SBT_CREDENTIALS = "$env:USERPROFILE\.sbt\.credentials"
foreach ($GsfFile in @(
    "$env:USERPROFILE\.sbt\repositories",
    "$env:USERPROFILE\.sbt\.credentials",
    "$env:USERPROFILE\.ivy2\.credentials"
)) {
    if (-not (Test-Path $GsfFile -PathType Leaf)) { throw "Не найден файл: $GsfFile" }
}
$GsfSbtOptions = @(
    ('-Dsbt.boot.credentials="' + $env:SBT_CREDENTIALS + '"')
    ('-Dsbt.repository.config="' + $env:USERPROFILE + '\.sbt\repositories"')
    '-Dsbt.override.build.repos=true'
) -join ' '
$env:JAVA_TOOL_OPTIONS = (($env:JAVA_TOOL_OPTIONS, $GsfSbtOptions) -join ' ').Trim()
# Если нужен корпоративный CA для Requests, задайте REQUESTS_CA_BUNDLE здесь.
Set-Location 'C:\programs\gsf-cli'
.\config.cmd enable_headless
if ($LASTEXITCODE -ne 0) { throw 'Не удалось включить headless' }
.\config.cmd load_config -f 'C:\programs\builds\config.json'
if ($LASTEXITCODE -ne 0) { throw 'Не удалось загрузить конфигурацию' }
.\manage.cmd -p main refresh
if ($LASTEXITCODE -ne 0) {
    .\manage.cmd -p main refresh
    if ($LASTEXITCODE -ne 0) { throw 'Обновление зависимостей не выполнено' }
}
.\manage.cmd -p main build --build-appsrc --skip-publication `
  --publish-path 'C:\programs\builds\publish\main'
if ($LASTEXITCODE -ne 0) { throw 'Сборка завершилась ошибкой' }
```

Для публичных SBT-источников без авторизации пропустите проверку этих файлов и блок настройки SBT-переменных.
Ограничение headless для публичного Git остаётся: записи Git credentials должны существовать. Две сборки одной установки
не запускайте одновременно.

## Диагностика

Ошибки `.cmd` ищите в `workspace\logs\cmd_error_log.txt`; подробности работы GSF CLI - в ежедневном `YYYY-MM-DD.log` там
же.

При ошибке Git сначала проверьте пользователя, ключ и записи `credential_manager.cmd show`. Для SBT проверьте обе копии
`.credentials`, realm/host и все три JVM-параметра. `unauthorized` означает проблему авторизации, `not found` -
отсутствие артефакта по запрошенному адресу или недоступность upstream. TLS-ошибка требует проверки хранилища именно
того инструмента, который её выдал.

### Подробный лог SBT: publishDevDependencies

Если ошибка возникает при подготовке dev-зависимостей, повторите SBT-задачу `publishDevDependencies` с параметром
`-debug`. Он включает подробный вывод SBT. У команды `manage build` такого параметра нет.

Для проекта, подготовленного через мастер, откройте командную строку Windows (**CMD**) под пользователем сборки.
Выполните две команды, заменив путь к GSF CLI и имя `main` своими:

```bat
cd /d "C:\programs\gsf-cli\workspace\links\main"
sbt.cmd -debug "publishDevDependencies"
```

Это ярлык конкретного проекта из `workspace\links\<project_name>`. Он задаёт окружение проекта и передаёт аргументы
в SBT. Общий ярлык `links\active_project_sbt.cmd` используется для обычного открытия консоли SBT.

Если проект настроен через `config.json` и ярлыков нет, в CMD используйте созданный при подготовке сборки `set_env.cmd`:

```bat
cd /d "C:\programs\gsf-cli\workspace\sources\main\application"
call build\scripts\set_env.cmd
sbt -debug "publishDevDependencies"
```

Для закрытых репозиториев по-прежнему нужны обе копии `.credentials`, `SBT_CREDENTIALS` и все три JVM-параметра.
При настройке через переменные пользователя Windows открывайте CMD после повторного входа в систему. Если вы задали
переменные только в текущем PowerShell, запустите CMD из него командой `cmd`, чтобы сохранить это окружение.

Смотрите первую ошибку и сообщения перед ней: по подробному выводу проще понять, на каком репозитории или зависимости
останавливается задача. Это повтор только `publishDevDependencies`; он не собирает `applib` и `appsrc` целиком.
После исправления ошибки вернитесь к обычным `refresh` и `build`. Возможности подробного лога описаны в
[справке SBT](https://www.scala-sbt.org/1.x/docs/Howto-Logging.html).

### Проверка запуска SBT

В PowerShell, где заданы JDK 21, SBT в `PATH` и переменные закрытых репозиториев:

```powershell
Set-Location 'C:\programs\gsf-cli\workspace\sources\main\application'
sbt -batch sbtVersion
sbt -batch "show externalResolvers"
```

Для полного окружения проекта можно открыть CMD из этой PowerShell-консоли и выполнить:

```bat
cd /d C:\programs\gsf-cli\workspace\sources\main\application
call build\scripts\set_env.cmd
sbt -batch sbtVersion
sbt -batch "show externalResolvers"
```

Закрытая сборка должна получать зависимости из настроенных внутренних источников. Если используются публичные адреса,
проверьте `sbt.override.build.repos` и проектную конфигурацию.

### Проверка с чистыми кешами

Остановите другие сборки этого пользователя. Проверьте, что все зависимости доступны во внутренних репозиториях.
Удаление `%USERPROFILE%\.ivy2\local` также удалит локально опубликованные артефакты. Файлы `repositories` и
`.credentials` сохраняйте.

В PowerShell с настроенным окружением:

```powershell
Set-Location 'C:\programs\gsf-cli'
.\manage.cmd -p main clean
if ($LASTEXITCODE -ne 0) { throw 'Не удалось очистить кеш GSF CLI' }
Set-Location 'workspace\sources\main\application'
sbt -batch clean
if ($LASTEXITCODE -ne 0) { throw 'Не выполнен sbt clean' }
```

Затем удалите через Проводник следующие папки кешей, если они существуют. Для перехода вставляйте путь в адресную
строку:

- `%USERPROFILE%\.sbt\boot`;
- `%USERPROFILE%\.ivy2\cache`;
- `%USERPROFILE%\.ivy2\local`;
- `%LOCALAPPDATA%\Coursier\Cache`;
- `%USERPROFILE%\.cache\coursier`;
- `%USERPROFILE%\.coursier\cache`.

Удаляйте только перечисленные подпапки, сохраняя `.sbt` и `.ivy2` с файлами доступа и настройками репозиториев.

`manage clean` удаляет только служебный `cache.json` GSF CLI, а `sbt clean` - результаты компиляции. После очистки
повторите блок сборки из этой главы с одним допустимым повтором `refresh`. Если он завершился успешно, соберите
`--build-appsrc --skip-publication`.

Для восстановления dev-зависимостей исходная инструкция также предусматривает отдельный запуск из `application`:

```powershell
sbt -batch "publishDevDependencies"
```

Эта задача уже вызывается при `refresh`; дополнительный запуск сохранён как часть исходного сценария. Заново вызывать
`publishLibFolder` и `publishSrcFolder` после `build --build-appsrc` не нужно.

### Аварийное отключение проверки TLS

Если цепочку доверия нельзя восстановить до необходимого запуска, можно временно отключить проверку HTTPS в запросах GSF
CLI к артефактам. Для текущего PowerShell:

```powershell
$env:GSF_CLI_INSECURE_TLS = 'true'
```

Для запуска из ярлыков откройте переменные среды текущего пользователя Windows и создайте `GSF_CLI_INSECURE_TLS` со
значением `true`. Завершите сеанс Windows и войдите снова. При первом HTTPS-запросе к артефактам GSF CLI предупредит об
отключённой проверке.

После исправления сертификатов удалите эту переменную пользователя или измените её значение на `false`, затем снова
войдите в Windows. Для текущего PowerShell удалите временное значение отдельно:

```powershell
Remove-Item Env:GSF_CLI_INSECURE_TLS -ErrorAction SilentlyContinue
```

Проверку отключает только `true` без учёта регистра и окружающих пробелов. Такой режим уязвим для MITM-атак. Он не
исправляет доверие Git и Java и должен быть выключен после восстановления сертификатов.
