كيفية نشر التغييرات تلقائيًا من GitHub إلى Cloud Run باستخدام Cloud Build

1. مقدمة

نظرة عامة

في هذا الدرس التطبيقي، ستتم تهيئة Cloud Run على إنشاء إصدارات جديدة ونشرها تلقائيًا من تطبيقك عند إرسال تغييرات رمز المصدر إلى مستودع GitHub.

يحفظ هذا التطبيق التجريبي بيانات المستخدم في firestore، ولكن يتم حفظ جزء جزئي فقط من البيانات بشكل صحيح. وستقوم بتهيئة عمليات النشر المستمرة بحيث يصبح الإصلاح متاحًا في نسخة جديدة عند إرسال إصلاح خطأ إلى مستودع جيت هب.

المعلومات التي ستطّلع عليها

  • كتابة تطبيق ويب Express باستخدام "محرِّر Cloud Shell"
  • ربط حسابك على GitHub بخدمة Google Cloud لإجراء عمليات النشر المستمرة
  • نشر تطبيقك تلقائيًا في "التشغيل في السحابة الإلكترونية"
  • تعرَّف على طريقة استخدام HTMX وTailwindCSS.

2. الإعداد والمتطلبات

المتطلبات الأساسية

تفعيل Cloud Shell

  1. من Cloud Console، انقر على تفعيل Cloud Shell d1264ca30785e435.png.

cb81e7c8e34bc8d.png

إذا كانت هذه هي المرة الأولى التي تبدأ فيها Cloud Shell، ستظهر لك شاشة وسيطة تصف ماهيتها. إذا ظهرت لك شاشة وسيطة، انقر على متابعة.

d95252b003979716.png

من المفترَض أن تستغرق عملية إدارة الحسابات والاتصال بخدمة Cloud Shell بضع دقائق فقط.

7833d5e1c5d18f54.png

يتم تحميل هذا الجهاز الافتراضي مع جميع أدوات التطوير اللازمة. وتوفّر هذه الشبكة دليلاً رئيسيًا دائمًا بسعة 5 غيغابايت ويتم تشغيله في Google Cloud، ما يحسّن بشكل كبير من أداء الشبكة والمصادقة. يمكنك تنفيذ معظم عملك، إن لم يكن كلّه، في هذا الدرس التطبيقي حول الترميز باستخدام متصفّح.

بعد الربط بخدمة Cloud Shell، من المفترض أن تتأكّد من أنّه تمّت مصادقتك وأنّ المشروع مضبوط على رقم تعريف مشروعك.

  1. شغِّل الأمر التالي في Cloud Shell لتأكيد مصادقتك:
gcloud auth list

مخرجات الأمر

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. شغّل الأمر التالي في Cloud Shell للتأكد من معرفة الأمر gcloud بمشروعك:
gcloud config list project

مخرجات الأمر

[core]
project = <PROJECT_ID>

إذا لم يكن كذلك، يمكنك تعيينه من خلال هذا الأمر:

gcloud config set project <PROJECT_ID>

مخرجات الأمر

Updated property [core/project].

3- تفعيل واجهات برمجة التطبيقات وضبط متغيرات البيئة

تفعيل واجهات برمجة التطبيقات

يتطلّب هذا الدرس التطبيقي حول الترميز استخدام واجهات برمجة التطبيقات التالية. يمكنك تمكين واجهات برمجة التطبيقات هذه عن طريق تشغيل الأمر التالي:

gcloud services enable run.googleapis.com \
    cloudbuild.googleapis.com \
    firestore.googleapis.com \
    iamcredentials.googleapis.com

إعداد متغيرات البيئة

يمكنك ضبط متغيّرات البيئة التي سيتم استخدامها خلال هذا الدرس التطبيقي حول الترميز.

REGION=<YOUR-REGION>
PROJECT_ID=<YOUR-PROJECT-ID>
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
SERVICE_ACCOUNT="firestore-accessor"
SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com

4. إنشاء حساب خدمة

ستستخدم Cloud Run حساب الخدمة هذا لطلب بيانات Vertex AI Gemini API. سيحصل حساب الخدمة هذا أيضًا على أذونات للقراءة والكتابة في Firestore وقراءة الأسرار من Secret Manager.

