μ΅κ·Ό μμ±νλ νλ‘μ νΈλ€μ΄ λΉμ·ν νμμΌλ‘ ꡬμ±λμ΄ CI/CD λ₯Ό GitHub Actions λ₯Ό νμ©νκ³ μμ΅λλ€.
κ°λ¨ν λ¨κ³λ κΈ°λ³Έμ μΌλ‘ μ 곡λλ GitHub Actions ν¨ν€μ§λ₯Ό νμ©νκ³ , νμλ‘ νλ νΉμ  κΈ°λ₯μ κ²μν΄λ³΄λ λλΆλΆ 곡κ°λ ν¨ν€μ§κ° μ‘΄μ¬ν©λλ€.
λͺλͺ μνμ°©μ€λ₯Ό κ±°μ³ .NET 5 λ°±μλ μμ©νλ‘κ·Έλ¨κ³Ό React νλ‘ νΈμλ μμ©νλ‘κ·Έλ¨μ λΉλνκ³ , νκ·Έν ν GitHub Release Draft λ₯Ό μμ±νκ³ , νκ·Έλ 컀λ°μ κΈ°μ€μΌλ‘ λ컀 μ΄λ―Έμ§λ₯Ό λΉλν΄μ λ μ§μ€νΈλ¦¬μ κ²μνκ² κ΅¬μ±λ GitHub Actions μ λλ€.
λμ νλ‘μ νΈ:
- λ°±μλ: .NET 5
- νλ‘ νΈμλ: React
- κ²μ: Docker registry
ꡬμ±λ μ μ₯μ:
π’ GitHub Actions
μλμ κ°μ΄ λκ°μ μν¬νλ‘μ°λ‘ λλ μ μμ±λμμΌλ©°, λ μν¬νλ‘μ°λ μλ‘ μμ‘΄νμ§ μμ΅λλ€.
Workflows:
- Build and tag
- Docker image
Build and Tag
name: Build and tag
on:
  push:
    branches:
      - main # Default release branch
    tags:
      - "!*"
jobs:
  build:
    name: build
    runs-on: ubuntu-latest
    steps:
      -
        name: Checkout
        uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - name: Setup dotnet
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 5.0.x
      - name: setup Node
        uses: actions/setup-node@v2
        with:
          node-version: '14'
      - name: build ClientApp
        run: |
          cd src/Sample.App/ClientApp
          npm install
          npm run build
      - name: Restore dependencies
        run: dotnet restore
      - name: Build
        run: dotnet build --no-restore
      - name: package-version
        run: grep '<Version>' < src/Sample.App/Sample.App.csproj | echo "::set-output name=version::$(sed 's/.*<Version>\(.*\)<\/Version>/\1/')"
        id: get_package_version
      - name: set-version  
        run: echo "PACKAGE_VERSION=${{ steps.get_package_version.outputs.version }}" >> $GITHUB_ENV
      - name: package-version-to-git-tag
        uses: pkgdeps/git-tag-action@v2
        with:
          github_token: ${{ secrets.GH_TOKEN }}
          github_repo: ${{ github.repository }}
          version: ${{ env.PACKAGE_VERSION }}
          git_commit_sha: ${{ github.sha }}
          git_tag_prefix: "v"
      - name: Release Drafter
        id: release_drafter
        uses: release-drafter/release-drafter@v5
        with:
          config-name: release-drafter.yml
          version: ${{ env.PACKAGE_VERSION }}
        env:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
