build: include all contents of the .bcr folder

PiperOrigin-RevId: 788127546
This commit is contained in:
Google APIs 2025-07-28 13:09:35 -07:00 committed by Copybara-Service
parent d2585c8369
commit 55c7471bc9
2 changed files with 470 additions and 0 deletions

280
.bcr/publish-to-bcr.sh Normal file
View file

@ -0,0 +1,280 @@
#!/bin/bash
#
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
set -e
readonly DEFAULT_REPO="googleapis"
readonly DEFAULT_ORG="googleapis"
readonly DEFAULT_PROTOBUF_VERSION="21.7"
readonly DEFAULT_BCR_ORGANIZATION="bazelbuild"
readonly SKIP_URL_CHECK_COMMENT='@bazel-io skip_check unstable_url'
readonly SKIP_FLAG_CHECK_COMMENT='@bazel-io skip_check incompatible_flags'
# This function checks out the .bcr folder
# to the specified commit SHA ($1) into the folder
# specified in $2
function checkout_definitions() {
local target_folder="$1"
local repo_url="$2"
local templates_ref="$3"
local definitions_ref="$4"
pushd "${target_folder}" > /dev/null
git init
git remote add origin "${repo_url}"
git sparse-checkout init --cone
git sparse-checkout set ".bcr"
git fetch --depth 1 origin "${templates_ref}"
git fetch --depth 2 origin "${definitions_ref}"
git checkout "${definitions_ref}"
git checkout "${templates_ref}"
popd > /dev/null
}
function get_version() {
local target_folder="$1"
local ref="$2"
pushd "${target_folder}" &> /dev/null
local commit_sha=$(git rev-parse "${ref}")
local commit_date=$(git log --format=%cd '--date=format:%Y%m%d' "${ref}~1..${ref}")
popd &> /dev/null
echo "0.0.0-${commit_date}-${commit_sha:0:8}"
}
# Replaces values in all files containing the ".template." string in
# their filenames
function render_templates() {
local target_folder="$1"
local ref="$2"
local protobuf_version="$3"
local org="$4"
local template_files=$(find "${target_folder}" -type f -name '*.template.*')
local version_string="$(get_version "${target_folder}" "${ref}")"
for file in ${template_files}; do
# here we render the values in each template file
sed -i "s|{VERSION}|${ref}|" "${file}"
sed -i "s|{GOOGLEAPIS_VERSION}|${version_string}|" "${file}"
sed -i "s|{PROTOBUF_VERSION}|${protobuf_version}|" "${file}"
sed -i "s|{OWNER}|${DEFAULT_ORG}|" "${file}"
sed -i "s|{REPO}|${DEFAULT_REPO}|" "${file}"
# we remove the .template string from the filename
mv "${file}" $(sed 's|\.template||' <<< "${file}")
done
}
# Creates a .patch file to indicate the introduction of MODULE.bazel
function create_module_patch() {
target_googleapis_module="$1"
filename="$(basename "${target_googleapis_module}")"
destination="$2"
diff -Nau /dev/null "${target_googleapis_module}" \
| sed "1 s|.*|--- ${filename}|" \
| sed "2 s|.*|+++ ${filename}|" > "${destination}"
}
# append the version specified in $2 to the specified metadata file
function append_version_to_metadata() {
local version="$1"
local metadata_file="$2"
cat <<< $(jq ".versions += [\"${version}\"]" "${metadata_file}") > "${metadata_file}"
}
function create_module_symlink() {
local bcr_module_folder="$1"
local target_symlink="${bcr_module_folder}/overlay/MODULE.bazel"
local target_file="${bcr_module_folder}/MODULE.bazel"
if [[ -f "${target_symlink}" ]]; then
rm "${target_symlink}"
fi
ln -rs "${target_file}" "${target_symlink}"
}
function update_module_integrity() {
bcr_folder="$1"
version="$2"
pushd "${bcr_folder}" > /dev/null
bazelisk run -- //tools:update_integrity "googleapis" \
|| exit 1
popd > /dev/null
}
function prepare_bcr_repo() {
local target_folder="$1"
local bcr_folder="$2"
local ref="$3"
local version=$(get_version "${target_folder}" "${ref}")
pushd "${bcr_folder}"
local googleapis_module_root="${bcr_folder}/modules/googleapis"
local googleapis_target_module="${googleapis_module_root}/${version}"
cp -r "${target_folder}" "${googleapis_target_module}"
create_module_symlink "${googleapis_target_module}"
append_version_to_metadata "${version}" "${googleapis_module_root}/metadata.json"
update_module_integrity "${bcr_folder}" "${version}"
popd
}
function validate_bcr_module() {
local target_folder="$1"
local bcr_folder="$2"
local ref="$3"
version="$(get_version "${target_folder}" "${ref}")"
pushd "${bcr_folder}" > /dev/null
# we skip the url stability check because we don't have releases of googleapis/googleapis
bazelisk run -- //tools:bcr_validation --skip_validation url_stability "--check=googleapis@${version}" \
|| exit "$?"
popd > /dev/null
}
function create_pull_request() {
local target_folder="$1"
local bcr_folder="$2"
local bcr_organization="$3"
local ref="$4"
local version=$(get_version "${target_folder}" "${ref}")
pushd "${bcr_folder}"
# we create a branch with a random string in case of multiple local runs
git checkout -b "add-googleapis-${version}-$(openssl rand -hex 3)"
git add "modules"
commit_message="Add googleapis ${version}"
git commit -m "${commit_message}"
git push origin
pr_command="gh pr create --title \"${commit_message}\" --body \"\" --repo \"${bcr_organization}/bazel-central-registry\""
read -p "The PR is ready to be raised. Do you wish to proceed? [y/N]: " -n 1 -r confirmation
if [[ "${confirmation}" =~ ^[Yy]$ ]]
then
echo "Creating Pull Request"
export pr_creation_output=$(bash -c "${pr_command}")
pr_url=$(grep '/pull/' <<< "${pr_creation_output}")
gh pr comment --body "${SKIP_URL_CHECK_COMMENT}" "${pr_url}"
gh pr comment --body "${SKIP_FLAG_CHECK_COMMENT}" "${pr_url}"
echo "Done! You can see the created PR in ${pr_url}"
else
echo "The branch is ready. You can create a PR by runing:"
echo "cd $(pwd) && ${pr_command}"
echo "Make sure to add a comment with the content: '${SKIP_URL_CHECK_COMMENT}'"
echo "as well as a comment with the content '${SKIP_FLAG_CHECK_COMMENT}'"
fi
popd
}
function confirm_tools() {
for tool in gh bazelisk git jq; do
if ! command -v "${tool}" &> /dev/null ; then
echo "Tool ${tool} is not installed. Please install it and try again."
exit 1
fi
done
}
function main() {
local ref="$1"
local templates_ref="$2"
local org="$3"
local protobuf_version="$4"
local bcr_organization="$5"
local bcr_folder="$6"
local target_folder="$(mktemp -d)"
readonly target_folder
local template_folder="${target_folder}/.bcr/template"
local repo_url="https://github.com/${org}/${DEFAULT_REPO}"
confirm_tools
checkout_definitions "${target_folder}" "${repo_url}" "${templates_ref}" "${ref}"
render_templates "${template_folder}" "${ref}" "${protobuf_version}" "${org}"
create_module_patch "${template_folder}/MODULE.bazel" "${template_folder}/patches/module_dot_bazel.patch"
if [[ -z "${bcr_folder}" ]] || [[ ! -d "${bcr_folder}" ]]; then
bcr_folder="${target_folder}/bazel-central-registry"
git clone "https://github.com/${bcr_organization}/bazel-central-registry" "${bcr_folder}"
fi
prepare_bcr_repo "${template_folder}" "${bcr_folder}" "${ref}"
validate_bcr_module "${template_folder}" "${bcr_folder}" "${ref}"
create_pull_request "${template_folder}" "${bcr_folder}" "${bcr_organization}" "${ref}"
}
# parse input parameters
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-r|--ref)
ref="$2"
shift
;;
-t|--templates_ref)
templates_ref="$2"
shift
;;
-o|--org)
org="$2"
shift
;;
-p|--protobuf_version)
protobuf_version="$2"
shift
;;
-b|--bcr_organization)
bcr_organization="$2"
shift
;;
-f|--bcr_folder)
bcr_folder="$2"
shift
;;
-d|--dry_run)
dry_run="$2"
shift
;;
*)
echo "Invalid option: [$1]"
exit 1
;;
esac
shift # past argument or value
done
if [[ -z "${ref}" ]]; then
echo "Missing option --ref"
exit 0
fi
if [[ -z "${templates_ref}" ]]; then
echo "assuming templates_ref to be the same as --ref"
templates_ref="${ref}"
fi
if [[ -z "${org}" ]]; then
echo "Using default value for --org: ${DEFAULT_ORG}"
org="${DEFAULT_ORG}"
fi
if [[ -z "${protobuf_version}" ]]; then
echo "Using default value for --protobuf_version: ${DEFAULT_PROTOBUF_VERSION}"
protobuf_version="${DEFAULT_PROTOBUF_VERSION}"
fi
if [[ -z "${bcr_organization}" ]]; then
echo "Using default value for --bcr_organization: ${DEFAULT_BCR_ORGANIZATION}"
bcr_organization="${DEFAULT_BCR_ORGANIZATION}"
fi
main "${ref}" "${templates_ref}" "${org}" "${protobuf_version}" "${bcr_organization}" "${bcr_folder}"

