-
Clone the seed project https://github.com/kerimovscreations/cicdworkshop1
-
Check the classes and tests work correctly
BookingService
,TimeManager
,BookingDatabase
,Models
,Main.main()
-
Create a public repo in your GitHub account with repo name
cicdworkshop1
-
Remove the old remote git from repo and add your new created remote repo
git remote remove origin
git remote add {your new remote}
git push origin
-
Checkout a new branch
develop
git checkout -b develop
Info: https://docs.github.com/en/actions/using-workflows/about-workflows
File directory: .github/workflows
Note: Fix the intendation of workflow text after copy/pasting.
-
"Demo" workflow
1.1. Checkout a new branch
feature/demo-workflow
fromdevelop
1.2. Create a yaml file
develop-pull-analyze.yml
:name: Demo pull check on: pull_request: branches: - develop jobs: Explore-GitHub-Actions: runs-on: ubuntu-latest steps: - run: echo "π The job was automatically triggered by a ${{ github.event_name }} event." - run: echo "π§ This job is now running on a ${{ runner.os }} server hosted by GitHub!" - run: echo "π The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." - name: Check out repository code uses: actions/checkout@v3 - run: echo "π‘ The ${{ github.repository }} repository has been cloned to the runner." - run: echo "π₯οΈ The workflow is now ready to test your code on the runner." - name: List files in the repository run: | ls ${{ github.workspace }} - run: echo "π This job's status is ${{ job.status }}."
1.3. Push remote and create PR from
feature/demo-workflow
todevelop
1.4. Check pipeline completed successfully and check logs
-
"Build and test" workflow (CI)
2.1. Checkout new branch
feature/ci-step
fromdevelop
2.2. Create
build-and-test.yml
name: Build and test on: pull_request: branches: - develop jobs: build: runs-on: ubuntu-latest steps: - name: Check out repository code uses: actions/checkout@v3 - name: Setup Java 17 uses: actions/setup-java@v3 with: distribution: temurin java-version: 17 - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Build run: ./gradlew build test: runs-on: ubuntu-latest steps: - name: Check out repository code uses: actions/checkout@v3 - name: Setup Java 17 uses: actions/setup-java@v3 with: distribution: temurin java-version: 17 - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Test run: ./gradlew test
2.3. Push remote and create a new PR from
feature/ci-step
todevelop
2.4. Check the pipeline workflow completed successfully
Continue to work on feature/ci-step
branch
-
Kover setup (more at https://github.com/Kotlin/kotlinx-kover)
1.1. Add
build.gradle.kts
:plugins { ... id("org.jetbrains.kotlinx.kover") version("0.5.1") apply(true) } tasks.test { ... extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) { isDisabled = false binaryReportFile.set(file("$buildDir/custom/result.bin")) includes = listOf("*") excludes = listOf() } } kover { isDisabled = false coverageEngine.set(kotlinx.kover.api.CoverageEngine.JACOCO) jacocoEngineVersion.set("0.8.7") } tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "11" } val koverOutputDir = "${rootProject.buildDir}/kover-reports" tasks.koverHtmlReport { isEnabled = true htmlReportDir.set(layout.buildDirectory.dir("${koverOutputDir}/html-result")) includes = listOf("*") excludes = listOf() } tasks.koverXmlReport { isEnabled = true xmlReportFile.set(layout.buildDirectory.file("${koverOutputDir}/result.xml")) includes = listOf("*") excludes = listOf() }
1.2. Run following commands in the terminal:
./gradlew test
./gradlew koverHtmlReport
./gradlew koverXmlReport
-
Check the generated HTML and XML reports in
build/kover-reports
directory
Continue to work on feature/ci-step
branch
-
Sonar project setup
1.1. Open https://sonarcloud.io
1.2. Register your personal GitHub account and repo newly created
1.3. Copy
SONAR_TOKEN
secret (Sonar project web page
->Administration
->Analysis method
->GitHub Actions
->Follow the tutorial
)1.3.1 setup with the same name in GitHub repo secrets (
Repo web page -> Settings -> Secrets -> Action
)1.4. Disable Automatic Analysis (
Sonar project web page
->Administration
->Analysis method
->SonarCloud Automatic Analysis
-> toggle off)1.5. Extract
Project Key
andOrganization Key
(Sonar project web page
->Information
) -
Sonar setup
2.1. Add
build.gradle.kts
:plugins { ... id("org.sonarqube") version("3.3") apply(true) } sonarqube { properties { property("sonar.host.url", "https://sonarcloud.io") property("sonar.organization", "{YOUR_ORGANIZATION_KEY}") property("sonar.projectKey", "{YOUR_PROJECT_KEY}") property( "sonar.coverage.jacoco.xmlReportPaths", "${koverOutputDir}/result.xml" ) } }
2.2. Add
build-and-test.yml
:test: runs-on: ubuntu-latest steps: ... - name: Generate coverage reports run: ./gradlew koverXmlReport - name: Analyze env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: ./gradlew sonarqube
-
Push remote and check Sonar analyze completed successfully
3.1. You will see
Bug A 0 Bugs Vulnerability A 0 Vulnerabilities Security Hotspot A 0 Security Hotspots Code Smell A 0 Code Smells No Coverage Information No Duplication Information
-
Add new code and tests
4.1. Add to file
TimeManager.kt
:interface TimeManager { ... fun endOfWorkDay(date: Date): Date } class TimeManagerImpl : TimeManager { ... override fun endOfWorkDay(date: Date): Date { val dateFormat = SimpleDateFormat("dd.MM.yyyy") val strDate: String = dateFormat.format(date) val endDateTime = "${strDate}/18:00" return getDate(endDateTime, format = "dd.MM.yyyy/HH:mm") } }
4.2. Add to file
BookingService.kt
:interface BookingService { ... fun suggestNextPeriod(period: Period): Period } class BookingServiceImpl() : BookingService { ... override fun suggestNextPeriod(period: Period): Period { var newStartTime = period.startTime.time val endLimit = timeManager.endOfWorkDay(period.startTime).time - period.unit.toMillis(period.duration) while (newStartTime < endLimit) { newStartTime += TimeUnit.MINUTES.toMillis(5) // skip each 5 minutes val newPeriod = Period( startTime = Date(newStartTime), duration = period.duration, unit = period.unit ) if (isAvailable(newPeriod)) { return newPeriod } } throw NotAvailableTimeSlotException } }
4.3. Add to file
BookingServiceImplTest.kt
:@Test fun testSuggestNextPeriod() { val title = "Test meeting 1" val invitees = listOf<Attendee>(Attendee.Required(fullName = "John Doe", email = "john@company.com")) val period = Period( startTime = timeManager.getDate("10.01.2022/09:00", format = "dd.MM.yyyy/HH:mm"), duration = 45, unit = TimeUnit.MINUTES ) val meeting = service.book( title = title, invitees = invitees, period = period ) assertNotNull(meeting.id) assertEquals(meeting.title, title) assertEquals(meeting.invitees, invitees) assertEquals(meeting.period, period) val title2 = "Test meeting 2" val invitees2 = listOf<Attendee>(Attendee.Required(fullName = "Sarah Doe", email = "sarah@company.com")) var period2 = Period( startTime = timeManager.getDate("10.01.2022/09:30", format = "dd.MM.yyyy/HH:mm"), duration = 25, unit = TimeUnit.MINUTES ) // conflicting time slot period2 = service.suggestNextPeriod(period2) val meeting2 = service.book( title = title2, invitees = invitees2, period = period2 ) assertNotNull(meeting2.id) assertEquals(meeting2.title, title2) assertEquals(meeting2.invitees, invitees2) assertEquals(meeting2.period, period2) }
-
Push latest changes and check code coverage and duplication metrics
5.1. You should get:
89.5% Coverage 0.0% Duplication
-
Add new test to increase coverage to 100%
Line Coverage - The percent of lines executed by this test run.
Branch coverage - The percent of branches executed by this test run.
-
Add protection rules (
Repo web page -> Settings -> Branches -> Add Rule
)1.1. Config:
Branch name: develop
β Require a pull request before merging
β Require approvals: 1
β Dismiss stale pull request approvals when new commits are pushed
β Require status checks to pass before merging
β Require conversation resolution before merging
-
Check the last open PR and you will observe the review requirement in the Status Check section at bottom
-
Merge the PR
feature/ci-setup
with administrator access
-
Checkout
develop
branch, pull itgit checkout develop git pull
-
Checkout a new branch
feature/cd-setup
fromdevelop
-
Create a new workflow file
deploy-internal.yml
:
name: Deploy internal artifact
on:
push:
branches:
- develop
jobs:
deploy-internal:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Setup Java 17
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: Assemble output jar
run: ./gradlew assemble
- name: Upload output jar
uses: actions/upload-artifact@v3
with:
name: cicdworkshop-output
path: build/libs/
-
Push remote and create a PR from
feature/cd-setup
todevelop
-
Merge the PR with administrator access and check the workflow star to run in
Actions
tab -
After completed workflow task, artifact should be visible in
Overview
of workflow
- initial CI/CD pipeline using GitHub actions
- code coverage
- code quality analizer