λΉλλ₯Ό νμΈνκ³ , Semver νμμ νκ·Έλ₯Ό μΆκ°ν©λλ€.
νκ·Έλ λ°±μλμ μμνλ‘μ νΈλ‘ μ€μ λλ νλ‘μ νΈμ λ²μ  Version μμλ₯Ό μ°Έμ‘°ν©λλ€.
λ°±μλμ μμ νλ‘μ νΈλ‘ μ€μ λλ νλ‘μ νΈ νμΌ(.csproj)μ λ°λμ <Version> μμκ° μ‘΄μ¬ν΄μΌ ν©λλ€.
μ)
<Version>1.0.0</Version>λΉλ, νκ·Έ μ‘μ μ νλμ μμ μΌλ‘ ꡬμ±λμ΄ μκ³ , μμ μ μ¬λ¬ λ¨κ³λ‘ ꡬμ±λ©λλ€.
μ‘μ μ λ©μΈ λΈλμΉμ 컀λ°μ΄ μ μ‘λμμ λ μμλ©λλ€. λ©μΈ λΈλμΉμ νκ·Έκ° μ μ‘λ κ²½μ°μλ μμλμ§ μμ΅λλ€.
μμ μ μλ λ¨κ³κ° μμλλ‘ μ€νλ©λλ€.
- 체ν¬μμ λ©μΈ λΈλμΉμ λ΄μ©μ 볡μ ν©λλ€.
- .NET 5 λꡬλ₯Ό μ€μΉν©λλ€.
- Nodejs λꡬλ₯Ό μ€μΉν©λλ€.
- ν΄λΌμ΄μΈνΈ μμ©νλ‘κ·Έλ¨ λλ ν°λ¦¬μμ npm ν¨ν€μ§λ₯Ό μ€μΉνκ³ , npm run build μ€ν¬λ¦½νΈλ₯Ό μ€νν΄μ ν΄λΌμ΄μΈνΈ μμ©νλ‘κ·Έλ¨μ λ²λ€λ§ν©λλ€.
- dotnet μμ‘΄ ν¨ν€μ§λ₯Ό μ€μΉν©λλ€.
- dotnet νλ‘μ νΈλ₯Ό λΉλν©λλ€.
- λ°±μλ μμ νλ‘μ νΈμ λ²μ μ νκ²½λ³μμ μ μ₯ν©λλ€.
- νκ²½λ³μμ μ μ₯λ λ²μ κ°μΌλ‘ νκ·Έλ₯Ό μμ±ν©λλ€. git tag
- μμ±λ νκ·Έμ μ°κ²°λ λ¦΄λ¦¬μ¦ μ΄μμ μμ±ν©λλ€.
Docker image
name: docker image
on:
  push:
    tags:
      - 'v*.*.*'
jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      -
        name: Checkout
        uses: actions/checkout@v2
        with:
          fetch-depth: 1
      -
        name: Docker meta
        id: meta
        uses: docker/metadata-action@v3
        with:
          # list of Docker images to use as base name for tags
          images: |
            ${{ secrets.DOCKER_REGISTRY_URI }}/${{ github.repository }}
          # generate Docker tags based on the following events/attributes
          tags: |
            type=schedule
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}
            type=sha
          labels: |
            org.opencontainers.image.title=Sample File Share
            org.opencontainers.image.description=File sharing sample web app
            org.opencontainers.image.vendor=bbonkr
      -
        name: Set up QEMU
        uses: docker/setup-qemu-action@v1
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1
      -
        name: Login to Registry
        uses: docker/login-action@v1 
        with:
          registry: ${{ secrets.DOCKER_REGISTRY_URI }}
          username: ${{ secrets.DOCKER_REGISTRY_USERNAME }}
          password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
      -
        name: Build and push
        uses: docker/build-push-action@v2
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
λ©μΈ λΈλμΉμ νκ·Έκ° μ μ‘λλ©΄ λ컀 μ΄λ―Έμ§λ₯Ό λΉλν ν λ μ§μ€νΈλ¦¬μ μ μ΄λ―Έμ§λ₯Ό μ μ‘ν©λλ€.
λ컀 μ΄λ―Έμ§μ λ²μ μ μ‘μ  νΈλ¦¬κ±°μΈ νκ·Έκ° μ¬μ©λ©λλ€.
λΉλ, νκ·Έ μ‘μ μ νλμ μμ μΌλ‘ ꡬμ±λμ΄ μκ³ , μμ μ μ¬λ¬ λ¨κ³λ‘ ꡬμ±λ©λλ€.
μ‘μ
μ μ μ₯μμ v*.*.* νμμ νκ·Έκ° μ μ‘λμμ λ μμλ©λλ€.
μμ μ μλ λ¨κ³κ° μμλλ‘ μ€νλ©λλ€.
- 체ν¬μμ λ©μΈ λΈλμΉμ λ΄μ©μ 볡μ ν©λλ€.
- λ컀 λ©νλ°μ΄ν°λ₯Ό μμ§ν©λλ€.
- λ컀 μ΄λ―Έμ§λ₯Ό λΉλνκΈ° μν΄ QEMU λꡬλ₯Ό μ€μΉν©λλ€.
- λ컀 μ΄λ―Έμ§λ₯Ό λΉλνκΈ° μν΄ Buildx λꡬλ₯Ό μ€μΉν©λλ€.
- λ μ§μ€νΈλ¦¬μ λ‘κ·ΈμΈν©λλ€.
- λ컀 μ΄λ―Έμ§λ₯Ό λΉλνκ³ , μΈμ¦λ λ μ§μ€νΈλ¦¬μ μ΄λ―Έμ§λ₯Ό κ²μν©λλ€.
π Conclusion
μ΄λ κ² κ΅¬μ±ν΄λλ©΄, λ²μ λ³ μΆμμ 보μ λ컀 μ΄λ―Έμ§λ₯Ό λ μ§μ€νΈλ¦¬μμ νμΈν μ μμ΅λλ€.