Skip to main content

Command Palette

Search for a command to run...

Origin

Настройка CloneKit в CI с помощью origin repo clone-fast

origin repo clone-fast заменяет git clone в CI-задачах. Вместо клонирования с нуля команда загружает заранее подготовленный набор для клонирования — упакованный snapshot репозитория, который Cursor собирает для каждого репозитория с включённым CloneKit, — а затем догружает только те объекты, которые появились после сборки набора. На этой странице показано, как передать на runner короткоживущий токен Origin и запустить команду в Buildkite, GitHub Actions и других CI-системах.

Как это работает

Каждый запуск origin repo clone-fast {owner}/{repo} [DIR] выполняет следующее:

  1. Получает манифест kit из Origin, используя токен из CURSOR_AUTH_TOKEN. В манифесте перечислены артефакты kit и коммит на его tip.
  2. Загружает pack-артефакты с gitcdn.origin.cursor.com и проверяет их размеры. С флагом --verify по ходу загрузки проверяются также их хеши.
  3. Устанавливает их в каталог назначения, который должен быть пустым.
  4. Дополняет клон: получает объекты, добавленные после сборки kit, и переключает рабочую копию на текущий tip ветки по умолчанию. С флагом --no-top-up рабочая копия остаётся на tip из kit. Флаг --bare создаёт голый репозиторий без working tree.

Если путь через kit срабатывает, команда выводит mode: clone-kit в stdout. Если какой-либо шаг завершается ошибкой, то при значении по умолчанию --fallback auto вместо него выполняется обычный git clone и выводится mode: git-clone. Укажите --fallback never, чтобы вместо этого завершить работу с кодом 1.

Токен и рабочая копия ведут себя одинаково в любой системе CI:

  • Срок действия токенов — не более 15 минут, и обновить их нельзя. Выпускайте токен непосредственно перед клонированием.
  • CLI читает токен из CURSOR_AUTH_TOKEN. Экспортируйте его в shell, где выполняется origin.
  • Клонирование завершается на tip ветки по умолчанию. Чтобы собрать конкретный коммит, выполните затем git fetch origin SHA и git checkout SHA. Origin отдаёт любой достижимый коммит по SHA.

Prerequisites

  • Origin CLI на runner. Установите его командой curl -fsSL https://downloads.cursor.com/origin/install.sh | sh — она размещает исполняемый файл в $HOME/.local/bin/origin. Runner на Linux требуют glibc на x64 или arm64; Alpine и другие образы на musl не поддерживаются. Runner на macOS поддерживаются. См. Install the Origin CLI.
  • bash, git, jq, curl 7.55 или новее и openssl 1.1.1 или новее. clone-fast использует curl для загрузок.
  • Сетевой egress к следующим хостам:
ХостДля чего используется
downloads.cursor.comУстановка CLI
origin.cursor.comМанифест kit и git operations
gitcdn.origin.cursor.comЗагрузка kit
api.cursor.comВыпуск токенов и запросы синхронизации mirror от Origin App

Выберите путь

CI-системаОткуда берётся токенРаздел
Buildkite, hosted или self-hosted агенты, с Origin, подключённым как repository providerBuildkite Agent API, в хуке checkoutBuildkite
GitHub ActionsВаш Origin App, в шаге workflowGitHub Actions
GitLab CI, Jenkins, CircleCI и self-hosted fleetВаш Origin App, в задачеДругие CI-системы

Buildkite

Buildkite выпускает токен за вас. Замените стандартный git checkout агента на хук checkout, который запрашивает токен у Buildkite Agent API и выполняет origin repo clone-fast.

Чтобы подключить Origin, нужно быть администратором организации в Buildkite, а чтобы установить приложение Buildkite — администратором Origin.

1

Подключите Origin к Buildkite

В Buildkite выберите Settings > Repository Providers > Add Provider > Origin или нажмите Connect Origin account на странице New Pipeline. Выберите владельца и репозитории, затем установите приложение Buildkite. Buildkite запрашивает доступ на чтение содержимого репозитория и pull request'ов, а также доступ на чтение и запись к проверкам. Подробнее см. Origin в документации Buildkite.

2

Установите Origin CLI на агенте

Выполните шаги из раздела Предварительные требования; curl, git и jq должны быть в PATH.

3

Держите каталог рабочей копии пустым

Если агент сохраняет каталог сборки между сборками, очистите $BUILDKITE_BUILD_CHECKOUT_PATH до запуска хука. Команде clone-fast нужен пустой каталог, и при наличии в нём файлов она переключается на git clone.

