This is an automated email from the ASF dual-hosted git repository.
klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/devlake.git
The following commit(s) were added to refs/heads/main by this push:
new 799147fff fix(grafana): run entrypoint with POSIX sh (#9312)
799147fff is described below
commit 799147fff766278fd580fa910bdc9e699dec91b8
Author: Dirk <[email protected]>
AuthorDate: Fri Nov 6 08:25:36 2026 +0100
fix(grafana): run entrypoint with POSIX sh (#9312)
grafana/grafana:13.3.0 no longer ships bash, so the image failed to start
with "exec /entrypoint.sh: no such file or directory". Switch the shebang
to /bin/sh and replace the only bashism ($'\n').
Add grafana/scripts/smoke-test.sh plus a pull_request workflow that builds
the image and checks that it starts and creates a healthy datasource
against MySQL and PostgreSQL, so base image bumps that break startup are
caught before merge.
Co-authored-by: Copilot <[email protected]>
---
.github/workflows/grafana-image-smoke-test.yml | 41 ++++++++++
grafana/scripts/entrypoint.sh | 8 +-
grafana/scripts/smoke-test.sh | 101 +++++++++++++++++++++++++
3 files changed, 148 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/grafana-image-smoke-test.yml
b/.github/workflows/grafana-image-smoke-test.yml
new file mode 100644
index 000000000..6b2901341
--- /dev/null
+++ b/.github/workflows/grafana-image-smoke-test.yml
@@ -0,0 +1,41 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+name: grafana-image-smoke-test
+
+on:
+ pull_request:
+ branches:
+ - main
+ - release-*
+ paths:
+ - 'grafana/**'
+ - '.github/workflows/grafana-image-smoke-test.yml'
+
+jobs:
+ grafana-image-smoke-test:
+ name: grafana image smoke test (${{ matrix.db }})
+ runs-on: ubuntu-24.04
+ strategy:
+ fail-fast: false
+ matrix:
+ db: [mysql, postgresql]
+ steps:
+ - uses: actions/checkout@v7
+ - name: Build grafana image
+ run: docker build -t devlake-dashboard:smoke grafana
+ - name: Start image and check datasource
+ run: grafana/scripts/smoke-test.sh devlake-dashboard:smoke ${{
matrix.db }}
diff --git a/grafana/scripts/entrypoint.sh b/grafana/scripts/entrypoint.sh
index de20a196c..99ffa44f3 100644
--- a/grafana/scripts/entrypoint.sh
+++ b/grafana/scripts/entrypoint.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
@@ -271,6 +271,9 @@ fi
echo "Creating datasource via API..."
DATASOURCE_OK=false
+# The grafana/grafana image (Alpine/busybox) ships no bash, so stay POSIX sh.
+NL='
+'
for i in $(seq 1 10); do
# "|| true": a failing curl must not end this script (set -e) and with it the
# container; the status check below reports the problem instead.
@@ -278,7 +281,8 @@ for i in $(seq 1 10); do
-H "Content-Type: application/json" \
-d @"$PAYLOAD_FILE" 2>&1) || true
STATUS="${RESPONSE##*HTTP_STATUS=}"
- RESPONSE="${RESPONSE%$'\n'HTTP_STATUS=*}"
+ RESPONSE="${RESPONSE%HTTP_STATUS=*}"
+ RESPONSE="${RESPONSE%"$NL"}"
if [ "$STATUS" = "401" ] || [ "$STATUS" = "403" ]; then
# Retrying cannot help: the credentials do not match the Grafana admin.
diff --git a/grafana/scripts/smoke-test.sh b/grafana/scripts/smoke-test.sh
new file mode 100755
index 000000000..fe7f4a1ac
--- /dev/null
+++ b/grafana/scripts/smoke-test.sh
@@ -0,0 +1,101 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+# Smoke test for the devlake-dashboard (Grafana) image: starts it next to a
+# throwaway database and checks that the entrypoint runs, Grafana becomes
+# healthy and the DevLake datasource is created and can reach the database.
+#
+# Usage: grafana/scripts/smoke-test.sh <image> [mysql|postgresql]
+# Uses uniquely named containers/network only; nothing else is touched.
+
+set -euo pipefail
+
+IMAGE="${1:?usage: $0 <image> [mysql|postgresql]}"
+DB_TYPE="${2:-mysql}"
+TIMEOUT="${SMOKE_TIMEOUT:-180}"
+MYSQL_IMAGE="${MYSQL_IMAGE:-mysql:8.4}"
+POSTGRES_IMAGE="${POSTGRES_IMAGE:-postgres:18}"
+
+PREFIX="grafana-smoke-$$"
+NET="$PREFIX-net"
+DB="$PREFIX-db"
+GRAFANA="$PREFIX-grafana"
+
+cleanup() {
+ docker rm -fv "$GRAFANA" "$DB" >/dev/null 2>&1 || true
+ docker network rm "$NET" >/dev/null 2>&1 || true
+}
+trap cleanup EXIT
+
+fail() {
+ echo "FAIL: $*"
+ echo "----- grafana container logs -----"
+ docker logs "$GRAFANA" 2>&1 | grep -v 'logger=' | tail -50 || true
+ exit 1
+}
+
+docker network create "$NET" >/dev/null
+
+case "$DB_TYPE" in
+ mysql)
+ docker run -d -q --name "$DB" --network "$NET" \
+ -e MYSQL_ROOT_PASSWORD=admin -e MYSQL_DATABASE=lake \
+ -e MYSQL_USER=merico -e MYSQL_PASSWORD=merico \
+ "$MYSQL_IMAGE" >/dev/null
+ DS_UID="devlake-mysql-api"
+ ;;
+ postgresql)
+ docker run -d -q --name "$DB" --network "$NET" \
+ -e POSTGRES_DB=lake -e POSTGRES_USER=merico -e POSTGRES_PASSWORD=merico \
+ "$POSTGRES_IMAGE" >/dev/null
+ DS_UID="devlake-postgres-api"
+ ;;
+ *)
+ echo "unknown database type '$DB_TYPE' (expected mysql or postgresql)"
+ exit 2
+ ;;
+esac
+
+docker run -d --name "$GRAFANA" --network "$NET" \
+ -e DATABASE_TYPE="$DB_TYPE" -e DATABASE_HOST="$DB" \
+ "$IMAGE" >/dev/null
+
+echo "Waiting for $IMAGE ($DB_TYPE) to create its datasource..."
+deadline=$((SECONDS + TIMEOUT))
+until docker logs "$GRAFANA" 2>&1 | grep -q "Datasource created successfully";
do
+ if [ "$(docker inspect -f '{{.State.Running}}' "$GRAFANA")" != "true" ]; then
+ fail "grafana container exited (exit code $(docker inspect -f
'{{.State.ExitCode}}' "$GRAFANA"))"
+ fi
+ if docker logs "$GRAFANA" 2>&1 | grep -q "ERROR: datasource"; then
+ fail "entrypoint could not create the datasource"
+ fi
+ [ "$SECONDS" -lt "$deadline" ] || fail "datasource not created within
${TIMEOUT}s"
+ sleep 3
+done
+
+api() {
+ docker exec "$GRAFANA" curl -fsS --user admin:admin
"http://localhost:3000/api/$1"
+}
+
+api health >/dev/null || fail "/api/health not OK"
+
+# The database may still be initialising; retry the datasource health check.
+until api "datasources/uid/$DS_UID/health" >/dev/null 2>&1; do
+ [ "$SECONDS" -lt "$deadline" ] || fail "datasource $DS_UID cannot reach the
$DB_TYPE database"
+ sleep 3
+done
+
+echo "OK: $IMAGE starts and datasource $DS_UID is healthy ($DB_TYPE)"