Pipelines CI/CD rápidos y fáciles con versiones semánticas para cualquier lenguaje o herramienta. El lanzamiento de una nueva versión de una herramienta debe ser aburrido. Sin ceremonia, sin planificación de reuniones, ni siquiera una discusión. Debe ser transparente, sin esfuerzo, confiable e incluso informativo - más sobre eso más adelante. Sin embargo, como consultor de software que ayuda a los equipos a implementar soluciones de DevOps, a menudo encuentro que esto no es el caso.Lo que encuentro varía enormemente en calidad, así que en lugar de decirte lo que no me gusta, nos centremos en cómo me gusta estructurar mis tuberías para embarcar nuevos repositorios de forma rápida y fácil. Para comprender mi perspectiva, me gustaría compartir cómo mis puntos de vista sobre el tema han evolucionado a lo largo de los años. Actualmente, aplico un proceso que involucra el arreglo de “blocos LEGO” de flujos de trabajo modulares con pequeños ajustes, como cambios de parámetros. Idealmente, me apunto a los estándares que me permiten simplemente copiar y pegar los mismos bloques de construcción de un proyecto a otro cuando comparten el mismo tipo. Por ejemplo, el proceso de construcción de la biblioteca de Rust X es probablemente el mismo que la biblioteca de Rust Y, y el servidor de nodo foo es probablemente el mismo que la barra de servidor de nodo. A lo largo de los años, he intentado montar componentes de código abierto para lograr esto, pero nunca he encontrado la combinación adecuada. Pero primero... Me doy cuenta de que la versión y el lanzamiento no son los temas más emocionantes; sin embargo, es un problema que enfrenta cada proyecto, incluso cuando está codificado con vibe (probablemente especialmente!). Doing it right will make your project look much more professional and communicate changes to its users. This establishes more trust in your projects by helping developers understand and adopt new changes in a clear and consistent way. En el inicio... Back at the beginning of my software engineering career, releases were quite the event. They were the byproduct of milestones, achieved through a series of sprints, planned, to the best of everyone’s ability, in a series of planning meetings where the backlog was scoured through, and tasks that were worthy were chosen. Haríamos todo lo posible para escribir una especificación de un plan, y estimar un número de Fibonacci o el tamaño de la camiseta, o en el peor de los casos, un número de días. El lanzamiento fue la culminación de este proceso. La entrega de entregables, enviado a los usuarios reales. Como la culminación de este proceso, y las altas apuestas por conseguirlo bien, el lanzamiento fue un momento emocional. ¿Excitante ? ¿El miedo? Quizás algunos de los que has desencadenado han tenido tu corazón en la carrera mientras ponen las máquinas en movimiento. There’s also the detail and drama of deciding a version number. ¿Esta es una versión grande? una versión completamente nueva?? menor? patch? A veces el marketing dicta la versión... 2.0! En este punto de mi carrera, en gran parte no fue mi opinión la que importaba en este tema de números de versiones, y mirando atrás, probablemente no debería haber sido de nadie (verá lo que quiero decir más adelante). I was not responsible for releases in the beginning… However… that would not last forever. En fuente abierta En algún momento de mi carrera, me interesé en contribuir al código abierto. En general, en forma de módulos npm, ya que estaba muy familiarizado con Node en ese momento. Fue mi turno ser el encargado de la liberación. No recuerdo cómo, tal vez a través de algún boletín de noticias o tal vez parte de la reunión de NYC NodeJS que Matt Walters y yo solíamos ejecutar juntos - me encontré con la biblioteca. Semantic Release implementó un principio, con el que vibré como desarrollador web, de una separación de preocupaciones basada en la semántica. semantic-release Before you go run off and start using semantic-release, there are some limitations I encountered with it… And I’ll get to those in a moment. En primer lugar, en un nivel alto, la idea básica que predicaba la publicación semántica era que seguiría un formato específico para sus mensajes de compromiso, y en base a los mensajes, se podría calcular la versión. Hablaba de eliminar la emoción de los lanzamientos, convirtiéndolos en robots, basándose en lo que realmente ha cambiado. Si hubo una nueva versión principal – v2 a v3, por ejemplo, eso significaba que hubo un cambio revolucionario. To signify this in your commit message, you’d include en el cuerpo del comité. BREAKING CHANGE: feat: revamp user auth flow BREAKING CHANGE: Per Chad's "game-changer" vision in the 3am Slack rant, we've ditched the old auth system for a blockchain-based solution because "passwords are so 2024." Update your clients or enjoy the 500 errors! Las versiones más pequeñas, como v2.0.0 a v2.1.0, significaron que se agregó una nueva característica. feat: add dark mode toggle Per the 47-comment thread in the "urgent" ticket, users can now save their retinas. Dark mode added, but brace for the inevitable "make it darker" feedback. Versiones de patches señalaron correcciones, o refactores, o básicamente cualquier otra cosa que debería desencadenar un nuevo lanzamiento. fix: revert "fix" by AI that skipped the breaking tests to avoid the failure Y finalmente, algunos compromisos no deberían desencadenar un lanzamiento en absoluto. “Chores” como me fueron introducidos. No-ops, como los llamo. chore: update release pipeline version from v3.1.0 to v3.1.1 Este formato específico de mensajes de compromiso era conocido como Puedes consultar su página para ver la especificación completa, pero quería compartir la veracidad de ella. Comisiones convencionales Además, esta información contenida en los compromisos también podría aparecer como un registro de cambios, como destacar un cambio de ruptura. What's new in v2.0.0 * feat: remove deprecated API BREAKING CHANGE: the FooBar API that was deprecated. To upgrade, you can use the new BazBar API. Así que, por un tiempo, la publicación semántica fue genial para mí. Para los proyectos de nodo, todavía es... Sin embargo... En el DevOps Siempre me interesó la idea de cómo construir sistemas distribuidos escalables, y así fue algo que perseguí durante bastante tiempo en mi carrera. historia corta, finalmente aprendí cómo hacerlo bien, pero tuve un nuevo problema: cómo desplegar todas las piezas. Estos no eran módulos npm; eran aplicaciones, servicios, bases de datos y colas! Me gustó la simplicidad de la publicación semántica, pero solo funcionó bien para los proyectos de Node.js. archivo a proyectos no nodos, pero eso era confuso y hacky en el mejor de los casos. package.json Around this time, I became deeply involved in DevOps, during the Jenkins era, as Docker was starting to gain traction. I wrote about ¡He aprendido a escribir tuberías con Jenkins y a desplegar todo con Docker Swarm! Mi primer viaje de DevOps y el descubrimiento de Docker para su implementación. También estaba abrazando el desarrollo basado en el tronco en ese momento, y comprometido con el dominio. mi artículo más reciente y aún relevante y exacto Guía para el desarrollo basado en el tronco explica el por qué y cómo de eso si está interesado. Yo también estaba abrazando el desarrollo basado en el tronco en ese momento, y Mi más reciente y aún relevante y exacta El artículo explica el porqué y cómo de eso si estás interesado. Comprometido con el Maestro Guía para ir con el desarrollo basado en trunk I always wanted these projects that I worked with to be versioned and released like semantic-release projects were, but I never found a solution that worked as well. Many wouldn’t be able to handle generating changelogs correctly, and some would sporadically bump the version wrong. No era lo más importante en mi lista, así que generalmente lo llamé lo suficientemente bueno y viví con los defectos. Con el tiempo, como consultor de DevOps, he encontrado y publicado varios proyectos en varios idiomas y marcos, y cada uno de ellos ha requerido versiones, versiones y cambios. Otro dolor de cabeza que a menudo encontré fueron las grandes tuberías monolíticas. Vino de la era de Jenkins, lo encuentro, así es como solía escribir tuberías también. Intentaría tener todo el flujo de liberación en una gran serie de pasos, comenzando con un empuje al principal. Si empujaste al principal, funcionarían varios controles de calidad, y se construiría, publicaría, etiquetaría y lanzaría una nueva versión. Esto no es terrible en sí mismo y también probablemente "bastante bueno" en la mayoría de los casos; sin embargo, como consultor de DevOps, tengo que hacerlo repetidamente. rompiendo las cosas en piezas más pequeñas y más modulares permite que esas piezas sirvan a más proyectos. , an idea that comes from embracing UNIX philosophies. La cobertura de las ideas Por ejemplo, un tubo de Node de calidad puede ser el mismo en cada proyecto de Node, asumiendo que se siguen las convenciones de script npm estándar, por lo que un tubo genérico para ejecutar el linting, la construcción y las pruebas de unidades puede ser compartido en la mayoría, si no todos, los proyectos de Node.js. y etc., y la sección de escritos de la El archivo determina lo que sucede cuando command is run. npm run lint --if-present package.json lint Pipelines de alto nivel para la versión y la liberación En un nivel alto, cada proyecto que necesita ser lanzado primero será etiquetado con un nuevo número de versión. Después, se puede crear una versión que está vinculada a ese número de versión, y se producirán artefactos como parte de sus tuberías asociadas. I use GitHub Actions most frequently, as I think pipelines are commodities, and it’s a simple one to get started with. I’ve used many, but they are all essentially the same — perform a series of steps with a shared volume. That shared volume generally contains the checked-out source code. La versión en sí misma, si estamos siguiendo el estándar de los compromisos convencionales vinculados a un aumento de la versión semántica, no es una cosa específica del idioma. Durante mucho tiempo, he atado las versiones a las versiones, pensando que eran parte de la misma cosa, cuando en realidad son dos entidades distintas unidas. Dividiéndolos por separado, la parte de versión de la tubería se convirtió en su propia pieza modular, que luego desencadenó una construcción de esa versión y su posterior lanzamiento. Déjame descomponerlo... Para cada proyecto, en GitHub Actions, tengo dos pipelines: On commit to the trunk branch, version, and tag When a commit occurs in the trunk branch (usually or ), trigger a pipeline that runs quality checks, and, if they pass, calculates a new version number. It then creates and pushes a tag with that new version number. We can also use this opportunity to update that version number in the code base if necessary, and generate a change log from the commit data that was used to calculate the new version number. main master When a tag is created, build new versions and release them. This step is simplified by the previous step of doing the version bump. Everything’s already been bumped to the new version. We can just run build and release using the tag as the base. Como mencioné anteriormente, después de intentar muchas veces reunir componentes de código abierto del mercado de acciones de GitHub, finalmente me he rendido y he escrito mi propia herramienta que maneja el paso uno. Introducción Siguiente Siguiente vnext es una herramienta Rust CLI rápida que utiliza los mensajes de comisión convencionales para calcular su próxima versión semántica, automatizando los golpes de mayor, menor o patch para versiones simplificadas. Aquí está cómo usarlo. NEXT_VERSION=v`vnext` vnext --changelog > CHANGELOG.md Me parece muy sencillo, dime lo que piensas. Está basado en la historia del proyecto. No importa el idioma o la herramienta que estés liberando; el proceso de versión es básicamente el mismo. Obtén una nueva versión, actualízala en algunos archivos, crea un changelog, etiqueta y empuje. La única diferencia es qué archivos deben ser actualizados - esto se cuenta en el flujo de trabajo que compartiré en un momento. Es fácil y rápido de instalar a través de - El Una vez configurado, puede ejecutar: vnext ubi El “Installer Binario Universal” ubi --project unbounded-tech/vnext También he creado un flujo de trabajo compartido de acciones de GitHub que puede llamar, que maneja todos esos detalles. Aquí está cómo usar el flujo de trabajo compartido: name: On Push Main, Version and Tag on: push: branches: - main - master permissions: packages: write contents: write jobs: version-and-tag: uses: unbounded-tech/workflow-vnext-tag/.github/workflows/workflow.yaml@v1 with: useDeployKey: true changelog: true La mayoría de los proyectos también tendrán que configurar cómo y qué archivos actualizar con el nuevo número de versión. En la actualidad, Se utilizan dos métodos genéricos ( y ) y algunas opciones específicas de idiomas. vnext yqPatches regexPatches Las opciones específicas del idioma son las más fáciles de usar. Dos Uso to update the project’s package files. with.node true npm Utilice la herramienta para corregir campos específicos en un archivo YAML. A menudo uso esto para actualizar los números de versiones en los gráficos de Helm: yqPatches yq version-and-tag: uses: unbounded-tech/workflow-vnext-tag/.github/workflows/workflow.yaml@v1.13.0 secrets: GH_PAT: ${{ secrets.YOUR_ORG_SECRET_PAT }} with: usePAT: true changelog: true yqPatches: | patches: - filePath: helm/values.yaml selector: .image.tag valuePrefix: "v" - filePath: helm/Chart.yaml selector: .version valuePrefix: "v" También aproveché esta oportunidad para compartir cómo usar un Token de Acceso Personal en lugar de una clave de implementación. También aproveché esta oportunidad para compartir cómo usar un Token de Acceso Personal en lugar de una clave de implementación. Uso para encontrar y reemplazar una cadena con el nuevo número de versión insertado. regexPatches sed regexPatches: | patches: - filePath: package/composition.yaml regex: /ghcr.io/org-name/package-name:(.*)/g valuePrefix: ghcr.io/org-name/package-name:v - filePath: README.md regex: /Current version: v[0-9]+\.[0-9]+\.[0-9]+/g valuePrefix: Current version: v También puede utilizar una combinación de opciones. Una nuance sobre las acciones de GitHub Cuando ejecutas una acción en GitHub Actions, por defecto, el trabajador genera un token de autenticación temporal de GitHub. Este token tiene permiso para realizar algunas tareas básicas; sin embargo, nunca se le permite desencadenar otras tuberías. Pero nuestro siguiente paso después de etiquetar una nueva versión fue desencadenar otro tubo con esa etiqueta! Do not fret, this is by design — simply a measure by GitHub to prevent unintentionally spinning up a bunch of runners. You are allowed to do so, but you need to be intentional about it. Hay algunas maneras de ser intencional acerca de ello - ya he mostrado un ejemplo de dos: Uso de una clave de despliegue y la correspondiente GitHub Actions Secret Using a Personal Access Token as a GitHub Actions Secret Crear una aplicación de GitHub (teóricamente — no he tenido necesidad de esta opción) El uso de un Token de acceso personal es conveniente si usted es un cliente de pago en el ecosistema de GitHub, ya que los secretos de toda la organización están disponibles. En el nivel gratuito, eso no es una opción, así que te mostraré cómo configurar una clave de implementación en su lugar. De hecho, lo hice muy sencillo al construirlo en el CLI. It is a great option, as when configured this way, no human needs to know it, and it can easily be rotated by rerunning the same command. vnext Para cada repositorio que desee configurar Para liberar con una clave de implementación, en el directorio del proyecto... vnext First, get an auth token using with permission to manage keys: gh gh auth refresh -h github.com -s admin:public_key -s admin:ssh_signing_key export GITHUB_TOKEN=$(gh auth token) Y luego corre: vnext generate-deploy-key Usted puede estar preguntando, ¿puede esto entrar en una tubería en sí? Desafortunadamente, no puedo encontrar una manera de automatizar este paso en Acciones de Github sin crear un Token de Acceso Personal, ya que el token predeterminado no puede modificar Secretos. Esto derrota la puposa de usar una clave de despliegue en lugar de en el nivel libre, ya que necesitaría crearlo en cada repo. Esto requiere algún tipo de gestión secreta para hacerlo fácil, y por lo tanto hace que funcione más que usar una clave de despliegue. Usted puede estar preguntando, ¿puede esto entrar en una tubería en sí? Desafortunadamente, no puedo encontrar una manera de automatizar este paso en Acciones de Github sin crear un Token de Acceso Personal, ya que el token predeterminado no puede modificar Secretos. Esto derrota la puposa de usar una clave de despliegue en lugar de en el nivel libre, ya que necesitaría crearlo en cada repo. Esto requiere algún tipo de gestión secreta para hacerlo fácil, y por lo tanto hace que funcione más que usar una clave de despliegue. Con su clave de implementación, o Token de acceso personal (PAT), en su lugar, y el flujo de trabajo de la versión y la etiqueta que se ejecuta empujando a su rama del tronco, el siguiente paso es liberarlo! Esto varía por proyecto, pero todos son desencadenados por la versión etiquetada desde el paso anterior. name: On Version Tag, Trigger GitHub Release on: push: tags: - 'v*.*.*' permissions: contents: write jobs: release: uses: unbounded-tech/workflow-simple-release/.github/workflows/workflow.yaml@v1 with: tag: ${{ github.ref_name }} name: ${{ github.ref_name }} Or, if it were a Rust project, maybe something like this: name: On Version Tagged, Build and Publish Rust Binaries on: push: tags: - "v*.*.*" permissions: contents: write jobs: build-and-release: uses: unbounded-tech/workflows-rust/.github/workflows/release.yaml@v1 with: binary_name: ${{ github.event.repository.name }} build_args: "--release --features vendored" O si fuera una aplicación con un Dockerfile, y k8s definiciones de recursos para implementaciones de Gitops, tal vez algo así: name: promote on: push: tags: - v*.*.* permissions: contents: write packages: write issues: write pull-requests: write jobs: publish: uses: unbounded-tech/workflows-containers/.github/workflows/publish.yaml@v1.1.1 release: needs: publish uses: unbounded-tech/workflow-simple-release/.github/workflows/workflow.yaml@v1 with: tag: ${{ github.ref_name }} name: ${{ github.ref_name }} promote: needs: release uses: unbounded-tech/workflows-gitops/.github/workflows/helm-promote.yaml@v1 secrets: GH_PAT: ${{ secrets.GH_PAT }} with: environment_repository: your-org/staging-env path: .gitops/deploy project: staging Nota: Una clave de implementación no puede empujar a otro repositorio, como en el paso de promoción de gitops, por lo que puede considerar usar un PAT en todas partes para este repositorio.Si tiene muchas cargas de trabajo como esta, la actualización a un org pagado podría valer la pena para los secretos de la organización en lugar de configurar cada repositorio por separado. Nota: Una clave de implementación no puede empujar a otro repositorio, como en el paso de promoción de gitops, por lo que puede considerar usar un PAT en todas partes para este repositorio.Si tiene muchas cargas de trabajo como esta, la actualización a un org pagado podría valer la pena para los secretos de la organización en lugar de configurar cada repositorio por separado. En general, cada versión es una combinación de algunas piezas de estos módulos similares, que hacen prácticamente las mismas cosas pero para sus herramientas o idiomas específicos. Normalmente, las organizaciones al menos tratan de seguir muchos de los mismos patrones en todas las aplicaciones y servicios que construyen, y así descomponer los flujos de trabajo en piezas modulares compartidas permite a los desarrolladores simplemente copiar y pegar su sabor de estas llamadas de flujo de trabajo compartido a cada proyecto de ese tipo. Publicaciones coherentes con Changelogs Puede que hayas notado una mirada sobre el Quería volver a ese punto, ya que es una parte importante de un proceso de lanzamiento maduro.El changelog es cómo puedes comunicar las maneras en que tu proyecto ha cambiado a otros desarrolladores. with.changelog Junto con las versiones de Bumping, También utiliza los mensajes de compromiso para construir este changelog y lo guarda en un archivo llamado CHANGELOG.md, que se compromete junto con la etiqueta de la versión y los bumps de la versión. vnext Oculto en esos flujos de trabajo de lanzamiento que compartió anteriormente, este archivo se utiliza como el cuerpo de la versión de GitHub. Proyecto en sí: vnext Este lanzamiento era una versión de patch, ya que contenía una corrección, en forma de una versión de dependencia, y una serie de otras tareas. Estas notas de lanzamiento se superponen luego en otras herramientas, como , una herramienta que hace que los PR mantengan sus dependencias actualizadas. Renovado Al fusionar Pull Requests, me gusta usar la función Squash and Merge de GitHub para tener la oportunidad de escribir un buen título y cuerpo del mensaje final de compromiso que se utilizará en el changelog. Por ejemplo: Como se muestra en el ejemplo, puede colocar Markdown completo como el cuerpo de compromiso. ¡se renderizará adecuadamente en las Notas de Release! Conclusion Sé que la versión y el lanzamiento no es el tema más sexy; es bastante aburrido - o mejor dicho, debería ser! Sin embargo, esto no siempre es así.Por eso he creado Y un montón de ¡Ahora, puedo embarcar fácilmente a la mayoría de los proyectos muy rápidamente! vnext Flujos de trabajo compartidos ¡Espero que también sea útil para ti! Por favor, déjame saber si usted da ¡Agradecería algunas estrellas en GitHub y compartirlas si las encuentra útiles! vnext