Skip to content

SATySFi v0.1.z のライブラリルートでのファイル配置の設計案

Takashi Suwa edited this page Dec 6, 2022 · 13 revisions

概要

用語の定義

  • パッケージpackage):
    • 実装のリリースに関する単位.
  • レジストリ (registry):
    • パッケージに関するメタデータを集積する場所.
      • このメタデータは,パッケージの各バージョンの実装を取得する方法や,その実装が依存する他のパッケージのバージョン制約などを含む.
    • レジストリはメタデータを記載した何らかのファイル(またはファイル群)を何らかのURLでネットワークに向けて公開しており,ユーザはGitやHTTPSなどのプロトコルによってその内容が適宜取得できる.
    • 基本的には,有志が自身の作成したパッケージを登録できる仕組みがレジストリに備わっている.
      • レジストリの管理者などから何らかのレビューを受けて許可されねばパッケージのメタデータを登録できないものもあれば,勝手に登録できるものもある.
      • レビューが必要な場合もそうでない場合も,パッケージ名は(SNSでのユーザ識別名のように)早い者勝ちで取得する.
    • レジストリ自体も,誰でも新設できる仕組みである場合もあれば,その存在が中央集権で管理されていて許可されねば設立できないものも考えられる.

新しいライブラリルートの構成案

ライブラリルートは以下のような構成に変更してはどうかと考えています(各々の解説は後述):

$LIBROOT/
├── satysfi-library-root.yaml
├── cache/
│   └── locks/
│       ├── foo.0.1.0.tar.gz
│       ├── foo.0.1.1.tar.gz
│       ...
├── registries/
│   ├── 165eecff8485b2d9126196c235804c68/  # github.com/SATySFi/default-registry
│   │   └── satysfi-registry.yaml
│   ├── 1db09faa6db8ce3e6e317e721d09cdf5/  # your-gitab-enterprise.co.jp/project/registry
│   │   └── satysfi-registry.yaml
│   ...
└── packages/
    ├── 165eecff8485b2d9126196c235804c68/
    │   ├── foo/
    │   │   ├── foo.0.1.0/
    │   │   │   ├── satysfi.yaml
    │   │   │   ├── src/
    │   │   │   ...
    │   │   ├── foo.0.1.1/
    │   │   ...
    │   ├── bar/
    │   │   ├── bar.0.0.1/
    │   │   ...
    │   ...
    ├── 1db09faa6db8ce3e6e317e721d09cdf5/
    │   ├── qux/ 
    │   │   ├── qux.1.0.0/
    │   │   ...
    │   ...
    ...

(要加筆)

  • ./satysfi-library-root.yaml
    • ライブラリルート全体に対するメタデータを記録したファイル.
    • 内容の形式は後述.
    • SATySFiは,ライブラリルートとなりうるディレクトリの候補リストから,このファイルが存在する最初のディレクトリをライブラリルートとみなす.したがって,このファイルが削除されると「ここがSATySFiのライブラリルートである」ということをSATySFiが知る方法が失われる.
  • ./cache/
    • ネットワークを介してフェッチしたデータなど,SATySFiが動作していない任意のタイミングで削除しても振舞いに変化をきたさないファイルが配置されるディレクトリ.
    • ./cache/locks/
      • パッケージの各バージョンの実装であるtarballを置いておく場所.
  • ./registries/
    • レジストリに関する情報を置いておくディレクトリ.
      • SATySFiに於けるレジストリはOPAMに於けるregistryの概念と同じ.
    • このディレクトリ以下のファイルを削除すると,どんなレジストリを使用していたかの情報が失われ,レジストリ情報の再設定と再取得が必要になる.
    • ./registries/〈HashValue〉/
      • 1つのレジストリが提供するメタデータが置かれる場所.
      • 〈HashValue〉 は,正規化したレジストリURLの md5sum をとるなどして得た識別用のalphanumericな文字列.
      • satysfi-registry.yaml がそのレジストリに登録されている各パッケージのインストールに必要なメタデータを記録したファイル.典型的にはGitリポジトリの特定のbranchをpullするかHTTPSでフェッチするかによりこのファイルが更新される.
  • ./packages/
    • 取得したパッケージの実装が展開・配置されるディレクトリ.
    • このディレクトリ以下のファイルを削除しても失われる情報はないが,該当ファイルに依存する既存の文書が正常に処理できなくなる.

satysfi-library-root.yaml の内容

(要加筆)

