<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Robin Shen</title>
    <description>The latest articles on Forem by Robin Shen (@robinshine).</description>
    <link>https://forem.com/robinshine</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F350568%2F355fc55c-8ba8-4e81-a564-e8c1ef365b2e.jpeg</url>
      <title>Forem: Robin Shen</title>
      <link>https://forem.com/robinshine</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/robinshine"/>
    <language>en</language>
    <item>
      <title>TOD - command line tool to run OneDev CI/CD jobs against local changes</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Tue, 27 Feb 2024 07:30:11 +0000</pubDate>
      <link>https://forem.com/robinshine/tod-command-line-tool-to-run-onedev-cicd-jobs-against-local-changes-1dm2</link>
      <guid>https://forem.com/robinshine/tod-command-line-tool-to-run-onedev-cicd-jobs-against-local-changes-1dm2</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/theonedev/onedev"&gt;OneDev 10.2&lt;/a&gt; comes with TOD (&lt;strong&gt;T&lt;/strong&gt;he*&lt;em&gt;O&lt;/em&gt;&lt;em&gt;ne&lt;/em&gt;&lt;em&gt;D&lt;/em&gt;*ev), a command line tool to test/debug CI/CD jobs against local changes to avoid the modify/commit/push/run/check cycles.&lt;/p&gt;

&lt;p&gt;Under the hood, tod stashes local changes to a commit and pushes to server under a temporal ref to run specified job. Log of running job is streamed back to tod so that you can check job progress and status without leaving terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzqlqxfrq2e0x2mv65fr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzqlqxfrq2e0x2mv65fr.gif" alt="Image description" width="1279" height="810"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since job runs on server, it does not have many quirks and limitations of other tools (gitlab-runner exec or nektos/act for instance), such as requiring you to set up job environments, unable to handle job dependencies, etc. And you can still get fast feedback via shallow clone and cache.&lt;/p&gt;

&lt;p&gt;For details, please check &lt;a href="https://github.com/theonedev/tod"&gt;tod project&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Open Source DevOps Server to Get Most Of Your .NET Projects</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Sat, 09 Sep 2023 03:51:35 +0000</pubDate>
      <link>https://forem.com/robinshine/open-source-devops-server-to-get-most-of-your-net-projects-27e5</link>
      <guid>https://forem.com/robinshine/open-source-devops-server-to-get-most-of-your-net-projects-27e5</guid>
      <description>&lt;p&gt;&lt;a href="https://onedev.io"&gt;OneDev&lt;/a&gt; is an open source git hosting and CI/CD server. Unlike traditional code hosting platforms, it parses C# code to enable symbol search and navigation, both in source view and diff view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xaXwgsXw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4f2708q6a4jyi6vdgsl1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xaXwgsXw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4f2708q6a4jyi6vdgsl1.gif" alt="Image description" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The latest version also adds the ability to suggest CI/CD job templates for .net projects, as well as analyzing TRX formatted unit test report, coverlet coverage report and roslynator code analysis report to annotate source and pull request diff to facilitate code review.&lt;/p&gt;

&lt;p&gt;Let’s test with a real project to demonstrate it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First start up OneDev server with a one-liner on a Linux machine with docker installed:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -it - rm -v $(pwd)/onedev:/opt/onedev -v /var/run/docker.sock:/var/run/docker.sock -p 6610:6610 1dev/server:9.1.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Access http:localhost:6610 to set up the server, and then import a repository like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wk5bwArf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ko8knndtuhgj12ugw4ig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wk5bwArf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ko8knndtuhgj12ugw4ig.png" alt="Image description" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Specify url as &lt;em&gt;https:github.com/bitwarden/server&lt;/em&gt;, and proceed with import with all others remain as default. After a while, we will see two projects get imported, respectively &lt;em&gt;bitwarden&lt;/em&gt;, and &lt;em&gt;bitwarden/server&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to project bitwarden/server and click the link &lt;em&gt;adding .onedev-buildspec.yml&lt;/em&gt; to set up CI/CD like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uK6YTYKJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5tqp6v7fh94veg2zkgx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uK6YTYKJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5tqp6v7fh94veg2zkgx.gif" alt="Image description" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OneDev under the hood checks if the project root contains dotnet solution file, and suggest a dotnet ci job if it exists. You may further tune the job to meet your requirement. For this demo project, we simply commit the change without any modification and the CI job will be running.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We can open the running build, and check its log like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hKCcrXkS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g836rc4en4hsvp18yrai.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hKCcrXkS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g836rc4en4hsvp18yrai.gif" alt="Image description" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The job takes some time. After it finishes, we will see three new tabs summarizing unit tests, code coverages, and code problems:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wwFRotg1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1k5hjft958momq2l93cn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wwFRotg1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1k5hjft958momq2l93cn.gif" alt="Image description" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the reports, you may drill down problems to source view directly&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When open source files in OneDev, coverage info and code problems will be displayed, and you can even comment the code problems to discuss with author:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4eppTJH6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y0crbazmlknvag4vkurz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4eppTJH6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y0crbazmlknvag4vkurz.gif" alt="Image description" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This also works for diff view in pull request, and is especially useful when review the change. That’s all. Thanks for watching.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>dotnet</category>
    </item>
    <item>
      <title>Code Review with OneDev</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Tue, 28 Jun 2022 04:52:16 +0000</pubDate>
      <link>https://forem.com/robinshine/repository-mirror-with-onedev-8p8</link>
      <guid>https://forem.com/robinshine/repository-mirror-with-onedev-8p8</guid>
      <description>&lt;p&gt;&lt;a href="https://onedev.io"&gt;OneDev&lt;/a&gt; understands importance of code review, and provides many facilities to make this process smooth and efficient. This article introduces how to perform code review in OneDev with these facilities.&lt;/p&gt;

&lt;h1&gt;
  
  
  Review Rule Definition
&lt;/h1&gt;

