Github action – Reusable

# https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions
name: XCrawler - Build & Tests

# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on
on:
  # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
  pull_request:
    branches: [ develop ]
    types: [ opened, synchronize ]

jobs:

  sonar_cloud:
    name: SonarCloud
    uses: ./.github/workflows/sonar_cloud.yml
    secrets: inherit

  security_check:
    name: Security check
    uses: ./.github/workflows/security_check.yml
    secrets: inherit

  code_standards:
    name: Code standards check
    needs: [ sonar_cloud, security_check ]
    strategy:
      matrix:
        lint: [ phpstan, phpmd, phpcs ]
    uses: ./.github/workflows/code_standards.yml
    with:
      lint: ${{ matrix.lint }}
    secrets: inherit

  tests:
    name: Execute UnitTest
    needs: [ code_standards ]
    strategy:
      matrix:
        test: [ Client, JAV, Flickr, Core ]
    uses: ./.github/workflows/unittest.yml
    with:
      test: ${{ matrix.test }}
    secrets: inherit

  finalizing_build:
    name: "All Parallel Tests passed"
    needs: [ tests ]
    uses: ./.github/workflows/codecov.yml
    secrets: inherit
Continue reading Github action – Reusable

Laravel – Code structure with UnitTest

Giả sử ta có một code structure như sau

Controller

  • Gọi Services để thực thi và respond

Services

  • Gọi Repository khi cần tương tác database
  • Trigger Event
  • Gọi các Services khác hoặc các code logic khác.

Vậy UnitTest sẽ cần những gì. Trong quan điểm mình là đi từ thấp nhất lên cao nhất

  • Repositories : Đảm bảo database được thực thi, assert các data như mong đợi.
    • Assert events đã dispatched.
    • Events : Dispatch events và assert data như mong đợi
  • Services
    • Nếu services có các events ẢNH HƯỞNG TRỰC TIẾP đến kết quả thì mình sẽ không fake event mà để event thực thi và assert kết quả
    • Nếu các events đó dùng cho 3rd thì mình assert để make sure events dispatched như mong đợi

Tất nhiên nếu service đó thuần túy gọi tới 3rd party thì mình chỉ việc mock và test respond từ 3rd party thôi.

Và sau cùng là assert controller trả về data như mong đợi

Microservice – XClient

https://github.com/jooservices/XClient

As a part of refactoring XCrawler. XClient under development

  • Receive crawl request from another service and process request to 3rd party
  • Receive queue request from another service. Queue will be scheduling process later and process callback to another service

Caching also provided

By this way, we don’t need care anything about Curl / Guzzle in another project / service.

Laravel – Different between Model trait boot & initialization

Trace back to Model we have

    protected static function bootTraits()
    {
        $class = static::class;

        $booted = [];

        static::$traitInitializers[$class] = [];

        foreach (class_uses_recursive($class) as $trait) {
            $method = 'boot'.class_basename($trait);

            if (method_exists($class, $method) && ! in_array($method, $booted)) {
                forward_static_call([$class, $method]);

                $booted[] = $method;
            }

            if (method_exists($class, $method = 'initialize'.class_basename($trait))) {
                static::$traitInitializers[$class][] = $method;

                static::$traitInitializers[$class] = array_unique(
                    static::$traitInitializers[$class]
                );
            }
        }
    }

