Questa guida spiega come configurare una pipeline di GitLab per aggiornare automaticamente una repository Git remota e illustra le principali best practice per farlo in modo sicuro, ripetibile e osservabile.
Prerequisiti
- Progetto ospitato su GitLab con Runners disponibili.
- Permessi di scrittura sul branch di destinazione.
- Un token con permessi di scrittura alla repository: consigliato un Project Access Token o Deploy Token con scope
write_repository
(in alternativa un Personal Access Token limitato).
Strategia generale
- La pipeline parte su un evento (push, tag, merge, schedule o trigger).
- Un job prepara l’area di lavoro, applica le modifiche (es. aggiornamento versioni o contenuti generati) e le committa.
- Il job esegue il push verso il branch remoto o apre una merge request automatica, a seconda della policy di protezione del branch.
Variabili d’ambiente consigliate
Imposta variabili protette nelle Settings > CI/CD > Variables:
GIT_PUSH_USER_NAME
eGIT_PUSH_USER_EMAIL
per firmare i commit di bot.REPO_PUSH_TOKEN
con scope minimo necessario (write_repository
), marcata come Protected e Masked.- Eventuali credenziali per registri o servizi esterni.
Pipeline di base: struttura
stages:
- test
- build
- update
default:
image: alpine:3.20
before_script:
- apk add --no-cache git bash curl
- git config --global user.name "\${GIT\_PUSH\_USER\_NAME:-ci-bot}"
- git config --global user.email "\${GIT\_PUSH\_USER\_EMAIL:[-ci-bot@example.local](mailto:-ci-bot@example.local)}"
# Evita problemi di "detected dubious ownership" nei container recenti
- git config --global --add safe.directory "\$CI\_PROJECT\_DIR"
variables:
GIT_STRATEGY: fetch # più veloce di clone completo nella maggior parte dei casi
GIT_DEPTH: "50" # shallow fetch per velocizzare
FF_USE_FASTZIP: "true" # migliora performance dei cache/artifacts recenti
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- .cache/
Esempio 1: aggiornare file e effettuare un push sullo stesso branch
Usa un token sicuro via HTTPS; evita SSH in ambienti ephemeral. Qui si ipotizza che il branch non sia Protected o che l’utente/il token abbiano permessi di push sul branch.
update:push-current-branch:
stage: update
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- |
# Applica modifiche (esempio: bump patch in package.json)
./scripts/bump_version.sh # <-- il tuo script
# Staging & commit
git add -A
if git diff --cached --quiet; then
echo "Nessuna modifica: salto il push."
exit 0
fi
git commit -m "chore(ci): aggiornamenti automatici [skip ci]"
- |
# Reimposta l'origin con token per evitare di loggare il token nella history
git remote set-url origin "https://oauth2:${REPO_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
# Evita conflitti: rebase veloce e push
git pull --rebase origin "$CI_COMMIT_BRANCH"
git push origin "HEAD:$CI_COMMIT_BRANCH"
dependencies: []
needs: []
allow_failure: false
artifacts:
when: always
expire_in: 1 week
reports:
dotenv: update.env
Esempio 2: aprire una merge request automatica
Per branch protetti, preferisci aprire una MR invece di pushare direttamente.
update:open-mr:
stage: update
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
image: alpine:3.20
script:
- apk add --no-cache git bash curl jq
- base_branch="$CI_DEFAULT_BRANCH"
- work_branch="ci/update-$(date +%Y%m%d-%H%M%S)"
- git checkout -b "$work_branch"
- ./scripts/generate_docs.sh
- git add -A
- |
if git diff --cached --quiet; then
echo "Nessuna modifica: esco."
exit 0
fi
- git commit -m "docs: aggiorna documentazione (auto)"
- git remote set-url origin "https://oauth2:${REPO_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
- git push origin "$work_branch"
- |
# Crea MR via API
curl --fail -sS --request POST \
--header "PRIVATE-TOKEN: ${REPO_PUSH_TOKEN}" \
--data-urlencode "source_branch=${work_branch}" \
--data-urlencode "target_branch=${base_branch}" \
--data-urlencode "remove_source_branch=true" \
--data-urlencode "title=Aggiornamenti automatici: ${work_branch}" \
"https://${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/merge_requests" \
| jq -r '.web_url'
Gestione credenziali e sicurezza
- Usa variabili protette e mascherate per i token; non inserirli nello YAML in chiaro.
- Limita lo scope e l’expiration dei token; preferisci token di progetto a quelli personali.
- Abilita i job di update solo su branch specifici con
rules
eonly/except
. - Evita di stampare il token nei log. Non usare echo del remote contenente credenziali.
- Per branch protetti, utilizza approvazioni MR e Code Owners.
Conflitti e sincronizzazione
Quando la pipeline committa, possono verificarsi conflitti con nuovi push:
- Esegui
git pull --rebase
prima del push per mantenere una storia lineare. - Se il rebase fallisce, fallisci il job e riprova nella run successiva o apri una MR.
- Valuta l’uso di un branch dedicato al bot (es.
ci/updates
) con MR ricorrente.
Ottimizzazioni di performance
GIT_DEPTH
per ridurre la storia scaricata; aumenta il valore per step che leggono tag/versioni.- Cache per dipendenze ripetibili e artifacts per passare build output tra stage.
- Runner con tag coerenti e immagini leggere (Alpine) quando possibile.
Osservabilità e audit
- Usa messaggi di commit chiari e convenzionali (es.
chore(ci): ...
). - Contrassegna i commit bot con
[skip ci]
quando non vuoi innescare pipeline ricorsive. - Pubblica log sintetici come artifact di testo (es. changelog generato).
Best practice riassunte
- Principio del minimo privilegio: token con scope limitato, scadenza breve, variabili protette.
- Branch protetti e MR automatiche: preferiscile per modifiche generate.
- Evitare loop di pipeline: usa
[skip ci]
nei commit generati. - Idempotenza: gli script devono poter essere eseguiti più volte senza effetti collaterali inattesi.
- Tracciabilità: messaggi di commit coerenti, artifact dei log e changelog aggiornati.
- Gestione conflitti: rebase prima del push, fallimento esplicito se non risolvibile automaticamente.
- Separazione dei compiti: job di test/build distinti dai job di aggiornamento.
- Velocità senza sacrificare la correttezza:
GIT_DEPTH
e cache dove possibile, ma fetch completo quando servono tag o history.
Verifiche finali
- Il token configurato ha lo scope minimo e non scade prima dell’esecuzione pianificata.
- Le regole del job impediscono trigger in branch inattesi.
- Gli script di aggiornamento producono modifiche deterministiche e testate.
- Se il branch è protetto, l’apertura MR è automatica e con revisori assegnati via regole del progetto.