190
.bcr/publish-to-bcr_test.sh Executable file
View file

@ -0,0 +1,190 @@
#!/bin/bash
#
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
set -e
# Test suite for the publish-to-bcr.sh script
# --- Mocks ---
function git() {
case "$1" in
"rev-parse")
echo "mock_commit_sha" "$@"
;;
"log")
echo "20250101"
;;
"fetch")
echo "mock git fetch" "$@"
;;
"checkout")
echo "mock git checkout" "$@"
;;
*)
command git "$@"
;;
esac
}
function jq() {
# A simple mock for the append_version_to_metadata test
echo '{"versions": ["1.0.0", "2.0.0"]}'
}
function bazelisk() {
echo "mock bazelisk" "$@"
}
function dos2unix() {
echo "mock dos2unix" "$@"
}
function ln() {
echo "mock ln" "$@"
}
# Load functions from the script without executing the main logic
functions_to_load=$(sed '/^# parse input parameters/,$d' "$(dirname "$0")/publish-to-bcr.sh")
eval "$functions_to_load"
# --- Test setup and teardown ---
function set_up() {
TEST_DIR=$(mktemp -d)
export TEST_DIR
pushd "${TEST_DIR}" > /dev/null
popd > /dev/null
}
function tear_down() {
rm -rf "${TEST_DIR}"
}
# --- Test functions ---
function test_checkout_definitions() {
local output
output=$(checkout_definitions "${TEST_DIR}" "mock_repo_url" "mock_templates_ref" "mock_definitions_ref")
local expected_output="$(cat <<EOF
Initialized empty Git repository in ${TEST_DIR}/.git/
mock git fetch fetch --depth 1 origin mock_templates_ref
mock git fetch fetch --depth 2 origin mock_definitions_ref
mock git checkout checkout mock_definitions_ref
mock git checkout checkout mock_templates_ref
EOF
)"
assert_equals "${expected_output}" "${output}" "checkout_definitions"
}
function test_get_version() {
local version
version=$(get_version "${TEST_DIR}" "dummy_ref")
assert_equals "0.0.0-20250101-mock_com" "${version}" "get_version"
}
function test_render_templates() {
mkdir -p "${TEST_DIR}/.bcr/template"
echo "{VERSION}|{GOOGLEAPIS_VERSION}|{PROTOBUF_VERSION}|{OWNER}|{REPO}" > "${TEST_DIR}/.bcr/template/file1.template.txt"
render_templates "${TEST_DIR}/.bcr/template" "my-ref" "my-proto" "my-org"
local content=$(cat "${TEST_DIR}/.bcr/template/file1.txt")
local expected="my-ref|0.0.0-20250101-mock_com|my-proto|googleapis|googleapis"
assert_equals "${expected}" "${content}" "render_templates content"
if [ -f "${TEST_DIR}/.bcr/template/file1.template.txt" ]; then
echo "FAIL: render_templates did not remove template file"
exit 1
fi
echo "PASS: render_templates removed template file"
}
function test_append_version_to_metadata() {
local metadata_file="${TEST_DIR}/metadata.json"
echo '{"versions": ["1.0.0"]}' > "${metadata_file}"
append_version_to_metadata "2.0.0" "${metadata_file}"
local content=$(cat "${metadata_file}")
assert_equals '{"versions": ["1.0.0", "2.0.0"]}' "${content}" "append_version_to_metadata"
}
function test_validate_bcr_module() {
local output
output=$(validate_bcr_module "${TEST_DIR}" "${TEST_DIR}" "dummy_ref")
assert_equals "mock bazelisk run -- //tools:bcr_validation --skip_validation url_stability --check=googleapis@0.0.0-20250101-mock_com" "${output}" "validate_bcr_module"
}
function test_create_module_symlink() {
mkdir -p "${TEST_DIR}/overlay"
touch "${TEST_DIR}/MODULE.bazel"
local output
output=$(create_module_symlink "${TEST_DIR}")
assert_equals "mock ln -rs ${TEST_DIR}/MODULE.bazel ${TEST_DIR}/overlay/MODULE.bazel" "${output}" "create_module_symlink"
}
function test_convert_line_endings() {
local convert_dir="${TEST_DIR}/convert"
mkdir -p "${convert_dir}"
touch "${convert_dir}/file1"
touch "${convert_dir}/file2"
local output
output=$(convert_line_endings "${convert_dir}" | sort)
local expected_output="$(cat <<EOF | sort
mock dos2unix ${convert_dir}/file1
mock dos2unix ${convert_dir}/file2
EOF
)"
assert_equals "${expected_output}" "${output}" "convert_line_endings"
}
function test_update_module_integrity() {
local output
output=$(update_module_integrity "${TEST_DIR}" "dummy_version")
assert_equals "mock bazelisk run -- //tools:update_integrity googleapis" "${output}" "update_module_integrity"
}
# --- Test runner ---
function assert_equals() {
local expected="$1"
local actual="$2"
local message="$3"
if [ "${expected}" != "${actual}" ]; then
echo "FAIL: ${message}. Expected '${expected}', but got '${actual}'"
exit 1
fi
echo "PASS: ${message}"
}
function run_tests() {
set_up
test_checkout_definitions
test_get_version
test_render_templates
test_append_version_to_metadata
test_validate_bcr_module
test_create_module_symlink
test_convert_line_endings
test_update_module_integrity
tear_down
}
run_tests
echo "All tests passed."