أولاً، أنشئ حساب الخدمة من خلال تنفيذ الأمر التالي:

gcloud iam service-accounts create $SERVICE_ACCOUNT \
  --display-name="Cloud Run access to Firestore"

والآن، امنح حساب الخدمة إذنًا بالقراءة والكتابة في Firestore.

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
  --role=roles/datastore.user

5- إنشاء مشروع على Firebase وإعداده

  1. في وحدة تحكُّم Firebase، انقر على إضافة مشروع.
  2. أدخِل <YOUR_PROJECT_ID>. لإضافة Firebase إلى أحد مشاريعك الحالية على Google Cloud
  3. راجِع بنود Firebase واقبلها إذا طُلب منك ذلك.
  4. انقر على متابعة.
  5. انقر على تأكيد الخطة لتأكيد خطة فوترة Firebase.
  6. يُعدّ تفعيل "إحصاءات Google" اختياريًا لهذا الدرس التطبيقي حول الترميز.
  7. انقر على إضافة Firebase.
  8. عند إنشاء المشروع، انقر على متابعة.
  9. من القائمة إنشاء، انقر على قاعدة بيانات Firestore.
  10. انقر على إنشاء قاعدة بيانات.
  11. اختر منطقتك من القائمة المنسدلة الموقع الجغرافي، ثم انقر على التالي.
  12. استخدِم الإعداد التلقائي البدء في وضع الإنتاج، ثم انقر على إنشاء.

6- كتابة بيانات الطلب

أولاً، أنشئ دليلاً لرمز المصدر والقرص المضغوط في هذا الدليل.

mkdir cloud-run-github-cd-demo && cd $_

بعد ذلك، أنشِئ ملف package.json يتضمّن المحتوى التالي:

{
  "name": "cloud-run-github-cd-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node app.js",
    "nodemon": "nodemon app.js",
    "tailwind-dev": "npx tailwindcss -i ./input.css -o ./public/output.css --watch",
    "tailwind": "npx tailwindcss -i ./input.css -o ./public/output.css",
    "dev": "npm run tailwind && npm run nodemon"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@google-cloud/firestore": "^7.3.1",
    "axios": "^1.6.7",
    "express": "^4.18.2",
    "htmx.org": "^1.9.10"
  },
  "devDependencies": {
    "nodemon": "^3.1.0",
    "tailwindcss": "^3.4.1"
  }
}

أولاً، أنشِئ ملف مصدر app.js يتضمّن المحتوى أدناه. يحتوي هذا الملف على نقطة دخول الخدمة ويحتوي على المنطق الرئيسي للتطبيق.

const express = require("express");
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
const path = require("path");
const { get } = require("axios");

const { Firestore } = require("@google-cloud/firestore");
const firestoreDb = new Firestore();

const fs = require("fs");
const util = require("util");
const { spinnerSvg } = require("./spinnerSvg.js");

const service = process.env.K_SERVICE;
const revision = process.env.K_REVISION;

app.use(express.static("public"));

app.get("/edit", async (req, res) => {
    res.send(`<form hx-post="/update" hx-target="this" hx-swap="outerHTML">
                <div>
  <p>
    <label>Name</label>    
    <input class="border-2" type="text" name="name" value="Cloud">
    </p><p>
    <label>Town</label>    
    <input class="border-2" type="text" name="town" value="Nibelheim">
    </p>
  </div>
  <div class="flex items-center mr-[10px] mt-[10px]">
  <button class="btn bg-blue-500 text-white px-4 py-2 rounded-lg text-center text-sm font-medium mr-[10px]">Submit</button>
  <button class="btn bg-gray-200 text-gray-800 px-4 py-2 rounded-lg text-center text-sm font-medium mr-[10px]" hx-get="cancel">Cancel</button>  
                ${spinnerSvg} 
                </div>
  </form>`);
});

app.post("/update", async function (req, res) {
    let name = req.body.name;
    let town = req.body.town;
    const doc = firestoreDb.doc(`demo/${name}`);

    //TODO: fix this bug
    await doc.set({
        name: name
        /* town: town */
    });

    res.send(`<div hx-target="this" hx-swap="outerHTML" hx-indicator="spinner">
                <p>
                <div><label>Name</label>: ${name}</div>
                </p><p>
                <div><label>Town</label>: ${town}</div>
                </p>
                <button
                    hx-get="/edit"
                    class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]"
                >
                    Click to update
                </button>               
            </div>`);
});

