مطالب
PowerShell 7.x - قسمت دوازدهم - آشنایی با GitHub Actions و بررسی یک مثال
GitHub Actions، یک راه‌حل Continuous Integration است که توسط آن می‌توان یکسری trigger workflowهایی را حین push کردن، ارسال PR و … اجرا کرد. برای کارهایی از قبیل اجرای تست‌های خودکار، اجرای یکسری تست و همچنین deploy کردن از آن استفاده میشود. GitHub Actions در واقع یک managed serviceیی است که توسط GitHub ارائه میشود. به این معنا که نیازی نیست خودمان درگیر مدیریت منابع باشیم. همچنین تعداد زیادی اکشن توسط community برای استفاده توسعه داده شده‌اند. در ادامه ابتدا مرور سریعی بر GitHub Actions خواهیم داشت، سپس یک مثال از آن را به همراه PowerShell بررسی خواهیم کرد.

ساختار یک اکشن
  • Workflow: یکی از مفاهیمی که باید با آن آشنا باشیم workflowها هستند. یک workflow مجموعه‌ایی از jobهایی هستند که در رخدادهای خاصی اجرا میشوند. در واقع یک workflow یک CI pipeline است که با کمک YAML آنها را تعریف میکنیم.
  • Runner: اینها به اصطلاح compute machineهایی هستند که workflowها را اجرا میکنند. این runnerها هم میتوانند به صورت سفارشی باشند و هم سرویس‌های ارائه شده توسط GitHub باشند.
  • Job: مجموعه‌ایی از مراحلی که درون یک runner workspace اجرا میشوند.
  • Step: در نهایت stepها هستند که کوچکترین بخش GitHub Actions هستند. stepها میتوانند فایل اسکریپت، Dockerfile یا یک community action باشند.

نمونه‌ی یک Workflow
در ادامه یک workflow را مشاهده میکنید. در اینجا نام آن را به Build Application Code تنظیم کرده‌ایم. سپس با کمک on، تریگر اجرای این workflow را تعیین کرده‌ایم. به این معنا که با push کردن بر روی ریپوزیتوری، workflow اجرا خواهد شد. سپس توسط job، لیست jobهایی را که میخواهیم این workflow اجرا کند، مشخص کرده‌ایم. اولین jobی که اجرا خواهد شد، build است. این job قرار است بر روی یک ماشین با آخرین نگارش ابونتو اجرا شود. مراحل یا stepهای این job نیز به ترتیب، clone کردن سورس‌کد و سپس نصب وابستگی‌های پروژه است. در نهایت job بعدی، test خواهد بود که با کمک needs تعیین کرده‌ایم که ابتدا مرحله‌ی قبل یعنی build اجرا شود و سپس وارد این مرحله شود. 
name: Build Application Code

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v2
      - name: Install Libraries
        uses: pip install -r requirements.txt -t .
    
    test:
      runs-on: ubuntu-latest
      needs: build
      steps:
      ...

مثال PowerShell
هدف، پویا کردن قسمت README یک پروفایل GitHub است. برای این مثال من از پروفایل خودم استفاده خواهم کرد. درون فایل README میخواهم لیست آخرین بلاگ‌پست‌هایی را که منتشر کرده‌ام، به همراه یک کامپوننت، تعداد قدم‌هایی را که در طول روز پیاده‌روی میکنم، نمایش دهم. برای نمایش آخرین دیتای درون پروفایلم، نیاز به دو Action Workflow داشتیم که هر یک در تایم خاصی اجرا شده و اسکریپت‌هایی را که در ادامه توضیح خواهم داد، اجرا کنند. برای اینکار درون دایرکتوری مخصوص github.، ساختار زیر را ایجاد کرده‌ام: 
├── .github
│   ├── scripts
│   └── workflows
├── README.md
├── assets
└── deps
ابتدا workflow اول یعنی نمایش بلاگ‌پست‌های اخیر را بررسی خواهیم کرد: 
name: Update Recent Blog Posts

on:
  schedule:
    - cron: "0 0 * * 0" # Run once a week at 00:00 (midnight) on Sunday
  workflow_dispatch:

