All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 32s
				
			Test / Sandbox (push) Successful in 2m29s
				
			Test / Hakurei (push) Successful in 4m6s
				
			Test / Hpkg (push) Successful in 4m45s
				
			Test / Sandbox (race detector) (push) Successful in 4m48s
				
			Test / Hakurei (race detector) (push) Successful in 6m4s
				
			Test / Flake checks (push) Successful in 1m26s
				
			This was a stopgap solution that lasted for way too long. This finally removes it and prepares internal/app for some major changes. Signed-off-by: Ophestra <cat@gensokyo.uk>
		
			
				
	
	
		
			176 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package app
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"log"
 | |
| 
 | |
| 	"hakurei.app/container"
 | |
| 	"hakurei.app/internal/hlog"
 | |
| )
 | |
| 
 | |
| // PrintRunStateErr prints an error message via [log] if runErr is not nil, and returns an appropriate exit code.
 | |
| //
 | |
| // TODO(ophestra): remove this function once RunState has been replaced
 | |
| func PrintRunStateErr(rs *RunState, runErr error) (code int) {
 | |
| 	code = rs.ExitStatus()
 | |
| 
 | |
| 	if runErr != nil {
 | |
| 		if rs.Time == nil {
 | |
| 			// no process has been created
 | |
| 			printMessageError("cannot start app:", runErr)
 | |
| 		} else {
 | |
| 			if m, ok := container.GetErrorMessage(runErr); !ok {
 | |
| 				// catch-all for unexpected errors
 | |
| 				log.Println("run returned error:", runErr)
 | |
| 			} else {
 | |
| 				var se *StateStoreError
 | |
| 				if !errors.As(runErr, &se) {
 | |
| 					// this could only be returned from a shim setup failure path
 | |
| 					log.Print(m)
 | |
| 				} else {
 | |
| 					// InnerErr is returned by c.Save(&sd, seal.ct), and are always unwrapped
 | |
| 					printMessageError("error returned during revert:",
 | |
| 						&FinaliseError{Step: "save process state", Err: se.InnerErr})
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if code == 0 {
 | |
| 			code = 126
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if rs.RevertErr != nil {
 | |
| 		var stateStoreError *StateStoreError
 | |
| 		if !errors.As(rs.RevertErr, &stateStoreError) || stateStoreError == nil {
 | |
| 			printMessageError("cannot clean up:", rs.RevertErr)
 | |
| 			goto out
 | |
| 		}
 | |
| 
 | |
| 		if stateStoreError.Errs != nil {
 | |
| 			if len(stateStoreError.Errs) == 2 { // storeErr.save(revertErr, store.Close())
 | |
| 				if stateStoreError.Errs[0] != nil { // revertErr is MessageError joined by errors.Join
 | |
| 					var joinedErrors interface {
 | |
| 						Unwrap() []error
 | |
| 						error
 | |
| 					}
 | |
| 					if !errors.As(stateStoreError.Errs[0], &joinedErrors) {
 | |
| 						printMessageError("cannot revert:", stateStoreError.Errs[0])
 | |
| 					} else {
 | |
| 						for _, err := range joinedErrors.Unwrap() {
 | |
| 							if err != nil {
 | |
| 								printMessageError("cannot revert:", err)
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				if stateStoreError.Errs[1] != nil { // store.Close() is joined by errors.Join
 | |
| 					log.Printf("cannot close store: %v", stateStoreError.Errs[1])
 | |
| 				}
 | |
| 			} else {
 | |
| 				log.Printf("fault during cleanup: %v", errors.Join(stateStoreError.Errs...))
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if stateStoreError.OpErr != nil {
 | |
| 			log.Printf("blind revert due to store fault: %v", stateStoreError.OpErr)
 | |
| 		}
 | |
| 
 | |
| 		if stateStoreError.DoErr != nil {
 | |
| 			printMessageError("state store operation unsuccessful:", stateStoreError.DoErr)
 | |
| 		}
 | |
| 
 | |
| 		if stateStoreError.Inner && stateStoreError.InnerErr != nil {
 | |
| 			printMessageError("cannot destroy state entry:", stateStoreError.InnerErr)
 | |
| 		}
 | |
| 
 | |
| 	out:
 | |
| 		if code == 0 {
 | |
| 			code = 128
 | |
| 		}
 | |
| 	}
 | |
| 	if rs.WaitErr != nil {
 | |
| 		hlog.Verbosef("wait: %v", rs.WaitErr)
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // TODO(ophestra): this duplicates code in cmd/hakurei/command.go, keep this up to date until removal
 | |
| func printMessageError(fallback string, err error) {
 | |
| 	if m, ok := container.GetErrorMessage(err); ok {
 | |
| 		if m != "\x00" {
 | |
| 			log.Print(m)
 | |
| 		}
 | |
| 	} else {
 | |
| 		log.Println(fallback, err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // StateStoreError is returned for a failed state save.
 | |
| type StateStoreError struct {
 | |
| 	// whether inner function was called
 | |
| 	Inner bool
 | |
| 	// returned by the Save/Destroy method of [state.Cursor]
 | |
| 	InnerErr error
 | |
| 	// returned by the Do method of [state.Store]
 | |
| 	DoErr error
 | |
| 	// stores an arbitrary store operation error
 | |
| 	OpErr error
 | |
| 	// stores arbitrary errors
 | |
| 	Errs []error
 | |
| }
 | |
| 
 | |
| // save saves arbitrary errors in [StateStoreError.Errs] once.
 | |
| func (e *StateStoreError) save(errs ...error) {
 | |
| 	if len(errs) == 0 || e.Errs != nil {
 | |
| 		panic("invalid call to save")
 | |
| 	}
 | |
| 	e.Errs = errs
 | |
| }
 | |
| 
 | |
| // equiv returns an error that [StateStoreError] is equivalent to, including nil.
 | |
| func (e *StateStoreError) equiv(step string) error {
 | |
| 	if e.Inner && e.InnerErr == nil && e.DoErr == nil && e.OpErr == nil && errors.Join(e.Errs...) == nil {
 | |
| 		return nil
 | |
| 	} else {
 | |
| 		return &FinaliseError{Step: step, Err: e}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (e *StateStoreError) Error() string {
 | |
| 	if e.Inner && e.InnerErr != nil {
 | |
| 		return e.InnerErr.Error()
 | |
| 	}
 | |
| 	if e.DoErr != nil {
 | |
| 		return e.DoErr.Error()
 | |
| 	}
 | |
| 	if e.OpErr != nil {
 | |
| 		return e.OpErr.Error()
 | |
| 	}
 | |
| 	if err := errors.Join(e.Errs...); err != nil {
 | |
| 		return err.Error()
 | |
| 	}
 | |
| 
 | |
| 	// equiv nullifies e for values where this is reached
 | |
| 	panic("unreachable")
 | |
| }
 | |
| 
 | |
| func (e *StateStoreError) Unwrap() (errs []error) {
 | |
| 	errs = make([]error, 0, 3+len(e.Errs))
 | |
| 	if e.InnerErr != nil {
 | |
| 		errs = append(errs, e.InnerErr)
 | |
| 	}
 | |
| 	if e.DoErr != nil {
 | |
| 		errs = append(errs, e.DoErr)
 | |
| 	}
 | |
| 	if e.OpErr != nil {
 | |
| 		errs = append(errs, e.OpErr)
 | |
| 	}
 | |
| 	for _, err := range e.Errs {
 | |
| 		if err != nil {
 | |
| 			errs = append(errs, err)
 | |
| 		}
 | |
| 	}
 | |
| 	return
 | |
| }
 |