#!/bin/bash # # Generate license files for all platform/arch combinations. # This script handles architecture-specific dependency differences by: # 1. Generating separate license reports per GOOS/GOARCH combination # 2. Grouping identical reports together (comma-separated arch names) # 3. Creating an index at the top of each platform file # 4. Copying all license files to third-party/ # # Note: third-party/ is a union of all license files across all architectures. # This means that license files for dependencies present in only some architectures # may still appear in third-party/. This is intentional and ensures compliance. # # Note: we ignore warnings because we want the command to succeed, however the output should be checked # for any new warnings, and potentially we may need to add license information. # # Normally these warnings are packages containing non go code, which may or may not require explicit attribution, # depending on the license. set -e # Pinned version for reproducibility # See: https://github.com/cli/cli/pull/11161 go install github.com/google/go-licenses/v2@v2.0.1 # actions/setup-go does not setup the installed toolchain to be preferred over the system install, # which causes go-licenses to raise "Package ... does not have module info" errors in CI. # For more information, https://github.com/google/go-licenses/issues/244#issuecomment-1885098633 if [ "$CI" = "true" ]; then export GOROOT=$(go env GOROOT) export PATH=${GOROOT}/bin:$PATH fi # actions/setup-go does not setup the installed toolchain to be preferred over the system install, # which causes go-licenses to raise "Package ... does not have module info" errors in CI. # For more information, https://github.com/google/go-licenses/issues/244#issuecomment-1885098633 if [ "$CI" = "true" ]; then export GOROOT=$(go env GOROOT) export PATH=${GOROOT}/bin:$PATH fi rm -rf third-party mkdir -p third-party export TEMPDIR="$(mktemp -d)" trap "rm -fr ${TEMPDIR}" EXIT # Cross-platform hash function (works on both Linux and macOS) compute_hash() { if command -v md5sum >/dev/null 2>&1; then md5sum | cut -d' ' -f1 elif command -v md5 >/dev/null 2>&1; then md5 -q else # Fallback to cksum if neither is available cksum | cut -d' ' -f1 fi } # Function to get architectures for a given OS get_archs() { case "$1" in linux) echo "386 amd64 arm64" ;; darwin) echo "amd64 arm64" ;; windows) echo "386 amd64 arm64" ;; esac } # Generate reports for each platform/arch combination for goos in darwin linux windows; do echo "Processing ${goos}..." archs=$(get_archs "$goos") for goarch in $archs; do echo " Generating for ${goos}/${goarch}..." # Generate the license report for this arch report_file="${TEMPDIR}/${goos}_${goarch}_report.md" GOOS="${goos}" GOARCH="${goarch}" GOFLAGS=-mod=mod go-licenses report ./... --template .github/licenses.tmpl > "${report_file}" 2>/dev/null || echo " (warnings ignored for ${goos}/${goarch})" # Save licenses to temp directory GOOS="${goos}" GOARCH="${goarch}" GOFLAGS=-mod=mod go-licenses save ./... --save_path="${TEMPDIR}/${goos}_${goarch}" --force 2>/dev/null || echo " (warnings ignored for ${goos}/${goarch})" # Copy to third-party (accumulate all - union of all architectures for compliance) if [ -d "${TEMPDIR}/${goos}_${goarch}" ]; then cp -fR "${TEMPDIR}/${goos}_${goarch}"/* third-party/ 2>/dev/null || true fi # Extract just the package list (skip header), sort it, and hash it # Use LC_ALL=C for consistent sorting across different systems packages_file="${TEMPDIR}/${goos}_${goarch}_packages.txt" if [ -s "${report_file}" ] && grep -qE '^ - \[' "${report_file}" 2>/dev/null; then grep -E '^ - \[' "${report_file}" | LC_ALL=C sort > "${packages_file}" hash=$(cat "${packages_file}" | compute_hash) else echo "(FAILED TO GENERATE LICENSE REPORT FOR ${goos}/${goarch})" > "${packages_file}" hash="FAILED_${goos}_${goarch}" fi # Store hash for grouping echo "${hash}" > "${TEMPDIR}/${goos}_${goarch}_hash.txt" done # Group architectures with identical reports (deterministic order) # Create groups file: hash -> comma-separated archs groups_file="${TEMPDIR}/${goos}_groups.txt" rm -f "${groups_file}" # Process architectures in order to build groups for goarch in $archs; do hash=$(cat "${TEMPDIR}/${goos}_${goarch}_hash.txt") # Check if we've seen this hash before if grep -q "^${hash}:" "${groups_file}" 2>/dev/null; then # Append to existing group existing=$(grep "^${hash}:" "${groups_file}" | cut -d: -f2) sed -i.bak "s/^${hash}:.*/${hash}:${existing}, ${goarch}/" "${groups_file}" rm -f "${groups_file}.bak" else # New group echo "${hash}:${goarch}" >> "${groups_file}" fi done # Generate the combined report for this platform output_file="third-party-licenses.${goos}.md" cat > "${output_file}" << 'EOF' # GitHub MCP Server dependencies The following open source dependencies are used to build the [github/github-mcp-server][] GitHub Model Context Protocol Server. ## Table of Contents EOF # Build table of contents (sorted for determinism) # Use LC_ALL=C for consistent sorting across different systems LC_ALL=C sort "${groups_file}" | while IFS=: read -r hash group_archs; do # Create anchor-friendly name anchor=$(echo "${group_archs}" | tr ', ' '-' | tr -s '-') echo "- [${group_archs}](#${anchor})" >> "${output_file}" done echo "" >> "${output_file}" echo "---" >> "${output_file}" echo "" >> "${output_file}" # Add each unique report section (sorted for determinism) # Use LC_ALL=C for consistent sorting across different systems LC_ALL=C sort "${groups_file}" | while IFS=: read -r hash group_archs; do # Get the packages from the first arch in this group first_arch=$(echo "${group_archs}" | cut -d',' -f1 | tr -d ' ') packages=$(cat "${TEMPDIR}/${goos}_${first_arch}_packages.txt") cat >> "${output_file}" << EOF ## ${group_archs} The following packages are included for the ${group_archs} architectures. ${packages} EOF done # Add footer echo "[github/github-mcp-server]: https://github.com/github/github-mcp-server" >> "${output_file}" echo "Generated ${output_file}" done echo "Done! License files generated."