app.get("/cancel", (req, res) => {
    res.send(`<div hx-target="this" hx-swap="outerHTML">
                <p>
                <div><label>Name</label>: Cloud</div>
                </p><p>
                <div><label>Town</label>: Nibelheim</div>
                </p>
                <div>
                <button
                    hx-get="/edit"
                    class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]"
                >
                    Click to update
                </button>                
                </div>
            </div>`);
});

const port = parseInt(process.env.PORT) || 8080;
app.listen(port, async () => {
    console.log(`booth demo: listening on port ${port}`);

    //serviceMetadata = helper();
});

app.get("/helper", async (req, res) => {
    let region = "";
    let projectId = "";
    let div = "";

    try {
        // Fetch the token to make a GCF to GCF call
        const response1 = await get(
            "http://metadata.google.internal/computeMetadata/v1/project/project-id",
            {
                headers: {
                    "Metadata-Flavor": "Google"
                }
            }
        );

        // Fetch the token to make a GCF to GCF call
        const response2 = await get(
            "http://metadata.google.internal/computeMetadata/v1/instance/region",
            {
                headers: {
                    "Metadata-Flavor": "Google"
                }
            }
        );

        projectId = response1.data;
        let regionFull = response2.data;
        const index = regionFull.lastIndexOf("/");
        region = regionFull.substring(index + 1);

        div = `
        <div>
        This created the revision <code>${revision}</code> of the 
        Cloud Run service <code>${service}</code> in <code>${region}</code>
        for project <code>${projectId}</code>.
        </div>`;
    } catch (ex) {
        // running locally
        div = `<div> This is running locally.</div>`;
    }

    res.send(div);
});

إنشاء ملف باسم spinnerSvg.js

module.exports.spinnerSvg = `<svg id="spinner" alt="Loading..."
                    class="htmx-indicator animate-spin -ml-1 mr-3 h-5 w-5 text-blue-500"
                    xmlns="http://www.w3.org/2000/svg"
                    fill="none"
                    viewBox="0 0 24 24"
                >
                    <circle
                        class="opacity-25"
                        cx="12"
                        cy="12"
                        r="10"
                        stroke="currentColor"
                        stroke-width="4"
                    ></circle>
                    <path
                        class="opacity-75"
                        fill="currentColor"
                        d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
                    ></path>
                </svg>`;

إنشاء ملف input.css لـ tailwindCSS

@tailwind base;
@tailwind components;
@tailwind utilities;

وأنشئ ملف tailwind.config.js لـ tailwindCSS

/** @type {import('tailwindcss').Config} */
module.exports = {
    content: ["./**/*.{html,js}"],
    theme: {
        extend: {}
    },
    plugins: []
};

وأنشئ ملف .gitignore.

node_modules/

npm-debug.log
coverage/

package-lock.json

.DS_Store

والآن، أنشِئ دليل public جديدًا.

mkdir public
cd public

