{"id":38908,"date":"2022-02-25T09:22:00","date_gmt":"2022-02-25T16:22:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=38908"},"modified":"2022-03-09T14:47:46","modified_gmt":"2022-03-09T21:47:46","slug":"dotnet-loves-github-actions","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/dotnet-loves-github-actions\/","title":{"rendered":".NET \ud83d\udc9c GitHub Actions: Intro to GitHub Actions for .NET"},"content":{"rendered":"<p>Hi friends, I put together posts where I&#8217;m going to teach you the basics of the <a href=\"https:\/\/github.com\/features\/actions\">GitHub Actions<\/a> platform. In this post, you&#8217;ll learn how GitHub Actions can improve your .NET development experience and team productivity. I&#8217;ll show you how to use them to automate common .NET app dev scenarios with workflow composition. <\/p>\n<h2>An introduction to GitHub Actions<\/h2>\n<p>Developers that use GitHub for managing their git repositories have a powerful continuous integration (CI) and continuous delivery (CD) feature with the help of GitHub Actions. A common developer scenario is when developers propose changes to the default branch (typically <code>main<\/code>) of a GitHub repository. These changes, while often scrutinized by reviewers, can have automated checks to ensure that the code compiles and tests pass.<\/p>\n<p>GitHub Actions allow you to build, test, and deploy your code right from your source code repository on <a href=\"https:\/\/github.com\">https:\/\/github.com<\/a>. GitHub Actions are consumed by GitHub workflows. A GitHub workflow is a YAML (either <em>*.yml<\/em> or <em>*.yaml<\/em>) file within your GitHub repository. These workflow files reside in the <em>.github\/workflows\/<\/em> directory from the root of the repository. A workflow references one or more GitHub Action(s) together as a series of instructions, where each instruction executes a specific task.<\/p>\n<h3>The GitHub Action terminology<\/h3>\n<p>To avoid mistakenly using some of these terms inaccurately, let&#8217;s define them:<\/p>\n<ul>\n<li><strong>GitHub Actions<\/strong>: <em>GitHub Actions<\/em> is a continuous integration and continuous delivery (CI\/CD) platform that allows you to automate your build, test, and deployment pipeline.<\/li>\n<li><strong>workflow<\/strong>: A <em>workflow<\/em> is a configurable automated process that will run one or more jobs.<\/li>\n<li><strong>event<\/strong>: An <em>event<\/em> is a specific activity in a repository that triggers a workflow run.<\/li>\n<li><strong>job<\/strong>: A <em>job<\/em> is a set of steps in a workflow that execute on the same runner.<\/li>\n<li><strong>action<\/strong>: An <em>action<\/em> is a custom application for the GitHub Actions platform that performs a complex but frequently repeated task.<\/li>\n<li><strong>runner<\/strong>: A <em>runner<\/em> is a server that runs your workflows when they&#8217;re triggered.<\/li>\n<\/ul>\n<p>For more information, see <a href=\"https:\/\/docs.github.com\/actions\/learn-github-actions\/understanding-github-actions\">GitHub Docs: Understanding GitHub Actions<\/a><\/p>\n<h3>Inside the GitHub workflow file<\/h3>\n<p>A workflow file defines a sequence of <code>jobs<\/code> and their corresponding <code>steps<\/code> to follow. Each workflow has a <code>name<\/code> and a set of triggers, or events to act <code>on<\/code>. You have to specify at least one trigger for your workflow to run unless it&#8217;s a <a href=\"https:\/\/github.blog\/2022-02-10-using-reusable-workflows-github-actions\">reusable workflow<\/a>. A common .NET GitHub workflow would be to <em>build<\/em> and <em>test<\/em> your C# code when changes are either pushed or when there&#8217;s a pull request targeting the default branch. Consider the following workflow file:<\/p>\n<pre><code class=\"language-yml\">name: build and test\r\non:\r\n  push:\r\n  pull_request:\r\n    branches: [ main ]\r\n    paths-ignore:\r\n    - 'README.md'\r\nenv:\r\n  DOTNET_VERSION: '6.0.x'\r\njobs:\r\n  build-and-test:\r\n    name: build-and-test-${{matrix.os}}\r\n    runs-on: ${{ matrix.os }}\r\n    strategy:\r\n      matrix:\r\n        os: [ubuntu-latest, windows-latest, macOS-latest]\r\n    steps:\r\n    - uses: actions\/checkout@v2\r\n    - name: Setup .NET\r\n      uses: actions\/setup-dotnet@v1\r\n      with:\r\n        dotnet-version: ${{ env.DOTNET_VERSION }}\r\n    - name: Install dependencies\r\n      run: dotnet restore\r\n    - name: Build\r\n      run: dotnet build --configuration Release --no-restore\r\n    - name: Test\r\n      run: dotnet test --no-restore --verbosity normal<\/code><\/pre>\n<p>I&#8217;m not going to assume that you have a deep understanding of this workflow, and while it&#8217;s less than thirty lines \u2014 there is still a lot to unpack. I put together a sequence diagram (powered by <a href=\"https:\/\/mermaid-js.github.io\/mermaid\">Mermaid<\/a>), that shows how a developer might visualize this workflow.<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-38912\" role=\"img\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/02\/github-workflow-sequence-diagram-1.svg\" alt=\"GitHub build and test workflow sequence diagram.\" width=\"1548\" height=\"906\" \/><\/p>\n<p>Here&#8217;s the same workflow file, but this time it is expanded with inline comments to add context (if you&#8217;re already familiar with the <a href=\"https:\/\/docs.github.com\/actions\/using-workflows\/workflow-syntax-for-github-actions?wt.mc_id=dapine\">workflow syntax<\/a>, feel free to skip past this):<\/p>\n<pre><code class=\"language-yml\"># The name of the workflow.\r\n# This is the name that's displayed for status\r\n# badges (commonly embedded in README.md files).\r\nname: build and test\r\n\r\n# Trigger this workflow on a push, or pull request to\r\n# the main branch, when either C# or project files changed\r\non:\r\n  push:\r\n  pull_request:\r\n    branches: [ main ]\r\n    paths-ignore:\r\n    - 'README.md'\r\n\r\n# Create an environment variable named DOTNET_VERSION\r\n# and set it as \"6.0.x\"\r\nenv:\r\n  DOTNET_VERSION: '6.0.x' # The .NET SDK version to use\r\n\r\n# Defines a single job named \"build-and-test\"\r\njobs:\r\n  build-and-test:\r\n\r\n    # When the workflow runs, this is the name that is logged\r\n    # This job will run three times, once for each \"os\" defined\r\n    name: build-and-test-${{matrix.os}}\r\n    runs-on: ${{ matrix.os }}\r\n    strategy:\r\n      matrix:\r\n        os: [ubuntu-latest, windows-latest, macOS-latest]\r\n\r\n    # Each job run contains these five steps\r\n    steps:\r\n\r\n    # 1) Check out the source code so that the workflow can access it.\r\n    - uses: actions\/checkout@v2\r\n\r\n    # 2) Set up the .NET CLI environment for the workflow to use.\r\n    #    The .NET version is specified by the environment variable.\r\n    - name: Setup .NET\r\n      uses: actions\/setup-dotnet@v1\r\n      with:\r\n        dotnet-version: ${{ env.DOTNET_VERSION }}\r\n\r\n    # 3) Restore the dependencies and tools of a project or solution.\r\n    - name: Install dependencies\r\n      run: dotnet restore\r\n\r\n    # 4) Build a project or solution and all of its dependencies.\r\n    - name: Build\r\n      run: dotnet build --configuration Release --no-restore\r\n\r\n    # 5) Test a project or solution.\r\n    - name: Test\r\n      run: dotnet test --no-restore --verbosity normal<\/code><\/pre>\n<p>The preceding workflow file contains many comments to help detail each area of the workflow. You might have noticed that the <code>steps<\/code> define various usages of GitHub Actions or simple <code>run<\/code> commands. The relationship between a GitHub Action and a consuming GitHub workflow is that workflows consume actions. A GitHub Action is only as powerful as the consuming workflow. Workflows can define anything from simple tasks to elaborate compositions and everything in between. For more information on creating GitHub workflows for .NET apps, see the following .NET docs resources:<\/p>\n<ul>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/devops\/dotnet-build-github-action?wt.mc_id=dapine\">Create a build validation workflow<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/devops\/dotnet-test-github-action?wt.mc_id=dapine\">Create a test validation workflow<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/devops\/dotnet-publish-github-action?wt.mc_id=dapine\">Create a deploy workflow<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/devops\/dotnet-secure-github-action?wt.mc_id=dapine\">Create a CodeQL security vulnerability scanning CRON job workflow<\/a><\/li>\n<\/ul>\n<p>I hope that you&#8217;re asking yourself, &#8220;why is this important?&#8221; Sure, we can create GitHub Actions, and we can compose workflows that consume them \u2014 but why is that important?! That answer is GitHub status checks \ud83e\udd13.<\/p>\n<h2>GitHub status checks<\/h2>\n<p>One of the primary benefits of using workflows is to define conditional status checks that can deterministically fail a build. A workflow can be configured as a status check for a pull request (PR), and if the workflow fails, for example the source code in the pull request doesn&#8217;t compile \u2014 the PR can be blocked from being merged. Consider the following screen capture, which shows that two checks have failed, thus blocking the PR from being merged.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/02\/failing-github-status-checks.png\" alt=\"Example GitHub pull request with failing status checks.\" \/><\/p>\n<p>As the developer who is responsible for reviewing a PR, you&#8217;d immediately see that the pull request has failing status checks. You&#8217;d work with the developer who proposed the PR to get all of the status checks to pass. The following is a screen capture showing a &#8220;green build&#8221;, a build that has all of its status checks as passing.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/02\/passing-github-status-checks.png\" alt=\"Example GitHub pull request with passing status checks.\" \/><\/p>\n<p>For more information, see <a href=\"https:\/\/docs.github.com\/pull-requests\/collaborating-with-pull-requests\/collaborating-on-repositories-with-code-quality-features\/about-status-checks?wt.mc_id=dapine\">GitHub Docs: GitHub status checks<\/a>.<\/p>\n<h2>GitHub Actions that .NET developers should know<\/h2>\n<p>As a .NET developer, you&#8217;re likely familiar with the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/tools?wt.mc_id=dapine\">.NET CLI<\/a>. The .NET CLI is included with the .NET SDK. If you don&#8217;t already have the .NET SDK, you can <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/6.0?wt.mc_id=dapine\">download the .NET 6 SDK<\/a>.<\/p>\n<p>Using the previous workflow file as a point of reference, there are five steps \u2014 each step includes either the <code>run<\/code> or <code>uses<\/code> syntax:<\/p>\n<table>\n<thead>\n<tr>\n<th>Action or command<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>uses: actions\/checkout@v2<\/code><\/td>\n<td>This action checks-out your repository under <code>$GITHUB_WORKSPACE<\/code>, so your workflow can access it. For more information, see <a href=\"https:\/\/github.com\/actions\/checkout?wt.mc_id=dapine\">actions\/checkout<\/a><\/td>\n<\/tr>\n<tr>\n<td><code>uses: actions\/setup-dotnet@v1<\/code><\/td>\n<td>This action sets up a .NET CLI environment for use in actions. For more information, see <a href=\"https:\/\/github.com\/actions\/setup-dotnet?wt.mc_id=dapine\">actions\/setup-dotnet<\/a><\/td>\n<\/tr>\n<tr>\n<td><code>run: dotnet restore<\/code><\/td>\n<td>Restores the dependencies and tools of a project or solution. For more information, see <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/tools\/dotnet-restore?wt.mc_id=dapine\">dotnet restore<\/a><\/td>\n<\/tr>\n<tr>\n<td><code>run: dotnet build<\/code><\/td>\n<td>Builds the project or solution. For more information, see <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/tools\/dotnet-build?wt.mc_id=dapine\">dotnet build<\/a><\/td>\n<\/tr>\n<tr>\n<td><code>run: dotnet test<\/code><\/td>\n<td>Runs the tests for the project or solution. For more information, see <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/tools\/dotnet-test?wt.mc_id=dapine\">dotnet test<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Some <code>steps<\/code> rely on GitHub Actions and reference them with the <code>uses<\/code> syntax, while others <code>run<\/code> commands. For more information on the differences, see Workflow syntax for GitHub Actions: <a href=\"https:\/\/docs.github.com\/actions\/using-workflows\/workflow-syntax-for-github-actions?wt.mc_id=dapine#jobsjob_idstepsuses\"><code>uses<\/code><\/a> and <a href=\"https:\/\/docs.github.com\/actions\/using-workflows\/workflow-syntax-for-github-actions?wt.mc_id=dapine#jobsjob_idstepsrun\"><code>run<\/code><\/a>.<\/p>\n<p>.NET applications rely on NuGet packages. You can optimize your workflows by caching various dependencies that change infrequently, such as NuGet packages. As an example, you can use the <code>actions\/cache<\/code> to cache NuGet packages:<\/p>\n<pre><code class=\"language-yml\">steps:\r\n- uses: actions\/checkout@v2\r\n- name: Setup dotnet\r\n  uses: actions\/setup-dotnet@v1\r\n  with:\r\n    dotnet-version: '6.0.x'\r\n- uses: actions\/cache@v2\r\n  with:\r\n    path: ~\/.nuget\/packages\r\n    # Look to see if there is a cache hit for the corresponding requirements file\r\n    key: ${{ runner.os }}-nuget-${{ hashFiles('**\/packages.lock.json') }}\r\n    restore-keys: |\r\n      ${{ runner.os }}-nuget\r\n- name: Install dependencies\r\n  run: dotnet add package Newtonsoft.Json --version 12.0.1\r\n<\/pre>\n<p><\/code><\/p>\n<p>For more information, see <a href=\"https:\/\/docs.github.com\/actions\/automating-builds-and-tests\/building-and-testing-net#caching-dependencies\">GitHub Docs: Building and testing .NET - Caching dependencies<\/a>.<\/p>\n<p>In addition to using the standard GitHub Actions or invoking .NET CLI commands using the <code>run<\/code> syntax, you might be interested in learning about some additional GitHub Actions.<\/p>\n<h3>Additional GitHub Actions<\/h3>\n<p>Several .NET GitHub Actions are hosted on the <a href=\"https:\/\/github.com\/dotnet?wt.mc_id=dapine\">dotnet GitHub organization<\/a>:<\/p>\n<table>\n<thead>\n<tr>\n<th>.NET GitHub Action<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/versionsweeper?wt.mc_id=dapine\">dotnet\/versionsweeper<\/a><\/td>\n<td>This action sweeps .NET repos for out-of-support target versions of .NET. The .NET docs team uses the .NET version sweeper GitHub Action to automate issue creation. The action runs as a <em>cron job<\/em> (or on a schedule). When it detects that .NET projects target out-of-support versions, it creates issues to report its findings. The output is configurable and helpful for tracking .NET version support concerns.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/code-analysis?wt.mc_id=dapine\">dotnet\/code-analysis<\/a><\/td>\n<td>This action runs the code analysis rules that are included in the .NET SDK as part of continuous integration (CI). The action runs both <a href=\"https:\/\/docs.microsoft.com\/dotnet\/fundamentals\/code-analysis\/quality-rules?wt.mc_id=dapine\">code-quality (CAXXXX) rules<\/a> and <a href=\"https:\/\/docs.microsoft.com\/dotnet\/fundamentals\/code-analysis\/style-rules?wt.mc_id=dapine\">code-style (IDEXXXX) rules<\/a>.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>.NET developer community spotlight<\/h3>\n<p>The .NET developer community is building GitHub Actions that might be useful in your organizations. As an example, check out the <a href=\"https:\/\/github.com\/zyborg\/dotnet-tests-report\"><code>zyborg\/dotnet-tests-report<\/code><\/a> which is a GitHub Action to run .NET tests and generate reports and badges. If you use this GitHub Action, be sure to give their repo a star \u2b50.<\/p>\n<p>There are many .NET GitHub Actions that can be consumed from workflows, see the <a href=\"https:\/\/github.com\/marketplace?type=actions&amp;query=.net\">GitHub Marketplace: .NET<\/a>.<\/p>\n<h3>A word on .NET workloads<\/h3>\n<p>.NET runs anywhere, and you can use it to build anything. There are optional workloads that may need to be installed when building from a GitHub workflow. There are many workloads available, see the output of the <code>dotnet workload search<\/code> command as an example:<\/p>\n<pre><code class=\"language-dotnetcli\">dotnet workload search\r\n\r\nWorkload ID         Description\r\n-----------------------------------------------------------------------------------------\r\nandroid             .NET SDK Workload for building Android applications.\r\nandroid-aot         .NET SDK Workload for building Android applications with AOT support.\r\nios                 .NET SDK Workload for building iOS applications.\r\nmaccatalyst         .NET SDK Workload for building macOS applications with MacCatalyst.\r\nmacos               .NET SDK Workload for building macOS applications.\r\nmaui                .NET MAUI SDK for all platforms\r\nmaui-android        .NET MAUI SDK for Android\r\nmaui-desktop        .NET MAUI SDK for Desktop\r\nmaui-ios            .NET MAUI SDK for iOS\r\nmaui-maccatalyst    .NET MAUI SDK for Mac Catalyst\r\nmaui-mobile         .NET MAUI SDK for Mobile\r\nmaui-windows        .NET MAUI SDK for Windows\r\ntvos                .NET SDK Workload for building tvOS applications.\r\nwasm-tools          .NET WebAssembly build tools<\/code><\/pre>\n<p>If you're writing a workflow for Blazor WebAssembly app, or .NET MAUI as an example \u2014 you'll likely <code>run<\/code> the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/tools\/dotnet-workload-install?wt.mc_id=dapine\">dotnet workload install<\/a> command as one of your <code>steps<\/code>. For example, an individual <code>run<\/code> step to install the WebAssembly build tools would look like:<\/p>\n<pre><code class=\"language-yml\">run: dotnet workload install wasm-tools<\/code><\/pre>\n<h2>Summary<\/h2>\n<p>In this post, I explained the key differences between GitHub Actions and GitHub workflows. I explained and scrutinized each line in an example workflow file. I then showed you how a developer might visualize the execution of a GitHub workflow as a sequence diagram. I shared a few additional resources you may not have known about. For more information, see <a href=\"https:\/\/docs.microsoft.com\/dotnet\/devops\/github-actions-overview?wt.mc_id=dapine\">.NET Docs: GitHub Actions and .NET<\/a>.<\/p>\n<p>This is just the start of blogs around using GitHub Actions for .NET. In future posts, I'll show <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automate-code-metrics-and-class-diagrams-with-github-actions?wt.mc_id=dapine\">how to create GitHub Actions using .NET<\/a>. I'll walk you through upgrading an existing .NET GitHub Action that is used to automatically maintain a _CODE<em>METRICS.md<\/em> file within the root of the repository. The code metrics analyze the C# source code of the target repository to determine things such as cyclomatic complexity and the maintainability index. In addition to these metrics, we'll add the ability to generate <a href=\"https:\/\/mermaid-js.github.io\/mermaid\/#\/classDiagram\">Mermaid class diagrams<\/a>, which is now natively supported by GitHub flavored markdown.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Use GitHub Actions to automate common .NET build, test, and deploy tasks.<\/p>\n","protected":false},"author":24662,"featured_media":38909,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,7589],"tags":[7607,6766,7606,7605,7222,7608],"class_list":["post-38908","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-github-actions","tag-cd","tag-ci","tag-continuous-integration","tag-devops","tag-github","tag-github-actions"],"acf":[],"blog_post_summary":"<p>Use GitHub Actions to automate common .NET build, test, and deploy tasks.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/38908","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/24662"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=38908"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/38908\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/38909"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=38908"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=38908"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=38908"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}