النشر من CI/CD

انشر عند كل دفع من GitHub Actions أو GitLab CI/CD، ببيانات اعتماد محدودة ومهمّة تفشل حين يفشل النشر.

يفعل خطّ النشر أربعة أشياء: يثبّت الـ CLI، ويسجّل الدخول ببيانات اعتماد محفوظة في نظام CI، وينشئ التطبيق في المرّة الأولى أو يحدّثه في كل مرّة بعدها، وينتظر النشر حتى تفشل المهمّة حين يفشل. يفعل قالبان جاهزان هذا بالضبط. تعرضهما هذه الصفحة كاملين، وتشرح كل جزء منهما، وتتناول ما قد يسوء.

1. أنشئ بيانات اعتماد لخطّ النشر

لا تُعِد استخدام بيانات الاعتماد التي على جهازك. أنشئ واحدة لـ CI وحده، لا تحمل إلا ما يحتاج إليه خطّ النشر، حتى يمكن إبطالها دون تسجيل خروجك، ولا تستطيع أكثر من النشر إن تسرّبت.

export ISOGRID_CONFIG_HOME="$(mktemp -d)"
isogrid --api https://api.isogrid.skyvault.pro login \
  --scope application:create,application:write-any \
  --name github-ci

يفتح المتصفّح على صفحة الموافقة. تحقّق من المؤسّسة الظاهرة هناك: بيانات الاعتماد مثبّتة عليها وستُرفض في أي مكان آخر. وافِق، ثم انسخ الرمز من الإعدادات المؤقّتة واحذفها:

jq -r '.contexts[].token' "$ISOGRID_CONFIG_HOME/config.json"
rm -rf "$ISOGRID_CONFIG_HOME"

استخدام ISOGRID_CONFIG_HOME مؤقّت يُبقي تسجيل دخولك أنت كما هو. ودونه، ستحلّ بيانات الاعتماد الجديدة محلّ الموجودة في ~/.isogrid/config.json لذلك العنوان، وستحتاج إلى isogrid login --force لاستعادة بياناتك.

أيّ نطاقات

النطاق ما يستطيعه خطّ النشر به
application:create إنشاء التطبيق في التشغيل الأول.
application:write-any تعديل كل تطبيق في المؤسّسة وبناؤه ونشره.
application:deploy-any بناء كل تطبيق ونشره وتشغيله وإيقافه، دون تعديل إعداداته.

ينشئ القالبان التطبيق إن لم يكن موجودًا، فيحتاجان إلى application:create وapplication:write-any. وبعد أن يوجد، يمكنك إنشاء بيانات اعتماد أضيق دون application:create. ولا يكفي application:deploy-any إلا إن كان خطّ النشر لا يمرّر أي إعدادات أبدًا: لا --env-file ولا تغيير للفرع.

النطاق لا يضيف أبدًا إلى ما تملكه. يُقاطَع مع دورك ومجموعاتك في كل طلب، فالنطاق الذي لا تملكه يُسقط بصمت، وخفض صلاحياتك يضيّق بيانات اعتماد خطّ النشر أيضًا. وهناك أشياء لا تستطيعها بيانات الاعتماد أبدًا، مهما طلبت: الفوترة، وتعيين المسؤولين، وإنشاء بيانات الاعتماد أو إبطالها. انظر الأدوار ومجموعات الصلاحيات والوصول.

تدوم بيانات الاعتماد 90 يومًا ما لم تختر مدّة أقصر في صفحة الموافقة. ضع تذكيرًا في تقويمك لاستبدالها قبل ذلك.

2. أبلغ نظام CI

الإعداد GitHub Actions GitLab CI/CD
ISOGRID_TOKEN secret (سرّ) في المستودع متغيّر CI/CD، Masked (مُقنَّع)
ISOGRID_API_URL variable (متغيّر) في المستودع، مثل https://api.isogrid.skyvault.pro متغيّر CI/CD
ISOGRID_APP متغيّر اختياري. افتراضيه اسم المستودع. متغيّر اختياري. افتراضيه اسم المشروع.
ISOGRID_CLUSTER اختياري. slug المنطقة؛ لازم حين تكون أكثر من منطقة مفتوحة لك. كذلك
ISOGRID_TIER اختياري. نوع النسخة؛ أصغر ما في المنطقة افتراضيًا. كذلك
ISOGRID_NETWORK اختياري. slug الشبكة؛ شبكة مؤسّستك افتراضيًا. كذلك
ISOGRID_ENV_FILE اختياري. ملف dotenv في المستودع يُطبَّق في كل نشر. كذلك

