Pular para o conteúdo principal

Atualizações de Apps

Atualizações de Apps

O framework Turn Apps suporta a atualização de apps para novas versões enquanto preserva estados importantes como configuração e URLs de webhook. Esta seção aborda como as atualizações funcionam e como implementar hooks de atualização no seu app.

Visão Geral

Quando você faz upload de uma nova versão de um app, a plataforma gerencia automaticamente as transições de versão:

  1. Arquivamento automático - Quando você faz upload de uma nova versão, a versão anterior é automaticamente arquivada
  2. Versão única ativa - Apenas uma versão de cada app pode estar ativa por vez por número
  3. Atualizar instalação existente - Se o app já estiver instalado, você pode atualizar a instalação existente para preservar o app_install.uuid (mantendo URLs de webhook estáveis), configuração e histórico de versões

Para apps instalados, você normalmente vai querer atualizar instalações existentes para manter a continuidade para usuários que integraram as URLs de webhook do seu app em sistemas externos.

O Que é Preservado Durante a Atualização

Ao atualizar um app, o seguinte é preservado:

  • UUID da Instalação do App - O identificador único desta instalação permanece o mesmo, o que significa que URLs de webhook (/apps/{uuid}/webhook) continuam funcionando
  • Configuração - Todos os valores de config são preservados por padrão (apps podem migrar config via hooks de atualização)
  • Histórico de Versões - O sistema rastreia até 10 versões anteriores, permitindo rollback se necessário

O Fluxo de Atualização

  1. Usuário faz upload da nova versão de um app existente
  2. Sistema detecta uma instalação existente com o mesmo nome de app
  3. Usuário escolhe "Atualizar instalação existente"
  4. Se atualizando para uma versão mais nova:
    • O pool de workers atual é parado
    • Histórico de versões registra a versão antiga e snapshot da config
    • AppInstall é atualizado para apontar para a nova versão (config preservada)
    • O evento upgrade executa com acesso às APIs turn.app.* para migração de config
    • Um novo pool de workers inicia com o novo código
  5. Se fazendo downgrade para uma versão mais antiga:
    • Processo similar, mas o evento downgrade executa em vez disso

Implementando Hooks de Atualização

Apps podem implementar eventos upgrade e downgrade em on_event para migrar configuração entre versões. Isso é particularmente útil quando:

  • Você renomeou campos de configuração
  • Você precisa transformar valores de config para a nova versão
  • Você quer adicionar metadados sobre a migração

O Evento upgrade

Chamado ao atualizar DE uma versão mais antiga PARA uma versão mais nova. O hook executa no código do novo app com acesso total às APIs turn.app.*:

function App.on_event(app, number, event, data)
if event == "upgrade" then
-- data.from_version: versão de origem (ex: "1.0.0")
-- data.to_version: versão de destino (ex: "2.0.0")

turn.logger.info("Atualizando de " .. data.from_version .. " para " .. data.to_version)

-- Ler config atual usando a API turn.app
local config = turn.app.get_config()

-- Exemplo: renomear um campo na v2.0.0
if config.old_api_endpoint then
config.api_url = config.old_api_endpoint
config.old_api_endpoint = nil
end

-- Adicionar metadados de atualização
config.upgraded_from = data.from_version
config.upgraded_at = os.date("!%Y-%m-%dT%H:%M:%SZ")

-- Salvar a config migrada
turn.app.set_config(config)

return true
end
end

O Evento downgrade

Chamado ao fazer downgrade DE uma versão mais nova PARA uma versão mais antiga. O hook executa no código do app antigo (de destino) com acesso total às APIs turn.app.*:

function App.on_event(app, number, event, data)
if event == "downgrade" then
-- data.from_version: versão de origem (ex: "2.0.0")
-- data.to_version: versão de destino (ex: "1.0.0")

turn.logger.info("Fazendo downgrade de " .. data.from_version .. " para " .. data.to_version)

-- Ler config atual usando a API turn.app
local config = turn.app.get_config()

-- Reverter a renomeação de campo da v2.0.0
if config.api_url then
config.old_api_endpoint = config.api_url
config.api_url = nil
end

