diff --git a/.github/workflows/just.yml b/.github/workflows/just.yml index fae7cce..3017a2d 100644 --- a/.github/workflows/just.yml +++ b/.github/workflows/just.yml @@ -18,3 +18,4 @@ jobs: - run: sudo apt-get update && sudo apt-get install -y just - run: just --version && just --summary - run: just figure-check + - run: just reading-time-check diff --git a/justfile b/justfile index 9d540e9..da1092f 100644 --- a/justfile +++ b/justfile @@ -14,17 +14,17 @@ figure-init: # Archify validates and checks each source (finalize), piabrota's look (profile/archify/piabrota.css) # goes over it, headless Chrome takes the picture. Needs node, jq, perl, Chrome (CHROME names another) # and the Google Fonts; writes profile/archify/figures.lock. -# Draw every profile/archify/.json to profile/img/.png +# Draw every profile/archify/.json to profile/img/.png, and pt/ to profile/img/pt/ figure: figure-init #!/usr/bin/env bash set -euo pipefail - mkdir -p build/figure profile/img - for src in profile/archify/*.json; do - slug="$(basename "$src" .json)"; out="build/figure/$slug" + mkdir -p build/figure profile/img/pt + for src in profile/archify/*.json profile/archify/pt/*.json; do + slug="${src#profile/archify/}"; slug="${slug%.json}"; base="$(basename "$slug")"; out="build/figure/$slug" rm -rf "$out"; mkdir -p "$out" ARCHIFY_UPDATE_CHECK_DISABLED=1 node vendor/archify/archify/bin/archify.mjs finalize "$(jq -r .diagram_type "$src")" "$src" "$out/page.html" --out-dir "$out/receipts" --quality "$(jq -r '.meta.quality_profile // "showcase"' "$src")" read -r w h < <(grep -o '|$ENV{FONTS}|; s| "$out/frame.html" "${CHROME:-google-chrome}" --headless --disable-gpu --hide-scrollbars --force-dark-mode --force-device-scale-factor=2 --user-data-dir="$PWD/$out/profile" --window-size="$w,$h" --virtual-time-budget=3000 --screenshot="$PWD/profile/img/$slug.png" "file://$PWD/$out/frame.html" 2>/dev/null @@ -36,18 +36,27 @@ _figure-lock: #!/usr/bin/env bash set -euo pipefail pin="$(git ls-files -s vendor/archify | cut -d' ' -f2)" - for src in profile/archify/*.json; do - printf '%s\t%s\n' "$(basename "$src" .json)" "$(cat "$src" profile/archify/piabrota.css profile/archify/crop.tsv | sha256sum | cut -c1-16)-${pin:0:7}" + for src in profile/archify/*.json profile/archify/pt/*.json; do + slug="${src#profile/archify/}"; printf '%s\t%s\n' "${slug%.json}" "$(cat "$src" profile/archify/piabrota.css profile/archify/crop.tsv | sha256sum | cut -c1-16)-${pin:0:7}" done # Cheap and offline: every source has its PNG, profile/README.md shows it, both match the last `just figure` figure-check: #!/usr/bin/env bash set -euo pipefail - for src in profile/archify/*.json; do - s="$(basename "$src" .json)" + for src in profile/archify/*.json profile/archify/pt/*.json; do + s="${src#profile/archify/}"; s="${s%.json}" + case "$s" in pt/*) readme=profile/README.pt-BR.md ;; *) readme=profile/README.md ;; esac test -s "profile/img/$s.png" || { echo "figure-check: no PNG for $src; run just figure" >&2; exit 1; } - grep -qF "](img/$s.png)" profile/README.md || { echo "figure-check: profile/README.md does not show img/$s.png" >&2; exit 1; } + grep -qF "](img/$s.png)" "$readme" || { echo "figure-check: $readme does not show img/$s.png" >&2; exit 1; } done just _figure-lock | cmp -s - profile/archify/figures.lock || { echo "figure-check: a source, the look or the archify pin changed since the PNG; run just figure" >&2; exit 1; } echo "figure-check: ok" + +# Write each README's reading-time badge +reading-time: + scripts/reading-time.sh --write + +# Fail when a README's reading-time badge is stale +reading-time-check: + scripts/reading-time.sh --check diff --git a/profile/README.md b/profile/README.md index 1261909..0a97fff 100644 --- a/profile/README.md +++ b/profile/README.md @@ -2,12 +2,13 @@ # pia, brota -Open Software Fabric made in RJ · in one minute · why it is open · projects · how it fits · board · partnerships · license +Open Software Fabric made in RJ · in one minute · why it is open · projects · how it fits · board · partnerships · license · 🇧🇷 português [![made in: Rio de Janeiro](https://img.shields.io/badge/made%20in-Rio%20de%20Janeiro-BC2318?style=flat-square)](#in-one-minute) [![code: FOSS](https://img.shields.io/badge/code-FOSS-1682D4?style=flat-square)](#license) [![rust](https://img.shields.io/badge/rust-first-1682D4?style=flat-square&logo=rust&logoColor=white)](#projects) [![web: piabrota.art](https://img.shields.io/badge/web-piabrota.art-1682D4?style=flat-square)](https://piabrota.art) +[![reading time: ~4 min](https://img.shields.io/badge/reading%20time-~4%20min-1682D4?style=flat-square)](#reading-time) @@ -105,6 +106,12 @@ The works with a DOI, by Matheus Hoffmann: - marola-bench: [10.5281/zenodo.23352633](https://doi.org/10.5281/zenodo.23352633) - nashkell, the open book: [10.5281/zenodo.23356739](https://doi.org/10.5281/zenodo.23356739) +## Reading time + +Both versions of this page show how long they take to read. `just reading-time` computes it, as +[marola](https://github.com/marola-dev/marola) does: the words a person reads, at 238 words per minute +in English and 181 in Portuguese (Brysbaert, 2019). CI fails when a badge is stale. + ## License This repository is under the [Mozilla Public License 2.0](../LICENSE). Each project has its own diff --git a/profile/README.pt-BR.md b/profile/README.pt-BR.md new file mode 100644 index 0000000..4f2a0a2 --- /dev/null +++ b/profile/README.pt-BR.md @@ -0,0 +1,118 @@ +
+ +# pia, brota + +Fábrica de software aberto feita no RJ · em um minuto · por que é aberto · projetos · como se encaixa · equipe · parcerias · licença · 🇬🇧 English + +[![feito no: Rio de Janeiro](https://img.shields.io/badge/feito%20no-Rio%20de%20Janeiro-BC2318?style=flat-square)](#em-um-minuto) +[![código: FOSS](https://img.shields.io/badge/c%C3%B3digo-FOSS-1682D4?style=flat-square)](#licença) +[![rust](https://img.shields.io/badge/rust-primeiro-1682D4?style=flat-square&logo=rust&logoColor=white)](#projetos) +[![web: piabrota.art](https://img.shields.io/badge/web-piabrota.art-1682D4?style=flat-square)](https://piabrota.art) +[![tempo de leitura: ~6 min](https://img.shields.io/badge/tempo%20de%20leitura-~6%20min-1682D4?style=flat-square)](#tempo-de-leitura) + +
+ +## Em um minuto + +- **piabrota é uma fábrica de software aberto, feita no Rio de Janeiro.** Cada projeto é um + repositório próprio, com README, licença e testes próprios. O código é aberto, e o método também. +- **Todo projeto é FOSS.** A maioria usa a Mozilla Public License 2.0. O texto do livro usa CC BY 4.0, + e o código do livro usa AGPL-3.0. +- **Os READMEs seguem uma disciplina:** um desenho no topo, uma linha de links, "em um minuto" e + figuras desenhadas com [archify](https://github.com/piabrota/archify) a partir de uma fonte + verificada. Cada figura tem um cartão que diz como ler a figura. +- **O domínio da org é [piabrota.art](https://piabrota.art).** Os projetos chegam ao domínio um + caminho por vez. + +## Por que é aberto + +- **Leia, use e construa o seu.** Você pode ter as suas próprias ideias lendo ou usando os + repositórios do piabrota. Os repositórios são documentação viva e prova do trabalho humano nos + bastidores. Com eles, outras pessoas podem construir o próprio projeto. +- **Feito com Claude Code, a caminho do FOSS.** O Claude Code constrói os projetos do piabrota na + maior parte do tempo. Mesmo assim, mantemos um esforço permanente para tornar a plataforma + piabrota [F]OSS. +- **Uma filosofia, não só uma economia.** A otimização de tokens e os modelos abertos, no Ollama e no + Ollaya, são a nossa forma de construir software aberto sobre software aberto, da forma mais agêntica + e acessível possível. + +## Projetos + +| | Projeto | O que é | Site | Licença | +|:---:|---|---|---|---| +| 無, mu, brushed in twelve strokes from Desmos equations | [**pirsig**](https://github.com/piabrota/pirsig) | Um segundo cérebro: PARA e um slip-box Zettelkasten no mesmo lugar. Um app em Rust com app de desktop, API REST e uma CLI de terminal, `psg`, sobre uma camada de modelo. | ainda não (piabrota.art/pirsig, em breve) | MPL-2.0 | +| A large G, like the one of the square and compasses, in the colours of the flag of Brazil | [**gramatico**](https://github.com/piabrota/gramatico) | Reescreve texto técnico em português brasileiro para leitura única: sujeito explícito, uma proposição por frase. É um plugin do Claude Code. O plugin marca com `⚠` o que não checou. | [piabrota.github.io/gramatico](https://piabrota.github.io/gramatico/) | MPL-2.0 | +| A Fibonacci wave and a bell, in one line | [**alerts**](https://github.com/piabrota/alerts) | Uma plataforma genérica de alertas. A plataforma coleta alertas só de fontes oficiais e entrega os alertas ao Discord e a um feed Atom com CAP 1.2. O primeiro risco coberto é o de enchentes no Sul do Brasil. | ainda não (piabrota.art/alerts, em breve) | MPL-2.0 | +| 代理, dairi, an agent, brushed in sixteen strokes among stars, inside an orbit | [**rax**](https://github.com/piabrota/rax) | Rustic Agentic Runtime: um port em Rust puro do AX, o orquestrador declarativo de tarefas agênticas sobre o Agent Substrate. O rax mantém a API do AX, então um manifesto escrito para o AX funciona aqui. | nenhum | MPL-2.0, partes do AX em Apache-2.0 | +| An open letter c with a dot, on a dark square | [**closed-source**](https://github.com/h0ffmann/closed-source) | *The god that failed*: um livro sobre por que o código fechado falhou como proteção, a começar pela ciência. Entra Markdown, sai PDF, toda referência é checada e toda versão é citável. | [h0ffmann.github.io/closed-source](https://h0ffmann.github.io/closed-source/) | texto CC BY 4.0, código AGPL-3.0 | +| 🎶 | [**ihero**](https://github.com/piabrota/ihero) | Um player de música e jogo FOSS, em Rust. Em breve: o repositório só tem a licença. | ainda não | MPL-2.0 | +| 无, mu, brushed in four strokes from Desmos equations | [**nix-os**](https://github.com/piabrota/nix-os) | A configuração NixOS da pequena VPS que serve piabrota.art/nixos. Cada mudança é julgada pelo que adiciona à closure e à memória. | [piabrota.art/nixos](https://piabrota.art/nixos) | MPL-2.0 | +| 🤖 | [**agents**](https://github.com/piabrota/agents) | Agentes pequenos que vigiam o nix-os e a VPS, e relatam no GitHub como piabrota-ops. Os agentes abrem pull requests e nunca fazem merge. | nenhum | MPL-2.0 | +| む, mu, brushed in its three strokes, in pure Nix | [**mu**](https://github.com/piabrota/mu) | O chart da empresa: o dogma, os padrões, o diário e o índice dos repositórios da org. | nenhum | CC BY 4.0, AGPL-3.0, MPL-2.0 | +| 道場 | [**dojo**](https://github.com/piabrota/dojo) | O umbrella dos projetos do piabrota. Em montagem. | ainda não | MPL-2.0 | + +Os desenhos carregam do repositório de cada projeto, em um commit fixado. Nenhum desenho é copiado +para cá. ihero, agents e dojo ainda não têm desenho. + +## Como se encaixa + +![A fábrica piabrota em cinco faixas: mu e dojo sustentam a empresa; pirsig, gramatico e closed-source pensam e escrevem; alerts, rax, nix-os e agents alertam e hospedam; ihero toca; alerts serve piabrota.art/alerts, nix-os serve piabrota.art/nixos, e pirsig vai chegar a piabrota.art/pirsig.](img/pt/fabric.png) + +
+Como ler esta figura + +**Em uma frase.** São dez projetos abertos, cada um por conta própria. Três deles chegam à web em piabrota.art. + +**Como ler.** Cada faixa é um tipo de trabalho. Uma seta significa "é servido em", e o rótulo da seta +é o caminho. "Em breve" significa planejado, ainda não publicado. A borda vermelha marca o domínio. + +**Fora da figura.** O site de cada projeto no GitHub Pages e as ferramentas em comum: Nix, Rust e +archify. + +**Evidência.** [`archify/pt/fabric.json`](archify/pt/fabric.json) é a fonte da figura. Os caminhos vêm +de `docs/HOSPEDAGEM.md` do alerts (`/alerts`), de `AGENTS.md` do nix-os (`/nixos`) e do README do +pirsig, "Ways to run Pirsig" (`/pirsig`, em breve). + +
+ +`just figure` desenha de novo com o archify fixado. `just figure-check` diz se o PNG está velho. + +## Equipe + +As pessoas que constroem o piabrota. Cada linha vem do perfil público da pessoa no GitHub. + +| | Pessoa | Sobre | +|:---:|---|---| +| O avatar de h0ffmann no GitHub | [**@h0ffmann**](https://github.com/h0ffmann)
Matheus Hoffmann | "water animal", em Florianópolis. Trabalha no pirsig, ptbr-writer, alerts e nix-os, escreve o closed-source e faz o [marola](https://marola.dev), o super app da água. As obras com DOI estão em [Como citar](#como-citar). | +| O avatar de aracyla no GitHub | [**@aracyla**](https://github.com/aracyla) | "General purpose developer currently focused on frontend development", no Brasil ([aracyla.xyz](https://aracyla.xyz)). Projetos pequenos em C para estudo: um Tetris e uma biblioteca de testes unitários. | +| O avatar de robkler no GitHub | [**@robkler**](https://github.com/robkler)
Rôb Klér Soares da Silva Júnior | "Senior Backend Engineer", no Rio de Janeiro: Go e JVM (Scala, Java), sistemas distribuídos, Kubernetes, AWS e GCP. Fez a extensão de navegador do [gramatico](https://github.com/piabrota/gramatico) (piabrota/gramatico#51) e é dono do código do gramatico com @h0ffmann. | + +## Parcerias + +Pedidos de parceria ou white label: mhoffmannfs [at] gmail.com. + +Para financiar o trabalho: [Open Collective](https://opencollective.com/piabrota) ou +[GitHub Sponsors](https://github.com/sponsors/h0ffmann). Todo repositório da org mostra o botão +Sponsor ([`.github/FUNDING.yml`](../.github/FUNDING.yml)). + +## Como citar + +As obras com DOI, de Matheus Hoffmann: + +- marola: [10.5281/zenodo.23224155](https://doi.org/10.5281/zenodo.23224155) +- ww3-gpu: [10.5281/zenodo.23221351](https://doi.org/10.5281/zenodo.23221351) +- picasso: [10.5281/zenodo.23311208](https://doi.org/10.5281/zenodo.23311208) +- closed source: [10.5281/zenodo.23333656](https://doi.org/10.5281/zenodo.23333656) +- marola-bench: [10.5281/zenodo.23352633](https://doi.org/10.5281/zenodo.23352633) +- nashkell, the open book: [10.5281/zenodo.23356739](https://doi.org/10.5281/zenodo.23356739) + +## Tempo de leitura + +As duas versões desta página mostram quanto tempo levam para ler. `just reading-time` faz a conta, como +faz o [marola](https://github.com/marola-dev/marola): as palavras que uma pessoa lê, a 238 palavras por +minuto em inglês e 181 em português (Brysbaert, 2019). O CI falha se um selo ficar velho. + +## Licença + +Este repositório usa a [Mozilla Public License 2.0](../LICENSE). Cada projeto tem a própria licença, +indicada na tabela acima. O nome e o logo PIABROTA são marcas da org e não estão sob essa licença. diff --git a/profile/archify/figures.lock b/profile/archify/figures.lock index a67f535..de27264 100644 --- a/profile/archify/figures.lock +++ b/profile/archify/figures.lock @@ -1 +1,2 @@ fabric 8f7431072134975f-208acba +pt/fabric 7f1c436bc32c482e-208acba diff --git a/profile/archify/pt/fabric.json b/profile/archify/pt/fabric.json new file mode 100644 index 0000000..71e1da0 --- /dev/null +++ b/profile/archify/pt/fabric.json @@ -0,0 +1 @@ +{"schema_version": 2, "diagram_type": "workflow", "meta": {"title": "A fábrica piabrota: dez projetos abertos, e onde eles chegam à web", "locale": "pt-BR", "quality_profile": "showcase", "legend": {"mode": "hidden"}, "output": "fabric.html"}, "lanes": [{"id": "company", "label": "A empresa"}, {"id": "think", "label": "Pensar e escrever"}, {"id": "serve", "label": "Alertar e hospedar"}, {"id": "play", "label": "Tocar e ouvir"}, {"id": "web", "label": "Na web"}], "mainPath": ["pirsig", "site"], "nodes": [{"id": "mu", "lane": "company", "col": 0, "type": "database", "label": "mu", "sublabel": "o chart da empresa", "width": 200}, {"id": "dojo", "lane": "company", "col": 1, "type": "database", "label": "dojo", "sublabel": "o umbrella, em montagem", "width": 200}, {"id": "gramatico", "lane": "think", "col": 0, "type": "backend", "label": "gramatico", "sublabel": "pt-BR claro, um plugin", "width": 200}, {"id": "pirsig", "lane": "think", "col": 1, "type": "frontend", "label": "pirsig", "sublabel": "um segundo cérebro", "width": 200}, {"id": "closed", "lane": "think", "col": 2, "type": "backend", "label": "closed-source", "sublabel": "um livro, md vira PDF", "width": 200}, {"id": "alerts", "lane": "serve", "col": 0, "type": "backend", "label": "alerts", "sublabel": "alertas oficiais", "width": 200}, {"id": "rax", "lane": "serve", "col": 1, "type": "backend", "label": "rax", "sublabel": "um runtime agêntico", "width": 200}, {"id": "nixos", "lane": "serve", "col": 2, "type": "database", "label": "nix-os", "sublabel": "a VPS NixOS", "width": 200}, {"id": "ihero", "lane": "play", "col": 2, "type": "frontend", "label": "ihero", "sublabel": "música e jogo, em Rust", "width": 200}, {"id": "site", "lane": "web", "col": 1, "type": "external", "label": "piabrota.art", "sublabel": "o domínio da org", "width": 200}, {"id": "agents", "lane": "serve", "col": 3, "type": "backend", "label": "agents", "sublabel": "vigiam o nix-os", "width": 200}], "edges": [{"id": "e1", "from": "alerts", "to": "site", "label": "/alerts"}, {"id": "e2", "from": "pirsig", "to": "site", "label": "/pirsig, em breve"}, {"id": "e3", "from": "nixos", "to": "site", "label": "/nixos"}]} diff --git a/profile/img/pt/fabric.png b/profile/img/pt/fabric.png new file mode 100644 index 0000000..454c2fb Binary files /dev/null and b/profile/img/pt/fabric.png differ diff --git a/scripts/reading-time.sh b/scripts/reading-time.sh new file mode 100755 index 0000000..6704154 --- /dev/null +++ b/scripts/reading-time.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Write or check the reading-time badge of the org profile, in both languages: computed, never by hand. +# The method is marola's (marola-tools reading-time). It counts the words a person reads, without code, +# comments, images, tags and URLs; a link keeps its text. The speed is Brysbaert (2019), Journal of Memory +# and Language 109, 104047: 238 words per minute in English, 181 in Portuguese. A code block adds 15 seconds. +# The result is rounded up to whole minutes. +# reading-time.sh --write rewrite each badge line +# reading-time.sh --check exit 1 when a badge is missing or stale +set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")/.." +mode="${1:-}" +case "$mode" in --write|--check) ;; *) echo "usage: reading-time.sh --write|--check" >&2; exit 2 ;; esac +export MODE="$mode" +perl -CSD -Mutf8 -e ' + use POSIX qw(ceil); + my $mode = $ENV{MODE}; my $bad = 0; + my @files = (["profile/README.md", 238, "reading time", "reading%20time", "#reading-time"], + ["profile/README.pt-BR.md", 181, "tempo de leitura", "tempo%20de%20leitura", "#tempo-de-leitura"]); + for my $f (@files) { + my ($path, $wpm, $alt, $label, $anchor) = @$f; + open(my $in, "<", $path) or die "$path: $!"; local $/; my $text = <$in>; close $in; + my $p = $text; my $blocks = 0; + $blocks++ while $p =~ s/^```.*?^```[ \t]*$//ms; + $p =~ s// /gs; $p =~ s/!\[[^\]]*\]\([^)]*\)/ /g; $p =~ s/\[([^\]]*)\]\([^)]*\)/$1/g; + $p =~ s/<[^>\n]+>/ /g; $p =~ s{https?://\S+}{ }g; + my $words = grep { /[\p{L}\p{N}]/ } split " ", $p; + my $min = ceil($words / $wpm + $blocks * 0.25); $min = 1 if $min < 1; + my $line = "[![$alt: ~$min min](https://img.shields.io/badge/$label-~$min%20min-1682D4?style=flat-square)]($anchor)"; + my $re = qr/^\[!\[\Q$alt\E: [^\n]*$/m; + if ($mode eq "--write") { + $text =~ s/$re/$line/ or die "$path: no badge line\n"; + open(my $out, ">", $path) or die "$path: $!"; print $out $text; close $out; + print "$path: $words words, ~$min min\n"; + } elsif ($text !~ /^\Q$line\E$/m) { + print STDERR "reading-time: $path is stale, want ~$min min; run just reading-time\n"; $bad = 1; + } else { print "$path: ~$min min\n"; } + } + exit $bad; +'