في GitHub تجدها تحت Settings → Secrets and variables → Actions (الإعدادات ← الأسرار والمتغيّرات ← Actions). وفي GitLab تحت Settings → CI/CD → Variables (الإعدادات ← CI/CD ← المتغيّرات)؛ وعلّم ISOGRID_TOKEN بـ Protected (محميّ) أيضًا إن كانت الفروع المحميّة وحدها تنشر.

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

3. أضف خطّ النشر

تكتب الـ CLI الملف نيابةً عنك:

isogrid ci init github --app shop --cluster eu-west    # .github/workflows/isogrid-deploy.yml
isogrid ci init gitlab --app shop --cluster eu-west    # .gitlab-ci.yml
isogrid ci init github --output -                      # print it instead

القيم التي تمرّرها (--app، --cluster، --tier، --network، --env-file) تُكتب في الملف. وكل ما تغفله يُقرأ من متغيّرات CI أعلاه. ولا يُستبدل ملف موجود دون --force. وبعد الكتابة، يطبع الأمر المتغيّرات التي بقي ضبطها.

يمكنك أيضًا تنزيل القالبين دون الـ CLI:

curl -fsSL https://api.isogrid.skyvault.pro/api/v1/cli/ci-templates/github -o .github/workflows/isogrid-deploy.yml
curl -fsSL https://api.isogrid.skyvault.pro/api/v1/cli/ci-templates/gitlab -o .gitlab-ci.yml

قبل التشغيل الأول

يجب أن يكون المستودع مربوطًا بمؤسّستك ومفتوحًا للشخص الذي يستخدم خطّ النشر بيانات اعتماده. يعرضه isogrid repos list حين يكون كذلك؛ ويجب أن يتضمّن العمود YOU القيمتين create-app وdeploy. انظر مستودعات Git.

قالب GitHub Actions

# Deploy to ISOGrid from GitHub Actions.
#
# Save as .github/workflows/isogrid-deploy.yml (or generate it with
# `isogrid ci init github`). On every push to main, and whenever you run it by
# hand, it installs the ISOGrid CLI and either updates the application and
# deploys the pushed commit, or - the first time - creates the application.
#
# Set these in the repository (Settings -> Secrets and variables -> Actions):
#
#   Secrets
#     ISOGRID_TOKEN      A CLI credential. Mint a narrow one for CI: run
#                        `isogrid login --scope application:create,application:write-any --name github-ci`
#                        on your machine, then copy the token from
#                        ~/.isogrid/config.json. Never commit it.
#
#   Variables
#     ISOGRID_API_URL    Your platform's API, e.g. https://api.isogrid.skyvault.pro
#     ISOGRID_APP        Application name (default: this repository's name)
#     ISOGRID_CLUSTER    Region slug (optional when only one region is open to you)
#     ISOGRID_TIER       Instance type name (optional: the region's smallest)
#     ISOGRID_NETWORK    Network slug (optional: your organization's network)
#     ISOGRID_ENV_FILE   A dotenv file in the repository to apply (optional)
#
# The repository must already be connected to the organization through the
# ISOGrid GitHub app (`isogrid repos list` shows it once it is).

name: Deploy to ISOGrid

on:
  push:
    branches: [main]
  workflow_dispatch:

# One deployment of a branch at a time; a newer push waits rather than racing.
concurrency:
  group: isogrid-deploy-${{ github.ref }}
  cancel-in-progress: false