jobs:
  update_posts:
    runs-on: ubuntu-latest

    steps:
    - name: Check out repository code
      uses: actions/checkout@v3

    - name: Run the script for fetching latest blog posts
      shell: pwsh
      run: |
        . ./.github/scripts/Get-Posts.ps1
        
    - name: Commit and Push the changes
      uses: mikeal/publish-to-github-action@master
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
workflow فوق یکبار در هفته فایل PowerShell موردنظر را اجرا خواهد کرد. در ادامه محتویات این فایل را مشاهده می‌کنید: 
Function Get-Posts {
    Param (
        [Parameter(Mandatory = $false)]
        [string]$rssUrl
    )
    $posts = @()
    $feed = [xml](Invoke-WebRequest -Uri $rssUrl).Content
    $feed.rss.channel.item | Select-Object -First 3 | ForEach-Object {
        $post = [PSCustomObject]@{
            Title       = $_.title."#cdata-section" ?? $_.title
            Link        = $_.link
            Description = $_.description."#cdata-section" ?? $_.description
            PubDate     = $_.pubDate
        }
        $posts += $post
    }
    $posts
}

Function Get-DntipsPosts {
    $assemblyPath = "$(Get-Location)/deps/CodeHollow.FeedReader.dll"
    [Reflection.Assembly]::LoadFile($assemblyPath)
    $feed = [CodeHollow.FeedReader.FeedReader]::ReadAsync("https://www.dntips.ir/feed/author/%d8%b3%db%8c%d8%b1%d9%88%d8%a7%d9%86%20%d8%b9%d9%81%db%8c%d9%81%db%8c").Result
    $posts = @()
    $feed.Items | Select-Object -First 3 | ForEach-Object {
        $post = [PSCustomObject]@{
            Title       = $_.Title
            Link        = $_.Link
            Description = $_.Description
            PubDate     = $_.PublishingDate
        }
        $posts += $post
    }
    $posts
}

Function Set-Posts {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [PSCustomObject[]]$posts,
        [Parameter(Mandatory = $false)]
        [string]$marker = "## Recent Blog Posts - English"
    )
    Begin {
        $readMePath = "./README.md"
        $readmeContents = Get-Content -Path $readMePath -Raw
        $markdownTable = "| Link | Published At |`n"
        $markdownTable += "| --- | --- |`n"
    }
    Process {
        if ($null -eq $_.Title) {
            return
        }
        $date = Get-Date -Date $_.PubDate
        $link = "[$($_.Title)]($($_.Link))"
        
        $markdownTable += "| $($link) | $($date.ToString("dd/MM/yy")) |`n"
    }
    End {
        $updatedContent = $readmeContents -replace "$marker\n([\s\S]*?)(?=#| $)", "$marker`n$($markdownTable)`n"
        $updatedContent | Set-Content -Path $readMePath
    }
}

Function Set-Blogs {
    $recentBlogPostsStr = "## Recent blog posts -"
    Get-Posts("https://dev.to/feed/sirwanafifi") | Set-Posts -marker "$recentBlogPostsStr dev.to"
    Get-Posts("https://sirwan.infohttps://www.dntips.ir/rss.xml") | Set-Posts -marker "$recentBlogPostsStr sirwan.info"
    Get-DntipsPosts | Set-Posts -marker "$recentBlogPostsStr dntips.ir"
}

Set-Blogs

در اینجا تابع Set-Blogs فراخوانی خواهد شد. کاری که این تابع انجام میدهد، دریافت آخرین بلاگ‌پست‌هایی که در جاهای مختلف منتشر کرده‌ام و سپس آپدیت کردن فایل README با دیتای جدید است. همانطور که مشاهده میکنید برای خواندن فید سایت جاری، از پکیج FeedReader استفاده کرده‌ام. در PowerShell توسط Invoke-WebRequest میتوانیم یک فید را پارز کنیم؛ اما برای سایت جاری با خطا روبرو شدم و در نهایت تصمیم گرفتم از یک پکیج دات‌نتی استفاده کنم. وابستگی موردنظر، درون دایرکتوری dep به صورت DLL قرار دارد. سپس از طریق PowerShell اسمبلی مربوطه بارگذاری شده و از کتابخانه موردنظر استفاده شده‌است. در نهایت برای آپدیت کردن فایل README.md یکسری marker تعیین کرده‌ام که با یک جایگزینی محتویات موردنظر، آنجا قرار خواهند گرفت.

workflow بعدی نیز به صورت زیر میباشد که در پایان هر روز در یک ساعت مشخص اجرا خواهد شد: 
name: Update Step Component

on:
  schedule:
    - cron: "0 18 * * *"
  workflow_dispatch:

