Configurar o CloneKit no CI com origin repo clone-fast
O Origin está atualmente em beta inicial. Você pode criar repositórios, fazer push e pull com git, espelhar do GitHub, navegar e pesquisar no código, abrir e mergear pull requests e compartilhar com sua equipe do Cherri Code.
Envie todo e qualquer feedback para [email protected] para nos ajudar a melhorar o produto.
O origin repo clone-fast substitui o git clone em jobs de CI. Em vez de clonar do zero, ele baixa um kit de clone pré-construído, um snapshot empacotado do repositório que o Cherri Code gera para cada repositório com o CloneKit ativado, e depois consulta apenas os objetos adicionados desde a criação do kit. Esta página mostra como disponibilizar um token do Origin de curta duração no runner e executar o comando no Buildkite, no GitHub Actions e em outros sistemas de CI.
Como funciona
Cada execução de origin repo clone-fast {owner}/{repo} [DIR] faz o seguinte:
- Consulta o manifesto do kit no Origin com o token em
CURSOR_AUTH_TOKEN. O manifesto lista os artifacts do kit e o commit na sua ponta. - Baixa os artifacts de pacote de
gitcdn.origin.cursor.come verifica seus tamanhos. Com--verify, também verifica os hashes durante o download. - Instala-os no diretório de destino, que precisa estar vazio.
- Complementa o clone: busca os objetos adicionados desde a criação do kit e faz checkout da ponta atual do default branch. Com
--no-top-up, o checkout permanece na ponta do kit. Já--barecria um repositório bare sem árvore de trabalho.
Quando o caminho do kit funciona, o comando imprime mode: clone-kit na saída padrão. Se alguma etapa falhar, o padrão --fallback auto executa um git clone comum e imprime mode: git-clone. Use --fallback never para, em vez disso, sair com status 1.
O token e o checkout se comportam da mesma forma em qualquer sistema de CI:
- Os tokens expiram em no máximo 15 minutos e não podem ser renovados. Emita um imediatamente antes do clone.
- A CLI lê o token de
CURSOR_AUTH_TOKEN. Exporte-o no shell que executa oorigin. - O clone termina na ponta do default branch. Para construir um commit específico, execute
git consultar origin SHAegit checkout SHAem seguida. O Origin entrega qualquer commit alcançável por SHA.
Prerequisites
O CloneKit está disponível nos planos Enterprise e é ativado por repositório; não há um toggle self-serve. Entre em contato com a equipe da sua conta Cherri Code para ativá-lo em cada repositório que você clonar. origin repo clone-fast --help lista as opções do comando.
- A CLI do Origin no runner. Instale-a com
curl -fsSL https://downloads.cursor.com/origin/install.sh | sh, que coloca o binário em$HOME/.local/bin/origin. Runners Linux precisam de glibc em x64 ou arm64; Alpine e outras imagens musl não são suportadas. Runners macOS são suportados. Veja Instalar a CLI do Origin. - bash, git, jq, curl 7.55 ou posterior e openssl 1.1.1 ou posterior. O
clone-fastusa ocurlpara fazer os downloads. - Egress de rede para estes hosts:
| Host | Usado para |
|---|---|
downloads.cursor.com | Instalar a CLI |
origin.cursor.com | Manifesto do kit e operações git |
gitcdn.origin.cursor.com | Downloads de kits |
api.cursor.com | Emitir tokens e solicitações de sincronização de espelho de um Origin App |
Escolha um caminho
| Sistema de CI | De onde vem o token | Seção |
|---|---|---|
| Buildkite, agentes hosted ou self-hosted, com o Origin conectado como repository provider | A API do Buildkite Agent, em um hook de checkout | Buildkite |
| GitHub Actions | Seu Origin App, em uma etapa do fluxo de trabalho | GitHub Actions |
| GitLab CI, Jenkins, CircleCI e fleets self-hosted | Seu Origin App, no job | Outros sistemas de CI |
Buildkite
O Buildkite gera o token para você. Substitua o git checkout padrão do agente por um hook checkout que solicita um token à API do Buildkite Agent e executa origin repo clone-fast.
Você precisa ser administrador da organização no Buildkite para conectar o Origin, e administrador do Origin para instalar o app do Buildkite.
Conectar o Origin ao Buildkite
No Buildkite, selecione Settings > Repository Providers > Add Provider > Origin, ou selecione Connect Origin account na página New Pipeline. Selecione o proprietário e os repositórios e instale o app do Buildkite. O Buildkite solicita acesso de leitura ao conteúdo do repositório e aos pull requests, e acesso de leitura e escrita às verificações. Para mais detalhes, consulte Origin na documentação do Buildkite.
Instalar a CLI do Origin no agente
Siga os Pré-requisitos, com curl, git e jq no PATH.
Manter o diretório de checkout vazio
Se o agente mantiver o diretório de build entre builds, esvazie $BUILDKITE_BUILD_CHECKOUT_PATH antes de o hook ser executado. O clone-fast precisa de um diretório vazio e recorre ao git clone quando encontra arquivos ali.
Adicionar o hook de checkout
Em um agente self-hosted, salve o script abaixo como checkout no diretório --hooks-path do agente. Em agentes hospedados pelo Buildkite, distribua-o como o hook checkout de um plugin não vendorizado, ou defina checkout: { skip: true } no step e execute os mesmos comandos no command do step. Um hook de repositório não pode definir checkout, porque o repositório ainda não foi baixado. Consulte Agent hooks e Git checkout na documentação do Buildkite.
O hook solicita um token restrito ao repositório do pipeline, exporta-o como CURSOR_AUTH_TOKEN, clona com clone-fast, registra o credential helper do git com origin auth setup-git e faz checkout de $BUILDKITE_COMMIT. O token do agente chega ao curl pelo stdin, portanto nunca aparece em uma linha de comando:
#!/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 espera owner/repo, não a 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"Quando um build começa sem um commit, BUILDKITE_COMMIT é HEAD. As duas linhas git então consultam o HEAD remoto e deixam a árvore de trabalho na ponta da branch padrão da qual o clone-fast já fez checkout.
- Envie
$BUILDKITE_REPOsem alterações.repo_urldeve corresponder exatamente à URL do repositório registrada no pipeline, incluindo o.git. Uma grafia diferente retorna HTTP 400. - O token é somente leitura e limitado ao repositório do pipeline. Ele carrega
repository:contents:reade expira após 15 minutos. Um job posterior, ou uma operação git realizada mais de 15 minutos depois da emissão, precisa de um novo token obtido pela mesma solicitação. - Tente novamente em caso de 503. Se a API do Buildkite Agent responder HTTP 503, aguarde o tempo indicado no header
Retry-Aftere tente novamente, como faz o próprio credential helper do Buildkite.
GitHub Actions e outros providers de CI
O GitHub Actions e outros sistemas de CI emitem seus próprios tokens. Você cria um app Origin uma vez, instala nos repositórios que vai clonar e fornece a cada job a chave privada do app e dois ids. O job assina um app JWT de curta duração e o troca por um installation token. Para a referência completa dos campos, consulte App JWT e Criar token de acesso da instalação.
Criar um app Origin
É preciso ser administrador do espaço de trabalho para instalar o app.
Registre o app
Gere um par de chaves Ed25519
Somente chaves Ed25519 são aceitas:
openssl genpkey -algorithm ED25519 -out origin-app-private.pemopenssl pkey -in origin-app-private.pem -pubout -out origin-app-public.pemCrie o app e adicione a chave pública
Nas configurações de apps do Origin, crie o app e adicione o conteúdo de origin-app-public.pem como signing key. Cada app comporta até 10 signing keys ativas.
Copie o App ID
O App ID fica na página do app. App IDs começam com app_.
Instalar o app
Instale o app no seu owner
Na página de instalação do app, nas mesmas configurações, instale o app no owner que contém os repositórios que você clona e selecione esses repositórios.
Copie o installation id
O installation id está na URL da página da instalação: /codebase/settings/apps/installations/{installationId}. Os installation ids começam com i_.
Para ler os installation ids programaticamente, chame List App Installations usando um app JWT como bearer. A resposta inclui o id, o target.slug, os scopes e o repoSelectionMode de cada instalação.
Armazene as credenciais
Armazene a chave privada como um segredo no seu sistema de CI e exponha-a ao job na variável de ambiente ORIGIN_APP_PRIVATE_KEY, contendo o texto PEM completo. Armazene o App ID e o installation id como variáveis simples ORIGIN_APP_ID e ORIGIN_INSTALLATION_ID. Se o seu sistema de CI monta segredos como arquivos, carregue a chave antes com ORIGIN_APP_PRIVATE_KEY=$(cat /path/to/origin-app-private.pem).
Emitir um token no job
A função abaixo assina o app JWT e o troca por um installation token. O JWT usa alg EdDSA, define iss e kid como o App ID e aud como origin-apps, e define exp para 15 minutos à frente. A referência de App JWT sugere um tempo de vida de cerca de cinco minutos; como um installation token nunca dura mais que o JWT que o emitiu, esta receita assina um JWT de 15 minutos para que o token aproveite seus 15 minutos completos. A solicitação pede repository:contents:read, que cobre clone, consultar e pull. Push exige repository:contents:write, e a instalação precisa ter concedido esse scope. Adicione "repositoryIds":[...] ao request body para restringir o token a apenas alguns repositórios da instalação.
A chave privada chega ao openssl por um descritor de arquivo e o header bearer chega ao curl pelo stdin, de modo que nenhum dos dois aparece em uma linha de comando, onde outros processes no runner poderiam lê-los. A função define e exporta CURSOR_AUTH_TOKEN diretamente, então o token nunca é gravado em um arquivo, e uma emissão malsucedida interrompe o job sob set -e. São necessários bash, openssl 1.1.1 ou posterior, curl 7.55 ou posterior e 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 precisa de uma entrada com suporte a busca; a entrada de assinatura não contém nenhum segredo. 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}Chame-o imediatamente antes do clone em um diretório vazio e, em seguida, registre o credential helper do git para que os comandos git seguintes se autentiquem enquanto CURSOR_AUTH_TOKEN continua exportado. Substitua acme/widgets pelo seu repositório e COMMIT_SHA pelo commit que o seu sistema de CI expõe para o build:
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
O GitHub Actions usa o caminho do app Origin, com a chave privada em um segredo do Actions e os dois ids em variáveis do repositório. Como o GitHub dispara o fluxo de trabalho, o repositório fica no GitHub e o Origin o mantém como um repositório espelhado. O fluxo de trabalho pede ao Origin que faça o pull do commit do build, clona com clone-fast no $GITHUB_WORKSPACE vazio e então faz o checkout desse commit. Ele nunca chama o GitHub, portanto não precisa de nenhuma permissão de GITHUB_TOKEN e não usa actions/checkout.
Adicione o segredo
No seu repositório, selecione Settings > Secrets and variables > Actions e adicione o segredo ORIGIN_APP_PRIVATE_KEY com o PEM completo da chave privada.
Adicione as variáveis
Na mesma página, adicione as variáveis de repositório ORIGIN_APP_ID e ORIGIN_INSTALLATION_ID.
Adicione o fluxo de trabalho
Salve o fluxo de trabalho abaixo como .github/workflows/ci.yml, defina ORIGIN_REPO como o {owner}/{repo} do repositório espelhado no Origin e adicione as etapas do seu build depois da etapa de clone.
O fluxo de trabalho instala a CLI, emite um token, aguarda o Origin espelhar o commit e o clona:
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 # O Origin espelha o GitHub com atraso. Aguarde até cerca de dois minutos por este commit. 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"- Em
pull_request, o fluxo de trabalho faz o build do head do pull request.github.shaé um commit de merge que existe apenas no GitHub, entãoBUILD_SHAeBUILD_BRANCHusam o head commit e a branch do pull request. - A solicitação de sync é Sync Mirror. Ela aceita o installation token, requer
repository:contents:reade retorna200com"synced": trueou202com"synced": falseapós seu budget de espera de cerca de dois minutos. Se o Origin for a source of truth e o GitHub for espelhado, remova a solicitação de sync: o commit já está no Origin, e o Origin rejeita solicitações de sync para repositórios que não fazem pull de uma fonte upstream. - Ambas as credenciais são mascaradas.
::add-mask::oculta o app JWT e o installation token no log do job. - Passos posteriores emitem o token novamente. Um passo posterior que precise de acesso git ao Origin define e chama a função novamente. O token nunca é escrito em
$GITHUB_ENVnem$GITHUB_OUTPUT, que são arquivos em disco. - Pull requests de forks não são espelhados. A head branch deles não está no seu repositório. Faça o build deles a partir do GitHub.
Outros sistemas de CI
GitLab CI, Jenkins, CircleCI e pools de workers self-hosted usam diretamente o caminho do app Origin. Armazene a chave privada e os dois ids conforme descrito em Armazene as credenciais, depois execute a função de Emitir um token no job imediatamente antes de origin repo clone-fast e consulte e faça o check out do commit que seu sistema de CI expõe para o build.
Verifique a primeira execução
Execute o primeiro job com --fallback never. Assim, a ausência de um kit faz o job falhar com exit status 1 em vez de executar silenciosamente um git clone lento. Mantenha a flag ativada até que o caminho do kit funcione.
Uma execução bem-sucedida imprime clone-kit: manifest=..., uma linha de download por artifact, um bloco clone-kit timings: e clone-kit: ready DIR (head SHA) no stderr, depois mode: clone-kit no stdout, e sai com status 0. O bloco de timings divide a execução nas fases de manifest, download, verify e checkout. Para medir o ganho, compare o total dele com um git clone comum do mesmo repositório no mesmo runner.
Quando o caminho do kit não é concluído, o stderr mostra uma única linha legível por máquina, clone-kit-result: status=fallback phase=PHASE ou clone-kit-result: status=failed phase=PHASE, seguida do motivo. Com o --fallback auto padrão, o stdout passa a mostrar mode: git-clone.
Solução de problemas
Cada entrada começa com a linha que o log do job mostra.
A consulta ao manifesto retorna 404
clone-kit-result: status=fallback phase=manifest com HTTP 404. Ainda não existe um kit de clone para o repositório; entre em contato com a equipe da sua conta Cherri Code para ativar o CloneKit. Se a URL na mensagem contiver https:// duas vezes, o comando recebeu uma URL de clone em vez de {owner}/{repo}.
A consulta ao manifesto retorna 401
clone-kit manifest fetch failed: HTTP 401. O origin rejeitou o token: ele expirou, nunca foi válido ou sua instalação foi removida. Emita um novo token imediatamente antes do clone. Com o padrão --fallback auto, o git clone de fallback falha com o mesmo código e status de saída 128, de modo que o log mostra os dois erros.
A consulta ao manifesto retorna 403
clone-kit manifest fetch failed: HTTP 403. A instalação, ou o token do pipeline do Buildkite, não abrange este repositório. Reinstale o app ou selecione novamente os repositórios que ele abrange. Assim como no 401, o git clone de fallback falha com o mesmo código e exit 128.
O Git pede um nome de usuário
fatal: could not read Username for 'https://origin.cursor.com'. Ou a CLI está desatualizada, ou CURSOR_AUTH_TOKEN não foi exportada no shell que executa o git. Execute origin update, ou exporte a variável.
O clone recorre ao fallback na fase de auth
clone-kit-result: status=fallback phase=auth com Not authenticated. CURSOR_AUTH_TOKEN não foi exportada para o processo que executa origin. Exporte-a no mesmo shell antes do comando origin.
A emissão retorna 401 Issuer is not authorized
401 {"code":16,"message":"Issuer is not authorized"} retornado pelo endpoint de token. A claim iss não é um App ID registrado, ou a signing key não está registrada nesse app. Confirme se ORIGIN_APP_ID corresponde ao app e se o app lista a signing key.
origin auth status relata um token válido, mas o clone falha
origin auth status deriva Token: valid ou Token: expired do claim de expiração dentro de CURSOR_AUTH_TOKEN. Ele não pergunta ao Origin se o token é aceito, portanto valid significa apenas que o token não expirou. A CLI considera um token expirado cinco minutos antes do seu claim de expiração. Os comandos origin recusam um token expirado antes de fazer qualquer solicitação, e o clone-fast registra clone-kit-result: status=fallback phase=auth com The injected CURSOR_AUTH_TOKEN session has expired. Emita um novo token e tente novamente.
Se o caminho do kit continuar falhando, envie à equipe da sua conta Cherri Code o log do job com a linha clone-kit-result, o repositório e a saída de origin --version.