mirror of
https://github.com/absmach/magistrala.git
synced 2026-08-07 07:14:46 +00:00
16ba29cf4a
Property Based Tests / api-test (push) Has been cancelled
Continuous Delivery / lint-and-build (push) Has been cancelled
Deploy GitHub Pages / swagger-ui (push) Has been cancelled
CI Pipeline / Lint Proto (push) Has been cancelled
CI Pipeline / Detect Changes (push) Has been cancelled
Continuous Delivery / Build and Push Docker Images (push) Has been cancelled
CI Pipeline / lint-and-build (push) Has been cancelled
CI Pipeline / Test ${{ matrix.module }} (push) Has been cancelled
CI Pipeline / Upload Coverage (push) Has been cancelled
Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusan <borovcanindusan1@gmail.com> Signed-off-by: Rodney Osodo <socials@rodneyosodo.com> Co-authored-by: Dušan Borovčanin <dusan.borovcanin@absmach.eu> Co-authored-by: Rodney Osodo <socials@rodneyosodo.com> Co-authored-by: dusan <borovcanindusan1@gmail.com>
175 lines
4.2 KiB
Go
175 lines
4.2 KiB
Go
// Copyright (c) Abstract Machines
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// report accumulates per-phase counts, skips, and blocking issues.
|
|
type report struct {
|
|
mu sync.Mutex
|
|
|
|
Mode string `json:"mode"`
|
|
StartedAt time.Time `json:"started_at"`
|
|
Counts map[string]int `json:"counts"` // phase -> rows written/planned
|
|
Skipped map[string]int `json:"skipped"` // reason -> count
|
|
Warnings []string `json:"warnings"` // non-blocking
|
|
Blocking []string `json:"blocking"` // must fix before --apply
|
|
Errors []string `json:"errors"`
|
|
|
|
// Follow-up lists surfaced for operators (force-reset users, re-issue PATs, etc.)
|
|
Todo map[string][]string `json:"todo"`
|
|
}
|
|
|
|
func newReport(mode string) *report {
|
|
return &report{
|
|
Mode: mode,
|
|
StartedAt: time.Now().UTC(),
|
|
Counts: map[string]int{},
|
|
Skipped: map[string]int{},
|
|
Todo: map[string][]string{},
|
|
}
|
|
}
|
|
|
|
func (r *report) count(phase string, n int) {
|
|
r.mu.Lock()
|
|
r.Counts[phase] += n
|
|
r.mu.Unlock()
|
|
}
|
|
|
|
func (r *report) skip(reason string) {
|
|
r.mu.Lock()
|
|
r.Skipped[reason]++
|
|
r.mu.Unlock()
|
|
}
|
|
|
|
func (r *report) warnf(format string, a ...any) {
|
|
r.mu.Lock()
|
|
r.Warnings = append(r.Warnings, fmt.Sprintf(format, a...))
|
|
r.mu.Unlock()
|
|
}
|
|
|
|
func (r *report) blockf(format string, a ...any) {
|
|
r.mu.Lock()
|
|
r.Blocking = append(r.Blocking, fmt.Sprintf(format, a...))
|
|
r.mu.Unlock()
|
|
}
|
|
|
|
func (r *report) Errorf(format string, a ...any) {
|
|
r.mu.Lock()
|
|
r.Errors = append(r.Errors, fmt.Sprintf(format, a...))
|
|
r.mu.Unlock()
|
|
}
|
|
|
|
func (r *report) todo(bucket, item string) {
|
|
r.mu.Lock()
|
|
r.Todo[bucket] = append(r.Todo[bucket], item)
|
|
r.mu.Unlock()
|
|
}
|
|
|
|
func (r *report) HasBlocking() bool {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
return len(r.Blocking) > 0 || len(r.Errors) > 0
|
|
}
|
|
|
|
func (r *report) Write(dir string) error {
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
stamp := r.StartedAt.Format("20060102-150405")
|
|
b, err := json.MarshalIndent(r, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "report-"+stamp+".json"), b, 0o644); err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(filepath.Join(dir, "report-"+stamp+".md"), []byte(r.markdown()), 0o644)
|
|
}
|
|
|
|
func (r *report) Summary() string {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
var b strings.Builder
|
|
fmt.Fprintf(&b, "\n=== atom-migration %s ===\n", r.Mode)
|
|
for _, k := range sortedKeys(r.Counts) {
|
|
fmt.Fprintf(&b, " %-28s %d\n", k, r.Counts[k])
|
|
}
|
|
if len(r.Skipped) > 0 {
|
|
b.WriteString(" skipped:\n")
|
|
for _, k := range sortedKeys(r.Skipped) {
|
|
fmt.Fprintf(&b, " %-26s %d\n", k, r.Skipped[k])
|
|
}
|
|
}
|
|
fmt.Fprintf(&b, " warnings=%d blocking=%d errors=%d\n",
|
|
len(r.Warnings), len(r.Blocking), len(r.Errors))
|
|
for _, x := range r.Blocking {
|
|
fmt.Fprintf(&b, " BLOCK: %s\n", x)
|
|
}
|
|
for _, x := range r.Errors {
|
|
fmt.Fprintf(&b, " ERROR: %s\n", x)
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func (r *report) markdown() string {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
var b strings.Builder
|
|
fmt.Fprintf(&b, "# atom-migration report (%s)\n\n%s\n\n", r.Mode, r.StartedAt.Format(time.RFC3339))
|
|
b.WriteString("## Counts\n\n| phase | rows |\n|---|---|\n")
|
|
for _, k := range sortedKeys(r.Counts) {
|
|
fmt.Fprintf(&b, "| %s | %d |\n", k, r.Counts[k])
|
|
}
|
|
if len(r.Skipped) > 0 {
|
|
b.WriteString("\n## Skipped\n\n| reason | count |\n|---|---|\n")
|
|
for _, k := range sortedKeys(r.Skipped) {
|
|
fmt.Fprintf(&b, "| %s | %d |\n", k, r.Skipped[k])
|
|
}
|
|
}
|
|
section := func(title string, items []string) {
|
|
if len(items) == 0 {
|
|
return
|
|
}
|
|
fmt.Fprintf(&b, "\n## %s\n\n", title)
|
|
for _, x := range items {
|
|
fmt.Fprintf(&b, "- %s\n", x)
|
|
}
|
|
}
|
|
section("Blocking", r.Blocking)
|
|
section("Errors", r.Errors)
|
|
section("Warnings", r.Warnings)
|
|
for _, bucket := range sortedKeys2(r.Todo) {
|
|
section("TODO: "+bucket, r.Todo[bucket])
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func sortedKeys(m map[string]int) []string {
|
|
ks := make([]string, 0, len(m))
|
|
for k := range m {
|
|
ks = append(ks, k)
|
|
}
|
|
sort.Strings(ks)
|
|
return ks
|
|
}
|
|
|
|
func sortedKeys2(m map[string][]string) []string {
|
|
ks := make([]string, 0, len(m))
|
|
for k := range m {
|
|
ks = append(ks, k)
|
|
}
|
|
sort.Strings(ks)
|
|
return ks
|
|
}
|