env:
  ISOGRID_API_URL: ${{ vars.ISOGRID_API_URL }}
  ISOGRID_TOKEN: ${{ secrets.ISOGRID_TOKEN }}
  ISOGRID_APP: ${{ vars.ISOGRID_APP || github.event.repository.name }}
  ISOGRID_CLUSTER: ${{ vars.ISOGRID_CLUSTER }}
  ISOGRID_TIER: ${{ vars.ISOGRID_TIER }}
  ISOGRID_NETWORK: ${{ vars.ISOGRID_NETWORK }}
  ISOGRID_ENV_FILE: ${{ vars.ISOGRID_ENV_FILE }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # Only needed for ISOGRID_ENV_FILE: the platform clones the repository
      # itself to build it.
      - uses: actions/checkout@v4

      - name: Install the ISOGrid CLI
        run: |
          curl -fsSL "$ISOGRID_API_URL/api/v1/cli/install.sh" | ISOGRID_INSTALL_DIR="$HOME/.local/bin" sh
          echo "$HOME/.local/bin" >> "$GITHUB_PATH"

      - name: Deploy
        run: |
          set -eu
          isogrid version

          ENV_ARGS=""
          if [ -n "$ISOGRID_ENV_FILE" ]; then
            ENV_ARGS="--env-file $ISOGRID_ENV_FILE"
          fi

          # --exact: an application called "api" must not be taken for "api-gateway".
          if isogrid apps get "$ISOGRID_APP" --exact --json > /dev/null 2>&1; then
            # Builds this exact commit and waits; the step fails if the build or
            # the deployment does.
            isogrid apps update "$ISOGRID_APP" \
              --branch "${{ github.ref_name }}" \
              $ENV_ARGS \
              --deploy --ref "${{ github.sha }}" --wait
          else
            isogrid apps create \
              --name "$ISOGRID_APP" \
              --provider github \
              --url "${{ github.server_url }}/${{ github.repository }}" \
              --branch "${{ github.ref_name }}" \
              --cluster "$ISOGRID_CLUSTER" \
              --tier "$ISOGRID_TIER" \
              --network "$ISOGRID_NETWORK" \
              $ENV_ARGS \
              --wait
          fi

قالب GitLab CI/CD

# Deploy to ISOGrid from GitLab CI/CD.
#
# Save as .gitlab-ci.yml at the root of the project (or generate it with
# `isogrid ci init gitlab`), or merge the job into the one you have. On every
# push to the default branch, and when run by hand, it installs the ISOGrid CLI
# and either updates the application and deploys the pushed commit, or - the
# first time - creates the application.
#
# Set these under Settings -> CI/CD -> Variables:
#
#   ISOGRID_TOKEN      A CLI credential; mark it Masked (and Protected if only
#                      protected branches deploy). Mint a narrow one for CI:
#                      `isogrid login --scope application:create,application:write-any --name gitlab-ci`
#                      on your machine, then copy the token from
#                      ~/.isogrid/config.json. Never commit it.
#   ISOGRID_API_URL    Your platform's API, e.g. https://api.isogrid.skyvault.pro
#
# and optionally override the defaults below the same way (project variables
# take precedence over the values written in this file):
#
#   ISOGRID_APP        Application name (default: the project's name)
#   ISOGRID_CLUSTER    Region slug (optional when only one region is open to you)
#   ISOGRID_TIER       Instance type name (optional: the region's smallest)
#   ISOGRID_NETWORK    Network slug (optional: your organization's network)
#   ISOGRID_ENV_FILE   A dotenv file in the repository to apply (optional)
#
# The project must already be connected to the organization through the
# ISOGrid GitLab integration (`isogrid repos list` shows it once it is).

stages:
  - deploy

variables:
  ISOGRID_APP: "$CI_PROJECT_NAME"
  ISOGRID_CLUSTER: ""
  ISOGRID_TIER: ""
  ISOGRID_NETWORK: ""
  ISOGRID_ENV_FILE: ""

isogrid-deploy:
  stage: deploy
  image: alpine:3.20
  # One deployment of a branch at a time.
  resource_group: isogrid-$CI_COMMIT_REF_SLUG
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_PIPELINE_SOURCE == "web"
  before_script:
    - apk add --no-cache curl
    - curl -fsSL "$ISOGRID_API_URL/api/v1/cli/install.sh" | ISOGRID_INSTALL_DIR=/usr/local/bin sh
    - isogrid version
  script:
    - |
      set -eu
      ENV_ARGS=""
      if [ -n "$ISOGRID_ENV_FILE" ]; then
        ENV_ARGS="--env-file $ISOGRID_ENV_FILE"
      fi

      # --exact: an application called "api" must not be taken for "api-gateway".
      if isogrid apps get "$ISOGRID_APP" --exact --json > /dev/null 2>&1; then
        # Builds this exact commit and waits; the job fails if the build or
        # the deployment does.
        isogrid apps update "$ISOGRID_APP" \
          --branch "$CI_COMMIT_REF_NAME" \
          $ENV_ARGS \
          --deploy --ref "$CI_COMMIT_SHA" --wait
      else
        isogrid apps create \
          --name "$ISOGRID_APP" \
          --provider gitlab \
          --url "$CI_PROJECT_URL" \
          --branch "$CI_COMMIT_REF_NAME" \
          --cluster "$ISOGRID_CLUSTER" \
          --tier "$ISOGRID_TIER" \
          --network "$ISOGRID_NETWORK" \
          $ENV_ARGS \
          --wait
      fi

يُعطى --provider gitlab صراحةً، فيعمل كذلك GitLab المستضاف ذاتيًا الذي لا يحتوي عنوانه على "gitlab"، ويحمل $CI_PROJECT_URL المجموعات المتداخلة كما هي.

ما يفعله خطّ النشر، خطوةً بخطوة

يثبّت الـ CLI من منصّتك، مع التحقّق من بصمتها. الـ CLI والمنصّة من الجيل نفسه دائمًا، لأن إحداهما تقدّم الأخرى.

يسأل عن وجود التطبيق بـ apps get --exact. ودون --exact، سيُظنّ تطبيق جديد اسمه api هو api-gateway الموجود، وسينشر خطّ النشر الشيء الخطأ.

يحدّثه إن كان موجودًا. يُبقي --branch التطبيق متتبّعًا للفرع الذي دُفع إليه. ويبني --deploy --ref <commit> ذلك الإيداع بعينه، لا ما يشير إليه الفرع لحظة بدء البناء، فلا يمكن لدفعتين متقاربتين أن تنشر إحداهما شيفرة الأخرى. ويدمج --env-file الملف في بيئة التطبيق؛ والمفاتيح التي حذفتها من الملف تبقى على التطبيق ما لم تضف --replace-env.

ينشئه في المرّة الأولى، من المستودع الذي أطلق التشغيل. وتعني القيم الفارغة لـ --cluster و--tier و--network القيم الافتراضية.

ينتظر. لا يعود --wait إلا بعد انتهاء البناء والنشر، ويخرج بالرمز 1 حين يفشل أيّهما، بما في ذلك نشر تُراجع عنه إلى النسخة السابقة. ورمز الخروج هذا هو ما يُفشل المهمّة.

يشغّل نشرًا واحدًا في كل مرّة لكل فرع: concurrency في GitHub، وresource_group في GitLab. الدفعة الثانية تنتظر الأولى بدل أن تسابقها.

كتابة خطّك الخاص

جوهر القالبين خمسة أسطر من أوامر الصدفة، فيمكن لأي نظام CI قادر على تشغيل صدفة أن يستخدمها:

curl -fsSL "$ISOGRID_API_URL/api/v1/cli/install.sh" | sh
if isogrid apps get "$APP" --exact --json > /dev/null 2>&1; then
  isogrid apps update "$APP" --branch "$BRANCH" --deploy --ref "$COMMIT_SHA" --wait
else
  isogrid apps create --name "$APP" --provider github --url "$REPO_URL" --branch "$BRANCH" --wait
fi

بديل: بناء الصورة في CI

إن كان خطّ النشر لديك يبني صورة حاوية أصلًا، فدعه يدفع الصورة إلى سجلّ مؤسّستك ودع المنصّة تنشرها، بدل البناء مرّة ثانية.

أنشئ التطبيق مرّة واحدة، من الصورة:

isogrid apps create --name worker --provider registry --url acme/worker:latest --cluster eu-west

ثم يدفع كل تشغيل وسمًا جديدًا ويوجّه التطبيق إليه:

docker build -t "$REGISTRY/$NAMESPACE/worker:$COMMIT_SHA" .
docker push "$REGISTRY/$NAMESPACE/worker:$COMMIT_SHA"
isogrid apps update worker --tag "$COMMIT_SHA" --deploy --wait

يحتاج خطّ النشر إلى بيانات اعتماد السجلّ بجانب ISOGRID_TOKEN: حساب آلي من صفحة Images (الصور) في وحدة التحكّم، يُحفظ في ISOGRID_REGISTRY_USER مع ISOGRID_REGISTRY_PASSWORD سرًّا أو متغيّرًا مُقنَّعًا، إضافةً إلى ISOGRID_REGISTRY (مضيف السجلّ) وISOGRID_NAMESPACE (مساحة أسمائك). يحمل القالبان هذه المهمّة معلَّقة في أسفل الملف. ويجب أن تكون أسماء الصور بحروف صغيرة، فاضبط ISOGRID_APP صراحةً حين لا يكون اسم المستودع كذلك.

حين تسوء الأمور

اجعل isogrid whoami أول أمر في المهمّة ما دمت تضبط الأمور. يطبع لمن تعود بيانات الاعتماد، والمؤسّسة المثبّتة عليها، والصلاحيات التي ستُسمح لها فعلًا.

"not signed in". ISOGRID_TOKEN فارغ في المهمّة. في GitHub، لا يُمرَّر السرّ إلى سير العمل الذي تُطلقه نسخ متفرّعة (forks). وفي GitLab، لا يُمرَّر المتغيّر Protected إلى الفروع غير المحميّة.

انتهت صلاحية بيانات الاعتماد، أو أُبطلت. أنشئ واحدة جديدة واستبدل السرّ. لا شيء آخر يحتاج إلى تغيير.

خطأ صلاحيات في apps create. تفتقر بيانات الاعتماد إلى application:create، أو أنك لا تملكه بنفسك. يبيّن whoami أيّهما.

خطأ صلاحيات في apps update. لا تستطيع بيانات الاعتماد تعديل ذلك التطبيق أو نشره. إمّا أنها تفتقر إلى application:write-any، وإمّا أنها لا تملك إلا application:deploy-any والأمر مرّر إعدادًا (--env-file، أو --branch مختلفًا).

"is not a repository you have been given access to"، أو "You do not have 'repo:deploy' on acme/api". الرمز يعمل باسمك، وأنت لا تستطيع البناء من ذلك المستودع. شخص آخر ربط الحساب الذي يأتي عبره المستودع ولم يفتحه لك. اطلب منه، أو من مسؤول إن كان قد غادر، أن يشغّل isogrid repos grant acme/api --member you@example.com --preset repo-deployer. وإن كان المستودع مفتوحًا لـ المسؤولين وكنت منهم، فأنشئ بيانات الاعتماد مع github:manage في نطاقاتها أيضًا: فعلى هذه الصلاحية يقوم وصول المسؤولين إلى المستودعات. انظر مستودعات Git.

"pinned to" مؤسّسة أخرى. وُوفق على بيانات الاعتماد بينما كان المتصفّح يعرض مؤسّسة مختلفة، أو أن ISOGRID_ORGANIZATION أو --organization يسمّي مؤسّسة ليست مثبّتة عليها. بدّل المؤسّسة في وحدة التحكّم وأنشئ بيانات الاعتماد مجدّدًا.

"choose a region with --cluster". أكثر من منطقة مفتوحة لك. اضبط ISOGRID_CLUSTER.

انتهت مهلة المهمّة لكن النشر اكتمل. توقّف --wait بعد --timeout (20 دقيقة افتراضيًا)؛ واستمرّ النشر. ارفعها بـ --timeout 40m لعمليات البناء البطيئة.

فشلت المهمّة والموقع ما زال يعمل. لم تصبح النسخة الجديدة سليمة أبدًا فأُعيدت السابقة. وهذا فشل يستحقّ أن تفشل المهمّة من أجله. اقرأ isogrid apps logs <app>.

رموز الخروج

الرمز المعنى
0 تمّ النشر، أو لا شيء يُغيَّر.
1 فشل شيء ما: خطأ من واجهة API أو خطأ صلاحيات، أو بناء فاشل، أو نشر فاشل أو متراجَع عنه، أو انتهاء المهلة.
2 استُدعي الأمر بشكل خاطئ: خيار مجهول، أو قيمة ناقصة، أو delete دون --yes. يكاد يكون دائمًا خطأً في ملف خطّ النشر.