Симптом: команда сборки завершается успешно, но следующий шаг CI сообщает, что бинарник или архив не найден. Самое быстрое исправление — прекратить угадывать структуру .build и получать каталог текущей задачи через swift build --show-bin-path, передавая ему те же конфигурацию, архитектуру и назначение, что и самой сборке.
Этот порядок подходит тем, кто поддерживает Shell-, Fastlane- или CI-скрипты после обновления SwiftPM 6.4. Он также нужен авторам Swift Package и плагинов, тест-инженерам и DevOps-специалистам, отвечающим за кэш, удалённый Mac и откат инструментария.
Последнее обновление: 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, изменении параметра отката либо закрытии соответствующей официальной проблемы.**Важно.** На 27 августа 2026 года официальные материалы SwiftPM описывают Swift Build как систему сборки по умолчанию и отдельно рекомендуют получать каталог бинарных результатов через
--show-bin-path. Статус Swift 6.4 и состояние Xcode 27 нужно повторно проверить перед выпуском: не следует превращать предварительные материалы или сообщения СМИ в гарантию совместимости.
Сначала отделите сбой сборки от сбоя поиска
Код возврата команды и наличие конечного файла отвечают на разные вопросы. Если swift build завершился успешно, но шаг загрузки сообщает No such file or directory, первичный подозреваемый — не компилятор, а логика поиска, рабочий каталог, очистка или кэш.
Зафиксируйте в одном диагностическом артефакте:
- полный вызов
swift buildвместе со всеми флагами; - рабочий каталог, полученный через
pwd; - вывод
swift --versionиswift package --version; - активное состояние
xcode-select; - фактический результат команды
--show-bin-path; - имя ожидаемого бинарника, тестового отчёта или архива;
- код возврата каждого этапа;
- пользователя, от имени которого работает CI.
- Компиляция не прошла. У команды сборки ненулевой код возврата, поэтому искать артефакт бессмысленно.
- Путь вычислен неверно. Сборка успешна, но скрипт обращается к старому подкаталогу
.build. - Рабочая область не совпадает. Сборка запущена из одного checkout-каталога, а упаковка или загрузка — из другого.
Что именно потребляет каждая роль
Владелец 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. В репозитории уже обсуждались различия выходных каталогов; например, [issue 1363 о расхождении путей](https://github.com/swiftlang/swift-build/issues/1363) нельзя заменять пересказом из форума или предположением о правах на удалённом Mac. Если ваш минимальный пример совпадает с описанной проблемой, приложите к отчёту версии, параметры, лог и ожидаемый результат.**Опыт эксплуатации.** Нельзя исправлять ошибку, удаляя каталоги до тех пор, пока не сохранены фактический путь, команда и лог. Агрессивная очистка стирает именно те признаки, которые отличают неправильный путь от дефекта Swift Build.
Перестройте сбор тестов и покрытия
Тестовый этап часто маскирует проблему с путём, потому что 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 лучше использовать механизм публикации логов, который сохраняет файл даже при неуспешной команде.
Для каждого тестового продукта ответьте на отдельные вопросы:
- откуда взят отчёт — из вывода тестового раннера, файла покрытия или внешнего конвертера;
- принадлежит ли он текущему запуску;
- создаётся ли файл при падении отдельного теста;
- умеет ли агрегатор показать конкретный неудачный тест;
- не объединяет ли кэш отчёт предыдущего коммита с текущим.
В период миграции оставьте рядом две задачи — Swift Build и native — с одним и тем же коммитом, входными данными и правилами публикации. Это не означает, что native автоматически считается правильным. Его роль — контрольная линия для ограничения области поиска.
Закрепите кэш и окружение удалённого Mac
Удалённый Mac добавляет к проблеме пути факторы, которых может не быть на рабочей станции: другой пользователь, другая рабочая папка, сохранённое состояние после предыдущей задачи, отличия архитектуры и неодинаковое состояние выбранного инструментария.
В идентификатор кэша включите как минимум:
- версию Swift-инструментария;
- реализацию системы сборки;
- конфигурацию;
- архитектуру;
- файл блокировки зависимостей;
- идентификатор репозитория или ветки по правилам вашей платформы.
Проверка узла перед задачей:
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
Затем выполните три прогона:
- новый checkout с пустым кэшем;
- повторный запуск с разрешённым кэшем;
- тот же тест после перезапуска узла.
--show-bin-path, список опубликованных файлов и статус тестов. Если холодный запуск проходит, горячий ломается, ищите пересечение кэша и рабочей области. Если после перезапуска меняется результат, проверяйте состояние инструментария, пользователя, точки монтирования и очистку, а не объявляйте это «проблемой сети».
Для процедурного контроля можно использовать руководство MACGPU по удалённой среде Mac, но команды и критерии приёмки в вашем проекте должны оставаться источником истины. Когда существующий узел нельзя безопасно очищать, изолированная аренда MACGPU удобна именно как среда для холодного кэша и повторной проверки после перезапуска; это не отменяет необходимости зафиксировать версии и параметры в CI.
Сопоставьте Swift Build и native перед выпуском
Официальное сообщение о смене системы сборки объясняет, почему старые предположения требуют пересмотра; обсуждение изменения значения по умолчанию полезно читать вместе с первичной документацией, а не вместо неё.
Составьте две независимые дорожки.
Swift Build:
- сборка продукта;
- запрос каталога через
--show-bin-path; - запуск тестов;
- сборка или извлечение отчёта;
- проверка состава результата;
- подпись и публикация, если они относятся к вашему процессу.
- те же исходные файлы;
- та же конфигурация;
- та же архитектура;
- те же зависимости;
- отдельный каталог кэша;
- отдельная публикация с явной маркировкой.
- бинарник запускается или корректно проходит следующую проверку;
- пакет содержит необходимые ресурсы;
- тестовый отчёт показывает реальные неудачи;
- подпись проверяется на чистом этапе;
- загруженный файл соответствует текущему коммиту;
- повторный запуск не использует случайный старый результат.
Статус 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-доступ и полный контроль над окружением.