Skip to content

Commit

Permalink
Add FwHeadless (renamed from CrdtMerge) deployment for k8s and GHA (#…
Browse files Browse the repository at this point in the history
…1171)

This adds a new k8s deployment for FwHeadless (renamed from CrdtMerge)
in its own pod, and adds GHA workflow steps to build and deploy it.
  • Loading branch information
rmunn authored Oct 31, 2024
1 parent a84c509 commit c7c515d
Show file tree
Hide file tree
Showing 32 changed files with 546 additions and 21 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/deploy-branch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,18 @@ jobs:
version: ${{ needs.set-version.outputs.version }}
label-latest: false

build-fw-headless:
name: Build FwHeadless
needs: [ set-version ]
uses: ./.github/workflows/lexbox-fw-headless.yaml
with:
version: ${{ needs.set-version.outputs.version }}
label-latest: false

deploy:
name: Deploy Develop
uses: ./.github/workflows/deploy.yaml
needs: [ build-api, build-ui, build-hgweb, set-version ]
needs: [ build-api, build-ui, build-hgweb, build-fw-headless, set-version ]
secrets: inherit
with:
version: ${{ needs.set-version.outputs.version }}
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
url: https://${{ inputs.deploy-domain }}
outputs:
api-version: ${{ steps.get-api-version.outputs.result }}
fw-headless-version: ${{ steps.get-fw-headless-version.outputs.result }}
ui-version: ${{ steps.get-ui-version.outputs.result }}
steps:
- name: Checkout lexbox repo
Expand Down Expand Up @@ -80,6 +81,11 @@ jobs:
id: get-api-version
with:
cmd: yq '.images.[] | select(.name == "ghcr.io/sillsdev/lexbox-api").newTag' "fleet/${{ inputs.k8s-environment }}/kustomization.yaml"
- name: Get FwHeadless version
uses: mikefarah/yq@0b34c9a00de1c575a34eea05af1d956a525c4fc1 # v4.34.2
id: get-fw-headless-version
with:
cmd: yq '.images.[] | select(.name == "ghcr.io/sillsdev/lexbox-fw-headless").newTag' "fleet/${{ inputs.k8s-environment }}/kustomization.yaml"
- name: Get UI version
uses: mikefarah/yq@0b34c9a00de1c575a34eea05af1d956a525c4fc1 # v4.34.2
id: get-ui-version
Expand Down
72 changes: 72 additions & 0 deletions .github/workflows/develop-fw-headless.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Develop FwHeadless CI/CD
on:
workflow_dispatch:
push:
paths:
- 'backend/FwHeadless/**'
- 'backend/FixFwData/**'
- 'backend/FwLite/FwDataMiniLcmBridge/**'
- 'backend/FwLite/LcmCrdt/**'
- 'backend/FwLite/MiniLcm/**'
- 'backend/FwLiteProjectSync/FwLiteProjectSync/**'
- 'backend/LexCore/**'
- 'backend/LexData/**'
- '.github/workflows/lexbox-fw-headless.yaml'
- '.github/workflows/deploy.yaml'
- 'deployment/base/fw-headless-deployment.yaml'
branches:
- develop
pull_request:
paths:
- 'backend/FwHeadless/**'
- 'backend/FixFwData/**'
- 'backend/FwLite/FwDataMiniLcmBridge/**'
- 'backend/FwLite/LcmCrdt/**'
- 'backend/FwLite/MiniLcm/**'
- 'backend/FwLiteProjectSync/FwLiteProjectSync/**'
- 'backend/LexCore/**'
- 'backend/LexData/**'
- '.github/workflows/lexbox-fw-headless.yaml'
- '.github/workflows/deploy.yaml'
- 'deployment/base/fw-headless-deployment.yaml'
branches:
- develop

jobs:
set-version:
name: Set Version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.setVersion.outputs.VERSION }}
steps:
- name: Set Version
id: setVersion
# set version to date in vYYYY-MM-DD-commitSha format
run: |
shortSha=$(echo ${{ github.sha }} | cut -c1-8)
echo "VERSION=v$(date --rfc-3339=date)-$shortSha" >> ${GITHUB_OUTPUT}
build-fw-headless:
name: Build FwHeadless
needs: set-version
uses: ./.github/workflows/lexbox-fw-headless.yaml
with:
version: ${{ needs.set-version.outputs.version }}
deploy-fw-headless:
name: Deploy FwHeadless
if: ${{github.ref == 'refs/heads/develop'}}
needs: [ build-fw-headless, set-version ]
uses: ./.github/workflows/deploy.yaml
secrets: inherit
with:
version: ${{ needs.set-version.outputs.version }}
image: 'ghcr.io/sillsdev/lexbox-fw-headless'
k8s-environment: develop
deploy-domain: lexbox.dev.languagetechnology.org

