HugoTechEuleNotepad about software engineering & devops2024-03-28T18:01:29+00:00Wassim Akachiwassim@BytesAndCodes.comhttps://techeule.com/Cost-Efficiency and Cloud-Native Applications: An Overviewhttps://techeule.com/posts/cost-driven-cloud-native-application/Wassim Akachi2023-07-01T15:00:10+00:002023-07-01T15:00:10+00:00
<p>In the dynamic realm of cloud deployment, the cost-effectiveness of each transaction is a crucial factor.
Leveraging cloud services and associated cost metrics effectively can lead to optimal resource utilization.
In this article, we’ll delve into the world of cost-driven cloud-native applications, using Amazon Web Services (AWS)
as our prime example due to its pioneering status and current leadership in the cloud industry.
It’s worth noting that similar services are offered by other providers such as Microsoft Azure,
Google Cloud Platform (GCP), Oracle Cloud (OCI), IBM Cloud, and more.</p>
<p>In this post, we’ll provide an extensive view of a cost-driven and cloud-native full-stack application.
The showcase application, named <strong>BlogQ-Editor</strong> (<em>BqEditor</em>), serves as a blog editing system,
designed to serve as a real world example, workshop template, and featured content on
<a
class="gblog-markdown__link"
href="https://TechEule.com"
>TechEule.com</a>.
Developed using Java, Eclipse MicroProfile 6.0, TypeScript, Redux (Redux-Toolkit), Lit, and Bulma.io.</p>
<p>It finds its home in the AWS Cloud through the AWS Cloud Development Kit (CDK),
embracing the concept of Infrastructure as Code (IaC).
For those interested, the source code is hosted on GitHub at
<a
class="gblog-markdown__link"
href="https://github.com/techeule/techeule-blog-quarkus"
>github.com/techeule/techeule-blog-quarkus</a>.</p>
<blockquote class="gblog-hint tip">
<div class="gblog-hint__title flex align-center"><i class="fa tip" title="Tip"></i></div>
<div class="gblog-hint__text">Expect upcoming articles dedicated to delving deeper into the application’s structural intricacies,
its source code (both front-end and back-end),
deployment specifics using AWS CDK, CI/CD setup,
and an exploration of the AWS services harmonized within this application.
Keep an eye out for the continuation of the <a
class="gblog-markdown__link"
href="/tags/BlogQ/"
>BlogQ series</a> <a
class="gblog-markdown__link"
href="/tags/BlogQ/"
>right here</a>.</div>
</blockquote>
<div class="gblog-post__anchorwrap">
<h2 id="application-architecture">
Application Architecture
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#application-architecture" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Application Architecture" href="https://techeule.com/posts/cost-driven-cloud-native-application/#application-architecture">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>BqEditor stands as a state-of-the-art application, sporting a Single Page Application (SPA) in the front-end,
coupled with a RESTful Web API in the back-end. All data is conveniently stored in an Amazon DynamoDB table.</p>
<div class="gblog-post__anchorwrap">
<h3 id="deployment-setup">
Deployment Setup
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#deployment-setup" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Deployment Setup" href="https://techeule.com/posts/cost-driven-cloud-native-application/#deployment-setup">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h3>
</div>
<div class="flex justify-center">
<figure
class="gblog-post__figure"
>
<a class="gblog-markdown__link--raw" href="https://techeule.com/posts/cost-driven-cloud-native-application/images/TechEule-BlogQ-Editor.png">
<picture>
<source
srcset="https://techeule.com/posts/cost-driven-cloud-native-application/images/TechEule-BlogQ-Editor_hu6b046aa8a9f2f2b9fffbe9fa0b610780_93073_600x0_resize_box_3.png 600w, https://techeule.com/posts/cost-driven-cloud-native-application/images/TechEule-BlogQ-Editor_hu6b046aa8a9f2f2b9fffbe9fa0b610780_93073_1200x0_resize_box_3.png 1200w" sizes="100vw"
/>
<img
src="https://techeule.com/posts/cost-driven-cloud-native-application/images/TechEule-BlogQ-Editor_hu6b046aa8a9f2f2b9fffbe9fa0b610780_93073_1800x0_resize_box_3.png"
alt="BlogQ-Editor Deployment Setup"
/>
</picture>
</a>
<figcaption>
BlogQ-Editor Deployment Setup
</figcaption>
</figure>
</div>
<div class="gblog-post__anchorwrap">
<h3 id="backend">
Backend
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#backend" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Backend" href="https://techeule.com/posts/cost-driven-cloud-native-application/#backend">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h3>
</div>
<p>At its core, the backend encompasses a web API dedicated to managing blog posts.
This segment is constructed using Java 17, Eclipse MicroProfile 6.0, and Quarkus.io version 3.x.
The backend can be seamlessly deployed as an AWS Lambda, made accessible via Amazon API Gateway
with a customized and easily memorable domain name.
DynamoDB serves as the cornerstone of the application’s data storage.
Furthermore, the backend’s Infrastructure as Code (IaC) source code can be found within the repository’s
<strong>cdk</strong> maven submodule. The backend code is organized into three distinct Maven modules:
<strong>blogq-engine</strong>, <strong>blogq-app</strong>, and <strong>blogq-backend</strong>.</p>
<div class="gblog-post__anchorwrap">
<h4 id="why-using-aws-lambda-for-the-backend">
Why using AWS Lambda for the backend?
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#why-using-aws-lambda-for-the-backend" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Why using AWS Lambda for the backend?" href="https://techeule.com/posts/cost-driven-cloud-native-application/#why-using-aws-lambda-for-the-backend">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h4>
</div>
<p>Contrary to the notion that serverless functions can’t effectively manage multiple endpoints,
BqEditor’s backend handles four distinct web endpoints.
With Quarkus providing seamless AWS Lambda support, you can develop your application without any
intricate AWS-specific configurations.
A couple of Maven dependencies and Quarkus-Maven build integration are all that’s needed for AWS Lambda compatibility.</p>
<div class="flex justify-center">
<figure
class="gblog-post__figure"
>
<a class="gblog-markdown__link--raw" href="https://techeule.com/posts/cost-driven-cloud-native-application/images/TechEule-BlogQ-Editor-Swagger-UI.png">
<picture>
<source
srcset="https://techeule.com/posts/cost-driven-cloud-native-application/images/TechEule-BlogQ-Editor-Swagger-UI_hue6b0f214ea632697cf12763a1ceab24f_270321_600x0_resize_box_3.png 600w, https://techeule.com/posts/cost-driven-cloud-native-application/images/TechEule-BlogQ-Editor-Swagger-UI_hue6b0f214ea632697cf12763a1ceab24f_270321_1200x0_resize_box_3.png 1200w" sizes="100vw"
/>
<img
src="https://techeule.com/posts/cost-driven-cloud-native-application/images/TechEule-BlogQ-Editor-Swagger-UI_hue6b0f214ea632697cf12763a1ceab24f_270321_1800x0_resize_box_3.png"
alt="BqEditor Swagger-UI"
/>
</picture>
</a>
<figcaption>
BqEditor Swagger-UI
</figcaption>
</figure>
</div>
<p>In terms of cost, the backend operation stays within the <code>USD 15</code> per month range.
The AWS Lambda component, in particular, costs approximately <code>USD 5.73</code>,
potentially even less with <a
class="gblog-markdown__link"
href="https://aws.amazon.com/savingsplans/"
>AWS savings plans</a> factored in.
The calculation is achieved through AWS’s <a
class="gblog-markdown__link"
href="https://calculator.aws/"
>pricing calculator</a>, enabling proactive cost estimates.</p>
<div class="gblog-expand">
<label class="gblog-expand__head flex justify-between" for="29afb968-3">
<span>Cost Calculation</span>
<span>↕</span>
</label>
<input id="29afb968-3" type="checkbox" class="gblog-expand__control hidden" />
<div class="gblog-markdown--nested gblog-expand__content">
<div class="gblog-post__anchorwrap">
<h5 id="i-have-made-the-following-assumptions">
I have made the following assumptions:
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#i-have-made-the-following-assumptions" class="gblog-post__anchor clip flex align-center" aria-label="Anchor I have made the following assumptions:" href="https://techeule.com/posts/cost-driven-cloud-native-application/#i-have-made-the-following-assumptions">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h5>
</div>
<ul>
<li><strong><code>USD 5.73</code></strong> for <em>AWS Lambda</em>:
<ul>
<li>Architecture: ARM CPU,</li>
<li>RAM: <code>2 GB</code>,</li>
<li>Number of requests: <code>2,000,000</code> per month, and</li>
<li>Amount of ephemeral storage allocated: <code>512 MB</code></li>
</ul>
</li>
<li><strong><code>USD 5.13</code></strong> for <em>Amazon DynamoDB</em>:
<ul>
<li>Table class: Standard,</li>
<li>Average item size (all attributes): <code>64 KB</code>,</li>
<li>Data storage size: <code>1</code> GB,</li>
<li><code>10,000</code> on-demand writes (90% Standard writes and 10% Transactional writes), and
<code>1,000,000</code> on-demand reads (100% Strongly consistent reads)</li>
</ul>
</li>
<li><strong><code>USD 3.00</code></strong> for <em>Amazon API Gateway</em>:
<ul>
<li>HTTP API requests: <code>3,000,000</code> per month, and</li>
<li>Average size of each request: <code>400 KB</code></li>
</ul>
</li>
</ul>
</div>
</div>
<p>Realistically, costs might fall below <code>USD 8</code> per month if the application experiences limited usage.
For periods when the application remains dormant and inactive, AWS Lambda charges are non-existent.
Additionally, the expenses for Amazon API Gateway and Amazon DynamoDB Table usage remain very low.</p>
<blockquote class="gblog-hint important">
<div class="gblog-hint__title flex align-center"><i class="fa important" title="Important"></i></div>
<div class="gblog-hint__text">If I were to opt for an alternative compute solution from AWS (e.g. ECS, EC2, or EKS, etc),
the costs would be substantially higher, even during periods of application inactivity.</div>
</blockquote>
<div class="gblog-post__anchorwrap">
<h3 id="front-end">
Front-End
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#front-end" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Front-End" href="https://techeule.com/posts/cost-driven-cloud-native-application/#front-end">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h3>
</div>
<p>BqEditor’s user interface resides within the user’s web browser as a Single Page Application (SPA).
TypeScript, Lit, and Redux/Redux-Toolkit are the driving forces behind its creation,
while styling is achieved through CSS and Bulma.io.
The front-end’s source code can be found in the
<a
class="gblog-markdown__link"
href="https://github.com/techeule/techeule-blog-quarkus/tree/main/blogq-web-ui"
>blogq-web-ui</a>
folder within the same repository.</p>
<div class="gblog-post__anchorwrap">
<h4 id="front-end-deployment">
Front-End Deployment
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#front-end-deployment" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Front-End Deployment" href="https://techeule.com/posts/cost-driven-cloud-native-application/#front-end-deployment">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h4>
</div>
<p>Front-end artifacts, essentially static text and media files, find their home in an Amazon S3 bucket.
However, there’s a twist. Amazon S3 website hosting doesn’t presently offer HTTPS endpoints.
For HTTPS via a custom domain name, Amazon CloudFront (CDN) steps in.
CloudFront serves files from Amazon S3 to the internet,
complete with a custom domain name and secure HTTPS protocol.
Notably, CloudFront provides advanced security measures, allowing access restriction through diverse rules.</p>
<p>One hurdle presented by Amazon CloudFront is caching.
Whenever a new front-end version is deployed, file names might remain unchanged.
Consequently, the CloudFront distribution is purged post-deployment,
ensuring that the subsequent request fetches the updated files from the Amazon S3 bucket.</p>
<p>In terms of cost, the front-end operation stays within the <code>USD 2.94</code> per month range.
The Amazon CloudFront component, in particular, costs approximately <code>USD 2.38</code>.
The calculation is achieved through AWS’s pricing calculator, enabling proactive cost estimates.</p>
<div class="gblog-expand">
<label class="gblog-expand__head flex justify-between" for="ef1f53d1-5">
<span>Cost Calculation</span>
<span>↕</span>
</label>
<input id="ef1f53d1-5" type="checkbox" class="gblog-expand__control hidden" />
<div class="gblog-markdown--nested gblog-expand__content">
<div class="gblog-post__anchorwrap">
<h5 id="i-have-made-the-following-assumptions">
I have made the following assumptions:
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#i-have-made-the-following-assumptions" class="gblog-post__anchor clip flex align-center" aria-label="Anchor I have made the following assumptions:" href="https://techeule.com/posts/cost-driven-cloud-native-application/#i-have-made-the-following-assumptions">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h5>
</div>
<ul>
<li><strong><code>USD 2.38</code></strong> for <em>Amazon CloudFront</em>:
<ul>
<li>(United States) Data transfer out to internet: <code>1 GB</code>,</li>
<li>(United States) Data transfer out to origin: <code>0 GB</code> per month,</li>
<li>(United States) Number of requests (HTTPS): <code>1,000,000</code> per month,</li>
<li>(Europe) Data transfer out to internet: <code>1 GB</code>,</li>
<li>(Europe) Data transfer out to origin: <code>0 GB</code> per month, and</li>
<li>(Europe) Number of requests (HTTPS): <code>1,000,000</code> per month</li>
</ul>
</li>
<li><strong><code>USD 0.56</code></strong> for <em>Amazon S3</em>:
<ul>
<li>S3 Standard,</li>
<li>S3 Standard storage: <code>1 GB</code>*,</li>
<li>PUT, COPY, POST, LIST requests to S3 Standard: <code>100,000</code>,</li>
<li>GET, SELECT, and all other requests from S3 Standard: <code>100,000</code>,</li>
<li>Data returned by S3 Select: <code>1 GB</code> per month*,</li>
<li>Data transfer Inbound: All other regions: <code>1 GB</code> per month*, and</li>
<li>Data transfer Outbound: Amazon CloudFront: <code>1 GB</code> per month*</li>
</ul>
</li>
</ul>
<blockquote class="gblog-hint note">
<div class="gblog-hint__title flex align-center"><i class="fa note" title="Note"></i></div>
<div class="gblog-hint__text">*) the smallest unit is <em>GB</em>, and smallest amount is <code>1</code>.</div>
</blockquote>
</div>
</div>
<blockquote class="gblog-hint important">
<div class="gblog-hint__title flex align-center"><i class="fa important" title="Important"></i></div>
<div class="gblog-hint__text">Deploying the front-end using ECS, EKS, or EC2 along with an AWS ALB (Amazon Application Load Balancer)
would result in significantly higher costs, spanning multiple orders of magnitude.</div>
</blockquote>
<div class="gblog-post__anchorwrap">
<h3 id="security">
Security
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#security" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Security" href="https://techeule.com/posts/cost-driven-cloud-native-application/#security">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h3>
</div>
<p>Security stands paramount in BqEditor, achieved through JWT (OpenID Connect/OAuth2) implementation.</p>
<div class="gblog-post__anchorwrap">
<h2 id="conclusion">
Conclusion
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#conclusion" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Conclusion" href="https://techeule.com/posts/cost-driven-cloud-native-application/#conclusion">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>In conclusion, this exploration has illuminated the intricacies of cost-efficient cloud-native applications.
By navigating the landscape of AWS, we’ve dissected the architecture, deployment nuances, and security aspects
of the BlogQ-Editor. The journey continues with in-depth insights into each of these components,
providing an opportunity for deeper engagement with cloud-native application development.</p>
<p>As always, while the setup illustrated above isn’t universally applicable,
it’s well-suited for scenarios where the backend doesn’t require continuous usage and the request
volume remains manageable. It’s advised to explore and compare various compute solutions, leveraging
AWS’s <a
class="gblog-markdown__link"
href="https://calculator.aws/"
>pricing calculator</a>.
This configuration is particularly effective for business applications operating during working hours or on
a set schedule. The integration of AWS services, streamlined deployment processes, and careful cost analysis make
this framework an attractive option for businesses seeking to enhance their cloud operations.</p>
<p>So, whether you’re embarking on a similar venture or charting a different cloud-native course,
the principles shared here can serve as guiding lights in your pursuit of efficient and cost-effective application
deployments. Stay tuned for more comprehensive explorations and insights as we delve deeper into the world of
cloud-native architecture and development on <a
class="gblog-markdown__link"
href="https://TechEule.com"
>TechEule.com</a>.</p>
<blockquote class="gblog-hint tip">
<div class="gblog-hint__title flex align-center"><i class="fa tip" title="Tip"></i></div>
<div class="gblog-hint__text">Expect upcoming articles dedicated to delving deeper into the application’s structural intricacies,
its source code (both front-end and back-end),
deployment specifics using AWS CDK, CI/CD setup,
and an exploration of the AWS services harmonized within this application.
Keep an eye out for the continuation of the <a
class="gblog-markdown__link"
href="/tags/BlogQ/"
>BlogQ series</a> <a
class="gblog-markdown__link"
href="/tags/BlogQ/"
>right here</a>.</div>
</blockquote>
<div class="gblog-post__anchorwrap">
<h3 id="_resources_">
<em>Resources</em>:
<a data-clipboard-text="https://techeule.com/posts/cost-driven-cloud-native-application/#_resources_" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Resources:" href="https://techeule.com/posts/cost-driven-cloud-native-application/#_resources_">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h3>
</div>
<ul>
<li><a
class="gblog-markdown__link"
href="https://github.com/techeule/techeule-blog-quarkus"
>Source Code of BlogQ-Editor</a></li>
<li><a
class="gblog-markdown__link"
href="https://aws.amazon.com/api-gateway/"
>https://aws.amazon.com/api-gateway/</a></li>
<li><a
class="gblog-markdown__link"
href="https://aws.amazon.com/cloudfront/"
>https://aws.amazon.com/cloudfront/</a></li>
<li><a
class="gblog-markdown__link"
href="https://aws.amazon.com/cdk/"
>https://aws.amazon.com/cdk/</a></li>
<li><a
class="gblog-markdown__link"
href="https://aws.amazon.com/lambda/"
>https://aws.amazon.com/lambda/</a></li>
<li><a
class="gblog-markdown__link"
href="https://aws.amazon.com/s3/"
>https://aws.amazon.com/s3/</a></li>
<li><a
class="gblog-markdown__link"
href="https://aws.amazon.com/dynamodb/"
>https://aws.amazon.com/dynamodb/</a></li>
<li><a
class="gblog-markdown__link"
href="https://jakarta.ee/"
>https://jakarta.ee/</a></li>
<li><a
class="gblog-markdown__link"
href="https://microprofile.io/"
>https://microprofile.io/</a></li>
<li><a
class="gblog-markdown__link"
href="https://quarkus.io/"
>https://quarkus.io/</a></li>
<li><a
class="gblog-markdown__link"
href="https://lit.dev/"
>https://lit.dev/</a></li>
</ul>Jenkins Library with Example for T12s Team Rotorhttps://techeule.com/posts/jenkins-library-with-example-for-t12s-team-rotor/Wassim Akachi2023-04-04T20:01:43+00:002023-04-04T20:01:43+00:00
<p>If you find yourself copy-pasting code snippets and pipeline sections in different
<a
class="gblog-markdown__link"
href="https://www.jenkins.io/doc/book/pipeline/"
>Jenkins pipelines</a>,
then it is time to ensure that the code is written correctly and properly. You usually have multiple software
projects with a similar CI/CD setup. What I have often observed is duplicated code all over the Jenkins pipelines,
even if all project structures are almost the same. In order to avoid duplicating the same code snippets in all
Jenkins pipelines and make the pipeline code clean, I use a library that provides steps, such as
<code>awsCloudPackerBuildImage</code>. This step has almost 200 lines of code, but using it makes the Jenkins pipeline
definition much easier to maintain and understand.</p>
<p>A Jenkins library is a simple git repository with certain file/folder structure.</p>
<div class="gblog-expand">
<label class="gblog-expand__head flex justify-between" for="b77146ad-0">
<span>Jenkins library file/folder structure</span>
<span>↕</span>
</label>
<input id="b77146ad-0" type="checkbox" class="gblog-expand__control hidden" />
<div class="gblog-markdown--nested gblog-expand__content">
<div class="gblog-include"><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">(root)
</span></span><span class="line"><span class="cl">+- src # Groovy source files
</span></span><span class="line"><span class="cl">| +- org
</span></span><span class="line"><span class="cl">| +- foo
</span></span><span class="line"><span class="cl">| +- Bar.groovy # for org.foo.Bar class
</span></span><span class="line"><span class="cl">+- vars
</span></span><span class="line"><span class="cl">| +- foo.groovy # for global 'foo' variable
</span></span><span class="line"><span class="cl">| +- foo.txt # help for 'foo' variable
</span></span><span class="line"><span class="cl">+- resources # resource files (external libraries only)
</span></span><span class="line"><span class="cl">| +- org
</span></span><span class="line"><span class="cl">| +- foo
</span></span><span class="line"><span class="cl">| +- bar.json # static helper data for org.foo.Bar
</span></span></code></pre></td></tr></table>
</div>
</div></div>
<p>– source <a
class="gblog-markdown__link"
href="https://www.jenkins.io/doc/book/pipeline/shared-libraries/#directory-structure"
>https://www.jenkins.io/doc/book/pipeline/shared-libraries/#directory-structure</a></p>
</div>
</div>
<p>At the time of writing the
<a
class="gblog-markdown__link"
href="https://github.com/techeule/t12s-team-rotor-jenkins-library"
>Jenkins library for Team Rotor</a>
looks like:
<div class="gblog-include"><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">(root)
</span></span><span class="line"><span class="cl">├── README.md
</span></span><span class="line"><span class="cl">├── pom.xml
</span></span><span class="line"><span class="cl">├── resources
</span></span><span class="line"><span class="cl">├── src
</span></span><span class="line"><span class="cl">│ └── io
</span></span><span class="line"><span class="cl">│ └── t12s
</span></span><span class="line"><span class="cl">│ └── automator
</span></span><span class="line"><span class="cl">│ └── team
</span></span><span class="line"><span class="cl">│ └── rotor
</span></span><span class="line"><span class="cl">│ └── jenkins
</span></span><span class="line"><span class="cl">│ └── T12sTeamRotor.groovy
</span></span><span class="line"><span class="cl">└── vars
</span></span><span class="line"><span class="cl"> ├── t12sRotationDryRun.groovy
</span></span><span class="line"><span class="cl"> ├── t12sRotationDryRun.md
</span></span><span class="line"><span class="cl"> ├── t12sRotationFetchResults.groovy
</span></span><span class="line"><span class="cl"> ├── t12sRotationFetchResults.md
</span></span><span class="line"><span class="cl"> ├── t12sRotationRun.groovy
</span></span><span class="line"><span class="cl"> └── t12sRotationRun.md
</span></span></code></pre></td></tr></table>
</div>
</div></div>
</p>
<p>I usually write the core logic of the library in (different) classes and store them in <code>src</code> folder.
For example the class <code>T12sTeamRotor</code> has the following content:
<div class="gblog-expand">
<label class="gblog-expand__head flex justify-between" for="4d4f3b41-2">
<span>source code of T12sTeamRotor.groovy</span>
<span>↕</span>
</label>
<input id="4d4f3b41-2" type="checkbox" class="gblog-expand__control hidden" />
<div class="gblog-markdown--nested gblog-expand__content">
<div class="gblog-include"><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span><span class="lnt">73
</span><span class="lnt">74
</span><span class="lnt">75
</span><span class="lnt">76
</span><span class="lnt">77
</span><span class="lnt">78
</span><span class="lnt">79
</span><span class="lnt">80
</span><span class="lnt">81
</span><span class="lnt">82
</span><span class="lnt">83
</span><span class="lnt">84
</span><span class="lnt">85
</span><span class="lnt">86
</span><span class="lnt">87
</span><span class="lnt">88
</span><span class="lnt">89
</span><span class="lnt">90
</span><span class="lnt">91
</span><span class="lnt">92
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="kn">package</span> <span class="n">io</span><span class="o">.</span><span class="na">t12s</span><span class="o">.</span><span class="na">automator</span><span class="o">.</span><span class="na">team</span><span class="o">.</span><span class="na">rotor</span><span class="o">.</span><span class="na">jenkins</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">groovy.json.JsonSlurperClassic</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">java.net.http.HttpClient</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">java.net.http.HttpRequest</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">java.net.http.HttpResponse</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">java.nio.charset.StandardCharsets</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">java.time.Duration</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">T12sTeamRotor</span> <span class="kd">implements</span> <span class="n">Serializable</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="nd">@Serial</span>
</span></span><span class="line"><span class="cl"> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">serialVersionUID</span> <span class="o">=</span> <span class="mi">1L</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">BASE_URI_STRING</span> <span class="o">=</span> <span class="s2">"https://team-api.t12s-automator.app/resources"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">baseUri</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">teamId</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">teamSecret</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">final</span> <span class="n">JsonSlurperClassic</span> <span class="n">jsonParser</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">final</span> <span class="n">HttpClient</span> <span class="n">httpClient</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">final</span> <span class="kt">def</span> <span class="n">step</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">final</span> <span class="n">Duration</span> <span class="n">timeoutDuration</span> <span class="o">=</span> <span class="n">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">45</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">T12sTeamRotor</span><span class="o">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">baseUri</span><span class="o">,</span> <span class="kd">final</span> <span class="n">step</span><span class="o">,</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">teamId</span><span class="o">,</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">teamSecret</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">.</span><span class="na">baseUri</span> <span class="o">=</span> <span class="n">baseUri</span>
</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">.</span><span class="na">teamId</span> <span class="o">=</span> <span class="n">teamId</span>
</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">.</span><span class="na">teamSecret</span> <span class="o">=</span> <span class="n">teamSecret</span>
</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">.</span><span class="na">step</span> <span class="o">=</span> <span class="n">step</span>
</span></span><span class="line"><span class="cl"> <span class="n">jsonParser</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JsonSlurperClassic</span><span class="o">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">httpClient</span> <span class="o">=</span> <span class="n">HttpClient</span><span class="o">.</span><span class="na">newHttpClient</span><span class="o">()</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">T12sTeamRotor</span><span class="o">(</span><span class="kd">final</span> <span class="kt">def</span> <span class="n">step</span><span class="o">,</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">teamId</span><span class="o">,</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">teamSecret</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">(</span><span class="n">BASE_URI_STRING</span><span class="o">,</span> <span class="n">step</span><span class="o">,</span> <span class="n">teamId</span><span class="o">,</span> <span class="n">teamSecret</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">List</span><span class="o"><</span><span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="o">?>></span> <span class="n">fetchRotationRunResults</span><span class="o">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">rotationId</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">def</span> <span class="n">rotationResultsUri</span> <span class="o">=</span> <span class="n">URI</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="n">baseUri</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"/team/"</span> <span class="o">+</span> <span class="n">encode</span><span class="o">(</span><span class="n">teamId</span><span class="o">)</span> <span class="o">+</span> <span class="s2">"/rotation/"</span> <span class="o">+</span> <span class="n">encode</span><span class="o">(</span><span class="n">rotationId</span><span class="o">)</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"/runResults?secret="</span> <span class="o">+</span> <span class="n">encode</span><span class="o">(</span><span class="n">teamSecret</span><span class="o">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">def</span> <span class="n">rotationResultsRequest</span> <span class="o">=</span> <span class="n">HttpRequest</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">(</span><span class="n">rotationResultsUri</span><span class="o">).</span>
</span></span><span class="line"><span class="cl"> <span class="n">timeout</span><span class="o">(</span><span class="n">timeoutDuration</span><span class="o">).</span>
</span></span><span class="line"><span class="cl"> <span class="n">header</span><span class="o">(</span><span class="s2">"accept"</span><span class="o">,</span> <span class="s2">"application/json"</span><span class="o">).</span>
</span></span><span class="line"><span class="cl"> <span class="n">GET</span><span class="o">().</span>
</span></span><span class="line"><span class="cl"> <span class="n">build</span><span class="o">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">rotationResultsResponse</span> <span class="o">=</span> <span class="n">httpClient</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">rotationResultsRequest</span><span class="o">,</span> <span class="n">HttpResponse</span><span class="o">.</span><span class="na">BodyHandlers</span><span class="o">.</span><span class="na">ofString</span><span class="o">())</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">rotationResultsResponse</span><span class="o">.</span><span class="na">statusCode</span><span class="o">()</span> <span class="o">==</span> <span class="mi">200</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">jsonParser</span><span class="o">.</span><span class="na">parseText</span><span class="o">(</span><span class="n">rotationResultsResponse</span><span class="o">.</span><span class="na">body</span><span class="o">())</span> <span class="k">as</span> <span class="n">List</span><span class="o"><</span><span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="o">?>></span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">step</span><span class="o">.</span><span class="na">echo</span><span class="o">(</span><span class="s2">"can not execute operation fetchRotationRunResults, statusCode: "</span> <span class="o">+</span> <span class="n">rotationResultsResponse</span><span class="o">.</span><span class="na">statusCode</span><span class="o">())</span>
</span></span><span class="line"><span class="cl"> <span class="n">step</span><span class="o">.</span><span class="na">echo</span><span class="o">(</span><span class="n">rotationResultsResponse</span><span class="o">.</span><span class="na">body</span><span class="o">())</span>
</span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalStateException</span><span class="o">(</span><span class="s2">"can not execute operation fetchRotationRunResults: \n"</span> <span class="o">+</span> <span class="n">rotationResultsResponse</span><span class="o">.</span><span class="na">body</span><span class="o">())</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="o">?></span> <span class="n">internalRotate</span><span class="o">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">rotationId</span><span class="o">,</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">runMode</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">def</span> <span class="n">rotateUri</span> <span class="o">=</span> <span class="n">URI</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="n">baseUri</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"/team/"</span> <span class="o">+</span> <span class="n">encode</span><span class="o">(</span><span class="n">teamId</span><span class="o">)</span> <span class="o">+</span> <span class="s2">"/rotation/"</span> <span class="o">+</span> <span class="n">encode</span><span class="o">(</span><span class="n">rotationId</span><span class="o">)</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"/runResults?secret="</span> <span class="o">+</span> <span class="n">encode</span><span class="o">(</span><span class="n">teamSecret</span><span class="o">)</span> <span class="o">+</span> <span class="s2">"&saveMode="</span> <span class="o">+</span> <span class="n">encode</span><span class="o">(</span><span class="n">runMode</span><span class="o">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">def</span> <span class="n">rotateRequest</span> <span class="o">=</span> <span class="n">HttpRequest</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">(</span><span class="n">rotateUri</span><span class="o">).</span>
</span></span><span class="line"><span class="cl"> <span class="n">timeout</span><span class="o">(</span><span class="n">timeoutDuration</span><span class="o">).</span>
</span></span><span class="line"><span class="cl"> <span class="n">header</span><span class="o">(</span><span class="s2">"accept"</span><span class="o">,</span> <span class="s2">"application/json"</span><span class="o">).</span>
</span></span><span class="line"><span class="cl"> <span class="n">POST</span><span class="o">(</span><span class="n">HttpRequest</span><span class="o">.</span><span class="na">BodyPublishers</span><span class="o">.</span><span class="na">noBody</span><span class="o">()).</span>
</span></span><span class="line"><span class="cl"> <span class="n">build</span><span class="o">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">rotateResponse</span> <span class="o">=</span> <span class="n">httpClient</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">rotateRequest</span><span class="o">,</span> <span class="n">HttpResponse</span><span class="o">.</span><span class="na">BodyHandlers</span><span class="o">.</span><span class="na">ofString</span><span class="o">())</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">rotateResponse</span><span class="o">.</span><span class="na">statusCode</span><span class="o">()</span> <span class="o">==</span> <span class="mi">200</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">jsonParser</span><span class="o">.</span><span class="na">parseText</span><span class="o">(</span><span class="n">rotateResponse</span><span class="o">.</span><span class="na">body</span><span class="o">())</span> <span class="k">as</span> <span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="o">?></span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">step</span><span class="o">.</span><span class="na">echo</span><span class="o">(</span><span class="s2">"can not execute operation internalRotate, statusCode: "</span> <span class="o">+</span> <span class="n">rotateResponse</span><span class="o">.</span><span class="na">statusCode</span><span class="o">())</span>
</span></span><span class="line"><span class="cl"> <span class="n">step</span><span class="o">.</span><span class="na">echo</span><span class="o">(</span><span class="n">rotateResponse</span><span class="o">.</span><span class="na">body</span><span class="o">())</span>
</span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalStateException</span><span class="o">(</span><span class="s2">"can not execute operation internalRotate: \n"</span> <span class="o">+</span> <span class="n">rotateResponse</span><span class="o">.</span><span class="na">body</span><span class="o">())</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="o">?></span> <span class="n">rotate</span><span class="o">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">rotationId</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">internalRotate</span><span class="o">(</span><span class="n">rotationId</span><span class="o">,</span> <span class="s1">'Save'</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="o">?></span> <span class="n">rotateDryRun</span><span class="o">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">rotationId</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">internalRotate</span><span class="o">(</span><span class="n">rotationId</span><span class="o">,</span> <span class="s1">'DryRun'</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">static</span> <span class="n">String</span> <span class="nf">encode</span><span class="o">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">text</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">URLEncoder</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">text</span><span class="o">,</span> <span class="n">StandardCharsets</span><span class="o">.</span><span class="na">UTF_8</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div></div>
<p>Source code:
<a
class="gblog-markdown__link"
href="https://raw.githubusercontent.com/techeule/t12s-team-rotor-jenkins-library/0.0.6/src/io/t12s/automator/team/rotor/jenkins/T12sTeamRotor.groovy"
>GitHub > Jenkins library for T12S-Automator Team Rotor > T12sTeamRotor.groovy</a></p>
</div>
</div>
The <a
class="gblog-markdown__link"
href="https://www.jenkins.io/doc/book/pipeline/#step"
>steps</a> for Jenkins are written in the <code>var</code> folder.
For example, the code for the t12sRotationRun step is stored in a file called <code>t12sRotationRun.groovy</code>.
This file contains a script that performs the following actions:
<div class="gblog-include"><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="kn">package</span> <span class="n">io</span><span class="o">.</span><span class="na">t12s</span><span class="o">.</span><span class="na">automator</span><span class="o">.</span><span class="na">team</span><span class="o">.</span><span class="na">rotor</span><span class="o">.</span><span class="na">jenkins</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="o">?></span> <span class="n">call</span><span class="o">(</span><span class="kd">final</span> <span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">></span> <span class="n">config</span> <span class="o">=</span> <span class="o">[:])</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">></span> <span class="n">defaultConfig</span> <span class="o">=</span> <span class="o">[</span><span class="s1">'verbose'</span><span class="o">:</span><span class="s1">'false'</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">></span> <span class="n">finalConfig</span> <span class="o">=</span> <span class="o">([:]</span> <span class="o"><<</span> <span class="n">defaultConfig</span><span class="o">)</span> <span class="o"><<</span> <span class="n">config</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">def</span> <span class="n">rotor</span> <span class="o">=</span> <span class="k">new</span> <span class="n">T12sTeamRotor</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">finalConfig</span><span class="o">.</span><span class="na">teamId</span><span class="o">,</span> <span class="n">finalConfig</span><span class="o">.</span><span class="na">teamSecret</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">def</span> <span class="n">rotationResult</span> <span class="o">=</span> <span class="n">rotor</span><span class="o">.</span><span class="na">rotate</span><span class="o">(</span><span class="n">finalConfig</span><span class="o">.</span><span class="na">rotationId</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="s1">'verbose'</span> <span class="k">in</span> <span class="n">finalConfig</span><span class="o">.</span><span class="na">keySet</span><span class="o">()</span> <span class="o">&&</span> <span class="n">finalConfig</span><span class="o">.</span><span class="na">verbose</span> <span class="o">==</span> <span class="s1">'true'</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">println</span> <span class="n">rotationResult</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">rotationResult</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span></span></span></code></pre></td></tr></table>
</div>
</div></div>
Source code: <a
class="gblog-markdown__link"
href="https://github.com/techeule/t12s-team-rotor-jenkins-library/blob/0.0.6/vars/t12sRotationRun.groovy"
>GitHub > Jenkins library for T12S-Automator Team Rotor > t12sRotationRun.groovy</a></p>
<div class="gblog-post__anchorwrap">
<h2 id="how-to-use-the-team-rotor-jenkins-library-in-your-jenkins-pipeline">
How to use the Team Rotor Jenkins library in your Jenkins pipeline
<a data-clipboard-text="https://techeule.com/posts/jenkins-library-with-example-for-t12s-team-rotor/#how-to-use-the-team-rotor-jenkins-library-in-your-jenkins-pipeline" class="gblog-post__anchor clip flex align-center" aria-label="Anchor How to use the Team Rotor Jenkins library in your Jenkins pipeline" href="https://techeule.com/posts/jenkins-library-with-example-for-t12s-team-rotor/#how-to-use-the-team-rotor-jenkins-library-in-your-jenkins-pipeline">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>The Git repository of the Jenkins library for Team Rotor can be found at GitHub
<a
class="gblog-markdown__link"
href="https://github.com/techeule/t12s-team-rotor-jenkins-library"
>https://github.com/techeule/t12s-team-rotor-jenkins-library</a>.</p>
<p>Before using a Jenkins library you need to define it in your Jenkins Server.
To do so, you can follow the steps at the Jenkins official docs
<a
class="gblog-markdown__link"
href="https://www.jenkins.io/doc/book/pipeline/shared-libraries/#using-libraries"
>https://www.jenkins.io/doc/book/pipeline/shared-libraries/#using-libraries</a></p>
<p>Here is a simple Jenkins pipeline with two stages. Each stage start/run a rotation:
<div class="gblog-include"><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="nd">@Library</span><span class="o">(</span><span class="s1">'t12s-team-rotor-jenkins-library'</span><span class="o">)</span> <span class="n">_</span> <span class="c1">// do not forget the underscore "_"
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">pipeline</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">stages</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">stage</span><span class="o">(</span><span class="s1">'Stand-Up Presenter'</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">steps</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">script</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">runRotation</span><span class="o">(</span><span class="s1">'9EF37F04-0301-4D14-B69C-672DF6C9BAE4'</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="n">stage</span><span class="o">(</span><span class="s1">'Sprint Review Presenter'</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">steps</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">script</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">runRotation</span><span class="o">(</span><span class="s1">'49A73189-2BB2-4DB5-9BBE-CA04231DE76E'</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kt">void</span> <span class="nf">runRotation</span><span class="o">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">rotationId</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// the following step "t12sRotationRun" is defined in the t12s-team-rotor-jenkins-library
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">final</span> <span class="kt">def</span> <span class="n">result</span> <span class="o">=</span> <span class="n">t12sRotationRun</span><span class="o">([</span>
</span></span><span class="line"><span class="cl"> <span class="s1">'teamId'</span> <span class="o">:</span> <span class="s1">'your-team-id'</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="s1">'teamSecret'</span><span class="o">:</span> <span class="s1">'your-team-secret'</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="s1">'rotationId'</span><span class="o">:</span> <span class="n">rotationId</span>
</span></span><span class="line"><span class="cl"> <span class="o">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">def</span> <span class="n">resultAsString</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="na">toString</span><span class="o">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">echo</span> <span class="n">resultAsString</span>
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">def</span> <span class="n">chosenName</span> <span class="o">=</span> <span class="o">(</span><span class="n">result</span><span class="o">.</span><span class="na">memberOrder</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="k">as</span> <span class="n">String</span><span class="o">).</span><span class="na">capitalize</span><span class="o">()</span>
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">def</span> <span class="n">rotationName</span> <span class="o">=</span> <span class="o">(</span><span class="n">result</span><span class="o">.</span><span class="na">rotationName</span> <span class="k">as</span> <span class="n">String</span><span class="o">).</span><span class="na">capitalize</span><span class="o">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">def</span> <span class="n">message</span> <span class="o">=</span> <span class="s2">"""
</span></span></span><span class="line"><span class="cl"><span class="s2"> ${rotationName} is :magic_wand: *${chosenName}* :party:.
</span></span></span><span class="line"><span class="cl"><span class="s2"> Decision was made at `${result.createdAt}`.
</span></span></span><span class="line"><span class="cl"><span class="s2"> _Fallback_: If *${chosenName}* can not do it, the next persons in line would be
</span></span></span><span class="line"><span class="cl"><span class="s2"> *`${result.memberOrder.subList(1, result.memberOrder.size())}`*.
</span></span></span><span class="line"><span class="cl"><span class="s2"> The order of the fallback persons could _change_ on next rotation run.
</span></span></span><span class="line"><span class="cl"><span class="s2"> """</span><span class="o">.</span><span class="na">stripIndent</span><span class="o">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">currentBuild</span><span class="o">.</span><span class="na">description</span> <span class="o">=</span> <span class="s2">"${rotationName}: <b>${chosenName}</b>"</span>
</span></span><span class="line"><span class="cl"> <span class="n">slackSend</span><span class="o">(</span><span class="nl">channel:</span> <span class="s1">'your-slack-channel-id_name'</span><span class="o">,</span> <span class="nl">color:</span> <span class="s1">'#00FF00'</span><span class="o">,</span> <span class="nl">message:</span> <span class="n">message</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span></span></span></code></pre></td></tr></table>
</div>
</div></div>
</p>
<div class="gblog-post__anchorwrap">
<h3 id="_resources_">
<em>Resources</em>:
<a data-clipboard-text="https://techeule.com/posts/jenkins-library-with-example-for-t12s-team-rotor/#_resources_" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Resources:" href="https://techeule.com/posts/jenkins-library-with-example-for-t12s-team-rotor/#_resources_">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h3>
</div>
<ul>
<li><a
class="gblog-markdown__link"
href="https://team.t12s-automator.app/"
>Team Rotor</a></li>
<li><a
class="gblog-markdown__link"
href="https://github.com/techeule/t12s-team-rotor-jenkins-library"
>Jenkins library for T12S-Automator Team Rotor</a></li>
<li><a
class="gblog-markdown__link"
href="https://www.jenkins.io/"
>Jenkins</a></li>
</ul>T12s Automator Team-Rotor Announcementhttps://techeule.com/posts/t12s-automator-team-rotor-announcement/Wassim Akachi2023-03-18T18:30:15+01:002023-03-18T18:30:15+01:00
<p>Some companies take the approach to rotate tasks over all team members.
In this case you may have experienced that every morning the same question pops up namely who is turn today, and who was the last time and that’s without the early coffee cup.</p>
<p>To be precise you would like to select one team member (randomly/alphabetical order) to do certain task during a given time period.
E.g. who presents this week in the daily stand-up?
Who does the release this week?
And the important question is: who did it last time?</p>
<p>So, I am happy to announce the first tool from TechEule <strong>Team-Rotor</strong> which could resolve all these questions ;-) .
Feel free to use the application on <a
class="gblog-markdown__link"
href="https://team.t12s-automator.app"
>https://team.t12s-automator.app</a></p>
<p>The Team-Rotor main functionalities are similar to a picker wheel:</p>
<ul>
<li>create a team,</li>
<li>configure one or more rotations,</li>
<li>run the rotations and</li>
<li>rotation results are stored 60 days by default.</li>
</ul>
<p>The team definition, rotation configurations and the rotation results are persisted.
The application is build with Java using Standards like <a
class="gblog-markdown__link"
href="https://microprofile.io/"
>Eclipse MicroProfile</a> in the backend.
Frontend is build using HTML, CSS (using <a
class="gblog-markdown__link"
href="https://bulma.io/"
>Bulma</a>) combined with vanilla JavaScript -
<a
class="gblog-markdown__link"
href="https://developer.mozilla.org/en-US/docs/Web/Web_Components"
>Web Components</a> and
<a
class="gblog-markdown__link"
href="https://redux.js.org"
>Redux</a>.</p>
<div class="gblog-post__anchorwrap">
<h1 id="how-to-use-the-team-rotor">
How to use the Team-Rotor
<a data-clipboard-text="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#how-to-use-the-team-rotor" class="gblog-post__anchor clip flex align-center" aria-label="Anchor How to use the Team-Rotor" href="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#how-to-use-the-team-rotor">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h1>
</div>
<p>The application is minimalistic.</p>
<div class="gblog-post__anchorwrap">
<h2 id="how-to-create-new-team">
How to create new Team
<a data-clipboard-text="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#how-to-create-new-team" class="gblog-post__anchor clip flex align-center" aria-label="Anchor How to create new Team" href="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#how-to-create-new-team">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>Visiting <a
class="gblog-markdown__link"
href="https://team.t12s-automator.app/create-team"
>https://team.t12s-automator.app/create-team</a>
<img src="/images/posts/t12s-automator-team-rotor-announcement/team-t12s-02-create-team.png"></p>
<p>The credentials are intend to be shared within the team. So do be carefully with the selection of the <code>Team Secret</code> value.
The <code>Team Id</code> should be somehow namespaced like <code>company-name-department-[team-name]</code> - it should be globally unique in
the application.</p>
<p>After you have created a new account/team successfully a “wizard” is activated to guide you through the required setup.</p>
<div class="gblog-post__anchorwrap">
<h2 id="how-to-add-team-members">
How to add Team Members
<a data-clipboard-text="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#how-to-add-team-members" class="gblog-post__anchor clip flex align-center" aria-label="Anchor How to add Team Members" href="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#how-to-add-team-members">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>First you need to add the team members to the profile.
<img src="/images/posts/t12s-automator-team-rotor-announcement/team-t12s-03-wizard-team-members.png"></p>
<p>You can edit the team profile by visiting
<a
class="gblog-markdown__link"
href="https://team.t12s-automator.app/team-profile"
>https://team.t12s-automator.app/team-profile</a>.
<img src="/images/posts/t12s-automator-team-rotor-announcement/team-t12s-07-team-profile.png"></p>
<div class="gblog-post__anchorwrap">
<h2 id="how-to-create-new-rotation-configuration">
How to create new Rotation Configuration
<a data-clipboard-text="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#how-to-create-new-rotation-configuration" class="gblog-post__anchor clip flex align-center" aria-label="Anchor How to create new Rotation Configuration" href="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#how-to-create-new-rotation-configuration">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>Second you need to create your first Rotation Configuration.
<img src="/images/posts/t12s-automator-team-rotor-announcement/team-t12s-04-wizard-rotation.png"></p>
<p>You can create new Rotation Configuration by visiting
<a
class="gblog-markdown__link"
href="https://team.t12s-automator.app/create-team-rotation"
>https://team.t12s-automator.app/create-team-rotation</a>.</p>
<div class="gblog-post__anchorwrap">
<h2 id="rotation-configuration-overview">
Rotation Configuration Overview
<a data-clipboard-text="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#rotation-configuration-overview" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Rotation Configuration Overview" href="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#rotation-configuration-overview">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>The overview for all Rotation Configurations can be found at
<a
class="gblog-markdown__link"
href="https://team.t12s-automator.app/team-rotation-overview"
>https://team.t12s-automator.app/team-rotation-overview</a>.</p>
<div class="gblog-post__anchorwrap">
<h2 id="triggerrun-rotation-configuration">
Trigger/Run Rotation Configuration
<a data-clipboard-text="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#triggerrun-rotation-configuration" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Trigger/Run Rotation Configuration" href="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#triggerrun-rotation-configuration">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>Open one rotation from the overview for all Rotation Configurations and click on the Rotate button.
<img src="/images/posts/t12s-automator-team-rotor-announcement/team-t12s-06-team-rotation-configuration.png"></p>
<p>You can also trigger the rotation in an automated way, e.g. from your automation tools.
The correct url for the specific rotation can be found in the rotation details view <em>Rotation Run URL</em>.</p>
<div class="gblog-post__anchorwrap">
<h2 id="share-login-data">
Share Login data
<a data-clipboard-text="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#share-login-data" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Share Login data" href="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#share-login-data">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>If you are logged in, a <em>Share Login Data</em> button is always visible at the bottom of the page.
<img src="/images/posts/t12s-automator-team-rotor-announcement/team-t12s-08-share-login-01.png">
When you click on the button you can see the relevant data in a modal like the following screenshot
<img src="/images/posts/t12s-automator-team-rotor-announcement/team-t12s-09-share-modal.png"></p>
<div class="gblog-post__anchorwrap">
<h2 id="web-api">
Web API
<a data-clipboard-text="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#web-api" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Web API" href="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#web-api">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>You can find Web-API documentation at
<a
class="gblog-markdown__link"
href="https://team-api.t12s-automator.app/q/openapi"
>https://team-api.t12s-automator.app/q/openapi</a> with the
<a
class="gblog-markdown__link"
href="https://spec.openapis.org/oas/v3.0.3"
>OpenAPI v3.0.3</a> format with swagger-ui at
<a
class="gblog-markdown__link"
href="https://team-api.t12s-automator.app/q/swagger-ui"
>https://team-api.t12s-automator.app/q/swagger-ui</a>.</p>
<div class="gblog-post__anchorwrap">
<h2 id="_resources_">
<em>Resources</em>:
<a data-clipboard-text="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#_resources_" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Resources:" href="https://techeule.com/posts/t12s-automator-team-rotor-announcement/#_resources_">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<ul>
<li><a
class="gblog-markdown__link"
href="https://team.t12s-automator.app"
>https://team.t12s-automator.app</a></li>
<li><a
class="gblog-markdown__link"
href="https://microprofile.io"
>Eclipse MicroProfile</a></li>
<li><a
class="gblog-markdown__link"
href="https://bulma.io"
>Bulma</a></li>
<li><a
class="gblog-markdown__link"
href="https://developer.mozilla.org/en-US/docs/Web/Web_Components"
>Web Components</a></li>
<li><a
class="gblog-markdown__link"
href="https://redux.js.org"
>Redux</a></li>
<li><a
class="gblog-markdown__link"
href="https://spec.openapis.org/oas/v3.0.3"
>OpenAPI v3.0.3</a></li>
</ul>Simple Java Avro Serializer Deserializerhttps://techeule.com/posts/Simple-Java-Avro-Serializer-Deserializer/Wassim Akachi2023-02-03T22:50:03+01:002023-02-03T22:50:03+01:00
<blockquote>
<p>“Apache Avro is the leading serialization format for record data,
and first choice for streaming data pipelines.
It offers excellent schema evolution, and has implementations
for the JVM …” - <em>source: <a
class="gblog-markdown__link"
href="https://avro.apache.org/"
>avro.apache.org</a></em></p>
</blockquote>
<p>In this post a simple Avro serializer and deserializer (AVRO serde) implementation is presented.
The Apache Avro library make use of the single responsibility principle, and it defines different classes for different
type of work.</p>
<p>The class <code>SpecificDatumWriter</code> is responsible for writing Avro object into an <code>OutputStream</code>.
The class <code>SpecificDatumReader</code> is responsible for determining the in-memory serialized data representation while
deserializing.</p>
<p>Here is an abbreviated source code of the class <code>AvroToBytesSerializerDeserializer<T extends GenericRecord></code>:</p>
<div class="gblog-include"><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nn">com.techeule.examples.avro</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ... shortened for readability ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">org.apache.avro.generic.GenericRecord</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">org.apache.avro.specific.SpecificDatumReader</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">org.apache.avro.specific.SpecificDatumWriter</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">AvroToBytesSerializerDeserializer</span><span class="o"><</span><span class="n">T</span> <span class="kd">extends</span> <span class="n">GenericRecord</span><span class="o">></span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">final</span> <span class="n">SpecificDatumWriter</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">datumWriter</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">final</span> <span class="n">SpecificDatumReader</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">datumReader</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">public</span> <span class="nf">AvroToBytesSerializerDeserializer</span><span class="o">(</span><span class="kd">final</span> <span class="n">Class</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">classType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">classType</span><span class="o">,</span> <span class="s">"classType must not be null."</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">datumWriter</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SpecificDatumWriter</span><span class="o"><>(</span><span class="n">classType</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">datumReader</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SpecificDatumReader</span><span class="o"><>(</span><span class="n">classType</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">public</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">serialize</span><span class="o">(</span><span class="kd">final</span> <span class="n">List</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">records</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// ... omitted for readability ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">public</span> <span class="n">List</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="nf">deserialize</span><span class="o">(</span><span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// ... omitted for readability ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span></span></span></code></pre></td></tr></table>
</div>
</div></div>
<div class="gblog-post__anchorwrap">
<h2 id="serialization">
Serialization
<a data-clipboard-text="https://techeule.com/posts/Simple-Java-Avro-Serializer-Deserializer/#serialization" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Serialization" href="https://techeule.com/posts/Simple-Java-Avro-Serializer-Deserializer/#serialization">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>The method <code>AvroToBytesSerializerDeserializer#serialize(List<T>):byte[]</code> serializes list of Avro objects into <code>byte[]</code>.</p>
<div class="gblog-include"><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"> <span class="kd">public</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">serialize</span><span class="o">(</span><span class="kd">final</span> <span class="n">List</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">records</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">((</span><span class="n">records</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">||</span> <span class="n">records</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="n">IllegalArgumentException</span><span class="o">(</span><span class="s">"records must not be null or empty"</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">var</span> <span class="n">byteArrayOutputStream</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ByteArrayOutputStream</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="o">(</span><span class="kd">final</span> <span class="n">var</span> <span class="n">dataFileWriter</span> <span class="o">=</span> <span class="k">new</span> <span class="n">DataFileWriter</span><span class="o"><>(</span><span class="n">datumWriter</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">tryToAppendRecords</span><span class="o">(</span><span class="n">byteArrayOutputStream</span><span class="o">,</span> <span class="n">dataFileWriter</span><span class="o">,</span> <span class="n">records</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="kd">final</span> <span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="n">RuntimeException</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">byteArrayOutputStream</span><span class="o">.</span><span class="na">toByteArray</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kt">void</span> <span class="nf">tryToAppendRecords</span><span class="o">(</span><span class="kd">final</span> <span class="n">ByteArrayOutputStream</span> <span class="n">byteArrayOutputStream</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">DataFileWriter</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">dataFileWriter</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">List</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">records</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">dataFileWriter</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="n">records</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">).</span><span class="na">getSchema</span><span class="o">(),</span> <span class="n">byteArrayOutputStream</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="o">(</span><span class="kd">final</span> <span class="n">T</span> <span class="n">avroRecord</span> <span class="o">:</span> <span class="n">records</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">avroRecord</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">dataFileWriter</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">avroRecord</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span></span></span></code></pre></td></tr></table>
</div>
</div></div>
<div class="gblog-post__anchorwrap">
<h2 id="deserialization">
Deserialization
<a data-clipboard-text="https://techeule.com/posts/Simple-Java-Avro-Serializer-Deserializer/#deserialization" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Deserialization" href="https://techeule.com/posts/Simple-Java-Avro-Serializer-Deserializer/#deserialization">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>The method <code>AvroToBytesSerializerDeserializer#deSerialize(byte[]):List<T></code> deserializes <code>byte[]</code> into a list of Avro
objects. The class <code>DataFileReader</code> is responsible to read the data from a <code>SeekableInput</code>
(e.g. file or <code>byte[]</code>) and putting them in memory.</p>
<div class="gblog-include"><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"> <span class="kd">public</span> <span class="n">List</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="nf">deserialize</span><span class="o">(</span><span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="o">(</span><span class="kd">final</span> <span class="n">DataFileReader</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">dataFileReader</span> <span class="o">=</span> <span class="k">new</span> <span class="n">DataFileReader</span><span class="o"><>(</span><span class="k">new</span> <span class="n">SeekableByteArrayInput</span><span class="o">(</span><span class="n">data</span><span class="o">),</span> <span class="n">datumReader</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">tryToReadRecords</span><span class="o">(</span><span class="n">dataFileReader</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="kd">final</span> <span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="n">RuntimeException</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="n">List</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="nf">tryToReadRecords</span><span class="o">(</span><span class="kd">final</span> <span class="n">DataFileReader</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">dataFileReader</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">List</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">records</span> <span class="o">=</span> <span class="k">new</span> <span class="n">LinkedList</span><span class="o"><>();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="o">(</span><span class="n">dataFileReader</span><span class="o">.</span><span class="na">hasNext</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">records</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">dataFileReader</span><span class="o">.</span><span class="na">next</span><span class="o">());</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">records</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span></span></span></code></pre></td></tr></table>
</div>
</div></div>
<div class="gblog-post__anchorwrap">
<h2 id="conclusion">
Conclusion
<a data-clipboard-text="https://techeule.com/posts/Simple-Java-Avro-Serializer-Deserializer/#conclusion" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Conclusion" href="https://techeule.com/posts/Simple-Java-Avro-Serializer-Deserializer/#conclusion">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>A simple Avro serializer and deserializer can be implemented with just one class consisting of only 68 lines of code
including the <code>import</code>-statements and empty lines.
The entire source code of the <code>AvroToBytesSerializerDeserializer</code> can be found in a maven project as usual at
<a
class="gblog-markdown__link"
href="https://github.com/techeule/te-java-avro-serialization-deserialization"
>GitHub</a>
(<a
class="gblog-markdown__link"
href="https://github.com/techeule/te-java-avro-serialization-deserialization/blob/20230203/src/main/java/com/techeule/examples/avro/AvroToBytesSerializerDeserializer.java"
>techeule / te-java-avro-serialization-deserialization</a>)
with some unit-tests. The unit-tests can be seen as showcases of <code>AvroToBytesSerializerDeserializer</code>.</p>
<div class="gblog-post__anchorwrap">
<h3 id="_resources_">
<em>Resources</em>:
<a data-clipboard-text="https://techeule.com/posts/Simple-Java-Avro-Serializer-Deserializer/#_resources_" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Resources:" href="https://techeule.com/posts/Simple-Java-Avro-Serializer-Deserializer/#_resources_">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h3>
</div>
<ul>
<li><a
class="gblog-markdown__link"
href="https://github.com/techeule/te-java-avro-serialization-deserialization/blob/20230203/src/main/java/com/techeule/examples/avro/AvroToBytesSerializerDeserializer.java"
>Java Avro Serialization Deserialization Example at GitHub</a></li>
<li><a
class="gblog-markdown__link"
href="https://avro.apache.org/docs/1.11.1/getting-started-java/"
>Avro Getting Started @ Apache Avro</a></li>
<li><a
class="gblog-markdown__link"
href="https://en.wikipedia.org/wiki/Single-responsibility_principle"
>Single Responsibility Principle @ Wiki</a></li>
<li><a
class="gblog-markdown__link"
href="https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html"
>Single Responsibility Principle @ cleancoder.com</a></li>
</ul>Multiple Git Configurations - conditional includeshttps://techeule.com/posts/multiple-git-configurations-conditional_includes/Wassim Akachi2023-01-28T15:00:00+01:002023-01-28T15:00:00+01:00
<p>Today, I got across the git feature <em><a
class="gblog-markdown__link"
href="https://git-scm.com/docs/git-config#_conditional_includes"
>conditional_includes</a></em>.
You can specify which git configurations should be applied under which circumstances.</p>
<p>As a software engineer I am used to work on different projects and sometimes for different organizations.
I do not want to use the same e-mail address for all git repositories.
I made use of the git local configuration to specify
the e-mail address I want to show in my git commits for the corresponding git repository
(for example: <code>git config --local user.email "Some.Name@TechEule.com"</code>).</p>
<p>Using git <em>conditional_includes</em> in the <code>~/.gitconfig</code> file would reduce unwanted e-mail addresses in the wrong
git repositories. There are very useful conditions which can be defined in <code>~/.gitconfig</code> file.</p>
<ul>
<li>
<p>Include for all repositories inside <code>$HOME/dev/organization-1/</code>.
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[includeIf "gitdir:~/dev/organization-1/"]</span>
</span></span><span class="line"><span class="cl"> <span class="na">path</span> <span class="o">=</span> <span class="s">~/.git-configs/organization-1.inc</span></span></span></code></pre></td></tr></table>
</div>
</div></p>
<p>Content of <code>~/.git-configs/organization-1.inc</code>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[user]</span>
</span></span><span class="line"><span class="cl"> <span class="na">name</span> <span class="o">=</span> <span class="s">Max Peter
</span></span></span><span class="line"><span class="cl"><span class="s"> email = peter.max.org1@organization-1.com</span></span></span></code></pre></td></tr></table>
</div>
</div></p>
</li>
<li>
<p>Include for all repositories inside <code>$HOME/clients/Xyz-Inc/</code>.
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[includeIf "gitdir:~/clients/Xyz-Inc/"]</span>
</span></span><span class="line"><span class="cl"> <span class="na">path</span> <span class="o">=</span> <span class="s">~/.git-configs/clients-Xyz-Inc.inc</span></span></span></code></pre></td></tr></table>
</div>
</div></p>
</li>
<li>
<p>Content of <code>~/.git-configs/clients-Xyz-Inc.inc</code>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[user]</span>
</span></span><span class="line"><span class="cl"> <span class="na">name</span> <span class="o">=</span> <span class="s">Max Peter (Xyz)
</span></span></span><span class="line"><span class="cl"><span class="s"> email = peterM@xyz-1-inc.com</span></span></span></code></pre></td></tr></table>
</div>
</div></p>
</li>
<li>
<p>Include only if a remote with the given URL exists.
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[includeIf "hasconfig:remote.*.url:https://git.organization-2.com/**"]</span>
</span></span><span class="line"><span class="cl"> <span class="na">path</span> <span class="o">=</span> <span class="s">~/.git-configs/organization-2.inc</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[includeIf "hasconfig:remote.*.url:git@github.com:organization-3/**"]</span>
</span></span><span class="line"><span class="cl"> <span class="na">path</span> <span class="o">=</span> <span class="s">~/.git-configs/organization-3.inc</span></span></span></code></pre></td></tr></table>
</div>
</div></p>
<p>Content of <code>~/.git-configs/organization-2.inc</code>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[user]</span>
</span></span><span class="line"><span class="cl"> <span class="na">email</span> <span class="o">=</span> <span class="s">MaxPeter@organization-2.com</span></span></span></code></pre></td></tr></table>
</div>
</div></p>
<p>Content of <code>~/.git-configs/organization-3.inc</code>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[user]</span>
</span></span><span class="line"><span class="cl"> <span class="na">email</span> <span class="o">=</span> <span class="s">PeterMax2@organization-3.com</span></span></span></code></pre></td></tr></table>
</div>
</div></p>
</li>
</ul>
<p>The examples are copied or inspired from
<a
class="gblog-markdown__link"
href="https://git-scm.com/docs/git-config#_example"
>https://git-scm.com/docs/git-config</a>.</p>
<div class="gblog-post__anchorwrap">
<h2 id="conclusion">
Conclusion
<a data-clipboard-text="https://techeule.com/posts/multiple-git-configurations-conditional_includes/#conclusion" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Conclusion" href="https://techeule.com/posts/multiple-git-configurations-conditional_includes/#conclusion">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h2>
</div>
<p>When using the above exampels, you can set it up only once and you do not have to re-configure e.g.
the <code>user.email</code> when ever you clone a repository.</p>
<div class="gblog-post__anchorwrap">
<h3 id="_resources_">
<em>Resources</em>:
<a data-clipboard-text="https://techeule.com/posts/multiple-git-configurations-conditional_includes/#_resources_" class="gblog-post__anchor clip flex align-center" aria-label="Anchor Resources:" href="https://techeule.com/posts/multiple-git-configurations-conditional_includes/#_resources_">
<svg class="gblog-icon gblog_link"><use xlink:href="#gblog_link"></use></svg>
</a>
</h3>
</div>
<ul>
<li><a
class="gblog-markdown__link"
href="https://git-scm.com/docs/"
>https://git-scm.com/docs/</a></li>
<li><a
class="gblog-markdown__link"
href="https://git-scm.com/docs/git-config"
>https://git-scm.com/docs/git-config</a></li>
</ul>Hello Worldhttps://techeule.com/posts/hello-world/Wassim Akachi2023-01-26T22:40:00+01:002023-01-26T22:40:00+01:00
<p><span class="emoji">🎉</span>
Finally, this site is up and running.
<span class="emoji">🎉</span></p>
<p>To know more about it check the <a
class="gblog-markdown__link"
href="/about/"
>about page</a></p>