Симптом: команда сборки завершается успешно, но следующий шаг CI сообщает, что бинарник или архив не найден. Самое быстрое исправление — прекратить угадывать структуру .build и получать каталог текущей задачи через swift build --show-bin-path, передавая ему те же конфигурацию, архитектуру и назначение, что и самой сборке.

Этот порядок подходит тем, кто поддерживает Shell-, Fastlane- или CI-скрипты после обновления SwiftPM 6.4. Он также нужен авторам Swift Package и плагинов, тест-инженерам и DevOps-специалистам, отвечающим за кэш, удалённый Mac и откат инструментария.

**Важно.** На 27 августа 2026 года официальные материалы SwiftPM описывают Swift Build как систему сборки по умолчанию и отдельно рекомендуют получать каталог бинарных результатов через --show-bin-path. Статус Swift 6.4 и состояние Xcode 27 нужно повторно проверить перед выпуском: не следует превращать предварительные материалы или сообщения СМИ в гарантию совместимости.

Последнее обновление: 27 августа 2026 года. Данные сверены с [официальной документацией Swift Build](https://github.com/swiftlang/swift-package-manager/blob/main/Sources/PackageManagerDocs/Documentation.docc/SwiftBuildPreview.md), [репозиторием Swift Package Manager](https://github.com/swiftlang/swift-package-manager), [репозиторием Swift Build](https://github.com/swiftlang/swift-build), страницей [статуса Swift Evolution](https://github.com/swiftlang/swift-evolution) и [материалами Swift на Apple Developer](https://developer.apple.com/wwdc26/guides/swift/). Следующая обязательная проверка — при стабильном выпуске Swift 6.4, появлении RC или финальной версии Xcode 27, изменении параметра отката либо закрытии соответствующей официальной проблемы.

Сначала отделите сбой сборки от сбоя поиска

Код возврата команды и наличие конечного файла отвечают на разные вопросы. Если swift build завершился успешно, но шаг загрузки сообщает No such file or directory, первичный подозреваемый — не компилятор, а логика поиска, рабочий каталог, очистка или кэш.

Зафиксируйте в одном диагностическом артефакте:

  • полный вызов swift build вместе со всеми флагами;
  • рабочий каталог, полученный через pwd;
  • вывод swift --version и swift package --version;
  • активное состояние xcode-select;
  • фактический результат команды --show-bin-path;
  • имя ожидаемого бинарника, тестового отчёта или архива;
  • код возврата каждого этапа;
  • пользователя, от имени которого работает CI.
Так вы не смешаете три разных класса ошибок:
  1. Компиляция не прошла. У команды сборки ненулевой код возврата, поэтому искать артефакт бессмысленно.
  2. Путь вычислен неверно. Сборка успешна, но скрипт обращается к старому подкаталогу .build.
  3. Рабочая область не совпадает. Сборка запущена из одного checkout-каталога, а упаковка или загрузка — из другого.
Официальное описание перехода предупреждает, что внутреннюю структуру каталогов нельзя считать стабильным интерфейсом для пользовательских скриптов. Публичный способ узнать каталог результата — запросить его у инструмента, а не повторять внутренний алгоритм в Shell или Makefile. Это особенно важно для **пути артефактов сборки SwiftPM 6.4**, поскольку сама смена системы сборки меняет предположения, на которых держались старые пайплайны.

Что именно потребляет каждая роль

Владелец Shell, Makefile или Fastlane потребляет бинарник, архив или каталог, который нужно скопировать. Его доказательство — совпадение параметров сборки и параметров запроса пути.

Автор Swift Package отвечает за продукты, бинарные цели, ресурсы и плагины. Его доказательство — объявленные входы и выходы, а не совпадение с каталогом, найденным перебором.

Тест-инженер потребляет статус swift test, отчёты, покрытие и журналы неудачных тестов. Простого условия «файл существует» недостаточно, если файл остался от предыдущего запуска.

CI-платформа и DevOps отвечают за идентичность узла, кэш и рабочую область. Их доказательство — воспроизводимость на холодном кэше, горячем кэше и после перезапуска удалённого Mac.

Ответственный за релиз сравнивает Swift Build и native не по одному зелёному запуску, а по сборке, тестированию, подписи и проверке содержимого результата.

Первый шаг: замените жёсткий путь на запрос инструмента

Найдите в репозитории все места, где встречаются .build, debug, release, имена архитектур или вручную собранные цепочки каталогов:

grep -RInE '(\.build|debug|release|Products|DerivedData)' \
  --exclude-dir=.git \
  ./scripts ./Makefile ./fastlane 2>/dev/null || true

Команда не доказывает наличие ошибки, но быстро показывает границы аудита. Проверяйте не только Shell: путь часто скрыт в Ruby-коде Fastlane, Python-помощнике, переменной Makefile или пользовательском скрипте плагина.

Базовая безопасная схема должна выглядеть так:

#!/bin/sh
set -eu

REPO_DIR="${REPO_DIR:?REPO_DIR is required}"
CONFIGURATION="${CONFIGURATION:-debug}"
ARCHITECTURE="${ARCHITECTURE:?ARCHITECTURE is required}"
DESTINATION="${DESTINATION:?DESTINATION is required}"
PRODUCT_NAME="${PRODUCT_NAME:?PRODUCT_NAME is required}"

cd "$REPO_DIR"

BUILD_ARGS="--configuration $CONFIGURATION --arch $ARCHITECTURE"

swift build $BUILD_ARGS
BIN_PATH="$(swift build $BUILD_ARGS --show-bin-path)"

test -d "$BIN_PATH"
test -e "$BIN_PATH/$PRODUCT_NAME"

mkdir -p "$REPO_DIR/artifacts"
cp "$BIN_PATH/$PRODUCT_NAME" "$REPO_DIR/artifacts/"

В реальном проекте добавьте DESTINATION в команды, если используемый вами SwiftPM-инструментарий поддерживает этот параметр для данной задачи. Критическое правило остаётся неизменным: запрос пути должен использовать тот же набор значимых параметров, что и построение результата.

Не делайте так:

swift build --configuration release --arch arm64
BIN_PATH="$(swift build --show-bin-path)"

Здесь запрос может описывать другую конфигурацию или архитектуру. Ошибка проявится только на части задач, из-за чего её легко принять за нестабильность удалённого Mac.

Для отладки сохраняйте вывод:

{
  printf '%s\n' '--- environment ---'
  pwd
  swift --version
  xcode-select -p
  printf '%s\n' '--- bin path ---'
  swift build $BUILD_ARGS --show-bin-path
} 2>&1 | tee "$REPO_DIR/artifacts/path-diagnostic.log"

Официальный вспомогательный скрипт SourceKit-LSP также полезен как пример того, почему путь следует получать у системы сборки, а не воссоздавать локальной логикой: реализация показывает практический подход к передаче параметров и поиску результатов.

Проверьте пакет, плагины и ресурсы отдельно

Если проблема появилась только у пакета с BuildToolPlugin, CommandPlugin, бинарной целью или обрабатываемыми ресурсами, исправление одного CI-скрипта может оказаться недостаточным.

Проведите аудит по четырём направлениям.

Плагины

Проверьте, не формирует ли плагин путь конкатенацией вроде:

let output = ".build/\(configuration)/\(architecture)/tool"

Такая строка привязана к внутренней раскладке. Плагин должен использовать предоставленные ему контексты и объявлять входы и выходы так, чтобы система сборки могла корректно построить граф. Если плагин печатает собственный путь, сохраните этот вывод рядом с журналом Swift Build.

Бинарные цели

Для binary target проверяйте не предполагаемый каталог распаковки, а фактическое имя продукта и способ его передачи следующему этапу. Архив, checksum и исполняемый файл — разные артефакты; наличие одного не подтверждает корректность остальных.

Ресурсы

Ресурсы могут быть частью продукта, но не обязаны лежать рядом с бинарником в том виде, как ожидает старый скрипт. Проверяйте содержимое конечного пакета или каталога назначения, а не только существование .build.

Скриптовые плагины

Если плагин вызывает внешний инструмент, запишите его стандартный вывод, код возврата и рабочую папку. Ошибка доступа к файлу, созданному плагином, может быть следствием неверного cwd, а не прав пользователя.

**Опыт эксплуатации.** Нельзя исправлять ошибку, удаляя каталоги до тех пор, пока не сохранены фактический путь, команда и лог. Агрессивная очистка стирает именно те признаки, которые отличают неправильный путь от дефекта Swift Build.

Отдельно заведите карточку для официальных проблем Swift Build. В репозитории уже обсуждались различия выходных каталогов; например, [issue 1363 о расхождении путей](https://github.com/swiftlang/swift-build/issues/1363) нельзя заменять пересказом из форума или предположением о правах на удалённом Mac. Если ваш минимальный пример совпадает с описанной проблемой, приложите к отчёту версии, параметры, лог и ожидаемый результат.

Перестройте сбор тестов и покрытия

Тестовый этап часто маскирует проблему с путём, потому что swift test может завершиться отдельно от шага, который собирает и загружает отчёт.

Разделите проверки:

set -eu

cd "$REPO_DIR"

swift test $TEST_ARGS
TEST_STATUS=$?

printf 'swift test exit status: %s\n' "$TEST_STATUS" \
  | tee "$REPO_DIR/artifacts/test-status.log"

exit "$TEST_STATUS"

Вариант выше нужен для явной фиксации статуса; при set -e дальнейшая логика должна быть устроена так, чтобы журнал не терялся до завершения процесса. В вашем CI лучше использовать механизм публикации логов, который сохраняет файл даже при неуспешной команде.

Для каждого тестового продукта ответьте на отдельные вопросы:

  • откуда взят отчёт — из вывода тестового раннера, файла покрытия или внешнего конвертера;
  • принадлежит ли он текущему запуску;
  • создаётся ли файл при падении отдельного теста;
  • умеет ли агрегатор показать конкретный неудачный тест;
  • не объединяет ли кэш отчёт предыдущего коммита с текущим.
Не считайте тест пройденным только потому, что после запуска существует файл с ожидаемым именем. Сверяйте код возврата, идентификатор коммита и временную метку запуска, если ваша система логирования их предоставляет. Числа и сроки в этой проверке не являются универсальными параметрами SwiftPM; их следует брать из политики именно вашего CI.

В период миграции оставьте рядом две задачи — Swift Build и native — с одним и тем же коммитом, входными данными и правилами публикации. Это не означает, что native автоматически считается правильным. Его роль — контрольная линия для ограничения области поиска.

Закрепите кэш и окружение удалённого Mac

Удалённый Mac добавляет к проблеме пути факторы, которых может не быть на рабочей станции: другой пользователь, другая рабочая папка, сохранённое состояние после предыдущей задачи, отличия архитектуры и неодинаковое состояние выбранного инструментария.

В идентификатор кэша включите как минимум:

  • версию Swift-инструментария;
  • реализацию системы сборки;
  • конфигурацию;
  • архитектуру;
  • файл блокировки зависимостей;
  • идентификатор репозитория или ветки по правилам вашей платформы.
Не подставляйте в идентификатор только номер коммита, если один и тот же checkout может выполняться разными версиями инструментария. Старый кэш способен вернуть файл в ожидаемое имя, а затем скрыть тот факт, что новый процесс создаёт результат иначе.

Проверка узла перед задачей:

set -eu

printf '%s\n' '--- node ---'
id
hostname
pwd
swift --version
xcode-select -p

printf '%s\n' '--- workspace ---'
git rev-parse --show-toplevel
git status --short

printf '%s\n' '--- package ---'
swift package describe

Затем выполните три прогона:

  1. новый checkout с пустым кэшем;
  2. повторный запуск с разрешённым кэшем;
  3. тот же тест после перезапуска узла.
В каждом случае сохраняйте путь, полученный --show-bin-path, список опубликованных файлов и статус тестов. Если холодный запуск проходит, горячий ломается, ищите пересечение кэша и рабочей области. Если после перезапуска меняется результат, проверяйте состояние инструментария, пользователя, точки монтирования и очистку, а не объявляйте это «проблемой сети».

Для процедурного контроля можно использовать руководство MACGPU по удалённой среде Mac, но команды и критерии приёмки в вашем проекте должны оставаться источником истины. Когда существующий узел нельзя безопасно очищать, изолированная аренда MACGPU удобна именно как среда для холодного кэша и повторной проверки после перезапуска; это не отменяет необходимости зафиксировать версии и параметры в CI.

Сопоставьте Swift Build и native перед выпуском

Официальное сообщение о смене системы сборки объясняет, почему старые предположения требуют пересмотра; обсуждение изменения значения по умолчанию полезно читать вместе с первичной документацией, а не вместо неё.

Составьте две независимые дорожки.

Swift Build:

  • сборка продукта;
  • запрос каталога через --show-bin-path;
  • запуск тестов;
  • сборка или извлечение отчёта;
  • проверка состава результата;
  • подпись и публикация, если они относятся к вашему процессу.
**Native:**
  • те же исходные файлы;
  • та же конфигурация;
  • та же архитектура;
  • те же зависимости;
  • отдельный каталог кэша;
  • отдельная публикация с явной маркировкой.
Сравнивайте не внутренние пути, а то, что получает потребитель:
  • бинарник запускается или корректно проходит следующую проверку;
  • пакет содержит необходимые ресурсы;
  • тестовый отчёт показывает реальные неудачи;
  • подпись проверяется на чистом этапе;
  • загруженный файл соответствует текущему коммиту;
  • повторный запуск не использует случайный старый результат.
Если native проходит, а Swift Build не проходит, не переключайте производство вслепую. Сохраните минимальный репозиторий, команду, рабочую папку, версии и полный лог. Затем решите, относится ли наблюдение к задокументированному поведению, к вашему скрипту или к официальной проблеме. [Репозиторий Swift Build](https://github.com/swiftlang/swift-build) и [официальный репозиторий SwiftPM](https://github.com/swiftlang/swift-package-manager) должны быть основными точками сверки изменений.

Статус Swift 6.4 нельзя выдавать за независимый стабильный релиз, пока это не подтверждено официальным каналом. Текущее состояние отслеживайте в Swift Evolution, а материалы Apple используйте для проверки связанного инструментария и статуса Xcode 27. Публикации СМИ могут подсказать направление поиска, но не доказывают конкретное поведение команды, дату релиза или совместимость.

Проведите приёмку по ролям

Перед разрешением миграции в производственный поток отметьте каждый пункт:

  • [ ] В логе сохранены полный вызов сборки, рабочая папка и версия инструментария.
  • [ ] Успешный код возврата сборки проверяется отдельно от поиска файла.
  • [ ] Скрипт больше не конструирует внутренний путь .build вручную.
  • [ ] swift build --show-bin-path вызывается с теми же параметрами, что и сборка.
  • [ ] Бинарник, ресурсы, тестовый отчёт и архив проверяются как разные типы результата.
  • [ ] Плагины не используют неописанную иерархию каталогов.
  • [ ] В кэш-ключ включены версия инструментария, система сборки, архитектура, конфигурация и lock-файл.
  • [ ] Холодный кэш проверен на новом checkout.
  • [ ] Горячий кэш проверен отдельным запуском.
  • [ ] После перезапуска удалённого Mac результат воспроизводится.
  • [ ] Swift Build и native сопоставлены на одном коммите.
  • [ ] Для известной проблемы подготовлен минимальный пример и полный журнал.
  • [ ] Указано условие возврата к Swift Build после временной диагностики.
Оценивать решение удобно по пяти критериям: корректность пути, чистота кэша, воспроизводимость узла, полнота тестовых доказательств и готовность отката. Если хотя бы один критерий подтверждён только единичным удачным запуском, миграция ещё не завершена.

FAQ: типовые случаи после обновления

После обновления бинарный файл пропал из .build. Что проверять первым? Сначала сравните код возврата сборки с ошибкой последующего шага, затем выведите pwd и вызовите swift build --show-bin-path с теми же параметрами. Если каталог отличается от ожидаемого, исправляйте скрипт и кэш. Не начинайте с выдачи дополнительных прав и не удаляйте рабочую область до сохранения полного лога.

Можно ли просто вернуть native в качестве постоянного решения? Для диагностики — да, если вы оставляете отдельный кэш и идентичные входные данные. Для производства — только после явной проверки требований к тестам, подписи, ресурсам и публикации. Успех native не доказывает неисправность Swift Build; это контрольная ветка, которая помогает определить границу проблемы и подготовить обоснованный временный откат.

Почему локальная сборка проходит, а CI на удалённом Mac не находит архив? Локальный и удалённый процессы могут использовать разные рабочие каталоги, аккаунты, архитектуры, выбранные инструментарии и сохранённые кэши. Сначала сохраните фактический путь и окружение на узле, затем повторите задачу с холодным кэшем и после перезапуска. Только после этого проверяйте права и передачу архива в систему публикации.

Как понять, что исправлен именно путь, а не случайно использован старый результат? Удалите или изолируйте кэш, выполните новый checkout, получите путь командой, соберите продукт и проверьте содержимое каталога перед копированием. Повторите процесс с горячим кэшем и после перезапуска узла. В опубликованный набор добавьте идентификатор коммита или другой проверяемый признак, чтобы старый файл не выглядел успешным результатом.

Что делать с текущей схемой и когда выбрать удалённый Mac

Если ваш текущий CI-узел продолжает жёстко ссылаться на .build, смешивает кэши разных инструментальных цепочек и запускает публикацию из непредсказуемой рабочей папки, он будет ломаться повторно при следующем изменении SwiftPM. Локальный Mac дополнительно связывает процесс с одной машиной, ручной очисткой и ограниченной доступностью для ночных или параллельных проверок. Linux-сервер не заменяет macOS-инструментарий там, где нужны SwiftPM на Mac, подпись или проверка Apple-специфичного результата.

После исправления скрипта разумно прогнать холодный кэш и сценарий восстановления на отдельной, сбрасываемой машине. Для временной миграции, воспроизводимой диагностики или постоянно доступного узла можно рассмотреть аренду Mac с помесячными вариантами в MACGPU. Это не лучший выбор для команды, которой нужен один физический интерфейс, локальная периферия или предсказуемая машина под длительную тяжёлую нагрузку без изменений. Но для изолированного CI, проверки SwiftPM 6.4 и повторного запуска после перезагрузки удалённый Mac обычно снимает зависимость от конкретного рабочего места, сохраняя SSH-доступ и полный контроль над окружением.