Gente, continua não sendo trivial. Mas pelo menos acho que não quebrei o servidor completamente.

A última vez que eu tinha colocado o Gitea, meio que o subdomínio dele se sobrepujava ao www, e tinham algumas coisinhas irritantes que não funcionavam 100%.

Mas eu vi que rolou bastante drama, e o Gitea que era um fork do Gogs porque o Gogs ficou muito comercial, ganhou um fork porque ficou muito comercial. E nasceu o Forgejo.

Depois da enorme desventura que foi gastar uma fortuna pra recuperar alguns dados de um SSD falhando da pior forma possível na minha cara – imediatamente antes de eu ter um backup do que eu mais precisava nele – acabou o rolê de protelar. Até porque estou quase perdendo outro disco.

Então a parte do código e de coisas tangenciais a código vão ganhar no mínimo um lar constantemente atualizado no Gitão dos Bróder pra evitar essa caca. Aí o que for interessante fazer, vou espelhando no Github.

Mas era importante aqui registrar que não foi só criar um docker-compose.yml e um usuário no banco para ele.


Túneis e Tapas

Se você caiu de para-quedas no rolê, para desembolar uma instância de uma plataforma de código como Gogs/Gitea/Forgejo, ou Gitlab, ou qualquer outra coisa que permita que você mesmo hospede as coisas, você tem que ouvir e atender pedidos da parte “site” da coisa, e ouvir e atender pedidos SSH, que o git usa para transporte autenticado de dados.

Aí você tem algumas opções. A que fica mais simples para um usuário… usar, é manter a porta 22 para o usuário git.

Isso era o que eu queria.

Bati a cabeça um pouco tentando as indicações da documentação do Gitea (porque o Forgejo meio que esqueceu de documentar isso), e eventualmente teve uma combinação de opções que me fez sacar o que era para acontecer; a maior parte sendo um shim do executável que na verdade faz o túnel para o contâiner como indicado na documentação do Gitea.

Bash
adduser git
usermod -aG docker git
usermod -aG sudo git

Com isso, temos o usuário para receber os pedidos SSH do Git. Agora, é hora de conferir se tudo no docker-compose.yml faz sentido.

Os detalhes de importância são ele escutar explicitamente o localhost na porta 22922, e ter um link de volume para a pasta .ssh do usuário externamente.

YAML
services:

  forgejo:
    image: codeberg.org/forgejo/forgejo:8
    container_name: forgejo
    restart: always
    ports:
      - 22900:3000
      - "127.0.0.1:22922:22"
    environment:
      - FORGEJO__database__DB_TYPE=mysql
      - FORGEJO__database__HOST=mariadb:3306
      - FORGEJO__database__NAME=banco_do_forgejo
      - FORGEJO__database__USER=usuario_do_forgejo
      - FORGEJO__database__PASSWD=senha_do_forgejo
      - USER=git
      - USER_UID=1001 
      - USER_GID=1001 
    volumes:
      - /cotti.com.br/docker/volumes/forgejo:/data
      - /home/git/.ssh/:/data/git/.ssh
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    networks:
      - containers

networks:
  containers:
    external: true

Então, foi preciso criar uma chave para o usuário git do hóspede… acessar o contâiner.

sudo -u git ssh-keygen -t rsa -b 4096 -C "Gitea Host Key"
sudo -u git cat /home/git/.ssh/id_rsa.pub | sudo -u git tee -a /home/git/.ssh/authorized_keys
sudo -u git chmod 600 /home/git/.ssh/authorized_keys

E o shim em si, que é assim:

#!/bin/sh
ssh -p 22922 -o StrictHostKeyChecking=no git@127.0.0.1 "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@"

A pegadinha é agora. Como não uso uma porta padrão internamente para levar a conexão para o contâiner, adicionei o seguinte no /etc/ssh/sshd_config:

Match User git
  AuthorizedKeysCommandUser git
  AuthorizedKeysCommand /usr/local/bin/gitea keys -c /data/gitea/conf/app.ini -e git -u %u -t %t -k %k

Aí você reinicia o sshd, adiciona uma chave SSH no usuário lá na interface web do Forgejo…

Não é que funciona?

Não é que não quero fazer isso nunca mais?

Imagem não literal, mas quase.

Eu tava de férias, né. Como era de se esperar, foi difícil ter um instante aqui e outro ali. Mas tava saindo um livro de visitas. Um projetinho bem anos 90, com uma roupagem dos tempos quase atuais – com a licença poética de não usar uma linguagem tão da hora e do momento.

