Menu da documentação
Painel e análises
Runners gerenciados
Equipe e notificações
Faturamento e planos
Arquivos de workflow
Execute seu arquivo de workflow no Latchkey
O arquivo de workflow do Latchkey: as chaves que ele aceita, as chaves que ele recusa pelo nome e por quê, onde o arquivo fica e o que acontece quando você faz push.
Resumo
- Faça commit de um workflow em
.latchkey/, no formato que você já conhece do GitHub Actions, menos oworkflows/ <name>.yml uses:. - O Latchkey recusa um arquivo em vez de executá-lo ignorando uma chave. Uma recusa por push: a primeira chave recusada no arquivo, nomeada exatamente como você escreveu, com a linha, ou o próprio arquivo quando a recusa é sobre o arquivo inteiro.
runs-on:nomeia um tamanho do Latchkey, então oruns-on: ubuntu-latestde um arquivo copiado viraruns-on: small.- O checkout é automático e a imagem do runner traz doze toolchains de linguagem, então os passos
actions/checkouteactions/setup-*simplesmente somem.
A promessa#
O Latchkey executa o seu arquivo de workflow. Faça commit de um workflow em .latchkey/workflows/, um arquivo por workflow, no formato que você já conhece do GitHub Actions: jobs, passos com run:, env:, runs-on: nomeando um tamanho de runner do Latchkey, timeout-minutes:, needs: para rodar um job depois de outro, uma matriz e containers de serviço em services:. Faça push e o Latchkey conclui um pequeno conjunto fixo de contextos no registro da execução e os reporta como checks aos quais a sua proteção de branch pode se vincular. O arquivo que você commita continua funcionando sem mudança em todas as versões seguintes.
A proteção de branch pode exigir os contextos Latchkey e Latchkey / plan, mas não os exija ainda: um check que o Latchkey não consegue escrever fica ausente, e um check exigido que está ausente bloqueia o merge; o Latchkey / plan também reporta as paradas do próprio Latchkey como falhas, então exigi-lo bloquearia merges por falhas do Latchkey.
Um check Latchkey neutro significa que o Latchkey não verificou este commit. O seu código nem passou nem falhou: algo parou a execução antes que os seus jobs pudessem decidir, e o texto do check diz o que a parou. Um check neutro não bloqueia um merge, então a proteção de branch deixa o pull request passar mesmo que nada tenha sido verificado.
O que roda hoje#
Nesta versão um arquivo de workflow dá a você jobs, passos que rodam um de cada vez, env:, um tamanho de runner, um timeout, uma matriz, containers de serviço e jobs que esperam outros jobs com needs:. Um passo pode ler um segredo definido no Latchkey para este repositório ou para o workspace dele: escreva ${{ secrets.NAME }} como o valor inteiro de uma variável no env: desse passo, e o Latchkey entrega o valor a esse passo quando ele começa. Um segredo de repositório prevalece sobre um segredo de workspace com o mesmo nome, um nome que não está definido em nenhum dos dois níveis recusa a execução antes de qualquer job começar, e uma execução de um pull request vindo de um fork não recebe nenhum segredo. Uma execução de uma correção que o Latchkey propôs em uma branch própria só os recebe quando a correção não altera nenhum arquivo de workflow. Em uma execução que não recebe segredos, os passos que nomeiam um deles não rodam. Um valor com menos de 28 bytes nunca é entregue, porque o log não conseguiria ocultá-lo.
Onde o arquivo fica#
Um arquivo por workflow em .latchkey/workflows/. .yml é a grafia canônica e .yaml também é lido, então um arquivo copiado de .github/workflows/ mantém a sua extensão. Renomear um arquivo de workflow inicia um novo pipeline; as execuções feitas sob o nome antigo permanecem sob ele.
name: CI
on:
push:
branches: [main]
pull_request:
latchkey:
schema: 1
jobs:
test:
runs-on: small
timeout-minutes: 20
env:
NODE_ENV: test
steps:
- name: Install dependencies
run: npm ci
- name: Run the suite
run: npm testEscolhendo um runner#
Valor de runs-on: | vCPU | Memória | Disco |
|---|---|---|---|
small or latchkey-small | 2 | 8 GB | 100 GB |
medium or latchkey-medium | 4 | 16 GB | 100 GB |
large or latchkey-large | 8 | 32 GB | 100 GB |
xlarge or latchkey-xlarge | 16 | 64 GB | 200 GB |
Todo job nomeia um runs-on:. As duas grafias são aceitas e nomeiam o mesmo runner, então uma linha runs-on: copiada de um workflow do Actions com Latchkey funciona sem mudança. A linha que um arquivo GitHub-hosted normalmente traz é runs-on: ubuntu-latest; o Latchkey não é GitHub-hosted e não vai mapear esse nome para um tamanho em silêncio, então troque para runs-on: small, ou para o tamanho de que o job precisa. Todos os runners são Ubuntu 24.04 LTS em x86_64; veja Limites e concorrência para o que cada tamanho entrega.
O que não emulamos#
O Latchkey não executa actions do GitHub. Um passo uses: é recusado com uma mensagem que nomeia o passo e o seu substituto no Latchkey: o checkout é automático (o runner clona o seu repositório no commit com um token por job, ou sem nenhum para um fork liberado), actions/cache é latchkey cache save e latchkey cache restore, as actions de setup são as próprias toolchains da imagem (Node, Python, Go, Rust e Java estão na imagem padrão do runner), artefatos ainda não são suportados, e uma action de terceiros vira um passo run:. Não prometemos um runtime compatível com o Actions, e não vamos prometer.
As toolchains já estão lá#
A imagem padrão do runner traz Python, Node, Go, Java, Rust, Ruby, PHP, .NET, Haskell, Swift, Kotlin e Julia, e o build da imagem falha se qualquer uma das doze estiver faltando. Um passo uses: actions/setup-* não tem substituto a escrever: apague o passo e chame a ferramenta.
O checkout#
O runner clona o seu repositório no commit enviado antes do primeiro passo rodar, então um arquivo de workflow não precisa de passo de checkout. Um pull request vindo de um fork só roda depois de liberado, e o commit dele é buscado no próprio endereço deste repositório sem token nenhum. O clone é raso e não traz tags, que é o mesmo padrão da própria action de checkout do fornecedor, então um passo que deriva uma versão de tags ou lê o histórico não os tem nesta versão.
Como um job roda#
O Latchkey roda os passos de um job um de cada vez, na ordem do arquivo, cada um como o seu próprio script bash --noprofile --norc -e -o pipefail, iniciado no workspace do job com o ambiente do job. Um passo para no primeiro comando que falha, e um comando que falha dentro de um pipeline faz o pipeline falhar. Depois que um passo falha, um passo seguinte só roda quando o seu if: pede failure() ou always(), e o job falha com o código de saída do primeiro passo que falhou. Um cd ou um export em um passo não passa para o próximo.
Como cada passo é o seu próprio script, no seu próprio grupo de processos, um passo que encerra o próprio shell ou o próprio grupo de processos, por exemplo com kill $$ ou kill 0, encerra só aquele passo: o passo falha com código de saída 143 e um passo failure() ou always() seguinte ainda roda. O script de cada passo também é lido separadamente, então um passo com erro de sintaxe falha sozinho com código de saída 2, e todo passo seguinte admitido pelo próprio if: ainda roda; um job cujo próprio if: não se cumpre não roda nenhum dos seus passos, com ou sem erro de sintaxe. E o script de cada passo é gravado no diretório temporário do job ($RUNNER_TEMP) quando esse passo começa, então um passo que remove esse diretório ou o diretório de trabalho do job, ou que de outro modo elimina o que o passo seguinte precisa para começar, interrompe o job no passo seguinte: ele não começa, nenhum passo depois dele roda, nem mesmo um passo always(), e o job falha sem código de saída.
if: aceita success(), failure(), always() e um teste de igualdade sobre um valor de matriz ou de env, e nada além disso. Um if: fora desse conjunto é recusado pelo nome em vez de avaliado como algo que o Latchkey adivinhou. Um job cujo próprio if: não se cumpre é pulado: os seus passos não rodam, e os checks da execução o contam como pulado; ele nem passou nem falhou.
Jobs que precisam de outros jobs#
Um job com needs: começa depois que todos os jobs que ele nomeia passaram. Nomeie um job (needs: build) ou vários (needs: [build, test]) pelas chaves deles em jobs:, no mesmo arquivo. Um job de matriz passou quando todas as suas pernas passaram. Um job sem needs: começa sem esperar outros jobs, como antes. Um job que ainda espera os jobs de que precisa 24 horas depois de entrar na fila é parado, e o bloco dele mostra "Stopped". Se um job de que ele precisa terminou sem passar até lá, ele é pulado em vez disso.
Enquanto espera, o bloco do job na página da execução mostra os jobs que ele ainda espera ("Waiting for build."). Quando um job de que ele precisa falha, é cancelado ou é pulado, o job é pulado antes de começar: os checks da execução o contam como pulado, ele nem passou nem falhou, nenhum runner é gasto com ele, e o bloco dele diz qual foi o caso ("Skipped: a job it needs failed."). Isso se propaga pela cadeia: um job que precisa de um job pulado também é pulado.
Em um job com needs:, um if: no nível do job pode testar um valor de matriz ou de env. failure() e always() ali são recusados pelo nome: quando um job de que ele precisa não passou, o job é pulado antes de qualquer if: ser lido, então nenhum dos dois conseguiria rodá-lo.
As variáveis de ambiente que o Latchkey define#
Em um job que o Latchkey iniciou a partir do seu arquivo de workflow, estes nomes são exportados para você:
| Nome | Valor |
|---|---|
LATCHKEY_CI_RUN_ID | A execução a que este job pertence |
LATCHKEY_CI_ATTEMPT | O número da tentativa, contando a partir de 1 |
LATCHKEY_CI_REPOSITORY_ID | Identificador do repositório no Latchkey |
LATCHKEY_CI_PROVIDER | O provedor de git, por exemplo github |
LATCHKEY_CI_SHA | O commit ao qual a execução está presa |
LATCHKEY_CI_REF | O ref exatamente como o provedor o entregou |
LATCHKEY_CI_ACTOR | Quem disparou a execução |
LATCHKEY_CI_PR_NUMBERS | Pull requests que carregam este commit, unidos por vírgula, no máximo 16, com um + no final quando há mais. |
LATCHKEY_CI_FORK | true quando o pull request vem de um fork; esse job roda sem token do repositório, sem cache, sem segredos e sem self-heal, e os passos dele alcançam apenas o GitHub e um conjunto fixo de registros públicos de pacotes, sem sudo, sem Docker e sem containers de serviço |
LATCHKEY_CI_BASE_REF | O ref base como um caminho refs/heads/ completo: a branch base de um pull request, ou o ref a partir do qual uma nova branch foi criada em um push. Vazio nos demais casos. |
LATCHKEY_CI_EVENT_CLASS | O evento normalizado que iniciou a execução |
LATCHKEY_CI_REPOSITORY | O repositório ao qual esta execução pertence, como owner/name |
LATCHKEY_CI_STEP_NUMBER | O número do passo em execução, contando a partir de 1. Legível nos comandos do passo, não no seu if: |
O nome completo do repositório é exportado como owner/name puro, nunca como uma URL. LATCHKEY_CI_REPOSITORY carrega o nome com que o repositório foi registrado, então ele não muda se você renomear o repositório no seu host de código. Todo nome que começa com LATCHKEY_ pertence ao Latchkey, então um arquivo de workflow não pode definir nenhum deles.
O bloco latchkey:#
Um bloco opcional de nível superior com exatamente um membro. Os valores são lidos como texto, então schema: 1 e schema: "1" são o mesmo arquivo. Um arquivo sem bloco latchkey: é lido como versão de schema 1. Um bloco presente precisa trazer schema: e mais nada.
Quando o arquivo roda#
on: aceita push, com branches: e tags: opcionais, e pull_request, com branches: e types: opcionais. Um on: push sozinho roda apenas em pushes de branch; um push de tag roda o arquivo apenas quando o bloco push nomeia tags:. Sob on: pull_request, branches: casa com a branch base do pull request e nunca com o head ref.
O Latchkey roda um workflow de pull request em opened, synchronize e reopened, os três, sempre, então uma lista types: precisa nomear exatamente esses três ou ser omitida. on: schedule ainda não é suportado.
Se você exige os checks do Latchkey, um pull request retido vindo de um fork bloqueia o merge até que um mantenedor aprove o commit ou que seja feito push da mudança em uma branch deste repositório como um novo commit.
Quando nada roda#
Três resultados significam que o push foi lido e nada casou, e um quarto significa que o Latchkey não roda o pull request. Nenhum deles é uma falha.
| Motivo | O que significa |
|---|---|
no_workflow_file | Não há arquivo de workflow em .latchkey/workflows/ neste commit, então nada rodou. |
no_workflow_matched | Nenhum workflow em .latchkey/workflows/ casa com este evento, então nada rodou. |
fork_base_no_workflow_file | Não há arquivo de workflow no commit base que este pull request tem como alvo, então nada rodou. Os arquivos de workflow do próprio fork nunca rodam; adicione o arquivo a uma branch deste repositório primeiro. |
fork_private_repository | O Latchkey não roda pull requests vindos de forks de um repositório privado, então este código não foi verificado. Para rodá-lo, faça push da mudança em uma branch deste repositório como um novo commit. |
Uma matriz#
Uma matriz expande para um job por combinação. A chave de uma perna acrescenta os valores da perna à chave do job, ordenados por chave e unidos por vírgulas, então um job chamado build com os: linux e node: 20 produz a chave de perna build(node=20,os=linux). Nenhum job pode expandir além de 50 pernas, nenhum arquivo de workflow pode ter mais de 50 pernas somando todos os jobs do arquivo, e nenhuma execução pode ter mais de 150 jobs, contados em todos os arquivos de workflow que o evento casou.
No Actions, fail-fast tem padrão true e cancela as outras pernas quando uma falha. O Latchkey roda todas as pernas e não cancela nenhuma, então fail-fast: false é aceito porque é exatamente o que o Latchkey faz, fail-fast: true é recusado em vez de silenciosamente ignorado, e max-parallel também é recusado.
Containers de serviço#
Um job pode declarar containers de serviço em services:, cada um com um rótulo e uma image:. O Latchkey inicia todos os serviços antes do primeiro passo do job, em uma rede só do job, e os seus passos alcançam um serviço pelo rótulo como nome de host, na porta em que a imagem escuta, então um serviço com o rótulo cache rodando redis:7 é cache:6379 para todo passo do job. Um serviço também pode publicar uma porta no runner em ports:, escrita como <runner port>:<service port>, e aí os seus passos o alcançam também em localhost:<runner port>. Um rótulo usa letras minúsculas, dígitos e -, começa com uma letra, não termina com hífen, tem no máximo 63 caracteres e não é localhost; um job declara no máximo 10 serviços. Os passos de um pull request vindo de um fork não alcançam nenhum container de serviço.
jobs:
test:
runs-on: small
services:
cache:
image: redis:7
steps:
- run: nc -z cache 6379jobs:
test:
runs-on: small
services:
db:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
steps:
- run: pg_isready -h localhost -p 5432O Latchkey espera até que cada serviço esteja pronto: saudável, quando a imagem declara um health check; senão, aceitando uma conexão em uma porta que a imagem expõe; senão, em execução. Ele espera até 5 minutos por todos eles, downloads incluídos. Um serviço cuja imagem não pode ser baixada, que para, ou que não fica pronto a tempo faz o job falhar antes de o primeiro passo rodar, e o log do job nomeia o serviço, a imagem e o motivo. Quando o job termina, seja como for, o Latchkey remove os serviços e tudo o que criou para eles, e o próprio runner é destruído depois do seu único job de qualquer forma.
Um serviço roda uma imagem pública. Dentro de um serviço, env: define as variáveis que a imagem lê ao iniciar, como POSTGRES_PASSWORD para postgres, e ports: publica uma porta no endereço de loopback do runner. options:, volumes: e credentials: são recusados pelo nome, então uma imagem que precisa de uma credencial para ser baixada não pode rodar como serviço: inicie-a em um passo run: em vez disso. Um valor de matriz não pode ser usado na image: nem no env: de um serviço, e o env: de um serviço não pode ler um segredo.
O Latchkey nunca escreve as configurações de um serviço no log do job. Uma configuração de 28 bytes ou mais também é ocultada onde quer que um passo a imprima, no log e no que o self-heal lê. Uma mais curta, como a senha de um banco de dados de teste, não é ocultada, então mantenha credenciais reais fora das configurações de serviço: um valor que parece uma credencial é recusado, e um segredo pertence ao env: de um passo.
Como uma recusa se lê#
O Latchkey recusa um arquivo em vez de executá-lo ignorando uma chave. Uma recusa por push: a primeira chave recusada no arquivo, nomeada exatamente como você escreveu, com a linha, ou o próprio arquivo quando a recusa é sobre o arquivo inteiro. O contexto de plano conclui com a recusa no registro da execução. O check Latchkey / plan nomeia onde a recusa aconteceu.
As três que você tem mais chance de encontrar ao portar um arquivo:
uses:não é suportado: escreva o passo comorun:. O checkout é automático, o cache élatchkey cache saveelatchkey cache restore, e as toolchains (Node, Python, Go, Rust, Java) estão na imagem do runner.options:,volumes:ecredentials:dentro de um serviço não são suportados: um serviço roda uma imagem pública com as configurações no seuenv:e as portas no seuports:, e os seus passos o alcançam pelo rótulo ou pela porta publicada.if:emnotifyusaalways(), enotifytemneeds:. Quando um job de que ele precisa não passa, o job é pulado antes de começar, entãoalways()efailure()não conseguem rodá-lo. Usesuccess(), um teste de matriz ou deenv, ou remova oneeds:.
O que um job que falha significa para você#
Um job que falha é encaminhado ao self-heal do Latchkey, e o reparo é reportado para o job como um todo, não por passo. O self-heal não roda em um pull request vindo de um fork. Nem toda falha é do seu código: o Latchkey distingue as falhas do próprio Latchkey, os seus cancelamentos e os problemas de cobrança ou de conexão do resultado do seu próprio comando. Vermelho no check Latchkey significa que o seu comando falhou ou que o seu job reportou a falha por conta própria; uma falha que o Latchkey consegue atestar como própria nunca deixa o check Latchkey vermelho, e o texto do check diz que a falha foi do Latchkey. Um job iniciado por um push ou por um pull request já serve o seu log hoje por latchkey logs <job id> e pela ferramenta MCP get_job_logs, e aparece na lista de jobs e na página da execução, onde cada passo mostra o seu próprio log enquanto roda. Um job que o Latchkey não conseguiu colocar em um runner diz isso e nunca é cobrado.
Para rodar de novo os jobs de uma execução que terminou, pressione Re-run no check Latchkey no GitHub, ou Re-run jobs that did not pass na execução, no painel. O Latchkey roda, como uma nova tentativa da mesma execução, todo job que não passou e todo job que precisa de um deles. Todos os outros jobs que passaram são mantidos e não são rodados nem cobrados de novo. Cancel run no painel cancela todos os jobs da execução que ainda não terminaram, e a execução passa a mostrar Cancelled.
O que continua igual nos seus outros repositórios#
Os seus jobs do GitHub Actions em runners latchkey-* continuam rodando exatamente como hoje, na mesma organização, na mesma fatura e no mesmo conjunto de minutos gratuitos. Você move os repositórios para o Latchkey CI um de cada vez.
Compatibilidade futura#
Um arquivo que você commita nesta versão nunca é migrado: o mesmo compilador o lê em todas as versões seguintes.