Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/scripts/resolve-sonar-project-key.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
#
# Prints this repository's SonarQube project key to stdout.
#
# Projects imported through the SonarQube GitHub App are assigned a generated
# key of the form "<org>_<repo>_<uuid>". The uuid cannot be derived from any
# GitHub context, so the key is looked up through the SonarQube API instead of
# being assumed from a naming convention.
#
# On any problem this prints nothing and exits 0. Callers must treat an empty
# result as "skip analysis": letting the scanner run with a guessed key would
# silently create a second, unbound project instead of reporting an error.
set -uo pipefail

log() { printf '%s\n' "$*" >&2; }

if [ -z "${SONAR_HOST_URL:-}" ]; then
log "SONAR_HOST_URL is not set."
exit 0
fi

if [ -z "${SONAR_TOKEN:-}" ]; then
log "SONAR_TOKEN is not set."
exit 0
fi

case "$SONAR_HOST_URL" in
http://*|https://*) ;;
*)
log "SONAR_HOST_URL must include a scheme, e.g. https://sonarqube.example.com"
exit 0
;;
esac

HOST="${SONAR_HOST_URL%/}"
OWNER="${GITHUB_REPOSITORY%%/*}"
REPO="${GITHUB_REPOSITORY#*/}"
PREFIX="${OWNER}_${REPO}"

RESPONSE="$(curl -sS -u "${SONAR_TOKEN}:" --get \
--data-urlencode "q=${REPO}" \
--data-urlencode "qualifiers=TRK" \
--data-urlencode "ps=500" \
"${HOST}/api/components/search")" || {
log "Could not query ${HOST}/api/components/search."
exit 0
}

if printf '%s' "$RESPONSE" | jq -e 'has("errors")' >/dev/null 2>&1; then
log "SonarQube returned an error: $(printf '%s' "$RESPONSE" | jq -r '[.errors[].msg] | join("; ")')"
exit 0
fi

# Preference order: exact "<org>_<repo>", then "<org>_<repo>_<uuid>" as created
# by the GitHub App import, then a project whose display name is the repo name,
# then a bare "<repo>" key.
KEY="$(printf '%s' "$RESPONSE" | jq -r --arg prefix "$PREFIX" --arg repo "$REPO" '
[.components[]?] as $c
| (
[ $c[] | select(.key == $prefix) | .key ]
+ [ $c[]
| select((.key | startswith($prefix + "_"))
and ((.key | length) == (($prefix | length) + 37)))
| .key ]
+ [ $c[] | select(.name == $repo) | .key ]
+ [ $c[] | select(.key == $repo) | .key ]
)
| first // empty
' 2>/dev/null)"

if [ -z "$KEY" ]; then
log "No SonarQube project matching '${REPO}' was found. Has it been imported?"
exit 0
fi

log "Resolved SonarQube project key: ${KEY}"
printf '%s\n' "$KEY"
9 changes: 8 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,11 @@ jobs:
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
run: ./gradlew sonar --info
run: |
SONAR_PROJECT_KEY="$(bash .github/scripts/resolve-sonar-project-key.sh)"
if [ -z "$SONAR_PROJECT_KEY" ]; then
echo "Skipping analysis: no bound SonarQube project resolved."
exit 0
fi
export SONAR_PROJECT_KEY
./gradlew sonar --info
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ plugins {

sonar {
properties {
property "sonar.projectKey", "deprecated-java-api-demo-2"
// The SonarQube GitHub App gives imported projects a generated key, so CI
// looks the real key up and passes it in through SONAR_PROJECT_KEY.
property "sonar.projectKey", System.getenv("SONAR_PROJECT_KEY") ?: "deprecated-java-api-demo-2"
property "sonar.projectName", "deprecated-java-api-demo-2"
}
}
Expand Down
Loading