4

Добавьте хук checkout

На self-hosted агенте сохраните приведённый ниже скрипт как checkout в каталоге --hooks-path агента. На агентах, размещённых в Buildkite, поставляйте его как хук checkout не-vendored плагина либо задайте checkout: { skip: true } на шаге и выполните те же команды в его command. Хук репозитория не может определять checkout, поскольку репозиторий ещё не выгружен. См. Agent hooks и Git checkout в документации Buildkite.

Хук запрашивает токен, ограниченный репозиторием пайплайна, экспортирует его как CURSOR_AUTH_TOKEN, клонирует репозиторий через clone-fast, регистрирует credential helper для git командой origin auth setup-git и выгружает $BUILDKITE_COMMIT. Токен агента передаётся в curl через stdin, поэтому он никогда не появляется в командной строке:

#!/usr/bin/env bashset -euo pipefailbody=$(printf '{"repo_url":"%s"}' "$BUILDKITE_REPO")token=$(printf 'Authorization: Token %s\n' "$BUILDKITE_AGENT_ACCESS_TOKEN" \  | curl -fsS -X POST -H @- \      -H 'Content-Type: application/json' -H 'Accept: application/json' \      --data "$body" \      "${BUILDKITE_AGENT_ENDPOINT%/}/jobs/${BUILDKITE_JOB_ID}/cursor_origin_access_token" \  | jq -er '.token')# clone-fast принимает owner/repo, а не clone URL.repo=${BUILDKITE_REPO#https://origin.cursor.com/}repo=${repo#git/}repo=${repo%.git}export CURSOR_AUTH_TOKEN="$token"origin repo clone-fast "$repo" "$BUILDKITE_BUILD_CHECKOUT_PATH" --verifyorigin auth setup-gitcd "$BUILDKITE_BUILD_CHECKOUT_PATH"git fetch origin "$BUILDKITE_COMMIT"git checkout -q "$BUILDKITE_COMMIT"

Когда build запускается без commit, BUILDKITE_COMMIT равен HEAD. Две строки git затем выполняют fetch remote HEAD и оставляют working tree на tip default branch, который clone-fast уже checked out.

  • Передавайте $BUILDKITE_REPO без изменений. repo_url должен в точности совпадать с URL-адресом репозитория, зарегистрированным в pipeline, включая .git. При другом написании возвращается HTTP 400.
  • Token является read-only и scoped на repository данного pipeline. Он содержит repository:contents:read и истекает через 15 минут. Для последующего job или для git operation спустя более 15 минут после выпуска потребуется новый token из того же request.
  • Повторяйте попытку при 503. Если Agent API отвечает HTTP 503, дождитесь времени, указанного в header Retry-After, и повторите попытку — так же, как это делает собственный credential helper Buildkite.

GitHub Actions и другие CI-поставщики

GitHub Actions и другие CI-системы выпускают собственные токены. Вы один раз создаёте Origin App, устанавливаете его в репозитории, которые клонируете, и передаёте каждому job приватный ключ приложения и два идентификатора. Job подписывает короткоживущий app JWT и обменивает его на installation token. Полное описание полей см. в разделах App JWT и Create Installation Access Token.

Создание Origin App

Для установки приложения необходимы права администратора рабочей области.

Регистрация приложения

1

Сгенерируйте пару ключей Ed25519

Принимаются только ключи Ed25519:

openssl genpkey -algorithm ED25519 -out origin-app-private.pemopenssl pkey -in origin-app-private.pem -pubout -out origin-app-public.pem
2

Создайте приложение и добавьте открытый ключ

В настройках приложений Origin создайте приложение и добавьте содержимое файла origin-app-public.pem в качестве ключа подписи. К приложению можно привязать до 10 активных ключей подписи.

3

Скопируйте идентификатор приложения

Идентификатор приложения указан на странице приложения. Идентификаторы приложения начинаются с app_.

Установка приложения

1

Установите приложение для своего владельца

На странице установки приложения в тех же настройках установите приложение для владельца, которому принадлежат клонируемые репозитории, и выберите эти репозитории.

2

Скопируйте installation id

Installation id указан в URL страницы установки: /codebase/settings/apps/installations/{installationId}. Installation id начинаются с i_.

Хранение credentials

