mirror of
https://github.com/mattermost/mattermost.git
synced 2026-11-02 20:31:01 -06:00
Document Mattermost cloud startup flow (#36559)
* Add Cursor Cloud Agent Docker environment Co-authored-by: Cursor <cursoragent@cursor.com> * Fix Cloud Agent enterprise and Docker access Co-authored-by: Cursor <cursoragent@cursor.com> * Fix Cloud Agent Go path setup Co-authored-by: Cursor <cursoragent@cursor.com> * Use Cursor multi-repo enterprise checkout Co-authored-by: Cursor <cursoragent@cursor.com> * Use sibling enterprise checkout layout Co-authored-by: Cursor <cursoragent@cursor.com> * Add tmux to Cloud Agent image Co-authored-by: Cursor <cursoragent@cursor.com> * Document Mattermost cloud startup flow Co-authored-by: nick.misasi <nick.misasi@mattermost.com> * Document Mattermost license behavior in cloud Co-authored-by: nick.misasi <nick.misasi@mattermost.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
d8612e378f
commit
11b55b77f3
8 files changed
+608
-1
No files matched your search
@@ -0,0 +1,166 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
ARG GO_VERSION=1.25.9
|
||||
ARG NODE_VERSION=24.11.1
|
||||
ARG NVM_VERSION=0.40.3
|
||||
ARG AGENT_BROWSER_VERSION=0.27.0
|
||||
ARG DOCKER_VERSION=5:28.5.2-1~ubuntu.24.04~noble
|
||||
ARG CONTAINERD_VERSION=2.2.3-1~ubuntu.24.04~noble
|
||||
ARG DOCKER_BUILDX_VERSION=0.33.0-1~ubuntu.24.04~noble
|
||||
ARG DOCKER_COMPOSE_VERSION=5.1.3-1~ubuntu.24.04~noble
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV NVM_DIR=/home/ubuntu/.nvm
|
||||
ENV GOPATH=/home/ubuntu/go
|
||||
ENV PATH=/home/ubuntu/.nvm/versions/node/v${NODE_VERSION}/bin:/usr/local/go/bin:/home/ubuntu/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
apt-transport-https \
|
||||
acl \
|
||||
bash \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
curl \
|
||||
dnsutils \
|
||||
file \
|
||||
fuse-overlayfs \
|
||||
git \
|
||||
git-lfs \
|
||||
gnupg \
|
||||
iproute2 \
|
||||
iptables \
|
||||
jq \
|
||||
less \
|
||||
libasound2t64 \
|
||||
libatk-bridge2.0-0 \
|
||||
libatk1.0-0 \
|
||||
libatspi2.0-0 \
|
||||
libcairo2 \
|
||||
libcups2 \
|
||||
libdrm2 \
|
||||
libgbm1 \
|
||||
libgtk-3-0 \
|
||||
libnspr4 \
|
||||
libnss3 \
|
||||
libpango-1.0-0 \
|
||||
libsecret-1-0 \
|
||||
libx11-xcb1 \
|
||||
libxcomposite1 \
|
||||
libxdamage1 \
|
||||
libxfixes3 \
|
||||
libxkbcommon0 \
|
||||
libxrandr2 \
|
||||
libxshmfence1 \
|
||||
locales \
|
||||
lsof \
|
||||
make \
|
||||
nano \
|
||||
netcat-openbsd \
|
||||
openssh-server \
|
||||
pgloader \
|
||||
pkg-config \
|
||||
procps \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
rsync \
|
||||
sudo \
|
||||
tar \
|
||||
tmux \
|
||||
unzip \
|
||||
vim \
|
||||
xmlsec1 \
|
||||
xz-utils \
|
||||
zip && \
|
||||
locale-gen en_US.UTF-8 && \
|
||||
git lfs install --system && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN install -m 0755 -d /etc/apt/keyrings && \
|
||||
curl --retry 3 --retry-delay 5 -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg && \
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
docker-ce=${DOCKER_VERSION} \
|
||||
docker-ce-cli=${DOCKER_VERSION} \
|
||||
containerd.io=${CONTAINERD_VERSION} \
|
||||
docker-buildx-plugin=${DOCKER_BUILDX_VERSION} \
|
||||
docker-compose-plugin=${DOCKER_COMPOSE_VERSION} && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /etc/docker && \
|
||||
printf '%s\n' \
|
||||
'{' \
|
||||
' "storage-driver": "fuse-overlayfs",' \
|
||||
' "features": {' \
|
||||
' "buildkit": true' \
|
||||
' }' \
|
||||
'}' > /etc/docker/daemon.json && \
|
||||
update-alternatives --set iptables /usr/sbin/iptables-legacy && \
|
||||
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
|
||||
|
||||
RUN install -m 0755 -d /etc/apt/keyrings && \
|
||||
curl --retry 3 --retry-delay 5 -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg -o /etc/apt/keyrings/githubcli-archive-keyring.gpg && \
|
||||
chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg && \
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends gh && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN case "$(dpkg --print-architecture)" in \
|
||||
amd64) go_arch=amd64 ;; \
|
||||
arm64) go_arch=arm64 ;; \
|
||||
*) echo "Unsupported Go architecture: $(dpkg --print-architecture)" >&2; exit 1 ;; \
|
||||
esac && \
|
||||
curl --retry 3 --retry-delay 5 -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${go_arch}.tar.gz" -o /tmp/go.tgz && \
|
||||
rm -rf /usr/local/go && \
|
||||
tar -C /usr/local -xzf /tmp/go.tgz && \
|
||||
ln -sfnT /usr/local/go/bin/go /usr/local/bin/go && \
|
||||
ln -sfnT /usr/local/go/bin/gofmt /usr/local/bin/gofmt && \
|
||||
rm -f /tmp/go.tgz
|
||||
|
||||
RUN case "$(dpkg --print-architecture)" in \
|
||||
amd64) aws_arch=x86_64 ;; \
|
||||
arm64) aws_arch=aarch64 ;; \
|
||||
*) echo "Unsupported AWS CLI architecture: $(dpkg --print-architecture)" >&2; exit 1 ;; \
|
||||
esac && \
|
||||
curl --retry 3 --retry-delay 5 -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-${aws_arch}.zip" -o /tmp/awscliv2.zip && \
|
||||
unzip -q /tmp/awscliv2.zip -d /tmp && \
|
||||
/tmp/aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli && \
|
||||
rm -rf /tmp/aws /tmp/awscliv2.zip
|
||||
|
||||
RUN install -m 0755 -d /var/run/sshd && \
|
||||
printf '%s\n' 'PasswordAuthentication no' 'ChallengeResponseAuthentication no' 'UsePAM no' > /etc/ssh/sshd_config.d/disable_password_auth.conf && \
|
||||
if ! id -u ubuntu >/dev/null 2>&1; then useradd -m -s /bin/bash ubuntu; fi && \
|
||||
groupadd -f docker && \
|
||||
usermod -aG docker,sudo ubuntu && \
|
||||
echo "ubuntu ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/ubuntu && \
|
||||
chmod 0440 /etc/sudoers.d/ubuntu && \
|
||||
echo "ubuntu:ubuntu" | chpasswd && \
|
||||
install -d -o ubuntu -g ubuntu "${NVM_DIR}" "${GOPATH}"
|
||||
|
||||
RUN git clone --depth 1 --branch "v${NVM_VERSION}" https://github.com/nvm-sh/nvm.git "${NVM_DIR}" && \
|
||||
chown -R ubuntu:ubuntu "${NVM_DIR}" "${GOPATH}" && \
|
||||
su ubuntu -c "bash -lc 'set -e; source ${NVM_DIR}/nvm.sh; nvm install ${NODE_VERSION}; nvm alias default ${NODE_VERSION}; nvm use default; npm install -g agent-browser@${AGENT_BROWSER_VERSION}; agent-browser install || true'" && \
|
||||
ln -sfn "${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/node" /usr/local/bin/node && \
|
||||
ln -sfn "${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/npm" /usr/local/bin/npm && \
|
||||
ln -sfn "${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/npx" /usr/local/bin/npx && \
|
||||
ln -sfn "${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/agent-browser" /usr/local/bin/agent-browser
|
||||
|
||||
RUN { \
|
||||
echo 'export NVM_DIR="$HOME/.nvm"'; \
|
||||
echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"'; \
|
||||
echo 'export GOPATH="$HOME/go"'; \
|
||||
echo 'export PATH="$NVM_DIR/versions/node/v24.11.1/bin:/usr/local/go/bin:$GOPATH/bin:$PATH"'; \
|
||||
} >> /home/ubuntu/.bashrc && \
|
||||
chown ubuntu:ubuntu /home/ubuntu/.bashrc
|
||||
|
||||
ENV LANG=en_US.UTF-8
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
|
||||
WORKDIR /home/ubuntu
|
||||
USER ubuntu
|
||||
@@ -0,0 +1,36 @@
|
||||
# Cursor Cloud Agent Environment
|
||||
|
||||
This directory defines the checked-in Cloud Agent environment for this repository. Cursor resolves `.cursor/environment.json` before personal or team saved environments, so this replaces the snapshot-dependent `/onboard` flow for agents started from this repo.
|
||||
|
||||
The Docker build context is `.cursor/` only. The Dockerfile intentionally does not copy the repository; Cursor checks out the requested commit at runtime.
|
||||
|
||||
## What Is Baked Into The Image
|
||||
|
||||
- Ubuntu 24.04.
|
||||
- Docker CE 28.5.2 with `fuse-overlayfs` and `iptables-legacy`, matching Cursor's Docker-in-Cloud guidance for complex compose setups.
|
||||
- Go 1.25.9 from `server/.go-version`.
|
||||
- Node 24.11.1/npm 11 via nvm, matching `.nvmrc` and `webapp/package.json`.
|
||||
- `agent-browser@0.27.0` and browser dependencies for screenshot workflows.
|
||||
- AWS CLI v2 for S3 uploads.
|
||||
- Common Mattermost build/test tools: `make`, `jq`, `xmlsec1`, `pgloader`, Git LFS, GitHub CLI, Python 3, and build essentials.
|
||||
|
||||
## Runtime Hooks
|
||||
|
||||
- `cloud-agent-install.sh` runs after Cursor checks out the repo. It refreshes nvm, installs agent-browser browsers, verifies Cursor's multi-repo `mattermost/enterprise` checkout, runs `server` Go dependency hydration, installs webapp dependencies, and runs Playwright `npm ci`.
|
||||
- `cloud-agent-start.sh` materializes `.cursor/cursor.md` as `.cursor/AGENTS.md`, fixes current-session Docker socket access, then starts Docker and waits until `docker info` and `docker compose version` succeed.
|
||||
|
||||
The environment declares `github.com/mattermost/enterprise` in `repositoryDependencies` so Cursor can provide it as part of the multi-repo workspace. Cursor currently clones the repositories as siblings, such as `/agent/repos/mattermost` and `/agent/repos/enterprise`, which matches `server/Makefile`'s default `../../enterprise` path. The install hook does not clone, pull, or symlink enterprise.
|
||||
|
||||
## Useful Skips
|
||||
|
||||
Set these environment variables to `true` to shorten startup for narrow tasks:
|
||||
|
||||
- `CLOUD_AGENT_SKIP_ENTERPRISE`
|
||||
- `CLOUD_AGENT_SKIP_GO_DEPS`
|
||||
- `CLOUD_AGENT_SKIP_WEBAPP_DEPS`
|
||||
- `CLOUD_AGENT_SKIP_PLAYWRIGHT_DEPS`
|
||||
- `CLOUD_AGENT_SKIP_AGENT_BROWSER_INSTALL`
|
||||
|
||||
## Expected Secrets
|
||||
|
||||
- AWS uploads use the standard AWS CLI environment variables provided to the Cloud Agent: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_S3_BUCKET_NAME`. The image only supplies the `aws` binary.
|
||||
@@ -0,0 +1,106 @@
|
||||
# Cursor Cloud Agent Instructions
|
||||
|
||||
These instructions apply to Cursor Cloud Agents after `.cursor/scripts/cloud-agent-start.sh` materializes this file as `.cursor/AGENTS.md`.
|
||||
|
||||
## Environment
|
||||
|
||||
- Docker must be available. If `docker info` fails, inspect `/tmp/docker-service-start.log` and `/tmp/dockerd.log`; do not assume a snapshot will provide Docker.
|
||||
- The image includes Go, Node/npm, Docker Compose, AWS CLI v2, and `agent-browser`.
|
||||
- Cursor should provide `mattermost/enterprise` through the multi-repo environment. The expected layout is sibling repositories, such as `/agent/repos/mattermost` and `/agent/repos/enterprise`; this matches `server/Makefile`'s default `../../enterprise` path.
|
||||
|
||||
## Running Mattermost
|
||||
|
||||
1. Start dependencies:
|
||||
|
||||
```bash
|
||||
cd server
|
||||
make start-docker
|
||||
```
|
||||
|
||||
2. Start the server:
|
||||
|
||||
```bash
|
||||
cd server
|
||||
make run-server
|
||||
```
|
||||
|
||||
3. Start the web app in another terminal when UI work needs live verification:
|
||||
|
||||
```bash
|
||||
cd webapp
|
||||
make run
|
||||
```
|
||||
|
||||
The Mattermost server is expected at `http://localhost:8065`. The webapp dev server commonly uses `http://localhost:9005`.
|
||||
|
||||
### Known-good Cloud flow
|
||||
|
||||
- In this multi-repo Cloud environment, `mattermost` and `enterprise` are expected to start from `master`, so sibling checkout skew should not need extra handling.
|
||||
- `server/Makefile`'s `run` target only reaches `run-client` if the server is backgrounded. In Cloud, the reliable combined startup is:
|
||||
|
||||
```bash
|
||||
cd server
|
||||
ENABLED_DOCKER_SERVICES='postgres redis' RUN_SERVER_IN_BACKGROUND=true make run
|
||||
```
|
||||
|
||||
- If you want split terminals instead, use:
|
||||
|
||||
```bash
|
||||
cd server
|
||||
ENABLED_DOCKER_SERVICES='postgres redis' make run-server
|
||||
```
|
||||
|
||||
and then:
|
||||
|
||||
```bash
|
||||
cd webapp
|
||||
make run
|
||||
```
|
||||
|
||||
- When the server starts and `MM_LICENSE` is present in the environment, the server applies that license automatically. If `MM_LICENSE` is not set, starting the server automatically applies an Entry license, which provides nearly all functionality needed for development.
|
||||
- `ENABLED_DOCKER_SERVICES='postgres redis'` avoids optional local-dev services such as Prometheus, Grafana, Loki, Minio, Azurite, and OpenLDAP. This is useful in Cloud when Docker Hub rate limits block the default `make start-docker` dependency set.
|
||||
- If the first-user signup UI is flaky but the server is already healthy, seed local state with `mmctl` and then log in through the browser:
|
||||
|
||||
```bash
|
||||
cd server
|
||||
./bin/mmctl --local user create --email cursor@example.com --username cursoradmin --password Password123! --system-admin --email-verified --disable-welcome-email
|
||||
./bin/mmctl --local team create --name cursorteam --display-name "Cursor Team" --email cursor@example.com
|
||||
```
|
||||
|
||||
- A healthy server responds at:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8065/api/v4/system/ping
|
||||
```
|
||||
|
||||
## Tests And Setup
|
||||
|
||||
- Backend workspace setup is handled by `cd server && make setup-go-work`; never run `go mod tidy` directly.
|
||||
- Webapp dependencies are installed with `cd webapp && make node_modules`.
|
||||
- Playwright dependencies are installed with `cd e2e-tests/playwright && npm ci`.
|
||||
- For full Playwright compose flows, use the existing `e2e-tests` Makefile and scripts. Docker Compose is available in the Cloud Agent image.
|
||||
|
||||
## Browser Screenshots
|
||||
|
||||
Use `agent-browser` for browser automation and screenshots. If the CLI is missing or browsers are unavailable, run:
|
||||
|
||||
```bash
|
||||
npm install -g agent-browser@0.27.0
|
||||
agent-browser install
|
||||
```
|
||||
|
||||
- Chrome is available in the Cloud image, and `agent-browser skills get core --full` is a quick check that the CLI and bundled skills are working.
|
||||
Prefer verifying UI changes against the running local Mattermost instance before opening or updating a PR.
|
||||
|
||||
## AWS And PR Artifacts
|
||||
|
||||
AWS CLI v2 is installed for uploading screenshots or reports. Cloud Agents should receive `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_S3_BUCKET_NAME` as environment variables.
|
||||
|
||||
Before uploading, verify credentials with:
|
||||
|
||||
```bash
|
||||
aws sts get-caller-identity
|
||||
```
|
||||
|
||||
- If the configured S3 bucket is public, upload with `aws s3 cp` and share the plain object URL `https://$AWS_S3_BUCKET_NAME.s3.amazonaws.com/<key>` instead of generating a presigned URL.
|
||||
Do not hardcode AWS credentials or bucket secrets in the repository.
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "Mattermost Cloud Agent",
|
||||
"user": "ubuntu",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"context": "."
|
||||
},
|
||||
"repositoryDependencies": [
|
||||
"github.com/mattermost/enterprise"
|
||||
],
|
||||
"install": "bash .cursor/scripts/cloud-agent-install.sh",
|
||||
"start": "bash .cursor/scripts/cloud-agent-start.sh",
|
||||
"ports": [
|
||||
{
|
||||
"name": "Mattermost server",
|
||||
"port": 8065
|
||||
},
|
||||
{
|
||||
"name": "Webapp dev server",
|
||||
"port": 9005
|
||||
}
|
||||
]
|
||||
}
|
||||
Executable
+179
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
log() {
|
||||
printf '[cloud-agent-install] %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
is_true() {
|
||||
case "${1:-}" in
|
||||
1 | true | TRUE | yes | YES) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
NODE_VERSION="${CLOUD_AGENT_NODE_VERSION:-24.11.1}"
|
||||
AGENT_BROWSER_VERSION="${CLOUD_AGENT_BROWSER_VERSION:-0.27.0}"
|
||||
|
||||
export GOPATH="${GOPATH:-$HOME/go}"
|
||||
export PATH="/usr/local/go/bin:$GOPATH/bin:/usr/local/bin:$PATH"
|
||||
|
||||
ensure_go() {
|
||||
if ! command -v go >/dev/null 2>&1; then
|
||||
log "Go is not available on PATH. PATH=$PATH"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Using $(go version)"
|
||||
}
|
||||
|
||||
source_node() {
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
||||
# shellcheck source=/dev/null
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_node() {
|
||||
source_node
|
||||
|
||||
if command -v nvm >/dev/null 2>&1; then
|
||||
nvm install "$NODE_VERSION" >/dev/null
|
||||
nvm alias default "$NODE_VERSION" >/dev/null
|
||||
nvm use "$NODE_VERSION" >/dev/null
|
||||
fi
|
||||
|
||||
if ! command -v node >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then
|
||||
log "Node.js/npm are not available; check the Cloud Agent Dockerfile build."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Using node $(node --version) and npm $(npm --version)"
|
||||
|
||||
if ! command -v agent-browser >/dev/null 2>&1; then
|
||||
npm install -g "agent-browser@${AGENT_BROWSER_VERSION}"
|
||||
fi
|
||||
|
||||
if ! is_true "${CLOUD_AGENT_SKIP_AGENT_BROWSER_INSTALL:-false}"; then
|
||||
agent-browser install || log "agent-browser install failed; continuing so code tasks are not blocked."
|
||||
fi
|
||||
}
|
||||
|
||||
enterprise_build_dir() {
|
||||
case "$BUILD_ENTERPRISE_DIR" in
|
||||
/*) realpath -m "$BUILD_ENTERPRISE_DIR" ;;
|
||||
*) realpath -m "$ROOT/server/$BUILD_ENTERPRISE_DIR" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
find_enterprise_checkout() {
|
||||
local candidates=()
|
||||
if [ -n "${ENTERPRISE_CHECKOUT_DIR:-}" ]; then
|
||||
candidates+=("$ENTERPRISE_CHECKOUT_DIR")
|
||||
fi
|
||||
if [ -n "${ENTERPRISE_DIR:-}" ]; then
|
||||
candidates+=("$ENTERPRISE_DIR")
|
||||
fi
|
||||
|
||||
candidates+=(
|
||||
"$ROOT/../enterprise"
|
||||
"$ROOT/../../enterprise"
|
||||
"$HOME/enterprise"
|
||||
)
|
||||
|
||||
local candidate
|
||||
for candidate in "${candidates[@]}"; do
|
||||
if git -C "$candidate" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
realpath -m "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
verify_enterprise_checkout() {
|
||||
if is_true "${CLOUD_AGENT_SKIP_ENTERPRISE:-false}"; then
|
||||
log "Skipping enterprise verification because CLOUD_AGENT_SKIP_ENTERPRISE is set."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local target
|
||||
if ! target="$(find_enterprise_checkout)"; then
|
||||
log "Enterprise checkout not found. Ensure the Cursor multi-repo environment includes github.com/mattermost/enterprise."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Enterprise checkout ready at $target."
|
||||
}
|
||||
|
||||
hydrate_go_dependencies() {
|
||||
if is_true "${CLOUD_AGENT_SKIP_GO_DEPS:-false}"; then
|
||||
log "Skipping Go dependency hydration."
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -d server ]; then
|
||||
if [ -n "${BUILD_ENTERPRISE_DIR:-}" ]; then
|
||||
local enterprise_dir
|
||||
enterprise_dir="$(enterprise_build_dir)"
|
||||
log "Hydrating Go workspace with BUILD_ENTERPRISE_DIR=$enterprise_dir"
|
||||
(
|
||||
cd server
|
||||
BUILD_ENTERPRISE_DIR="$enterprise_dir" make setup-go-work
|
||||
go mod download
|
||||
if [ -f public/go.mod ]; then
|
||||
(cd public && go mod download)
|
||||
fi
|
||||
)
|
||||
else
|
||||
log "Hydrating Go workspace with server/Makefile default enterprise path."
|
||||
(
|
||||
cd server
|
||||
make setup-go-work
|
||||
go mod download
|
||||
if [ -f public/go.mod ]; then
|
||||
(cd public && go mod download)
|
||||
fi
|
||||
)
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
hydrate_webapp_dependencies() {
|
||||
if is_true "${CLOUD_AGENT_SKIP_WEBAPP_DEPS:-false}"; then
|
||||
log "Skipping webapp dependency hydration."
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -f webapp/package.json ]; then
|
||||
log "Hydrating webapp dependencies."
|
||||
(cd webapp && make node_modules)
|
||||
fi
|
||||
}
|
||||
|
||||
hydrate_playwright_dependencies() {
|
||||
if is_true "${CLOUD_AGENT_SKIP_PLAYWRIGHT_DEPS:-false}"; then
|
||||
log "Skipping Playwright dependency hydration."
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -f e2e-tests/playwright/package-lock.json ]; then
|
||||
log "Hydrating Playwright dependencies."
|
||||
(cd e2e-tests/playwright && npm ci)
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_go
|
||||
ensure_node
|
||||
verify_enterprise_checkout
|
||||
hydrate_go_dependencies
|
||||
hydrate_webapp_dependencies
|
||||
hydrate_playwright_dependencies
|
||||
|
||||
log "AWS CLI: $(aws --version 2>&1 || printf 'not available')"
|
||||
log "Install hook complete."
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
log() {
|
||||
printf '[cloud-agent-start] %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
materialize_cloud_agent_instructions() {
|
||||
if [ -f .cursor/cursor.md ]; then
|
||||
cp .cursor/cursor.md .cursor/AGENTS.md
|
||||
log "Materialized Cloud Agent instructions at .cursor/AGENTS.md."
|
||||
fi
|
||||
}
|
||||
|
||||
materialize_cloud_agent_instructions
|
||||
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
log "Docker CLI is missing. The Cloud Agent image did not build from .cursor/Dockerfile."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ensure_docker_socket_access() {
|
||||
if [ ! -S /var/run/docker.sock ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
sudo groupadd -f docker
|
||||
sudo usermod -aG docker "$(id -un)"
|
||||
sudo chgrp docker /var/run/docker.sock >/dev/null 2>&1 || true
|
||||
sudo chmod g+rw /var/run/docker.sock >/dev/null 2>&1 || true
|
||||
|
||||
if command -v setfacl >/dev/null 2>&1; then
|
||||
sudo setfacl -m "u:$(id -un):rw" /var/run/docker.sock >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -f /proc/sys/kernel/apparmor_restrict_unprivileged_userns ]; then
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 >/dev/null 2>&1 || \
|
||||
log "Could not relax AppArmor user namespace restriction; openldap-based tests may need a larger host profile."
|
||||
fi
|
||||
|
||||
ensure_docker_socket_access
|
||||
|
||||
if docker info >/dev/null 2>&1; then
|
||||
log "Docker is already running."
|
||||
docker compose version
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Starting Docker daemon."
|
||||
if command -v service >/dev/null 2>&1; then
|
||||
sudo sh -c 'service docker start >/tmp/docker-service-start.log 2>&1' || \
|
||||
log "service docker start failed; falling back to direct dockerd startup."
|
||||
fi
|
||||
|
||||
if ! pgrep -x dockerd >/dev/null 2>&1; then
|
||||
sudo sh -c 'nohup dockerd --host=unix:///var/run/docker.sock >/tmp/dockerd.log 2>&1 &'
|
||||
fi
|
||||
|
||||
for _ in {1..60}; do
|
||||
ensure_docker_socket_access
|
||||
|
||||
if docker info >/dev/null 2>&1; then
|
||||
log "Docker is ready."
|
||||
docker version
|
||||
docker compose version
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
done
|
||||
|
||||
log "Docker did not become ready within 60 seconds."
|
||||
if [ -f /tmp/docker-service-start.log ]; then
|
||||
log "docker service output:"
|
||||
tail -n 80 /tmp/docker-service-start.log >&2 || true
|
||||
fi
|
||||
if [ -f /tmp/dockerd.log ]; then
|
||||
log "dockerd output:"
|
||||
tail -n 120 /tmp/dockerd.log >&2 || true
|
||||
fi
|
||||
|
||||
exit 1
|
||||
+9
-1
@@ -166,7 +166,15 @@ docker-compose.override.yaml
|
||||
**/CLAUDE.md
|
||||
.claude
|
||||
.cursorrules
|
||||
.cursor/
|
||||
.cursor/*
|
||||
!.cursor/README.md
|
||||
!.cursor/cursor.md
|
||||
!.cursor/Dockerfile
|
||||
!.cursor/environment.json
|
||||
!.cursor/scripts/
|
||||
.cursor/scripts/*
|
||||
!.cursor/scripts/cloud-agent-install.sh
|
||||
!.cursor/scripts/cloud-agent-start.sh
|
||||
server/prev-report.xml
|
||||
server/prev-gotestsum.json
|
||||
server/shard-*.txt
|
||||
@@ -10,3 +10,9 @@ When creating a pull request, follow `.github/PULL_REQUEST_TEMPLATE.md` exactly:
|
||||
- Remove all `<!-- -->` comments.
|
||||
- Omit sections that are not applicable (Ticket Link, Screenshots) — do not write N/A, just remove the header.
|
||||
- The `#### Release Note` header and its "```release-note" fenced code block **must always be present** (WITHOUT escaping the ``` characters). Write `NONE` if the change has no API, schema, UI, or breaking changes.
|
||||
|
||||
## Cursor Cloud Agents
|
||||
|
||||
This repository has a checked-in Cloud Agent environment under `.cursor/`. Docker is started by `.cursor/scripts/cloud-agent-start.sh`; if Docker is unavailable in Cloud, treat that as an environment failure rather than falling back to snapshot assumptions.
|
||||
|
||||
The environment declares `mattermost/enterprise` as a Cursor multi-repo dependency. Cursor clones the repositories as siblings, so `server/Makefile` can use its default `../../enterprise` path; the install hook does not clone or symlink enterprise.
|
||||
Reference in new issue
Block a user