# TODO: Run FwHeadless tests once we have developed them, but we don't need to run the whole integration test suite if only FwHeadless changes are being pushed
# integration-test-gha:
# name: GHA integration tests
# needs: [build-fw-headless, set-version]
# uses: ./.github/workflows/integration-test-gha.yaml
# with:
# lexbox-fw-headless-tag: ${{ needs.set-version.outputs.version }}
11 changes: 10 additions & 1 deletion .github/workflows/integration-test-gha.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,16 @@ jobs:
uses: mikefarah/yq@0b34c9a00de1c575a34eea05af1d956a525c4fc1 # v4.34.2
with:
cmd: yq eval -i '(.images.[] | select(.name == "ghcr.io/sillsdev/lexbox-api").newTag) = "${{ inputs.lexbox-api-tag }}"' "./deployment/gha/kustomization.yaml"
# It's also possible that hgweb and/or ui image may have changed; if so, pull them and update kustomization.yaml for them as well
# It's also possible that hgweb, fw-headless, and/or ui image may have changed; if so, pull them and update kustomization.yaml for them as well
- name: Pull fw-headless if updated
id: fw-headless_image
continue-on-error: true
run: docker pull ghcr.io/sillsdev/lexbox-fw-headless:${{ inputs.lexbox-api-tag }}
- name: Update image fw-headless version
if: ${{ steps.fw-headless_image.outcome == 'success' }}
uses: mikefarah/yq@0b34c9a00de1c575a34eea05af1d956a525c4fc1 # v4.34.2
with:
cmd: yq eval -i '(.images.[] | select(.name == "ghcr.io/sillsdev/lexbox-fw-headless").newTag) = "${{ inputs.lexbox-api-tag }}"' "./deployment/gha/kustomization.yaml"
- name: Pull hgweb if updated
id: hgweb_image
continue-on-error: true
Expand Down
99 changes: 99 additions & 0 deletions .github/workflows/lexbox-fw-headless.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Build FwHeadless

# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#on
on:
workflow_call:
inputs:
version:
description: 'The version of the image to build'
required: true
type: string
label-latest:
description: 'The label to apply to the latest image'
type: boolean
default: false

env:
IMAGE_NAME: ghcr.io/sillsdev/lexbox-fw-headless


jobs:
publish-fw-headless:
timeout-minutes: 60
runs-on: ubuntu-latest

# postgres db is for automated tests
# services:
# postgres:
# image: postgres:15-alpine
# env:
# POSTGRES_PASSWORD: 972b722e63f549938d07bd8c4ee5086c
# POSTGRES_DB: lexbox-tests
# # Set health checks to wait until postgres has started
# options: >-
# --health-cmd pg_isready
# --health-interval 10s
# --health-timeout 5s
# --health-retries 5
# ports:
# # Maps tcp port 5432 on service container to the host
# - 5433:5432

env:
# https://docs.docker.com/develop/develop-images/build_enhancements/
DOCKER_BUILDKIT: 1

steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.x'
- name: Dotnet build
run: dotnet build backend/FwHeadless/FwHeadless.csproj
# TODO: Write FwHeadless unit tests, probably based on existing sync tests
# - name: Unit tests
# run: dotnet test backend/FwHeadless/FwHeadless.csproj --logger:"xunit;LogFileName={assembly}.results.xml" --results-directory ./test-results --filter "Category!=Integration&Category!=FlakyIntegration" --blame-hang-timeout 10m
# - name: Publish unit test results
# uses: EnricoMi/publish-unit-test-result-action@8885e273a4343cd7b48eaa72428dea0c3067ea98 # v2.14.0
# if: always()
# with:
# check_name: C# Unit Tests
# files: ./test-results/*.xml
# - name: Upload test results
# if: always()
# uses: actions/upload-artifact@v4
# with:
# name: dotnet-unit-test-results
# path: ./test-results

