mirror of
https://github.com/LukeHagar/slinky.git
synced 2025-12-06 04:21:20 +00:00
Refactor PR comment handling by moving it to the CLI in check.go, removing the previous implementation from entrypoint.sh. Enhance report summary to include the number of files scanned and ensure markdown report generation is streamlined.
This commit is contained in:
130
cmd/check.go
130
cmd/check.go
@@ -1,9 +1,12 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -222,25 +225,46 @@ func init() {
|
||||
_ = f.Close()
|
||||
}
|
||||
|
||||
// Optionally write Markdown report for PR comment consumption
|
||||
if mdOut != "" {
|
||||
base := repoBlobBase
|
||||
if strings.TrimSpace(base) == "" {
|
||||
base = os.Getenv("SLINKY_REPO_BLOB_BASE_URL")
|
||||
}
|
||||
summary := report.Summary{
|
||||
RootPath: displayRoot,
|
||||
StartedAt: startedAt,
|
||||
FinishedAt: time.Now(),
|
||||
Processed: total,
|
||||
OK: okCount,
|
||||
Fail: failCount,
|
||||
JSONPath: jsonOut,
|
||||
RepoBlobBaseURL: base,
|
||||
}
|
||||
if _, err := report.WriteMarkdown(mdOut, failedResults, summary); err != nil {
|
||||
// Build report summary
|
||||
base := repoBlobBase
|
||||
if strings.TrimSpace(base) == "" {
|
||||
base = os.Getenv("SLINKY_REPO_BLOB_BASE_URL")
|
||||
}
|
||||
summary := report.Summary{
|
||||
RootPath: displayRoot,
|
||||
StartedAt: startedAt,
|
||||
FinishedAt: time.Now(),
|
||||
Processed: total,
|
||||
OK: okCount,
|
||||
Fail: failCount,
|
||||
FilesScanned: countFiles(urlToFiles),
|
||||
JSONPath: jsonOut,
|
||||
RepoBlobBaseURL: base,
|
||||
}
|
||||
|
||||
// Ensure we have a markdown file if needed for PR comment
|
||||
mdPath := mdOut
|
||||
ghRepo, ghPR, ghToken, ghOK := detectGitHubPR()
|
||||
if strings.TrimSpace(mdPath) != "" {
|
||||
if _, err := report.WriteMarkdown(mdPath, failedResults, summary); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if ghOK {
|
||||
p, err := report.WriteMarkdown("", failedResults, summary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mdPath = p
|
||||
}
|
||||
|
||||
// If running on a PR, post or update the comment
|
||||
if ghOK && strings.TrimSpace(mdPath) != "" {
|
||||
b, rerr := os.ReadFile(mdPath)
|
||||
if rerr == nil {
|
||||
body := string(b)
|
||||
body = fmt.Sprintf("%s\n%s", "<!-- slinky-report -->", body)
|
||||
_ = upsertPRComment(ghRepo, ghPR, ghToken, body)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Checked %d URLs: %d OK, %d failed\n", total, okCount, failCount)
|
||||
@@ -284,3 +308,75 @@ func toSlash(p string) string {
|
||||
func hasGlobMeta(s string) bool {
|
||||
return strings.ContainsAny(s, "*?[")
|
||||
}
|
||||
|
||||
func countFiles(urlToFiles map[string][]string) int {
|
||||
seen := make(map[string]struct{})
|
||||
for _, files := range urlToFiles {
|
||||
for _, f := range files {
|
||||
seen[f] = struct{}{}
|
||||
}
|
||||
}
|
||||
return len(seen)
|
||||
}
|
||||
|
||||
func detectGitHubPR() (repo string, prNumber int, token string, ok bool) {
|
||||
repo = os.Getenv("GITHUB_REPOSITORY")
|
||||
token = os.Getenv("GITHUB_TOKEN")
|
||||
eventPath := os.Getenv("GITHUB_EVENT_PATH")
|
||||
if repo == "" || eventPath == "" || token == "" {
|
||||
return "", 0, "", false
|
||||
}
|
||||
data, err := os.ReadFile(eventPath)
|
||||
if err != nil {
|
||||
return "", 0, "", false
|
||||
}
|
||||
var ev struct {
|
||||
PullRequest struct {
|
||||
Number int `json:"number"`
|
||||
} `json:"pull_request"`
|
||||
}
|
||||
_ = json.Unmarshal(data, &ev)
|
||||
if ev.PullRequest.Number == 0 {
|
||||
return "", 0, "", false
|
||||
}
|
||||
return repo, ev.PullRequest.Number, token, true
|
||||
}
|
||||
|
||||
func upsertPRComment(repo string, prNumber int, token string, body string) error {
|
||||
apiBase := "https://api.github.com"
|
||||
listURL := fmt.Sprintf("%s/repos/%s/issues/%d/comments?per_page=100", apiBase, repo, prNumber)
|
||||
req, _ := http.NewRequest(http.MethodGet, listURL, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Accept", "application/vnd.github+json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var comments []struct {
|
||||
ID int `json:"id"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
_ = json.Unmarshal(b, &comments)
|
||||
var existingID int
|
||||
for _, c := range comments {
|
||||
if strings.Contains(c.Body, "<!-- slinky-report -->") {
|
||||
existingID = c.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
payload, _ := json.Marshal(map[string]string{"body": body})
|
||||
if existingID > 0 {
|
||||
u := fmt.Sprintf("%s/repos/%s/issues/comments/%d", apiBase, repo, existingID)
|
||||
req, _ = http.NewRequest(http.MethodPatch, u, bytes.NewReader(payload))
|
||||
} else {
|
||||
u := fmt.Sprintf("%s/repos/%s/issues/%d/comments", apiBase, repo, prNumber)
|
||||
req, _ = http.NewRequest(http.MethodPost, u, bytes.NewReader(payload))
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Accept", "application/vnd.github+json")
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
_, _ = http.DefaultClient.Do(req)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -84,59 +84,7 @@ if [ "${STEP_SUMMARY_ARG}" = "true" ] && [ -n "${GITHUB_STEP_SUMMARY:-}" ] && [
|
||||
cat "${MD_OUT_ARG}" >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
|
||||
# Post PR comment if this is a PR and requested (even if the run failed)
|
||||
if [ "${COMMENT_PR_ARG}" = "true" ]; then
|
||||
if [ -z "${MD_OUT_ARG}" ] || [ ! -f "${MD_OUT_ARG}" ]; then
|
||||
echo "[slinky] No markdown report found at '${MD_OUT_ARG}', skipping PR comment."
|
||||
else
|
||||
PR_NUMBER=""
|
||||
if [ -n "${GITHUB_EVENT_PATH:-}" ] && command -v jq >/dev/null 2>&1; then
|
||||
PR_NUMBER="$(jq -r '.pull_request.number // empty' "$GITHUB_EVENT_PATH" || true)"
|
||||
fi
|
||||
if [ -z "${PR_NUMBER}" ]; then
|
||||
echo "[slinky] Not a pull_request event or PR number not found; skipping PR comment."
|
||||
elif [ -z "${GITHUB_TOKEN:-}" ]; then
|
||||
echo "[slinky] GITHUB_TOKEN not available; ensure the workflow grants permissions and passes the token as env. Skipping PR comment."
|
||||
elif [ -z "${GITHUB_REPOSITORY:-}" ]; then
|
||||
echo "[slinky] GITHUB_REPOSITORY not set; skipping PR comment."
|
||||
else
|
||||
BODY_CONTENT="$(cat "${MD_OUT_ARG}")"
|
||||
COMMENT_BODY="$(printf '%s\n%s\n' '<!-- slinky-report -->' "${BODY_CONTENT}")"
|
||||
|
||||
# Try to find an existing slinky comment to update
|
||||
COMMENTS_JSON=$(curl -sS -H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments?per_page=100") || COMMENTS_JSON="[]"
|
||||
|
||||
EXISTING_ID=$(printf '%s' "${COMMENTS_JSON}" | jq -r '[.[] | select((.body // "") | contains("<!-- slinky-report -->"))][0].id // empty')
|
||||
|
||||
if [ -n "${EXISTING_ID}" ]; then
|
||||
# Update existing comment
|
||||
curl -sS -H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
-X PATCH "https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/comments/${EXISTING_ID}" \
|
||||
-d "$(printf '{"body": %s}' "$(jq -Rs . <<EOF
|
||||
${COMMENT_BODY}
|
||||
EOF
|
||||
)" )" >/dev/null || true
|
||||
echo "[slinky] Updated existing PR comment #${EXISTING_ID}."
|
||||
else
|
||||
# Create new comment
|
||||
curl -sS -H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
-X POST "https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \
|
||||
-d "$(printf '{"body": %s}' "$(jq -Rs . <<EOF
|
||||
${COMMENT_BODY}
|
||||
EOF
|
||||
)" )" >/dev/null || true
|
||||
echo "[slinky] Posted PR comment to #${PR_NUMBER}."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# PR comment handling is now done in the CLI itself when running on a PR
|
||||
|
||||
exit ${SLINKY_EXIT_CODE:-0}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ type Summary struct {
|
||||
AvgRPS float64
|
||||
PeakRPS float64
|
||||
LowRPS float64
|
||||
FilesScanned int
|
||||
JSONPath string
|
||||
RepoBlobBaseURL string // e.g. https://github.com/owner/repo/blob/<sha>
|
||||
}
|
||||
@@ -51,17 +52,21 @@ func WriteMarkdown(path string, results []web.Result, s Summary) (string, error)
|
||||
// Title
|
||||
buf.WriteString("## Slinky Test Report\n\n")
|
||||
|
||||
// Last run (not in the bullet list)
|
||||
// Last run (not in the bullet list). Render in US Central Time.
|
||||
dur := s.FinishedAt.Sub(s.StartedAt)
|
||||
if dur < 0 {
|
||||
dur = 0
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("Last Run: %s (Duration: %s)\n\n", s.StartedAt.Format("2006-01-02 15:04:05 MST"), dur.Truncate(time.Millisecond)))
|
||||
loc, _ := time.LoadLocation("America/Chicago")
|
||||
buf.WriteString(fmt.Sprintf("Last Run: %s (Duration: %s)\n\n", s.StartedAt.In(loc).Format("2006-01-02 15:04:05 MST"), dur.Truncate(time.Millisecond)))
|
||||
|
||||
// Summary list: Pass, Fail, Total
|
||||
buf.WriteString(fmt.Sprintf("- **Pass**: %d\n", s.OK))
|
||||
buf.WriteString(fmt.Sprintf("- **Fail**: %d\n", s.Fail))
|
||||
buf.WriteString(fmt.Sprintf("- **Total**: %d\n", s.Processed))
|
||||
if s.FilesScanned > 0 {
|
||||
buf.WriteString(fmt.Sprintf("- **Files Scanned**: %d\n", s.FilesScanned))
|
||||
}
|
||||
|
||||
// Optional root
|
||||
if strings.TrimSpace(s.RootPath) != "." && strings.TrimSpace(s.RootPath) != "" && s.RootPath != string(filepath.Separator) {
|
||||
|
||||
Reference in New Issue
Block a user