Theo như code trên có thể thấy

  • boot sẽ call đến static method. Hay nói cách khác sẽ tác động tới mọi instances của Model này
  • trong khi đó `initialize thì ngược lại. Chỉ tác động tới instance hiện tại.

1 ví dụ thực tế

trait HasSlug
{
    public function initializeHasSystem()
    {
        $this->mergeCasts([
            'slug' => 'string'
        ]);

        $this->mergeFillable([
            'slug'
        ]);
    }

    public static function bootHasSlug()
    {
        static::creating(function ($model) {
            $model->{$model->getSlugName()} = Str::slug($model->name);
        });

        static::updating(function ($model) {
            $model->{$model->getSlugName()} = Str::slug($model->name);
        });
    }

    public function scopeSlug($query, $uuid)
    {
        return $query->where($this->getSlugName(), $uuid);
    }

    public function getSlugName()
    {
        return property_exists($this, 'slug') ? $this->slug : 'slug';
    }
}

HAProxy Health check fails

backend be_staging
        option httpchk

        server staging 192.168.1.60:80 check

Web server sử dụng Nginx

Đối với config trên thì HAProxy báo be_staging not available dù rằng mọi access đều working fine. !

Check Nginx log ta có

192.168.1.4 - - [27/Aug/2023:21:36:00 +0700] "OPTIONS / HTTP/1.0" 405 166 "-" "-"

Vậy là do Nginx nó trả về 405 cho OPTIONS requests.

Update HAProxy

backend be_staging
        option httpchk GET /
        http-check expect status 200

        server staging 192.168.1.60:80 check

Và mọi thứ trở về lại bình thường !

Laravel – Stable Diffusion

Sau một thời gian nghịch Stable Diffusion thì giờ cũng chút hứng thú với … code Laravel cho em nó.

https://github.com/jooservices/Stable-Diffusion

Về cơ bản

  • Tạo ra queues để liên tục generate images
  • Tạo ra các “base prompt” để có thể tái` sử dụng ( ví dụ có thể tạo ``$prompt->portraits('Irene Aronson') thì sẽ tự add prompt là portraits của nghệ sĩ Irene Aronson

Alrite. Tuy nhiên trước đó thì cần chuẩn bị 1 số thứ

  • Stable Diffusion ( webUI ) chạy service
Description=systemd service start stable-diffusion

[Service]
Type=simple
Restart=always
RestartSec=1
ExecStart=/bin/bash /home/joos/stable-diffusion-webui/webui.sh
User=joos
WorkingDirectory=/home/joos/stable-diffusion-webui/
StandardOutput=append:/var/log/sdwebui.log
StandardError=append:/var/log/sdwebui.log

[Install]
WantedBy=multi-user.target
  • 1 em cron để chạy schedule:run của Laravel

Xong ! Giờ thì ta có 2 cách

  • Generate ra “queues” qua command
php artisan stable-diffusion:generate --prompt="a beautiful girl at 20 years old. she has colorful hair"

Command này sẽ sinh ra 1.001 queues ứng với prompt trên và … toàn bộ models mình đang có.

  • Code thêm theo ý
        $response = $service
            ->txt2img()
            ->generate($payload)
            ->save($queue->uuid)
            ->getResponse();

Stable Diffusion – Public port

Bài viết này mình không tập trung vào Stable Diffusion mà là cách xử lý bài toán public port khi bản thân nó chỉ bind cho localhost.

By default Stable Diffusion chỉ listening trên 127.0.0.1:7860, nghĩa là bạn không thể public ra ngoài Internet hoặc access từ các máy khác trong LAN.

Tất nhiên ta vẫn có thể sử dụng paramter –listen để listening trên 0.0.0.0:7860. Tuy nhiên cái giá phải trả là sẽ bị hạn chế nhiều thứ ( góc nhìn của developer AUTOMATIC1111 có lẽ là hạn chế bị change settings, cài extensions ngoài ý muốn khi public ).

Nhưng nếu bạn vẫn muốn làm điều này thì sao ? Giải quyết bài toán này khá đơn giản với HAProxy

  • HAProxy có thể dùng Frontend để listening 1 port nhất định sau đó forwarding về 1 IP khác. Trong trường hợp này là 127.0.0.1

Vậy trên chính cái máy bạn install Stable Diffusion WebUI, ta setup thêm HAProxy và cấu hình

frontend http
        bind :8080
        mode http

        default_backend be_develop



backend be_develop
    # default backend
        #option httpchk

        server hosting 127.0.0.1:7860

Nghĩa là ta listening port 8080 từ ngoài vào ( tất nhiên router đã NAT 8080 về IP máy rồi ).

Sau đó do HAProxy nằm trên chính máy Stable Diffusion nên hiển nhiên access 127.0.0.1 được. Vậy ta cứ forward về đó thôi.

Xong 😀

Tất nhiên lúc này ta có thể secure thêm 1 chút bằng basic authenticate.