وفي هذا الدليل العام، أنشئ ملف index.html للواجهة الأمامية، التي ستستخدم htmx.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta
            name="viewport"
            content="width=device-width, initial-scale=1.0"
        />
        <script
            src="https://unpkg.com/htmx.org@1.9.10"
            integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
            crossorigin="anonymous"
        ></script>

        <link href="./output.css" rel="stylesheet" />
        <title>Demo 1</title>
    </head>
    <body
        class="font-sans bg-body-image bg-cover bg-center leading-relaxed"
    >
        <div class="container max-w-[700px] mt-[50px] ml-auto mr-auto">
            <div class="hero flex items-center">                    
                <div class="message text-base text-center mb-[24px]">
                    <h1 class="text-2xl font-bold mb-[10px]">
                        It's running!
                    </h1>
                    <div class="congrats text-base font-normal">
                        Congratulations, you successfully deployed your
                        service to Cloud Run. 
                    </div>
                </div>
            </div>

            <div class="details mb-[20px]">
                <p>
                    <div hx-trigger="load" hx-get="/helper" hx-swap="innerHTML" hx-target="this">Hello</div>                   
                </p>
            </div>

            <p
                class="callout text-sm text-blue-700 font-bold pt-4 pr-6 pb-4 pl-10 leading-tight"
            >
                You can deploy any container to Cloud Run that listens for
                HTTP requests on the port defined by the
                <code>PORT</code> environment variable. Cloud Run will
                scale automatically based on requests and you never have to
                worry about infrastructure.
            </p>

            <h1 class="text-2xl font-bold mt-[40px] mb-[20px]">
                Persistent Storage Example using Firestore
            </h1>
            <div hx-target="this" hx-swap="outerHTML">
                <p>
                <div><label>Name</label>: Cloud</div>
                </p><p>
                <div><label>Town</label>: Nibelheim</div>
                </p>
                <div>
                <button
                    hx-get="/edit"
                    class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]"
                >
                    Click to update
                </button>                
                </div>
            </div>

            <h1 class="text-2xl font-bold mt-[40px] mb-[20px]">
                What's next
            </h1>
            <p class="next text-base mt-4 mb-[20px]">
                You can build this demo yourself!
            </p>
            <p class="cta">
                <button
                    class="bg-blue-500 text-white px-4 py-2 rounded-lg text-center text-sm font-medium"
                >
                    VIEW CODELAB
                </button>
            </p> 
        </div>
   </body>
</html>

7. تشغيل التطبيق على الجهاز

في هذا القسم، ستُشغِّل التطبيق محليًا للتأكّد من وجود خطأ في التطبيق عندما يحاول المستخدم حفظ البيانات.

أولاً، يجب أن يكون لديك دور "مستخدم تخزين البيانات" للوصول إلى Firestore (في حال استخدام هويتك للمصادقة، على سبيل المثال، الجهاز قيد التشغيل في Cloud Shell) أو يمكنك انتحال هوية حساب المستخدم الذي تم إنشاؤه سابقًا.

استخدام ADC عند التشغيل محليًا

إذا كنت تستخدم Cloud Shell، يعني هذا أنّك تعمل على جهاز افتراضي في Google Compute Engine. سيتم تلقائيًا استخدام بيانات الاعتماد المرتبطة بهذا الجهاز الافتراضي (كما هو موضّح عند تشغيل gcloud auth list) من خلال بيانات الاعتماد التلقائية للتطبيق (ADC)، لذا ليس من الضروري استخدام الأمر gcloud auth application-default login. ومع ذلك، ستظلّ هويتك بحاجة إلى دور "مستخدم تخزين البيانات". يمكنك التخطّي إلى القسم تشغيل التطبيق محليًا.

ومع ذلك، إذا كنت تستخدم الوحدة الطرفية المحلية (أي ليس في Cloud Shell)، فستحتاج إلى استخدام "بيانات الاعتماد التلقائية للتطبيق" لمصادقة Google APIs. يمكنك 1) تسجيل الدخول باستخدام بيانات الاعتماد الخاصة بك (شرط أن يكون لديك دور "مستخدم تخزين البيانات") أو 2) تسجيل الدخول من خلال انتحال هوية حساب الخدمة المستخدَم في هذا الدرس التطبيقي حول الترميز.

الخيار 1) استخدام بيانات الاعتماد الخاصة بك لـ ADC

إذا أردت استخدام بيانات الاعتماد، يمكنك أولاً تشغيل gcloud auth list للتحقّق من كيفية مصادقتك في gcloud. بعد ذلك، قد تحتاج إلى منح هويتك دور مستخدم Vertex AI. إذا كان لهوية هويتك دور "المالك"، سيكون لديك حاليًا دور مستخدم "مستخدم تخزين البيانات" هذا. وإذا لم يكن الأمر كذلك، يمكنك تشغيل هذا الأمر لمنح هويتك دور مستخدم Vertex AI ودور "مستخدم تخزين البيانات".

USER=<YOUR_PRINCIPAL_EMAIL>

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member user:$USER \
  --role=roles/datastore.user

ثم شغّل الأمر التالي

gcloud auth application-default login

الخيار 2) انتحال هوية حساب خدمة لـ ADC

