splitsh-lite를 사용하여 PHP monorepo를 Packagist에 배포하기

발행: (2026년 3월 23일 AM 10:40 GMT+9)
15 분 소요
원문: Dev.to

Source: Dev.to

Series context: 이것은 Waaseyaa 시리즈의 10부입니다.
이전 글에서는 다음을 다루었습니다:

설치할 수 없는 프레임워크는 프레임워크가 아니다. 그것은 데모일 뿐이다.

이 게시물에서는 waaseyaa가 모든 하위 패키지가 @dev 경로 저장소에 의존하던 모노레포에서 개별 버전이 지정된 패키지로 Packagist에 옮겨간 과정을 다룹니다.

“그냥 퍼블리시”의 문제점

Waaseyaa는 모노레포입니다. 루트 composer.jsonpackages/ 아래에 43개의 서브‑패키지를 정의하고 있으며, 각각을 @dev 제약조건을 가진 path repository 로 참조합니다. 개발 단계에서는 이것이 편리합니다:

  • Composer가 모든 것을 로컬에서 해결합니다.
  • 버전 관리에 대한 부담이 없습니다.

하지만 루트 패키지를 Packagist에 등록하려고 하면 문제가 명확해집니다:

  • Packagist path repository를 해결할 수 없습니다.
  • 서브‑패키지의 require 블록에 있는 모든 "waaseyaa/entity": "@dev"는 레지스트리에는 존재하지 않는 로컬 디렉터리를 가리킵니다.
  • 따라서 루트 패키지는 모든 서브‑패키지를 먼저 퍼블리시하지 않으면 퍼블리시할 수 없습니다.

이는 메타데이터 수정으로 해결되는 문제가 아니라, 모노레포가 소비자와 어떻게 관계를 맺는가에 대한 아키텍처적 결정입니다.

네 가지 전략, 하나의 승자

전략최초 설치까지 시간유지보수사용자 편의성
별도 저장소로 분리높음 – 유지해야 할 저장소 43개깨끗하지만 개발이 어려움
Monorepo + splitsh‑lite낮음 – 태그 시 자동 분할깨끗한 설치, 모노레포 개발
프라이빗 Satis 레지스트리중간 – 자체 호스팅 레지스트리Satis 인프라 필요
Composer 메타패키지시간낮음 – 모든 것을 설치, 세분화 없음

splitsh‑lite가 승리한 이유는 모노레포를 단일 진실의 원천으로 유지하면서 Packagist가 필요로 하는 것을 제공하기 때문입니다: 패키지당 하나의 저장소, 각각 고유한 composer.json과 태그된 릴리스가 있습니다.

개발자 워크플로우는 변하지 않습니다. 여전히 모노레포에서 작업하고 루트에서 테스트를 실행합니다. 분할은 릴리스 관점의 문제이며, 개발 관점의 문제는 아닙니다.

splitsh‑lite 작동 방식

splitsh‑lite 은 Git 히스토리에서 하위 디렉터리를 읽어 오직 해당 디렉터리의 내용만 포함하는 새로운 커밋 트리를 생성합니다. 마치 그 디렉터리가 처음부터 별도의 저장소였던 것처럼 동작합니다. 파일을 체크아웃하지 않고 Git 객체를 직접 다루기 때문에 빠릅니다.

일반적인 워크플로우

  1. 모노레포에서 릴리스를 태그합니다(예: v1.1.0).
  2. 각 서브‑패키지마다 해당 디렉터리를 대상으로 splitsh‑lite 를 실행합니다.
  3. 분할된 커밋을 미러 저장소(waaseyaa/entity, waaseyaa/field, …)에 푸시합니다.
  4. 미러 저장소에 동일한 버전으로 태그를 붙입니다.
  5. Packagist 가 웹훅을 통해 미러에서 자동으로 동기화됩니다.
splitsh-lite --prefix=packages/entity --target=refs/heads/main

이 명령은 packages/entity/ 의 내용만 포함하는 커밋 해시를 생성하며, 히스토리를 보존합니다. 해당 커밋을 미러 저장소에 푸시하고 태그를 붙입니다.

GitHub Actions 워크플로우

수동 분할은 43개의 패키지에 적용하기 어렵습니다. 다음 GitHub Actions 워크플로우는 모든 태그 푸시 시 실행되어 각 패키지를 분할하고 결과를 해당 미러에 푸시합니다.

on:
  push:
    tags:
      - 'v*'

