Skip to content

Commit 9c13f03

Browse files
committed
feat: improve ci support
Signed-off-by: Josef Andersson <josef.andersson@digg.se>
1 parent d288b91 commit 9c13f03

8 files changed

Lines changed: 789 additions & 65 deletions

File tree

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,40 @@ The justfile picks this up automatically:
375375
devtools_repo := env("DEVBASE_CHECK_REPO", "https://github.com/diggsweden/devbase-check")
376376
```
377377

378+
## CI Integration
379+
380+
The `verify.sh` script automatically detects the CI environment and adjusts output accordingly:
381+
382+
| Environment | Detection | Output |
383+
|-------------|-----------|--------|
384+
| **Local/Console** | No CI env vars | Colored table in terminal |
385+
| **GitHub Actions** | `GITHUB_STEP_SUMMARY` set | Markdown summary in Actions UI |
386+
| **GitLab CI** | `CI_JOB_URL` set | Console output (fallback) |
387+
| **Codeberg/Gitea** | `GITEA_ACTIONS` set | Console output (fallback) |
388+
389+
### GitHub Actions
390+
391+
When running in GitHub Actions, results appear in the job summary with:
392+
393+
- Markdown table showing each linter's status
394+
- Expandable error details for failed linters
395+
- Pass/fail/skip counts
396+
397+
No configuration needed - detection is automatic.
398+
399+
### Using with reusable-ci
400+
401+
If using [reusable-ci](https://github.com/diggsweden/reusable-ci), enable devbase-check in your workflow:
402+
403+
```yaml
404+
jobs:
405+
lint:
406+
uses: diggsweden/reusable-ci/.github/workflows/pullrequest-orchestrator.yml@main
407+
with:
408+
project-type: maven
409+
linters.devbasecheck: true
410+
```
411+
378412
## Directory Structure
379413

380414
```text
@@ -410,6 +444,10 @@ devbase-check/
410444
│ ├── check-tools.sh
411445
│ ├── setup.sh
412446
│ └── verify.sh
447+
├── summary/
448+
│ ├── common.sh # CI environment detection
449+
│ ├── console.sh # Terminal output (colored table)
450+
│ └── github.sh # GitHub Actions markdown summary
413451
├── utils/
414452
│ └── colors.sh
415453
├── examples/

scripts/verify.sh

Lines changed: 35 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ set -uo pipefail
88

99
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
1010
LINTERS_DIR="${SCRIPT_DIR}/../linters"
11+
1112
# shellcheck source=../utils/colors.sh
1213
source "${SCRIPT_DIR}/../utils/colors.sh"
1314

