build: regenerate CREDITS from the linked module set and guard it in CI

The shipped CREDITS predated the fork's dependency work: 38 of the
modules in the current go.mod, among them lestrrat-go/jwx, go-tpm,
go-spiffe and the charmbracelet family, had no entry at all, while
entries lingered for modules no longer in the build. An attribution
file that silently drifts from go.mod is worse than none, so it is now
generated, not curated.

buildscripts/gen-credits.sh (make credits) rebuilds the file from the
licenses of every module go list -deps reports for the main package --
the set actually linked into the silo binary, so test-only and tool
dependencies stay out. Entries keep the established name / URL /
license-text layout. New over the old file:

- Modules replaced in go.mod are annotated with the repository that
  actually serves them, so the pgsty/silo-console, pgsty/mc and
  pgsty/silo-pkg forks are named next to their upstream import paths.
- Bundled NOTICE files are reproduced after the license text, which
  Apache License 2.0 section 4(d) requires when redistributing; 23
  modules carry one.
- minio/colorjson, minio/csvparser and minio/filepath publish no
  license file at all; they repackage Go standard library code and
  their sources carry the Go Authors' BSD-style header, so the Go
  project license is reproduced for them with a note saying why.
- The Go license text itself comes from the pinned golang.org/x/sys
  module rather than GOROOT, because Homebrew's Go omits
  GOROOT/LICENSE and the module copy is version-locked.

check-gen now runs the generator and fails on a CREDITS diff, the same
treatment go.mod and go.sum already get, so dependency changes cannot
leave stale attributions behind. Output is deterministic: two runs are
byte-identical, and LC_ALL=C sorting plus version-pinned inputs keep it
that way across machines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
Feng Ruohang
2026-08-06 15:03:09 +08:00
parent a6d6d9b028
commit 6bd9cf77ef
3 changed files with 4006 additions and 5929 deletions
+3881 -5928
View File
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -43,10 +43,14 @@ rebrand-guard: ## verify Silo branding and protected compatibility identifiers
@env bash $(PWD)/buildscripts/verify-rebrand.sh
@env bash $(PWD)/dockerscripts/docker-entrypoint_test.sh
credits: ## regenerate CREDITS from the licenses of Go modules linked into the binary
@env bash $(PWD)/buildscripts/gen-credits.sh
check-gen: ## check for updated autogenerated files
@go generate ./... >/dev/null
@go mod tidy -compat=1.26
@changed=$$(git diff --name-only -- '*_gen.go' '*_gen_test.go' '*_msgp_test.go' '*_string.go' go.mod go.sum); \
@env bash $(PWD)/buildscripts/gen-credits.sh
@changed=$$(git diff --name-only -- '*_gen.go' '*_gen_test.go' '*_msgp_test.go' '*_string.go' go.mod go.sum CREDITS); \
if [ -n "$$changed" ]; then \
echo "Non-committed generated changes detected:"; \
echo "$$changed"; \
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/env bash
set -euo pipefail
# Regenerates CREDITS from the license text of every Go module linked into the
# silo binary. The module set is what `go list -deps` reports for the main
# package, so test-only and tool dependencies stay out: CREDITS documents what
# a shipped binary actually contains. check-gen runs this and fails on a diff,
# which keeps CREDITS from drifting when go.mod changes.
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_dir="$(cd "${script_dir}/.." && pwd)"
cd "${repo_dir}"
out_file="${1:-${repo_dir}/CREDITS}"
tmp_file="${out_file}.tmp"
trap 'rm -f "${tmp_file}"' EXIT
rule_dash='----------------------------------------------------------------'
rule_equal='================================================================'
# These modules repackage Go standard library code and publish no license
# file; their source files carry the Go Authors' BSD-style header pointing at
# the Go project license, so that text is reproduced for them.
stdlib_derived='github.com/minio/colorjson github.com/minio/csvparser github.com/minio/filepath'
is_stdlib_derived() {
case " ${stdlib_derived} " in
*" $1 "*) return 0 ;;
*) return 1 ;;
esac
}
# Command substitution strips trailing newlines; printf adds exactly one back,
# so every entry ends the same way regardless of how the license file ends.
emit_text() {
printf '%s\n' "$(cat "$1")"
}
# The module cache must hold every dependency before .Dir can resolve.
go mod download
# The Go project license text is taken from the pinned golang.org/x/sys module
# rather than GOROOT: Homebrew's Go does not ship GOROOT/LICENSE, and the
# module copy is version-locked by go.mod, so the output cannot vary with the
# machine's toolchain packaging.
go_license="$(go list -m -f '{{.Dir}}' golang.org/x/sys)/LICENSE"
if [ ! -f "${go_license}" ]; then
echo "Missing Go license text: ${go_license}" >&2
exit 1
fi
{
printf '%s\n' \
'Silo bundles third-party software under the licenses reproduced below.' \
'Generated by buildscripts/gen-credits.sh (make credits) from the Go' \
'modules linked into the silo binary. Do not edit by hand.' \
'' \
"${rule_equal}" \
''
printf '%s\n%s\n%s\n' 'Go (the standard library)' 'https://golang.org/' "${rule_dash}"
emit_text "${go_license}"
printf '\n%s\n\n' "${rule_equal}"
go list -deps -f '{{if and (not .Standard) .Module}}{{.Module.Path}}{{end}}' . \
| LC_ALL=C sort -u \
| grep -vx 'github.com/minio/minio' \
| xargs go list -m -f '{{.Path}}|{{with .Replace}}{{.Path}}{{end}}|{{.Dir}}' \
| while IFS='|' read -r path replacement dir; do
if [ -z "${dir}" ] || [ ! -d "${dir}" ]; then
echo "Module cache directory missing for ${path}" >&2
exit 1
fi
name="${path}"
url="https://${path}"
# A same-path replace only pins a version; the annotation is for
# dependencies actually served from a different repository.
if [ -n "${replacement}" ] && [ "${replacement}" != "${path}" ]; then
name="${path} (replaced by ${replacement})"
url="https://${replacement}"
fi
printf '%s\n%s\n%s\n' "${name}" "${url}" "${rule_dash}"
if is_stdlib_derived "${path}"; then
printf '%s\n%s\n\n' \
'This module repackages Go standard library code and publishes no' \
'license file; the Go project license below applies per its file headers.'
emit_text "${go_license}"
else
license_file=''
for candidate in LICENSE LICENSE.txt LICENSE.md COPYING COPYING.txt LICENCE UNLICENSE; do
if [ -f "${dir}/${candidate}" ]; then
license_file="${dir}/${candidate}"
break
fi
done
if [ -z "${license_file}" ]; then
echo "No license file found for ${path} in ${dir}" >&2
exit 1
fi
emit_text "${license_file}"
fi
# Apache License 2.0 section 4(d) requires redistributing the NOTICE
# file contents alongside the licensed work.
for notice in NOTICE NOTICE.txt; do
if [ -f "${dir}/${notice}" ]; then
printf '\n%s\n\n' 'Bundled NOTICE file:'
emit_text "${dir}/${notice}"
break
fi
done
printf '\n%s\n\n' "${rule_equal}"
done
} > "${tmp_file}"
mv "${tmp_file}" "${out_file}"