Compartir un blog de Astro entre dos sitios
Faltaba un dev log en imperfectsystems.com. El blog que ya existe en vdaluz.com funciona bien, así que el movimiento obvio era reutilizar su código en vez de escribir uno segundo. “Reutilizar” resultó ser más interesante de lo esperado.
No se puede extraer un blog
El primer instinto fue “extraer el blog a un paquete.” Eso no existe realmente en Astro. Las rutas viven en src/pages/ y tienen que quedarse en cada app. El contenido es una carpeta de markdown por sitio. La configuración de Shiki, la de Tailwind, y el CSS del tema también son por app. Lo que realmente es portable es más chico de lo que suena: los componentes que renderizan una tarjeta de post, una barra de paginación, la lista de posts relacionados, más la función de puntaje y el schema de contenido.
Así que la versión honesta del objetivo no es “blog compartido.” Es “una pequeña librería de componentes, más algo de pegamento en cada sitio.” Una vez planteado así, el diseño se volvió fácil.
Un paquete, dos paletas
vdaluz.com es un sitio claro/oscuro con acento azul. imperfectsystems es negro con verde neón. Si los componentes compartidos tuvieran algún color escrito a fuego, esto se caería. No lo tienen. Cada componente hace referencia a tokens de diseño en variables CSS como bg, surface, accent. El paquete nunca dice qué significan. Cada sitio define los valores en su propio CSS.
Ese es todo el truco. El mismo PostCard se renderiza azul sobre blanco en un sitio y verde sobre negro en el otro, sin ninguna bifurcación. También se quitó la dependencia del tipo CollectionEntry<'blog'> de Astro a favor de un tipo estructural plano, así el paquete no le importa cómo cada sitio nombra su colección.
Dos formas en que el CI mordió
El paquete compila local a la primera. Después se hizo push, y el CI falló dos veces por razones que nunca aparecen en la laptop.
Primero: una dependencia github:user/repo es reescrita por npm a una URL git+ssh en el lockfile. Incluso una URL git+https explícita se reescribe. Esta máquina tiene una llave SSH así que clona sin problema. El runner de CI no la tiene, así que npm ci no podía traer el paquete de ninguna forma. El arreglo para un repo público es saltarse git por completo y depender de un tarball fijado a un tag: https://github.com/vdaluz/astro-blog/archive/refs/tags/v0.1.0.tar.gz. Https plano, anónimo, con un hash de integridad.
Segundo, y más traicionero: se regeneró el lockfile en la Mac para resolver un conflicto de merge. Eso escribió en silencio solo la build de macOS de rollup en el lockfile y descartó la de Linux. En el runner de CI con Linux, el build murió con Cannot find module @rollup/rollup-linux-x64-gnu. Es un bug conocido de npm con dependencias nativas opcionales. El arreglo fue restaurar el lockfile bueno conocido y agregar la única dependencia nueva con npm install --package-lock-only, que conserva los binarios de cada plataforma en vez de podar solo a la propia.
La lección que se repite: un build verde en la propia máquina no dice nada sobre un runner de Linux sin llave SSH. Ahora se corre npm ci && npm run build en node 22 antes de confiar en un push.
Dónde quedó
imperfectsystems.com/blog está en vivo, renderizando a través del paquete compartido en pleno neón. vdaluz.com todavía tiene su propia copia por ahora, moverlo al paquete es el siguiente paso. Dos sitios, un solo conjunto de componentes, y un par de cicatrices de CI para mostrar.
Lecturas relacionadas
Arrancando una librería compartida de enlaces de afiliados en dos sitios
Cumplimiento por construcción: un plugin de remark que falla el build cuando un enlace de afiliado se publica sin su divulgación, empaquetado igual que los componentes del blog.
Enviar una función de un paquete compartido a través de dos repos para una sola ruta
Un feed RSS que se dividió en un helper puro dentro del paquete compartido y una ruta local por sitio, más la disciplina de release que exige una dependencia de tarball fijada a un tag.
Analítica que pregunta primero: construir una biblioteca de rastreo solo por consentimiento
El script de rastreo no existe hasta que das tu consentimiento: no desactivado, no en pausa, no existe. Así es esta biblioteca de analítica opt-in.
También te podría ser útil
Proton Pass
Gestor de contraseñas centrado en la privacidad, del equipo detrás de Proton Mail.
Como socio de Proton, obtengo ingresos por las compras que califican de los servicios de privacidad y seguridad de Proton (Pass, Mail, VPN, Drive).
Más informaciónProton Mail
Correo electrónico cifrado de extremo a extremo, con arquitectura de acceso cero.
Como socio de Proton, obtengo ingresos por las compras que califican de los servicios de privacidad y seguridad de Proton (Pass, Mail, VPN, Drive).
Más informaciónAdGuard para iOS
Bloqueo de anuncios y rastreadores en todo el sistema en iOS, sin necesidad de un servidor DNS aparte.
Como afiliado de AdGuard, obtengo ingresos por las compras que califican.
Más información