&lt;p&gt;Review rule specifies who should review a certain change, and how many approvals should get to accept the change into a branch. Review rule is defined in branch protection settings like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6Yl9SiQt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mu3t34bbx2v6t82wum6t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Yl9SiQt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mu3t34bbx2v6t82wum6t.png" alt="Image description" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example we define rules for changes to be accepted into main branch:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The change must pass the CI build&lt;/li&gt;
&lt;li&gt;Changes touching models must get at least one approval from core team&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this rule specified, one will not be able to push changes touching models to main branch directly even with repository write permission. Instead a pull request must be submitted to go through the review process. &lt;/p&gt;

&lt;h1&gt;
  
  
  Reviewer Auto-Suggestion
&lt;/h1&gt;

&lt;p&gt;When reviewers is specified as a group, OneDev will suggest the most appropriate members to review the change, based on commit history of touched files in the change. If for some reason, suggested reviewer can not review the change, you may simply remove and OneDev will pick up other appropriate reviewers. And you can add other reviewers as necessary even if review rule does not require it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7aDEBTPh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e16dia8fvjc7j4kdtlnv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7aDEBTPh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e16dia8fvjc7j4kdtlnv.png" alt="Image description" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Pull Request Build to Verify Changes
&lt;/h1&gt;

&lt;p&gt;In above screenshot, the pull request also needs to be verified by CI build. We need to disable branch protection rule temporarily to edit build spec of main branch directly to add a pull request trigger for CI job like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hKOzO6PW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pjzy07b6opz8yifvbjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hKOzO6PW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pjzy07b6opz8yifvbjr.png" alt="Image description" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After committing change, do not forget to re-enable the branch protection setting. Check the pull request again, a CI job will be running to verify merged commit of pull request head and target branch head.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mo6c4gV_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wekt8uqlpq5qadsqm3ri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mo6c4gV_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wekt8uqlpq5qadsqm3ri.png" alt="Image description" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Select Changes to Review
&lt;/h1&gt;

&lt;p&gt;Code review can be performed from file changes tab. File changes can be shown commit by commit, for multiple commits, or since last review (last time approve or request for changes).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XRrjvGdv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a27dyk0yzqo3ekggj8fu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XRrjvGdv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a27dyk0yzqo3ekggj8fu.png" alt="Image description" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Further, files can be filtered to only show interested changes. For instance, below screenshot shows all changed java files under model package:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--09A7vvII--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qmw3mok6odulds4da3mw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--09A7vvII--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qmw3mok6odulds4da3mw.png" alt="Image description" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Jump to Symbol Definition
&lt;/h1&gt;

&lt;p&gt;While reviewing the change, one can jump to symbol definitions, or show all occurrences of the symbol in current commit:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MzT21iZz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tnio7cv090y6cuzjn2bq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MzT21iZz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tnio7cv090y6cuzjn2bq.gif" alt="Image description" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Test Coverage and Code Problems
&lt;/h1&gt;

&lt;p&gt;OneDev can analyze test coverage and code analysis report generated by CI job to help code review, for instance:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5BOkApQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nrbk0x6h4atdd2rl2s0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5BOkApQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nrbk0x6h4atdd2rl2s0j.png" alt="Image description" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For an example set up of diff annotation, please check &lt;a href="https://robinshen.medium.com/annotate-source-with-jest-eslint-information-in-onedev-c622641caa45"&gt;https://robinshen.medium.com/annotate-source-with-jest-eslint-information-in-onedev-c622641caa45&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Code Comment to Discuss Changes
&lt;/h1&gt;

&lt;p&gt;To raise concerns or suggest changes for a code snippet, just select with mouse to add a code comment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lfA8DTBZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyub6neurg0slbn6nbdo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lfA8DTBZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyub6neurg0slbn6nbdo.gif" alt="Image description" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After reviewing the changes, reviewer can give opinion by approving or requesting for changes. The pull request can only be merged after getting approvals from all reviewers.&lt;/p&gt;

&lt;p&gt;Pull request submitter can check added code comments in code comments tab. For each suggested change, submitter can commit the change individually, or add to batch to commit together:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9JuojPiR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ft6vl2y4qjva6lxvvhfb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9JuojPiR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ft6vl2y4qjva6lxvvhfb.gif" alt="Image description" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Free-Style Review on Commit and File
&lt;/h1&gt;

&lt;p&gt;Besides pull request based review, you can open arbitrary file or commit to add comment to initiate free-style review. User changing selected code will automatically be mentioned and notified to participate in the discussion. Further, source file will be annotated with history comments to help code understanding even if file is modified or renamed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1riFsF3G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppaydi1daqjl2nx8x0lo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1riFsF3G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppaydi1daqjl2nx8x0lo.gif" alt="Image description" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this mode, you can still suggest changes. When apply suggested changes, user has to tell which branch to apply the change.&lt;/p&gt;

&lt;p&gt;That’s all. Hope these facilities helps with your next code review. Thanks for watching!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>development</category>
    </item>
    <item>
      <title>Performance Comparison of OneDev and GitLab</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Wed, 01 Jun 2022 01:07:09 +0000</pubDate>
      <link>https://forem.com/robinshine/performance-comparison-of-onedev-and-gitlab-1597</link>
      <guid>https://forem.com/robinshine/performance-comparison-of-onedev-and-gitlab-1597</guid>
      <description>&lt;p&gt;TLDR;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git Push: OneDev is 40% faster than GitLab&lt;/li&gt;
&lt;li&gt;Git Clone: OneDev is 20% slower than GitLab&lt;/li&gt;
&lt;li&gt;Web UI: OneDev is 10x~30x faster than GitLab&lt;/li&gt;
&lt;li&gt;Memory: OneDev uses 70% less memory than GitLab&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For every feature built into &lt;a href="https://onedev.io"&gt;OneDev&lt;/a&gt;, I take performance seriously. It is very fast with moderate resource usage. In this article I compare it with GitLab to show some numbers.&lt;/p&gt;

&lt;h1&gt;
  
  
  Environment Setup
&lt;/h1&gt;

