From 3fa44bd1dd7303850a398464d7ba67943d0d47cf Mon Sep 17 00:00:00 2001 From: Paul Yu Date: Thu, 21 Sep 2023 10:52:14 -0700 Subject: [PATCH] feat: release workflow enhancements and version numbers in healthchecks --- .github/workflows/package-ai-service.yaml | 1 + .../workflows/package-makeline-service.yaml | 1 + .github/workflows/package-order-service.yaml | 1 + .../workflows/package-product-service.yaml | 1 + .github/workflows/package-store-admin.yaml | 1 + .github/workflows/package-store-front.yaml | 1 + .../workflows/package-virtual-customer.yaml | 1 + .github/workflows/package-virtual-worker.yaml | 1 + .../workflows/release-container-images.yaml | 75 +++++++++++++++++++ src/ai-service/Dockerfile | 6 ++ src/ai-service/README.md | 1 + src/ai-service/main.py | 6 +- src/makeline-service/Dockerfile | 10 ++- src/makeline-service/main.go | 3 +- src/order-service/Dockerfile | 6 ++ src/order-service/routes/root.js | 4 +- src/product-service/Dockerfile | 9 +++ src/product-service/src/main.rs | 3 +- src/store-admin/Dockerfile | 7 ++ .../src/components/HealthCheck.vue | 3 - src/store-admin/src/router.js | 2 - src/store-admin/vue.config.js | 6 ++ src/store-front/Dockerfile | 7 ++ .../src/components/HealthCheck.vue | 3 - src/store-front/src/router.js | 2 - src/store-front/vue.config.js | 6 ++ src/virtual-customer/Dockerfile | 6 ++ src/virtual-worker/Dockerfile | 6 ++ 28 files changed, 161 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/release-container-images.yaml delete mode 100644 src/store-admin/src/components/HealthCheck.vue delete mode 100644 src/store-front/src/components/HealthCheck.vue diff --git a/.github/workflows/package-ai-service.yaml b/.github/workflows/package-ai-service.yaml index 59f7c117..6be09c47 100644 --- a/.github/workflows/package-ai-service.yaml +++ b/.github/workflows/package-ai-service.yaml @@ -53,6 +53,7 @@ jobs: with: context: src/ai-service file: src/ai-service/Dockerfile + platforms: linux/amd64,linux/arm64 push: true tags: | ${{ steps.set-variables.outputs.REPOSITORY }}/${{ steps.set-variables.outputs.IMAGE }}:latest diff --git a/.github/workflows/package-makeline-service.yaml b/.github/workflows/package-makeline-service.yaml index a961583a..9bbf5f9b 100644 --- a/.github/workflows/package-makeline-service.yaml +++ b/.github/workflows/package-makeline-service.yaml @@ -53,6 +53,7 @@ jobs: with: context: src/makeline-service file: src/makeline-service/Dockerfile + platforms: linux/amd64,linux/arm64 push: true tags: | ${{ steps.set-variables.outputs.REPOSITORY }}/${{ steps.set-variables.outputs.IMAGE }}:latest diff --git a/.github/workflows/package-order-service.yaml b/.github/workflows/package-order-service.yaml index 323e2161..309330db 100644 --- a/.github/workflows/package-order-service.yaml +++ b/.github/workflows/package-order-service.yaml @@ -53,6 +53,7 @@ jobs: with: context: src/order-service file: src/order-service/Dockerfile + platforms: linux/amd64,linux/arm64 push: true tags: | ${{ steps.set-variables.outputs.REPOSITORY }}/${{ steps.set-variables.outputs.IMAGE }}:latest diff --git a/.github/workflows/package-product-service.yaml b/.github/workflows/package-product-service.yaml index d743639a..40271c95 100644 --- a/.github/workflows/package-product-service.yaml +++ b/.github/workflows/package-product-service.yaml @@ -53,6 +53,7 @@ jobs: with: context: src/product-service file: src/product-service/Dockerfile + platforms: linux/amd64,linux/arm64 push: true tags: | ${{ steps.set-variables.outputs.REPOSITORY }}/${{ steps.set-variables.outputs.IMAGE }}:latest diff --git a/.github/workflows/package-store-admin.yaml b/.github/workflows/package-store-admin.yaml index 104d4ac8..6f48caef 100644 --- a/.github/workflows/package-store-admin.yaml +++ b/.github/workflows/package-store-admin.yaml @@ -53,6 +53,7 @@ jobs: with: context: src/store-admin file: src/store-admin/Dockerfile + platforms: linux/amd64,linux/arm64 push: true tags: | ${{ steps.set-variables.outputs.REPOSITORY }}/${{ steps.set-variables.outputs.IMAGE }}:latest diff --git a/.github/workflows/package-store-front.yaml b/.github/workflows/package-store-front.yaml index ceb2f092..1e1b15ec 100644 --- a/.github/workflows/package-store-front.yaml +++ b/.github/workflows/package-store-front.yaml @@ -53,6 +53,7 @@ jobs: with: context: src/store-front file: src/store-front/Dockerfile + platforms: linux/amd64,linux/arm64 push: true tags: | ${{ steps.set-variables.outputs.REPOSITORY }}/${{ steps.set-variables.outputs.IMAGE }}:latest diff --git a/.github/workflows/package-virtual-customer.yaml b/.github/workflows/package-virtual-customer.yaml index 102593f3..30ce1088 100644 --- a/.github/workflows/package-virtual-customer.yaml +++ b/.github/workflows/package-virtual-customer.yaml @@ -53,6 +53,7 @@ jobs: with: context: src/virtual-customer file: src/virtual-customer/Dockerfile + platforms: linux/amd64,linux/arm64 push: true tags: | ${{ steps.set-variables.outputs.REPOSITORY }}/${{ steps.set-variables.outputs.IMAGE }}:latest diff --git a/.github/workflows/package-virtual-worker.yaml b/.github/workflows/package-virtual-worker.yaml index 16d53e4e..14f8299c 100644 --- a/.github/workflows/package-virtual-worker.yaml +++ b/.github/workflows/package-virtual-worker.yaml @@ -53,6 +53,7 @@ jobs: with: context: src/virtual-worker file: src/virtual-worker/Dockerfile + platforms: linux/amd64,linux/arm64 push: true tags: | ${{ steps.set-variables.outputs.REPOSITORY }}/${{ steps.set-variables.outputs.IMAGE }}:latest diff --git a/.github/workflows/release-container-images.yaml b/.github/workflows/release-container-images.yaml new file mode 100644 index 00000000..1cddaa66 --- /dev/null +++ b/.github/workflows/release-container-images.yaml @@ -0,0 +1,75 @@ +name: release-container-images + +on: + release: + types: [published] + +permissions: + contents: read + packages: write + +jobs: + publish-container-image: + + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + apps: [ + ai-service, + makeline-service, + order-service, + product-service, + store-admin, + store-front, + virtual-customer, + virtual-worker + ] + + steps: + - name: Set environment variables + id: set-variables + run: | + echo "REPOSITORY=ghcr.io/$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" + echo "IMAGE=${{ matrix.apps }}" >> "$GITHUB_OUTPUT" + echo "VERSION=$(echo ${GITHUB_REF#refs/tags/})" >> "$GITHUB_OUTPUT" + echo "CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> "$GITHUB_OUTPUT" + + - name: Env variable output + id: test-variables + run: | + echo ${{ steps.set-variables.outputs.REPOSITORY }} + echo ${{ steps.set-variables.outputs.IMAGE }} + echo ${{ steps.set-variables.outputs.VERSION }} + echo ${{ steps.set-variables.outputs.CREATED }} + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: src/${{ matrix.apps }} + file: src/${{ matrix.apps }}/Dockerfile + platforms: linux/amd64,linux/arm64 + build-args: | + APP_VERSION=${{ steps.set-variables.outputs.VERSION }} + push: true + tags: | + ${{ steps.set-variables.outputs.REPOSITORY }}/${{ steps.set-variables.outputs.IMAGE }}:latest + ${{ steps.set-variables.outputs.REPOSITORY }}/${{ steps.set-variables.outputs.IMAGE }}:${{ steps.set-variables.outputs.VERSION }} + labels: | + org.opencontainers.image.source=${{ github.repositoryUrl }} + org.opencontainers.image.created=${{ steps.set-variables.outputs.CREATED }} + org.opencontainers.image.revision=${{ steps.set-variables.outputs.VERSION }} \ No newline at end of file diff --git a/src/ai-service/Dockerfile b/src/ai-service/Dockerfile index ef80460c..7594c7c5 100644 --- a/src/ai-service/Dockerfile +++ b/src/ai-service/Dockerfile @@ -4,6 +4,9 @@ FROM python:3.11.1-slim-buster # Set the working directory to /app WORKDIR /app +# Set the build argument for the app version number +ARG APP_VERSION=0.1.0 + # Copy the requirements file into the container COPY requirements.txt . @@ -16,5 +19,8 @@ COPY . . # Expose port 5001 for the FastAPI application EXPOSE 5001 +# Set the environment variable for the app version number +ENV APP_VERSION=$APP_VERSION + # Start the FastAPI application CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "5001"] \ No newline at end of file diff --git a/src/ai-service/README.md b/src/ai-service/README.md index 8c1493d0..9765a24b 100644 --- a/src/ai-service/README.md +++ b/src/ai-service/README.md @@ -23,6 +23,7 @@ source .venv/bin/activate pip install -r requirements.txt export USE_AZURE_OPENAI=True # set to False if you are not using Azure OpenAI +export USE_AZURE_AD=False # set to True if you are using Azure OpenAI with Azure AD authentication export AZURE_OPENAI_DEPLOYMENT_NAME= # required if using Azure OpenAI export AZURE_OPENAI_ENDPOINT= # required if using Azure OpenAI export OPENAI_API_KEY= # always required diff --git a/src/ai-service/main.py b/src/ai-service/main.py index 6cda7846..a38edae3 100644 --- a/src/ai-service/main.py +++ b/src/ai-service/main.py @@ -2,9 +2,9 @@ from routers.description_generator import description from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse -import json +import os -app = FastAPI() +app = FastAPI(version=os.environ.get("APP_VERSION", "0.1.0")) app.include_router(description) app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) @@ -14,4 +14,4 @@ async def get_products(): """ Returns status code 200 """ - return JSONResponse(content={"status": 'ok'}, status_code=status.HTTP_200_OK) + return JSONResponse(content={"status": 'ok', "version": app.version}, status_code=status.HTTP_200_OK) diff --git a/src/makeline-service/Dockerfile b/src/makeline-service/Dockerfile index 7a47bac1..3398ef80 100644 --- a/src/makeline-service/Dockerfile +++ b/src/makeline-service/Dockerfile @@ -4,20 +4,28 @@ FROM golang:1.20.5-alpine as builder # Set the working directory to /app WORKDIR /app +# Set the build argument for the app version number +ARG APP_VERSION=0.1.0 + # Copy the current directory contents into the container at /app COPY . /app # Build the Go app -RUN go build -o main . +RUN go build -ldflags "-X main.version=$APP_VERSION" -o main . # Run the app on alpine FROM alpine:latest as runner +ARG APP_VERSION=0.1.0 + # Copy the build output from the builder container COPY --from=builder /app/main . # Expose port 3001 for the container EXPOSE 3001 +# Set the environment variable for the app version number +ENV APP_VERSION=$APP_VERSION + # Run the Go app when the container starts CMD ["./main"] \ No newline at end of file diff --git a/src/makeline-service/main.go b/src/makeline-service/main.go index 637db0e0..94ab30e9 100644 --- a/src/makeline-service/main.go +++ b/src/makeline-service/main.go @@ -288,7 +288,8 @@ func main() { router.PUT("/order", updateOrder) router.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ - "status": "UP", + "status": "ok", + "version": os.Getenv("APP_VERSION"), }) }) router.Run(":3001") diff --git a/src/order-service/Dockerfile b/src/order-service/Dockerfile index 107cbf02..b85a5c77 100644 --- a/src/order-service/Dockerfile +++ b/src/order-service/Dockerfile @@ -4,6 +4,9 @@ FROM node:18.16.0-alpine as builder # Set the working directory to /app WORKDIR /app +# Set the build argument for the app version number +ARG APP_VERSION=0.1.0 + # Copy package.json and package-lock.json to the container COPY package*.json ./ @@ -16,5 +19,8 @@ COPY . . # Expose the port the app listens on EXPOSE 3000 +# Set the environment variable for the app version number +ENV APP_VERSION=$APP_VERSION + # Start the app CMD [ "npm", "start" ] \ No newline at end of file diff --git a/src/order-service/routes/root.js b/src/order-service/routes/root.js index 2e06f070..69de9a28 100644 --- a/src/order-service/routes/root.js +++ b/src/order-service/routes/root.js @@ -1,6 +1,5 @@ 'use strict' - module.exports = async function (fastify, opts) { fastify.post('/', async function (request, reply) { const msg = request.body @@ -9,7 +8,8 @@ module.exports = async function (fastify, opts) { }) fastify.get('/health', async function (request, reply) { - return { status: 'ok' } + const appVersion = process.env.APP_VERSION || '0.1.0' + return { status: 'ok', version: appVersion } }) fastify.get('/hugs', async function (request, reply) { diff --git a/src/product-service/Dockerfile b/src/product-service/Dockerfile index 538a0b44..252180b1 100644 --- a/src/product-service/Dockerfile +++ b/src/product-service/Dockerfile @@ -5,6 +5,9 @@ RUN USER=root cargo new --bin product-service # Create a new directory for our application WORKDIR /product-service +# Set the build argument for the app version number +ARG APP_VERSION=0.1.0 + # Copy the Cargo.toml and Cargo.lock files to the container COPY Cargo.toml Cargo.lock ./ @@ -27,11 +30,17 @@ RUN cargo build --release FROM debian:bullseye-slim as runner WORKDIR /app +# Set the build argument for the app version number +ARG APP_VERSION=0.1.0 + # Not ideal but needed to execute health checks in docker-compose RUN apt-get update && apt-get install -y wget # Copy the binary from the builder stage COPY --from=builder /product-service/target/release/product-service /app +# Set the environment variable for the app version number +ENV APP_VERSION=$APP_VERSION + # Run the application CMD ["./product-service"] \ No newline at end of file diff --git a/src/product-service/src/main.rs b/src/product-service/src/main.rs index 978a663f..d00577db 100644 --- a/src/product-service/src/main.rs +++ b/src/product-service/src/main.rs @@ -10,7 +10,8 @@ use std::sync::Mutex; const MAX_SIZE: usize = 262_144; // max payload size is 256k async fn health() -> Result { - let health = json!({"status": "ok"}); + let version = std::env::var("APP_VERSION").unwrap_or_else(|_| "0.1.0".to_string()); + let health = json!({"status": "ok", "version": version}); Ok(HttpResponse::Ok().json(health)) } diff --git a/src/store-admin/Dockerfile b/src/store-admin/Dockerfile index 2fbb89cd..6f52b8d9 100644 --- a/src/store-admin/Dockerfile +++ b/src/store-admin/Dockerfile @@ -1,7 +1,11 @@ # Use an official Node.js runtime as a parent image FROM node:18.16.0-alpine as builder + WORKDIR /app +# Set the build argument for the app version number +ARG APP_VERSION=0.1.0 + # Copy package.json and package-lock.json to the container COPY package*.json ./ @@ -14,6 +18,9 @@ COPY . . # Expose the port the app listens on EXPOSE 80 +# Set the environment variable for the app version number +ENV APP_VERSION=$APP_VERSION + # Start the app CMD [ "npm", "run", "serve" ] diff --git a/src/store-admin/src/components/HealthCheck.vue b/src/store-admin/src/components/HealthCheck.vue deleted file mode 100644 index 8662be9e..00000000 --- a/src/store-admin/src/components/HealthCheck.vue +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/store-admin/src/router.js b/src/store-admin/src/router.js index 2493c14e..6d5433df 100644 --- a/src/store-admin/src/router.js +++ b/src/store-admin/src/router.js @@ -4,11 +4,9 @@ import OrderDetail from "./components/OrderDetail"; import ProductList from "./components/ProductList"; import ProductDetail from "./components/ProductDetail"; import ProductForm from "./components/ProductForm"; -import HealthCheck from "./components/HealthCheck"; const routes = [ { path: "/", component: OrderList }, - { path: "/health", component: HealthCheck }, { path: "/orders", component: OrderList }, { path: "/order/:id", component: OrderDetail }, { path: "/products", component: ProductList }, diff --git a/src/store-admin/vue.config.js b/src/store-admin/vue.config.js index f651ac1f..8f65590e 100644 --- a/src/store-admin/vue.config.js +++ b/src/store-admin/vue.config.js @@ -20,6 +20,12 @@ module.exports = defineConfig({ devServer.app.use(bodyParser.json()) + // Health check + devServer.app.get('/health', (_, res) => { + const version = process.env.APP_VERSION || '0.1.0' + res.send({ status: 'ok', version: version}) + }) + // Get all orders devServer.app.get('/makeline/order/fetch', (_, res) => { console.log(MAKELINE_SERVICE_URL) diff --git a/src/store-front/Dockerfile b/src/store-front/Dockerfile index 2fbb89cd..6f52b8d9 100644 --- a/src/store-front/Dockerfile +++ b/src/store-front/Dockerfile @@ -1,7 +1,11 @@ # Use an official Node.js runtime as a parent image FROM node:18.16.0-alpine as builder + WORKDIR /app +# Set the build argument for the app version number +ARG APP_VERSION=0.1.0 + # Copy package.json and package-lock.json to the container COPY package*.json ./ @@ -14,6 +18,9 @@ COPY . . # Expose the port the app listens on EXPOSE 80 +# Set the environment variable for the app version number +ENV APP_VERSION=$APP_VERSION + # Start the app CMD [ "npm", "run", "serve" ] diff --git a/src/store-front/src/components/HealthCheck.vue b/src/store-front/src/components/HealthCheck.vue deleted file mode 100644 index 8662be9e..00000000 --- a/src/store-front/src/components/HealthCheck.vue +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/store-front/src/router.js b/src/store-front/src/router.js index c6d90cfe..c44be0c5 100644 --- a/src/store-front/src/router.js +++ b/src/store-front/src/router.js @@ -2,11 +2,9 @@ import { createWebHistory, createRouter } from "vue-router"; import ProductList from "./components/ProductList"; import ProductDetail from "./components/ProductDetail"; import ShoppingCart from "./components/ShoppingCart"; -import HealthCheck from "./components/HealthCheck"; const routes = [ { path: "/", component: ProductList }, - { path: "/health", component: HealthCheck }, { path: "/product/:id", component: ProductDetail }, { path: "/cart", component: ShoppingCart }, ]; diff --git a/src/store-front/vue.config.js b/src/store-front/vue.config.js index c6a171b5..ea48fdad 100644 --- a/src/store-front/vue.config.js +++ b/src/store-front/vue.config.js @@ -20,6 +20,12 @@ module.exports = defineConfig({ devServer.app.use(bodyParser.json()) + // Health check + devServer.app.get('/health', (_, res) => { + const version = process.env.APP_VERSION || '0.1.0' + res.send({ status: 'ok', version: version}) + }) + devServer.app.get('/products', (_, res) => { fetch(`${PRODUCT_SERVICE_URL}`) .then(response => response.json()) diff --git a/src/virtual-customer/Dockerfile b/src/virtual-customer/Dockerfile index 1183f1be..6ae5b90a 100644 --- a/src/virtual-customer/Dockerfile +++ b/src/virtual-customer/Dockerfile @@ -5,6 +5,9 @@ RUN USER=root cargo new --bin virtual-customer # Create a new directory for our application WORKDIR /virtual-customer +# Set the build argument for the app version number +ARG APP_VERSION=0.1.0 + # Copy the Cargo.toml and Cargo.lock files to the container COPY Cargo.toml Cargo.lock ./ @@ -30,5 +33,8 @@ WORKDIR /app # Copy the binary from the builder stage COPY --from=builder /virtual-customer/target/release/virtual-customer /app +# Set the environment variable for the app version number +ENV APP_VERSION=$APP_VERSION + # Run the application CMD ["./virtual-customer"] \ No newline at end of file diff --git a/src/virtual-worker/Dockerfile b/src/virtual-worker/Dockerfile index f42579f5..95888135 100644 --- a/src/virtual-worker/Dockerfile +++ b/src/virtual-worker/Dockerfile @@ -5,6 +5,9 @@ RUN USER=root cargo new --bin virtual-worker # Create a new directory for our application WORKDIR /virtual-worker +# Set the build argument for the app version number +ARG APP_VERSION=0.1.0 + # Copy the Cargo.toml and Cargo.lock files to the container COPY Cargo.toml Cargo.lock ./ @@ -30,5 +33,8 @@ WORKDIR /app # Copy the binary from the builder stage COPY --from=builder /virtual-worker/target/release/virtual-worker /app +# Set the environment variable for the app version number +ENV APP_VERSION=$APP_VERSION + # Run the application CMD ["./virtual-worker"] \ No newline at end of file