Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Investigate docs generation improvements

Abierto
#415 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
25/100
Tipo de issue
Refactorización
Claridad
Bastante claro
Estado de actividad
Estancado
Stack tecnológico
go

Línea de trabajo

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.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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())
			}
		}
	}
}
Lenguaje dominante
Go
Estrellas
1.1k
Forks
70
Merge medio
1 d 3 h
PR fusionados (30 d)
50

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de hookdeck/outpost

Todos los issues de hookdeck/outpost

Issues similares

Más issues de Go

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.