&lt;p&gt;OneDev and GitLab runs on same machine. When testing OneDev, GitLab is stopped, and vice versa.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hardware: AWS EC2 c5a.xlarge (4 core, 8G mem, GP2 SSD EBS storage)&lt;/li&gt;
&lt;li&gt;OS: Ubuntu 18.04&lt;/li&gt;
&lt;li&gt;OneDev: 7.3.2, installs using the zip file, running on openjdk 8&lt;/li&gt;
&lt;li&gt;GitLab: 14.10.2-ee, installs from official repository with apt-get&lt;/li&gt;
&lt;li&gt;Test repository: Kubernetes (100K+ commits)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since OneDev by default uses 50% of available memory, and this is too deluxe for a 8G machine, I modified its conf/wrapper.conf to change max memory to be 20%.&lt;/p&gt;

&lt;h1&gt;
  
  
  Repository Push/Clone
&lt;/h1&gt;

&lt;p&gt;Command used for a fresh push (repository is empty initially):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ time git push &amp;lt;repository url&amp;gt; master:master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And command used for clone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ time git clone &amp;lt;repository url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tests done from another c5a.xlarge linux machine in same subnet. Result is as following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrglh7syr6tuskxhcy9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrglh7syr6tuskxhcy9x.png" alt="Image description" width="448" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Git Push: OneDev is 40% faster than GitLab&lt;/li&gt;
&lt;li&gt;Git Clone: OneDev is 20% slower than GitLab&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is measured for full push/clone of a large repository. For daily increment push/pull , this is almost negligible.&lt;/p&gt;

&lt;p&gt;I did not measure concurrent push/clone, as both rely on native git to do the heavy lift, and the numbers should be on par.&lt;/p&gt;

&lt;h1&gt;
  
  
  Web UI
&lt;/h1&gt;

&lt;p&gt;Web UI test is performed with JMeter. The selenium web drive plugin is installed running chrome in headless mode to do end-to-end test. The idea is to visit a url, then wait for certain elements to appear. Taking GitLab pull request changes page for instance, I am using below groovy script for web drive sampler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.openqa.selenium.*
import org.openqa.selenium.support.ui.*
import java.time.Duration
def wait = new WebDriverWait(WDS.browser, 60)
WDS.sampleResult.sampleStart()
WDS.browser.get('http://testserver/root/kubernetes/-/merge_requests/1/diffs')
wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector('.diff-content')))
WDS.sampleResult.sampleEnd()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I started four c5a.2xlarge EC2 machine (8 cores, 16G mem) in same subnet as test server, each machine running 8 browsers via JMeter, and start another c5a.xlarge machine to aggregate results. This effectively simulates 32 concurrent users, and can drive server CPU to nearly 100%. Note that running 32 browsers on a 32 core VM can not drive OneDev to use 100% CPU, due to browser resource contention on same machine.&lt;/p&gt;

&lt;p&gt;Six similar pages in OneDev and GitLab are selected for test, displaying same set of data. For each round of test, the page is warmed up for 1 minute, then run 10 minutes to get the performance metrics. Gravatar of both products are disabled to avoid out-of-server resource loading. GitLab auto devops is also disabled to save cpu cycles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repository Root Page
&lt;/h3&gt;

&lt;p&gt;Both products show root of the repository including the readme content:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr8zs3ijn8bnuqffurwkw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr8zs3ijn8bnuqffurwkw.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniockgp2tmzrtkxlo9dn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniockgp2tmzrtkxlo9dn.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4pnkxmu6q55f36tx0l5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4pnkxmu6q55f36tx0l5.png" alt="Image description" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that for OneDev test in this case, I have to launch five c5a.2xlarge machine to simulate 40 concurrent users to drive it to use nearly 100% cpu.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recent Commits Page
&lt;/h3&gt;

&lt;p&gt;Both products show recent commits of the repository. OneDev shows 50 commits initially, while GitLab shows 40 commits. OneDev shows commits as verified as it trust GitHub GPG signing key by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlin1p5ho8xa8721yds8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlin1p5ho8xa8721yds8.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31euq3cd8akrnhxcdttp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31euq3cd8akrnhxcdttp.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhj88v44n9i8msrj8k0fr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhj88v44n9i8msrj8k0fr.png" alt="Image description" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Commit Detail Page
&lt;/h3&gt;

&lt;p&gt;Both products show detail of same commit, including diff of all changed files&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falhbzv29w8qb7hig8kt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falhbzv29w8qb7hig8kt2.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxh4yeqt9z67so3xlaba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxh4yeqt9z67so3xlaba.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68cbug6b6l625lcdn58x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68cbug6b6l625lcdn58x.png" alt="Image description" width="800" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  File Content Page
&lt;/h3&gt;

&lt;p&gt;Both products show content of same repository file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7tdqbewg7qv8it5uqd3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7tdqbewg7qv8it5uqd3.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx5fxcwn2fqms5jxt9j6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx5fxcwn2fqms5jxt9j6n.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmia1wc0478pqaolp4fpy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmia1wc0478pqaolp4fpy.png" alt="Image description" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pull Request Commits Page
&lt;/h3&gt;

&lt;p&gt;Both products show commit list of same pull request. OneDev shows earliest commit first, while GitLab shows latest commit first&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yqw146564duz7qq3esa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yqw146564duz7qq3esa.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5sorlzb39sd9h0n1fw1r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5sorlzb39sd9h0n1fw1r.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fchf393h5c3fsfgxxqdkw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fchf393h5c3fsfgxxqdkw.png" alt="Image description" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pull Request Changes Page
&lt;/h3&gt;

&lt;p&gt;Both products show diffs of changed files of same pull request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9yd9x6ktbr2469gwcq2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9yd9x6ktbr2469gwcq2.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn17c1nunp8cm4xah23yh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn17c1nunp8cm4xah23yh.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcpd6asvzvfkw7cn9ssoq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcpd6asvzvfkw7cn9ssoq.png" alt="Image description" width="800" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For all tests above, OneDev consumes 2.2G mem, while GitLab consumes 4G~4.5G mem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;OneDev web UI performs orders of magnitude faster than GitLab, often 10x~30x faster, with much less memory usage. The UI performance difference is very obvious when navigating through different pages of both products: OneDev loads page instantly after warm up, while GitLab has noticeable delays loading pages all the time.&lt;/p&gt;

