Rendere pubblico un repo: cosa ho controllato prima del primo push
Questo articolo fa parte della serie «Un repo per i miei articoli». Per rendere pubblico lo strumento descritto in Tenere gli articoli di Dev.to in Git e accorgersi quando il portale cambia, devto-workbench, sono dovuto
Questo articolo fa parte della serie «Un repo per i miei articoli». Per rendere pubblico lo strumento descritto in Tenere gli articoli di Dev.to in Git e accorgersi quando il portale cambia, devto-workbench, sono dovuto partire da un repo nuovo, perché in quello degli articoli ci sono le bozze. Qui racconto come convivono i due repo, cosa ho controllato prima del primo push e perché ho messo un hook di git che lo fa da solo.
Perché non bastava cambiare la visibilità
Su GitHub la visibilità appartiene all'intero repo, non a una cartella: un monorepo con una parte pubblica e una privata non esiste. Il problema vero però è la storia. Rendere pubblico il repo degli articoli avrebbe mostrato ogni versione di ogni bozza, comprese quelle poi riscritte o tagliate, e i messaggi dei commit. Prima era tutto in un solo repo, e solo una parte di quel contenuto poteva diventare pubblica:
flowchart TB
subgraph prima["Un solo repo privato"]
A["articles/ e assets/<br>articoli e bozze"]
B["skill di scrittura<br>la mia voce"]
C["web/<br>server e pagina"]
D["scripts/<br>importazione"]
E["skill di sync"]
end
A --> P["resta privato"]
B --> P
C --> U["va nel repo pubblico"]
D --> U
E --> U
La soluzione è separare i due repo: lo strumento in un repo pubblico, gli articoli in uno privato che include il primo come submodule. Un submodule è un repo dentro un altro, con un puntatore a un commit preciso: il repo privato non tiene una copia del codice, ricorda soltanto quale versione usa.
flowchart LR
subgraph priv["Repo privato"]
A["articles/ e assets/"]
B["skill di scrittura"]
S["tools/devto-workbench<br>puntatore a un commit"]
end
subgraph pub["Repo pubblico devto-workbench"]
C["web/, scripts/<br>skill di sync"]
end
S -->|"punta al commit"| C
git submodule add https://github.com/minnogit/devto-workbench.git tools/devto-workbench
Il repo pubblico è nato con un solo commit iniziale, invece di estrarre i file dello strumento insieme alla loro storia. Estraendo la storia mi sarei portato dietro i messaggi dei commit che toccano anche gli articoli.
Il prezzo sono due cose da ricordare. Chi clona il repo privato deve usare git clone --recurse-submodules (oppure git submodule update --init dopo un clone normale), altrimenti la cartella dello strumento resta vuota.
La seconda riguarda le modifiche allo strumento. In entrambi i percorsi il repo privato continua a puntare alla versione vecchia finché non registra il nuovo commit con un commit suo:
flowchart LR
subgraph A["Percorso A: modifico dentro il submodule"]
A1["modifico i file in<br>tools/devto-workbench"] --> A2["commit e push<br>vanno nel repo pubblico"] --> A3["nel repo privato:<br>commit del nuovo puntatore"]
end
subgraph B["Percorso B: modifico in un clone separato"]
B1["modifico i file nel<br>clone del repo pubblico"] --> B2["commit e push<br>vanno nel repo pubblico"] --> B3["nel repo privato:<br>git submodule update --remote"] --> B4["nel repo privato:<br>commit del nuovo puntatore"]
end
A3 --> F["il repo privato usa<br>la versione nuova"]
B4 --> F
Nel percorso A si lavora dentro la cartella del submodule, con commit e push nel repo pubblico, e poi nel repo privato si fa il commit che aggiorna il puntatore. Nel percorso B si lavora in un clone separato del repo pubblico. Poi, nel repo privato, si scarica l'ultima versione dello strumento e si registra il nuovo puntatore:
git submodule update --remote tools/devto-workbench
git add tools/devto-workbench
git commit -m "Aggiorna il submodule devto-workbench"
Se si dimentica update --remote, la cartella resta alla versione vecchia anche se su GitHub c'è già quella nuova.
Controllare prima, non dopo
Un segreto (una chiave API, un token, una password) si può cercare in due momenti: prima che il codice parta dal mio computer, oppure dopo che è arrivato su GitHub. Il secondo, da solo, su un repo pubblico arriva tardi.
GitGuardian, collegato a GitHub come app, ascolta gli eventi allo stadio post-receive, cioè dopo che il codice è stato inviato, e descrive come risposta gli incidenti e gli avvisi. Su un repo pubblico questo significa che l'avviso arriva quando il segreto è già visibile a chiunque. Il riferimento è la pagina How Internal Monitoring works.
GitHub ha una sua protezione sul push. Secondo la documentazione quella a livello di utente è attiva di default e vale per i repo pubblici su GitHub.com, mentre quella a livello di repo è disattivata di default.
C'è poi un motivo pratico: se il segreto è già arrivato, ripulire costa molto. Riscrivere la storia con un force-push non basta, perché GitHub tiene un riferimento per ogni pull request e i vecchi commit restano raggiungibili dai loro indirizzi diretti. Per quanto ne so può ripulirli solo il supporto di GitHub.
Servono quindi controlli sul computer, prima che il codice parta.
L'hook che lo fa da solo
Ho aggiunto ggshield, la riga di comando di GitGuardian, il servizio già collegato a GitHub. Si può lanciare a mano su tutta la storia:
ggshield secret scan repo .
La risposta è stata «No secrets have been found». Il passo che conta è farlo partire da solo. ggshield lo installa come hook di git:
ggshield install --mode local -t pre-commit
ggshield install --mode local -t pre-push
Il pre-commit blocca il segreto prima che entri nella storia. Il pre-push è la seconda barriera, subito prima dell'invio. Per aggirarli, secondo la documentazione del pre-commit, basta git commit -n oppure SKIP=ggshield git commit.
Provare l'hook con un token finto
Per vedere se l'hook funziona conviene committare un token finto, ma non uno qualunque. Un ghp_ seguito da 36 caratteri casuali non viene riconosciuto: l'hook parte, risponde «No secrets have been found» e il commit passa. Un token finto con il checksum corretto invece viene riconosciuto come «GitHub Personal Access Token» (con validità «Invalid») e il commit è rifiutato. Se il commit di prova passa, va annullato con git reset --hard e il reflog ripulito.
I limiti degli hook
- Sono locali e non vengono versionati: chi clona il repo non li ha.
- Il
pre-pushdi ggshield per default non scansiona i push con più di 50 commit (dalla documentazione, configurabile conmax-commits-for-hook). Con una storia lunga conviene lanciare a parte la scansione completa conscan repo.
Cosa un hook non vede
Un hook cerca segreti. Non sa se nel repo c'è qualcosa che non dovrebbe esserci, e queste cose le ho controllate a mano:
- Il server legge
articles/eassets/dalla cartella da cui lo lancio e le crea se mancano, quindi ungit add -Adistratto dal repo pubblico le metterebbe in un commit. Stanno nel.gitignore, insieme a.mcp.json: nel repo c'è un.mcp.json.examplecon percorsi e segreti finti. - Ogni commit porta nome ed email dell'autore, e su un repo pubblico li legge chiunque. Per il repo nuovo ho scelto l'indirizzo noreply, nella forma
<id>+<utente>@users.noreply.github.com, e l'ho impostato prima del primo push congit commit --amend --reset-author --no-edit: dopo, cambiare l'autore vorrebbe dire riscrivere una storia già pubblicata. - Durante le scansioni ggshield crea nella cartella corrente un file
.cache_ggshield, con dentro l'hash dei segreti trovati. Con ungit add -Adistratto finirebbe nel commit, quindi va nel.gitignore. - Uno scanner di segreti non dice se tra i file tracciati ci sono file di configurazione personali, file di terzi senza avviso di copyright o una licenza che manca. Per questo prima di pubblicare conviene leggere l'elenco con
git ls-files.
Il repo è pubblico e tutti i suoi commit hanno l'autore noreply.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.