Правила упаковки Golang

В этом документе описаны лучшие практики упаковки пакетов Golang.

Предыдущая версия этих правил предполагала создание отдельных пакетов -devel, содержащих исходный код Go для каждой библиотечной зависимости. Начиная с Changes/GolangPackagesVendoredByDefault, новые пакеты Go ДОЛЖНЫ собираться с встроенными (vendored) зависимостями, как описано в текущей версии правил.

go2rpm

go2rpm — это инструмент, автоматизирующий многие из этих шагов. Прежде чем пытаться написать SPEC вручную, рекомендуется сначала попробовать go2rpm --name NAME --profile vendor IMPORT_PATH. go2rpm создаст spec-файл, соответствующий правилам, загрузит исходные коды upstream, создаст архив vendor с помощью go_vendor_archive из Go Vendor Tools, а затем использует go_vendor_license для сканирования исходных кодов upstream и встроенных зависимостей на наличие файлов лицензий, запросит у пользователя любые лицензии, которые не смог определить, и затем сгенерирует совокупное выражение SPDX.

Путь импорта

В Golang пакеты обозначаются полными URL-адресами, перечисленными в файле go.mod проекта.

%global goipath     github.com/kr/pretty

Именование

Новые пакеты библиотек Golang больше не допускаются, поэтому все пакеты Golang являются приложениями, ориентированными на пользователя, и ДОЛЖНЫ называться в соответствии со стандартными правилами именования. В частности, пакеты Go с встроенными зависимостями НЕ ДОЛЖНЫ иметь префикс golang-, если только это не является частью имени upstream проекта.

Это также относится к существующим пакетам. При преобразовании пакета с префиксом golang-, созданного по старым правилам, для использования встроенных зависимостей пакет ДОЛЖЕН пройти процесс переименования пакета. Это правило направлено на создание чёткого разделения между пакетами, созданными по старому подходу, и новым методом со встроенными зависимостями, а также на предотвращение случаев, когда подпакеты -devel удаляются из существующих пакетов golang- и затем возвращаются в стабильные ветви.

Архитектуры языка Go

