Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions pipelines/azure-build-docker-container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
# is the "Build and deploy Node.js app to Azure Web App" step.
#
# Azure DevOps port of .github/workflows/build-docker-container.yml. The image
# build runs entirely inside ACR via `az acr build`: the agent only uploads the
# ts/ build context (Dockerfile + sources) and ACR's build agents run the
# multi-stage build (pnpm install + build + pnpm deploy) — see ts/Dockerfile.
# build runs entirely inside ACR via `az acr run`: the agent only uploads the
# ts/ build context and ACR's build agents run the BuildKit-enabled task in
# ts/acr-build.yaml (pnpm install + build + pnpm deploy, then push).
#
# Differences from the GitHub Actions version, and why:
# * Credentials moved out of GitHub. The GitHub job authenticated with
Expand All @@ -19,7 +19,7 @@
# Identity Federation) referenced by $(azureSubscription). The AzureCLI@2
# task signs in through it with no stored secret to rotate.
# * Node / pnpm setup and docker/setup-buildx-action are dropped. `az acr
# build` performs the whole image build inside ACR from ts/Dockerfile, so
# run` performs the whole image build inside ACR from ts/Dockerfile, so
# the agent never runs pnpm, Node, or a local Docker build — those GitHub
# steps contributed nothing to the actual build.
# * dorny/paths-filter is replaced by the trigger `paths` filter below (build
Expand Down Expand Up @@ -77,20 +77,20 @@ jobs:
displayName: Checkout TypeAgent repository

# Federated (secret-free) login via the ARM service connection, then hand
# the build off to ACR. `az acr build` uploads $(buildDirectory) as the
# build context, runs ts/Dockerfile on ACR's build agents, and pushes the
# result as $(containerRegistry).azurecr.io/$(imageName):$(imageTag).
# the build off to ACR. The task enables BuildKit for secret mounts and
# pushes the result as $(containerRegistry).azurecr.io/$(imageName):$(imageTag).
- task: AzureCLI@2
displayName: Build image in ACR (az acr build)
displayName: Build and push image in ACR (BuildKit)
inputs:
azureSubscription: $(azureSubscription)
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
set -euo pipefail
az account show --query "{subscription:name, id:id}" -o table
az acr build \
az acr run \
--registry "$(containerRegistry)" \
--image "$(imageName):$(imageTag)" \
--file "$(buildDirectory)/Dockerfile" \
--file acr-build.yaml \
--set "imageName=$(imageName)" \
--set "imageTag=$(imageTag)" \
"$(buildDirectory)"
13 changes: 11 additions & 2 deletions ts/.dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
node_modules
**/node_modules
.git
.gitignore
*.md
dist
**/dist
**/out
**/*.tsbuildinfo
**/*.done.build.log
**/.npmrc
**/.npmrc.*
**/.env
**/.env.*
**/config.local.yaml
**/config.local.yml
74 changes: 49 additions & 25 deletions ts/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
FROM node:22 AS base
# syntax=docker/dockerfile:1
FROM node:24.22.1-bookworm AS base

ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN npm install -g corepack@latest
RUN corepack enable pnpm
RUN corepack install -g pnpm@latest

# dependencies
RUN apt-get update
RUN apt-get install -y libsecret-1-0
RUN apt-get install -y libsecret-1-dev
RUN apt install -y gnome-keyring
RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash
WORKDIR /usr/src/app
COPY package.json ./package.json
# npm honors private-registry credentials; Corepack does not read .npmrc.
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm install --global corepack@0.36.0 \
&& corepack disable pnpm \
&& npm install --global "$(node -p "require('./package.json').packageManager.split('+')[0]")" \
&& npm cache clean --force

# Install dependencies required for Chrome/Puppeteer
RUN apt-get update && apt-get install -y \
# Refresh security updates as well as installing the app's system dependencies.
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
libsecret-1-0 \
libsecret-1-dev \
gnome-keyring \
curl \
wget \
gnupg \
ca-certificates \
procps \
libxss1 \
libgconf-2-4 \
libatk1.0-0 \
libatk-bridge2.0-0 \
libgdk-pixbuf2.0-0 \
Expand All @@ -29,19 +34,34 @@ RUN apt-get update && apt-get install -y \
libnss3-dev \
libxss-dev \
fonts-liberation \
libappindicator3-1 \
libasound2 \
libdbus-1-3 \
libnspr4 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
xdg-utils
xdg-utils \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists

RUN mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://packages.microsoft.com/keys/microsoft.asc -o /tmp/microsoft.asc \
&& gpg --dearmor -o /etc/apt/keyrings/microsoft.gpg /tmp/microsoft.asc \
&& chmod 644 /etc/apt/keyrings/microsoft.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/microsoft.gpg] https://packages.microsoft.com/repos/azure-cli/ bookworm main" > /etc/apt/sources.list.d/azure-cli.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends azure-cli=2.91.0-1~bookworm \
&& rm /tmp/microsoft.asc \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists

# Install Chrome for Puppeteer
RUN wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
&& apt-get install -y ./google-chrome-stable_current_amd64.deb \
&& rm google-chrome-stable_current_amd64.deb
RUN curl -fsSL https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -o /tmp/google-chrome.deb \
&& apt-get update \
&& apt-get install -y --no-install-recommends /tmp/google-chrome.deb \
&& rm /tmp/google-chrome.deb \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists

# base only image
FROM base AS build
Expand All @@ -52,10 +72,14 @@ WORKDIR /usr/src/app
# runtime) and that download is flaky in CI (fails with ECONNRESET), so skip
# it — the bundled CPU execution provider is all this container uses.
ENV ONNXRUNTIME_NODE_INSTALL_CUDA=skip
RUN pnpm install --frozen-lockfile
RUN pnpm run -r build
RUN pnpm deploy --legacy --filter=agent-api --prod /prod/api
RUN pnpm deploy --legacy --filter=tools-scripts --prod /prod/tools
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
pnpm install --frozen-lockfile
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
pnpm run -r build
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
pnpm deploy --legacy --filter=agent-api --prod /prod/api
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
pnpm deploy --legacy --filter=tools-scripts --prod /prod/tools

# image with api and tool bits only
FROM base AS api
Expand All @@ -82,7 +106,7 @@ WORKDIR /prod/api
#RUN mkdir /blob-cache
######################################################################################################

EXPOSE 80:3000
EXPOSE 3000

# The API server binds loopback by default so a local run isn't reachable from
# the network. In the container the published port is the whole point and the
Expand All @@ -102,8 +126,8 @@ RUN echo az login --identity >> start.sh
#RUN echo blobfuse2 mount /mnt/blob --config-file=mount-blobfuse.yaml >> start.sh
# run getKeys
RUN echo node ../tools/scripts/getKeys.mjs >> start.sh
# start API server
RUN echo pnpm start >> start.sh
# Start Node directly so startup does not need npm registry credentials.
RUN echo node ./dist/index.js >> start.sh

# run start script
CMD ["bash", "start.sh"]
41 changes: 41 additions & 0 deletions ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,47 @@ In this directory:
- Run `pnpm i`
- Run `pnpm run build`

### Docker image

The production [Dockerfile](./Dockerfile) uses Node 24 on Debian Bookworm,
Corepack 0.36.0, and Azure CLI 2.91.0. The pnpm version comes from
`packageManager` in [package.json](./package.json). Chrome is installed from
Google's stable channel. The image currently targets `linux/amd64`.
The API listens on container port 3000; publish a host port with Docker's
`--publish` option, for example `--publish 80:3000`.

Build from this directory with Docker BuildKit:

```console
docker build --pull --no-cache --platform linux/amd64 --tag typeagent:local .
```

`--pull` refreshes the base image, and `--no-cache` reruns the OS security
updates instead of reusing an older package-install layer. Updating this image
does not update the host Docker Engine; update Docker Desktop or Docker Engine
separately.

For a private npm registry, supply a Linux-compatible npm configuration
containing the registry URL and its authentication as a BuildKit secret:

```console
docker build --pull --no-cache --platform linux/amd64 --secret id=npmrc,src=<npm-config-path> --tag typeagent:local .
```

Alternatively, use `--secret id=npmrc,env=TYPEAGENT_BUILD_NPMRC` when the
configuration is held in an environment variable. Host-specific `tokenHelper`
commands must be resolved before passing the configuration to the Linux build.
The secret is mounted only for package installation and build commands; npm
credentials, local API-key configuration, and host build outputs are excluded
from the build context. pnpm is installed through npm so the same registry
authentication is used for its installation. Application startup uses Node
directly and does not need npm registry access.

The [ACR task](./acr-build.yaml) enables BuildKit for the
[container pipeline](../pipelines/azure-build-docker-container.yml) and retains
the existing build-and-push behavior. The image's normal startup still uses
Azure managed identity.

### Agent Specific Steps (Optional)

#### VSCode Agent
Expand Down
14 changes: 14 additions & 0 deletions ts/acr-build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

version: v1.1.0
stepTimeout: 3600
steps:
- build: >-
--pull --no-cache --platform linux/amd64
-t {{.Run.Registry}}/{{.Values.imageName}}:{{.Values.imageTag}}
-f Dockerfile .
env:
- DOCKER_BUILDKIT=1
- push:
- "{{.Run.Registry}}/{{.Values.imageName}}:{{.Values.imageTag}}"
Loading