From a08e972da5c6eb04f87b563cf0683d5c971a2d5d Mon Sep 17 00:00:00 2001 From: Patrick Dwyer Date: Tue, 3 Mar 2026 17:18:51 +1000 Subject: [PATCH] Add github action workflows Signed-off-by: Patrick Dwyer --- .github/workflows/ci.yml | 31 ++++++ .github/workflows/release-cli.yml | 150 ++++++++++++++++++++++++++ .github/workflows/release-client.yml | 96 +++++++++++++++++ .github/workflows/release-web.yml | 101 +++++++++++++++++ src/CoderPatros.Tea.Cli/semver.txt | 1 + src/CoderPatros.Tea.Client/semver.txt | 1 + src/CoderPatros.Tea.Web/Dockerfile | 9 ++ src/CoderPatros.Tea.Web/semver.txt | 1 + 8 files changed, 390 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release-cli.yml create mode 100644 .github/workflows/release-client.yml create mode 100644 .github/workflows/release-web.yml create mode 100644 src/CoderPatros.Tea.Cli/semver.txt create mode 100644 src/CoderPatros.Tea.Client/semver.txt create mode 100644 src/CoderPatros.Tea.Web/Dockerfile create mode 100644 src/CoderPatros.Tea.Web/semver.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..38c25be --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: + pull_request: + branches: [main] + +jobs: + test: + name: Test (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore + + - name: Test + run: dotnet test --no-build --verbosity normal diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml new file mode 100644 index 0000000..b4328b6 --- /dev/null +++ b/.github/workflows/release-cli.yml @@ -0,0 +1,150 @@ +name: Release CLI + +on: + workflow_dispatch: + +jobs: + validate: + name: Validate version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.read_version.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Read version from semver.txt + id: read_version + run: | + version=$(tr -d '[:space:]' < src/CoderPatros.Tea.Cli/semver.txt) + echo "version=$version" >> "$GITHUB_OUTPUT" + + - name: Validate semver format + run: | + if ! echo "${{ steps.read_version.outputs.version }}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$'; then + echo "::error::Version '${{ steps.read_version.outputs.version }}' is not valid semver" + exit 1 + fi + + - name: Check tag does not exist + run: | + if git ls-remote --tags https://github.com/${{ github.repository }} | grep -q "refs/tags/cli/v${{ steps.read_version.outputs.version }}$"; then + echo "::error::Tag cli/v${{ steps.read_version.outputs.version }} already exists" + exit 1 + fi + + test: + name: Test (${{ matrix.os }}) + needs: validate + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore + + - name: Test + run: dotnet test --no-build --verbosity normal + + build: + name: Build (${{ matrix.rid }}) + needs: [validate, test] + runs-on: ${{ matrix.runner }} + env: + VERSION: ${{ needs.validate.outputs.version }} + strategy: + fail-fast: false + matrix: + include: + - rid: linux-x64 + runner: ubuntu-latest + archive: tar.gz + - rid: linux-arm64 + runner: ubuntu-latest + archive: tar.gz + - rid: osx-x64 + runner: macos-latest + archive: tar.gz + - rid: osx-arm64 + runner: macos-latest + archive: tar.gz + - rid: win-x64 + runner: windows-latest + archive: zip + - rid: win-arm64 + runner: windows-latest + archive: zip + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Publish + run: > + dotnet publish src/CoderPatros.Tea.Cli/CoderPatros.Tea.Cli.csproj + -c Release + -r ${{ matrix.rid }} + --self-contained true + -p:PublishSingleFile=true + -p:DebugType=none + -p:Version=${{ env.VERSION }} + -o ./publish + + - name: Archive (tar.gz) + if: matrix.archive == 'tar.gz' + run: tar -czf tea-cli-v${{ env.VERSION }}-${{ matrix.rid }}.tar.gz -C ./publish . + + - name: Archive (zip) + if: matrix.archive == 'zip' + shell: pwsh + run: Compress-Archive -Path ./publish/* -DestinationPath tea-cli-v${{ env.VERSION }}-${{ matrix.rid }}.zip + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: tea-cli-v${{ env.VERSION }}-${{ matrix.rid }} + path: tea-cli-v${{ env.VERSION }}-${{ matrix.rid }}.${{ matrix.archive }} + + release: + name: Release + needs: [validate, build] + runs-on: ubuntu-latest + permissions: + contents: write + env: + VERSION: ${{ needs.validate.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: ./artifacts + merge-multiple: true + + - name: Create and push tag + run: | + git tag "cli/v${{ env.VERSION }}" + git push origin "cli/v${{ env.VERSION }}" + + - name: Create GitHub release + uses: softprops/action-gh-release@v2 + with: + tag_name: cli/v${{ env.VERSION }} + name: CLI v${{ env.VERSION }} + generate_release_notes: true + files: ./artifacts/* diff --git a/.github/workflows/release-client.yml b/.github/workflows/release-client.yml new file mode 100644 index 0000000..88b883f --- /dev/null +++ b/.github/workflows/release-client.yml @@ -0,0 +1,96 @@ +name: Release Client Libraries + +on: + workflow_dispatch: + +jobs: + validate: + name: Validate version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.read_version.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Read version from semver.txt + id: read_version + run: | + version=$(tr -d '[:space:]' < src/CoderPatros.Tea.Client/semver.txt) + echo "version=$version" >> "$GITHUB_OUTPUT" + + - name: Validate semver format + run: | + if ! echo "${{ steps.read_version.outputs.version }}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$'; then + echo "::error::Version '${{ steps.read_version.outputs.version }}' is not valid semver" + exit 1 + fi + + - name: Check tag does not exist + run: | + if git ls-remote --tags https://github.com/${{ github.repository }} | grep -q "refs/tags/client/v${{ steps.read_version.outputs.version }}$"; then + echo "::error::Tag client/v${{ steps.read_version.outputs.version }} already exists" + exit 1 + fi + + test: + name: Test (${{ matrix.os }}) + needs: validate + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore + + - name: Test + run: dotnet test --no-build --verbosity normal + + release: + name: Release + needs: [validate, test] + runs-on: ubuntu-latest + permissions: + contents: write + env: + VERSION: ${{ needs.validate.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Pack Client + run: dotnet pack src/CoderPatros.Tea.Client/CoderPatros.Tea.Client.csproj -c Release -p:Version=${{ env.VERSION }} -o ./artifacts + + - name: Pack Client DI Extensions + run: dotnet pack src/CoderPatros.Tea.Client.Extensions.DependencyInjection/CoderPatros.Tea.Client.Extensions.DependencyInjection.csproj -c Release -p:Version=${{ env.VERSION }} -o ./artifacts + + - name: Push to NuGet + run: dotnet nuget push ./artifacts/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json + + - name: Create and push tag + run: | + git tag "client/v${{ env.VERSION }}" + git push origin "client/v${{ env.VERSION }}" + + - name: Create GitHub release + uses: softprops/action-gh-release@v2 + with: + tag_name: client/v${{ env.VERSION }} + name: Client Libraries v${{ env.VERSION }} + generate_release_notes: true + files: ./artifacts/*.nupkg diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml new file mode 100644 index 0000000..fca6c0b --- /dev/null +++ b/.github/workflows/release-web.yml @@ -0,0 +1,101 @@ +name: Release Web + +on: + workflow_dispatch: + +jobs: + validate: + name: Validate version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.read_version.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Read version from semver.txt + id: read_version + run: | + version=$(tr -d '[:space:]' < src/CoderPatros.Tea.Web/semver.txt) + echo "version=$version" >> "$GITHUB_OUTPUT" + + - name: Validate semver format + run: | + if ! echo "${{ steps.read_version.outputs.version }}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$'; then + echo "::error::Version '${{ steps.read_version.outputs.version }}' is not valid semver" + exit 1 + fi + + - name: Check tag does not exist + run: | + if git ls-remote --tags https://github.com/${{ github.repository }} | grep -q "refs/tags/web/v${{ steps.read_version.outputs.version }}$"; then + echo "::error::Tag web/v${{ steps.read_version.outputs.version }} already exists" + exit 1 + fi + + test: + name: Test (${{ matrix.os }}) + needs: validate + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore + + - name: Test + run: dotnet test --no-build --verbosity normal + + release: + name: Release + needs: [validate, test] + runs-on: ubuntu-latest + permissions: + contents: write + env: + VERSION: ${{ needs.validate.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: src/CoderPatros.Tea.Web/Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: | + coderpatros/tea-web:${{ env.VERSION }} + coderpatros/tea-web:latest + + - name: Create and push tag + run: | + git tag "web/v${{ env.VERSION }}" + git push origin "web/v${{ env.VERSION }}" + + - name: Create GitHub release + uses: softprops/action-gh-release@v2 + with: + tag_name: web/v${{ env.VERSION }} + name: Web v${{ env.VERSION }} + generate_release_notes: true diff --git a/src/CoderPatros.Tea.Cli/semver.txt b/src/CoderPatros.Tea.Cli/semver.txt new file mode 100644 index 0000000..8acdd82 --- /dev/null +++ b/src/CoderPatros.Tea.Cli/semver.txt @@ -0,0 +1 @@ +0.0.1 diff --git a/src/CoderPatros.Tea.Client/semver.txt b/src/CoderPatros.Tea.Client/semver.txt new file mode 100644 index 0000000..8acdd82 --- /dev/null +++ b/src/CoderPatros.Tea.Client/semver.txt @@ -0,0 +1 @@ +0.0.1 diff --git a/src/CoderPatros.Tea.Web/Dockerfile b/src/CoderPatros.Tea.Web/Dockerfile new file mode 100644 index 0000000..bede83c --- /dev/null +++ b/src/CoderPatros.Tea.Web/Dockerfile @@ -0,0 +1,9 @@ +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src +COPY . . +RUN dotnet publish src/CoderPatros.Tea.Web/CoderPatros.Tea.Web.csproj -c Release -o /app/publish + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 +WORKDIR /app +COPY --from=build /app/publish . +ENTRYPOINT ["dotnet", "CoderPatros.Tea.Web.dll"] diff --git a/src/CoderPatros.Tea.Web/semver.txt b/src/CoderPatros.Tea.Web/semver.txt new file mode 100644 index 0000000..8acdd82 --- /dev/null +++ b/src/CoderPatros.Tea.Web/semver.txt @@ -0,0 +1 @@ +0.0.1