צ'קליסט CI לפרויקט

בכל פעם שאני מתחיל פרויקט חדש, אישי או של העבודה, אני צריך להיזכר מחדש איזה בדיקות וכלים אני נוהג להוסיף, תלוי בשפה של הפרויקט, בצרכים שלו והאם הוא אישי או ארגוני.

אז הנה רשימה של כלים ובדיקות להוסיף לפרויקט חדש.

תשלחו לי כלים שאתם משתמשים בהם ואני אוסיף אותם לרשימה.

Git Hooks

בפרויקטים של JavaScript אני מוסיף את husky שמאפשר לי להגדיר Git hooks (כלומר פקודות שירוצו לפני\אחרי commit/push וכו'), ובעזרתו אני יכול לחסוך למפתח טעויות פשוטות שאחרת הוא יגלה רק כשהוא יפתח PR.

את husky אני משלב עם lint-staged שמאפשר לי למנוע מהמפתח לקבל שגיאות שלא נגרמו על ידו, ולהריץ את הבדיקות רק על הקוד שהוא מבקש להכניס.

בשלב הזה אני מריץ את הכלים הפשוטים שחוסכים לכולנו כאב ראש:

וזה נראה כך:

# package.json
"lint-staged": {
    "**/*": "prettier --write --ignore-unknown",
    "{src,tests}/**/*.js": "eslint --fix"
}
# .husky/pre-commit
#!/bin/sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

דוגמא מפרויקט Overlay

Pull Request Validation

כאן לא מדובר בכלים ספציפיים, אלא בפעולות שכדאי להוסיף עוד בשלב הPull Request כדי לחסוך הערות מיותרות בסקירה, וגם כדי לזהות בהקדם בעיות שעלולות לצוץ בענף הראשי:

דוגמא מפרויקט כספיון (TypeScript)
דוגמא מפרויקט 2ms (Go) _ולא לשכוח להשתמש בmerge_group

lockfile-lint

[JavaScript/TypeScript]

בפרויקטים של JS, החבילות מותקנות על פי מה שכתוב בקובץ lock (package-lock.json/yarn.lock), ולכן חשוב לוודא שהקובץ הזה תקין ולא נערך בצורה לא צפויה, מכיוון שתוקף יכול לשנות את הרישום של אחת החבילות בקובץ, ואנחנו לא נשים לב, כי מי מסתכל על הקובץ הזה בכלל?

דוגמא מפרויקט Overlay

new-dependencies-advisor

[JavaScript/TypeScript]

כלי שמתריע על תלות חדשה שנוספת לפרויקט בקוד הנוכחי, ומציג את הציון והפידבק של Snyk Advisor על החבילה החדשה.

דוגמא מפרויקט Overlay

gosec

[Go]

כלי בדיקת אבטחה לGo.

כלי אבטחה אני מריץ בכמה הזדמנויות. קודם כל בשלב הPull Request כמובן, כדי לוודא שלא מכניסים בעיות אבטחה חדשות. אבל מכיוון שכלי אבטחה חייבים להתעדכן עם הזמן, אני מריץ אותם באופן קבוע (אחת ליום או לשבוע) גם על הענף הראשי, כדי לוודא שלא צצו בעיות אבטחה חדשות.

דוגמא מפרויקט 2ms

Kics

Kics הוא מנוע לזיהוי חולשות אבטחה בInfrastructure as Code. כמעט בכל פרויקט יש לפחות קובץ Dockerfile, וKics סורק אותו למציאת בעיות פוטנציאליות.

דוגמא מפרויקט 2ms

PR title

אני משתדל להקפיד על Conventional Commits כמו שנראה בהמשך, ולכן אני מוסיף בדיקה שהכותרת של הPR עומדת בקונבנציה. בדרך כלל אני נעזר בcommitlint בשביל לבדוק.

name: Validate Conventional Commit title

on:
  pull_request:
    types: [opened, edited, synchronize, reopened]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - name: install commitlint
        run: npm install -g @commitlint/cli @commitlint/config-conventional
      - name: config commitlint
        run: |
          echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
      - name: validate PR title
        run: |
          echo ${{ github.event.pull_request.title }} | commitlint

את הבדיקה הזאת צריך לתמוך על ידי ההגדרה שהכותרת של הPR תהיה גם ההודעה בcommit:

Squash Pull Request Title

דוגמא מפרויקט Overlay

CI

אז מעבר לפעולות הברורות מאליהן של העלאת חבילה לnpm, עדכון האתר או Dockerhub, יש כמה פעולות נוספות שאני מבצע.

בעקרון רציתי לנצל את ההזדמנות לבדוק את פרויקט allero שעושה ולידציה על הCI, אבל ראיתי שהוא לא מתוחזק אז ויתרתי.

Semantic Release

כדי לשמור על עקרונות semver, אני משתמש בכלי שנקרא semantic-release, ועל פי הפורמט של הקומיטים (שתואר בPR Title למעלה), מעדכן את מספר הגרסה ויוצר גרסה.

מצד אחד מדובר בכלי מדהים, הוא יודע כמעט בלי הגדרות מיוחדות ליצור Github Release, להעלות לnpmjs.org, ליצור changelog ולכתוב בתגובה לIssues/Pull Requests שהם נכללו בגרסה האחרונה.

הבעיה שלי אם הכלי הזה היא שברגע שאני רוצה להתאים אותו לצרכים שלי, אם אני רוצה לשנות את הפורמט של התיאור של הגרסה, או להיעזר בו ליצירת גרסה זמנית שאני מעלה אליה קבצים, אני תמיד מסתבך ומבזבז על זה הרבה זמן.

דוגמא מפרויקט כספיון

ניהול פרויקט ואינטגרציות

פינוי משימות וPull Requests שנשכחו

כמנהל פרויקט קוד פתוח, קורה לי הרבה שבאים אנשים ומציעים לי תרומת קוד, ובתהליך הReview הם נשברים או סתם נהיים עסוקים, ולא משלימים את העבודה.

קורה גם לפעמים שיש לי הערות שאולי קשה לי להסביר אבל קל לי מאוד לבצע.

התוצאה של הסיבות הללו היא שבסופו של דבר יש Pull Request שלא קורה איתו כלום שבוע, או שיותר גרוע, אני חסר סבלנות ואני לוקח למישהו Pull Request ומשלים אותו.

בשביל להימנע מהמבוכה הזאת, אני מבצע כמה פעולות.

השמה אוטומטית של Pull Request

ברגע שמישהו פותח PR, אני אוטומטית assign עליו את הPR. זה יעזור לי בהמשך כפי שתראו.

assign-author:
  runs-on: ubuntu-latest
  if: github.event_name == 'pull_request_target'
  steps:
    - uses: toshimaru/auto-author-assign@v1.6.2

התראה על Pull Request לא פעיל

יש Github Action רשמי של Github שעוזר לנו לנהל stale issues. אני משתמש בו עם פרמטרים מאוד מסוימים כדי למצוא Issues/PRs שלא פעילים כמה ימים, ואז אני כותב בהם הודעה שקוראת למפתח לעדכן מה הסטטוס, ואני מוסיף תגית מיוחדת.

הורדת משימה ממפתח

בצורה אוטומטית אני עובר על הIssues/PRs שסומנו קודם לכן בתגית המיוחדת, ואם לא התבצעה בהם פעולה בימים האחרונים (מאז ששמתי את התגית), אני מוריד מהם את הassign, ובעצם מסמן שאפשר לקחת אותם ולהמשיך את העבודה שמישהו אחר עשה.

comment-on-possible-stale-issues:
  # This job uses the stale action to find inactive assigned issues and pull requests,
  # and comments on them to remind the assignee to take action.
  # If the assignee does not take action, the another action will unassign them.
  name: Comment on possible stable issues
  if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
  runs-on: ubuntu-latest
  steps:
    - uses: actions/stale@v8
      with:
        include-only-assigned: true
        exempt-assignees: "baruchiro"
        days-before-stale: 7
        days-before-close: -1 # Never close an issue/pr
        stale-issue-message: "Hey! This task was taken over a few days ago, but nothing has happened since then. Maybe the current contributor can comment on this?"
        stale-pr-message: "Hey! This pull request was made a few days ago and still needs changes, but nothing has happened since then. Maybe the current contributor can comment on this?"
        stale-issue-label: "Waiting for contributor"
        stale-pr-label: "Waiting for contributor"
        exempt-issue-labels: "on hold"
        exempt-pr-labels: "on hold"
        remove-stale-when-updated: true
    - uses: boundfoxstudios/action-unassign-contributor-after-days-of-inactivity@1.0.2
      with:
        last-activity: 7
        labels: "Waiting for contributor"
        exempt-assignees: "baruchiro"
        labels-to-remove: "Waiting for contributor"
        message: "Due to a long period of inactivity, this task was unassigned automatically."

דוגמא מפרויקט Overlay

Community tag

בפרק 19 של הפודקאסט "קוד פתוח", ליזה כץ אומרת שבמבט לאחור, באלסטיק היו צריכים לסמן איזה קוד הגיע מתורמים חיצוניים, מכיוון שבגיטאהב אין את המידע הזה.

אצלנו באחד הפרויקטים התחלנו מהיום הראשון לסמן PRים שמגיעים מבחוץ:

- name: Mark as Community if PR is from a fork
  if: github.event.pull_request.head.repo.full_name != github.repository
  uses: actions/github-script@v6
  with:
    script: |
      github.rest.issues.addLabels({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        labels: ['Community']
      })

דוגמא מפרויקט 2ms

Notify Discord

בימים שעוד היה API פתוח לטוויטר, הייתי שולח לשם, אבל בימינו שטוויטר מתחיל להיסגר, ודיסקורד מתחיל לעלות, אני מעוניין לשלוח עדכונים מסוימים לערוצים מסוימים.

למשל, לשלוח הודעות על גרסה חדשה שיוצאת, כמובן. בנוסף, בשביל ערוצי קוד פתוח, אני שולח good first issues חדשים שנפתחים.

בדיסקורד זה מאוד פשוט, יוצרים Webhook ושולחים אליו הודעה.

- name: Notify Discord
  if: ${{ steps.semantic_release_info.outputs.git_tag }}
  run: |
    curl -X POST -H "Content-Type: application/json" -d "{\"content\": \"$msg\"}" ${{ secrets.DISCORD_CHECKMARX_WEBHOOK }}
    curl -X POST -H "Content-Type: application/json" -d "{\"content\": \"$msg\"}" ${{ secrets.DISCORD_MAAKAF_WEBHOOK }}
    curl -X POST -H "Content-Type: application/json" -d "{\"content\": \"$msg\"}" ${{ secrets.DISCORD_SCAR_WEBHOOK }}
  env:
    msg: 'Yay! 🎉 \n Version ${{ steps.semantic_release_info.outputs.version }} was released! \n Check it out in: ${{ steps.upload_artifacts.outputs.url }}'

דוגמא מפרויקט Overlay (הודעה על good first issue)

Badges

עוד אין לי רשימה, זה בטח גם תלוי בכלים שאני משתמש בהם.

תוספות מתגובות לפוסט

ישראל המליץ בטוויטר על cookiecutter, כלי ליצירת Templates לפרויקטים. האמת שנראה טוב ואני מקווה שביום שאני אצור פרויקט לשימוש חוזר אני אשתמש בזה (אם כי אני כבר חושב שאולי זה מוגבל רק לvariables ולא לרמה של איזה קבצים לכלול בפרויקט, אבל לא קראתי את כל התיעוד)

מענדי המליץ בטוויטר על ts-reset, זאת חבילה שקצת מתקנת Types של TypeScript. אני שומר את זה פה אבל זה כנראה שייך לפוסט מעט דומה על כלים לעבודה על פרויקטים (כאן אנחנו מתמקדים בCI)

מענדי המליץ גם על sync-dotenv להוספה לpre-commit Git Hook שתיארתי למעלה. המטרה של הכלי הזה היא לוודא שרשימת משתני הסביבה בקובץ .env.example תואמת לרשימה שיש לנו ב.env (שכמובן לא נכנס לGit)