إذا أردت استخدام حساب الخدمة الذي تم إنشاؤه في هذا الدرس التطبيقي حول الترميز، يجب أن يكون لحساب المستخدم دور "منشئ الرموز المميّزة لحساب الخدمة". يمكنك الحصول على هذا الدور من خلال تنفيذ الأمر التالي:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member user:$USER \
  --role=roles/iam.serviceAccountTokenCreator

بعد ذلك، عليك تشغيل الأمر التالي لاستخدام ADC مع حساب الخدمة.

gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS

تشغيل التطبيق على الجهاز

بعد ذلك، تأكَّد من أنّك في الدليل الجذري cloud-run-github-cd-demo الخاص بالدرس التطبيقي حول الترميز.

cd .. && pwd

الآن، ستقوم بتثبيت التبعيات.

npm install

وأخيرًا، يمكنك تشغيل التطبيق من خلال تشغيل النص البرمجي التالي. سينشئ هذا النص البرمجي أيضًا ملف exit.css من tailwindCSS.

npm run dev

والآن افتح متصفح الويب إلى http://localhost:8080. إذا كنت في Cloud Shell، يمكنك فتح الموقع الإلكتروني عن طريق فتح زر Web Preview (معاينة المنفذ) 8080.

معاينة الويب - زر المعاينة على المنفذ 8080

أدخل نصًا لحقلي إدخال الاسم والبلدة واضغط على حفظ. وبعد ذلك، أعِد تحميل الصفحة. ستلاحظ أن حقل المدينة لم يستمر. ويمكنك إصلاح هذا الخطأ في القسم اللاحق.

إيقاف تشغيل التطبيق السريع محليًا (على سبيل المثال Ctrl^c في نظام التشغيل MacOS).

8. إنشاء مستودع GitHub

في الدليل المحلي، أنشِئ موقعًا إلكترونيًا جديدًا باسم "الفرع الرئيسي" كاسم الفرع التلقائي.

git init
git branch -M main

إكمال قاعدة الرموز الحالية التي تحتوي على الخطأ يمكنك إصلاح الخطأ بعد ضبط عملية النشر المستمر.

git add .
git commit -m "first commit for express application"

انتقِل إلى GitHub وأنشِئ مستودعًا فارغًا خاصًا بك أو متاحًا للجميع. يوصي هذا الدرس التطبيقي بتسمية مستودعك cloud-run-auto-deploy-codelab لإنشاء مستودع فارغ، عليك عدم وضع علامة على كل الإعدادات التلقائية أو ضبطها على "بدون" بحيث لا يكون أي محتوى في المستودع تلقائيًا عند إنشائه، على سبيل المثال.

إعدادات GitHub التلقائية

إذا أكملت هذه الخطوة بشكل صحيح، سترى التعليمات التالية على صفحة المستودع الفارغة:

تعليمات مستودع GitHub فارغ

عليك اتّباع تعليمات إرسال مستودع حالي من سطر الأوامر عن طريق تشغيل الأوامر التالية:

أولاً، أضف المستودع البعيد عن طريق تشغيل

git remote add origin <YOUR-REPO-URL-PER-GITHUB-INSTRUCTIONS>

ثم دفع الفرع الرئيسي إلى المستودع الرئيسي.

git push -u origin main

9. إعداد النشر المستمر

الآن بعد أن أصبح لديك رمز في GitHub، يمكنك إعداد نشر مستمر. انتقِل إلى Cloud Console للتشغيل في السحابة الإلكترونية.

  • انقر على "إنشاء خدمة".
  • انقر على النشر باستمرار من المستودع.
  • انقر على إعداد إنشاء السحابة الإلكترونية.
  • ضمن مستودع المصدر
    • اختيار GitHub كموفِّر للمستودع
    • انقر على إدارة المستودعات المرتبطة لضبط إذن الوصول إلى Cloud Build إلى المستودع.
    • اختَر المستودع وانقر على التالي.
  • ضمن تكوين الإصدار
    • ترك الفرع كـ ^main$
    • بالنسبة إلى نوع الإصدار، اختَر Go أو Node.js أو Python أو Java أو .NET Core أو Ruby أو PHP من خلال حِزم التطبيقات في Google Cloud
  • ترك دليل سياق الإصدار باسم /
  • انقر على حفظ.
  • ضمن المصادقة
    • انقر على السماح بالاستدعاءات غير المُصدَّق عليها
  • ضمن الحاويات(الحاويات)، المجلدات، الشبكات، الأمان
    • ضمن علامة التبويب "الأمان"، اختَر حساب الخدمة الذي أنشأته في خطوة سابقة، على سبيل المثال: Cloud Run access to Firestore
  • انقر على إنشاء.

