{"id":1181,"date":"2025-09-08T11:16:30","date_gmt":"2025-09-08T11:16:30","guid":{"rendered":"https:\/\/davidka.net\/?p=1181"},"modified":"2025-09-15T23:57:32","modified_gmt":"2025-09-15T21:57:32","slug":"ci-cd-from-basics-to-production","status":"publish","type":"post","link":"https:\/\/it.davidka.net\/it\/ci-cd-from-basics-to-production\/","title":{"rendered":"\ud83d\ude80 CI\/CD: Dalle basi alla produzione su GCP con GitHub Actions \u2013 Una guida completa con esempi \ud83d\ude80"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Ciao, sviluppatori! In questo articolo parler\u00f2 di CI\/CD \u2013 un concetto.<\/p>\n\n\n\n<div id=\"rtoc-mokuji-wrapper\" class=\"rtoc-mokuji-content frame2 preset1 animation-fade rtoc_open default\" data-id=\"1181\" data-theme=\"Neve - Davidka\">\n\t\t\t<div id=\"rtoc-mokuji-title\" class=\"rtoc_btn_none rtoc_left\">\n\t\t\t\n\t\t\t<span>In Questo Articolo<\/span>\n\t\t\t<\/div><ul class=\"rtoc-mokuji mokuji_ul level-2\"><li class=\"rtoc-item\"><a href=\"#rtoc-1\">Cos&#8217;\u00e8 una pipeline CI\/CD nel contesto della programmazione?<\/a><ul class=\"rtoc-mokuji mokuji_ul level-2\"><li class=\"rtoc-item\"><a href=\"#rtoc-2\">\ud83d\udd04 Di cosa \u00e8 composta di solito una pipeline CI\/CD?<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-3\">\ud83d\udee0 Strumenti popolari per CI\/CD:<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-4\">\ud83e\udde0 Perch\u00e9 abbiamo bisogno di CI\/CD?<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-5\">\ud83d\udce6 Esempi semplici di CI\/CD con GitHub Actions<\/a><ul class=\"rtoc-mokuji mokuji_none level-3\"><li class=\"rtoc-item\"><a href=\"#rtoc-6\">\ud83d\udc0d CI\/CD per Python (con <code class=\"\" data-line=\"\">pytest<\/code> e <code class=\"\" data-line=\"\">flake8<\/code>)<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-7\">\ud83c\udf10 CI\/CD per Node.js (con <code class=\"\" data-line=\"\">npm test<\/code> e <code class=\"\" data-line=\"\">eslint<\/code>)<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-8\">\ud83d\udc33 CI\/CD per Docker (build e push su Docker Hub)<\/a><\/li><\/ul><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-9\">\ud83d\ude9a Distribuzione su piattaforme popolari<\/a><ul class=\"rtoc-mokuji mokuji_none level-3\"><li class=\"rtoc-item\"><a href=\"#rtoc-10\">\ud83d\udfe3 Distribuzione su Heroku<\/a><\/li><\/ul><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-11\">\ud83d\udfe8 Distribuzione su AWS (ad esempio, file statici su S3)<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-12\">\ud83d\udd35 Distribuzione su Google Cloud Platform (GCP App Engine)<\/a><ul class=\"rtoc-mokuji mokuji_none level-3\"><li class=\"rtoc-item\"><a href=\"#rtoc-13\">\ud83d\udfea Distribuzione su Render.com<\/a><\/li><\/ul><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-14\">\ud83c\udf1f CI\/CD avanzato: Build Docker \u2192 Push su GHCR \u2192 Staging\/Production su GCP Cloud Run<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-15\">1. Build e pubblicazione dell&#8217;immagine Docker su GHCR<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-16\">2. Distribuzione automatica su Staging (GCP Cloud Run)<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-17\">3. Distribuzione in produzione con conferma manuale (GCP Cloud Run)<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-18\">Punti importanti di questa pipeline avanzata:<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-19\">\ud83d\udd10 Sicurezza \u2013 \u00e8 importante!<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-20\">Cos&#8217;\u00e8 una pipeline CI\/CD nel contesto della programmazione?<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-21\">\ud83d\udd04 Di cosa \u00e8 composta di solito una pipeline CI\/CD?<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-22\">\ud83d\udee0 Strumenti popolari per CI\/CD:<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-23\">\ud83e\udde0 Perch\u00e9 abbiamo bisogno di CI\/CD?<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-24\">\ud83d\udce6 Esempi semplici di CI\/CD con GitHub Actions<\/a><ul class=\"rtoc-mokuji mokuji_none level-3\"><li class=\"rtoc-item\"><a href=\"#rtoc-25\">\ud83d\udc0d CI\/CD per Python (con <code class=\"\" data-line=\"\">pytest<\/code> e <code class=\"\" data-line=\"\">flake8<\/code>)<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-26\">\ud83c\udf10 CI\/CD per Node.js (con <code class=\"\" data-line=\"\">npm test<\/code> e <code class=\"\" data-line=\"\">eslint<\/code>)<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-27\">\ud83d\udc33 CI\/CD per Docker (build e push su Docker Hub)<\/a><\/li><\/ul><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-28\">\ud83d\ude9a Distribuzione su piattaforme popolari<\/a><ul class=\"rtoc-mokuji mokuji_none level-3\"><li class=\"rtoc-item\"><a href=\"#rtoc-29\">\ud83d\udfe3 Distribuzione su Heroku<\/a><\/li><\/ul><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-30\">\ud83d\udfe8 Distribuzione su AWS (ad esempio, file statici su S3)<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-31\">\ud83d\udd35 Distribuzione su Google Cloud Platform (GCP App Engine)<\/a><ul class=\"rtoc-mokuji mokuji_none level-3\"><li class=\"rtoc-item\"><a href=\"#rtoc-32\">\ud83d\udfea Distribuzione su Render.com<\/a><\/li><\/ul><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-33\">\ud83c\udf1f CI\/CD avanzato: Build Docker \u2192 Push su GHCR \u2192 Staging\/Production su GCP Cloud Run<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-34\">1. Build e pubblicazione dell&#8217;immagine Docker su GHCR<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-35\">2. Distribuzione automatica su Staging (GCP Cloud Run)<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-36\">3. Distribuzione in produzione con conferma manuale (GCP Cloud Run)<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-37\">Punti importanti di questa pipeline avanzata:<\/a><\/li><li class=\"rtoc-item\"><a href=\"#rtoc-38\">\ud83d\udd10 Sicurezza \u2013 \u00e8 importante!<\/a><\/li><\/ul><\/li><\/ol><\/div><h3 id=\"rtoc-1\"  class=\"wp-block-heading\">Cos&#8217;\u00e8 una pipeline CI\/CD nel contesto della programmazione?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Una pipeline CI\/CD (Continuous Integration \/ Continuous Delivery o Continuous Deployment)<\/strong> \u00e8 un processo automatizzato che consente agli sviluppatori di consegnare rapidamente e in modo affidabile le modifiche al codice in un ambiente di produzione.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Analizziamo i concetti chiave:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd27 <strong>CI \u2014 Continuous Integration (Integrazione Continua)<\/strong><br>Questa \u00e8 una pratica in cui gli sviluppatori integrano frequentemente le modifiche in una codebase condivisa. Ogni modifica di questo tipo viene automaticamente: * <strong>Costruita<\/strong> (build) * <strong>Testata<\/strong> (test unitari, test di integrazione) * <strong>Verificata per la conformit\u00e0 agli standard<\/strong> (linting, analisi statica)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udc49 **Obiettivo della CI:** Identificare gli errori il prima possibile, prima che rompano qualcosa di importante o finiscano in una release.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\ude80 **CD \u2014 Continuous Delivery (Consegna Continua) o Continuous Deployment (Distribuzione Continua)**<br>Qui ci sono due opzioni:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 **Continuous Delivery (Consegna Continua)**<br>Dopo il successo della fase CI, le modifiche vengono automaticamente: * Sottoposte a test aggiuntivi (ad esempio, test E2E \u2013 end-to-end) * Distribuite su un server di staging (test)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udc49 **Ma la distribuzione in produzione richiede ancora una conferma manuale.** Questo d\u00e0 al team il controllo su *quando* esattamente gli utenti vedranno le modifiche.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83e\udd16 **Continuous Deployment (Distribuzione Continua)**<br>Questo \u00e8 il passo successivo dopo la Continuous Delivery. Qui, la distribuzione in produzione avviene **completamente automaticamente**, se tutte le fasi precedenti della pipeline (build, tutti i test) sono state completate con successo. Questo \u00e8 il livello pi\u00f9 avanzato di automazione.<\/p>\n\n\n\n<h3 id=\"rtoc-2\"  class=\"wp-block-heading\">\ud83d\udd04 Di cosa \u00e8 composta di solito una pipeline CI\/CD?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Una pipeline tipica include le seguenti fasi:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Checkout<\/strong> \u2014 Clonazione dell&#8217;ultima versione del codice dal repository.<\/li>\n\n\n\n<li><strong>Build<\/strong> \u2014 Costruzione del progetto (compilazione, assemblaggio di artefatti, immagini Docker).<\/li>\n\n\n\n<li><strong>Test<\/strong> \u2014 Esecuzione di vari tipi di test (unitari, di integrazione, E2E).<\/li>\n\n\n\n<li><strong>Lint\/Code Quality<\/strong> \u2014 Controllo del codice per la conformit\u00e0 allo stile e potenziali errori utilizzando analizzatori statici.<\/li>\n\n\n\n<li><strong>Deploy<\/strong> \u2014 Distribuzione dell&#8217;applicazione (su un server di staging o di produzione).<\/li>\n\n\n\n<li><strong>Notify<\/strong> \u2014 Invio di notifiche sullo stato della pipeline al team (ad esempio, in Slack, Email).<\/li>\n<\/ol>\n\n\n\n<h3 id=\"rtoc-3\"  class=\"wp-block-heading\">\ud83d\udee0 Strumenti popolari per CI\/CD:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>GitHub Actions<\/strong> (il nostro focus oggi!)<\/li>\n\n\n\n<li>GitLab CI\/CD<\/li>\n\n\n\n<li>Jenkins<\/li>\n\n\n\n<li>CircleCI<\/li>\n\n\n\n<li>Bitbucket Pipelines<\/li>\n\n\n\n<li>Azure DevOps<\/li>\n\n\n\n<li>TeamCity<\/li>\n<\/ul>\n\n\n\n<h3 id=\"rtoc-4\"  class=\"wp-block-heading\">\ud83e\udde0 Perch\u00e9 abbiamo bisogno di CI\/CD?<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Riduce l&#8217;errore umano:<\/strong> L&#8217;automazione elimina gli errori associati alle operazioni manuali.<\/li>\n\n\n\n<li><strong>Rilevamento rapido dei bug:<\/strong> Gli errori vengono trovati prima, rendendoli pi\u00f9 facili e meno costosi da correggere.<\/li>\n\n\n\n<li><strong>Automazione delle attivit\u00e0 di routine:<\/strong> Gli sviluppatori dedicano meno tempo alla costruzione e alla distribuzione e pi\u00f9 tempo alla codifica.<\/li>\n\n\n\n<li><strong>Miglioramento della qualit\u00e0 del codice:<\/strong> Controlli e test continui aumentano il livello di qualit\u00e0 complessivo.<\/li>\n\n\n\n<li><strong>Consegna rapida delle funzionalit\u00e0 agli utenti:<\/strong> Le nuove funzionalit\u00e0 raggiungono l&#8217;utente finale pi\u00f9 rapidamente e pi\u00f9 frequentemente.<\/li>\n<\/ul>\n\n\n\n<h3 id=\"rtoc-5\"  class=\"wp-block-heading\">\ud83d\udce6 Esempi semplici di CI\/CD con GitHub Actions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Diamo un&#8217;occhiata alle pipeline di base per le tecnologie pi\u00f9 diffuse. Tutti gli esempi utilizzano GitHub Actions e vengono salvati nella directory <code class=\"\" data-line=\"\">.github\/workflows\/<\/code> del tuo progetto.<\/p>\n\n\n\n<h4 id=\"rtoc-6\"  class=\"wp-block-heading\">\ud83d\udc0d CI\/CD per Python (con <code class=\"\" data-line=\"\">pytest<\/code> e <code class=\"\" data-line=\"\">flake8<\/code>)<\/h4>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/python-ci.yml\nname: Python CI\n\non: &#091;push, pull_request]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - name: Set up Python\n        uses: actions\/setup-python@v4\n        with:\n          python-version: &#039;3.11&#039; # Specifica la tua versione\n\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          pip install -r requirements.txt # Assicurati di avere requirements.txt\n          pip install flake8 pytest\n\n      - name: Lint with flake8\n        run: |\n          # Controlla il codice nelle cartelle src e tests (adatta al tuo progetto)\n          flake8 src tests\n\n      - name: Run tests\n        run: |\n          pytest\n<\/code><\/pre>\n\n\n\n<h4 id=\"rtoc-7\"  class=\"wp-block-heading\">\ud83c\udf10 CI\/CD per Node.js (con <code class=\"\" data-line=\"\">npm test<\/code> e <code class=\"\" data-line=\"\">eslint<\/code>)<\/h4>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/node-ci.yml\nname: Node.js CI\n\non: &#091;push, pull_request]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: &#091;18.x] # Specifica la tua versione di Node.js\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions\/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Install dependencies\n        run: npm install # o npm ci per un&#039;installazione pi\u00f9 prevedibile\n\n      - name: Lint with ESLint\n        run: npx eslint . # Assicurati che ESLint sia configurato nel progetto\n\n      - name: Run tests\n        run: npm test\n<\/code><\/pre>\n\n\n\n<h4 id=\"rtoc-8\"  class=\"wp-block-heading\">\ud83d\udc33 CI\/CD per Docker (build e push su Docker Hub)<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Per questo esempio, avrai bisogno dei segreti <code class=\"\" data-line=\"\">DOCKER_USERNAME<\/code> e <code class=\"\" data-line=\"\">DOCKER_PASSWORD<\/code> (o un token) nelle impostazioni del tuo repository GitHub (<code class=\"\" data-line=\"\">Settings -&gt; Secrets and variables -&gt; Actions<\/code>).<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/docker-ci.yml\nname: Docker CI\/CD\n\non:\n  push:\n    branches: &#091; main ] # Esegui solo per il branch main\n\njobs:\n  docker:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - name: Log in to Docker Hub\n        run: echo &quot;${{ secrets.DOCKER_PASSWORD }}&quot; | docker login -u &quot;${{ secrets.DOCKER_USERNAME }}&quot; --password-stdin\n\n      - name: Build Docker image\n        # Sostituisci myapp con il nome della tua applicazione\n        run: docker build -t ${{ secrets.DOCKER_USERNAME }}\/myapp:latest .\n\n      - name: Push Docker image\n        run: docker push ${{ secrets.DOCKER_USERNAME }}\/myapp:latest\n<\/code><\/pre>\n\n\n\n<h3 id=\"rtoc-9\"  class=\"wp-block-heading\">\ud83d\ude9a Distribuzione su piattaforme popolari<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ora che abbiamo artefatti costruiti e testati (ad esempio, un&#8217;immagine Docker), vediamo come possono essere distribuiti.<\/p>\n\n\n\n<h4 id=\"rtoc-10\"  class=\"wp-block-heading\">\ud83d\udfe3 Distribuzione su Heroku<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud83d\udd10 Segreti GitHub:<\/strong> <code class=\"\" data-line=\"\">HEROKU_API_KEY<\/code>, <code class=\"\" data-line=\"\">HEROKU_APP_NAME<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-heroku.yml\nname: Deploy to Heroku\n\non:\n  push:\n    branches: &#091;main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v3\n      - name: Install Heroku CLI\n        run: curl https:\/\/cli-assets.heroku.com\/install.sh | sh\n      - name: Login to Heroku\n        env:\n          HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}\n        run: heroku auth:token\n      - name: Deploy to Heroku\n        env:\n          HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}\n        run: |\n          heroku git:remote -a ${{ secrets.HEROKU_APP_NAME }}\n          git push heroku main -f # Fai attenzione con -f (force push)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se stai distribuendo un&#8217;immagine Docker su Heroku:<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># ... (passaggi di build e login a Docker Hub\/GHCR dagli esempi precedenti) ...\n# deploy:\n#   name: Deploy to Heroku\n#   needs: build # Dipende dal job di build dell&#039;immagine\n#   runs-on: ubuntu-latest\n#   steps:\n#     # ...\n#     - name: Login to Heroku container registry\n#       run: echo &quot;${{ secrets.HEROKU_API_KEY }}&quot; | docker login --username=_ --password-stdin registry.heroku.com\n#     - name: Tag image for Heroku\n#       # Supponendo che l&#039;immagine sia costruita come ghcr.io\/username\/repo\/myapp:latest\n#       run: docker tag ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:latest registry.heroku.com\/${{ secrets.HEROKU_APP_NAME }}\/web\n#     - name: Push image to Heroku\n#       run: docker push registry.heroku.com\/${{ secrets.HEROKU_APP_NAME }}\/web\n#     - name: Release Heroku App\n#       env:\n#         HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}\n#       run: heroku container:release web --app ${{ secrets.HEROKU_APP_NAME }}\n<\/code><\/pre>\n\n\n\n<h3 id=\"rtoc-11\"  class=\"wp-block-heading\">\ud83d\udfe8 Distribuzione su AWS (ad esempio, file statici su S3)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud83d\udd10 Segreti GitHub:<\/strong> <code class=\"\" data-line=\"\">AWS_ACCESS_KEY_ID<\/code>, <code class=\"\" data-line=\"\">AWS_SECRET_ACCESS_KEY<\/code>, <code class=\"\" data-line=\"\">AWS_REGION<\/code>, <code class=\"\" data-line=\"\">S3_BUCKET_NAME<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-aws-s3.yml\nname: Deploy Static Site to AWS S3\n\non:\n  push:\n    branches: &#091;main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v3\n      - name: Configure AWS credentials\n        uses: aws-actions\/configure-aws-credentials@v4\n        with:\n          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}\n          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n          aws-region: ${{ secrets.AWS_REGION }}\n      - name: Sync files to S3\n        # Sostituisci .\/public con il percorso dei tuoi file statici\n        run: aws s3 sync .\/public s3:\/\/${{ secrets.S3_BUCKET_NAME }} --delete\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Per la distribuzione su **AWS Elastic Beanstalk** viene solitamente utilizzata la CLI EB, la pipeline sar\u00e0 simile, ma con i comandi `eb deploy`.<\/p>\n\n\n\n<h3 id=\"rtoc-12\"  class=\"wp-block-heading\">\ud83d\udd35 Distribuzione su Google Cloud Platform (GCP App Engine)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud83d\udd10 Segreti GitHub:<\/strong> <code class=\"\" data-line=\"\">GCP_CREDENTIALS<\/code> (chiave JSON dell&#8217;account di servizio), <code class=\"\" data-line=\"\">GCP_PROJECT_ID<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-gcp-app-engine.yml\nname: Deploy to GCP App Engine\n\non:\n  push:\n    branches: &#091;main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v3\n      - name: Set up Cloud SDK\n        uses: google-github-actions\/setup-gcloud@v2\n        with:\n          project_id: ${{ secrets.GCP_PROJECT_ID }}\n          service_account_key: ${{ secrets.GCP_CREDENTIALS }}\n          export_default_credentials: true\n      - name: Deploy to App Engine\n        # Assicurati di avere app.yaml nella root del progetto\n        run: gcloud app deploy --quiet<\/code><\/pre>\n\n\n\n<h4 id=\"rtoc-13\"  class=\"wp-block-heading\">\ud83d\udfea Distribuzione su Render.com<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Render spesso distribuisce automaticamente al push su GitHub, se il repository \u00e8 connesso. Ma per un trigger manuale (o come parte di una pipeline pi\u00f9 complessa) \u00e8 possibile utilizzare un Deploy Hook.<br><strong>\ud83d\udd10 Segreti GitHub:<\/strong> <code class=\"\" data-line=\"\">RENDER_DEPLOY_HOOK<\/code> (URL ottenuto dalle impostazioni del servizio Render).<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-render.yml\nname: Trigger Render Deploy\n\non:\n  workflow_dispatch: # Avvio manuale dall&#039;interfaccia utente di GitHub\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Trigger Render Deploy Hook\n        run: curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }}<\/code><\/pre>\n\n\n\n<h3 id=\"rtoc-14\"  class=\"wp-block-heading\">\ud83c\udf1f CI\/CD avanzato: Build Docker \u2192 Push su GHCR \u2192 Staging\/Production su GCP Cloud Run<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">E ora la ciliegina sulla torta! Costruiamo una pipeline avanzata: 1. Build dell&#8217;immagine Docker. 2. Pubblicazione dell&#8217;immagine nel GitHub Container Registry (ghcr.io). 3. Distribuzione automatica nell&#8217;ambiente di **staging** su GCP Cloud Run. 4. Distribuzione nell&#8217;ambiente di **produzione** su GCP Cloud Run **dopo conferma manuale**.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Per questo, avremo bisogno di diversi file di workflow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Segreti GitHub richiesti:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>`GCP_PROJECT_ID`: ID del tuo progetto GCP.<\/li>\n\n\n\n<li>`GCP_CREDENTIALS`: Chiave JSON dell&#8217;account di servizio GCP con autorizzazioni per la distribuzione su Cloud Run e l&#8217;accesso a GHCR (se necessario). Di solito `GITHUB_TOKEN` \u00e8 sufficiente per l&#8217;accesso a GHCR da Actions.<\/li>\n\n\n\n<li>`GCP_REGION`: Regione per Cloud Run (ad esempio, `europe-west1`).<\/li>\n<\/ul>\n\n\n\n<h3 id=\"rtoc-15\"  class=\"wp-block-heading\">1. Build e pubblicazione dell&#8217;immagine Docker su GHCR<\/h3>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/build.yml\nname: Build &amp; Push to GHCR\n\non:\n  push:\n    branches: &#091;main] # Esegui al push su main\n\njobs:\n  build-and-push:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read      # Per checkout\n      packages: write     # Per push su GHCR\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - name: Log in to GitHub Container Registry\n        uses: docker\/login-action@v3\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Build and push Docker image\n        uses: docker\/build-push-action@v5\n        with:\n          context: .\n          push: true\n          tags: ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:latest\n          # Puoi aggiungere un tag per SHA del commit per l&#039;unicit\u00e0:\n          # tags: |\n          #   ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:latest\n          #   ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:${{ github.sha }}\n<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>`github.repository_owner`: Proprietario del repository (il tuo username o organizzazione).<\/li>\n\n\n\n<li>`github.event.repository.name`: Nome del repository.<\/li>\n\n\n\n<li>`myapp`: Nome della tua applicazione\/immagine.<\/li>\n<\/ul>\n\n\n\n<h3 id=\"rtoc-16\"  class=\"wp-block-heading\">2. Distribuzione automatica su Staging (GCP Cloud Run)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Questo workflow verr\u00e0 eseguito automaticamente dopo il completamento con successo di `build.yml`.<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-staging.yml\nname: Deploy to GCP Cloud Run (Staging)\n\non:\n  workflow_run:\n    workflows: &#091;&quot;Build &amp; Push to GHCR&quot;]\n    types:\n      - completed\n\njobs:\n  deploy-staging:\n    runs-on: ubuntu-latest\n    if: ${{ github.event.workflow_run.conclusion == &#039;success&#039; }}\n\n    environment:\n      name: staging\n      url: ${{ steps.deploy.outputs.url }}\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - id: &#039;auth&#039;\n        uses: &#039;google-github-actions\/auth@v2&#039;\n        with:\n          credentials_json: &#039;${{ secrets.GCP_CREDENTIALS }}&#039;\n\n      - name: &#039;Deploy to Cloud Run (Staging)&#039;\n        id: deploy\n        uses: &#039;google-github-actions\/deploy-cloudrun@v2&#039;\n        with:\n          service: &#039;myapp-staging&#039;\n          region: &#039;${{ secrets.GCP_REGION }}&#039;\n          image: &#039;ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:latest&#039;\n          project_id: &#039;${{ secrets.GCP_PROJECT_ID }}&#039;\n          flags: &#039;--allow-unauthenticated --platform=managed&#039;\n<\/code><\/pre>\n\n\n\n<h3 id=\"rtoc-17\"  class=\"wp-block-heading\">3. Distribuzione in produzione con conferma manuale (GCP Cloud Run)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Questo workflow viene attivato manualmente tramite l&#8217;interfaccia utente di GitHub Actions.<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-prod.yml\nname: Deploy to GCP Cloud Run (Production)\n\non:\n  workflow_dispatch: # Consente l&#039;avvio manuale\n\njobs:\n  deploy-production:\n    runs-on: ubuntu-latest\n\n    environment:\n      name: production\n      url: ${{ steps.deploy.outputs.url }}\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - id: &#039;auth&#039;\n        uses: &#039;google-github-actions\/auth@v2&#039;\n        with:\n          credentials_json: &#039;${{ secrets.GCP_CREDENTIALS }}&#039;\n\n      - name: &#039;Deploy to Cloud Run (Production)&#039;\n        id: deploy\n        uses: &#039;google-github-actions\/deploy-cloudrun@v2&#039;\n        with:\n          service: &#039;myapp-production&#039;\n          region: &#039;${{ secrets.GCP_REGION }}&#039;\n          image: &#039;ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:latest&#039;\n          project_id: &#039;${{ secrets.GCP_PROJECT_ID }}&#039;\n          flags: &#039;--allow-unauthenticated --platform=managed&#039;\n          # Per la produzione puoi aggiungere --no-traffic e poi spostare gradualmente il traffico\n          # traffic:\n          #   latest: true\n          #   percent: 100\n<\/code><\/pre>\n\n\n\n<h3 id=\"rtoc-18\"  class=\"wp-block-heading\">Punti importanti di questa pipeline avanzata:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>GitHub Container Registry (ghcr.io):<\/strong> Lo usiamo per archiviare le immagini Docker. \u00c8 comodo perch\u00e9 \u00e8 strettamente integrato con GitHub Actions.<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">workflow_run<\/code>:<\/strong> Consente di eseguire un workflow (distribuzione di staging) al completamento di un altro (build).<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">workflow_dispatch<\/code>:<\/strong> Offre la possibilit\u00e0 di attivare manualmente un workflow (distribuzione di produzione), garantendo il controllo.<\/li>\n\n\n\n<li><strong>GitHub Environments:<\/strong> Ti consentono di configurare regole di protezione per la produzione (ad esempio, richiedere l&#8217;approvazione di revisori specifici) e di archiviare segreti specifici dell&#8217;ambiente.<\/li>\n\n\n\n<li><strong>GCP Cloud Run:<\/strong> Un&#8217;ottima opzione serverless per eseguire applicazioni containerizzate.<\/li>\n<\/ul>\n\n\n\n<h3 id=\"rtoc-19\"  class=\"wp-block-heading\">\ud83d\udd10 Sicurezza \u2013 \u00e8 importante!<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Usa i segreti GitHub:<\/strong> Non archiviare mai token, password, chiavi API direttamente nei file YAML. Usa `Settings -> Secrets and variables -> Actions` nel tuo repository.<\/li>\n\n\n\n<li><strong>Privilegi minimi:<\/strong> Per gli account di servizio (ad esempio, GCP), concedi solo le autorizzazioni strettamente necessarie per l&#8217;esecuzione delle attivit\u00e0 CI\/CD.<\/li>\n\n\n\n<li><strong>Isola gli ambienti:<\/strong> Staging e produzione dovrebbero essere il pi\u00f9 isolati possibile. Progetti\/account diversi nei provider cloud sono una buona pratica.<\/li>\n\n\n\n<li><strong>Protezione dei branch:<\/strong> Configura la protezione per il branch `main` (o `master`) in modo che i push siano possibili solo tramite Pull Request con controlli CI obbligatori.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\ude80 <strong>CI\/CD: Dalle basi alla produzione su GCP con GitHub Actions \u2013 Una guida completa con esempi<\/strong> \ud83d\ude80<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ciao, sviluppatori! In questo articolo parler\u00f2 di CI\/CD \u2013 un concetto.<\/p>\n\n\n\n<h3 id=\"rtoc-20\"  class=\"wp-block-heading\">Cos&#8217;\u00e8 una pipeline CI\/CD nel contesto della programmazione?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Una pipeline CI\/CD (Continuous Integration \/ Continuous Delivery o Continuous Deployment)<\/strong> \u00e8 un processo automatizzato che consente agli sviluppatori di consegnare rapidamente e in modo affidabile le modifiche al codice in un ambiente di produzione.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Analizziamo i concetti chiave:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd27 <strong>CI \u2014 Continuous Integration (Integrazione Continua)<\/strong><br>Questa \u00e8 una pratica in cui gli sviluppatori integrano frequentemente le modifiche in una codebase condivisa. Ogni modifica di questo tipo viene automaticamente: * <strong>Costruita<\/strong> (build) * <strong>Testata<\/strong> (test unitari, test di integrazione) * <strong>Verificata per la conformit\u00e0 agli standard<\/strong> (linting, analisi statica)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udc49 **Obiettivo della CI:** Identificare gli errori il prima possibile, prima che rompano qualcosa di importante o finiscano in una release.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\ude80 **CD \u2014 Continuous Delivery (Consegna Continua) o Continuous Deployment (Distribuzione Continua)**<br>Qui ci sono due opzioni:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 **Continuous Delivery (Consegna Continua)**<br>Dopo il successo della fase CI, le modifiche vengono automaticamente: * Sottoposte a test aggiuntivi (ad esempio, test E2E \u2013 end-to-end) * Distribuite su un server di staging (test)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udc49 **Ma la distribuzione in produzione richiede ancora una conferma manuale.** Questo d\u00e0 al team il controllo su *quando* esattamente gli utenti vedranno le modifiche.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83e\udd16 **Continuous Deployment (Distribuzione Continua)**<br>Questo \u00e8 il passo successivo dopo la Continuous Delivery. Qui, la distribuzione in produzione avviene **completamente automaticamente**, se tutte le fasi precedenti della pipeline (build, tutti i test) sono state completate con successo. Questo \u00e8 il livello pi\u00f9 avanzato di automazione.<\/p>\n\n\n\n<h3 id=\"rtoc-21\"  class=\"wp-block-heading\">\ud83d\udd04 Di cosa \u00e8 composta di solito una pipeline CI\/CD?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Una pipeline tipica include le seguenti fasi:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Checkout<\/strong> \u2014 Clonazione dell&#8217;ultima versione del codice dal repository.<\/li>\n\n\n\n<li><strong>Build<\/strong> \u2014 Costruzione del progetto (compilazione, assemblaggio di artefatti, immagini Docker).<\/li>\n\n\n\n<li><strong>Test<\/strong> \u2014 Esecuzione di vari tipi di test (unitari, di integrazione, E2E).<\/li>\n\n\n\n<li><strong>Lint\/Code Quality<\/strong> \u2014 Controllo del codice per la conformit\u00e0 allo stile e potenziali errori utilizzando analizzatori statici.<\/li>\n\n\n\n<li><strong>Deploy<\/strong> \u2014 Distribuzione dell&#8217;applicazione (su un server di staging o di produzione).<\/li>\n\n\n\n<li><strong>Notify<\/strong> \u2014 Invio di notifiche sullo stato della pipeline al team (ad esempio, in Slack, Email).<\/li>\n<\/ol>\n\n\n\n<h3 id=\"rtoc-22\"  class=\"wp-block-heading\">\ud83d\udee0 Strumenti popolari per CI\/CD:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>GitHub Actions<\/strong> (il nostro focus oggi!)<\/li>\n\n\n\n<li>GitLab CI\/CD<\/li>\n\n\n\n<li>Jenkins<\/li>\n\n\n\n<li>CircleCI<\/li>\n\n\n\n<li>Bitbucket Pipelines<\/li>\n\n\n\n<li>Azure DevOps<\/li>\n\n\n\n<li>TeamCity<\/li>\n<\/ul>\n\n\n\n<h3 id=\"rtoc-23\"  class=\"wp-block-heading\">\ud83e\udde0 Perch\u00e9 abbiamo bisogno di CI\/CD?<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Riduce l&#8217;errore umano:<\/strong> L&#8217;automazione elimina gli errori associati alle operazioni manuali.<\/li>\n\n\n\n<li><strong>Rilevamento rapido dei bug:<\/strong> Gli errori vengono trovati prima, rendendoli pi\u00f9 facili e meno costosi da correggere.<\/li>\n\n\n\n<li><strong>Automazione delle attivit\u00e0 di routine:<\/strong> Gli sviluppatori dedicano meno tempo alla costruzione e alla distribuzione e pi\u00f9 tempo alla codifica.<\/li>\n\n\n\n<li><strong>Miglioramento della qualit\u00e0 del codice:<\/strong> Controlli e test continui aumentano il livello di qualit\u00e0 complessivo.<\/li>\n\n\n\n<li><strong>Consegna rapida delle funzionalit\u00e0 agli utenti:<\/strong> Le nuove funzionalit\u00e0 raggiungono l&#8217;utente finale pi\u00f9 rapidamente e pi\u00f9 frequentemente.<\/li>\n<\/ul>\n\n\n\n<h3 id=\"rtoc-24\"  class=\"wp-block-heading\">\ud83d\udce6 Esempi semplici di CI\/CD con GitHub Actions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Diamo un&#8217;occhiata alle pipeline di base per le tecnologie pi\u00f9 diffuse. Tutti gli esempi utilizzano GitHub Actions e vengono salvati nella directory <code class=\"\" data-line=\"\">.github\/workflows\/<\/code> del tuo progetto.<\/p>\n\n\n\n<h4 id=\"rtoc-25\"  class=\"wp-block-heading\">\ud83d\udc0d CI\/CD per Python (con <code class=\"\" data-line=\"\">pytest<\/code> e <code class=\"\" data-line=\"\">flake8<\/code>)<\/h4>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/python-ci.yml\nname: Python CI\n\non: &#091;push, pull_request]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - name: Set up Python\n        uses: actions\/setup-python@v4\n        with:\n          python-version: &#039;3.11&#039; # Specifica la tua versione\n\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          pip install -r requirements.txt # Assicurati di avere requirements.txt\n          pip install flake8 pytest\n\n      - name: Lint with flake8\n        run: |\n          # Controlla il codice nelle cartelle src e tests (adatta al tuo progetto)\n          flake8 src tests\n\n      - name: Run tests\n        run: |\n          pytest\n<\/code><\/pre>\n\n\n\n<h4 id=\"rtoc-26\"  class=\"wp-block-heading\">\ud83c\udf10 CI\/CD per Node.js (con <code class=\"\" data-line=\"\">npm test<\/code> e <code class=\"\" data-line=\"\">eslint<\/code>)<\/h4>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/node-ci.yml\nname: Node.js CI\n\non: &#091;push, pull_request]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: &#091;18.x] # Specifica la tua versione di Node.js\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions\/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Install dependencies\n        run: npm install # o npm ci per un&#039;installazione pi\u00f9 prevedibile\n\n      - name: Lint with ESLint\n        run: npx eslint . # Assicurati che ESLint sia configurato nel progetto\n\n      - name: Run tests\n        run: npm test\n<\/code><\/pre>\n\n\n\n<h4 id=\"rtoc-27\"  class=\"wp-block-heading\">\ud83d\udc33 CI\/CD per Docker (build e push su Docker Hub)<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Per questo esempio, avrai bisogno dei segreti <code class=\"\" data-line=\"\">DOCKER_USERNAME<\/code> e <code class=\"\" data-line=\"\">DOCKER_PASSWORD<\/code> (o un token) nelle impostazioni del tuo repository GitHub (<code class=\"\" data-line=\"\">Settings -&gt; Secrets and variables -&gt; Actions<\/code>).<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/docker-ci.yml\nname: Docker CI\/CD\n\non:\n  push:\n    branches: &#091; main ] # Esegui solo per il branch main\n\njobs:\n  docker:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - name: Log in to Docker Hub\n        run: echo &quot;${{ secrets.DOCKER_PASSWORD }}&quot; | docker login -u &quot;${{ secrets.DOCKER_USERNAME }}&quot; --password-stdin\n\n      - name: Build Docker image\n        # Sostituisci myapp con il nome della tua applicazione\n        run: docker build -t ${{ secrets.DOCKER_USERNAME }}\/myapp:latest .\n\n      - name: Push Docker image\n        run: docker push ${{ secrets.DOCKER_USERNAME }}\/myapp:latest\n<\/code><\/pre>\n\n\n\n<h3 id=\"rtoc-28\"  class=\"wp-block-heading\">\ud83d\ude9a Distribuzione su piattaforme popolari<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ora che abbiamo artefatti costruiti e testati (ad esempio, un&#8217;immagine Docker), vediamo come possono essere distribuiti.<\/p>\n\n\n\n<h4 id=\"rtoc-29\"  class=\"wp-block-heading\">\ud83d\udfe3 Distribuzione su Heroku<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud83d\udd10 Segreti GitHub:<\/strong> <code class=\"\" data-line=\"\">HEROKU_API_KEY<\/code>, <code class=\"\" data-line=\"\">HEROKU_APP_NAME<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-heroku.yml\nname: Deploy to Heroku\n\non:\n  push:\n    branches: &#091;main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v3\n      - name: Install Heroku CLI\n        run: curl https:\/\/cli-assets.heroku.com\/install.sh | sh\n      - name: Login to Heroku\n        env:\n          HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}\n        run: heroku auth:token\n      - name: Deploy to Heroku\n        env:\n          HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}\n        run: |\n          heroku git:remote -a ${{ secrets.HEROKU_APP_NAME }}\n          git push heroku main -f # Fai attenzione con -f (force push)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se stai distribuendo un&#8217;immagine Docker su Heroku:<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># ... (passaggi di build e login a Docker Hub\/GHCR dagli esempi precedenti) ...\n# deploy:\n#   name: Deploy to Heroku\n#   needs: build # Dipende dal job di build dell&#039;immagine\n#   runs-on: ubuntu-latest\n#   steps:\n#     # ...\n#     - name: Login to Heroku container registry\n#       run: echo &quot;${{ secrets.HEROKU_API_KEY }}&quot; | docker login --username=_ --password-stdin registry.heroku.com\n#     - name: Tag image for Heroku\n#       # Supponendo che l&#039;immagine sia costruita come ghcr.io\/username\/repo\/myapp:latest\n#       run: docker tag ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:latest registry.heroku.com\/${{ secrets.HEROKU_APP_NAME }}\/web\n#     - name: Push image to Heroku\n#       run: docker push registry.heroku.com\/${{ secrets.HEROKU_APP_NAME }}\/web\n#     - name: Release Heroku App\n#       env:\n#         HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}\n#       run: heroku container:release web --app ${{ secrets.HEROKU_APP_NAME }}\n<\/code><\/pre>\n\n\n\n<h3 id=\"rtoc-30\"  class=\"wp-block-heading\">\ud83d\udfe8 Distribuzione su AWS (ad esempio, file statici su S3)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud83d\udd10 Segreti GitHub:<\/strong> <code class=\"\" data-line=\"\">AWS_ACCESS_KEY_ID<\/code>, <code class=\"\" data-line=\"\">AWS_SECRET_ACCESS_KEY<\/code>, <code class=\"\" data-line=\"\">AWS_REGION<\/code>, <code class=\"\" data-line=\"\">S3_BUCKET_NAME<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-aws-s3.yml\nname: Deploy Static Site to AWS S3\n\non:\n  push:\n    branches: &#091;main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v3\n      - name: Configure AWS credentials\n        uses: aws-actions\/configure-aws-credentials@v4\n        with:\n          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}\n          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n          aws-region: ${{ secrets.AWS_REGION }}\n      - name: Sync files to S3\n        # Sostituisci .\/public con il percorso dei tuoi file statici\n        run: aws s3 sync .\/public s3:\/\/${{ secrets.S3_BUCKET_NAME }} --delete\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Per la distribuzione su **AWS Elastic Beanstalk** viene solitamente utilizzata la CLI EB, la pipeline sar\u00e0 simile, ma con i comandi `eb deploy`.<\/p>\n\n\n\n<h3 id=\"rtoc-31\"  class=\"wp-block-heading\">\ud83d\udd35 Distribuzione su Google Cloud Platform (GCP App Engine)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud83d\udd10 Segreti GitHub:<\/strong> <code class=\"\" data-line=\"\">GCP_CREDENTIALS<\/code> (chiave JSON dell&#8217;account di servizio), <code class=\"\" data-line=\"\">GCP_PROJECT_ID<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-gcp-app-engine.yml\nname: Deploy to GCP App Engine\n\non:\n  push:\n    branches: &#091;main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v3\n      - name: Set up Cloud SDK\n        uses: google-github-actions\/setup-gcloud@v2\n        with:\n          project_id: ${{ secrets.GCP_PROJECT_ID }}\n          service_account_key: ${{ secrets.GCP_CREDENTIALS }}\n          export_default_credentials: true\n      - name: Deploy to App Engine\n        # Assicurati di avere app.yaml nella root del progetto\n        run: gcloud app deploy --quiet<\/code><\/pre>\n\n\n\n<h4 id=\"rtoc-32\"  class=\"wp-block-heading\">\ud83d\udfea Distribuzione su Render.com<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Render spesso distribuisce automaticamente al push su GitHub, se il repository \u00e8 connesso. Ma per un trigger manuale (o come parte di una pipeline pi\u00f9 complessa) \u00e8 possibile utilizzare un Deploy Hook.<br><strong>\ud83d\udd10 Segreti GitHub:<\/strong> <code class=\"\" data-line=\"\">RENDER_DEPLOY_HOOK<\/code> (URL ottenuto dalle impostazioni del servizio Render).<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-render.yml\nname: Trigger Render Deploy\n\non:\n  workflow_dispatch: # Avvio manuale dall&#039;interfaccia utente di GitHub\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Trigger Render Deploy Hook\n        run: curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }}<\/code><\/pre>\n\n\n\n<h3 id=\"rtoc-33\"  class=\"wp-block-heading\">\ud83c\udf1f CI\/CD avanzato: Build Docker \u2192 Push su GHCR \u2192 Staging\/Production su GCP Cloud Run<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">E ora la ciliegina sulla torta! Costruiamo una pipeline avanzata: 1. Build dell&#8217;immagine Docker. 2. Pubblicazione dell&#8217;immagine nel GitHub Container Registry (ghcr.io). 3. Distribuzione automatica nell&#8217;ambiente di **staging** su GCP Cloud Run. 4. Distribuzione nell&#8217;ambiente di **produzione** su GCP Cloud Run **dopo conferma manuale**.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Per questo, avremo bisogno di diversi file di workflow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Segreti GitHub richiesti:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>`GCP_PROJECT_ID`: ID del tuo progetto GCP.<\/li>\n\n\n\n<li>`GCP_CREDENTIALS`: Chiave JSON dell&#8217;account di servizio GCP con autorizzazioni per la distribuzione su Cloud Run e l&#8217;accesso a GHCR (se necessario). Di solito `GITHUB_TOKEN` \u00e8 sufficiente per l&#8217;accesso a GHCR da Actions.<\/li>\n\n\n\n<li>`GCP_REGION`: Regione per Cloud Run (ad esempio, `europe-west1`).<\/li>\n<\/ul>\n\n\n\n<h3 id=\"rtoc-34\"  class=\"wp-block-heading\">1. Build e pubblicazione dell&#8217;immagine Docker su GHCR<\/h3>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/build.yml\nname: Build &amp; Push to GHCR\n\non:\n  push:\n    branches: &#091;main] # Esegui al push su main\n\njobs:\n  build-and-push:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read      # Per checkout\n      packages: write     # Per push su GHCR\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - name: Log in to GitHub Container Registry\n        uses: docker\/login-action@v3\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Build and push Docker image\n        uses: docker\/build-push-action@v5\n        with:\n          context: .\n          push: true\n          tags: ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:latest\n          # Puoi aggiungere un tag per SHA del commit per l&#039;unicit\u00e0:\n          # tags: |\n          #   ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:latest\n          #   ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:${{ github.sha }}\n<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>`github.repository_owner`: Proprietario del repository (il tuo username o organizzazione).<\/li>\n\n\n\n<li>`github.event.repository.name`: Nome del repository.<\/li>\n\n\n\n<li>`myapp`: Nome della tua applicazione\/immagine.<\/li>\n<\/ul>\n\n\n\n<h3 id=\"rtoc-35\"  class=\"wp-block-heading\">2. Distribuzione automatica su Staging (GCP Cloud Run)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Questo workflow verr\u00e0 eseguito automaticamente dopo il completamento con successo di `build.yml`.<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-staging.yml\nname: Deploy to GCP Cloud Run (Staging)\n\non:\n  workflow_run:\n    workflows: &#091;&quot;Build &amp; Push to GHCR&quot;]\n    types:\n      - completed\n\njobs:\n  deploy-staging:\n    runs-on: ubuntu-latest\n    if: ${{ github.event.workflow_run.conclusion == &#039;success&#039; }}\n\n    environment:\n      name: staging\n      url: ${{ steps.deploy.outputs.url }}\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - id: &#039;auth&#039;\n        uses: &#039;google-github-actions\/auth@v2&#039;\n        with:\n          credentials_json: &#039;${{ secrets.GCP_CREDENTIALS }}&#039;\n\n      - name: &#039;Deploy to Cloud Run (Staging)&#039;\n        id: deploy\n        uses: &#039;google-github-actions\/deploy-cloudrun@v2&#039;\n        with:\n          service: &#039;myapp-staging&#039;\n          region: &#039;${{ secrets.GCP_REGION }}&#039;\n          image: &#039;ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:latest&#039;\n          project_id: &#039;${{ secrets.GCP_PROJECT_ID }}&#039;\n          flags: &#039;--allow-unauthenticated --platform=managed&#039;\n<\/code><\/pre>\n\n\n\n<h3 id=\"rtoc-36\"  class=\"wp-block-heading\">3. Distribuzione in produzione con conferma manuale (GCP Cloud Run)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Questo workflow viene attivato manualmente tramite l&#8217;interfaccia utente di GitHub Actions.<\/p>\n\n\n\n<pre class=\"wp-block-code line-numbers\"><code class=\" language-python\" data-line=\"\"># .github\/workflows\/deploy-prod.yml\nname: Deploy to GCP Cloud Run (Production)\n\non:\n  workflow_dispatch: # Consente l&#039;avvio manuale\n\njobs:\n  deploy-production:\n    runs-on: ubuntu-latest\n\n    environment:\n      name: production\n      url: ${{ steps.deploy.outputs.url }}\n\n    steps:\n      - uses: actions\/checkout@v3\n\n      - id: &#039;auth&#039;\n        uses: &#039;google-github-actions\/auth@v2&#039;\n        with:\n          credentials_json: &#039;${{ secrets.GCP_CREDENTIALS }}&#039;\n\n      - name: &#039;Deploy to Cloud Run (Production)&#039;\n        id: deploy\n        uses: &#039;google-github-actions\/deploy-cloudrun@v2&#039;\n        with:\n          service: &#039;myapp-production&#039;\n          region: &#039;${{ secrets.GCP_REGION }}&#039;\n          image: &#039;ghcr.io\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/myapp:latest&#039;\n          project_id: &#039;${{ secrets.GCP_PROJECT_ID }}&#039;\n          flags: &#039;--allow-unauthenticated --platform=managed&#039;\n          # Per la produzione puoi aggiungere --no-traffic e poi spostare gradualmente il traffico\n          # traffic:\n          #   latest: true\n          #   percent: 100\n<\/code><\/pre>\n\n\n\n<h3 id=\"rtoc-37\"  class=\"wp-block-heading\">Punti importanti di questa pipeline avanzata:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>GitHub Container Registry (ghcr.io):<\/strong> Lo usiamo per archiviare le immagini Docker. \u00c8 comodo perch\u00e9 \u00e8 strettamente integrato con GitHub Actions.<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">workflow_run<\/code>:<\/strong> Consente di eseguire un workflow (distribuzione di staging) al completamento di un altro (build).<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">workflow_dispatch<\/code>:<\/strong> Offre la possibilit\u00e0 di attivare manualmente un workflow (distribuzione di produzione), garantendo il controllo.<\/li>\n\n\n\n<li><strong>GitHub Environments:<\/strong> Ti consentono di configurare regole di protezione per la produzione (ad esempio, richiedere l&#8217;approvazione di revisori specifici) e di archiviare segreti specifici dell&#8217;ambiente.<\/li>\n\n\n\n<li><strong>GCP Cloud Run:<\/strong> Un&#8217;ottima opzione serverless per eseguire applicazioni containerizzate.<\/li>\n<\/ul>\n\n\n\n<h3 id=\"rtoc-38\"  class=\"wp-block-heading\">\ud83d\udd10 Sicurezza \u2013 \u00e8 importante!<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Usa i segreti GitHub:<\/strong> Non archiviare mai token, password, chiavi API direttamente nei file YAML. Usa `Settings -> Secrets and variables -> Actions` nel tuo repository.<\/li>\n\n\n\n<li><strong>Privilegi minimi:<\/strong> Per gli account di servizio (ad esempio, GCP), concedi solo le autorizzazioni strettamente necessarie per l&#8217;esecuzione delle attivit\u00e0 CI\/CD.<\/li>\n\n\n\n<li><strong>Isola gli ambienti:<\/strong> Staging e produzione dovrebbero essere il pi\u00f9 isolati possibile. Progetti\/account diversi nei provider cloud sono una buona pratica.<\/li>\n\n\n\n<li><strong>Protezione dei branch:<\/strong> Configura la protezione per il branch `main` (o `master`) in modo che i push siano possibili solo tramite Pull Request con controlli CI obbligatori.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Ciao, sviluppatori! In questo articolo parler\u00f2 di CI\/CD \u2013 un concetto. In Questo Articolo Cos&#8217;\u00e8 una pipeline CI\/CD nel contesto della programmazione? \ud83d\udd04 Di cosa \u00e8 composta di solito una pipeline CI\/CD? \ud83d\udee0 Strumenti popolari per CI\/CD: \ud83e\udde0 Perch\u00e9 abbiamo bisogno di CI\/CD? \ud83d\udce6 Esempi semplici di CI\/CD con GitHub Actions \ud83d\udc0d CI\/CD per Python&hellip;&nbsp;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"off","neve_meta_content_width":70,"neve_meta_title_alignment":"left","neve_meta_author_avatar":"on","neve_post_elements_order":"[\"content\",\"tags\",\"comments\"]","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","_themeisle_gutenberg_block_has_review":false,"footnotes":""},"categories":[660],"tags":[614],"class_list":["post-1181","post","type-post","status-publish","format-standard","hentry","category-devops","tag-tag-github-actions"],"acf":[],"_links":{"self":[{"href":"https:\/\/it.davidka.net\/it\/wp-json\/wp\/v2\/posts\/1181","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/it.davidka.net\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/it.davidka.net\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/it.davidka.net\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/it.davidka.net\/it\/wp-json\/wp\/v2\/comments?post=1181"}],"version-history":[{"count":0,"href":"https:\/\/it.davidka.net\/it\/wp-json\/wp\/v2\/posts\/1181\/revisions"}],"wp:attachment":[{"href":"https:\/\/it.davidka.net\/it\/wp-json\/wp\/v2\/media?parent=1181"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/it.davidka.net\/it\/wp-json\/wp\/v2\/categories?post=1181"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/it.davidka.net\/it\/wp-json\/wp\/v2\/tags?post=1181"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}