&lt;p&gt;Thanks for watching. If you would like to run the tests yourself, check the &lt;a href="https://code.onedev.io/projects/406"&gt;setup detail&lt;/a&gt; here.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>git</category>
    </item>
    <item>
      <title>OneDev 7.1</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Fri, 06 May 2022 03:24:56 +0000</pubDate>
      <link>https://forem.com/robinshine/onedev-71-2eli</link>
      <guid>https://forem.com/robinshine/onedev-71-2eli</guid>
      <description>&lt;p&gt;&lt;a href="https://onedev.io"&gt;OneDev&lt;/a&gt; is a self-hosted git service with built-in CI/CD and &lt;br&gt;
Kanban. The 7.1 release comes with below major improvements:&lt;/p&gt;

&lt;h3&gt;
  
  
  Command palette to access features quickly
&lt;/h3&gt;

&lt;p&gt;Press cmd/ctrl-k to bring up command palette from anywhere. Search everything and &lt;br&gt;
jump to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TCi9oDDt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2um71c2dit5auwo95ecf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TCi9oDDt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2um71c2dit5auwo95ecf.gif" alt="Image description" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Easy project navigation via tree breadbrumb
&lt;/h3&gt;

&lt;p&gt;Project navigation is much easier now with tree view in project list and breadcrumb&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ee_v6L6y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xp7957jffioog0cmn45b.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ee_v6L6y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xp7957jffioog0cmn45b.gif" alt="Image description" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Two-way repository sync
&lt;/h3&gt;

&lt;p&gt;Push to remote repository upon changes, or pull from remote repository upon remote change. Check &lt;a href="https://medium.com/p/acf8b85fc69e"&gt;this tutorial&lt;/a&gt; for details&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Klz6ptNR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gcu1lhqff3y43a2oanc3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Klz6ptNR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gcu1lhqff3y43a2oanc3.png" alt="Image description" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Display similar issues when create new issue
&lt;/h3&gt;

&lt;p&gt;Similar issues will be displayed in real-time when typing title of new issue, which is very convenient to prevent duplication issues being filed&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w0bx_wIf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lw2wvmwoy4brwy0f0ab8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w0bx_wIf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lw2wvmwoy4brwy0f0ab8.gif" alt="Image description" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Full-text search with stop word filtering and word stemming
&lt;/h3&gt;

&lt;p&gt;When do fuzzy search against issues and pull requests, stop words are filtered (for major languages), and word stemming will be performed (for English currently)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5jto70A0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mexdbsh4xw76zw9607i8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5jto70A0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mexdbsh4xw76zw9607i8.gif" alt="Image description" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Able to change target branch of existing pull request
&lt;/h3&gt;

&lt;p&gt;Occasionally we need to change target branch of existing pull request without losing discussion context&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OE5Ph4Kr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4rtlww5sz2kg630zbfqh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OE5Ph4Kr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4rtlww5sz2kg630zbfqh.gif" alt="Image description" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>git</category>
    </item>
    <item>
      <title>Repository Mirror with OneDev</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Wed, 04 May 2022 07:27:50 +0000</pubDate>
      <link>https://forem.com/robinshine/repository-mirror-with-onedev-5046</link>
      <guid>https://forem.com/robinshine/repository-mirror-with-onedev-5046</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;It is often desirable to mirror repositories between OneDev and other hosting services. For instance you may want to push changes to a copy at GitHub for public access, or want to pull back any changes contributed by others at GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://onedev.io"&gt;OneDev&lt;/a&gt; 7.1 made this task trivial with the new repository sync steps. Let’s see how to achieve this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparation
&lt;/h3&gt;

&lt;p&gt;First of all, start OneDev by running below command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd)/onedev:/opt/onedev -p 6610:6610 -p 6611:6611 1dev/server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point your browser to &lt;em&gt;localhost:6610&lt;/em&gt; to setup OneDev, and create a test project.&lt;/p&gt;

&lt;p&gt;Then create an empty repository at GitHub side to be used as mirror of our test repository, assume the url is: &lt;em&gt;&lt;a href="https://github.com/youraccount/test"&gt;https://github.com/youraccount/test&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Push Changes to Mirror
&lt;/h3&gt;

&lt;p&gt;Now we configure OneDev to push changes to GitHub mirror as long as there is a change in branch or tag:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Login to your GitHub account and generate a personal access token able to push to the mirror repository&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At OneDev side, create a job secret in the test project and use access token above as its value:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tHeukxiy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s05g69x60sdob6kgwzqm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tHeukxiy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s05g69x60sdob6kgwzqm.png" alt="Image description" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Define build spec to add a job say &lt;em&gt;Push to GitHub&lt;/em&gt;, and add a step of type Repository &lt;em&gt;Sync / Push to Remote&lt;/em&gt;. For &lt;em&gt;password / access token&lt;/em&gt; field, choose the job token defined above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---GKIiIvC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kgypo4gp5udi9z2m4yaf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---GKIiIvC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kgypo4gp5udi9z2m4yaf.png" alt="Image description" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add triggers to run job automatically when branch is updated or tag is created:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UtC1RP43--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tkq1hl0nopc1w81qxaup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UtC1RP43--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tkq1hl0nopc1w81qxaup.png" alt="Image description" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now save the build spec and the job will run automatically to push changes to GitHub mirror. Check at GitHub side, and you will see it contains newly created file &lt;em&gt;.onedev-buildspec.yml&lt;/em&gt;. Experiment with other changes such as creating new branch/tag to see the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pull Changes from Mirror
&lt;/h3&gt;