jobs:
  split:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        package:
          - { local: 'packages/entity', remote: 'entity' }
          - { local: 'packages/field', remote: 'field' }
          - { local: 'packages/access', remote: 'access' }
          # … all 43 packages
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # full history required by splitsh‑lite

      - name: Install splitsh‑lite
        run: |
          curl -sL https://github.com/splitsh/lite/releases/download/v1.0.1/lite_linux_amd64.tar.gz | tar xz
          sudo mv splitsh-lite /usr/local/bin/splitsh-lite

      - name: Split and push
        env:
          SPLIT_GH_TOKEN: ${{ secrets.SPLIT_GITHUB_TOKEN }}
          LOCAL_PREFIX: ${{ matrix.package.local }}
          REMOTE_REPO: ${{ matrix.package.remote }}
          REPO_OWNER: ${{ github.repository_owner }}
          TAG_NAME: ${{ github.ref_name }}
        run: |
          REMOTE_URL="https://x-access-token:${SPLIT_GH_TOKEN}@github.com/${REPO_OWNER}/${REMOTE_REPO}.git"
          SHA=$(splitsh-lite --prefix="${LOCAL_PREFIX}")
          git remote add split "${REMOTE_URL}" 2>/dev/null || true
          git push split "${SHA}:refs/heads/main" --force
          git push split "${SHA}:refs/tags/${TAG_NAME}" --force
  • 워크플로우는 splitsh‑lite를 GitHub 릴리스에서 직접 설치합니다.
  • 매트릭스 항목마다 병렬로 실행되며, 43개 패키지를 모두 분할하는 데 약 2분 정도 걸립니다.
  • fetch-depth: 0 은 필수입니다—splitsh‑lite는 올바른 서브트리 커밋을 만들기 위해 전체 Git 히스토리가 필요합니다. 얕은 클론은 손상된 분할을 생성합니다.

각 서브‑패키지 준비

첫 번째 분할 전에, 모든 서브‑패키지의 composer.json에 두 가지 변경이 필요했습니다.

1. @dev 제약을 올바른 semver 범위로 교체

{
  "require": {
    "waaseyaa/typed-data": "^0.1",
    "waaseyaa/plugin": "^0.1"
  }
}

경로 저장소는 로컬에서 @dev를 해결하지만, Packagist는 실제 버전 번호를 요구합니다.

2. 미러 저장소를 위한 repositories 항목 추가 (선택 사항)

개발 중에 Composer가 미러를 우선하도록 하려면 다음을 추가할 수 있습니다:

{
  "repositories": [
    {
      "type": "vcs",
      "url": "https://github.com/waaseyaa/entity"
    }
  ]
}

(로컬 테스트에만 필요합니다; 공개된 패키지는 Packagist에서 가져옵니다.)

Result

  • Monorepo는 개발 및 테스트를 위한 단일 진실의 원천으로 남아 있습니다.
  • 43개의 독립 패키지가 이제 Packagist에 게시 가능하며, 각각 고유한 버전 기록을 가지고 있습니다.
  • 사용자는 Composer를 통해 다른 라이브러리와 마찬가지로 waaseyaa 패키지 중 원하는 부분만 설치할 수 있습니다.
  • 릴리스 과정은 GitHub Actions를 통해 완전 자동화되며, 태그 푸시만 하면 됩니다.

Bottom line: splitsh‑lite는 중앙 집중식 개발과 배포 가능한 버전 관리 패키지를 동시에 제공하여 PHP 생태계에 최적의 환경을 제공합니다.

Constraints

^0.1 범위는 “0.1.0부터 시작하는 모든 0.x 릴리스”를 의미합니다.

두 번째로, 모든 composer.json 파일에 Packagist가 요구하는 필드가 포함되어 있는지 확인하십시오:

{
  "name": "waaseyaa/entity",
  "type": "library",
  "description": "Entity system for the Waaseyaa framework",
  "license": "GPL-2.0-or-later",
  "autoload": {
    "psr-4": {
      "Waaseyaa\\Entity\\": "src/"
    }
  }
}

Packagist는 name, description, license가 누락된 패키지를 거부합니다. autoload 블록은 기술적으로 선택 사항이지만 실제로는 필수이며, 이 블록이 없으면 사용자는 패키지를 사용할 수 없습니다.

Source:

입증된 POC