Mas tinha de quase tudo um pouco ali; captcha do Cloudflare para coibir spam, uma API caprichadinha para receber e ter um gerenciadorzinho, com JWT configurado bonitinho, refresh token

Tinha um design usando (ainda que fosse overkill, mas a ideia era entender mesmo) Clean Architecture, CQRS, tudo refinadinho. Usei até MongoDB, que nunca tinha mexido.

Criei um Dockerfile, criei um docker-compose.yml, e o mais importante: criei um docker-compose.yml que funcionou muito bem. Ele segurava as variáveis de ambiente que o backend usava, e fez tudo certinho. Rodava junto com o MongoDB e até um Manager enquanto não fazia o frontend.

Francamente, tava legal. Dava pra usar para começar a ter um mini-portifólio. Pelo menos eu podia mostrar que consigo desembolar esse tipo de coisa, ainda que não esteja trabalhando com isso agora.

Fiz a documentação inicial no README, conferi se podia usar a GNU AGPLv3, fiz uma página com um exemplo da parte do envio das mensagens pro livro. Tava pronto pra publicar no repositório.

Mas aí eu pensei, não, vou só adicionar logging primeiro. “Vai ser legal deixar um Serilog pronto, mesmo que com um sink só pra arquivo. Qualquer coisa coloca um sink pro Seq e adiciona ele no docker-compose.yml.”

O castigo vem de bicicleta.

Eu tinha considerado várias vezes manter uma instância do Forgejo aqui no meu servidor, e espelhar as coisas no Github. Me permitiria não ter muita cerimônia pra salvar o que eu quisesse. Mas protelei.

Eu pensei várias vezes na estratégia que devia tomar pra começar um backup mais extensivo e automático de coisinhas jogadas em trocentas pastas pelos discos. Ou, finalmente, centralizar tudo e então replicar. Mas protelei.

O SSD onde estavam todos os projetos que ainda não tinham repositório externo, uma quantidade difícil de saber de dados que podem ou não ser importantes – porque a minha memória continua sendo um lixo – e a maioria das aplicações e definições de aplicações, foi pro saco.

Levei para uma empresa de recuperação de dados, identificaram que foi o controlador, e vai dar um custo insano. SE der certo. Se não der certo, vai ser só um terço de insanidade pela tentativa.

Agora eu nem sei por onde recomeçar, pois francamente ainda estou atônito. As coisas que já estavam sem gosto, agora estão intragáveis.

Façam backup, pessoas. Especialmente se você acha que dividir dados entre discos para reduzir danos resolve por um tempo. Nem era meu SSD mais antigo ou mais usado.

Agora é ficar olhando pras feridas, sem saber como lamber.

Pela enésima vez nos vemos recomeçando.

Só que agora, depois de décadas de árdua contestação das regras arcanas de outrora da FAPESP, que mantém o Registro.br, finalmente sou o detentor de um domínio que almejava desde a infância.

O de agora.

A última vez que lembro ter pesquisado, o domínio era de um squatter – pois se procurarmos na Wayback Machine não encontramos nada. E antes disso, nem era possível que uma mera pessoa física como eu pudesse ter um .com.br. Já tive o .net.br por um tempo, que por motivos insanos era mais permissivo que o .com.br. Exigiam CNPJ e o caramba – documentação e mais documentação. A flexibilização disso é um fenômeno que chegou com penoso atraso, e a interface de gerenciamento do Registro.br é péssima. Ainda bem que pelo menos funciona fazer essas coisas pelo Cloudflare.

Mas como nada é fácil para o cidadão, .com.br é um dos domínios que o Cloudflare não atua como registrar. Assim como o .moe. Então ele só atua como DNS e cache mesmo.

Mas eu confesso que vou sentir falta do .moe. É uma decisão difícil.


Cotti.moe – uma breve eulogia


Foi uma disputa de prioridades entre o que me define e o que eu desejava. Certamente o charme da leviandade do .moe é a minha cara, ter que explicar o conceito é menos.


Mas a minha criança interior, ironicamente, queria a pomposidade que o .com.br proporcionava (na falta do .com, que hoje em dia é de uma marca de café, e da última vez que vi à venda custava a bagatela de pelo menos 50 mil dólares).