- name: Docker meta
id: meta
if: ${{ !env.ACT }}
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
with:
images: ${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=raw,enable=${{ inputs.label-latest }},value=latest
type=raw,value=${{ inputs.version }}
- name: ghcr.io login
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
if: ${{ !env.ACT }}
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0
with:
context: backend
file: backend/FwHeadless/Dockerfile
build-args: |
APP_VERSION=${{ inputs.version }}
push: ${{ !env.ACT && github.repository == 'sillsdev/languageforge-lexbox' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
10 changes: 9 additions & 1 deletion .github/workflows/release-pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,18 @@ jobs:
version: ${{ needs.set-version.outputs.version }}
label-latest: true

build-fw-headless:
name: Build fw-headless
needs: [ set-version ]
uses: ./.github/workflows/lexbox-fw-headless.yaml
with:
version: ${{ needs.set-version.outputs.version }}
label-latest: true

deploy:
name: Deploy Staging
uses: ./.github/workflows/deploy.yaml
needs: [ build-api, build-ui, build-hgweb, set-version ]
needs: [ build-api, build-ui, build-hgweb, build-fw-headless, set-version ]
secrets: inherit
with:
version: ${{ needs.set-version.outputs.version }}
Expand Down
2 changes: 1 addition & 1 deletion LexBox.sln
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LcmDebugger", "backend\LfNe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniLcm.Tests", "backend\FwLite\MiniLcm.Tests\MiniLcm.Tests.csproj", "{00AE5440-0E36-4488-935B-5B11301BA57D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrdtMerge", "backend\CrdtMerge\CrdtMerge.csproj", "{ECBA46AB-AF87-4D4D-9716-FD77264B817F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FwHeadless", "backend\FwHeadless\FwHeadless.csproj", "{ECBA46AB-AF87-4D4D-9716-FD77264B817F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
34 changes: 34 additions & 0 deletions backend/FwHeadless/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# syntax=docker/dockerfile:1
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build

COPY . .
RUN --mount=type=cache,target=/root/.nuget/packages dotnet restore "FwHeadless/FwHeadless.csproj"

ARG APP_VERSION
LABEL version=$APP_VERSION

RUN --mount=type=cache,target=/root/.nuget/packages dotnet build /p:InformationalVersion=$APP_VERSION "FwHeadless/FwHeadless.csproj" -c Release -o /app/build

FROM build AS publish
RUN --mount=type=cache,target=/root/.nuget/packages dotnet publish /p:InformationalVersion=$APP_VERSION "FwHeadless/FwHeadless.csproj" -c Release -o /app/publish

FROM base AS final
RUN mkdir -p /var/lib/fw-headless /var/www/.local/share && chown -R www-data:www-data /var/lib/fw-headless /var/www/.local/share
RUN apt-get update \
&& apt-get install --yes --no-install-recommends tini iputils-ping python3 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=publish /app/publish .
# Ensure Mercurial exec bit was not stripped by dotnet CLI tools
RUN chmod +x Mercurial/hg && chmod +x Mercurial/chg 2>/dev/null || true
# Fix up mercurial.ini path to fixutf8
RUN sed -i -e 's/fixutf8 = \/FwHeadless/fixutf8 = \/app/' Mercurial/mercurial.ini
USER www-data:www-data
ENV XDG_DATA_HOME=/var/www/.local/share
ENTRYPOINT ["tini", "--"]
CMD ["dotnet", "FwHeadless.dll"]
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.ComponentModel.DataAnnotations;

namespace CrdtMerge;
namespace FwHeadless;

public class CrdtMergeConfig
public class FwHeadlessConfig
{
[Required, Url, RegularExpression(@"^.+/$", ErrorMessage = "Must end with '/'")]
public required string LexboxUrl { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
using FwLiteProjectSync;
using LcmCrdt;

namespace CrdtMerge;
namespace FwHeadless;

public static class CrdtMergeKernel
public static class FwHeadlessKernel
{
public static void AddCrdtMerge(this IServiceCollection services)
public static void AddFwHeadless(this IServiceCollection services)
{
services
.AddLogging(builder => builder.AddConsole().AddDebug().AddFilter("Microsoft.EntityFrameworkCore", LogLevel.Warning));
services.AddOptions<CrdtMergeConfig>()
.BindConfiguration("SendReceiveConfig")
services.AddOptions<FwHeadlessConfig>()
.BindConfiguration("FwHeadlessConfig")
.ValidateDataAnnotations()
.ValidateOnStart();
services.AddScoped<SendReceiveService>();
Expand Down
10 changes: 7 additions & 3 deletions backend/CrdtMerge/Program.cs → backend/FwHeadless/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using CrdtMerge;
using FwHeadless;
using FwDataMiniLcmBridge;
using FwLiteProjectSync;
using LcmCrdt;
Expand All @@ -14,12 +14,14 @@
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();

builder.Services.AddHealthChecks();

builder.Services.AddLexData(
autoApplyMigrations: false,
useOpenIddict: false
);

builder.Services.AddCrdtMerge();
builder.Services.AddFwHeadless();

var app = builder.Build();

Expand All @@ -33,6 +35,8 @@

app.UseHttpsRedirection();

app.MapHealthChecks("/api/healthz");

app.MapPost("/sync", ExecuteMergeRequest);

app.Run();
Expand All @@ -41,7 +45,7 @@
ILogger<Program> logger,
IServiceProvider services,
SendReceiveService srService,
IOptions<CrdtMergeConfig> config,
IOptions<FwHeadlessConfig> config,
FwDataFactory fwDataFactory,
ProjectsService projectsService,
ProjectLookupService projectLookupService,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using LexData;
using Microsoft.EntityFrameworkCore;

namespace CrdtMerge;
namespace FwHeadless;

public class ProjectLookupService(LexBoxDbContext dbContext)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using FwDataMiniLcmBridge;
using SIL.Progress;

namespace CrdtMerge;
namespace FwHeadless;

public static class SendReceiveHelpers
{
Expand All @@ -12,7 +12,7 @@ public record ProjectPath(string Code, string Dir)

public record SendReceiveAuth(string Username, string Password)
{
public SendReceiveAuth(CrdtMergeConfig config) : this(config.LexboxUsername, config.LexboxPassword) { }
public SendReceiveAuth(FwHeadlessConfig config) : this(config.LexboxUsername, config.LexboxPassword) { }
};

public record LfMergeBridgeResult(string Output, string ProgressMessages);
Expand Down
Loading

0 comments on commit c7c515d

Please sign in to comment.