"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Un ejemplo práctico de bibliotecas compartidas en un monorepo

Un ejemplo práctico de bibliotecas compartidas en un monorepo

Publicado el 2024-11-09
Navegar:769

A practical example of shared libraries in a monorepo

Uno de los aspectos más poderosos de trabajar en un monorepo es la capacidad de compartir código entre paquetes/equipos/jerarquías. En esta publicación intentaré explicar un escenario del mundo real muy simple

Escenario de ejemplo

Imagine que desea desarrollar una biblioteca para mostrar los tamaños de archivos en megabytes que cree que podrían ser útiles para otras partes de su monorepo. La biblioteca acepta el tamaño como entero (por ejemplo, 2048 bytes) y puede devolver una cadena humanizada (por ejemplo, 2 MB). Para agregar cierta garantía de calidad, también escribiremos una prueba para el mismo.

¿Cómo permite Bazel compartir código?

Del escenario anterior, somos conscientes de que necesitamos desarrollar esta función como una biblioteca compartida que luego será importada por otro paquete para su uso. Bazel hace que esto sea extremadamente simple al permitirnos definir la función en una biblioteca y exportarla a otros servicios que la necesitarán. Como expliqué en mi publicación anterior vinculada al final de esta publicación, también podemos controlar a qué otras bibliotecas se les puede permitir importarlo para su uso.

Empecemos a codificar

Para fines de organización del código, tendremos un directorio de bibliotecas en la raíz de nuestro espacio de trabajo con un directorio secundario llamado humanize_filesize que es donde escribiremos el código de nuestra biblioteca.

Escribamos un código Go muy elemental en humanize_filesize.go

package humanize_filesize

import "fmt"

// GetHumanizedFilesize takes size_in_bytes as an int32 pointer and returns the size in megabytes.
func GetHumanizedFilesize(size_in_bytes *int32) string {
    if size_in_bytes != nil {
        size_in_megabytes := float64(*size_in_bytes) / (1024 * 1024)
        return fmt.Sprintf("%.4f MB", size_in_megabytes)
    }
    return "0 MB"
}

Este código simplemente toma un int32 como entrada y devuelve una cadena de megabytes legible calculada con una precisión de 4 decimales

Esta función definitivamente no es completa y definitivamente se puede mejorar, pero ese no es el objetivo de este ejercicio.

Afirme también que nuestra lógica funciona según lo previsto; agregaremos una prueba muy elemental junto con nuestro código go en un archivo llamado humanize_filesize_test.go

package humanize_filesize

import (
    "testing"
)

func TestHumanizeFilesize(t *testing.T) {
    tests := []struct {
        name          string
        size_in_bytes *int32
        expected      string
    }{
        {
            name:          "nil bytes",
            size_in_bytes: nil,
            expected:      "0 MB",
        },
        {
            name:          "2048 bytes",
            size_in_bytes: int32Ptr(2048),
            expected:      "0.0020 MB",
        },
        {
            name:          "0 bytes",
            size_in_bytes: int32Ptr(0),
            expected:      "0.0000 MB",
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := GetHumanizedFilesize(tt.size_in_bytes)
            if result != tt.expected {
                t.Errorf("expected %s, got %s", tt.expected, result)
            }
        })
    }
}

func int32Ptr(n int32) *int32 {
    return &n
}

Una prueba muy simple con pruebas básicas para nil, int32 y 0 como entradas

Ahora viene la parte jugosa de cómo exportar esta función para que pueda importarse dentro de otros paquetes o servicios. Aquí es donde tenemos que definir el archivo BUILD.bazel.

load("@rules_go//go:def.bzl", "go_library", "go_test")

go_library(
    name = "humanize_filesize",
    srcs = ["humanize_filesize.go"],
    importpath = "basil/libraries/humanize_filesize",
    visibility = ["//visibility:public"],
)

go_test(
    name = "humanize_filesize_test",
    srcs = ["humanize_filesize_test.go"],
    embed = [":humanize_filesize"],
)

Aquí estamos definiendo dos reglas principales. Uno para la biblioteca real y otro para el archivo de prueba que escribimos.

