diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..961823f --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +registry=https://pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/npm/registry/ +always-auth=true diff --git a/azure-pipelines/agentbase-deploy.yml b/azure-pipelines/agentbase-deploy.yml index a582e7b..cb6dd02 100644 --- a/azure-pipelines/agentbase-deploy.yml +++ b/azure-pipelines/agentbase-deploy.yml @@ -85,6 +85,10 @@ stages: displayName: 'Use Node.js 20' inputs: versionSpec: '20.x' + - task: NpmAuthenticate@0 + displayName: 'Authenticate npm → Azure Artifacts' + inputs: + workingFile: .npmrc - script: | corepack enable && corepack prepare pnpm@9 --activate pnpm install --frozen-lockfile || pnpm install @@ -111,6 +115,11 @@ stages: displayName: 'Use Python 3.12' inputs: versionSpec: '3.12' + - task: PipAuthenticate@1 + displayName: 'Authenticate pip → Azure Artifacts' + inputs: + artifactsFeeds: 'agentbase/python' + onlyAddExtraIndex: false - script: | pip install -r packages/ai-service/requirements.txt pip install pip-audit pytest diff --git a/azure-pipelines/templates/deploy-env.yml b/azure-pipelines/templates/deploy-env.yml index 9a893d5..13d8584 100644 --- a/azure-pipelines/templates/deploy-env.yml +++ b/azure-pipelines/templates/deploy-env.yml @@ -67,24 +67,34 @@ stages: echo "##vso[task.setvariable variable=AI_URL]$(out aiUrl)" # 2 · Build & push the three images server-side in ACR (no agent Docker). + # AZURE_ARTIFACTS_TOKEN (System.AccessToken) is passed as a build arg so + # each Dockerfile can authenticate to the Azure Artifacts npm/PyPI feeds + # during the package install step inside the build. - task: AzureCLI@2 displayName: '2 · Build & push images (az acr build)' + env: + AZURE_ARTIFACTS_TOKEN: $(System.AccessToken) inputs: azureSubscription: ${{ parameters.serviceConnection }} scriptType: bash scriptLocation: inlineScript inlineScript: | set -euo pipefail + # Construct authenticated PyPI URL from the same token (basic-auth format) + ARTIFACTS_PIP_URL="https://build:${AZURE_ARTIFACTS_TOKEN}@pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/pypi/simple/" az acr build --registry "$(ACR_NAME)" \ + --build-arg AZURE_ARTIFACTS_TOKEN="$AZURE_ARTIFACTS_TOKEN" \ --image agentbase-core:$(imageTag) --image agentbase-core:latest \ --file packages/core/Dockerfile . az acr build --registry "$(ACR_NAME)" \ + --build-arg AZURE_ARTIFACTS_PIP_URL="$ARTIFACTS_PIP_URL" \ --image agentbase-ai-service:$(imageTag) --image agentbase-ai-service:latest \ --file packages/ai-service/Dockerfile . az acr build --registry "$(ACR_NAME)" \ - --image agentbase-frontend:$(imageTag) --image agentbase-frontend:latest \ + --build-arg AZURE_ARTIFACTS_TOKEN="$AZURE_ARTIFACTS_TOKEN" \ --build-arg NEXT_PUBLIC_API_URL="$(CORE_URL)/api" \ --build-arg NEXT_PUBLIC_AI_URL="$(AI_URL)/api" \ + --image agentbase-frontend:$(imageTag) --image agentbase-frontend:latest \ --file packages/frontend/Dockerfile . # 3 · Seed Key Vault. Secret values are mapped through env: (required for diff --git a/packages/ai-service/Dockerfile b/packages/ai-service/Dockerfile index 86002c3..5be8676 100644 --- a/packages/ai-service/Dockerfile +++ b/packages/ai-service/Dockerfile @@ -1,16 +1,21 @@ # ---- Build Stage ---- -FROM python:3.12-slim AS builder +FROM mcr.microsoft.com/cbl-mariner/base/python:3.12 AS builder WORKDIR /app COPY packages/ai-service/requirements.txt . -RUN pip install --no-cache-dir --target=/deps -r requirements.txt + +# Inject Azure Artifacts PyPI URL; falls back to public PyPI for local builds without the arg +ARG AZURE_ARTIFACTS_PIP_URL +RUN pip install --no-cache-dir \ + --index-url "${AZURE_ARTIFACTS_PIP_URL:-https://pypi.org/simple/}" \ + --target=/deps -r requirements.txt # ---- Production Stage ---- -FROM python:3.12-slim AS production +FROM mcr.microsoft.com/cbl-mariner/base/python:3.12 AS production WORKDIR /app -RUN addgroup --system agentbase && adduser --system --group agentbase -RUN apt-get update && apt-get install -y --no-install-recommends wget && rm -rf /var/lib/apt/lists/* +RUN groupadd -r agentbase && useradd -r -g agentbase -s /sbin/nologin -M agentbase +RUN tdnf install -y wget COPY --from=builder /deps /usr/local/lib/python3.12/site-packages COPY packages/ai-service/app ./app diff --git a/packages/core/Dockerfile b/packages/core/Dockerfile index 7d3c7a2..77b4858 100644 --- a/packages/core/Dockerfile +++ b/packages/core/Dockerfile @@ -1,17 +1,22 @@ # ---- Build Stage ---- -FROM node:20-alpine AS builder +FROM mcr.microsoft.com/cbl-mariner/base/nodejs:20 AS builder WORKDIR /app # Install pnpm RUN corepack enable && corepack prepare pnpm@9 --activate -# Copy workspace config -COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./ +# Copy workspace config and registry settings +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* .npmrc ./ COPY packages/core/package.json packages/core/ COPY packages/shared/package.json packages/shared/ -# Install dependencies +# Inject Azure Artifacts auth token; strip it after install so the layer has no creds +ARG AZURE_ARTIFACTS_TOKEN +RUN if [ -n "${AZURE_ARTIFACTS_TOKEN}" ]; then \ + echo "//pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/npm/registry/:_authToken=${AZURE_ARTIFACTS_TOKEN}" >> .npmrc; \ + fi RUN pnpm install --frozen-lockfile 2>/dev/null || pnpm install +RUN sed -i '/_authToken/d' .npmrc # Copy source COPY packages/shared packages/shared @@ -23,11 +28,11 @@ WORKDIR /app/packages/core RUN npx nest build # ---- Production Stage ---- -FROM node:20-alpine AS production +FROM mcr.microsoft.com/cbl-mariner/base/nodejs:20 AS production WORKDIR /app RUN corepack enable && corepack prepare pnpm@9 --activate -RUN apk add --no-cache tini +RUN tdnf install -y wget # Copy built app COPY --from=builder /app/packages/core/dist ./dist @@ -35,7 +40,7 @@ COPY --from=builder /app/packages/core/package.json ./ COPY --from=builder /app/node_modules ./node_modules # Non-root user -RUN addgroup -g 1001 -S agentbase && adduser -S agentbase -u 1001 +RUN groupadd -g 1001 agentbase && useradd -u 1001 -g agentbase -s /sbin/nologin -M agentbase USER agentbase ENV NODE_ENV=production @@ -44,5 +49,4 @@ EXPOSE 3001 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \ CMD wget -q --spider http://localhost:3001/api/health || exit 1 -ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "dist/main.js"] diff --git a/packages/frontend/Dockerfile b/packages/frontend/Dockerfile index 10e4b1f..25b9f07 100644 --- a/packages/frontend/Dockerfile +++ b/packages/frontend/Dockerfile @@ -1,14 +1,21 @@ # ---- Build Stage ---- -FROM node:20-alpine AS builder +FROM mcr.microsoft.com/cbl-mariner/base/nodejs:20 AS builder WORKDIR /app RUN corepack enable && corepack prepare pnpm@9 --activate -COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./ +# Copy workspace config and registry settings +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* .npmrc ./ COPY packages/frontend/package.json packages/frontend/ COPY packages/shared/package.json packages/shared/ +# Inject Azure Artifacts auth token; strip it after install so the layer has no creds +ARG AZURE_ARTIFACTS_TOKEN +RUN if [ -n "${AZURE_ARTIFACTS_TOKEN}" ]; then \ + echo "//pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/npm/registry/:_authToken=${AZURE_ARTIFACTS_TOKEN}" >> .npmrc; \ + fi RUN pnpm install --frozen-lockfile 2>/dev/null || pnpm install +RUN sed -i '/_authToken/d' .npmrc COPY packages/shared packages/shared COPY packages/frontend packages/frontend @@ -23,16 +30,16 @@ ENV NEXT_PUBLIC_AI_URL=$NEXT_PUBLIC_AI_URL RUN npx next build # ---- Production Stage ---- -FROM node:20-alpine AS production +FROM mcr.microsoft.com/cbl-mariner/base/nodejs:20 AS production WORKDIR /app -RUN apk add --no-cache tini +RUN tdnf install -y wget COPY --from=builder /app/packages/frontend/.next/standalone ./ COPY --from=builder /app/packages/frontend/.next/static ./.next/static COPY --from=builder /app/packages/frontend/public ./public -RUN addgroup -g 1001 -S agentbase && adduser -S agentbase -u 1001 +RUN groupadd -g 1001 agentbase && useradd -u 1001 -g agentbase -s /sbin/nologin -M agentbase USER agentbase ENV NODE_ENV=production @@ -41,5 +48,4 @@ EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \ CMD wget -q --spider http://localhost:3000/ || exit 1 -ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "server.js"]