jobs:
  update_steps:
    runs-on: ubuntu-latest

    steps:
    - name: Check out repository code
      uses: actions/checkout@v3

    - name: Run the script for fetching my latest steps
      shell: pwsh
      env:
          STEPS_URI: ${{ secrets.STEPS_URI }}
      run: |
        . ./.github/scripts/Get-Steps.ps1
    
    - name: Commit and Push the changes
      uses: mikeal/publish-to-github-action@master
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
workflow فوق نیز همانند روال قبل فایل اسکریپت موردنظر را توسط dot sourcing اجرا میکند. این روال هر روز، ساعت ۱۸ انجام خواهد شد. اسکریپت مربوطه نیز به صورت زیر پیاده‌سازی شده است: 
Function Set-Steps {
    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [PSObject]$json
    )
    Write-Host ($json | ConvertTo-Json)
    $SvgPath = "$(Get-Location)/assets/step.svg"
    $SvgContent = Get-Content -Path $SvgPath -Raw
    $TextTags = @"
    <tspan id="step-count" font-weight="bold">$([System.String]::Format("{0:n0}", [int]$json.steps))</tspan>
"@
    $DatetimeTags = "<text id=""datetime"" x=""800"" y=""72"" font-size=""39"" fill="#99989E"">$($json.date)</text>"
    $SvgContent = $SvgContent -Replace '<tspan id="step-count" font-weight="bold">.*?</tspan>', $TextTags
    $SvgContent = $SvgContent -Replace '<text id="datetime" x="800" y="72" font-size="39" fill="#99989E">.*?</text>', $DatetimeTags
    $SvgContent | Set-Content -Path $SvgPath
}

Function Get-LatestSteps {
    Try {
        $Uri = $env:STEPS_URI
        Write-Host "Uri: $Uri"
        $JsonResult = (Invoke-WebRequest -Uri $Uri).Content | ConvertFrom-Json
        Write-Host "Steps: $($JsonResult.steps)"
        Return $JsonResult
    }
    Catch {
        Return @{
            steps = 0
            date  = Get-Date -Format "yyyy-MM-dd"
        }
    }
}

Write-Host "Getting latest steps..."
Get-LatestSteps | Set-Steps
Write-Host "Done!"
اسکریپت فوق نیز همانند منطق اسکریپت قبلی یعنی جایگذاری رشته‌ی موردنظر با کمک عبارات باقاعده انجام شده‌است. در اینجا دیتای مربوط به قدم‌های من از APIایی که از طریق Environment Variable تعیین شده‌است، دریافت میشود و سپس خروجی آن که یک JSON است به تابع Set-Steps برای بروزرسانی فایل README.md ارسال میشود. در دو workflow نشان داده شده بعد از ایجاد تغییرات بر روی فایل‌های README.md و همچنین فایل SVG نیاز است که تغییرات را مجدداً به ریپوزیتوری پوش کنیم. برای اینکار از یک community action با نام  publish-to-github-action استفاده شده‌است. این اکشن نیاز به دسترسی پوش به ریپوزیتوری‌مان دارد که در اینجا ما از یک secret key مخصوص، با نام GITHUB_TOKEN استفاده کرده‌ایم. این توکن به صورت خودکار جنریت میشود و نیازی نیست خودمان آن را تنظیم کنیم.
خروجی در نهایت، اینچنین خواهد بود:

اشتراک‌ها
تبدیل دکمه ها به حالت loading با استفاده از Angular Ladda

Boostrap Ladda یک پلاگین برای نمایش دکمه هایی است که پس از کلیک باید به حالت loading تبدیل شوند. این پلاگین در فرم‌های ایجاد و یا ویرایش و یا صفحات جستجو بسیار مفید هستند. Angular-Ladda نسخه همگام سازی شده این پلاگین با AngularJs است که به سادگی میتواند به برنامه تک صفحه ای شما اضافه شود.

تبدیل دکمه ها به حالت loading با استفاده از Angular Ladda
نظرات مطالب
AngularJS #1
knockoutjs کتاب خانه ای برای انجام  DataBinding در وب هست، یعنی برای مقید سازی اشیاء جاوا اسکریپت به کنترل‌های html. 
AngularJS یک Toolset برای درست کردن framework و برنامه‌های تک صفحه ای می‌باشد، angular قابلیت مقید سازی داده‌ها را هم فراهم می‌کند. 
لطفا برای اطلاعات بیشتر این مطلب  را مطالعه کنید.
اشتراک‌ها
کتابخانه fontfaceobserver

Font Face Observer is a small @font-face loader and monitor (3.5KB minified and 1.3KB gzipped) compatible with any webfont service. It will monitor when a webfont is loaded and notify you. It does not limit you in any way in where, when, or how you load your webfonts. Unlike the Web Font Loader Font Face Observer uses scroll events to detect font loads efficiently and with minimum overhead.

var font = new FontFaceObserver('My Family', {
  weight: 400
});

font.load().then(function () {
  console.log('Font is available');
}, function () {
  console.log('Font is not available');
});
$ npm install fontfaceobserver
کتابخانه fontfaceobserver