config.downgraded_from = data.from_version

-- Salvar a config migrada
turn.app.set_config(config)

return true
end
end

Apps Sem Hooks de Atualização

Se seu app não implementa hooks de upgrade/downgrade, a configuração existente é preservada como está durante as atualizações. Este é o comportamento padrão e funciona bem para apps onde a estrutura da config não muda entre versões.

Histórico de Versões e Rollback

A plataforma mantém um histórico de versões para cada instalação de app, registrando:

  • String de versão - A versão semântica (ex: "1.0.0")
  • UUID da definição do app - Referência à definição do app
  • Atualizado em - Timestamp ISO 8601 de quando a atualização ocorreu
  • Snapshot de config - A configuração completa no momento da atualização

Este histórico é limitado a 10 entradas por padrão para evitar crescimento ilimitado.

Capacidade de Rollback

Usuários podem fazer rollback para a versão anterior a partir da página de configurações do app. Ao fazer rollback:

  1. O sistema recupera a definição do app anterior do histórico de versões
  2. O snapshot de config daquela versão é restaurado
  3. O hook de downgrade executa no código da versão anterior (se implementado)
  4. O pool de workers reinicia com a versão anterior

Isso fornece uma rede de segurança quando atualizações causam problemas inesperados.

Rollback Automático em Caso de Falha

O processo de atualização inclui rollback automático para garantir a estabilidade do app. Se qualquer um dos seguintes ocorrer durante a atualização:

  1. Hook de upgrade retorna false - O manipulador do evento upgrade do app indica falha
  2. Pool falha ao iniciar - O novo código do app falha ao inicializar

A plataforma irá automaticamente:

  1. Restaurar a definição do app anterior
  2. Restaurar o snapshot de config de antes da atualização
  3. Remover a entrada do histórico de versões que acabou de ser adicionada
  4. Reiniciar o pool de workers com a versão anterior
  5. Retornar uma mensagem de erro explicando o que aconteceu

Isso garante que os apps permaneçam funcionais mesmo quando atualizações falham. O rollback automático preserva:

  • A versão e código original do app
  • Todos os valores de configuração de antes da tentativa de atualização
  • Funcionalidade completa (o pool reinicia com a versão funcionando)

Exemplo de tratamento de falhas de atualização:

function App.on_event(app, number, event, data)
if event == "upgrade" then
-- Validar que serviços externos necessários estão disponíveis
local ok, err = validate_external_dependencies()
if not ok then
turn.logger.error("Atualização falhou: " .. err)
-- Retornar false dispara o rollback automático
return false
end

-- Prosseguir com a migração de config
local config = turn.app.get_config()
-- ... lógica de migração ...
turn.app.set_config(config)

return true
end
end

Melhores Práticas para Atualizações

  1. Sempre Teste Caminhos de Atualização - Antes de lançar uma nova versão, teste a atualização a partir da versão anterior

  2. Faça Hooks de Atualização Idempotentes - Se um hook de atualização executar múltiplas vezes, deve produzir o mesmo resultado

  3. Lide com Campos Ausentes Graciosamente - Não assuma que campos existem; use valores padrão:

    local timeout = migrated_config.timeout or 30
  4. Registre Ações de Atualização - Ajude os usuários a entender o que mudou durante a atualização:

    turn.logger.info("Migrado api_endpoint para api_url")
  5. Documente Mudanças Importantes - Use get_app_info_markdown para comunicar mudanças de versão:

    elseif event == "get_app_info_markdown" then
    return [[
    # Meu App v2.0

    ## Notas de Atualização

    - Campo de config `api_endpoint` foi renomeado para `api_url`
    - O hook de atualização migra isso automaticamente para você
    ]]
    end
  6. Preserve Versionamento Semântico - Use semver apropriado para comunicar o tipo de mudanças:

    • MAJOR: Mudanças incompatíveis requerendo migração de config
    • MINOR: Novas funcionalidades, compatíveis com versões anteriores
    • PATCH: Correções de bugs