Сохраните приватный ключ как секрет в вашей системе CI и передайте его в job через переменную окружения ORIGIN_APP_PRIVATE_KEY, содержащую полный текст PEM. Идентификатор приложения и installation id сохраните как обычные переменные ORIGIN_APP_ID и ORIGIN_INSTALLATION_ID. Если ваша система CI монтирует секреты как файлы, сначала загрузите ключ командой ORIGIN_APP_PRIVATE_KEY=$(cat /path/to/origin-app-private.pem).

Выпуск токена в job

Приведённая ниже функция подписывает app JWT и обменивает его на installation token. В JWT используется alg EdDSA, поля iss и kid задаются равными идентификатору приложения, aud — origin-apps, а exp — на 15 минут вперёд. В справочнике App JWT рекомендуется время жизни около пяти минут, но поскольку installation token не живёт дольше выпустившего его JWT, здесь подписывается JWT на 15 минут — чтобы у токена были все свои 15 минут. В запросе запрашивается repository:contents:read, чего достаточно для clone, fetch и pull. Для push нужен repository:contents:write, причём этот scope должен быть предоставлен при установке. Добавьте "repositoryIds":[...] в request body, чтобы ограничить токен частью репозиториев установки.

Приватный ключ передаётся в openssl через файловый дескриптор, а bearer-header — в curl через stdin, поэтому ни то, ни другое не появляется в командной строке, где их могли бы прочитать другие процессы на runner. Функция задаёт и экспортирует CURSOR_AUTH_TOKEN напрямую, так что токен нигде не сохраняется в файл, а неудачный выпуск останавливает job при включённом set -e. Потребуются bash, openssl 1.1.1 или новее, curl 7.55 или новее и jq:

origin_app_token() {  b64url() { openssl base64 -A | tr '+/' '-_' | tr -d '='; }  now=$(date +%s)  header=$(printf '{"alg":"EdDSA","kid":"%s","typ":"JWT"}' "$ORIGIN_APP_ID" | b64url)  claims=$(printf '{"iss":"%s","aud":"origin-apps","iat":%d,"exp":%d}' \    "$ORIGIN_APP_ID" "$now" "$((now + 900))" | b64url)  # openssl -rawin требует входные данные с поддержкой позиционирования; подписываемые данные не содержат секретов.  signing_input=$(mktemp)  trap 'rm -f "$signing_input"' EXIT  printf '%s.%s' "$header" "$claims" > "$signing_input"  signature=$(openssl pkeyutl -sign -rawin -in "$signing_input" \    -inkey <(printf '%s\n' "$ORIGIN_APP_PRIVATE_KEY") | b64url)  app_jwt="$header.$claims.$signature"  CURSOR_AUTH_TOKEN=$(printf 'Authorization: Bearer %s\n' "$app_jwt" \    | curl -fsS -X POST -H @- -H 'Content-Type: application/json' \        --data '{"scopes":["repository:contents:read"]}' \        "https://api.cursor.com/v1/origin/app/installations/${ORIGIN_INSTALLATION_ID}/access_tokens" \    | jq -er '.token')  export CURSOR_AUTH_TOKEN}

Вызовите её непосредственно перед клонированием в пустой каталог, а затем зарегистрируйте git credential helper, чтобы последующие команды git проходили аутентификацию, пока CURSOR_AUTH_TOKEN остаётся экспортированным. Замените acme/widgets на свой репозиторий, а COMMIT_SHA — на коммит, который ваша CI-система предоставляет для сборки:

set -euo pipefailorigin_app_tokenorigin repo clone-fast acme/widgets . --verifyorigin auth setup-gitgit fetch origin "$COMMIT_SHA"git checkout -q "$COMMIT_SHA"

GitHub Actions

GitHub Actions использует вариант с Origin App: приватный ключ хранится в секрете Actions, а два идентификатора — в переменных репозитория. Поскольку workflow запускает GitHub, репозиторий находится на GitHub, а Origin хранит его как зеркало. Workflow просит Origin подтянуть коммит сборки, клонирует репозиторий командой clone-fast в пустой $GITHUB_WORKSPACE, а затем переключается на этот коммит. Он вообще не обращается к GitHub, поэтому ему не нужны никакие права доступа GITHUB_TOKEN и не требуется actions/checkout.

1

Добавьте секрет

В своём репозитории выберите Settings > Secrets and variables > Actions, затем добавьте секрет ORIGIN_APP_PRIVATE_KEY с полным PEM приватного ключа.

2

Добавьте переменные

На той же странице добавьте переменные репозитория ORIGIN_APP_ID и ORIGIN_INSTALLATION_ID.

3

Добавьте workflow

Сохраните приведённый ниже workflow как .github/workflows/ci.yml, укажите в ORIGIN_REPO значение {owner}/{repo} зеркала на Origin и добавьте свои шаги сборки после шага клонирования.

Workflow устанавливает CLI, выпускает токен, ждёт, пока Origin отзеркалит коммит, и клонирует его:

name: cion:  push:    branches: [main]  pull_request:permissions: {}jobs:  build:    runs-on: ubuntu-latest    defaults:      run:        shell: bash    steps:      - name: Install the Origin CLI        run: |          curl -fsSL https://downloads.cursor.com/origin/install.sh | sh          echo "$HOME/.local/bin" >> "$GITHUB_PATH"      - name: Clone from Origin with clone-fast        env:          ORIGIN_APP_ID: ${{ vars.ORIGIN_APP_ID }}          ORIGIN_INSTALLATION_ID: ${{ vars.ORIGIN_INSTALLATION_ID }}          ORIGIN_APP_PRIVATE_KEY: ${{ secrets.ORIGIN_APP_PRIVATE_KEY }}          ORIGIN_REPO: acme/widgets          BUILD_BRANCH: ${{ github.head_ref || github.ref_name }}          BUILD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}        run: |          set -euo pipefail          origin_app_token() {            b64url() { openssl base64 -A | tr '+/' '-_' | tr -d '='; }            now=$(date +%s)            header=$(printf '{"alg":"EdDSA","kid":"%s","typ":"JWT"}' "$ORIGIN_APP_ID" | b64url)            claims=$(printf '{"iss":"%s","aud":"origin-apps","iat":%d,"exp":%d}' \              "$ORIGIN_APP_ID" "$now" "$((now + 900))" | b64url)            signing_input=$(mktemp)            trap 'rm -f "$signing_input"' EXIT            printf '%s.%s' "$header" "$claims" > "$signing_input"            signature=$(openssl pkeyutl -sign -rawin -in "$signing_input" \              -inkey <(printf '%s\n' "$ORIGIN_APP_PRIVATE_KEY") | b64url)            app_jwt="$header.$claims.$signature"            echo "::add-mask::$app_jwt"            CURSOR_AUTH_TOKEN=$(printf 'Authorization: Bearer %s\n' "$app_jwt" \              | curl -fsS -X POST -H @- -H 'Content-Type: application/json' \                  --data '{"scopes":["repository:contents:read"]}' \                  "https://api.cursor.com/v1/origin/app/installations/${ORIGIN_INSTALLATION_ID}/access_tokens" \              | jq -er '.token')            echo "::add-mask::$CURSOR_AUTH_TOKEN"            export CURSOR_AUTH_TOKEN          }          origin_app_token          # Origin зеркалирует GitHub с задержкой. Ждём этот коммит примерно до двух минут.          body=$(printf '{"ref":"refs/heads/%s","sha":"%s","wait":true}' "$BUILD_BRANCH" "$BUILD_SHA")          printf 'Authorization: Bearer %s\n' "$CURSOR_AUTH_TOKEN" \            | curl -fsS -X POST -H @- -H 'Content-Type: application/json' --data "$body" \                "https://api.cursor.com/v1/origin/repos/${ORIGIN_REPO}:syncMirror" \            | jq -e '.synced' > /dev/null \            || { echo "Origin has not mirrored $BUILD_SHA from $BUILD_BRANCH yet" >&2; exit 1; }          origin repo clone-fast "$ORIGIN_REPO" . --verify          origin auth setup-git          git fetch origin "$BUILD_SHA"          git checkout -q "$BUILD_SHA"
  • При pull_request workflow собирает head-ветку pull request. github.sha — это merge-коммит, который существует только на GitHub, поэтому BUILD_SHA и BUILD_BRANCH используют вместо него head-коммит и ветку pull request.
  • Запрос синхронизации — это Sync Mirror. Он принимает installation token, требует repository:contents:read и возвращает 200 с "synced": true либо 202 с "synced": false по истечении лимита ожидания около двух минут. Если source of truth — Origin, а GitHub является зеркалом, удалите запрос синхронизации: коммит уже есть в Origin, а Origin отклоняет запросы синхронизации для репозиториев, которые не подтягивают изменения из upstream-источника.
  • Оба credentials маскируются. ::add-mask:: скрывает app JWT и installation token в логе задачи.
  • Более поздние шаги выпускают токен заново. Более поздний шаг, которому нужен git-доступ к Origin, снова определяет и вызывает функцию. Токен никогда не записывается в $GITHUB_ENV или $GITHUB_OUTPUT — это файлы на диске.
  • Pull requests из форков не зеркалируются. Их head-ветки нет в вашем репозитории. Собирайте такие изменения из GitHub.

Другие CI-системы

GitLab CI, Jenkins, CircleCI и self-hosted fleet используют способ с Origin App напрямую. Сохраните приватный ключ и два идентификатора, как описано в разделе Store the credentials, затем вызовите функцию из раздела Mint a token in the job непосредственно перед origin repo clone-fast, а затем выполните fetch и check out того commit, который ваша CI-система предоставляет для сборки.

Проверка первого запуска

Запустите первый job с флагом --fallback never. В этом случае отсутствующий kit завершит job с кодом выхода 1, а не приведёт к незаметному запуску медленного git clone. Не убирайте этот флаг, пока путь через kit не начнёт срабатывать успешно.

При успешном запуске в stderr выводятся clone-kit: manifest=..., по одной строке загрузки на каждый artifact, блок clone-kit timings: и clone-kit: ready DIR (head SHA), затем в stdout — mode: clone-kit, и запуск завершается с кодом 0. Блок timings разбивает запуск на фазы manifest, download, verify и checkout. Чтобы оценить выигрыш, сравните его итоговое время с обычным git clone того же repository на том же runner.

Если путь через kit не удаётся завершить, в stderr появляется одна машиночитаемая строка — clone-kit-result: status=fallback phase=PHASE или clone-kit-result: status=failed phase=PHASE — а за ней указывается причина. При значении по умолчанию --fallback auto в stdout затем выводится mode: git-clone.

Устранение неполадок

Каждая запись начинается со строки, которая видна в журнале задания.

Загрузка манифеста возвращает 404

clone-kit-result: status=fallback phase=manifest с HTTP 404. Для репозитория ещё нет набора для клонирования; обратитесь к команде вашего аккаунта Cursor, чтобы включить CloneKit. Если в URL из сообщения https:// встречается дважды, значит команда получила URL для клонирования вместо {owner}/{repo}.

Загрузка манифеста возвращает 401

clone-kit manifest fetch failed: HTTP 401. Origin отклонил токен: он истёк, никогда не был действительным либо его установка была удалена. Выпустите новый токен непосредственно перед клонированием. При значении по умолчанию --fallback auto резервный git clone завершается с тем же кодом и кодом выхода 128, поэтому в журнале видны обе ошибки.

Получение манифеста возвращает 403

clone-kit manifest fetch failed: HTTP 403. Установка или токен конвейера Buildkite не распространяется на этот репозиторий. Переустановите приложение или заново выберите охватываемые репозитории. Как и при 401, резервный git clone завершается с тем же кодом и кодом выхода 128.

Git запрашивает имя пользователя

fatal: could not read Username for 'https://origin.cursor.com'. Либо версия CLI устарела, либо переменная CURSOR_AUTH_TOKEN не экспортирована в shell, который запускает git. Выполните origin update или экспортируйте переменную.

Клонирование переходит в резервный режим на этапе аутентификации

clone-kit-result: status=fallback phase=auth с Not authenticated. Переменная CURSOR_AUTH_TOKEN не экспортирована в процесс, который запускает origin. Экспортируйте её в том же shell перед командой origin.

Выпуск токена возвращает 401 Issuer is not authorized

401 {"code":16,"message":"Issuer is not authorized"} от token endpoint. Claim iss не является зарегистрированным App ID, либо ключ подписи не зарегистрирован для этого приложения. Убедитесь, что ORIGIN_APP_ID соответствует приложению и что этот ключ подписи указан в списке ключей приложения.

origin auth status сообщает о действительном токене, но клонирование не удаётся

origin auth status определяет Token: valid или Token: expired по claim со сроком действия внутри CURSOR_AUTH_TOKEN. Команда не спрашивает Origin, принимает ли он токен, поэтому valid означает лишь то, что срок действия токена не истёк. CLI считает токен истёкшим за пять минут до времени, указанного в claim со сроком действия. Команды origin отклоняют истёкший токен ещё до отправки запроса, а clone-fast записывает в журнал clone-kit-result: status=fallback phase=auth с сообщением The injected CURSOR_AUTH_TOKEN session has expired. Выпустите новый токен и повторите попытку.

Если путь через набор по-прежнему не работает, отправьте команде вашего аккаунта Cursor журнал задания со строкой clone-kit-result, название репозитория и выходные данные команды origin --version.