38개의 미러 레포지토리를 만들기 전에, 세 개의 패키지를 이용한 개념 증명(Proof of Concept)이 접근 방식을 검증했습니다: waaseyaa/foundation, waaseyaa/entity, 그리고 waaseyaa/api. 이들은 프레임워크의 레이어 0, 2, 5에 해당합니다. 의존성 체인이 레이어 간에 깔끔하게 해결된다면, 나머지도 문제없이 작동할 것입니다.

테스트는 간단했습니다:

composer require waaseyaa/foundation waaseyaa/entity waaseyaa/api

정상적으로 설치되었습니다. 자동 로딩도 정상 작동했습니다. 의존성 체인이 충돌 없이 해결되었습니다. 이것만으로도 나머지 40개의 미러 레포지토리를 만들고 전체 분할 작업을 진행할 충분한 확신을 얻을 수 있었습니다.

소비자가 보는 내용

소비자 입장에서, waaseyaa는 이제 일반적인 Composer 패키지 세트입니다. 전체 프레임워크를 설치하거나 개별 패키지를 선택하세요:

# Install everything
composer require waaseyaa/framework

# Or pick what you need
composer require waaseyaa/entity waaseyaa/field waaseyaa/access

모노레포 루트는 waaseyaa/framework로 배포되며 모든 하위 패키지를 요구합니다. 개별 패키지는 자체 의존성을 선언하므로 waaseyaa/entity를 설치하면 waaseyaa/typed-datawaaseyaa/plugin이 자동으로 함께 설치되지만 waaseyaa/apiwaaseyaa/admin을 강제로 설치하게 하지는 않습니다.

변함없는 것

이 프로세스에서 중요한 점은 바뀌지 않은 부분이다.

  • 모노레포는 여전히 개발 환경이다.
  • 테스트는 여전히 루트에서 실행된다.
  • CI는 전체 의존성 그래프를 검증한다.
  • 기여자들은 여전히 하나의 레포에 PR을 연다.

분할은 개발 중에는 보이지 않는다. 릴리스 시점에만 의미가 있으며 완전히 자동화되어 있다. 릴리스를 태그하고, 2분을 기다리면 43개의 패키지가 일치하는 버전으로 Packagist에 나타난다.

Source:

전체 그림

이 시리즈는 질문으로 시작되었습니다: 오늘날 AI를 1급 개발 도구로 설계한다면 PHP CMS 프레임워크는 어떻게 보일까?

열 개의 포스트 후, 답은 다음과 같습니다:

  • 7개의 레이어에 걸친 38개의 패키지
  • 3개의 프로덕션 애플리케이션
  • 모든 패키지를 아우른 데이터베이스 마이그레이션
  • 토착 언어를 위한 i18n 설계
  • 인‑메모리 구현을 기반으로 한 테스트 전략
  • 90 분 만에 사이트를 배포하는 인프라스트럭처
  • 엔티티 스키마를 기계가 읽을 수 있게 하는 AI 통합 패키지

Waaseyaa는 Drupal의 최고의 아이디어를 유지하면서 레거시를 버리려는 한 사람의 시도에서 시작되었습니다. 이제는 다음을 구동하는 프레임워크로 성장했습니다:

이처럼 큰 규모를 혼자 구축할 수 있었던 것은 GitHub 이슈를 범위 정의에 활용하고, 아키텍처 일관성을 위한 컨텍스트를 규정화하며, 구현 작업을 기계적으로 수행하는 AI를 결합한 워크플로우 덕분이었습니다.

프레임워크는 오픈 소스이며 활발히 개발 중입니다. Drupal의 콘텐츠 모델링 깊이, Laravel의 개발자 경험, 그리고 처음부터 AI 통합이 필요한 콘텐츠 플랫폼을 구축하고 있다면 waaseyaa 를 주목해 보세요.

이 시리즈를 처음 접한다면, 시작점부터 읽어보세요: Waaseyaa: building a Drupal‑inspired PHP CMS with AI.

0 조회
Back to Blog

관련 글

더 보기 »

조건 없음. 약관 없음

대체 용어 notermsnoconditions.com https://notermsnoconditions.com 이 사이트에 접속하거나 이용함으로써 귀하는 다음 약관을 인정하고 수락합니다. 그들은 a...

터미널용 로그 파일 뷰어

터미널용 로그 파일 뷰어. Merge, tail, search, filter, 그리고 query 로그 파일을 손쉽게 할 수 있습니다. 서버가 필요 없습니다. 설정도 필요 없습니다. 여전히 풍부한 기능을 제공합니다. !Screenshot of lnav https:...