Inclusive a questão do nome foi um dos principais motivos para minha escolha na época do Homestead ao invés do gigante Geocities. Outra foi o espaço, 8 megabytes ao invés de uns… 5? Aí tão pedindo pra lembrar muito. Um simples subdomínio no 8m.com (que agora é controlado por um squatter chinês, que tristeza a queda do Homestead), ao invés das 3 ou 4 subpastas no Geocities. Estamos falando aqui de 1999, quando opções de redirecionamento como o cjb.net e o .tk ainda reinavam – ao custo de pop-ups. Opções menos invasivas eram muito bem-vindas, e lembro do Homestead ser mais tranquilo que o Geocities, que também tinha pop-ups.


Mas onde eu estava? Na vontade de ter um domínio e tal. Isso. Eu tive, não necessariamente nessa ordem, .biz, .net.br, .moe e agora .com.br. Talvez algum outro…? Realmente nem lembro mais. Faz parte também da minha década perdida, então já viu né.


Mas foi divertido. Passei por diferentes serviços de VPS, cada um comigo fazendo mais caquinha que o outro. Passei a ter o famoso email próprio, ainda que gerenciado por fora. Enquanto eu ainda for dono dele, vai redirecionar qualquer coisa pra cá, mas tenho medo dele virar bolor – ou pior, um squatter comprar. Hoje tem de tudo…


Mas se pá ainda tem espaço para projetos que com alguma certeza não devem ficar por aqui. Vai saber…

Até lá, você me acha aqui. Quer um email mais fácil que felipe@cotti.com.br?

Imortal em meus versos, Cotti.moe

Coisas que eu não vou conseguir entender tão cedo, parte 935612198

Por que StreamReader e StreamWriter não se dão bem com métodos de extensão…?

Fiquei uma semana empacado nessa bagaça. Acabou que uma ideia quase tão boa quanto – de criar um par de callbacks pra cuidar de serializar e desserializar as mensagens de uma Pipe – funciona direitinho e posso usar a lasanha de tipos que eu bolei.

Esse post estava n’O Bifão, uma tentativa (como todas as outras, falha) de manter uma espécie de blog nesses tempos tão difíceis em que tudo se resolve em volta de rede social.

Um ano de um trampo bem diferente, ainda que cheio de paralelos – que ajudaram na colocação pra começo de conversa – me fizeram ir pra vários lugares diferentes, tecnicamente.

Eu tinha pensado por um tempo já em fazer um balanço do que sem querer ou por querer aprendi no ano. Fui testado pelas circunstâncias e pelos requisitos, e deu pra construir um pequeno playground cutucando coisas aqui e ali.


1. AvaloniaUI

Já tinha um bom tempo que eu ficava de olho numa alternativa a fazer alguma coisa para desktop, com C#, que não fosse o WPF. Ou WinForms.

Ou WinUI. Ou UWP.

Ou Xamarin. Ou MAUI.

Francamente, a confusão que eu fiquei quando criei um projeto de brincadeira com MAUI não tá no gibi. E eu já me esqueci de tudo que eu já tinha decorado que era bom de fazer em um projeto novo com WPF. E, de toda forma, ser independente de plataforma é essencial pra mim.

Então veio a oportunidade de mexer com um produto em que eu pude escolher as coisas desde a incepção, e na criação da prova de conceito decidi prender a respiração e usar o AvaloniaUI, o qual já tinha ouvido falar anos atrás. O tempo para colocar as coisas em um nível testado era curto, então o ideal era não errar nessa escolha.

Confesso que minhas melhores expectativas foram superadas. É como o WPF devia ser desde o começo. Não que eu não tenha encontrado alguns pontos onde empaquei, ou onde eu não veja sentido nas escolhas (é um suplício trocar a cor da marca do CheckBox corretamente), mas a maior parte do tempo usando o AvaloniaUI é genuinamente divertido.

MessengerPlusSoundBankExtractor, um dos prováveis programas mais de nicho de todos os tempos, é um extrator de áudios de pacotes de backup do já extinto por mais de 10 anos Messenger Plus – e usa AvaloniaUI.


2. AppImage

Tá aí uma outra coisa que eu tinha curiosidade mas não tinha oportunidade. Desembolar um AppImage que funcionasse, e conseguir enxergar – ainda que parcialmente – a lógica por trás.

É bem satisfatório ter um programa para desktop prontinho pra GNU/Linux (e funcionando), sem mexer uma linha de código do que é gerado pra Windows.

