开发非平凡软件产品的主要要求是建立正式的工作流程,以进行连续集成和连续交付。无论是个人还是小组项目,CI/CD工作流程为从开发人员的框中获取代码到实时环境的基础。
,但它超越了这一点。 CI/CD允许在保持质量和可靠性的同时,经常部署代码。通常,这是在开发管道中特定点的自动单元和集成测试的帮助下完成的。测试将有助于确保新代码不会破坏现有代码。
连续集成是如此重要,以至于未能正式实施它会导致偶然的开发和代码质量的损失,这只会给工程团队带来更大的压力。您可以在此中阅读有关CI/CD的更多信息。
依赖管理
由于现代软件开发的性质,整个系统是通过依靠不同组件的功能来构建的。由于允许这些组件独立发展,因此需要有一种方法来确保它们保持兼容。我们不希望一个组件上的更新会导致错误在另一个组件中表现出来。因此,为了撰写软件,我们需要依靠依赖性管理。
依赖性管理使我们能够打包项目中的依赖项,并在需要的情况下优雅地升级这些依赖项。在Java生态系统中,有两个主要的依赖管理工具:Maven和Gradle。 Maven使用XML配置来定义项目构建,包括所有插件和依赖项。另一方面,Gradle使用基于凹槽 / Kotlin的域特定语言(DSL)来定义构建任务和依赖项。< / p>
让我们谈论版本化。
版本控制
版本控制是将唯一数字分配给软件工件的特定版本的过程。因此,在每个时间点,软件版本编号都会了解工件如何发展。
版本控制是依赖性管理的重要组成部分,因为它使我们能够跟踪依赖关系中的最新发展。
可能会出现一些问题:我们如何升级项目的依赖性版本?我们采用哪种格式进行版本控制?我们如何确保跟踪对依赖项的升级并且不会破坏现有功能?
当前,语义版本控制非常广泛使用。这里的外观:
给定一个版本编号:例如1.5.2,有3个部分:主要(1),次要(5),补丁(2)。
Major :进行不兼容的API更改时会更新;也就是说,可以使用该依赖关系破坏现有系统的更改。
次要:以向后兼容的方式添加功能时进行更新。
补丁:进行向后兼容的错误修复时更新。
现在我们可以深入研究git中的分支模型。
分支模型
分支模型是在共享代码库上工作时的一组规则开发人员。它们有助于构建分支和合并代码的方法,这有助于团队更有效地工作。我严格遵守一个分支模型,该模型使我可以根据GIT历史记录计算项目版本。软件开发中有一些分支模型:
- gitflow
- github流
- 基于躯干的开发
我将在gitflow上散发出更多的启示,因为它是最受欢迎的。 Gitflow使用不同的分支类型:
- Master或Main:这是稳定的分支,其中包含发布到生产中的最后版本,应始终准备就绪。
- 功能:功能分支是用于实现功能的。通常,开发人员将开发并编写与该功能分支上有关该功能的所有代码,然后再合并其开发
- 开发:开发人员合并以在准备将功能集成到主人的功能中时,开发分支是整合计划发布版本的不同功能的分支
- 版本:开发和用于准备生产版本的分支。测试发布分支时,通常将其合并为开发和主机。
- hotfix :用于修复主分支上出现的错误。 Hotfix分支机构off Master,并合并回主,并在完成和测试时开发。
Gitflow模型最适合大型团队,尽管可能更繁琐。
git流量分支模型参考:https://nvie.com/posts/a-successful-git-branching-model/
一个过程在某种程度上是gitflow和稍微流行的Trunk-based development的组合。
- 创建两个永久分支:主要和开发。主要准备生产代码,开发功能集成。
- 在创作功能时,开发分支并创建一个带有功能/'的分支。例如,要在用户登录中工作,请创建一个名为“功能/用户login”的分支开发。
- 当功能准备就绪后,将最新功能代码推向远程存储库,并创建一个拉动请求以开发。
- 运行代码审核,源代码分析,自动化单元和功能分支的集成测试,以确保合并以合并。
- 合并代码以开发和运行自动化单元和集成测试,以确保功能合并没有破坏其他功能。
- 将代码合并到主分支。运行标签,版本控制并准备伪影的释放。
- 发布。
- 合并回发展。
这个过程流量和更流行的gitflow之间的主要区别是,我不创建释放分支。取而代之的是,在测试开发后,我合并到Main并在Main上运行发布管理。开发分支存在用于测试和集成目的。这将确保仅将经过全面测试的代码合并到MAIN。如果您希望高级开发人员在到达MAIN之前,可以自动(由CI触发)合并为MAIN的过程。
。从某种意义上说,此过程类似于基于树干的开发,其中主要和发展都是后备箱。在运行发布管理后,合并Main Back以开发。 github避免了周期性的构建,因此合并以开发不会触发ci。
让我们在GitHub动作上实施CI管道
首先,我们安装gitversion:
$ brew install gitversion
我使用gitversion来处理我的版本。
gitversion使用git提交历史记录来计算软件项目的版本。对某些模式进行了提交消息:
- 添加新功能时,提交消息应包括: +SEMVER:功能
- 添加错误修复时,提交消息应包括: +semver:patch
- 当提交引入打破变化时,该消息应包括: +SEMVER:MAXER
设置项目以进行语义版本支持。我使用bash脚本:
#! /bin/bash
git flow init
gitversion init
git commit -m "Setup Versioning"
上面的脚本将在命令提示符中显示一个向导,要求填写有关项目设置的信息。巫师非常简单易懂。完成后,您应该有一个 gitversion.yml 文件,在项目root中具有以下结构:
mode: ContinuousDeployment
branches: {}
ignore:
sha: []
merge-message-formats: {}
在项目的根文件夹中,您应该在.github/Workflows路径中声明3个工作流文件。这样的东西:
功能/热修正分支集成。 (/.github/workflows/non-mainline-branch-update.yml)
name: Feature/Hotfix Build
on:
push:
branches:
- 'feature/*'
- 'hotfix/*'
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Set up JDK 17
- uses: actions/checkout@v3
uses: actions/setup-java@v3
with:
java-version: 17
distribution: zulu
- name: Cache SonarCloud packages
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2-
- name: Build and analyze
env:
run: mvn -B verify -s settings.xml -f pom.xml
上面的脚本是在与以下模式匹配的分支的推送事件上触发的:功能/*,hotfix/*。步骤名称描述在构建的每个步骤中所做的事情。
- 设置JDK环境,
- 恢复声纳的缓存。如果不适用于您,则可以跳过声纳缓存步骤。我使用声纳云来扫描我的工件,因此我需要包括声纳缓存步骤以加快构建。
- 修复Maven的缓存。这有助于避免重新下载依赖性,从而加快构建速度。缓存键与项目中的POM文件的哈希相关,因此POMS的更改将计算新的哈希案并触发依赖关系的重新下载。
- 最后一步将运行构建和分析
开发分支集成(/.github/workflows/develop-integration.yml)
name: Develop Branch Integration
on:
pull_request:
branches: [develop]
types: [closed]
jobs:
build:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
steps:
- uses: actions/checkout@v3
- name: Setup Java 17 env
uses: actions/setup-java@v1
with:
java-version: 17
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2-
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn -B verify -s settings.xml -f pom.xml
- name: Set Commit Message
id: commit
run: |
${{ startsWith(github.head_ref, 'feature/') }} && echo ::set-output name=message::"+semver: feature" \
|| echo ::set-output name=message::"+semver: patch"
- name: Commit Build Message
env:
COMMIT_MSG: ${{ steps.commit.outputs.message }}
run: |
git config user.email ${{ secrets.GIT_EMAIL }}
git config user.name ${{ secrets.GIT_USERNAME }}
git add .
git commit -m "$COMMIT_MSG" --allow-empty || true
- name: Push changes
uses: ad-m/github-push-action@master
with:
branch: develop
github_token: ${{ secrets.GITHUB_TOKEN }}
merge-main:
name: Merge to Main
needs: [build]
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Fetching
run: |
git fetch --all
- name: Merge to Main
uses: devmasx/merge-branch@v1.1.0
with:
type: now
target_branch: 'main'
env:
GITHUB_TOKEN: ${{ secrets.GIT_ACCESS_TOKEN }}
这些步骤是为了合并开发的条件。因此,摘要:if: github.event.pull_request.merged == true"
。该构建几乎像以前的构建一样开始,但是这次有一个设置提交消息',我在输出中设置了一个我打算在下一个构建步骤中使用的消息。此消息将在“提交构建消息步骤”中使用,并将在提交中构成消息的一部分。
在主构建中,将通过gitversion利用提交消息来计算软件的版本。如果我合并了功能/*分支,我会使用消息– semver:else featureâ+semver:patch。为了打破文物引入的变化,我会手动发出一条信息 - +semver:专业的当地人。测试成功进行后,我提交更改以开发分支并自动触发合并到Main。
如果不声明您的git存储库设置中的秘密,上面的脚本将无法正常工作。转到 秘密和变量下的动作 。
当您将代码推到带有功能/*前缀的分支时,hotfix/*触发了上述管道。您可以在“操作”选项卡下检查作业的状态。
主分支集成(/.github/workflows/main-integration.yml)
name: Main Branch CI
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the main branch
on:
push:
branches: [main]
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Fetching All
run: |
git fetch --prune --unshallow
# Install .NET Core as it is required by GitVersion action
- name: Setup .NET Core
uses: actions/setup-dotnet@v3
with:
dotnet-version: |
3.1.x
5.0.x
# Install Git Version
- name: Installing GitVersion
uses: gittools/actions/gitversion/setup@v0.9.3
with:
versionSpec: '5.3.x'
# Use Git Version to compute version of the project
- name: Use GitVersion
id: gitversion
uses: gittools/actions/gitversion/execute@v0.9.3
# Setup Java environment
- name: Setup Java 17 env
uses: actions/setup-java@v1
with:
java-version: 17
# Cache and restore Maven dependencies
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2-
# For a maven artifact, set version to what was computed by GitVersion in earlier step
- name: Evaluate New Artifact Version
run: |
NEW_VERSION=${{ steps.gitversion.outputs.semVer }}
echo "Artifact Semantic Version: $NEW_VERSION"
mvn versions:set -DnewVersion=${NEW_VERSION}-SNAPSHOT -s settings.xml
# Deploy artifact to repository. Could be ossrh, archiva etc.
- name: Build and Deploy with Maven
env:
ARTIFACT_REPO_USERNAME: ${{ secrets.ARTIFACT_REPO_USERNAME }}
ARTIFACT_REPO_PASSWORD: ${{ secrets.ARTIFACT_REPO_PASSWORD }}
run: |
export MAVEN_OPTS="--add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.desktop/java.awt.font=ALL-UNNAMED"
mvn clean deploy -s settings.xml -f pom.xml
# Optional step where I like to write the version number to a file in the project root.
- name: Upgrading Version
run: |
RELEASE_TAG=${{ steps.gitversion.outputs.semVer }}
echo $RELEASE_TAG > version.ver
git config user.email ${{ secrets.GIT_EMAIL }}
git config user.name ${{ secrets.GIT_USERNAME }}
git add .
git commit -m "Upgraded Version >> $RELEASE_TAG" || true
- name: Push changes
uses: ad-m/github-push-action@master
with:
branch: main
github_token: ${{ secrets.GITHUB_TOKEN }}
merge-develop:
name: Merge to Develop
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Fetching
run: |
git fetch --all
- name: Merge to Develop
uses: devmasx/merge-branch@v1.1.0
with:
type: now
target_branch: develop
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
按照主构建中的评论应直接。我使用gitversion计算工件的版本,然后在调用mvn版本后部署工件:setâ。新版本致力于Main,然后合并回去开发。部署步骤将根据工件的性质而有所不同。例如,这可能是在Heroku上部署的服务。在这种情况下,我将有这样的步骤:
- name: Push changes to Heroku
uses: akhileshns/heroku-deploy@v3.12.12
with:
heroku_api_key: ${{secrets.HEROKU_API_KEY}}
heroku_app_name: ${{secrets.HEROKU_APP_NAME}}
heroku_email: ${{secrets.HEROKU_EMAIL}}
我们真的可以做任何事情。部署到Heroku,AWS,GCP或推到Docker。根据我们的流程流动的任何作用。
这是一个序列图来回顾我们的CI的外观:
结论
在本文中,我们讨论了如何使用GitHub动作自动化Maven伪像的CI过程。通过使用GitHub操作,您可以轻松地配置和运行Maven命令,例如MVN Test'和MVN部署,每当将更改推到存储库时。这有助于尽早捕获错误,并确保代码始终处于可发布状态。
请注意,这是一个示例,可能有必要调整步骤和命令以匹配您的特定用例ð