&lt;p&gt;Our GitHub mirror may accept pull requests from contributors, so let’s set up OneDev to pull changes from it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Continue to edit build spec above, add a job say &lt;em&gt;Pull from GitHub&lt;/em&gt;, and add a step of type &lt;em&gt;Repository Sync / Pull from Remote&lt;/em&gt; like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9J8jAFQ1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l67ziaithai8ypch2ffa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9J8jAFQ1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l67ziaithai8ypch2ffa.png" alt="Image description" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here we use the same job token defined above to access GitHub mirror (access token can be left empty if the mirror is publicly accessible).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add a job trigger of cron type to run job on scheduled time, for instance, on 1:00AM every night:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5p2RZUYT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ytan9e8yo6gaynhx7k0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5p2RZUYT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ytan9e8yo6gaynhx7k0.png" alt="Image description" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If your OneDev instance can be accessed publicly, you can configure GitHub to trigger this pull job upon mirror changes. This can be done by creating a webhook at GitHub side like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bMzj_ClZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41zk3bdao6ud0mb2t7sn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bMzj_ClZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41zk3bdao6ud0mb2t7sn.png" alt="Image description" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The payload url is defined as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://onedev.example.com/api/trigger-job?project=test&amp;amp;job=Pull%20from%20GitHub&amp;amp;branch=master&amp;amp;access-token=&amp;lt;OneDev access token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;em&gt;onedev.example.com&lt;/em&gt; is dns name accessing your OneDev instance. Job name is url encoded as it contains space. &lt;em&gt;&lt;/em&gt; should be replaced by access token of a OneDev user with permission to run the job like below::&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rkjcdnaopfq3oych4xea.png)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For production use, it is important to enable https protocol for payload url to avoid leaking of access token.&lt;/p&gt;

&lt;p&gt;Save the webhook and OneDev should be pulling from GitHub mirror whenever it is changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;We’ve completed setup of a two-way sync of OneDev repository and its GitHub mirror. Since it is just an ordinary CI job, we can subscribe certain build query to be notified of sync failures, check build log to investigate sync failures, or even configure job resource setting to control resource usage of multiple sync jobs, etc.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>git</category>
      <category>github</category>
    </item>
    <item>
      <title>Understanding OneDev Pipeline</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Mon, 11 Apr 2022 09:57:41 +0000</pubDate>
      <link>https://forem.com/robinshine/understanding-onedev-pipeline-47el</link>
      <guid>https://forem.com/robinshine/understanding-onedev-pipeline-47el</guid>
      <description>&lt;p&gt;&lt;a href="https://onedev.io"&gt;OneDev&lt;/a&gt; introduced pipeline concept since 7.0. In short, a pipeline is an execution of job dependency graph. It can be created by running any job in the graph, either manually or automatically via job triggers.&lt;/p&gt;

&lt;h1&gt;
  
  
  Create a Pipeline
&lt;/h1&gt;

&lt;p&gt;Login to &lt;a href="https://code.onedev.io/login"&gt;OneDev demo site&lt;/a&gt; with account &lt;em&gt;demo/demo&lt;/em&gt;, switch to &lt;a href="https://code.onedev.io/projects/369/blob/master/.onedev-buildspec.yml"&gt;CI spec definition page&lt;/a&gt; of the demo project, you will see job definitions like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M7FQKwnK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v16vx1176yiflrzwtnm4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M7FQKwnK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v16vx1176yiflrzwtnm4.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we define a &lt;em&gt;test&lt;/em&gt; job to compile and test our app, and then publish compiled app as artifacts so that it can be deployed later. The &lt;em&gt;deploy&lt;/em&gt; job retrieves published app via dependency to the &lt;em&gt;test&lt;/em&gt; job, and gets it deployed as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HICNdeSO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qdyqlmpcew0xv4tp1h9v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HICNdeSO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qdyqlmpcew0xv4tp1h9v.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now run the &lt;em&gt;test&lt;/em&gt; job, you will be brought to the page showing generated build. A pipeline will also be created as the job dependency graph is executed as result of running the &lt;em&gt;test&lt;/em&gt; job. The pipeline tab shows the created pipeline current build is part of:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ezHsyxp5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjww98kyem72vaj24nu0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ezHsyxp5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjww98kyem72vaj24nu0.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: pipeline tab is only visible to users with code read permission&lt;/p&gt;

&lt;h1&gt;
  
  
  Pipeline Characteristics
&lt;/h1&gt;

&lt;p&gt;Pipeline has two characteristics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Builds fired manually from pipeline view, or by other builds in the pipeline via job triggers, dependencies and post-build actions belong to same pipeline.&lt;/li&gt;
&lt;li&gt;For a particular pipeline, only one build will be generated from same job and parameter. If no parameter is defined for the job, it can only be triggered once then.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To demonstrate, run the &lt;em&gt;deploy&lt;/em&gt; job from above pipeline, from the build log you will see that it is actually deploying the app published by existing test build in that pipeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GVvnq0jf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qn0qhzgq937euzsu715j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GVvnq0jf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qn0qhzgq937euzsu715j.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also pipeline tab of the deploy build shows that it belongs to same pipeline as test build above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--heSTTtx6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1b0zp7s2nd2jcvf150jt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--heSTTtx6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1b0zp7s2nd2jcvf150jt.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we run &lt;em&gt;deploy&lt;/em&gt; job again in this pipeline, OneDev will indicate that the build is already fired:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g5GNceyL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v0sa0j6cb8zrjsqns4xc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5GNceyL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v0sa0j6cb8zrjsqns4xc.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However if you run &lt;em&gt;deploy&lt;/em&gt; job from outside of the pipeline, a new pipeline will be created and new builds for &lt;em&gt;test&lt;/em&gt; and &lt;em&gt;deploy&lt;/em&gt; job will be generated for the new pipeline. Again, let’s check this in action. Switch to &lt;a href="https://code.onedev.io/projects/369/commits"&gt;commits page&lt;/a&gt; to run &lt;em&gt;deploy&lt;/em&gt; job for some commit with existing test build:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gKYAQbxg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l20yl4e0mue3xuyb8ouv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gKYAQbxg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l20yl4e0mue3xuyb8ouv.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new deploy build will be generated, as well as a new pipeline. Switch to pipeline tab of the new build, you will see that new test build also gets generated in regardless of existing test builds for the commit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Y0I4k5N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hintq9nq1balwmeaqaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Y0I4k5N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hintq9nq1balwmeaqaj.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Pipeline Automation
&lt;/h1&gt;

