Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Investigate docs generation improvements

Aperta
#415 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
25/100
Tipo di issue
Refactoring
Chiarezza
Abbastanza chiara
Stato di attività
Ferma
Stack tecnologico
go
Ambito
documentation

Direzione di ricerca

Start by reading the docgen command logic and the config structs in the internal/config package, then review the discussion on PR #406. Compare the current AST-based approach with the reflection proposal in this issue. Done means the simpler approach has been assessed and the generated documentation still works correctly.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

documentation

The docgen command logic is quite complicated (AST, ahhh). I didn't do a full audit but I did run it locally and seems like it works well. My only feedback is I think we could consider a different approach that is a bit simpler.
My understanding is we're parsing files within the internal/config package and using AST to detect config structs and parse through it.

I wonder if it's easier to use the reflect package to traverse the main Config struct. I'll share a snippet below, maybe it can be helpful if you want to explore this approach. Happy to just merge this for now and reiterate later.

See https://github.com/hookdeck/outpost/pull/406#pullrequestreview-2872006658

func parseWithReflection() error {
	cfg := config.Config{} // Create an instance of the actual Config struct
	cfg.InitDefaults()     // Initialize it with default values

	// Start the recursive traversal
	fmt.Println("\n--- Starting Reflection-Based Parsing ---")
	parseTypeRecursive(reflect.TypeOf(cfg), reflect.ValueOf(cfg), "", 0)
	fmt.Println("--- Finished Reflection-Based Parsing ---")

	return nil
}

func parseTypeRecursive(typ reflect.Type, val reflect.Value, pathPrefix string, indentLevel int) {
	// If 'val' is a pointer, dereference it to get the actual struct value.
	// Also, update 'typ' to be the type of the dereferenced value.
	if val.Kind() == reflect.Ptr {
		if val.IsNil() { // Important: can't proceed if the pointer is nil
			indent := strings.Repeat("  ", indentLevel)
			fmt.Printf("%sStruct: %s (Path: %s) is a <nil pointer>\n", indent, typ.String(), pathPrefix)
			return
		}
		val = val.Elem()
		typ = val.Type() // Update typ to the element's type
	}

	// Ensure we are dealing with a struct.
	if val.Kind() != reflect.Struct {
		// This can happen if a field is a basic type, slice, map, etc.,
		// and a recursive call was made on it.
		// Or if the initial call wasn't a struct or pointer to struct.
		return
	}
	// typ should now definitely be a struct type because val is a struct value.

	indent := strings.Repeat("  ", indentLevel)
	fmt.Printf("%sStruct: %s (Path: %s)\n", indent, typ.Name(), pathPrefix) // typ.Name() is fine here

	configPkgPath := reflect.TypeOf(config.Config{}).PkgPath() // For checking recursion scope

	for i := 0; i < typ.NumField(); i++ {
		fieldDesc := typ.Field(i) // reflect.StructField - describes the field
		fieldVal := val.Field(i)  // reflect.Value - holds the runtime value of this field

		currentPath := fieldDesc.Name
		if pathPrefix != "" {
			currentPath = pathPrefix + "." + fieldDesc.Name
		}

		fmt.Printf("%s  Field: %s (Type: %s, Path: %s)\n", indent, fieldDesc.Name, fieldDesc.Type.String(), currentPath)
		fmt.Printf("%s    YAML: '%s', Env: '%s', Desc: '%s', Required: '%s'\n",
			indent,
			fieldDesc.Tag.Get("yaml"),
			fieldDesc.Tag.Get("env"),
			fieldDesc.Tag.Get("desc"),
			fieldDesc.Tag.Get("required"))

		// Correctly use fieldVal to get the current value
		var currentValueInterface interface{}
		if fieldVal.CanInterface() { // Check if the field is exported
			currentValueInterface = fieldVal.Interface()
			if fieldVal.Kind() == reflect.Ptr && fieldVal.IsNil() {
				currentValueInterface = "<nil pointer>"
			}
		} else {
			currentValueInterface = "<unexported field>"
		}
		fmt.Printf("%s    Current Value: %v\n", indent, currentValueInterface)

		// --- Recursive Call Logic ---
		// We need the type of the field for recursion checks, and the value for the next call.
		fieldTypeForRecursion := fieldDesc.Type
		fieldValueForRecursion := fieldVal

		// Determine the actual element type if fieldTypeForRecursion is a pointer
		actualFieldTypeForCheck := fieldTypeForRecursion
		if actualFieldTypeForCheck.Kind() == reflect.Ptr {
			actualFieldTypeForCheck = actualFieldTypeForCheck.Elem()
		}

		if actualFieldTypeForCheck.Kind() == reflect.Struct {
			if actualFieldTypeForCheck.PkgPath() == configPkgPath {
				// Pass the field's type and the field's value to the recursive call.
				parseTypeRecursive(fieldTypeForRecursion, fieldValueForRecursion, currentPath, indentLevel+1)
			} else {
				fmt.Printf("%s    (Skipping recursion into struct from different package: %s from %s)\n", indent, actualFieldTypeForCheck.Name(), actualFieldTypeForCheck.PkgPath())
			}
		}
	}
}
Lingua principale
Go
Stelle
1.1k
Fork
70
Merge medio
1g 3h
PR unite (30g)
50

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di hookdeck/outpost

Tutte le issue di hookdeck/outpost

Issue simili

Altre issue su Go

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.