Incidentalmente, isso foi em boa parte contribuição do excelente PupNet-Deploy, que serviu de base para depois sair futucando ingratamente no AppImage que ele criou até ver como as peças se encaixavam. Ler especificações é para quem nasceu sabendo, afinal.

O que ainda é um pouco confuso pra mim é que aparentemente existem múltiplos empacotadores de AppImage por aí. Mas depois eu aprendo o que está acontecendo. Certamente é melhor que Flatpak e Snap…


3. Instaladores

Criar um instalador (especialmente e especificamente para Windows) não é uma tarefa exatamente casual (especialmente e especificamente antes de aprender sobre o PupNet-Deploy, mas isso são outros nhenhenhentos).

Graças ao vídeo-tutorial-bateção-de-cabeça do AngelSix mostrando o passo-a-passo de criar um instalador usando o WiX (versão 3), eu eventualmente consegui criar um instalador usando o WiX (versão 3).

O bendito tutorial. Como eu tive paciência, você pergunta? Eu não tive, é a resposta.

A complexidade é um belo exemplar de Desnecessaurus Rex. Mas funcionou bem, e depois da configuração inicial não precisei mais fazer alterações.

A experiência de conhecer o mundo dos instaladores para Windows
A experiência de conhecer o mundo dos instaladores para Windows

4 – Não Deixe O Paralelismo Passar Fome Por Causa De Uma Linha

Necessidade é o Frank Zappa & As Mães da Invenção, já dizia o velho deitado. Quando você tem que usar uma biblioteca pra escrever um arquivo do Excel, pior ainda.

Passar por potencialmente todos os arquivos de um disco, que pode ser uma unidade de rede ainda por cima, tem um bom custo de I/O. Como muitas propriedades mais particulares deles eram usadas, tem uma quantidade interessante de syscalls sendo feitas.

Claro, um belo Paralell.ForEach() foi usado – com configurações bem-definidas (ainda que agressivas) de processamentos simultâneos, checagens colocadas no lugar certo se aquele loop deveria cessar, tudo do bom e do melhor.

Ele voa de cara!

Ele voa menos quando um disco em rede com mais de 2 milhões de arquivos é usado. Na verdade parava nos 8% depois de 25 horas.

Algo de errado não estava certo. Essas coisas deviam ir pra 100%. A conclusão é que alguma coisa estava causando thread starvation. Mas que tipo de coisa arcana e indomável podia fazer isso? Era o acesso à rede? Era essa busca por propriedades mais aprofundada nos arquivos?

Deixei um profiler rolando por uns minutos escaneando meu disco local todo. O problema tá lá também. Não era acesso à rede.

O que podia ser? Tudo que a gente fazia era pegar os arquivos, ler essas informações, jogar numa planilha de Excel, adicionar uma regra de validação em uma coluna…

Opa.

O profiler indicou o culpado – ninguém menos que o método de inserir validação na planilha do Excel. De alguma forma ele tende a travar a thread. E se a gente não ser ingenuidade e adicionar a validação de uma só vez selecionando a coluna inteira depois de adicionar todas as linhas?

Mudar uma linha de código de um lugar para o outro, e mudar o parâmetro de uma linha só para um range de colunas. Agora atravessar o disco de rede inteiro leva umas poucas horas, e não desacelera. Faz um tempo bem melhor que a solução anterior do cliente, que era feita em VB6.


5 – Patinho de borracha

Em meio a alucinações e devaneios, se esconde um bom patinho de borracha.

A inegável beleza da Internet contemporânea, em toda sua glória.

Patinhos de borracha substancialmente sujos e desbotados.

A inegável beleza da Internet contemporânea, em toda sua glória.

Assim é um LLM. Não caí de anel assim pelo ChatGPT da vida, mas descrever o problema e olhar com cuidado as respostas já me economizou um bom tempo.

Mas é impressionante como as coisas evoluíram de pouco tempo pra cá.

Eu tenho usado o Phind, que é otimizado para desenvolvimento, e provê algumas buscas usando GPT-4 por dia. Mas mesmo quando o modelo com o GPT-3.5 é usado, os resultados são na média muito bons.

É só garantir que não tem lama no meio. É relativamente comum alucinações em que são usadas APIs que foram removidas, por exemplo – mesmo quando a versão da coisa que você está usando é especificada.

LLM tem que RTFM um pouco mais.

Light