&lt;p&gt;Pipeline can be created automatically if a job is set to fire upon certain event. For instance, in &lt;a href="https://code.onedev.io/projects/370/blob/master/.onedev-buildspec.yml"&gt;CI spec&lt;/a&gt; of this demo project, we set job &lt;em&gt;test&lt;/em&gt; to trigger automatically when there are new commits in master branch:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y0teM84d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3es07pzacn5j4usck0dz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y0teM84d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3es07pzacn5j4usck0dz.png" alt="Image description" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://code.onedev.io/projects/369/blob/master/app.txt?mode=edit"&gt;Make some changes to app.txt&lt;/a&gt; and save the file, you will see that &lt;em&gt;test&lt;/em&gt; job is running:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ez51O61E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q5o3oxgnhsxwbxsn09n5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ez51O61E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q5o3oxgnhsxwbxsn09n5.png" alt="Image description" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the running test build and you will be brought to build detail page, from which created pipeline can be accessed.&lt;/p&gt;

&lt;p&gt;Now assume you want to deploy the app automatically when &lt;em&gt;test&lt;/em&gt; job is successful. To do it, add a trigger to fire the &lt;em&gt;deploy&lt;/em&gt; job when dependency jobs finished like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ipGYVnlu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wqizc9n1wp3lbuna5tiu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ipGYVnlu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wqizc9n1wp3lbuna5tiu.png" alt="Image description" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To test it in action, &lt;a href="https://code.onedev.io/projects/370/blob/master/app.txt?mode=edit"&gt;make some changes to app.txt in this project&lt;/a&gt;, save the file, open created pipeline and wait to see &lt;em&gt;deploy&lt;/em&gt; job running automatically after test job is successful:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l2z71Jeg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjdlpsml7x9opa0oam16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l2z71Jeg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjdlpsml7x9opa0oam16.png" alt="Image description" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, you may also configure a branch update trigger for &lt;em&gt;deploy&lt;/em&gt; job. In that case, both &lt;em&gt;deploy&lt;/em&gt; job and &lt;em&gt;test&lt;/em&gt; job will be fired upon changing file, but &lt;em&gt;deploy&lt;/em&gt; job will wait for &lt;em&gt;test&lt;/em&gt; job to complete before running as shown in the pipeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xSJG2N8b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/idxigvzd7kda2n6sajh5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xSJG2N8b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/idxigvzd7kda2n6sajh5.png" alt="Image description" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you should get an idea how pipeline works in OneDev. Any questions, please post at our &lt;a href="https://code.onedev.io/projects/160/issues?query=%22State%22+is+%22Open%22"&gt;issue tracker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for watching!&lt;/p&gt;

</description>
      <category>devops</category>
    </item>
    <item>
      <title>Set Up OneDev Issue Labels</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Sun, 10 Apr 2022 07:48:45 +0000</pubDate>
      <link>https://forem.com/robinshine/set-up-onedev-issue-labels-i1g</link>
      <guid>https://forem.com/robinshine/set-up-onedev-issue-labels-i1g</guid>
      <description>&lt;p&gt;&lt;a href="https://onedev.io"&gt;OneDev&lt;/a&gt; is designed to be flexible, and this is no different for issues. In this tutorial, I will show how to customize issue labels.&lt;/p&gt;

&lt;p&gt;OneDev issues by default do not have label field, as custom fields work much better, for instance, you may have &lt;em&gt;Type&lt;/em&gt; field with values &lt;em&gt;Feature/Bug/Task&lt;/em&gt;, or &lt;em&gt;Subsystem&lt;/em&gt; field with values &lt;em&gt;Client/Server/Api&lt;/em&gt;, etc. However if you do want to have labels, here is how to do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Login as administrator, and switch to custom issue fields page&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Kvfzjnpn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1xm70hott022h7z3pcz3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Kvfzjnpn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1xm70hott022h7z3pcz3.png" alt="Image description" width="800" height="552"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a custom issue field like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ENhbFMFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hxlnyf7p4hzotpolpp4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ENhbFMFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hxlnyf7p4hzotpolpp4.png" alt="Image description" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Continue to add available choices to include all possible label values, for instance:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4pbyDG51--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mskf3u5s6ondr1kjuc7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4pbyDG51--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mskf3u5s6ondr1kjuc7o.png" alt="Image description" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now create an issue and you will see that we can specify labels:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QZ9gfCgm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kw1tq556ao46re8uuiwd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QZ9gfCgm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kw1tq556ao46re8uuiwd.png" alt="Image description" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To specify labels for existing issues, batch edit them and choose Labels field to edit:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nfNrUcY0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v242zg36dgaubedpch94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nfNrUcY0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v242zg36dgaubedpch94.png" alt="Image description" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tZr20NKu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggocjai2dttax6a0gq7n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tZr20NKu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggocjai2dttax6a0gq7n.png" alt="Image description" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;By default newly created field is not displayed in issue list or board. This can be changed by configuring display fields option in issue list and board respectively:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4ratwgKZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4915xetrbyswwskx51e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4ratwgKZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4915xetrbyswwskx51e.png" alt="Image description" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2r343IE9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4n5uuh4mfflhencdx6py.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2r343IE9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4n5uuh4mfflhencdx6py.png" alt="Image description" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s all. Thanks for watching!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>git</category>
    </item>
    <item>
      <title>OneDev 7 - self-hosted git service with CI/CD and Kanban</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Mon, 04 Apr 2022 12:02:31 +0000</pubDate>
      <link>https://forem.com/robinshine/onedev-7-self-hosted-git-service-with-cicd-and-kanban-3ihp</link>
      <guid>https://forem.com/robinshine/onedev-7-self-hosted-git-service-with-cicd-and-kanban-3ihp</guid>
      <description>&lt;p&gt;The self-hosted open source git service &lt;a href="https://github.com/theonedev/onedev"&gt;OneDev&lt;/a&gt; reaches 7.0. Some feature highlights:&lt;/p&gt;

