diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..e002d7ebc --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +**/node_modules +**/bin +**/obj \ No newline at end of file diff --git a/.github/workflows/copilot-build-images.yml b/.github/workflows/copilot-build-images.yml new file mode 100644 index 000000000..dc9d84d3d --- /dev/null +++ b/.github/workflows/copilot-build-images.yml @@ -0,0 +1,79 @@ +name: copilot-build-images + +on: + workflow_call: + inputs: + REACT_APP_BACKEND_URI: + required: true + type: string + REACT_APP_AAD_AUTHORITY: + required: true + type: string + REACT_APP_AAD_CLIENT_ID: + required: true + type: string + REACT_APP_AAD_API_SCOPE: + required: false + type: string +env: + REGISTRY: ghcr.io + +jobs: + build-and-push-image: + name: Build and push images + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - file: ./docker/webapi/Dockerfile + image: ${{ github.repository }}-webapi + build-args: | + + - file: ./docker/webapp/Dockerfile + image: ${{ github.repository }}-webapp + build-args: | + + - file: ./docker/webapp/Dockerfile.nginx + image: ${{ github.repository }}-webapp-nginx + build-args: | + REACT_APP_BACKEND_URI=${{ inputs.REACT_APP_BACKEND_URI }} + REACT_APP_AAD_AUTHORITY=${{ inputs.REACT_APP_AAD_AUTHORITY }} + REACT_APP_AAD_CLIENT_ID=${{ inputs.REACT_APP_AAD_CLIENT_ID }} + REACT_APP_AAD_API_SCOPE=${{ inputs.REACT_APP_AAD_API_SCOPE }} + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Login container registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY }}/${{ matrix.image }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}} + type=semver,pattern={{major}}.{{minor}} + + - name: Build and push image + uses: docker/build-push-action@v3 + with: + context: . + file: ${{ matrix.file }} + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: ${{ matrix.build-args }} diff --git a/.gitignore b/.gitignore index 99475c678..2c6cd77ee 100644 --- a/.gitignore +++ b/.gitignore @@ -489,4 +489,4 @@ webapi/CopilotChatWebApi.sln /deploy/ # Tesseract OCR language data files -*.traineddata +*.traineddata \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 000000000..162e6d3e1 --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,33 @@ +version: '3' +services: + chat-copilot-webapp: + image: chat-copilot-webapp-nginx + build: + context: .. + dockerfile: docker/webapp/Dockerfile + ports: + - 3000:3000 + env_file: + - ../webapp/.env + depends_on: + chat-copilot-webapi: + condition: service_started + chat-copilot-webapi: + image: chat-copilot-webapi + build: + context: .. + dockerfile: docker/webapi/Dockerfile + ports: + - 8080:8080 + env_file: + - ../webapi/.env + environment: + - MemoryStore__Qdrant__Host=http://qdrant + depends_on: + qdrant: + condition: service_started + qdrant: + image: qdrant/qdrant + ports: + - 6333:6333 + diff --git a/docker/webapi/Dockerfile b/docker/webapi/Dockerfile new file mode 100644 index 000000000..2ed5dd74a --- /dev/null +++ b/docker/webapi/Dockerfile @@ -0,0 +1,29 @@ +# docker build -f docker/webapi/Dockerfile -t chat-copilot-webapi . + +# Learn about building .NET container images: +# https://github.com/dotnet/dotnet-docker/blob/main/samples/README.md +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /source + +# generate dev-certs for https +RUN dotnet dev-certs https + +# copy csproj and restore as distinct layers +COPY webapi/*.csproj . +RUN dotnet restore --use-current-runtime + +# copy everything else and build app +COPY webapi/. . +RUN apt update && apt install -y wget +RUN wget -P data https://raw.githubusercontent.com/tesseract-ocr/tessdata/main/eng.traineddata +RUN dotnet publish --use-current-runtime --self-contained false --no-restore -o /app + + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:6.0 +ENV Kestrel__Endpoints__Http__Url=http://0.0.0.0:8080 +WORKDIR /app +COPY --from=build /app . +COPY --from=build /root/.dotnet/corefx/cryptography/x509stores/my/* /root/.dotnet/corefx/cryptography/x509stores/my/ + +ENTRYPOINT ["./CopilotChatWebApi"] \ No newline at end of file diff --git a/docker/webapp/Dockerfile b/docker/webapp/Dockerfile new file mode 100644 index 000000000..3fc4671e0 --- /dev/null +++ b/docker/webapp/Dockerfile @@ -0,0 +1,19 @@ +# docker build -f docker/webapp/Dockerfile -t chat-copilot-webapp . + +# builder +FROM node:lts-alpine as builder +WORKDIR /app +COPY webapp/ . +RUN yarn install \ + --prefer-offline \ + --frozen-lockfile \ + --non-interactive \ + --production=false + +# final stage/image +FROM node:lts-alpine +WORKDIR /app +COPY --from=builder /app . +ENV HOST 0.0.0.0 +EXPOSE 3000 +ENTRYPOINT [ "yarn", "start" ] \ No newline at end of file diff --git a/docker/webapp/Dockerfile.nginx b/docker/webapp/Dockerfile.nginx new file mode 100644 index 000000000..ccfec52eb --- /dev/null +++ b/docker/webapp/Dockerfile.nginx @@ -0,0 +1,34 @@ +# source webapp/.env +# docker build --build-arg REACT_APP_BACKEND_URI=$REACT_APP_BACKEND_URI --build-arg REACT_APP_AAD_AUTHORITY=$REACT_APP_AAD_AUTHORITY --build-arg REACT_APP_AAD_CLIENT_ID=$REACT_APP_AAD_CLIENT_ID --build-arg REACT_APP_AAD_API_SCOPE=$REACT_APP_AAD_API_SCOPE -f docker/webapp/Dockerfile.nginx -t chat-copilot-webapp-nginx . + +# builder +FROM node:lts-alpine as builder + +ARG REACT_APP_BACKEND_URI +ENV REACT_APP_BACKEND_URI $REACT_APP_BACKEND_URI + +ARG REACT_APP_AAD_AUTHORITY +ENV REACT_APP_AAD_AUTHORITY $REACT_APP_AAD_AUTHORITY + +ARG REACT_APP_AAD_CLIENT_ID +ENV REACT_APP_AAD_CLIENT_ID $REACT_APP_AAD_CLIENT_ID + +ARG REACT_APP_AAD_API_SCOPE +ENV REACT_APP_AAD_API_SCOPE $REACT_APP_AAD_API_SCOPE + +WORKDIR /app +COPY webapp/ . +RUN yarn install \ + --prefer-offline \ + --frozen-lockfile \ + --non-interactive \ + --production=true + +RUN yarn build + +# final stage/image +FROM nginx:stable-alpine +EXPOSE 3000 +RUN sed -i 's/80/3000/g' /etc/nginx/conf.d/default.conf +COPY --from=builder /app/build /usr/share/nginx/html +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file