20200808のGitに関する記事は5件です。

.gitディレクトリを探索(objects編)

普段は覗かない.gitディレクトリ。どういう構造になっているのか、そしてどうやってバージョンを管理しているのか、確認してみました。動きを追っていくと、gitの仕組みが見えてきます。今回は、init~commitまでにobjectsディレクトリがどう変化するかを見てみます。

まずはinit

まずはinitします。

$ git init
Initialized empty Git repository in D:/gittest/.git/
$ tree .git
.git
|-- HEAD
|-- config
|-- description
|-- hooks
|   |-- applypatch-msg.sample
|   |-- commit-msg.sample
|   |-- fsmonitor-watchman.sample
|   |-- post-update.sample
|   |-- pre-applypatch.sample
|   |-- pre-commit.sample
|   |-- pre-merge-commit.sample
|   |-- pre-push.sample
|   |-- pre-rebase.sample
|   |-- pre-receive.sample
|   |-- prepare-commit-msg.sample
|   `-- update.sample
|-- info
|   `-- exclude
|-- objects
|   |-- info
|   `-- pack
`-- refs
    |-- heads
    `-- tags

8 directories, 16 files

initすると、8つのディレクトリと16のファイルが生成されました。objectsの下にはinfo/とpack/のみ存在しています。以降は.git/objects/のみを対象とします。

ファイル作成

2つのファイルを作成して、.gitフォルダの変化を確認します。

$ echo "hello qiita" > file1.txt
$ mkdir dir
$ echo "hello git" > dir/file2.txt

当然、これらのファイルはgit管理下にはないので、.git/objects/に変化はありません。

$ tree .git/objects/
.git/objects/
|-- info
`-- pack

2 directories, 0 files

git add

次にgit addします。

$ git add *
$ tree .git/objects/
.git/objects/
|-- 8d
|   `-- 0e41234f24b6da002d962a26c2495ea16a425f
|-- 92
|   `-- ca5c8297eb1e13c6039ae6ad11dc03f89057ed
|-- info
`-- pack

4 directories, 2 files

addすると、objects/の下に2つのディレクトリ、その下に各1つのファイルが生成されました。git addしたファイルのハッシュ値を確認すると、その正体が分かります。

$ git hash-object file1.txt
92ca5c8297eb1e13c6039ae6ad11dc03f89057ed

$ git hash-object dir/file2.txt
8d0e41234f24b6da002d962a26c2495ea16a425f

生成されたファイルの中身のハッシュ値を確認すると、先頭2桁をディレクトリ名とし、残り38桁をファイル名としていることが分かりました。objects/直下に大量のファイルが生成され、読み取り速度が低下することを防ぐために、このような設計になっているようです。

次に、git cat-fileコマンドでオブジェクトのタイプと中身を確認します。(なぜか、git cat-file -t 92/ca5c8297eb1e13c6039ae6ad11dc03f89057edとすると、エラーになる)

$ cd .git/objects/
$ git cat-file -t 92ca5c8297eb1e13c6039ae6ad11dc03f89057ed
blob
$ git cat-file -p 92ca5c8297eb1e13c6039ae6ad11dc03f89057ed
hello qiita

$ git cat-file -t 8d0e41234f24b6da002d962a26c2495ea16a425f
blob
$ git cat-file -p 8d0e41234f24b6da002d962a26c2495ea16a425f
hello git

どちらもgit addしたファイルのBlobオブジェクトです。つまり、git addされたファイル(の内容)は、objects/にBlobオブジェクトとして格納されます。(圧縮されているため、catでは内容を確認できない)

commit

commitします。どのように.git/objects/が変化するのでしょうか?

$ git commit -m "commit 1"
[master (root-commit) 98664b3] commit 1
 2 files changed, 2 insertions(+)
 create mode 100644 dir/file2.txt
 create mode 100644 file1.txt

$ tree .git/objects/
.git/objects/
|-- 46
|   `-- 660d479ecbb352fe6139074d4cca2ec9ac2f31
|-- 8d
|   `-- 0e41234f24b6da002d962a26c2495ea16a425f
|-- 92
|   `-- ca5c8297eb1e13c6039ae6ad11dc03f89057ed
|-- 98
|   `-- 664b3fad7883101dafcb8f4891c0f7cc9f4798
|-- f9
|   `-- 68a777a91646758326fe23b06633280db6bc02
|-- info
`-- pack

7 directories, 5 files

46/、98/、f9/が追加されていることが分かります。git cat-fileでそれぞれのオブジェクトタイプと内容を確認します。

$ git cat-file -t 46660d479ecbb352fe6139074d4cca2ec9ac2f31
tree
$ git cat-file -p 46660d479ecbb352fe6139074d4cca2ec9ac2f31
040000 tree f968a777a91646758326fe23b06633280db6bc02    dir
100644 blob 92ca5c8297eb1e13c6039ae6ad11dc03f89057ed    file1.txt

$ git cat-file -t 98664b3fad7883101dafcb8f4891c0f7cc9f4798
commit
$ git cat-file -p 98664b3fad7883101dafcb8f4891c0f7cc9f4798
tree 46660d479ecbb352fe6139074d4cca2ec9ac2f31
author XXXX <XXXX@gmail.com> 1596887068 +0900
committer XXXX <XXXX@gmail.com> 1596887068 +0900

commit 1

$ git cat-file -t f968a777a91646758326fe23b06633280db6bc02
tree
$ git cat-file -p f968a777a91646758326fe23b06633280db6bc02
100644 blob 8d0e41234f24b6da002d962a26c2495ea16a425f    file2.txt

それぞれ、1つのcommitオブジェクトと2つのtreeオブジェクトです。
commitオブジェクトはcommitの内容、およびコミットした対象(treeオブジェクト)が書かれています。
一方、treeオブジェクトはファイルのディレクトリを表しています。

生成されたオブジェクトを図示するとこんな感じ。
tree1.gif

再度commit

ファイルを変更して、再度commitしてみます。

ファイル更新

$ echo "hello qiita again" >> file1.txt
$ tree .git/objects/
.git/objects/
|-- 46
|   `-- 660d479ecbb352fe6139074d4cca2ec9ac2f31
|-- 8d
|   `-- 0e41234f24b6da002d962a26c2495ea16a425f
|-- 92
|   `-- ca5c8297eb1e13c6039ae6ad11dc03f89057ed
|-- 98
|   `-- 664b3fad7883101dafcb8f4891c0f7cc9f4798
|-- f9
|   `-- 68a777a91646758326fe23b06633280db6bc02
|-- info
`-- pack

7 directories, 5 files

この時点では、.gitフォルダには変化はありません。オブジェクトは、addして初めて生成されるためです。

git add

$ git add file1.txt
$ tree .git/objects/
.git/objects/
|-- 23
|   `-- ce4645d730951533cb1b75c03cd7e8b5d9e071
|-- 46
|   `-- 660d479ecbb352fe6139074d4cca2ec9ac2f31
|-- 8d
|   `-- 0e41234f24b6da002d962a26c2495ea16a425f
|-- 92
|   `-- ca5c8297eb1e13c6039ae6ad11dc03f89057ed
|-- 98
|   `-- 664b3fad7883101dafcb8f4891c0f7cc9f4798
|-- f9
|   `-- 68a777a91646758326fe23b06633280db6bc02
|-- info
`-- pack