&lt;h2&gt;
  
  
  Show job dependencies as pipeline
&lt;/h2&gt;

&lt;p&gt;Visualize job dependencies as pipeline to get instant understanding of job relationships and their execution statuses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SHADoMct--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fg9fuwwm1jn4vnkm3rf7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SHADoMct--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fg9fuwwm1jn4vnkm3rf7.gif" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two-factor authentication
&lt;/h2&gt;

&lt;p&gt;TOTP based two-factor authentication can be enabled by user. Site administrator can also enforce 2FA for all users or certain groups&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bgqpfs66--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o59eygu6pcps7kt8rqnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bgqpfs66--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o59eygu6pcps7kt8rqnp.png" alt="Image description" width="880" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Commit/Tag GPG signature verification and generation
&lt;/h2&gt;

&lt;p&gt;Commits and tags with GPG signature can be verified. Branch/tag protection rule can be set up to enforce signed commits/tags. OneDev can use its own signing key to sign commits/tags generated by itself (pull request merge commits etc). GPG public key of other git hosting services can be imported so that their signed commits/tags can be trusted&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WcS3i8Pp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xc08zujp0d0peudtdr58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WcS3i8Pp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xc08zujp0d0peudtdr58.png" alt="Image description" width="880" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Able to cache cloned repository to improve CI/CD speed
&lt;/h2&gt;

&lt;p&gt;Repository can now be cloned into a sub directory under job workspace, and can be cached to improve speed. For instance a fresh clone of Linux repository takes 5 minutes, while updating a cached one takes 5 seconds&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EHnKHD0k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ryiwuepnc6k7e05gtbtl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EHnKHD0k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ryiwuepnc6k7e05gtbtl.png" alt="Image description" width="880" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Other improvements
&lt;/h2&gt;

&lt;p&gt;For a complete list of changes, please check the &lt;a href="https://code.onedev.io/projects/160/builds/2475/fixed-issues?query=%22State%22+is+%22Released%22+order+by+%22Type%22+asc+and+%22Priority%22+desc"&gt;release notes&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>git</category>
    </item>
    <item>
      <title>Effortless OneDev CI/CD Farm with Agents</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Thu, 10 Feb 2022 08:17:35 +0000</pubDate>
      <link>https://forem.com/robinshine/effortless-onedev-cicd-farm-with-agents-2gd4</link>
      <guid>https://forem.com/robinshine/effortless-onedev-cicd-farm-with-agents-2gd4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;From day 1, &lt;a href="https://github.com/theonedev/onedev"&gt;OneDev&lt;/a&gt; is able to run CI/CD jobs as pods on Kubernetes clusters. In case you do not have a Kubernetes cluster, it is now possible to install OneDev agents on other machines to get a CI/CD farm. Agent is designed to be maintenance free: the server will push updates to agents automatically when it is upgraded.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Set Up
&lt;/h2&gt;

&lt;p&gt;Let's go through an example to see how to set up the CI/CD farm&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Start OneDev server with below command on Linux or Mac (this command is for demonstration purpose, follow installation guide for production use):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd)/onedev:/opt/onedev -p 6610:6610 -p 6611:6611 1dev/server
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open browser to visit &lt;a href="http://localhost:6610"&gt;http://localhost:6610&lt;/a&gt; to complete the setup&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Visit agent management page, click the add agent button, and show the docker command to run agent:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PlQKb-UF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/igkp70c8u5ql3kgs3b5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PlQKb-UF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/igkp70c8u5ql3kgs3b5u.png" alt="Image description" width="880" height="581"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the command to your terminal (Linux/Mac), change serverUrl environment to http://:6610 (do not use localhost here as otherwise OneDev agent running inside container can not access OneDev server), and run the command to start agent. If connected, the agent will be online in agent management page like below:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k8P6MvzG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1v9uq5qwh88zw89ctr12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k8P6MvzG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1v9uq5qwh88zw89ctr12.png" alt="Image description" width="720" height="472"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now let's create a job executor to use the agent. Switch to job executors page, delete the default auto-discover executor, and create an executor of type Remote Docker Executor, give it a name and leave others as default. Test and save the setting:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P-czp_6f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y323n70a3qiaht5whq6f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P-czp_6f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y323n70a3qiaht5whq6f.png" alt="Image description" width="720" height="553"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a test project, define CI spec with a simple job, and run the job. You will see that the job is running on the agent:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m3s38aAi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nk3ar69y5k0zqldjnblc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m3s38aAi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nk3ar69y5k0zqldjnblc.gif" alt="Image description" width="879" height="557"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When OneDev dispatches jobs to agents, it choses the best one based on agent cpu/memory capacity and job cpu/memory requirement. If no agents satisfies the requirement, the job will be in waiting state.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>git</category>
    </item>
    <item>
      <title>Connect Customer to Developer with OneDev Service Desk</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Fri, 06 Aug 2021 05:14:24 +0000</pubDate>
      <link>https://forem.com/robinshine/connect-customer-to-developer-with-onedev-service-desk-526g</link>
      <guid>https://forem.com/robinshine/connect-customer-to-developer-with-onedev-service-desk-526g</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://onedev.io"&gt;OneDev&lt;/a&gt; is a performant open source git server, with customizable issue workflow and built-in CI/CD.&lt;/p&gt;

&lt;p&gt;Version 4.9 comes with the service desk feature, which allows your customers to submit tickets via email without the need to have a OneDev account. These tickets can then be created in desired projects, and assigned to appropriate staffs in your team. All further discussions over the tickets can take place completely in email, with  discussion contents posted to ticket as comments. Customer will also get notifications when there are any ticket activities, for instance, when a relevant release is created or deployed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Feature Highlights
&lt;/h1&gt;