Для компиляции на различных архитектурах доступны компиляторы golang и gcc-go. Компилятор golang в настоящее время поддерживает x86, x86_64, ppc64le, ppc64 (частично, см. issue#13192 upstream), s390x, armv7hl и aarch64.

Двоичные пакеты ДОЛЖНЫ устанавливать ExclusiveArch, чтобы мы пытались собирать пакеты только на этих архитектурах. Теперь это автоматически добавляется макросом %gometa с использованием макроса %{golang_arches}. Упаковщики могут исключить %ix86 (см. Changes/EncourageI686LeafRemoval), передав -f макросу %gometa. Флаг -f указывает %gometa установить ExclusiveArch: %{golang_arches_future} вместо ExclusiveArch: %{golang_arches}. %{golang_arches_future} включает те же архитектуры, что и {golang_arches}, за исключением %ix86.

Флаги компилятора Go

Сохранение флагов компилятора

Пакеты ДОЛЖНЫ сохранять флаги компилятора golang Fedora, используя макрос %gobuild или передавая соответствующие макросы %gobuild_*flags в сборочный скрипт upstream.

# Using %gobuild
%gobuild -o %{gobuilddir}/bin/%{name} %{goipath}

# Using a simple upstream build script
%make_build BUILD_OPTS=%{gobuild_baseflags_shescaped}

# Using an upstream build script that provides separate options for ldflags
%make_build \
  BUILD_OPTS=%{gobuild_baseflags_shescaped} \
  GO_LDFLAGS=%{gobuild_ldflags_shescaped}

Дополнительную информацию о передаче флагов компилятора в сборочный скрипт upstream см. во встроенных комментариях в macros.go-compilers-golang.

Рекомендуется использовать макрос %gobuild, если это возможно. Некоторые сборочные скрипты upstream могут не предоставлять надлежащего способа передачи дополнительных флагов компилятору или иметь другие проблемы, которые могут быть источником ошибок. При использовании %gobuild обязательно задайте соответствующие флаги компоновщика и теги сборки, как описано ниже.

Передача дополнительных флагов

Go поддерживает передачу дополнительных флагов компоновщика (например, для включения функциональности --version) и тегов сборки для условной компиляции. При использовании сборочного скрипта upstream они могут устанавливаться автоматически. В противном случае упаковщик должен задать их вручную.

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

Флаги компоновщика

В некоторых случаях может потребоваться передать дополнительные флаги компоновщику Go. Это можно сделать, установив переменную окружения $GO_LDFLAGS, которая затем будет прочитана макросами.

# The correct value to pass to -X differs between projects; this is an example.
export GO_LDFLAGS="-X %{goipath}/internal.Version=%{version}"

Пакеты НЕ ДОЛЖНЫ задавать флаги компоновщика Go с помощью переменной окружения $LDFLAGS. Это поддерживается для обратной совместимости, но устарело.

Теги сборки

Go поддерживает теги сборки для условного включения или исключения кода из сборки. Чтобы передать теги сборки компилятору Go, установите $GO_BUILDTAGS в список тегов, разделённых пробелами.

# These tags are just examples. Each project has its own.
export GO_BUILDTAGS="systemd selinux"

Пакеты НЕ ДОЛЖНЫ задавать теги Go с помощью переменной окружения $BUILDTAGS.

Зависимости макросов

Пакеты ДОЛЖНЫ иметь BuildRequires: go-rpm-macros, чтобы подтянуть макросы и компилятор Go.

Это автоматизировано макросом %gometa.

Пакеты, использующие макросы %go_vendor_license_*, ДОЛЖНЫ иметь BuildRequires: go-vendor-tools.

Вендоренные зависимости

Пакеты ДОЛЖНЫ включать свои зависимости Go-модулей как встроенные. Упаковщикам СЛЕДУЕТ использовать команду go_vendor_archive из Go Vendor Tools для создания воспроизводимого архива vendor. Пакеты, не использующие Go Vendor Tools, должны включать скрипт или другую стандартизированную, документированную процедуру для загрузки исходных кодов с помощью go mod vendor и воспроизводимого создания архива. Упаковщикам СЛЕДУЕТ самостоятельно пересоздавать архивы vendor, даже если upstream включает каталог vendor в свои исходные коды. Это позволяет упростить обновления безопасности с помощью Go Vendor Tools.

Традиционно Source0 в spec-файле — это основной архив, а Source1 — архив vendor, и их значения автоматически заполняются с помощью макросов forge.

Source0:        %{gosource}
# %%archivename is the basename as the archive provided
# by %%gosource without the extension.
Source1:        %{archivename}-vendor.tar.bz2
# go-vendor-tools configuration generated by go2rpm
Source2:        go-vendor-tools.toml

Затем выполните go_vendor_archive create <PACKAGE_NAME>.spec, чтобы создать архив vendor.

Предоставления встроенных пакетов

Пакеты ДОЛЖНЫ включать bundled(golang(IMPORT_PATH)) = VERSION Provides для всех встроенных Go-модулей. Это обрабатывается автоматически генератором зависимостей. Он работает с любым файлом modules.txt в каталоге лицензий. Отметьте файл vendor/modules.txt с помощью %license в %files, и затем генератор просканирует файл и создаст bundled() Provides. vendor/modules.txt по умолчанию включён в go_vendor_license_filelist (см. Установка лицензий), поэтому большинству пакетов не потребуется делать это вручную.

# Only necessary to include explicitly when not using %%go_vendor_license_filelist
%license vendor/modules.txt

Лицензирование

Пакеты должны следовать правилам лицензирования Fedora. Для пакетов Go со встроенными зависимостями это означает, что файлы лицензий основного проекта, а также всех встроенных Go-модулей ДОЛЖНЫ быть включены в пакет и помечены %license. Каждый встроенный Go-модуль ДОЛЖЕН включать файл лицензии; Go-модули, у которых отсутствуют файлы лицензий, НЕ ДОЛЖНЫ включаться во встроенные архивы до выяснения вопросов лицензирования.

Кроме того, тег License: пакета ДОЛЖЕН включать совокупное выражение SPDX, охватывающее как основной пакет, так и встроенные Go-модули.

Лицензирование с помощью Go Vendor Tools

Go Vendor Tools предоставляет команду и макросы go_vendor_license, помогающие определить правильное выражение License: и установить файлы лицензий, как предписано в предыдущем разделе.

Упаковщики ДОЛЖНЫ выполнить go_vendor_license report (напрямую, через go2rpm или с использованием макроса %go_vendor_license_check в локальной сборке mock) и перепроверить его вывод перед загрузкой исходных кодов в lookaside-кэш. Любые ошибки ДОЛЖНЫ быть устранены. Если какая-либо часть вывода неверна, поведение go_vendor_license можно изменить в разделе лицензирования файла конфигурации Go Vendor Tools.

Этот документ описывает только стандартное использование Go Vendor Tools, необходимое для соблюдения правил — обратитесь к документации Go Vendor Tools для получения подробной информации о продвинутом использовании. Упаковщики могут следовать документации по сценариям, чтобы создать новый spec-файл с помощью go2rpm или обновить существующие spec-файлы для новых версий upstream, чтобы запустить go_vendor_license, убедиться, что лицензия действительна, и при необходимости автоматически обновить её перед запуском полной сборки.

Хотя Go Vendor Tools предоставляет средства для сканирования лицензий и создания выражений SPDX, ответственность за выполнение базовой проверки вывода и обеспечение соответствия правилам лицензирования Fedora, включая проверку того, что все ключи в теге License являются разрешёнными лицензиями в Fedora, по-прежнему лежит на упаковщике, прежде чем загружать какие-либо исходные коды в lookaside-кэш.

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

go-vendor-tools.toml

Пакеты ДОЛЖНЫ включать файл go-vendor-tools.toml для указания бэкенда детектора лицензий и других параметров конфигурации. Дополнительную информацию см. в справочнике по конфигурации. Рекомендуемый подход — использовать go2rpm, который автоматически создаёт корректный go-vendor-tools.toml. Минимальная конфигурация выглядит так:

[licensing]
detector = "askalono"

Этот файл используется макросами и традиционно включается в spec-файл как Source2:, сразу под архивом upstream и tar-архивом, созданным go_vendor_archive. В этом документе %{S:2} (разворачивается в путь к Source2) будет использоваться для обозначения пути к файлу конфигурации Go Vendor Tools.

Установка go-vendor-tools

Пакеты, использующие макросы Go Vendor Tools, ДОЛЖНЫ иметь BuildRequires: go-vendor-tools и использовать макрос %go_vendor_license_buildrequires для генерации требований, необходимых для выбранного бэкенда детектора лицензий.

BuildRequires:  go-vendor-tools
%generate_buildrequires
%go_vendor_license_buildrequires -c %{S:2}

Установка лицензий

Упаковщикам СЛЕДУЕТ использовать макрос %go_vendor_license_install для установки файлов лицензий основного проекта и всех встроенных Go-модулей. По умолчанию этот макрос устанавливает файлы лицензий в каталог лицензий основного пакета. Макрос также копирует файл vendor/modules.txt в каталог лицензий, чтобы включить генератор bundled() Golang (см. Предоставления встроенных пакетов).

# Install into the main package's license directory
%go_vendor_license_install -c %{S:2}

Затем макрос заполнит %{go_vendor_license_filelist} списком файлов, которые можно передать в %files -f.

%files -f %{go_vendor_license_filelist}

Проверка лицензий

Как указано, упаковщики ДОЛЖНЫ проверять лицензии с помощью Go Vendor Tools перед загрузкой исходных кодов в lookaside-кэш. Любые ошибки, вызванные go_vendor_license, ДОЛЖНЫ быть устранены.

В качестве дополнительной меры макрос %go_vendor_license_check выполняет то же сканирование лицензий, что и команда go_vendor_license report. Пакетам СЛЕДУЕТ использовать go_vendor_license_check в %check, чтобы сборка пакета завершилась неудачей при наличии ошибок лицензирования. Go Vendor Tools сканирует исходные коды upstream и встроенные библиотеки на наличие файлов лицензий и генерирует совокупное выражение SPDX. Кроме того, макрос проверяет, что выражение в теге License пакета эквивалентно (независимо от порядка или возможного упрощения выражения) тому, что ожидает go_vendor_license report.

# Scan licenses and verify that the License tag is equivalent to what
# go_vendor_license calculates.
%go_vendor_license_check -c %{S:2}

# The macro can also compare a license expression stored in a macro with
# go_vendor_license's output.
%go_vendor_license_check -c %{S:2} %{go_licenses}

В случае отсутствия лицензий

go_vendor_license проверяет, отсутствует ли файл лицензии у какого-либо Go-модуля. Опять же, Go-модули, у которых отсутствуют файлы лицензий, НЕ ДОЛЖНЫ включаться во встроенные архивы до исправления ситуации в upstream.

В некоторых случаях go mod vendor может не загрузить файл лицензии проекта, даже если он существует для соответствующей версии в репозитории upstream. Обычно это происходит потому, что файлы лицензий имеют нестандартные имена или потому что лицензии установлены в подкаталоге вместо корня модуля. В первом случае go_vendor_archive МОЖЕТ быть настроен с помощью post_commands для загрузки файла лицензии из репозитория upstream в качестве временной меры, но упаковщики ДОЛЖНЫ сообщить об этом upstream и попросить переименовать файл лицензии в формат, поддерживаемый go mod vendor. Если файл лицензии включён в подкаталог модуля, файл следует скопировать в корневой каталог, чтобы удовлетворить средство проверки лицензий Go Vendor Tools.

Тестирование

Вам СЛЕДУЕТ запускать модульные тесты.

Некоторые тесты могут быть отключены, особенно следующие виды модульных тестов несовместимы с безопасной средой сборки, такой как mock:

  • тесты, которые вызывают удалённый сервер или API через Интернет,

  • тесты, которые пытаются перенастроить систему,

  • тесты, которые полагаются на конкретное приложение, работающее в системе, например базу данных или сервер syslog.

Если тест сломан по какой-то другой причине, вы можете отключить его таким же образом. Однако вам также СЛЕДУЕТ сообщить о проблеме в upstream. Не забудьте проследить в комментарии, почему каждая проверка была отключена, со ссылками на возможные отчёты о проблемах в upstream.

Тесты можно запускать с помощью макроса %gocheck2, который внутренне вызывает go test, сохраняя флаги сборки Fedora и предоставляя дополнительные параметры для пропуска определённых тестов.

Пакеты НЕ ДОЛЖНЫ использовать устаревший макрос %gocheck.

Режим Go-модулей

Пакетам СЛЕДУЕТ включить режим Go-модулей, включив %global gomodulesmode GO111MODULE=on в spec-файл для установки переменной окружения GO111MODULE. Обычно это определение включается в начале %build перед вызовом %gobuild.

Go-модули — это система, которую go использует для управления зависимостями. Режим Go-модулей заменяет режим $GOPATH (предыдущую систему управления зависимостями). Go можно настроить на использование модулей или режима $GOPATH, установив переменную окружения $GO111MODULE в on, off или auto. auto — это значение по умолчанию в upstream. По умолчанию %gobuild в Fedora устанавливает GO111MODULE=off для совместимости с устаревшими правилами упаковки Golang, но этот подход не рекомендуется для новых пакетов, собираемых со встроенными зависимостями. Следует использовать режим модулей, чтобы %gobuild читал метаданные модулей и включал метаданные о зависимостях пакета в бинарный файл, а также чтобы убедиться, что Go embed работает правильно.

Макросы

RPM-макросы, предоставляемые go-vendor-tools, используются для проверки и установки лицензий. См. документацию по RPM-макросам Go Vendor Tools и Лицензирование с помощью Go Vendor Tools.

go-rpm-macros в первую очередь отвечает за макрос %gobuild, который вызывает go build с флагами сборки Fedora.

go-rpm-macros также предоставляет обёртки вокруг макросов Forge из пакета forge-srpm-macros, которые упрощают указание URL источника и распаковку исходных кодов для проектов Go, размещённых на распространённых программных площадках, таких как Github. Эти макросы включают %gometa, %gosource и %goprep. Упаковщики могут использовать эти макросы вместо указания источников и ручного вызова %autosetup / %setup. См. отдельную документацию и пример spec-файла в этих правилах для получения дополнительной информации.

Пример

Конфигурация go-vendor-tools

Эти записи были созданы автоматически go2rpm. Все пакеты Go ДОЛЖНЫ иметь файл go-vendor-tools.toml, зафиксированный в distgit вместе с spec-файлом.

go-vendor-tools.toml
[archive]

[licensing]
detector = "askalono"

[[licensing.licenses]]
path = "vendor/github.com/google/shlex/COPYING"
sha256sum = "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"
expression = "Apache-2.0"

[[licensing.licenses]]
path = "vendor/github.com/jwalton/gchalk/LICENSE-chalk"
sha256sum = "44e453533edb9f1c037cb260c58f66f1d9b2e7823a07407cd6d04320e3925fea"
expression = "MIT"

[[licensing.licenses]]
path = "vendor/github.com/jwalton/gchalk/pkg/ansistyles/LICENSE-ansi-styles"
sha256sum = "310f4b4de77142b34acc5a58de93558fde5dea75891c7822b4086f71372ec983"
expression = "MIT"

[[licensing.licenses]]
path = "vendor/github.com/jwalton/go-supportscolor/LICENSE"
sha256sum = "892282511d65ac08025fbabcd8af330d0aa94e459d81a818251ff5a934383816"
expression = "MIT"

[[licensing.licenses]]
path = "vendor/gopkg.in/yaml.v3/LICENSE"
sha256sum = "d18f6323b71b0b768bb5e9616e36da390fbd39369a81807cca352de4e4e6aa0b"
expression = "MIT AND (MIT AND Apache-2.0)"

Spec-файл с обёртками макросов forge

Это метод по умолчанию, используемый go2rpm, и текущее соглашение для пакетов Go.

ov.spec
# Generated by go2rpm 1.17.1 (with extra code comments added manually)
%bcond check 1

# https://github.com/noborus/ov
%global goipath         github.com/noborus/ov
Version:                0.43.0

%gometa -L -f

Name:           ov
Release:        %autorelease
Summary:        Feature-rich terminal-based text viewer

# Generated by go-vendor-tools
License:        Apache-2.0 AND BSD-3-Clause AND MIT AND MPL-2.0
URL:            %{gourl}
Source0:        %{gosource}
# Generated by go-vendor-tools
Source1:        %{archivename}-vendor.tar.bz2
# Go Vendor Tools configuration generated by go2rpm
Source2:        go-vendor-tools.toml

BuildRequires:  go-vendor-tools

%description
Feature-rich terminal-based text viewer. It is a so-called terminal pager.

%prep
# Unpack upstream sources (Source0) and apply patches if they exist.
# This also creates the %%{gobuilddir} directory used to store the binary built
# during %%build.
%goprep -p1
# Unpack the vendor archive in Source1.
tar -xf %{S:1}

%generate_buildrequires
# Install license scanner dependencies.
%go_vendor_license_buildrequires -c %{S:2}

%build
# Enable Go modules mode as required by the Guidelines.
%global gomodulesmode GO111MODULE=on
# Set version in binary. The exact value to pass to -X differs by project.
export GO_LDFLAGS="-X main.Version=%{version}"
# Build the binary
%gobuild -o %{gobuilddir}/bin/ov %{goipath}
# Generate shell completions
%{gobuilddir}/bin/%{name} --completion bash > %{name}.bash
%{gobuilddir}/bin/%{name} --completion fish > %{name}.fish
%{gobuilddir}/bin/%{name} --completion zsh  > %{name}.zsh

%install
# Install license files
%go_vendor_license_install -c %{S:2}

# Install binaries built during %%build
install -m 0755 -vd                     %{buildroot}%{_bindir}
install -m 0755 -vp %{gobuilddir}/bin/* %{buildroot}%{_bindir}/

# Install shell completions generated during %%build
install -Dpm 0644 ov.bash %{buildroot}%{bash_completions_dir}/ov
install -Dpm 0644 ov.fish %{buildroot}%{fish_completions_dir}/ov.fish
install -Dpm 0644 ov.zsh  %{buildroot}%{zsh_completions_dir}/_ov

%check
# Perform license check
%go_vendor_license_check -c %{S:2}
# Run Go unit tests
%if %{with check}
%gocheck2
%endif

%files -f %{go_vendor_license_filelist}
%doc README.md
%{_bindir}/ov
%{bash_completions_dir}/ov
%{fish_completions_dir}/ov.fish
%{zsh_completions_dir}/_ov

%changelog
%autochangelog

Spec-файл с ручными определениями

Приведённый выше подход используется по умолчанию в go2rpm, но пакеты Go можно также собирать без макросов forge. Обязательно включите ручную зависимость от go-rpm-macros и соответствующий вызов ExclusiveArch.

ov.spec
%bcond check 1

Name:           ov
Version:        0.43.0
Release:        %autorelease
Summary:        Feature-rich terminal-based text viewer
# Generated by go-vendor-tools
License:        Apache-2.0 AND BSD-3-Clause AND MIT AND MPL-2.0
URL:            https://github.com/noborus/ov
Source0:        %{url}/archive/v%{version}/ov-%{version}.tar.gz
# Generated by go-vendor-tools
Source1:        ov-%{version}-vendor.tar.bz2
Source2:        go-vendor-tools.toml

ExclusiveArch:  %{golang_arches_future}
BuildRequires:  go-rpm-macros
BuildRequires:  go-vendor-tools

%description
Feature-rich terminal-based text viewer. It is a so-called terminal pager.

%prep
# Unpack upstream sources (Source0) and apply patches if they exist.
%autosetup -p1
# Unpack the vendor archive in Source1.
tar -xf %{S:1}

%generate_buildrequires
# Install license scanner dependencies.
%go_vendor_license_buildrequires -c %{S:2}

%build
# Enable Go modules mode as required by the Guidelines.
%global gomodulesmode GO111MODULE=on
# Set version in binary. The exact value to pass to -X differs by project.
export GO_LDFLAGS="-X main.Version=%{version}"
# Build the binary
%gobuild -o ov .
# Generate shell completions
./ov --completion bash > ov.bash
./ov --completion fish > ov.fish
./ov --completion zsh  > ov.zsh

%install
# Install license files
%go_vendor_license_install -c %{S:2}

# Install binaries built during %%build
install -Dp ./ov -t %{buildroot}%{_bindir}

# Install shell completions generated during %%build
install -Dpm 0644 ov.bash %{buildroot}%{bash_completions_dir}/ov
install -Dpm 0644 ov.fish %{buildroot}%{fish_completions_dir}/ov.fish
install -Dpm 0644 ov.zsh  %{buildroot}%{zsh_completions_dir}/_ov

%check
# Perform license check
%go_vendor_license_check -c %{S:2}
# Run Go unit tests
%if %{with check}
%gocheck2
%endif

%files -f %{go_vendor_license_filelist}
%doc README.md
%{_bindir}/ov
%{bash_completions_dir}/ov
%{fish_completions_dir}/ov.fish
%{zsh_completions_dir}/_ov

%changelog
%autochangelog