snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
166
.cursor/Dockerfile
Normal file
166
.cursor/Dockerfile
Normal file
@@ -0,0 +1,166 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
# Keep in sync with server/.go-version. cloud-agent-install.sh reinstalls if they drift.
|
||||
ARG GO_VERSION=1.26.7
|
||||
# Image pin on the .nvmrc line (currently 24.11). cloud-agent-install.sh uses .nvmrc at bootstrap.
|
||||
ARG NODE_VERSION=24.11.1
|
||||
ARG NVM_VERSION=0.40.3
|
||||
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'" && \
|
||||
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
|
||||
|
||||
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
|
||||
40
.cursor/README.md
Normal file
40
.cursor/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 from `server/.go-version` (currently 1.26.7). The install hook installs that version if the image is stale or the agent did not boot from this Dockerfile.
|
||||
- Node from `.nvmrc` (currently 24.11, image pin 24.11.1) via nvm. The install hook installs that version if the image is stale or the agent did not boot from this Dockerfile.
|
||||
- Browser runtime libraries for the Playwright e2e suite.
|
||||
- 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, locates or clones `mattermost/enterprise`, 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, starts Docker, waits until `docker info` and `docker compose version` succeed, then logs in to Docker Hub when credentials are configured.
|
||||
|
||||
The environment declares `github.com/mattermost/enterprise` in `repositoryDependencies` so Cursor can provide it as part of the multi-repo workspace and so the Cloud Agent GitHub token can read that private repo. Cursor currently clones the repositories as siblings when you explicitly select the multi-repo environment, such as `/agent/repos/mattermost` and `/agent/repos/enterprise`, which matches `server/Makefile`'s default `../../enterprise` path.
|
||||
|
||||
Git-triggered automations (for example, a PR label) still launch in a single-repo layout. `repositoryDependencies` only adds enterprise to the token scope in that mode; it does not check the repo out. The install hook therefore clones `https://github.com/mattermost/enterprise.git` into `../enterprise` when no usable checkout is present. It uses `ENTERPRISE_BRANCH` when set, otherwise preferring an exact branch matching the Mattermost checkout and falling back to Enterprise's default branch. If the sibling path is not writable or is not a usable work tree (for example a bare repo), the hook clones into `$HOME/enterprise` and writes `BUILD_ENTERPRISE_DIR` to `server/config.override.mk`. `server/Makefile` includes that file before Enterprise detection so `make run-server` honors the fallback path.
|
||||
|
||||
## 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`
|
||||
|
||||
## Expected Secrets
|
||||
|
||||
Configure these in the [Cursor Cloud Agents dashboard](https://cursor.com/dashboard/cloud-agents) as environment-scoped secrets for the Mattermost Cloud Agent environment.
|
||||
|
||||
- AWS uploads use the standard AWS CLI environment variables: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_S3_BUCKET_NAME`. The image only supplies the `aws` binary.
|
||||
- Docker Hub pulls use the same variable names as CI: `DOCKERHUB_USERNAME` and `DOCKERHUB_TOKEN`. The start hook runs `docker login` after `dockerd` is ready. Mark `DOCKERHUB_TOKEN` as **redacted** in the dashboard. When both are set, agents can pull the full default `make start-docker` image set without hitting anonymous rate limits.
|
||||
125
.cursor/cursor.md
Normal file
125
.cursor/cursor.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# 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, and AWS CLI v2.
|
||||
- Prefer Cursor's multi-repo layout for `mattermost/enterprise` when it is available: sibling repositories such as `/agent/repos/mattermost` and `/agent/repos/enterprise`, matching `server/Makefile`'s default `../../enterprise` path.
|
||||
- Git-triggered automations start as a single-repo checkout. If enterprise is missing, `cloud-agent-install.sh` clones it with the token scoped by `repositoryDependencies`, preferring an exact like-named branch and falling back to Enterprise's default branch. If that clone lands somewhere other than `../enterprise`, `BUILD_ENTERPRISE_DIR` is written to `server/config.override.mk`, which `server/Makefile` reads before Enterprise detection.
|
||||
|
||||
## 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.
|
||||
- When `DOCKERHUB_USERNAME` and `DOCKERHUB_TOKEN` are configured as Cloud Agent secrets, `cloud-agent-start.sh` logs in to Docker Hub and the full default `make start-docker` dependency set can be used without trimming services.
|
||||
- `ENABLED_DOCKER_SERVICES='postgres redis'` avoids optional local-dev services such as Prometheus, Grafana, Loki, Minio, Azurite, and OpenLDAP. Use this fallback when Docker Hub credentials are unavailable and anonymous pulls hit rate limits.
|
||||
- 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.
|
||||
|
||||
### Playwright rolling-upgrade tests
|
||||
|
||||
Rolling-upgrade coverage lives in its own CI pipeline (`e2e-tests-playwright-rolling-upgrades.yml`),
|
||||
not in `e2e-tests-playwright-template.yml`. Locally, only the harness runs, and `upgrade-to` adopts
|
||||
the stack `upgrade-from` created, so Testcontainers reuse must be on
|
||||
(`echo testcontainers.reuse.enable=true >> ~/.testcontainers.properties`; see
|
||||
`e2e-tests/playwright/lib/README.md`):
|
||||
|
||||
```bash
|
||||
cd e2e-tests/playwright
|
||||
npm run testcontainers:down
|
||||
MM_LICENSE='<your-license-key>' PW_UPGRADE_FROM_SERVER_IMAGE=mattermostdevelopment/mattermost-enterprise-edition:release-11.9 npm run test:upgrade:from
|
||||
SERVER_IMAGE=mattermostdevelopment/mattermost-enterprise-edition:master npm run test:upgrade:to
|
||||
npm run testcontainers:down
|
||||
```
|
||||
|
||||
- `script/resolve_upgrade_matrix.mjs` prints the from-version matrix (`dockerTag`, `isESR`,
|
||||
`contextLabel`), or `[]` when none are supported.
|
||||
- CI runs on release cut and on demand via the workflow's **Run workflow** button — not on merge. A
|
||||
PR runs it by enabling `run_rolling_upgrades` on a manual `e2e-tests-ci.yml` dispatch, or
|
||||
automatically when it touches the harness.
|
||||
- The entry workflow calls the template once per from-version. Each worker upgrades, then continues
|
||||
into the full suite with no re-preparation, so the suite runs against an upgraded server. One
|
||||
commit status per from-version.
|
||||
|
||||
## Browser Verification
|
||||
|
||||
Use the `computerUse` subagent's desktop (Chrome is preinstalled) for browser automation and screenshots. 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.
|
||||
23
.cursor/environment.json
Normal file
23
.cursor/environment.json
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
578
.cursor/scripts/cloud-agent-install.sh
Executable file
578
.cursor/scripts/cloud-agent-install.sh
Executable file
@@ -0,0 +1,578 @@
|
||||
#!/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
|
||||
}
|
||||
|
||||
is_git_work_tree() {
|
||||
[ "$(git -C "${1:-}" rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ]
|
||||
}
|
||||
|
||||
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
NVM_VERSION="${CLOUD_AGENT_NVM_VERSION:-0.40.3}"
|
||||
|
||||
export GOPATH="${GOPATH:-$HOME/go}"
|
||||
export PATH="/usr/local/go/bin:$GOPATH/bin:/usr/local/bin:$PATH"
|
||||
|
||||
required_go_version() {
|
||||
local version_file="$ROOT/server/.go-version"
|
||||
if [ ! -f "$version_file" ]; then
|
||||
log "server/.go-version is missing; cannot determine the required Go version."
|
||||
return 1
|
||||
fi
|
||||
tr -d '[:space:]' < "$version_file"
|
||||
}
|
||||
|
||||
current_go_version() {
|
||||
command -v go >/dev/null 2>&1 || return 1
|
||||
go version | awk '{print $3}' | sed 's/^go//'
|
||||
}
|
||||
|
||||
install_go() {
|
||||
local version="$1"
|
||||
local go_arch
|
||||
case "$(dpkg --print-architecture)" in
|
||||
amd64) go_arch=amd64 ;;
|
||||
arm64) go_arch=arm64 ;;
|
||||
*)
|
||||
log "Unsupported Go architecture: $(dpkg --print-architecture)"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
local tarball="/tmp/go${version}.linux-${go_arch}.tar.gz"
|
||||
log "Downloading Go ${version} (${go_arch})."
|
||||
curl --retry 3 --retry-delay 5 -fsSL "https://go.dev/dl/go${version}.linux-${go_arch}.tar.gz" -o "$tarball"
|
||||
sudo rm -rf /usr/local/go
|
||||
sudo tar -C /usr/local -xzf "$tarball"
|
||||
sudo ln -sfnT /usr/local/go/bin/go /usr/local/bin/go
|
||||
sudo ln -sfnT /usr/local/go/bin/gofmt /usr/local/bin/gofmt
|
||||
rm -f "$tarball"
|
||||
}
|
||||
|
||||
persist_go_path() {
|
||||
local marker="# mattermost-cloud-agent-go-path"
|
||||
local rc="${HOME}/.bashrc"
|
||||
if [ -f "$rc" ] && grep -Fq "$marker" "$rc"; then
|
||||
return 0
|
||||
fi
|
||||
{
|
||||
echo "$marker"
|
||||
echo 'export GOPATH="${GOPATH:-$HOME/go}"'
|
||||
echo 'export PATH="/usr/local/go/bin:$GOPATH/bin:$PATH"'
|
||||
} >> "$rc"
|
||||
}
|
||||
|
||||
ensure_go() {
|
||||
export GOPATH="${GOPATH:-$HOME/go}"
|
||||
export PATH="/usr/local/go/bin:/usr/local/bin:${GOPATH}/bin:${PATH}"
|
||||
|
||||
local required current
|
||||
required="$(required_go_version)"
|
||||
current="$(current_go_version || true)"
|
||||
|
||||
if [ "$current" != "$required" ]; then
|
||||
log "Go ${current:-not found} does not match server/.go-version (${required}); installing."
|
||||
install_go "$required"
|
||||
hash -r
|
||||
persist_go_path
|
||||
current="$(current_go_version || true)"
|
||||
fi
|
||||
|
||||
if [ "$current" != "$required" ]; then
|
||||
log "Go is ${current:-not found} after install; expected ${required}."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Using $(go version)"
|
||||
}
|
||||
|
||||
required_node_version() {
|
||||
if [ -n "${CLOUD_AGENT_NODE_VERSION:-}" ]; then
|
||||
printf '%s\n' "${CLOUD_AGENT_NODE_VERSION#v}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local version_file="$ROOT/.nvmrc"
|
||||
if [ ! -f "$version_file" ]; then
|
||||
log ".nvmrc is missing; cannot determine the required Node version."
|
||||
return 1
|
||||
fi
|
||||
tr -d '[:space:]' < "$version_file" | sed 's/^v//'
|
||||
}
|
||||
|
||||
current_node_version() {
|
||||
command -v node >/dev/null 2>&1 || return 1
|
||||
node --version | sed 's/^v//'
|
||||
}
|
||||
|
||||
# .nvmrc may pin a major or major.minor (e.g. 24.11); node --version is always
|
||||
# major.minor.patch. Require an exact match on every component the pin specifies.
|
||||
node_version_matches() {
|
||||
local required="$1"
|
||||
local current="$2"
|
||||
if [ -z "$required" ] || [ -z "$current" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local r1 r2 r3 c1 c2 c3
|
||||
IFS=. read -r r1 r2 r3 <<< "$required"
|
||||
IFS=. read -r c1 c2 c3 <<< "$current"
|
||||
[ "$c1" = "$r1" ] || return 1
|
||||
[ -z "${r2:-}" ] || [ "$c2" = "$r2" ] || return 1
|
||||
[ -z "${r3:-}" ] || [ "$c3" = "$r3" ] || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
ensure_nvm() {
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
||||
# shellcheck source=/dev/null
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "nvm is not installed; cloning v${NVM_VERSION}."
|
||||
git clone --depth 1 --branch "v${NVM_VERSION}" https://github.com/nvm-sh/nvm.git "$NVM_DIR"
|
||||
# shellcheck source=/dev/null
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
}
|
||||
|
||||
link_node_bins() {
|
||||
local resolved="$1"
|
||||
local bindir="${NVM_DIR}/versions/node/v${resolved}/bin"
|
||||
if [ ! -x "${bindir}/node" ]; then
|
||||
log "nvm Node binary not found at ${bindir}/node."
|
||||
return 1
|
||||
fi
|
||||
sudo ln -sfn "${bindir}/node" /usr/local/bin/node
|
||||
sudo ln -sfn "${bindir}/npm" /usr/local/bin/npm
|
||||
sudo ln -sfn "${bindir}/npx" /usr/local/bin/npx
|
||||
}
|
||||
|
||||
persist_node_path() {
|
||||
local marker="# mattermost-cloud-agent-node-path"
|
||||
local rc="${HOME}/.bashrc"
|
||||
if [ -f "$rc" ] && grep -Fq "$marker" "$rc"; then
|
||||
return 0
|
||||
fi
|
||||
{
|
||||
echo "$marker"
|
||||
printf 'export NVM_DIR=%q\n' "${NVM_DIR:-$HOME/.nvm}"
|
||||
echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"'
|
||||
} >> "$rc"
|
||||
}
|
||||
|
||||
install_node() {
|
||||
local version="$1"
|
||||
ensure_nvm
|
||||
log "Installing Node ${version} via nvm."
|
||||
nvm install "$version"
|
||||
nvm alias default "$version"
|
||||
nvm use "$version"
|
||||
|
||||
local resolved
|
||||
resolved="$(current_node_version)"
|
||||
link_node_bins "$resolved"
|
||||
}
|
||||
|
||||
ensure_node() {
|
||||
local required current
|
||||
required="$(required_node_version)"
|
||||
ensure_nvm
|
||||
nvm use "$required" >/dev/null 2>&1 || true
|
||||
|
||||
current="$(current_node_version || true)"
|
||||
if ! node_version_matches "$required" "$current"; then
|
||||
log "Node ${current:-not found} does not match .nvmrc (${required}); installing."
|
||||
install_node "$required"
|
||||
hash -r
|
||||
persist_node_path
|
||||
current="$(current_node_version || true)"
|
||||
fi
|
||||
|
||||
if ! node_version_matches "$required" "$current"; then
|
||||
log "Node is ${current:-not found} after install; expected ${required}."
|
||||
return 1
|
||||
fi
|
||||
|
||||
link_node_bins "$current"
|
||||
persist_node_path
|
||||
|
||||
if ! command -v npm >/dev/null 2>&1; then
|
||||
log "npm is not available after Node ${current} install."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Using node $(node --version) and npm $(npm --version)"
|
||||
}
|
||||
|
||||
enterprise_build_dir() {
|
||||
case "$BUILD_ENTERPRISE_DIR" in
|
||||
/*) realpath -m "$BUILD_ENTERPRISE_DIR" ;;
|
||||
*) realpath -m "$ROOT/server/$BUILD_ENTERPRISE_DIR" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
default_enterprise_checkout() {
|
||||
realpath -m "$ROOT/../enterprise"
|
||||
}
|
||||
|
||||
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
|
||||
if [ -n "${BUILD_ENTERPRISE_DIR:-}" ]; then
|
||||
case "$BUILD_ENTERPRISE_DIR" in
|
||||
/*) candidates+=("$BUILD_ENTERPRISE_DIR") ;;
|
||||
*) candidates+=("$ROOT/server/$BUILD_ENTERPRISE_DIR") ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
candidates+=(
|
||||
"$(default_enterprise_checkout)"
|
||||
"$ROOT/../../enterprise"
|
||||
"$HOME/enterprise"
|
||||
)
|
||||
|
||||
local candidate
|
||||
for candidate in "${candidates[@]}"; do
|
||||
if is_git_work_tree "$candidate"; then
|
||||
realpath -m "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
enterprise_clone_url() {
|
||||
printf 'https://github.com/mattermost/enterprise.git\n'
|
||||
}
|
||||
|
||||
requested_enterprise_branch() {
|
||||
local branch
|
||||
if [ -n "${ENTERPRISE_BRANCH:-}" ]; then
|
||||
branch="$ENTERPRISE_BRANCH"
|
||||
elif ! branch="$(git -C "$ROOT" symbolic-ref --quiet --short HEAD 2>/dev/null)"; then
|
||||
return 1
|
||||
fi
|
||||
if ! git check-ref-format --branch "$branch" >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$branch"
|
||||
}
|
||||
|
||||
enterprise_branch_exists() {
|
||||
local branch="$1"
|
||||
local url
|
||||
url="$(enterprise_clone_url)"
|
||||
|
||||
GIT_TERMINAL_PROMPT=0 git ls-remote --exit-code --heads "$url" "refs/heads/$branch" >/dev/null 2>&1 && return 0
|
||||
local status=$?
|
||||
if [ "$status" -eq 2 ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! command -v gh >/dev/null 2>&1; then
|
||||
return 2
|
||||
fi
|
||||
|
||||
local exists
|
||||
if ! exists="$(
|
||||
gh api graphql \
|
||||
-f query="query(\$qualifiedName: String!) { repository(owner: \"mattermost\", name: \"enterprise\") { ref(qualifiedName: \$qualifiedName) { id } } }" \
|
||||
-f qualifiedName="refs/heads/$branch" \
|
||||
--jq '.data.repository.ref != null' 2>/dev/null
|
||||
)"; then
|
||||
return 2
|
||||
fi
|
||||
|
||||
[ "$exists" = "true" ]
|
||||
}
|
||||
|
||||
matching_enterprise_branch() {
|
||||
local branch
|
||||
if ! branch="$(requested_enterprise_branch)"; then
|
||||
log "No valid Enterprise branch was requested; using Enterprise's default branch."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local status
|
||||
if enterprise_branch_exists "$branch"; then
|
||||
printf '%s\n' "$branch"
|
||||
return 0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
|
||||
if [ "$status" -eq 1 ]; then
|
||||
log "Enterprise branch $branch does not exist; using Enterprise's default branch."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Could not verify Enterprise branch $branch; attempting it before the default branch."
|
||||
printf '%s\n' "$branch"
|
||||
}
|
||||
|
||||
can_clone_into() {
|
||||
local dest="$1"
|
||||
local parent
|
||||
parent="$(dirname "$dest")"
|
||||
if [ ! -d "$parent" ] || [ ! -w "$parent" ] || [ ! -x "$parent" ]; then
|
||||
return 1
|
||||
fi
|
||||
if [ -L "$dest" ]; then
|
||||
return 1
|
||||
fi
|
||||
if [ ! -e "$dest" ]; then
|
||||
return 0
|
||||
fi
|
||||
if is_git_work_tree "$dest"; then
|
||||
return 0
|
||||
fi
|
||||
if [ ! -d "$dest" ] || [ ! -w "$dest" ] || [ ! -x "$dest" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local entries
|
||||
if ! entries="$(ls -A "$dest" 2>/dev/null)"; then
|
||||
return 1
|
||||
fi
|
||||
[ -z "$entries" ]
|
||||
}
|
||||
|
||||
publish_enterprise_checkout() {
|
||||
local source="$1"
|
||||
local dest="$2"
|
||||
|
||||
if mv -T -n "$source" "$dest" && [ ! -e "$source" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
rm -rf "$source"
|
||||
if is_git_work_tree "$dest"; then
|
||||
log "Another process populated Enterprise checkout at $dest; reusing it."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Could not publish Enterprise checkout at $dest."
|
||||
return 1
|
||||
}
|
||||
|
||||
clone_enterprise_ref() {
|
||||
local dest="$1"
|
||||
local url="$2"
|
||||
shift 2
|
||||
local clone_args=("$@")
|
||||
local temp
|
||||
|
||||
if ! temp="$(mktemp -d "$(dirname "$dest")/.enterprise-clone.XXXXXX")"; then
|
||||
return 1
|
||||
fi
|
||||
if GIT_TERMINAL_PROMPT=0 git clone "${clone_args[@]}" "$url" "$temp"; then
|
||||
publish_enterprise_checkout "$temp" "$dest"
|
||||
return $?
|
||||
fi
|
||||
rm -rf "$temp"
|
||||
|
||||
if ! command -v gh >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "git clone failed; retrying with GitHub CLI."
|
||||
if ! temp="$(mktemp -d "$(dirname "$dest")/.enterprise-clone.XXXXXX")"; then
|
||||
return 1
|
||||
fi
|
||||
if gh repo clone mattermost/enterprise "$temp" -- "${clone_args[@]}"; then
|
||||
publish_enterprise_checkout "$temp" "$dest"
|
||||
return $?
|
||||
fi
|
||||
rm -rf "$temp"
|
||||
return 1
|
||||
}
|
||||
|
||||
enterprise_clone_dest() {
|
||||
local sibling
|
||||
sibling="$(default_enterprise_checkout)"
|
||||
if can_clone_into "$sibling"; then
|
||||
printf '%s\n' "$sibling"
|
||||
return 0
|
||||
fi
|
||||
|
||||
realpath -m "$HOME/enterprise"
|
||||
}
|
||||
|
||||
clone_enterprise_checkout() {
|
||||
local dest="$1"
|
||||
local url
|
||||
url="$(enterprise_clone_url)"
|
||||
|
||||
if [ -z "$dest" ] || [ "$dest" = "/" ] || [ "$(basename "$dest")" != "enterprise" ]; then
|
||||
log "Refusing unsafe enterprise clone dest: ${dest:-<empty>}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! can_clone_into "$dest"; then
|
||||
log "Refusing unusable enterprise clone dest: $dest"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -e "$dest" ]; then
|
||||
if is_git_work_tree "$dest"; then
|
||||
return 0
|
||||
fi
|
||||
rmdir "$dest"
|
||||
fi
|
||||
|
||||
log "Enterprise checkout missing; cloning mattermost/enterprise into $dest."
|
||||
log "Git-triggered automations stay single-repo; repositoryDependencies only scopes the GitHub token."
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
|
||||
local clone_args=(--depth 1 --single-branch)
|
||||
local branch
|
||||
if branch="$(matching_enterprise_branch)"; then
|
||||
clone_args+=(--branch "$branch")
|
||||
log "Trying matching Enterprise branch $branch."
|
||||
fi
|
||||
|
||||
if clone_enterprise_ref "$dest" "$url" "${clone_args[@]}"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -n "${branch:-}" ]; then
|
||||
log "Could not clone matching Enterprise branch $branch; retrying with the default branch."
|
||||
clone_args=(--depth 1 --single-branch)
|
||||
clone_enterprise_ref "$dest" "$url" "${clone_args[@]}" && return 0
|
||||
fi
|
||||
|
||||
log "Failed to clone mattermost/enterprise. repositoryDependencies must include github.com/mattermost/enterprise so the GitHub token can access it."
|
||||
return 1
|
||||
}
|
||||
|
||||
persist_build_enterprise_dir() {
|
||||
local target="$1"
|
||||
export BUILD_ENTERPRISE_DIR="$target"
|
||||
|
||||
local default_target
|
||||
default_target="$(default_enterprise_checkout)"
|
||||
if [ "$target" = "$default_target" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local override="$ROOT/server/config.override.mk"
|
||||
if [ -f "$override" ] && grep -q '^BUILD_ENTERPRISE_DIR' "$override"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf 'BUILD_ENTERPRISE_DIR := %s\n' "$target" >> "$override"
|
||||
log "Wrote BUILD_ENTERPRISE_DIR to $override so make uses the cloned checkout."
|
||||
}
|
||||
|
||||
ensure_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
|
||||
persist_build_enterprise_dir "$target"
|
||||
log "Enterprise checkout ready at $target."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local dest
|
||||
dest="$(enterprise_clone_dest)"
|
||||
clone_enterprise_checkout "$dest"
|
||||
target="$(realpath -m "$dest")"
|
||||
if ! is_git_work_tree "$target"; then
|
||||
log "Enterprise clone completed but checkout was not found at $dest."
|
||||
return 1
|
||||
fi
|
||||
|
||||
persist_build_enterprise_dir "$target"
|
||||
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
|
||||
ensure_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."
|
||||
100
.cursor/scripts/cloud-agent-start.sh
Executable file
100
.cursor/scripts/cloud-agent-start.sh
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/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
|
||||
}
|
||||
|
||||
docker_login_if_configured() {
|
||||
if [ -z "${DOCKERHUB_USERNAME:-}" ] || [ -z "${DOCKERHUB_TOKEN:-}" ]; then
|
||||
log "Docker Hub credentials not configured; anonymous pulls may hit rate limits."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Logging in to Docker Hub as ${DOCKERHUB_USERNAME}."
|
||||
if echo "${DOCKERHUB_TOKEN}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin >/tmp/docker-login.log 2>&1; then
|
||||
log "Docker Hub login succeeded."
|
||||
else
|
||||
log "Docker Hub login failed; see /tmp/docker-login.log."
|
||||
tail -n 20 /tmp/docker-login.log >&2 || 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
|
||||
docker_login_if_configured
|
||||
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
|
||||
docker_login_if_configured
|
||||
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
|
||||
Reference in New Issue
Block a user