&lt;p&gt;Before diving into service desk setup, let’s check what the service desk can do for you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Customer can either send ticket to a uniformed email address, for instance &lt;em&gt;&lt;a href="mailto:support@yourcomany.com"&gt;support@yourcomany.com&lt;/a&gt;&lt;/em&gt;, or send to project specific one like &lt;em&gt;&lt;a href="mailto:support+yourproject@yourcompany.com"&gt;support+yourproject@yourcompany.com&lt;/a&gt;&lt;/em&gt;. Tickets sent to uniformed email address will be created in default project of the customer, and you can move them to other projects later if desired.&lt;/li&gt;
&lt;li&gt;Customer can only create tickets in authorized projects. Authorization is done by checking customer email address.&lt;/li&gt;
&lt;li&gt;All recipients of the email will be added to watch list of created ticket, so that they can be notified of ticket activities and join the discussions.&lt;/li&gt;
&lt;li&gt;Html format and attachments of the email are carefully handled to achieve same experience at Email side and web UI side.&lt;/li&gt;
&lt;li&gt;For different customers, you may configure different ticket properties such as priorities and assignees.&lt;/li&gt;
&lt;li&gt;All emails relating to a particular ticket will be organized into a single thread to track discussion context, as well as reduce notification noises. Users can also unsubscribe from the ticket.&lt;/li&gt;
&lt;li&gt;Content of the ticket notification email can be customized.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  How to Set Up
&lt;/h1&gt;

&lt;p&gt;First of all, you need to enable mail setting and its check incoming email option:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6cub0d5G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xs7ky9iwv7bldvrcbui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6cub0d5G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xs7ky9iwv7bldvrcbui.png" alt="Alt Text" width="800" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also &lt;a href="https://en.wikipedia.org/wiki/Email_address#Subaddressing"&gt;subaddressing&lt;/a&gt; needs to be enabled at your mail server for system email address. Test mail sending and receiving at bottom of the page to make sure it works. &lt;/p&gt;

&lt;p&gt;Now OneDev is capable to send ticket notifications via email. Users receiving notification email can also add comments to the ticket by replying the email. Content of the notification email can be customized via notification templates below the mail setting.&lt;/p&gt;

&lt;p&gt;However to be able to create tickets via email, you should also enable the service desk setting like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DktdGira--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/erjckekrteh0erj89zdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DktdGira--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/erjckekrteh0erj89zdr.png" alt="Alt Text" width="800" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All done! Now authorized users can send email to system email address to create tickets!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>git</category>
    </item>
    <item>
      <title>CI/CD configuration reuse in OneDev</title>
      <dc:creator>Robin Shen</dc:creator>
      <pubDate>Sat, 24 Apr 2021 07:42:50 +0000</pubDate>
      <link>https://forem.com/robinshine/ci-cd-configuration-reuse-in-onedev-5c0i</link>
      <guid>https://forem.com/robinshine/ci-cd-configuration-reuse-in-onedev-5c0i</guid>
      <description>&lt;p&gt;Maintaining CI/CD configuration for many projects can be complex and challenging. To reduce the overhead, &lt;a href="https://onedev.io" rel="noopener noreferrer"&gt;OneDev 4.3&lt;/a&gt; adds the mechanism to define common CI/CD building blocks in separate projects and use/customize them in other projects.&lt;/p&gt;

&lt;p&gt;CI/CD configuration is maintained in OneDev via build spec. When define build spec for a project, it is now possible to import build specs from other projects. Objects (jobs, services, step templates and properties) contained in imported build specs are treated as if they are defined locally, except that imported objects will be overridden by local objects with same name. In case of multiple imports, latter imported objects will override prior imported objects with same name.&lt;/p&gt;

&lt;p&gt;With this import and overridden mechanism, you may maintain common jobs/services/step templates in a separate build spec, and use property placeholders in places where customization might be necessary. When this build spec is imported into other build specs, user may selectively override these properties to get different behaviors for different projects.&lt;/p&gt;

&lt;p&gt;Let’s practice with an example to see how it works.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Start OneDev with below command in Linux or Mac:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd)/onedev:/opt/onedev -p 6610:6610 -p 6611:6611 1dev/server:4.3.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open browser to visit &lt;em&gt;&lt;a href="http://localhost:6610" rel="noopener noreferrer"&gt;http://localhost:6610&lt;/a&gt;&lt;/em&gt; to complete the setup. Add a project say &lt;em&gt;commons&lt;/em&gt;, and set up its build spec as below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nmz4yehs27ptngqgj5o.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nmz4yehs27ptngqgj5o.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we define a common CI job running a step with image specified as value of property &lt;em&gt;nodeImage&lt;/em&gt;. Other projects can import this build spec and override the property to use different node image if desired. This job also defines a trigger to fire build automatically when there are new commits and property &lt;em&gt;applicable projects&lt;/em&gt; is defined to exclude the &lt;em&gt;commons&lt;/em&gt; project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we have a common CI job. In order to reuse it in other projects, we need to add a tag. We also configure default role of the project so that everyone in the system has permission to read code for simplicity reason:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F885ui86izo58tciv37uv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F885ui86izo58tciv37uv.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Let’s continue to add a test project, and set up its build spec to import and use the job:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43cfuakkwh2ywko7l11l.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43cfuakkwh2ywko7l11l.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we override property &lt;em&gt;nodeImage&lt;/em&gt; to use &lt;em&gt;node:14.16-buster&lt;/em&gt;. Save the build spec and the imported job will be running automatically to print the expected node version.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Besides overriding property to customize imported objects, we can also use parameters to customize common step templates. Now let’s practice with this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Continue to edit build spec of our commons project as below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F130yk3a0fyrg9a3630ul.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F130yk3a0fyrg9a3630ul.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we delete objects defined previously, and add a step template instead to do the job. The template defines a parameter to get image name dynamically from caller&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a new tag say &lt;em&gt;v2&lt;/em&gt; for our new version of build spec&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2xf9befokzlgum9h4wb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2xf9befokzlgum9h4wb.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Edit build spec of the test project to import the new version, add a CI job to use imported step template and specify parameter &lt;em&gt;Node Image&lt;/em&gt; as &lt;em&gt;node:14.16-buster&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwevzj6q39sz8y41tf2lo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwevzj6q39sz8y41tf2lo.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save the build spec to see the job running automatically to print expected node version.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>devops</category>
    </item>
  </channel>
</rss>