go_library define que el objetivo humanize_filesize usa humanize_filesize.go como una de sus fuentes que se puede importar mediante la ruta especificada en importpath y es visible públicamente dentro del espacio de trabajo para que otros paquetes lo importen. Aprenderemos cómo controlar la visibilidad en una publicación futura.

go_test define un objetivo de prueba que incorpora el código de la salida de go_library.

En este punto deberíamos poder probar la biblioteca ejecutando nuestro conjunto de pruebas de la siguiente manera

bazel build //... && bazel run //bibliotecas/humanize_filesize:humanize_filesize_test

Debería poder ver el resultado de la prueba de la siguiente manera, lo que indica que todas las pruebas han pasado.

INFO: Analyzed target //libraries/humanize_filesize:humanize_filesize_test (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //libraries/humanize_filesize:humanize_filesize_test up-to-date:
  bazel-bin/libraries/humanize_filesize/humanize_filesize_test_/humanize_filesize_test
INFO: Elapsed time: 0.392s, Critical Path: 0.24s
INFO: 5 processes: 1 internal, 4 darwin-sandbox.
INFO: Build completed successfully, 5 total actions
INFO: Running command line: external/bazel_tools/tools/test/test-setup.sh libraries/humanize_filesize/humanize_filesize_test_/humanize_filesize_test
exec ${PAGER:-/usr/bin/less} "$0" || exit 1
Executing tests from //libraries/humanize_filesize:humanize_filesize_test
-----------------------------------------------------------------------------
PASS

? ¡¡¡Guau!!! ? Ahora sabemos que nuestra biblioteca funciona según lo previsto.

Ahora usemos esta biblioteca en un servicio service1 dentro de un directorio de servicios que crearemos en la raíz del espacio de trabajo con el siguiente código go y el archivo BUILD.bazel.

servicio1.go

package main

import (
    "basil/libraries/humanize_filesize"
    "fmt"
    "math/rand"
)

func main() {
    v := rand.Int31n(1000000)
    fmt.Printf(`%d bytes = %s\n`, v, humanize_filesize.GetHumanizedFilesize(&v))
}

CONSTRUIR.bazel

load("@rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
    name = "service1_lib",
    srcs = ["service1.go"],
    importpath = "basil/services/service1",
    visibility = ["//visibility:private"],
    deps = ["//libraries/humanize_filesize"],
)

go_binary(
    name = "service1",
    embed = [":service1_lib"],
    visibility = ["//visibility:public"],
)

El código go es bastante simple: importa nuestra biblioteca que declaramos anteriormente y usa la función GetHumanizedFilesize de nuestra biblioteca y pasa un valor entero aleatorio e imprime la salida.

Ahora, cuando ejecute bazel build //services/service1, bazel resolverá todas las dependencias para nuestro objetivo, incluida la biblioteca que desarrollamos y las construirá.

service1 ahora se puede ejecutar usando bazel run //services/service1 ya que solo tenemos un objetivo binario definido. Si tiene más de un objetivo binario, por ejemplo: serviceX, puede ejecutarlo usando bazel run //services/service1:serviceX. De forma predeterminada, cuando no se especifica un destino, bazel siempre intentará encontrar un destino binario con el mismo nombre que el directorio y ejecutarlo.

Entonces... ahí lo tienes. Hemos creado su primera biblioteca compartida que puede ser utilizada por otras partes de nuestro monorepo.

Todo el código para este ejemplo se puede encontrar en https://github.com/nixclix/basil/pull/3/commits/61c673b8757860bd5e60eb2ab6c35f3f4da78c87

Si te gusta el contenido de esta publicación, no dudes en compartirla. Además, suscríbete y deja comentarios sobre lo que piensas sobre esta publicación y si hay cosas que te gustaría que mejorara.

Declaración de liberación Este artículo se reproduce en: https://dev.to/nikhildev/a-practical-example-of-shared-libraries-in-a-monorepo-2548?1 Si hay alguna infracción, comuníquese con [email protected] para borrarlo
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3