比較: 他言語の既存パッケージマネージャによるレジストリの扱い方

  • OPAM:
    • どのレジストリを使うかはユーザ側がレジストリ名を指定して $(\mathit{registryName} \mapsto \mathit{registryUrl})$ の写像として手元に保持しておく.
      • 😄 ユーザが指定したレジストリ名 $\mathit{registryName}$ はそのマシン全体で使われるが,単に表示やCLIでの指定をわかりやすくするための識別に使うものなので,コンフィグファイルの移植性などには影響しない.
    • ユーザの手元の環境に於いて,パッケージ名は提供元レジストリによらずフラットな名前空間に並ぶ.
    • 異なるレジストリが同一パッケージ名のバージョン違いを提供している場合,それらは同一パッケージの異なるバージョンとみなされる.バージョンも一致した場合は衝突.
      • 😄 独自forkが別レジストリで同名のままリリースできて扱いやすいことがある.
      • 😰 同一のものであるという意図なくパッケージ名がレジストリ間で衝突してしまうことがある.しかもユーザにはどうしようもない.
      • 😰 バージョンの衝突も起こりうる.衝突を防ぐのがレジストリの責務なのかユーザの責務なのかも微妙なところ.
  • Cargo:
    • どのレジストリを使うかはユーザ側がレジストリ名を指定して $(\mathit{registryName} \mapsto \mathit{registryUrl})$ の写像として手元に保持しておく.
      • 具体的には,プロジェクトの .cargo/config.tomlregistries に記載しておく.
      • OPAMと違い,レジストリ名は Cargo.toml 中で依存関係の指定に使われるなど,ビルド処理に関しても意味がある.
        • 😰 レジストリ名 $\mathit{registryName}$ はそれを使うクレートが各々自由に定めており,同一システム内での共通化(つまり node_modules 相当のものをグローバルに1箇所にまとめること)をやるとしたら $\mathit{registryUrl}$ を識別に用いるしかない.
    • ユーザの手元の環境に於いて,パッケージ名は提供元レジストリごとの名前空間に並ぶ.すなわち, $(\mathit{registryName} \mapsto (\mathit{packageName} \mapsto \mathit{packageMetadata}))$ の形で保持されている.
      • したがって,同一パッケージ名の2パッケージも提供元レジストリが違えば別パッケージと扱われる.
      • ただし,1つのレジストリからしか提供されていないパッケージ名は,簡単のためレジストリ名を省略して依存関係を指定できる.
        • 😄 典型的には crates.io のみを使い,ほとんどの場合レジストリ名を書かずに済むので簡便である.
        • 😰 ただし,どれかのレジストリが新たに同名のパッケージを提供し始める可能性があり,そうなるとビルドできなくなるので,将来にわたっての再現性は失われている.
    • 😰 crates.io は誰でもレビューなどを経ずに簡単にパッケージが登録できる上に実質的にフラットな名前空間で扱えるので,(特に有名パッケージに似た名前の)悪意のあるパッケージの登録に脆弱.
  • Go言語:
    • ※Go言語の用語では,正確にはここでいうパッケージは「モジュール」.それより小さい,ややモジュールに近い機構がGo言語では「パッケージ」と呼ばれる.
      • Go 1.11から導入されたmodule-aware modeでは,複数の「パッケージ」をまとめた「モジュール」を認識するようになった.
    • レジストリ相当のものは「パッケージ」の名前の一部に組み込まれている
      • 各「パッケージ」はGitリポジトリやその下に掘られた1つのディレクトリで扱われている.
      • 「パッケージ」の名前は github.com/john-smith/foo/bargopkg.in/yaml.v3 という具合でほぼURLそのものであり,レジストリ相当の接頭辞が含まれている.こうしたURL然とした長い名前をソースコード中でも逐一 require に書く.
        • 😄 何らかの名前を介さず実質的にURLで指定するため,どのレジストリがパッケージを命名しているのかなどを気にする必要がない.また,実装がどこから取得されているのかも明瞭.
        • 😰 バージョン制約も(コンフィグファイルなどではなく)ソースコード中の require に書く仕組みであり,しかも複数のソースファイルが共通のパッケージを使う場合に複数回バージョン制約を書かねばならないので冗長.
        • 標準ライブラリとして提供されている一部の「パッケージ」はレジストリ名の接頭辞などを伴わず fmt などと裸の名前になっている.
  • Elm:
    • レジストリ相当の機構は存在せず,全て package.elm-lang.org で管理されている.
      • パッケージ名は必ず 〈UserName〉/〈PackageName〉 で,標準ライブラリも elm/http などとなっている.
      • 😄 ユーザごとにパッケージ名の名前空間が分かれている上に中央集権なので,悪意のある似た名前のライブラリを提供しにくい.
      • 😰 中央集権であり,勝手に独自のレジストリを立てることはできない.
      • 😰 特定の組織内で閉じたコードベースをレジストリの仕組みに乗せて扱うことなどはできない.

有志の方のご意見

参考文献

  1. Registries - The Cargo Book
  2. Configuration - The Cargo Book
  3. New module changes in Go 1.16 - The Go Programming Language
  4. Go のモジュール管理【バージョン 1.17 改訂版】 - Spiegel | Zenn
Clone this wiki locally