8 directories, 6 files

$ git cat-file -t 23ce4645d730951533cb1b75c03cd7e8b5d9e071
blob
$ git cat-file -p 23ce4645d730951533cb1b75c03cd7e8b5d9e071
hello qiita
hello qiita again

新しいBlobオブジェクトが生成されました。ファイルを更新してaddするたびに、Blobオブジェクトが新規に作成されるようです。

commit

次にcommitします。

$ git commit -m "commit 2"
[master e2e5b8a] commit 2
 1 file changed, 1 insertion(+)

$ tree .git/objects/
.git/objects/
|-- 23
|   `-- ce4645d730951533cb1b75c03cd7e8b5d9e071
|-- 36
|   `-- b5886d72132ff023c27b2ca783eba3f3759b19
|-- 46
|   `-- 660d479ecbb352fe6139074d4cca2ec9ac2f31
|-- 8d
|   `-- 0e41234f24b6da002d962a26c2495ea16a425f
|-- 92
|   `-- ca5c8297eb1e13c6039ae6ad11dc03f89057ed
|-- 98
|   `-- 664b3fad7883101dafcb8f4891c0f7cc9f4798
|-- e2
|   `-- e5b8a03da8735003da7e78cefbc84d45e524ce
|-- f9
|   `-- 68a777a91646758326fe23b06633280db6bc02
|-- info
`-- pack

10 directories, 8 files

36/、e2/ディレクトリが追加されました。これまでの流れから、これらはtreeオブジェクトとcommitオブジェクトのはずです。確認してみましょう。

$ git cat-file -t 36b5886d72132ff023c27b2ca783eba3f3759b19
tree

$ git cat-file -p 36b5886d72132ff023c27b2ca783eba3f3759b19
040000 tree f968a777a91646758326fe23b06633280db6bc02    dir
100644 blob 23ce4645d730951533cb1b75c03cd7e8b5d9e071    file1.txt

「23ce46」は、今回のcommitで新しく生成されたBlobオブジェクトです。

$ git cat-file -t e2e5b8a03da8735003da7e78cefbc84d45e524ce
commit

$ git cat-file -p e2e5b8a03da8735003da7e78cefbc84d45e524ce
tree 36b5886d72132ff023c27b2ca783eba3f3759b19
parent 98664b3fad7883101dafcb8f4891c0f7cc9f4798
author XXXX <XXXX@gmail.com> 1596887500 +0900
committer XXXX <XXXX@gmail.com> 1596887500 +0900

commit 2

parentが98664bとなっており、これは1回目のcommitのオブジェクトです。
オブジェクトを図示すると、下記の様になります。
tree2.gif

まとめ

  • initすると、.gitディレクトリおよびいくつかのサブディレクトリとファイルが作成される。
  • ファイルをaddすると、.git/objects/にファイルの内容のハッシュ値に基づいてディレクトリとファイル(Blobオブジェクト)が生成される。ディレクトリ名はハッシュ値の先頭2桁、ファイル名は残り38桁となる。
  • commitするたびにcommitオブジェクトとtreeオブジェクトが生成される。
  • commitオブジェクトはコミットの内容を表す。「parent」に親コミットのオブジェクトが記載され、コミットの親子関係を把握することができる。「tree」はコミット対象のtreeオブジェクトを表す。
  • treeオブジェクトはBlobオブジェクトまたは他のtreeオブジェクトを指しており、ディレクトリを表現している。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コピペでつくるGitサーバーとCI/CD環境(Gitea+Jenkins)

はじめに

「いまあるリポジトリからブランチきって進めてくれればいいよ」
よくある会話なんですよね?これって。

やりかたが分からずコマンドレベルで教えてもらいました。ずびばぜん。。。
ナウでヤングな開発をしたかったのでGitのべんきょうをしようとおもいました。

ついでにテストとかデプロイとかの自動化をしたかったので、環境を構築してみました。
手段の目的化ばんざい!
べろべろーとコピペすると、VPSやらオンプレ環境でGitサーバ+CIツールが使えるようになります。

このエントリを読むとできること

以下のような処理を自動的に行えるようになる。
image.png

  1. orginizationという組織のrepo-Aリポジトリにpushすると、、、
  2. GiteaからSlackおよびJenkinsのwebhookをたたく
  3. Jenkinsではrepo-Aにpushがあったという通知をGiteaプラグインが受ける
  4. pushされたrepo-AにJenkinsfileがあるかチェックする
  5. Jenkinsfileがあればそれをもとにpipeline処理を行う
  6. pipeline処理の結果を判別し
  7. JenkinsからSlackのwebhookをたたく

ということができる。

「CI/CDする」までは解説できていませんので、「CI/CDができる環境を作る」です(よね?)
この環境を構築したうえで、ちゃんとテスト自動化できるようなコードを書いたりpipeline処理を書いたり、デプロイを自動化するための処理を書いたりすることで、「CI/CDする」んだと思います。

本エントリの解説では、組織単位で発生する全イベントでwebhookをたたくようになっているので、

  • リポジトリごとに設定したり
  • ブランチ毎に設定したり
  • マージされた場合だけという設定をしたり

とやっていくと、必要なことができるようになるんではないかとおもいます。

あと、pipeline処理の結果はマークダウン形式で常時確認できるので、リポジトリのREADME.mdに書いておけば、そのリポジトリ(のブランチ)の状態がわかるようになる。

こんな感じで。
image.png

対象機器および環境

検証環境

  • CentOS8 (8.2.2004)
  • Gitea (1.13.0+dev-389-ge17e3f71f)←記事作成時点のMasterブランチものです
  • Jenkins (2.251)

構築パラメータ

私用環境で検証しています。
FQDNやらポート番号やらは、必要に応じて修正してください。
(あとで紹介するコピペコマンドを修正する必要があります)

項目 Gitリポジトリマネージャ CI/CDツール
Software Gitea Jenkins
Path /opt/gitea /opt/jenkins
Port 3000 8080
User gitea gitea
FQDN(A) vmnetserv03.prosper2.net vmnetserv03.prosper2.net
FQDN(CNAME) gitea.prosper2.net jenkins.prosper2.net

また、GiteaとJenkinsを動作させるために、以下のユーザを新規作成します。
適当なユーザがいれば、それを利用してもOKですが、シェルが必要なので、nobodyなどの /sbin/nologin/bin/false ではダメです。

項目
ユーザ名 gitea
グループ名 gitea
Shell /bin/bash
Home /opt/gitea

注意事項

すべてHTTPでつくっています。(GitはSSHですけど)
HTTPSでやるなら証明書発行したり、Nginxでsslをかぶせたりしてください。
プライベートPKIを利用するなら、JenkinsがJAVAなのでkeytoolでルートとか中間のCAをトラストストアに追加するか、チェーン済み証明書をNginxに配置するなど工夫してください。

作業内容

CentOS8の準備

CentOS8をクリーンインストールしておく。(最小構成でOK)
ESXi6.7にCentOS8を最小構成で構築

サーバ構築

さぁ、コピペの時間だ。

あ、コピペコマンドが長くなるのがイヤだったので、 sudo してません。
すべて root で作業してください。

事前準備

  • タイムゾーンの修正
  • SELinuxを無効
  • 動作に必要なソフトウェアのインストール
  • nginxのサービス登録と起動
  • Giteaユーザの追加
timedatectl set-timezone Asia/Tokyo
sed -i -e 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
shutdown -r now

firewall-cmd --add-service=http --zone=public --permanent
firewall-cmd --reload
dnf install -y nginx
systemctl enable nginx
systemctl start nginx
useradd --user-group --shell /bin/bash --home-dir /opt/gitea gitea

Giteaの構築

  • gitのインストール
  • 必要なディレクトリを作成
  • Giteaのバイナリを取得
  • GiteaのServiceファイルを作成し、サービス登録と起動
  • Nginx設定ファイルを作成し、サービス再起動
dnf install -y git
mkdir -p /opt/gitea/{gitea-repositories,data,custom,log}
curl -L https://dl.gitea.io/gitea/master/gitea-master-linux-amd64 -o /opt/gitea/gitea
chmod 755 /opt/gitea/gitea
chown -R gitea.gitea /opt/gitea

cat <<EOF > /usr/lib/systemd/system/gitea.service
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target

[Service]
RestartSec=2s
Type=simple
User=gitea
Group=gitea
WorkingDirectory=/opt/gitea/
ExecStart=/opt/gitea/gitea web --config /opt/gitea/custom/conf/app.ini -p 3000
Restart=always
Environment=USER=gitea HOME=/opt/gitea GITEA_WORK_DIR=/opt/gitea

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl start gitea
systemctl enable gitea


cat <<'EOF' > /etc/nginx/conf.d/gitea.conf
server {
    listen 80;
    server_name gitea.prosper2.net;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}
EOF
systemctl restart nginx

Jenkinsの構築

  • javaのインストール
  • 必要なディレクトリを作成
  • Jenkinsのwarファイルを取得し展開
  • JenkinsのServiceファイルを作成し、サービス登録と起動
  • Nginx設定ファイルを作成し、サービス再起動
dnf install -y java-11-openjdk-devel java-11-openjdk

mkdir -p /opt/jenkins/{data,web} /var/log/jenkins
curl -L https://updates.jenkins-ci.org/latest/jenkins.war -o /opt/jenkins/jenkins.war
cd /opt/jenkins/web
jar -xf /opt/jenkins/jenkins.war
chown -R gitea.gitea /opt/jenkins /var/log/jenkins

cat <<EOF > /usr/lib/systemd/system/jenkins.service
[Unit]
Description=Jenkins Automation Server
After=syslog.target network.target

[Service]
Type=simple
ExecStart=/usr/bin/java -jar /opt/jenkins/jenkins.war --webroot=/opt/jenkins/web --httpPort=8080 --logfile=/var/log/jenkins/jenkins.log
User=gitea
Restart=always
RestartSec=300s
Environment="JENKINS_HOME=/opt/jenkins/data"

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl start jenkins
systemctl enable jenkins

cat <<'EOF' > /etc/nginx/conf.d/jenkins.conf
server {
    listen 80;
    server_name jenkins.prosper2.net;

    location / {
      proxy_set_header        Host $host:$server_port;
      proxy_set_header        X-Real-IP $remote_addr;
      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header        X-Forwarded-Proto $scheme;

      # Fix the "It appears that your reverse proxy set up is broken" error.
      proxy_pass          http://127.0.0.1:8080;
      proxy_read_timeout  90;

      proxy_redirect      http://127.0.0.1:8080 jenkins.prosper2.net;

      # Required for new HTTP-based CLI
      proxy_http_version 1.1;
      proxy_request_buffering off;
      # workaround for https://issues.jenkins-ci.org/browse/JENKINS-45651
      add_header 'X-SSH-Endpoint' 'jenkins.prosper2.net:50022' always;
    }
  }
EOF
systemctl restart nginx

各サーバの初期設定

Giteaのセットアップとユーザ追加

Giteaに設定したFQDNへブラウザでHTTPでアクセスする(今回の例では http://gitea.prosper2.net/

登録アイコンをクリック
image.png

設定していく
image.png

以下のように変更して、画面下部の「Giteaをインストール」をクリック

項目
データベースのタイプ SQLite3
SSHサーバーのドメイン gitea.prosper2.net
GiteaのベースURL http://gitea.prosper2.net/

インストール後はこんな画面なので、
「アカウントが必要ですか? 今すぐ登録しましょう。」をクリック
image.png

ユーザ名 gitadmin を登録する
image.png

こうなればとりあえずOK
image.png

組織を作成するので、画面右上の「+」のアイコンから「新しい組織」を選択して、、
image.png

test_org として組織を作成する。
image.png

Giteaの設定ファイルのチューニング

Giteaの設定ファイルは /opt/gitea/custom/conf/app.ini にあります。

ここに以下を追加することで、リポジトリ内のファイルやIssueの検索ができるようになります。

/opt/gitea/custom/conf/app.ini
----8<----(snip)----8<----
[indexer]
ISSUE_INDEXER_TYPE   = bleve
ISSUE_INDEXER_PATH   = indexers/issues.bleve
REPO_INDEXER_ENABLED = true
REPO_INDEXER_PATH    = indexer/repos.bleve
UPDATE_BUFFER_LEN    = 20
MAX_FILE_SIZE        = 1048576
----8<----(snip)----8<----

ここに以下を追加することで、OpenID利用の停止と、ユーザが自由に登録することができなくなります。
(つまり、ユーザを追加するには、最初に作成したユーザで追加するようになります)

/opt/gitea/custom/conf/app.ini
以下を変更する
----8<----(snip)----8<----
[openid]
;ENABLE_OPENID_SIGNIN = true
;ENABLE_OPENID_SIGNUP = true
ENABLE_OPENID_SIGNIN = false
ENABLE_OPENID_SIGNUP = false

[service]
;DISABLE_REGISTRATION = false
DISABLE_REGISTRATION = true
----8<----(snip)----8<----

こうしてからGiteaを再起動します。

systemctl restart gitea

Giteaに公開鍵を登録

もし自分の秘密鍵を生成していない場合には、ssh-keygen -t rsa -b 4096などで生成しておく。

# cat ~/.ssh/id_rsa.pub
ssh-rsa AAAA(うんちゃらかんちゃら)7iQ== root@vmnetserv01.prosper2.net

これをコピーしておく。

SSH鍵の登録は、画面右上のアイコンから「設定」を選択して、、、
image.png

上部メニュのSSH/GPGキーから「キーを追加」を選択して、、、
image.png

ここに先ほどの公開鍵をはりつけて「キーを追加」をクリック
image.png

これでキーが追加できた。
image.png

GiteaにSlackのwebhookを登録する(組織単位)

ここでは特定の組織に対して共通のwebhookを設定します。

Slack投稿用のチャネルを作成する
image.png

適当に入力して「作成」
image.png

Slackアプリ管理画面( https://api.slack.com/apps )の「Create New App」をクリック
image.png

アプリ名と自分のワークスペースを選択して「Create App」する
image.png

Basic Information → Building Apps for Slack → Add features and functionality → Incoming Webhooks
を選択
image.png

作成したては「Off」になっているので、これを「On」にする
image.png

スクロールして下部の「Add New Webhook to Workspace」をクリック
image.png

先ほど作成したチャネルを指定して「許可する」をクリック
image.png

Slackアプリから「App」を選択して、giteaがいるか確認する
image.png

以下のように追加されたら、curlコマンドをコピペしてターミナルから発行する。
image.png

通知がくれば成功
image.png

組織のwebhookを設定するので、先ほど作成して「test_org」を表示させ、組織名の横にある歯車マークをクリック
image.png

設定メニュの「webhook」から「webhookを追加」の「Slack」をクリック
image.png

ターゲットURLに先ほど作成したSlackのURL(curlじゃないほう)を、チャネルにSlackアプリを追加したチャネル(#gitea)を設定し、最下部の「有効」にチェックをいれ、「webhookを追加」します。
(テストなので、すべてのイベント、すべてのブランチとしています)
image.png

これで、この組織に関連するイベントはすべてSlackに投稿されることになります。
ためしにリポジトリを作成するので、画面右上のプラスマークから「新しいリポジトリ」を選択
image.png

オーナーを「test_org」リポジトリ名を「sample」として作成する。(それ以外は空欄でOK)
image.png

すると、おなじみの指示が表示される
image.png

同時にSlackにも通知がくる。
image.png

なんか通知が2通きてますね。
image.png

これのような気もしますが、closeしてるしなぁ。。。
https://github.com/go-gitea/gitea/issues/9183

GiteaにJenkinsのwebhookを登録する

GiteaのイベントをJenkinsに通知するためのwebhookを設定します。Slackのwebhookとだいたい同じです。

組織の設定メニュからwebhookを追加します(webhookの種別は「gitea」を選択します。)
image.png

ターゲットURLは http://jenkins.prosper2.net/gitea-webhook/post のように設定し、最下部の「有効」にチェックをいれ、「webhookを追加」します。
(テストなので、すべてのイベント、すべてのブランチとしています)
image.png

Jenkinsのセットアップとユーザ追加

Jenkinsに設定したFQDNへブラウザでHTTPでアクセスする(今回の例では http://jenkins.prosper2.net/

こんな画面になっている
image.png

初期パスワードはJenkinsをインストールしたサーバにあるので、これを入力

# cat /opt/jenkins/data/secrets/initialAdminPassword
b2546555f9184c4a8a28266b4cac7628

インターネット接続環境があれば、「Install Suggested plugins」を選ぶと勝手によさげなものをダウンロードしてくれる。
image.png

プラグインのインストール完了を待つ。
image.png

ユーザ名 gitadmin を登録する
image.png

このままでOK
image.png

「Start Using Jenkins」をクリックすると完了
image.png

こうなってればOK
image.png

Jenkinsにプラグインを追加

ひとまず、以下のプラグインを導入しておく

  • Gitea
  • Global Slack Notifier
  • Embeddable Build Status(ビルド状態のバッヂを追加する)

サイドメニュの「Jenkinsの管理」の「プラグインの管理」をクリック
image.png

「利用可能」タブを選択してプラグインを検索してインストールする。
image.png

こんな感じで Global Slack Notifier と Embeddable Build Status もインストールしておく。

Jenkinsのslackアプリを追加

Slackアプリ管理画面( https://api.slack.com/apps )の画面上部の検索窓に「jenkins」と入力して検索
image.png

「Jenkins CI」の部分がリンクになっているので、これをクリック
image.png

「Slackに追加」をクリック
image.png

投稿先は先ほど作成した #gitea とし、「Jenkins CI インテグレーションの追加」をして、画面に従って設定していく。
image.png

Step3にある、「チームサブドメイン」と「インテグレーション用トークン認証情報 ID」があとで必要なので、控えておく
image.png

「Jenkinsの設定」→「システムの設定」の最下部にSlackがあるので、workspaceとチャネルを入力
(workspaceは先ほどの「チームサブドメイン」もしくは https://[workspace].slack.com/ のworkspaceの部分です)
image.png

クレデンシャル情報を追加するために、「追加」→「jenkins」を選択し、
image.png

「種類」を「Secret Text」にして、先ほどの「インテグレーション用トークン認証情報 ID」として登録する。
image.png

もとの設定画面でクレデンシャル情報を選択して、TestConnectionをクリックする。(成功するとSuccessと表示される)
image.png

TestConnectionが成功すると、Slackにはこんな通知がくる。
image.png

テストのため、ビルド結果のすべてをSlack通知するため、「Jenkinsの設定」→「システムの設定」の中央付近にGlobal Slack Messagesがのすべての欄にチェックをいれる。
image.png

Jenkinsのslackアプリが追加できない場合

なんらかの理由でSlackアプリが導入できない場合には、GiteaのSlack設定したときと同じような、
https://hooks.slack.com/services/(~~なんだかながいやつ~~) を利用する。

クレデンシャル情報を追加する。
image.png

「種類」を「Secret Text」にして、https://hooks.slack.com/services/(~~なんだかながいやつ~~)(~~なんだかながいやつ~~) を登録する。
(スラッシュが入ったディレクトリ構造になってても、そっくりそのまま全部入れる。)
image.png

「高度な設定」をクリックして展開する。
「Override url」に https://hooks.slack.com/services/(~~なんだかながいやつ~~)https://hooks.slack.com/services/ を入力する。
もとの設定画面でクレデンシャル情報を選択して、TestConnectionをクリックする。(成功するとSuccessと表示される)
image.png

Slackの通知はこんなかんじ
image.png

Gitea→Slackの通知とまざってよくわからん状態ですが、緑の部分がJenkins→Slack通知のビルド成功、赤の部分がJenkins→Slack通知のビルド失敗です。
Slackアプリでなくても動作できてますね。
image.png

動作確認してみよう

README.mdをpushする

先ほど作成した test_org 配下の sample リポジトリに対して、以下のようにREADME.mdを作成してcommit -> pushする
Giteaではmermaid.jsが利用できるので、この書式を試してみます。

mkdir -p ~/gitea/sample
cd ~/gitea/sample

git init

cat <<'EOF' > README.md
# Markdown Rendering

## mermaid.js

```mermaid
graph TD;
    A(stuff)-->B[one];
    A-->C[two];
    A-->D[three];
