自分の作品がAIの学習に許可なく使用されたかどうかを簡単に確認できるようにするための法案「人工知能ネットワークの透明性と責任(TRAIN)法」がアメリカで提出されました。この法案が実現すれば、著作権者は学習の記録にアクセスできるようになり、自分の作品が使用されたかどうかを確認できるようになるとのことです。
自分の創作物がAI学習に使用されたかどうかを確認できるようになる「AIの透明性と責任に関する法案」がアメリカで提出される - GIGAZINE
TRAIN法は、著作権者が「自分の作品がモデルのトレーニングに使用された」という「誠実な信念(good faith belie)」を宣言できれば、著作権者は生成AIモデルのトレーニング記録の開示を請求できると定めるものです。
自分の創作物がAI学習に使用されたかどうかを確認できるようになる「AIの透明性と責任に関する法案」がアメリカで提出される - GIGAZINE
法案では、AIの開発者が反論するには、著作権者の著作物が使用されたかどうかを特定するのに十分な資料を明らかにするだけでよいとされています。明らかにしない場合、法的には、開発者は著作権で保護された作品を使用していると仮定されます。
自分の創作物がAI学習に使用されたかどうかを確認できるようになる「AIの透明性と責任に関する法案」がアメリカで提出される - GIGAZINE
⇧『著作権者の著作物が使用されたかどうかを特定するのに十分な資料を明らかにするだけでよい』って簡単に言ってくれていますけど、「学習データ」が「ネット上の情報」とかも含んでいる場合、「著作物」が使用されたかどうかの明確な判定は不可能に近い気がするですけど...
「ネット上の情報」に対して、事前に許可申請とか無理な気がするんだが...
エンジニアの負担が増えていく一方ですな...
pygit2とは
公式のGitHubに上がっている情報によると、
Bindings to the libgit2 shared library, implements Git plumbing. Supports Python 3.10 to 3.13 and PyPy3 7.3+
⇧ とあり、「libgit2」というライブラリの「Python」でラッパーしたライブラリということらしい。
「libgit2」のREADMEによると、
libgit2
is a portable, pure C implementation of the Git core methods provided as a linkable library with a solid API, allowing to build Git functionality into your application. Language bindings like Rugged (Ruby), LibGit2Sharp (.NET), pygit2 (Python) and NodeGit (Node) allow you to build Git tooling in your favorite language.
⇧『a portable, pure C implementation of the Git core methods provided as a linkable library with a solid API, allowing to build Git functionality into your application』とあるので、「C言語」に習熟している人向けという気がする。
一応、
A2.2 Appendix B: Embedding Git in your Applications - Libgit2
Libgit2
Another option at your disposal is to use Libgit2. Libgit2 is a dependency-free implementation of Git, with a focus on having a nice API for use within other programs. You can find it at https://libgit2.org.
⇧「Git」の公式のドキュメントで、「libgit2」が紹介されている。
2024年12月8日(日)時点で、「libgit2」のREADMEによると、
Language Bindings
Here are the bindings to libgit2 that are currently available:
- C++
- libqgit2, Qt bindings https://projects.kde.org/projects/playground/libs/libqgit2/repository/
- Chicken Scheme
- chicken-git https://wiki.call-cc.org/egg/git
- D
- Delphi
- GitForDelphi https://github.com/libgit2/GitForDelphi
- libgit2-delphi https://github.com/todaysoftware/libgit2-delphi
- Erlang
- Go
- GObject
- libgit2-glib https://wiki.gnome.org/Projects/Libgit2-glib
- Guile
- Guile-Git https://gitlab.com/guile-git/guile-git
- Haskell
- Java
- Javascript / WebAssembly ( browser and nodejs )
- Julia
- Lua
- .NET
- libgit2sharp https://github.com/libgit2/libgit2sharp
- Node.js
- Objective-C
- objective-git https://github.com/libgit2/objective-git
- OCaml
- Parrot Virtual Machine
- parrot-libgit2 https://github.com/letolabs/parrot-libgit2
- Perl
- Pharo Smalltalk
- libgit2-pharo-bindings https://github.com/pharo-vcs/libgit2-pharo-bindings
- PHP
- Python
- R
- Ruby
- Rust
- Swift
- SwiftGit2 https://github.com/SwiftGit2/SwiftGit2
- Tcl
- Vala
⇧ 28個のプログラミング言語で利用できるようになっている。
実際には、「Delphi」、「Java」、「R」が2つずつライブラリが存在するらしいので、31個のライブラリが用意されていることになるっぽい。
No | プログラミング言語 | ライブラリ名 | URL |
---|---|---|---|
1 | C++ | libqgit2, Qt bindings |
https://projects.kde.org/projects/playground/libs/libqgit2/repository/ |
2 | Chicken Scheme | chicken-git | |
3 | D | dlibgit | |
4 | Delphi | GitForDelphi | |
5 | Delphi | libgit2-delphi | |
6 | Erlang | Geef | |
7 | Go | git2go | |
8 | GObject | libgit2-glib | |
9 | Guile | Guile-Git | |
10 | Haskell | hgit2 | |
11 | Java | Jagged | |
12 | Java | Git24j | |
13 | Javascript / WebAssembly | WASM-git | |
14 | Julia | LibGit2.jl |
https://github.com/JuliaLang/julia/tree/master/stdlib/LibGit2 |
15 | Lua | luagit2 | |
16 | .NET | libgit2sharp | |
17 | Node.js | nodegit | |
18 | Objective-C | objective-git | |
19 | OCaml | ocaml-libgit2 | |
20 | Parrot Virtual Machine | parrot-libgit2 | |
21 | Perl | Git-Raw | |
22 | Pharo Smalltalk | libgit2-pharo-bindings | |
23 | PHP | php-git2 | |
24 | Python | pygit2 | |
25 | R | gert | |
26 | R | git2r | |
27 | Ruby | Rugged | |
28 | Rust | git2-rs | |
29 | Swift | SwiftGit2 | |
30 | Tcl | lg2 | |
31 | Vala | libgit2.vapi |
とは言え、ネット上で上記のライブラリを使ってみた的な情報が少ないところを見るに、あまり知名度は高くないのかもしれない...
話を「pygit2」に戻すと、公式のドキュメントに、
⇧ 対応表が掲載されておりました。
pygit2は結局のところlibgit2に習熟している人向けのライブラリっぽい気はする...
悲報...
⇧ stackoverflowを見た感じ、「pygit2」の公式のドキュメントで利用方法についての説明は無いということで、手軽に利用できるライブラリとは言い難い...
「C言語」に習熟していない人でも利用できるように「Python」でラッパーしたライブラリであるはずの「pygit2」なのだが、肝心の利用方法については、「libgit2」の仕組みについて把握している必要があるという...
まぁ、ネット上に利用してみたの情報がほとんど見当たらないので、何となく嫌な予感はしていたのですが、「pygit2」を利用するには人柱になる必要があるということですな...
とは言え、「Python」初心者であり、「Git」についても習熟しているわけではない我輩にとっては、公式のドキュメントのAPIリファレンスを一から読解するとうな時間も無く、有識者の実装例をお手本としたく、ネットの情報を漁っていたところ、
pygit 2 snippets - most examples assume using a bare repository.
https://gist.github.com/danielmt/81ae37541270714c2605b8f3d574d993
⇧ 上記サイト様が「pygit2」で「bare repository」を扱う実装例を公開して下さっていた、圧倒的感謝。
pygit2でGitのbare repositoryに対してGit操作を行う実装をしてみる
ほぼ、
pygit 2 snippets - most examples assume using a bare repository.
https://gist.github.com/danielmt/81ae37541270714c2605b8f3d574d993
⇧ 上記サイト様の実装を流用させていただき、「private repository」を「git clone --bare」する部分を追加した感じ。
■/home/ts0818/work/app/python/app/src/main/py/api/service/pygit2/pygit2_service.py
"""app/src/main/py/api/service/pygit2/pygit2_service.py.""" import email.utils import logging from urllib.parse import urlparse import pygit2 from src.main.py.enum.pygit2.repo_status import RepoStatus from src.main.py.enum.pygit2.token_type import TokenType class Pygit2Service: """pygit2のAPIを実行するクラス.""" _logger = logging.getLogger(__name__) _repo: pygit2.Repository = None def __init__(self, git_repo_dir_path: str, bare_repo_path: str, access_token: str, token_type: TokenType, username: str) -> None: """初期化.""" self._repo: pygit2.Repository = self._open_git_repo(git_repo_dir_path, bare_repo_path, access_token, token_type, username) @classmethod def git_push(cls, access_token: str, username: str = "x-access-token", refs: tuple[str] = ["refs/heads/main"]) -> bool: """Git push.""" try: cls._logger.info("[start]git_push") # git push callback = pygit2.RemoteCallbacks(pygit2.UserPass(username, access_token)) cls._repo.remotes["origin"].push(refs, callback) cls._logger.info("Success git push") return True except Exception: cls._logger.exception("Error: git_push") return False finally: cls._logger.info("[finish]git_push") @classmethod def git_commit_with_add_blob(cls, file_name: str, file_path: str, username: str, useremail: str, reference_name: str, commit_message: str) -> bool: """ファイルを追加してコミットする.""" try: cls._logger.info("[start]git_commit_with_add_blob") # Gitリポジトリが、新規か既存かチェックする repo_status: RepoStatus = cls._check_git_repo_for_new_or_exists() if repo_status is RepoStatus.NEW: cls._logger.info("new local git bare repository") elif repo_status is RepoStatus.EXISTING: cls._logger.info("existing local git bare repository") # blobを作成する if not cls._git_add_blob_from_file(file_path): cls._logger.warning("Failed: _git_add_blob_from_file") return False # blobを追加する if not cls._git_add_tree_from_blob(file_name): cls._logger.warning("Failed: _git_add_tree_from_blob") return False # indexに追加する if not cls._git_add_index(): cls._logger.warning("Failed: _git_add_index") return False # commitするauthorを設定する if not cls._git_config_commit_auther(username, useremail): cls._logger.warning("Failed: _git_config_commit_auther") return False # commitする if not cls._git_commit(reference_name, commit_message): cls._logger.warning("Failed: _git_commit") return False cls._logger.info("Success git commit") return True except Exception: cls._logger.exception("Error: git_commit_with_add_blob") finally: cls._logger.info("[finish]git_commit_with_add_blob") # private method def _open_git_repo(self, git_repo_dir_path: str, bare_repo_path: str, access_token: str, token_type: TokenType, username: str = "x-access-token") -> pygit2.Repository: """Gitリポジトリを扱えるようにする.""" local_repository_path: str = pygit2.discover_repository(git_repo_dir_path) if local_repository_path is None: if not self._git_clone_bare(git_repo_dir_path, bare_repo_path, access_token, token_type, username): self._logger.warning("Failed _open_git_repo") return pygit2.Repository(local_repository_path) def _git_clone_bare(self, git_repo_dir_path: str, bare_repo_path: str, access_token: str, token_type: TokenType, username: str = "x-access-token") -> bool: """Git clone --bare.""" try: parsed_url = urlparse(bare_repo_path) clone_url: str = f"https://{username}:{access_token}@{parsed_url.netloc}{parsed_url.path}.git" # git clone --bare by auth Personal Access Token(PAT) if token_type == TokenType.GITHUB_PERSONAL_ACCESS_TOKEN: self._logger.info("git clone --bare by auth personal access token") # git clone --bare by auth GitHub App Installation Token if token_type == TokenType.GITHUB_APP_INSTALLATION_TOKEN: self._logger.info("git clone --bare by auth GitHub App Installation Token") self._logger.info("git clone url: %s", clone_url) callbacks: pygit2.RemoteCallbacks = pygit2.RemoteCallbacks(pygit2.UserPass(username, access_token)) self._repo = pygit2.clone_repository(url=bare_repo_path, path=git_repo_dir_path, bare=True, callbacks=callbacks) return self._repo is not None except Exception: self._logger.exception("Error: _git_clone_bare") return False # private method def _check_git_repo_for_new_or_exists(self) -> RepoStatus: """Gitリポジトリが新規かどうかチェックする.""" if self._repo.head_is_unborn: self._tree = self._repo.TreeBuilder() self._parent = [] return RepoStatus.NEW self._tree = self._repo.TreeBuilder(self._repo.head.peel().tree.id) self._parent = [self._repo.head.target] return RepoStatus.EXISTING def _git_add_blob_from_file(self, file_path: str) -> bool: """ファイルを元にblobを作成する.""" try: self._blob_id: pygit2.Oid = self._repo.create_blob_fromdisk(file_path) return self._blob_id is not None except Exception: self._logger.exception("Error: _git_add_blob_from_file") return False # private method def _git_add_blob_from_data(self, file_contents: bytes) -> bool: """ファイルの内容を元にblobを作成する.""" try: self._blob_id: pygit2.Oid = self._repo.create_blob(file_contents) return self._blob_id is not None except Exception: self._logger.exception("Error: _git_add_blob_from_data") return False # private method def _git_add_tree_from_blob(self, file_name: str) -> bool: """blobをtreeに追加する.""" try: self._tree.insert(file_name, self._blob_id, pygit2.GIT_FILEMODE_BLOB) self._commit_tree: pygit2.Oid = self._tree.write() return self._commit_tree is not None except Exception: self._logger.exception("Error: _git_add_tree_from_blob") return False # private method def _git_add_index(self) -> bool: """indexに追加する.""" try: self._repo.index.write() return True except Exception: self._logger.exception("Error: _git_add_index") return False def _git_config_commit_auther(self, username: str, useremail: str) -> bool: """commitのautherを設定する.""" name: str addr: str name, addr = email.utils.parseaddr(useremail) if not email.utils.validate_email(addr): self._logger.warning("Warning: Email format invalid. before parse: %s . after parse: %s %s", useremail, name, addr) return False self._author = pygit2.Signature(username, useremail) return self._author is not None # private method def _git_commit(self, reference_name: str, commit_message: str) -> bool: """Git commit.""" try: self._commit_hash = self._repo.create_commit(reference_name, self._author, self._author, commit_message, self._tree, self._parent) return self._commit_hash is not None except Exception: self._logger.exception("Error: _git_commit") return False
⇧ のような感じの実装になりました。
「pygit2」での「bare repository」に対する実装を調べるのに疲弊して、動作確認はできていません...
本当は、「Ruby」製のライブラリ「Oxidized」を利用している環境なので、「Oxidized」の内部で「Rugged」という「libgit2」の「Ruby」ラッパーによって、「git commit」部分までは済んでいる状態なので、「git push」部分だけ実現できれば良いのだけど。
とりあえず、「pygit2」について、「bare repository」に対する処理のAPIは、どれを利用すれば良いかについては公式のドキュメントに実装例を載せて欲しいですな...
毎度モヤモヤ感が半端ない…
今回はこのへんで。