سيؤدي هذا إلى نشر خدمة Cloud Run التي تحتوي على الخطأ الذي ستصلحه في القسم التالي.

10. إصلاح الخطأ

إصلاح الخطأ في الرمز البرمجي

في Cloud Shell Editor، يمكنك إضافة ملف app.js والانتقال إلى التعليق المسمى //TODO: fix this bug.

تغيير السطر التالي من

 //TODO: fix this bug
    await doc.set({
        name: name
    });

إلى

//fixed town bug
    await doc.set({
        name: name,
        town: town
    });

التحقّق من الإصلاح من خلال التشغيل

npm run start

وافتح متصفح الويب. احفظ البيانات مجددًا للمدينة وأعِد تحميلها. عند إعادة التحميل، ستظهر بيانات البلدة التي تم إدخالها حديثًا وقد تم الاحتفاظ بها بشكل صحيح.

الآن بعد أن تحققت من الإصلاح، أصبحت جاهزًا لنشره. أولاً، اتّبع الحل.

git add .
git commit -m "fixed town bug"

ثم إرسالها إلى المستودع الرئيسي على جيت هب.

git push origin main

ستنشر Cloud Build التغييرات تلقائيًا. يمكنك الانتقال إلى Cloud Console لخدمة Cloud Run لمراقبة تغييرات النشر.

التأكّد من حلّ المشكلة في قناة الإصدار العلني

عندما تُظهِر "وحدة التحكّم في Cloud Console" الخاصة بخدمة Cloud Run أنّ نسخة مراجعة ثانية تعمل الآن بنسبة 100%، على سبيل المثال https://console.cloud.google.com/run/detail/<YOUR_ معرفة>/<YOUR_SERVICE_NAME>/revisions، يمكنك فتح عنوان URL لخدمة "تشغيل السحابة الإلكترونية" في المتصفّح والتحقّق من استمرار بيانات البلدة التي تم إدخالها حديثًا بعد إعادة تحميل الصفحة.

11. تهانينا

تهانينا على إكمال الدرس التطبيقي حول الترميز.

ننصحك بمراجعة مستندات تشغيل السحابة الإلكترونية والنشر المستمر من git.

النقاط التي تناولناها

  • كتابة تطبيق ويب Express باستخدام "محرِّر Cloud Shell"
  • ربط حسابك على GitHub بخدمة Google Cloud لإجراء عمليات النشر المستمرة
  • نشر تطبيقك تلقائيًا في "التشغيل في السحابة الإلكترونية"
  • تعرَّف على طريقة استخدام HTMX وTailwindCSS.

12. تَنظيم

لتجنب تحصيل رسوم غير مقصودة، (على سبيل المثال، إذا تم استدعاء خدمات Cloud Run عن غير قصد أكثر من تخصيص استدعاء Cloud Run الشهري في الفئة المجانية)، يمكنك إما حذف Cloud Run أو حذف المشروع الذي أنشأته في الخطوة 2.

لحذف خدمة "تشغيل السحابة الإلكترونية"، انتقِل إلى "وحدة التحكم في تشغيل السحابة الإلكترونية في السحابة الإلكترونية" https://console.cloud.google.com/run واحذف خدمة "تشغيل السحابة الإلكترونية" التي أنشأتها في هذا الدرس التطبيقي حول الترميز، مثل حذف خدمة cloud-run-auto-deploy-codelab.

إذا اخترت حذف المشروع بالكامل، يمكنك الانتقال إلى https://console.cloud.google.com/cloud-resource-manager، واختيار المشروع الذي أنشأته في الخطوة الثانية، ثم اختيار "حذف". إذا حذفت المشروع، ستحتاج إلى تغيير المشاريع في حزمة تطوير البرامج (SDK) للسحابة الإلكترونية. يمكنك عرض قائمة بجميع المشاريع المتاحة من خلال تشغيل gcloud projects list.