```

EOF

git add README.md
git commit -m "first commit"
git remote add origin gitea@gitea.prosper2.net:test_org/sample.git
git push -u origin master

すると、Slackに通知がくる(また2通きてる。。。)
image.png

webUIからみると、以下のようにみえるはず。
image.png

リポジトリのpushを検知してJenkinsで自動ビルドさせる

JenkinsがGiteaにアクセスできるようにGiteaでトークンを発行しておく。
右上メニュの「設定」をクリックして、、、
image.png

上部メニュの「アプリケーション」でトークン名を入力して、「トークンを生成」する。
やべぇトークンがゲシュタルト崩壊してきた。
image.png

生成されたトークンをひかえておく
image.png

JenkinsにGiteaサーバを登録するので、「Jenkinsの管理」から「システムの設定」を選択
image.png

システム設定の中央付近にある「Gitea Servers」にURLを入力し、設定を保存する。
(きちんと認識されれば、Giteaのバージョン名が表示される。)
image.png

新規ジョブを作成する
image.png

「Enter an item name」に適当な名称を入力し、「Gitea Organization」を選択してOKをクリック
image.png

上部メニュの「Projects」→「Gitea Organization」→「Creadential」→「追加」から先ほど作成したプロジェクト名を選択する
image.png

トークン入力画面が表示されるので、「種類」を「Gitea Personal Access Token」にして、先ほどGiteaで生成したトークンを「追加」する
image.png

Credentialとして追加したトークンを選択し、「Owner」には対象の組織名を入力する。で、保存。
image.png

すると勝手に組織内のリポジトリを走査してくれる。
現時点ではsampleというリポジトリが見つかり、そこにJenkinsfileがない、と言われている。
image.png

なので、このリポジトリにJenkinsfileをpushしてやればよい。

cd ~/gitea/sample

cat <<'EOF' > Jenkinsfile
pipeline {
  agent any 
  stages {
    stage('First Stage') { 
      steps {
        echo "Build pipeline : First Stage"
      }
    }
  }
}
EOF

git add Jenkinsfile
git commit -m "add Jenkinsfile"
git push -u origin master

すると、Slackに通知がくる
通知はGiteaにpushされたトリガーでGitea→Slackのwebhookがひとつ(のはずだが、また2通きてる。。。)
もうひとつは、GiteaにpushされたトリガーでGitea→Jenkinsのwebhookを叩いた結果、Jenkinsfileが見つかったので、自動的にビルドされ、その結果をJenkins→Slackのwebhookで通知したもの。
image.png

Jenkinsのビルド履歴にビルドされたログが表示されるので、右端にあるターミナルアイコンをクリックする。
image.png

こんな感じでビルド履歴がみれる。
今回は文字列表示のみなので、ログにその文字列が出力されている。
image.png

ビルド状態をGiteaのREADME.mdにバッヂで表示させてみる

画面上部のメニュのネスト表示があり、現在は「#1」のビルド履歴が表示されているが、「master」をクリックして、masterブランチの状態を表示させる。
このときのサイドメニュのEmbeddable Build Statusをクリックすると、、、
image.png

こんな感じでいろいろな書き方でバッヂステータスが表現できる。
ので、このなかからMarkdownを見つけて、README.mdに追記してpushしてみよう。
image.png

cd ~/gitea/sample

vi README.md
(以下を先頭に追記)
----8<----(snip)----8<----    
[![Build Status](http://jenkins.prosper2.net/job/gitea%20build%20test/job/sample/job/master/badge/icon)](http://jenkins.prosper2.net/job/gitea%20build%20test/job/sample/job/master/)
----8<----(snip)----8<----    

git add README.md
git commit -m "add Badge Status"
git push -u origin master

ビルド履歴を見るとビルドが走ったことがわかる。
image.png

Giteaのリポジトリを見ると、バッジが追加されている。
image.png

以下のようにJenkinsfileを修正して、ビルドが失敗するようにして、再度pushしてみる。
(echoで文字列を表示させる代わりに、シェルを走らせようとしている。この場合、そんなコマンドは存在しない。)

sed -i -e 's/echo/sh/' ./Jenkinsfile
git add Jenkinsfile
git commit -m "mod Jenkinsfile"
git push -u origin master

自動的にビルドされるが、失敗している。
image.png

理由はコマンドがみつからないため。
image.png

Giteaのステータスもfailingになっている。このバッヂ部分はJenkinsのビルドログへのリンクになっているので、クリックすると、、、
image.png

こんな感じでビルド結果がみれるようになる。
image.png

SlackにはGiteaにpushされた通知と、ビルドが失敗した通知がきている。
image.png

さいごに

テストとかデプロイの自動化とか、いろいろやりたいことがありすぎてなんなら自前でたてちまえ。ということで全部を足元においてみました。
AnsibleやDockerでも良かったのですが、クリーンインストール直後でもすぐ使えるように、コピペでまとめました。
それに公式のDockerfileならまだしも、個人が作成したDockerfileとかdocker-composeとかって、実は何してんだかよくわかんないなぁ、と思ったりもしました。ので、危険な処理してないよ。という意味も含めてです。

検証をやり直しながら進めたのでスクリーンショットの時系列が一部入れ替わってる部分があります。ごめんなさい。
さて、環境がつくれたので、つぎはテストコードを含めたホントのCIをやるぞぉー。

出典

https://docs.gitea.io/en-us/reverse-proxies/
https://wiki.jenkins.io/display/JENKINS/Jenkins+behind+an+nginx+reverse+proxy
https://www.eclipse.org/jetty/documentation/9.4.31.v20200723/startup-unix-service.html
https://gcube.wiki.gcube-system.org/gcube/Gitea/Jenkins:_Setting_up_Webhooks
https://plugins.jenkins.io/slack/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub奮闘記②【手順まとめ】

 ㊗︎ 初 push

用語に続いて、この勢いで 手順もまとめます。
GitHubのユーザー登録は済ませて下さい。

 手順

 0.最終的に、こんな感じ。

「GitTestProject」というリポジトリ名。
initial Commitに加えて、追加したCommitが反映されています。

push出来てた!.png

 1. gitの初期設定?

Gitに「ユーザ名」「メールアドレス」を登録します。
GitHubと同じものを指定しておくのが良いです。

以下のコードを元に、ターミナルで設定します。(1行ずつ)

git config --global user.name "ユーザー名"
git config --global user.email メールアドレス

ターミナル ?

Xcode内にターミナルは無いですが、Macに標準インストールされてます。
Finder -> アプリケーション -> ユーティリティ -> ターミナル

 2. プロジェクト開始と同時に、リポジトリを作成?

Create Git repository on (My Mac)にチェック。✅
initial Commitされます。

20160301231957.png

Commit = 変更履歴を、ローカルリポジトリに保存すること。

✅により、プロジェクト開始と同時にローカルリポジトリが作成され、
そこにソースコードが保存されました。

inital Commit.png

initial Commitでは、デフォルトのコードしかCommitされません。

viewController.swift
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    } 
}

 補足

Xcodeメニューバーにて、Source Control > Create Git Repositories...からでもinitial Commit出来ますが、

リポジトリを「✅」で作成するデメリットは特に無いので、
チェック入れるのが吉。

 3. リモートリポジトリを作成する。?

リモートリポジトリ作成(pushing ぐるぐる).png

image.png

Remoteを右クリック > Create "GitTestProject" Remote..
で、Create画面が出ます。

createを押すと、「Pushing...」 ぐるぐるLoadingされます。?

ご自身でGitHubにログインして、
リモートレポジトリが作られているか確認してみましょう。

initial Commit のみがpushされていると思います。
無事、push完了です?

 4. コード変更後、Xcodeでプッシュする。?

さて、『いつ、誰が、どこに、どのような変更を行ったか』。
バージョン管理がGitの利点です。

実際にコードを変更して、Commitして、pushしてみます。

 コードいじる。

ViewController.swiftを修正後、Commitします。
今回は以下のコードを追記してみました。

ViewController.swift
 func Log() {
        print("Hello")
    }

 変更したので、Commitする。

ゲームでいう、「セーブ」

  • Mのついたファイルを右クリックして、Source Control > Commit "ViewController.swift"... (M= Modify: 修正する)
  • Xcodeメニューバーからでも、出来ます。

こんな感じ。
変更箇所が、とても分かりやすいです。
ローカルリポジトリにCommit.png

 Commit完了!

Commit 完成.png

 補足

Commit Message欄には、『どのような変更を行ったか』を記載します。
書かないと、Commitを実行できません。
Commit Messages is required.png

 コード変更したので、Xcodeでプッシュする。

Xcodeメニューバーにて、Source Control > Push...

push出来てた!.png

GitHubを確認。
無事、pushできています✌️

変更履歴がちゃんと表示されてます!.png

変更箇所も分かりやすい。
これで共同開発チームの皆が、コード変更を見ることができます。?

おしまい。

 参考サイト

Xcodeでgit機能を使う(プロジェクトの作成からgithubにpushするまで)
XcodeからgitとGitHubを使う方法・基本編

 関連記事

追記予定

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub奮闘記①【用語まとめ】

 ㊗︎ 初 push

先日まで push が出来なかったのですが、

gitの初期設定をしていなかった事が原因でした。
以下のコードを元に、ターミナルで設定します。(1行ずつ)

git config --global user.name "ユーザー名"
git config --global user.email メールアドレス

ターミナル ?

Xcode内にターミナルは無いですが、Macに標準インストールされてます。
Finder -> アプリケーション -> ユーティリティ -> ターミナル

 用語

 「リポジトリ」 (repository)

- ソースコードを管理する単位。
-「保管場所」をカッコ付けて言った表現。
-「ローカル」と「リモート」 がある。
- ローカルリポジトリで作業を行い、その作業内容をリモートリポジトリへpush。
- 
今さら聞けない!GitHubの使い方【超初心者向け】

 「ソースコード」

人間が理解しやすいプログラミング言語を使って、記述されたもの。

プログラムの"元" (source)なので、ソースプログラムとも言われる。

ソースコードをコンパイルして、プログラムが作られる。
「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

 「バージョン管理」

システムの変更を管理すること。
『いつ、誰が、どこに、どのような変更を行ったか』

Webデザイン, Webライターとかにも使われる。

 ちょっと、ややこしい用語?

Git と GitHub

-Git ソースコードのバージョンを管理するためのシステム。

-GitHub GitHub社が作ったサービス。Gitに、便利な機能を付け加えた。

 「Git」

download-1.png!

リーナス・トーバルズ氏が開発。
Linux?も開発した、すごい人らしい。


 「GitHub」

download.png

よく見かける生物。
以下、GitHub共同創業者さんのインタビュー引用。

コマンドラインで使うにはGitは最高だったんですが、
オンラインでコードをシェアするのが難しかったのです。

他の開発者と、コードをシェアしたかったのです。
GitHubは、週末のサイドプロジェクトとしてスタートしました。

共同創業者に聞いた、GitHubは何が違ったのか?

push と commit

-commit 変更履歴を、ローカルリポジトリに保存すること。
-push ローカルリポジトリ内の変更履歴を、GitHubに送ること。
(=リモートリポジトリ にアップロードすること。)

git_repository_figure.png

commit → ゲームのセーブ
push → セーブデータをサーバに保存
commit → メールの下書き保存
push → メールの送信

おしまい。

 関連記事

追記予定

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitでCommitする粒度について

Gitでバージョン管理をするのはフロントエンド、バックエンドエンジニアだけでなく、インフラエンジニアにもコードを必要がある昨今だが、大規模のプロジェクトでGitを使ったことがなかった。
自分の経験を踏まえてGitでCommitする粒度について残しておく

対象読者

  • Gitを使ったことない人
  • Gitの操作はわかるけど使いどころがわからない人
  • 駆け出しエンジニア(笑)

※ 個人の経験にもとづいているので注意。

GithubだろうとGitlabだろうと何でも考え方は同じだろう。

最適なコミットの粒度とは?

結論はずばり私の”粒度は細かれば細かいほど良い”である。
では理由をつらつらと書いていこうと思います。

今回はお問い合わせForm機能の追加と言う例を用いて説明しようと思う。
私ならば以下のようなタスクになるだろう。
1.HTMLの作成
2.CSSの作成
3.JavaScriptでValidationを追加
4.サーバサイドのプログラム作成(Ruby、PHP、Pythonなど)

戻し作業が楽になる

細かくコミットをすることで
- どこまで動いていたのか?
- どこからバグったのか?
が仕組みでわかるようになり、バグった時の戻しが楽になります。

Gitがバージョン管理ツールである以上、これが大きな理由だろう。
上の1〜4の作業を1つのコミットに入れてしまうと、バグってしまった時の切り分けが難しくなる。
特に3、4のプログラムを組見切った後にバグが発生すると、大変なことになります。

タスクをサブタスク化して整理しやすくなる。ToDoに落とし込める

コミットを細かくすることでタスクをサブタスク化することになります。
つまりToDoに落とし込めることになります。

上の例のJavaScriptのValidationで考えてみましょう。
Validationだけだと、何するのかフワッとしています。
メールアドレスを入力するFormのValidationを行う場合は、

  • メールアドレスの形式があっているか?
  • 全角文字が入っていない?
  • RFCに準拠しているか?

など1つのFormに対してもいくつも実装しなければいけない機能があります。
細かくすることで〇〇の機能を実装する、〇〇と〇〇の機能を結合させると言った具合にタスクをToDoレベルに落とし込むことができます。
これにより、タスクで何をしなければいけないのか?いう作業に時間を使うのではなく、実装にパワーを使うことができます。

また上司やクライアントのレポートラインに対して何がどこまで終わっているのか?が明確になります。

誰かに引き継ぎしやすくなる。

タスクをサブタスク化して整理しやすくなる。ToDoに落とし込めると似ていますが、これもメリットです。
どこまで終わって、何をやらなければいけないか整理されていたら、引き継ぎのコストは下がります。
また引き継がれた人は実装だけにパワーを使うので余計な手戻りのリスクが下がります。
自分が忙しくて手が回らなくなっても、誰かに仕事をお願いできるのはメリットだと思います。

以上、私が考えるメリットです。何かありましたコメントお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む