15+
# shellcheck source=../summary/common.sh
16+
source "${SCRIPT_DIR}/../summary/common.sh"
17+
1418
# Base linters
1519
LINTERS=(
1620
"Commits|conform|just lint-commits"
@@ -26,6 +30,7 @@ LINTERS=(
2630
)
2731

2832
declare -A RESULTS
33+
declare -A OUTPUTS
2934

3035
detect_language_linters() {
3136
local recipes
@@ -64,6 +69,9 @@ run_linters() {
6469
output=$(eval "$cmd" 2>&1) || exit_code=$?
6570
echo "$output"
6671

72+
# Store output for summary module
73+
OUTPUTS["$check"]="$output"
74+
6775
# Parse status from output
6876
local status details
6977
if [[ $exit_code -eq 0 ]]; then
@@ -100,86 +108,48 @@ run_linters() {
100108
print_summary() {
101109
local passed=0 skipped=0 na=0 failed=0
102110

103-
printf "\n%-22s %-12s\n" "Check" "Tool"
104-
printf "%.0s-" {1..45}
105-
printf "\n"
111+
# Count results first
112+
for linter_def in "${LINTERS[@]}"; do
113+
IFS='|' read -r check_name _ _ <<<"$linter_def"
114+
IFS='|' read -r status _ _ <<<"${RESULTS[$check_name]}"
115+
116+
case "$status" in
117+
pass) ((passed++)) ;;
118+
skip) ((skipped++)) ;;
119+
n/a) ((na++)) ;;
120+
fail) ((failed++)) ;;
121+
esac
122+
done
123+
124+
# Initialize summary module
125+
local total=$((passed + failed + skipped + na))
126+
summary_init "$total"
106127

128+
# Add results to summary module
107129
for linter_def in "${LINTERS[@]}"; do
108130
IFS='|' read -r check_name tool _ <<<"$linter_def"
109131
IFS='|' read -r status real_tool raw_details <<<"${RESULTS[$check_name]}"
110132

111133
# Skip disabled linters entirely
112134
[[ "$status" == "disabled" ]] && continue
113135

114-
# Format status icon
115-
local icon plain
116-
case "$status" in
117-
pass)
118-
# shellcheck disable=SC2153
119-
icon="${GREEN}${CHECK}${NC}"
120-
plain=""
121-
((passed++))
122-
;;
123-
skip)
124-
icon="${YELLOW}-${NC}"
125-
plain="-"
126-
((skipped++))
127-
;;
128-
n/a)
129-
icon="${CYAN}-${NC}"
130-
plain="-"
131-
((na++))
132-
;;
133-
fail)
134-
# shellcheck disable=SC2153
135-
icon="${RED}${CROSS}${NC}"
136-
plain=""
137-
((failed++))
138-
;;
139-
esac
140-
141-
# Only show meaningful details
142-
local details=""
143-
case "$status" in
144-
skip) details="$raw_details" ;;
145-
n/a) [[ $raw_details != "n/a" ]] && details="$raw_details" ;;
146-
pass | fail)
147-
if [[ $raw_details =~ [0-9]+\ commit ]]; then
148-
details="$raw_details"
149-
fi
150-
;;
151-
esac
152-
153-
# Center icon in 5-char column
154-
local pad=$(((5 - ${#plain}) / 2))
155-
local right_pad=$((5 - ${#plain} - pad))
136+
# Get output for this linter (for error details in GitHub summary)
137+
local output="${OUTPUTS[$check_name]:-}"
156138

157-
printf "%-22s %b%-12s%b%*s%b%*s%-30s\n" \
158-
"$check_name" "${DIM}" "$real_tool" "${NC}" "$pad" "" "$icon" "$right_pad" "" "$details"
139+
summary_add_result "$check_name" "$real_tool" "$status" "0" "$raw_details" "$output"
159140
done
160141

161-
printf "%.0s-" {1..45}
162-
printf "\n\n"
163-
164-
# Summary line
165-
local total=$((passed + failed + skipped + na))
166-
printf "Total: %b%d passed%b" "${GREEN}" "$passed" "${NC}"
167-
((failed > 0)) && printf ", %b%d failed%b" "${RED}" "$failed" "${NC}"
168-
((skipped > 0)) && printf ", %b%d skipped%b" "${YELLOW}" "$skipped" "${NC}"
169-
((na > 0)) && printf ", %b%d n/a%b" "${CYAN}" "$na" "${NC}"
170-
printf " (of %d)\n\n" "$total"
171-
172-
# Help message
173-
if ((failed > 0)); then
174-
printf "%bRun %bjust lint-fix%b to auto-fix some issues%b\n\n" \
175-
"${YELLOW}" "${GREEN}" "${YELLOW}" "${NC}"
176-
return 1
177-
fi
142+
# Finalize summary
143+
summary_finalize "$total" "$passed" "$failed" "$skipped"
144+
local summary_exit=$?
178145

179-
return 0
146+
return $summary_exit
180147
}
181148

182149
main() {
150+
# Load appropriate summary module based on CI environment
151+
load_summary_module
152+
183153
detect_language_linters
184154

185155
run_linters

summary/common.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env bash
2+
3+
# SPDX-FileCopyrightText: 2025 Digg - Agency for Digital Government
4+
#
5+
# SPDX-License-Identifier: MIT
6+
7+
# Common utilities shared by all summary modules
8+
#
9+
# This file provides:
10+
# - CI environment detection
11+
# - Summary module loading
12+
13+
SUMMARY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14+
15+
# Detect which CI environment we're running in
16+
# Returns: github, gitlab, codeberg, or console
17+
detect_ci_environment() {
18+
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
19+
echo "github"
20+
elif [[ -n "${CI_JOB_URL:-}" ]]; then
21+
echo "gitlab"
22+
elif [[ -n "${GITEA_ACTIONS:-}" ]]; then
23+
echo "codeberg"
24+
else
25+
echo "console"
26+
fi
27+
}
28+
29+
# Load the appropriate summary module based on CI environment
30+
# Falls back to console if no matching module exists
31+
load_summary_module() {
32+
local ci_env="${1:-}"
33+
34+
if [[ -z "$ci_env" ]]; then
35+
ci_env=$(detect_ci_environment)
36+
fi
37+
38+
local module_path="${SUMMARY_DIR}/${ci_env}.sh"
39+
40+
if [[ -f "$module_path" ]]; then
41+
# shellcheck source=/dev/null
42+
source "$module_path"
43+
else
44+
# Fallback to console
45+
# shellcheck source=console.sh
46+
source "${SUMMARY_DIR}/console.sh"
47+
fi
48+
}

summary/console.sh

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#!/usr/bin/env bash
2+
3+
# SPDX-FileCopyrightText: 2025 Digg - Agency for Digital Government
4+
#
5+
# SPDX-License-Identifier: MIT
6+
7+
# Console summary module for devbase-check
8+
# Outputs a formatted table to the terminal
9+
#
10+
# Required interface functions:
11+
# summary_init $linter_count
12+
# summary_add_result $name $tool $status $duration $details
13+
# summary_finalize $total $passed $failed $skipped
14+
15+
# shellcheck source=../utils/colors.sh
16+
SUMMARY_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17+
source "${SUMMARY_SCRIPT_DIR}/../utils/colors.sh"
18+
19+
# Storage for results (to print in table format at finalize)
20+
declare -a _CONSOLE_RESULTS=()
21+
declare -i _CONSOLE_PASSED=0
22+
declare -i _CONSOLE_FAILED=0
23+
declare -i _CONSOLE_SKIPPED=0
24+
declare -i _CONSOLE_NA=0
25+
26+
# Initialize summary output
27+
# Called once at start
28+
summary_init() {
29+
local linter_count="$1"
30+
_CONSOLE_RESULTS=()
31+
_CONSOLE_PASSED=0
32+
_CONSOLE_FAILED=0
33+
_CONSOLE_SKIPPED=0
34+
_CONSOLE_NA=0
35+
}
36+
37+
# Add a linter result
38+
# Called for each linter after it runs
39+
summary_add_result() {
40+
local linter_name="$1"
41+
local tool="$2"
42+
local status="$3"
43+
local duration="$4"
44+
local details="${5:-}"
45+
46+
# Store result for later printing
47+
_CONSOLE_RESULTS+=("${linter_name}|${tool}|${status}|${duration}|${details}")
48+
49+
# Update counters
50+
case "$status" in
51+
pass) ((_CONSOLE_PASSED++)) || true ;;
52+
fail) ((_CONSOLE_FAILED++)) || true ;;
53+
skip) ((_CONSOLE_SKIPPED++)) || true ;;
54+
n/a) ((_CONSOLE_NA++)) || true ;;
55+
esac
56+
}
57+
58+
# Finalize and print summary table
59+
# Called once at end
60+
summary_finalize() {
61+
local total="${1:-0}"
62+
local passed="${2:-$_CONSOLE_PASSED}"
63+
local failed="${3:-$_CONSOLE_FAILED}"
64+
local skipped="${4:-$_CONSOLE_SKIPPED}"
65+
66+
printf "\n%-22s %-12s\n" "Check" "Tool"
67+
printf "%.0s-" {1..45}
68+
printf "\n"
69+
70+
for result in "${_CONSOLE_RESULTS[@]}"; do
71+
IFS='|' read -r check_name tool status duration details <<<"$result"
72+
73+
# Skip disabled linters entirely
74+
[[ "$status" == "disabled" ]] && continue
75+
76+
# Format status icon
77+
local icon plain
78+
case "$status" in
79+
pass)
80+
icon="${GREEN}${CHECK}${NC}"
81+
plain="$CHECK"
82+
;;
83+
skip)
84+
icon="${YELLOW}-${NC}"
85+
plain="-"
86+
;;
87+
n/a)
88+
icon="${CYAN}-${NC}"
89+
plain="-"
90+
;;
91+
fail)
92+
icon="${RED}${CROSS}${NC}"
93+
plain="$CROSS"
94+
;;
95+
esac
96+
97+
# Only show meaningful details
98+
local display_details=""
99+
case "$status" in
100+
skip) display_details="$details" ;;
101+
n/a) [[ "$details" != "n/a" ]] && display_details="$details" ;;
102+
pass | fail)
103+
if [[ "$details" =~ [0-9]+\ commit ]]; then
104+
display_details="$details"
105+
fi
106+
;;
107+
esac
108+
109+
# Center icon in 5-char column
110+
local pad=$(((5 - ${#plain}) / 2))
111+
local right_pad=$((5 - ${#plain} - pad))
112+
113+
printf "%-22s %b%-12s%b%*s%b%*s%-30s\n" \
114+
"$check_name" "${DIM}" "$tool" "${NC}" "$pad" "" "$icon" "$right_pad" "" "$display_details"
115+
done
116+
117+
printf "%.0s-" {1..45}
118+
printf "\n\n"
119+
120+
# Summary line
121+
local actual_total=$((passed + failed + skipped + _CONSOLE_NA))
122+
printf "Total: %b%d passed%b" "${GREEN}" "$passed" "${NC}"
123+
((failed > 0)) && printf ", %b%d failed%b" "${RED}" "$failed" "${NC}"
124+
((skipped > 0)) && printf ", %b%d skipped%b" "${YELLOW}" "$skipped" "${NC}"
125+
((_CONSOLE_NA > 0)) && printf ", %b%d n/a%b" "${CYAN}" "$_CONSOLE_NA" "${NC}"
126+
printf " (of %d)\n\n" "$actual_total"
127+
128+
# Help message
129+
if ((failed > 0)); then
130+
printf "%bRun %bjust lint-fix%b to auto-fix some issues%b\n\n" \
131+
"${YELLOW}" "${GREEN}" "${YELLOW}" "${NC}"
132+
return 1
133+
fi
134+
135+
return 0
136+
}

0 commit comments

Comments
 (0)