1(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[63005],{19937:(e,n,a)=>{"use strict";a.d(n,{R:()=>i});var o=a(14232),t=a(4047);function i(e){let n=(0,o.useId)();return{...(0,t.o)(n),...e}}},49150:(e,n,a)=>{(window.__NEXT_P=window.__NEXT_P||[]).push(["/blog/automated-releases-part-two",function(){return a(80015)}])},80015:(e,n,a)=>{"use strict";a.r(n),a.d(n,{default:()=>s});var o=a(37876),t=a(19937);function i(e){let n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",img:"img",li:"li",p:"p",pre:"pre",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsxs)(n.p,{children:["tl;dr\n",(0,o.jsx)(n.a,{href:"/blog/automated-releases/",children:"Here"})," you can find the first blog post about automated releasing. The purpose of this blog post is to show how you can do the same automation in non-JavaScript projects. Even if JavaScript community created tooling, you can still use it in other projects and don't freak out."]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["This post and the ",(0,o.jsx)(n.a,{href:"/blog/automated-releases/",children:"previous one"})," come from our experience we gained when working on full automation for all tools maintained by ",(0,o.jsx)(n.a,{href:"https://github.com/asyncapi/asyncapi/",children:"AsyncaPI Initiative"}),"."]}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.a,{href:"https://github.com/asyncapi/asyncapi/",children:"AsyncAPI"})," is a specification that you use to create machine-readable definitions of your event-driven APIs."]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["The previous post focused on JavaScript as the first library that we automated was our ",(0,o.jsx)(n.a,{href:"https://github.com/asyncapi/generator/",children:"generator"}),". It covered publishing to NPM and usage of the JavaScript community ecosystem. Now we have automation rolled out to all our libraries, Go-written too."]}),"\n",(0,o.jsx)(n.h2,{id:"what-i-need-to-automate-release",children:"What I need to automate release?"}),"\n",(0,o.jsx)(n.p,{children:"To automate a release efficiently, you need two things:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Machine-readable information that allows you to identify if a given commit should trigger a release or not."}),"\n",(0,o.jsx)(n.li,{children:"Tooling that you can easily plug in and configure without the need to write everything from scratch."}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"This automation is possible thanks to the following:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["The ",(0,o.jsx)(n.a,{href:"https://www.conventionalcommits.org/en/v1.0.0/",children:"Conventional Commits"})," specification. The purpose of Conventional Commits is to make commits machine-readable but also human-readable. It defines a set of commit prefixes that can be easily parsed and analyzed by tooling and looks good to the human eye too."]}),"\n",(0,o.jsxs)(n.li,{children:["The ",(0,o.jsx)(n.a,{href:"https://github.com/semantic-release/semantic-release",children:"Semantic Release"})," package and related plugins that support Conventional Commits and publishing to different channels like GitHub, NPM, Slack, and others."]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"wheres-the-catch",children:"Where's the catch?"}),"\n",(0,o.jsx)(n.p,{children:"This blog post is about the automation of releases for non-JavaScript projects. Let me be honest though, solutions I mentioned in the previous chapter come from the JavaScript community."}),"\n",(0,o.jsxs)(n.p,{children:["The problem is, there are people who ",(0,o.jsx)(n.a,{href:"https://www.reddit.com/r/javascript/comments/9pwzpn/why_do_people_hate_javascript/",children:"Hate JavaScript"}),", they truly ",(0,o.jsx)(n.a,{href:"https://www.quora.com/Why-is-JavaScript-so-hated",children:"hate it"})," like it is a living thing. Although, I'm personally proud to be an idiot that has a programming language that I can use."]}),"\n",(0,o.jsxs)(n.p,{children:["Conventional Commits specification is heavily inspired by ",(0,o.jsx)(n.a,{href:"https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#commit-message-format",children:"Angular Commit Guidelines"}),". The Semantic Release package and its plugins ecosystem are all Node.js packages."]}),"\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.img,{src:"https://media.giphy.com/media/10FHR5A4cXqVrO/giphy.gif",alt:""})}),"\n",(0,o.jsxs)(n.p,{children:["If you have Java or Go project, you can still use these tools. You do not have to keep ",(0,o.jsx)(n.code,{children:"package.json"})," in your repository, so don't worry, you can keep your repository clean. The great folks from Semantic Release thought about you too."]}),"\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.img,{src:"https://media.giphy.com/media/QynGWwS6GdOMj6cvmz/giphy-downsized.gif",alt:""})}),"\n",(0,o.jsx)(n.h2,{id:"using-semantic-release-with-github-action-in-go-project",children:"Using Semantic Release with GitHub Action in Go project"}),"\n",(0,o.jsxs)(n.p,{children:["One of the projects where we use this JavaScript tools is our parser for AsyncAPI documents. It is a ",(0,o.jsx)(n.a,{href:"https://github.com/asyncapi/parser-go",children:"Go parser"}),"."]}),"\n",(0,o.jsx)(n.h3,{id:"semantic-release-configuration",children:"Semantic Release configuration"}),"\n",(0,o.jsxs)(n.p,{children:["The Semantic Release package supports configuration files in different formats and file types. You are not bound to ",(0,o.jsx)(n.code,{children:"package.json"}),". We chose to use ",(0,o.jsx)(n.code,{children:".releaserc"})," file in YAML format but there are ",(0,o.jsx)(n.a,{href:"https://github.com/semantic-release/semantic-release/blob/master/docs/usage/configuration.md#configuration-file",children:"other options"})," too."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:'---\nbranches:\n- master\nplugins:\n- - "@semantic-release/commit-analyzer"\n - preset: conventionalcommits\n- - "@semantic-release/release-notes-generator"\n - preset: conventionalcommits\n- - "@semantic-release/github"\n - assets:\n - path: asyncapi-parser.darwin.amd64\n label: Binary - Darwin AMD64\n - path: asyncapi-parser.linux.amd64\n label: Binary - Linux AMD64\n - path: asyncapi-parser.windows.amd64.exe\n label: Binary - Windows AMD64\n'})}),"\n",(0,o.jsx)(n.p,{children:"Our configuration uses plugins to:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Analyze Git commits with Conventional Commits specification."}),"\n",(0,o.jsx)(n.li,{children:"Create a Git tag and generate changelog for release notes."}),"\n",(0,o.jsx)(n.li,{children:"Publish a release with additional assets. We compile our parser as binaries that are compatible with many platforms and we want to have them easily accessible with each release."}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["We place the configuration under ",(0,o.jsx)(n.code,{children:".github/workflows/"}),", next to our GitHub Action release workflow file: ",(0,o.jsx)(n.code,{children:"release.yml"}),". It indicates that it is for release only, nothing else."]}),"\n",(0,o.jsx)(n.h3,{id:"release-workflow",children:"Release workflow"}),"\n",(0,o.jsxs)(n.p,{children:["Let us have a look at the differences between this workflow and the workflow I described for a typical JavaScript project ",(0,o.jsx)(n.a,{href:"/blog/automated-releases/",children:"here"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["First, you define a ",(0,o.jsx)(n.code,{children:"test"})," job with the Go environment to trigger tests with different versions of Go."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"test:\n name: 'Testing'\n runs-on: ubuntu-latest\n strategy:\n matrix:\n go: \n - '1.14'\n - '1.13'\n - '1.12' \n steps:\n - name: Checkout repo\n uses: actions/checkout@v2\n - name: Setup Go\n uses: actions/[email protected]\n with:\n go-version: '${{ matrix.go }}'\n - name: Invoking go test\n run: go test ./...\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The next step is the ",(0,o.jsx)(n.code,{children:"release"})," job, where you can differentiate two core steps. The first part is the generation of the binaries that you want to expose in the GitHub release."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"- name: Setup Go\n uses: actions/[email protected]\n with:\n go-version: '1.14'\n- name: Invoking go vet and binaries generation\n run: |\n go vet ./...\n GOOS=darwin GOARCH=amd64 go build -o=.github/workflows/asyncapi-parser.darwin.amd64 ./cmd/api-parser/main.go\n GOOS=linux GOARCH=amd64 go build -o=.github/workflows/asyncapi-parser.linux.amd64 ./cmd/api-parser/main.go\n GOOS=windows GOARCH=amd64 go build -o=.github/workflows/asyncapi-parser.windows.amd64.exe ./cmd/api-parser/main.go\n"})}),"\n",(0,o.jsxs)(n.p,{children:["So far, it is all Go-related operations. How about the release? For the release, you need to set up a Node.js environment to run Semantic Release. Node.js community has this excellent package, ",(0,o.jsx)(n.a,{href:"https://www.npmjs.com/package/npx",children:"npx"}),", that allows you to run a package without installing it, and this is what you can do here in the workflow."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"- name: Setup Node.js\n uses: actions/setup-node@v1\n with:\n node-version: 13\n- name: Add plugin for conventional commits\n run: npm install c
1onventional-changelog-conventionalcommits\n working-directory: ./.github/workflows\n- name: Release to GitHub\n working-directory: ./.github/workflows\n env:\n GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}\n GIT_AUTHOR_NAME: asyncapi-bot\n GIT_AUTHOR_EMAIL: [email protected]\n GIT_COMMITTER_NAME: asyncapi-bot\n GIT_COMMITTER_EMAIL: [email protected]\n run: npx semantic-release\n"})}),"\n",(0,o.jsxs)(n.p,{children:["You only have to install ",(0,o.jsx)(n.code,{children:"conventional-changelog-conventionalcommits"})," explicitly if you want to use ",(0,o.jsx)(n.code,{children:"conventionalcommits"})," preset when analyzing Git commits and generating the changelog:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{children:'plugins:\n- - "@semantic-release/commit-analyzer"\n - preset: conventionalcommits\n- - "@semantic-release/release-notes-generator"\n - preset: conventionalcommits\n'})}),"\n",(0,o.jsx)(n.p,{children:"Take a look at full release workflow for reference:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"name: Release\n\non:\n push:\n branches:\n - master\n\njobs:\n test:\n name: 'Testing'\n runs-on: ubuntu-latest\n strategy:\n matrix:\n go: \n - '1.14'\n - '1.13'\n - '1.12' \n steps:\n - name: Checkout repo\n uses: actions/checkout@v2\n - name: Setup Go\n uses: actions/[email protected]\n with:\n go-version: '${{ matrix.go }}'\n - name: Invoking go test\n run: go test ./...\n \n release:\n name: 'Release to GitHub'\n runs-on: ubuntu-latest\n needs: \n - test\n steps:\n - name: Checkout repo\n uses: actions/checkout@v2\n - name: Setup Go\n uses: actions/[email protected]\n with:\n go-version: '1.14'\n - name: Invoking go vet and binaries generation\n run: |\n go vet ./...\n GOOS=darwin GOARCH=amd64 go build -o=.github/workflows/asyncapi-parser.darwin.amd64 ./cmd/api-parser/main.go\n GOOS=linux GOARCH=amd64 go build -o=.github/workflows/asyncapi-parser.linux.amd64 ./cmd/api-parser/main.go\n GOOS=windows GOARCH=amd64 go build -o=.github/workflows/asyncapi-parser.windows.amd64.exe ./cmd/api-parser/main.go\n - name: Setup Node.js\n uses: actions/setup-node@v1\n with:\n node-version: 13\n - name: Add plugin for conventional commits\n run: npm install conventional-changelog-conventionalcommits\n working-directory: ./.github/workflows\n - name: Release to GitHub\n working-directory: ./.github/workflows\n env:\n GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}\n GIT_AUTHOR_NAME: asyncapi-bot\n GIT_AUTHOR_EMAIL: [email protected]\n GIT_COMMITTER_NAME: asyncapi-bot\n GIT_COMMITTER_EMAIL: [email protected]\n run: npx semantic-release\n"})}),"\n",(0,o.jsx)(n.p,{children:'You see, you can still have your project "clean" from any JavaScript-specific files and references. Everything you need for running your release with the JavaScript community tooling is only in the release-related configuration.'}),"\n",(0,o.jsx)(n.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,o.jsx)(n.p,{children:'I don\'t think I can ever understand this "hate" towards JavaScript. I think, though, that you can "hate" the language, but if you see some amazing tooling built with it, that can increase your productivity, grit your teeth, put bias aside, and enjoy life. Especially, if in exchange you get this excellent feature, notification about release under the Issue and Pull Request:'}),"\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.img,{src:"/img/posts/pr-indicator.webp",alt:"pr info about release"})}),"\n",(0,o.jsxs)(n.p,{children:["In case you want to have more explanation on the release automation subject, I recommend reading ",(0,o.jsx)(n.a,{href:"/blog/automated-releases/",children:"the first part of the automation story"}),". You can also ",(0,o.jsx)(n.a,{href:"https://www.asyncapi.com/slack-invite/",children:"join our Slack"})," for further discussion."]}),"\n",(0,o.jsx)(n.p,{children:(0,o.jsxs)(n.em,{children:["* Cover photo by ",(0,o.jsx)(n.a,{href:"https://unsplash.com/@rocknrollmonkey",children:"Rock'n Roll Monkey"})," on Unsplash"]})})]})}function s(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(i,{...e})}):i(e)}}},e=>{e.O(0,[56166,90096,6729,9583,51550,4047,90636,46593,38792],()=>e(e.s=49150)),_N_E=e.O()}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.