2020-04-10 21:59:16 +08:00
#!/usr/bin/env bash
# vim: ts=2 et
2019-04-17 20:41:06 +08:00
# Setting -x is absolutely forbidden as it could leak the GitHub token.
2019-04-11 20:31:04 +08:00
set -uo pipefail
# GITHUB_TOKEN required scope: repo.repo_public
git_mail = "prometheus-team@googlegroups.com"
git_user = "prombot"
2020-06-17 22:51:32 +08:00
branch = "repo_sync"
commit_msg = "Update common Prometheus files"
pr_title = "Synchronize common files from prometheus/prometheus"
2025-05-05 20:27:23 +08:00
pr_msg = "Propagating changes from prometheus/prometheus default branch.\n\n*Source can be found [here](https://github.com/prometheus/prometheus/blob/main/scripts/sync_repo_files.sh).*"
2020-04-10 21:34:34 +08:00
orgs = "prometheus prometheus-community"
2019-04-11 20:31:04 +08:00
2021-03-25 16:52:32 +08:00
color_red = '\e[31m'
color_green = '\e[32m'
2021-06-01 18:45:31 +08:00
color_yellow = '\e[33m'
2021-03-25 16:52:32 +08:00
color_none = '\e[0m'
echo_red( ) {
2021-06-01 18:45:31 +08:00
echo -e " ${ color_red } $@ ${ color_none } " 1>& 2
2021-03-25 16:52:32 +08:00
}
echo_green( ) {
2021-06-01 18:45:31 +08:00
echo -e " ${ color_green } $@ ${ color_none } " 1>& 2
}
echo_yellow( ) {
echo -e " ${ color_yellow } $@ ${ color_none } " 1>& 2
2021-03-25 16:52:32 +08:00
}
2019-04-11 20:31:04 +08:00
GITHUB_TOKEN = " ${ GITHUB_TOKEN :- } "
if [ -z " ${ GITHUB_TOKEN } " ] ; then
2021-03-25 16:52:32 +08:00
echo_red 'GitHub token (GITHUB_TOKEN) not set. Terminating.'
2020-04-10 21:59:16 +08:00
exit 1
2019-04-11 20:31:04 +08:00
fi
2020-06-17 22:51:32 +08:00
# List of files that should be synced.
2024-08-13 02:06:48 +08:00
SYNC_FILES = "CODE_OF_CONDUCT.md LICENSE Makefile.common SECURITY.md .yamllint scripts/golangci-lint.yml .github/workflows/scorecards.yml .github/workflows/container_description.yml .github/workflows/stale.yml"
2020-06-17 22:51:32 +08:00
2019-04-11 20:31:04 +08:00
# Go to the root of the repo
2020-04-10 21:59:16 +08:00
cd " $( git rev-parse --show-cdup) " || exit 1
2019-04-11 20:31:04 +08:00
2020-06-17 22:51:32 +08:00
source_dir = " $( pwd ) "
2019-04-11 20:31:04 +08:00
2020-04-10 21:59:16 +08:00
tmp_dir = " $( mktemp -d) "
trap 'rm -rf "${tmp_dir}"' EXIT
2021-03-27 21:33:22 +08:00
## Internal functions
2021-03-16 06:59:27 +08:00
github_api( ) {
local url
url = " https://api.github.com/ ${ 1 } "
shift 1
curl --retry 5 --silent --fail -u " ${ git_user } : ${ GITHUB_TOKEN } " " ${ url } " " $@ "
}
get_default_branch( ) {
github_api " repos/ ${ 1 } " 2> /dev/null |
jq -r .default_branch
2021-02-23 03:19:08 +08:00
}
2020-04-10 21:59:16 +08:00
fetch_repos( ) {
2021-03-25 16:52:32 +08:00
github_api " orgs/ ${ 1 } /repos?type=public&per_page=100 " 2> /dev/null |
jq -r '.[] | select( .archived == false and .fork == false and .name != "prometheus" ) | .name'
2020-04-10 21:59:16 +08:00
}
push_branch( ) {
2021-03-16 06:59:27 +08:00
local git_url
git_url = " https:// ${ git_user } : ${ GITHUB_TOKEN } @github.com/ ${ 1 } "
2020-04-10 21:59:16 +08:00
# stdout and stderr are redirected to /dev/null otherwise git-push could leak
# the token in the logs.
2019-09-12 21:25:26 +08:00
# Delete the remote branch in case it was merged but not deleted.
2021-03-16 06:59:27 +08:00
git push --quiet " ${ git_url } " " : ${ branch } " 1>/dev/null 2>& 1
git push --quiet " ${ git_url } " --set-upstream " ${ branch } " 1>/dev/null 2>& 1
2020-04-10 21:59:16 +08:00
}
post_pull_request( ) {
2021-03-16 06:59:27 +08:00
local repo = " $1 "
local default_branch = " $2 "
local post_json
post_json = " $( printf '{"title":"%s","base":"%s","head":"%s","body":"%s"}' " ${ pr_title } " " ${ default_branch } " " ${ branch } " " ${ pr_msg } " ) "
echo " Posting PR to ${ default_branch } on ${ repo } "
github_api " repos/ ${ repo } /pulls " --data " ${ post_json } " --show-error |
jq -r '"PR URL " + .html_url'
2020-04-10 21:59:16 +08:00
}
2020-06-22 18:35:04 +08:00
check_license( ) {
# Check to see if the input is an Apache license of some kind
echo " $1 " | grep --quiet --no-messages --ignore-case 'Apache License'
}
2021-09-10 22:30:18 +08:00
check_go( ) {
local org_repo
local default_branch
org_repo = " $1 "
default_branch = " $2 "
curl -sLf -o /dev/null " https://raw.githubusercontent.com/ ${ org_repo } / ${ default_branch } /go.mod "
}
2024-03-14 16:20:40 +08:00
check_docker( ) {
local org_repo
local default_branch
org_repo = " $1 "
default_branch = " $2 "
curl -sLf -o /dev/null " https://raw.githubusercontent.com/ ${ org_repo } / ${ default_branch } /Dockerfile "
}
2020-04-10 21:59:16 +08:00
process_repo( ) {
2021-03-16 06:59:27 +08:00
local org_repo
local default_branch
org_repo = " $1 "
2021-03-25 16:52:32 +08:00
echo_green " Analyzing ' ${ org_repo } ' "
2019-04-11 20:31:04 +08:00
2021-03-16 06:59:27 +08:00
default_branch = " $( get_default_branch " ${ org_repo } " ) "
2021-02-24 06:23:51 +08:00
if [ [ -z " ${ default_branch } " ] ] ; then
2021-02-23 03:19:08 +08:00
echo "Can't get the default branch."
return
fi
2021-03-16 06:59:27 +08:00
echo " Default branch: ${ default_branch } "
2021-02-23 03:19:08 +08:00
2020-06-22 18:35:04 +08:00
local needs_update = ( )
2020-06-17 22:51:32 +08:00
for source_file in ${ SYNC_FILES } ; do
2020-06-21 08:18:21 +08:00
source_checksum = " $( sha256sum " ${ source_dir } / ${ source_file } " | cut -d' ' -f1) "
2023-03-30 17:04:37 +08:00
if [ [ " ${ source_file } " = = 'scripts/golangci-lint.yml' ] ] && ! check_go " ${ org_repo } " " ${ default_branch } " ; then
echo " ${ org_repo } is not Go, skipping golangci-lint.yml. "
continue
fi
2024-03-14 16:20:40 +08:00
if [ [ " ${ source_file } " = = '.github/workflows/container_description.yml' ] ] && ! check_docker " ${ org_repo } " " ${ default_branch } " ; then
echo " ${ org_repo } has no Dockerfile, skipping container_description.yml. "
continue
fi
2020-06-22 18:35:04 +08:00
if [ [ " ${ source_file } " = = 'LICENSE' ] ] && ! check_license " ${ target_file } " ; then
echo " LICENSE in ${ org_repo } is not apache, skipping. "
continue
fi
2023-03-30 17:04:37 +08:00
target_filename = " ${ source_file } "
if [ [ " ${ source_file } " = = 'scripts/golangci-lint.yml' ] ] ; then
2023-07-01 17:31:51 +08:00
target_filename = ".github/workflows/golangci-lint.yml"
2021-09-10 22:30:18 +08:00
fi
2023-03-30 17:04:37 +08:00
target_file = " $( curl -sL --fail " https://raw.githubusercontent.com/ ${ org_repo } / ${ default_branch } / ${ target_filename } " ) "
2020-06-21 08:18:21 +08:00
if [ [ -z " ${ target_file } " ] ] ; then
2023-07-01 17:31:51 +08:00
echo " ${ target_filename } doesn't exist in ${ org_repo } "
2021-01-17 03:07:55 +08:00
case " ${ source_file } " in
2024-03-18 15:20:42 +08:00
CODE_OF_CONDUCT.md | SECURITY.md | .github/workflows/container_description.yml)
2021-01-17 03:07:55 +08:00
echo " ${ source_file } missing in ${ org_repo } , force updating. "
needs_update += ( " ${ source_file } " )
; ;
esac
2020-06-17 22:51:32 +08:00
continue
fi
target_checksum = " $( echo " ${ target_file } " | sha256sum | cut -d' ' -f1) "
if [ " ${ source_checksum } " = = " ${ target_checksum } " ] ; then
echo " ${ source_file } is already in sync. "
continue
fi
2021-03-16 06:59:27 +08:00
echo " ${ source_file } needs updating. "
2020-06-22 18:35:04 +08:00
needs_update += ( " ${ source_file } " )
2020-06-17 22:51:32 +08:00
done
2020-06-22 18:35:04 +08:00
if [ [ " ${# needs_update [@] } " -eq 0 ] ] ; then
2020-06-17 22:51:32 +08:00
echo "No files need sync."
2020-04-10 21:59:16 +08:00
return
fi
2019-04-11 20:31:04 +08:00
2020-04-10 21:59:16 +08:00
# Clone target repo to temporary directory and checkout to new branch
git clone --quiet " https://github.com/ ${ org_repo } .git " " ${ tmp_dir } / ${ org_repo } "
cd " ${ tmp_dir } / ${ org_repo } " || return 1
git checkout -b " ${ branch } " || return 1
2019-04-11 20:31:04 +08:00
2021-09-12 02:08:07 +08:00
# If we need to add an Actions file this directory needs to be present.
mkdir -p "./.github/workflows"
2020-06-17 22:51:32 +08:00
# Update the files in target repo by one from prometheus/prometheus.
2021-03-16 06:59:27 +08:00
for source_file in " ${ needs_update [@] } " ; do
2023-03-30 17:04:37 +08:00
target_filename = " ${ source_file } "
if [ [ " ${ source_file } " = = 'scripts/golangci-lint.yml' ] ] ; then
2023-07-01 17:31:51 +08:00
target_filename = ".github/workflows/golangci-lint.yml"
2023-03-30 17:04:37 +08:00
fi
2021-03-27 21:33:22 +08:00
case " ${ source_file } " in
2023-03-30 17:04:37 +08:00
*) cp -f " ${ source_dir } / ${ source_file } " " ./ ${ target_filename } " ; ;
2021-03-27 21:33:22 +08:00
esac
2020-06-17 22:51:32 +08:00
done
2021-03-16 06:59:27 +08:00
if [ [ -n " $( git status --porcelain) " ] ] ; then
2020-04-10 21:59:16 +08:00
git config user.email " ${ git_mail } "
git config user.name " ${ git_user } "
git add .
git commit -s -m " ${ commit_msg } "
if push_branch " ${ org_repo } " ; then
2021-03-16 06:59:27 +08:00
if ! post_pull_request " ${ org_repo } " " ${ default_branch } " ; then
return 1
fi
else
echo " Pushing ${ branch } to ${ org_repo } failed "
return 1
2020-04-10 21:59:16 +08:00
fi
fi
}
2019-04-11 20:31:04 +08:00
2021-03-27 21:33:22 +08:00
## main
2020-04-10 21:59:16 +08:00
for org in ${ orgs } ; do
mkdir -p " ${ tmp_dir } / ${ org } "
# Iterate over all repositories in ${org}. The GitHub API can return 100 items
# at most but it should be enough for us as there are less than 40 repositories
# currently.
fetch_repos " ${ org } " | while read -r repo; do
2019-09-12 21:25:26 +08:00
# Check if a PR is already opened for the branch.
2021-03-16 06:59:27 +08:00
fetch_uri = " repos/ ${ org } / ${ repo } /pulls?state=open&head= ${ org } : ${ branch } "
2021-06-27 06:29:56 +08:00
prLink = " $( github_api " ${ fetch_uri } " --show-error | jq -r '.[0].html_url' ) "
if [ [ " ${ prLink } " != "null" ] ] ; then
echo_green " Pull request already opened for branch ' ${ branch } ': ${ prLink } "
echo "Either close it or merge it before running this script again!"
continue
fi
if ! process_repo " ${ org } / ${ repo } " ; then
2021-03-25 16:52:32 +08:00
echo_red " Failed to process ' ${ org } / ${ repo } ' "
2020-04-10 21:59:16 +08:00
exit 1
fi
done
2019-04-11 20:31:04 +08:00
done