Gitの速さでSnapを自動リリースする

この記事ではSnapストアをどのようにDevOpsに組み込んで行けるのかについて説明をさせていただこうと思います。その具体例としてRaspberryPi3をGitlabのspecific runnerとして登録し、Gitlabリポジトリにプッシュされるたびに自動でSnapをビルドし、Edgeチャネルにリリースする仕組みを作っていきたいと思います。

7/29日に本記事に関しての[ウェビナー](https://connpass.com/event/218426/)を開催しますのでそちらにもご参加ください。

#####環境
+ Gitlabアカウント(フリー)
+ Ubuntu18.04以降
+ Linuxコマンドラインとgitに関する少しの知識
+ https://dashboard.snapcraft.ioのストアアカウント
+ Raspberry Pi3 (running Ubuntu 20.04 Server)
+ [SSOアカウント](https://login.ubuntu.com/ "GitLab")は作成済み


#1.GitLabのセットアップ

スナップの継続的デリバリーを利用してSnapパッケージをSnapStoreに配信する前に、Snapパッケージを使用したプロジェクトが必要になります。今回はGitLabを利用します。

####1.1 Gitlabのアカウントとプロジェクトの作成
まず、[https://gitlab.com/users/sign_in](https://gitlab.com/users/sign_in "GitLab")にアクセスしてアカウントの作成を行います。次に、[https://gitlab.com/projects/new](https://gitlab.com/projects/new"GitHubNew")にアクセスします。次に、プロジェクトの詳細を入力しテストSnap用のリポジトリを作成します。（ここではSanpleSnapBuildという名前にしました）[Create Project]ボタンをクリックしてプロジェクトを作成します。プロジェクトが作成され、以下のような画面が表示されると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692438/ae4aa944-5fc7-93da-a453-3778741280ce.png)

####1.2 Snapの作成
それでは早速サンプルSnapプロジェクトを作成し[このレポジトリ](https://gitlab.com/masahiro_nakagawa/samplesnapbuild)にプッシュしていきます。ターミナルを開き、次のコマンドを実行しgitをインストールします。

```
sudo apt install git
```
SampleSnapBuildをワークスペース(~/03.work/07.CICD/99.BlogPost/01.Gitlab)に複製します。

```
hiro@hiro-System-Product-Name:~/03.work/07.CICD/99.BlogPost/01.Gitlab$ git clone https://gitlab.com/masahiro_nakagawa/samplesnapbuild.git
Cloning into 'samplesnapbuild'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.

```
snapcraft initを実行して、Snapcraft.yamlを作成します。

```
hiro@hiro-System-Product-Name:~/03.work/07.CICD/99.BlogPost/01.Gitlab/samplesnapbuild$ snapcraft init
Created snap/snapcraft.yaml.
Go to https://docs.snapcraft.io/the-snapcraft-format/8337 for more information about the snapcraft.yaml format.

```
作成されたsnapcraft.yaml(snap/snapcraft.yaml)ファイルを下のように編集します。

```
name: ci-test-masahiro # you probably want to 'snapcraft register <name>'
base: core18 # the base snap is the execution environment for this snap
version: '1.0' # just for humans, typically '1.2+git' or '1.3.2'
summary: Single-line elevator pitch for your amazing snap # 79 char long summary
description: |
  This is demo snap used to demonstrate how to utilize gitlab CI/CD.
  Gitlab CI/CD automatically builds snap and releases it to snap store edge channel.
  store.

grade: stable # must be 'stable' to release into candidate/stable channels
confinement: strict # use 'strict' once you have the right plugs and slots


apps:
  hello:
    command: bin/hello

parts:
  gnu-hello:
    source: http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz
    plugin: autotools
```

(**_スナップ名をあなたの名前に置き換えることを忘れないでください！_**Snap名はストア全体で一意である必要があります。たとえ製作者が異なっていても、同じ名前のSnapパッケージはストアに登録できません。）

これは非常に単純なスナップのパッケージであり、説明にあるように、挨拶を出力するだけです。 [最初のスナップを作成する](https://ubuntu.com/tutorials/create-your-first-snap#1-overview)チュートリアルでは、このファイルのすべての行の詳細を説明しているので、パッケージの詳細については、このチュートリアルを参照してください。ここでは、継続的デリバリーのみに焦点を当てます。このスナップをGitHubリポジトリにプッシュしましょう。(ほかにもサポートしている言語毎のチュートリアルは[こちら](https://snapcraft.io/docs/creating-a-snap?_ga=2.211511381.158591877.1626317791-427623051.1603775243)をご覧ください)

```
git add snap/snapcraft.yaml
git commit -m "Commit sample (hello world) snap project" 
git push
```

####1.3 Snap名の登録
スナップ名を選択したので、ストアアカウントにスナップ名を登録する必要があります（スナップ名は一意であり、公開する前に登録する必要があります)「ci-test-masahiro」をsnapcraft.yamlの名前フィールドに使用したものに置き換えます。また”snapcraft login”を実行するとパスワードとユーザー名を確認されるので、[SSOアカウント](https://login.ubuntu.com/ "GitLab")を作成した際に使用したE-mailアドレスと、パスワードを入力します。

```
snapcraft login
snapcraft register ci-test-masahiro
```

これで、SnapストアにSnap名が登録されたので、CI/CDを利用して自動でSnapストアにSnapをリリースする準備ができました。

#2.Gitlabにspecific Runnerを登録する

今回はspecific RunnerとしてRaspberryPiを使用したいと思います。

####2.1 RaspberryPi用の起動SDカード作成
UbuntuImagerを利用してUbuntu20.04の起動SDカードを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692438/7f829d4e-319d-2b6b-a7b9-43df07f70c49.png)

SDカードが作成できたら、RaspberryPIにカードをセットしてRaspberryPiを起動します。

####2.1 Gitlab Runnerのインストールと登録
次にGitlab RunnerをダウンロードしRaspberry PIにインストールします。
(docker: command not foundと表示されますが、Dockerは使用しないので問題ないと思います)

```
curl -LJO "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_arm64.deb"
sudo dpkg -i gitlab-runner_arm64.deb
```

次にRaspberryPIをRunnerとしてGitlabに登録します。この際にTokenとURLが必要になるので、前もって調べておきます。GitlabProjectからSettings→CI/CD→ Runnersと辿っていくと、Specific Runnerという表示の下にURLとTokenが表示されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692438/b22f0702-014e-3dd4-b483-8de08f53c54f.png)

Raspberry Piからコマンドを発行してRaspberryPiをRunnerとして登録します。

```
ubuntu@ubuntu:~$ sudo gitlab-runner register
Runtime platform                                    arch=arm os=linux pid=2335 revision=c1edb478 version=14.0.1
Running in system-mode.                            
                                                   
Enter the GitLab instance URL (for example, https://gitlab.com/):
https://gitlab.com                            
Enter the registration token:
MjU-aYqDx3D-Zvt3rhU-
Enter a description for the runner:
[ubuntu]: Rsberry pi 3
Enter tags for the runner (comma-separated):
rpi3runner
Registering runner... succeeded         runner=MjU-aYqD
Enter an executor: docker-ssh, shell, ssh, virtualbox, docker+machine, kubernetes, custom, docker, parallels, docker-ssh+machine:
shell
Runner registered succe
```

Runnerに新しくRaspberryPiが追加されていることを確認します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692438/127a4071-4d3d-a3c9-2d08-248d03d5cf6e.png)

これでRaspberryPiをSpecific Runnerとして登録できました。

#3.GitLab Runner設定する
ここで、RaspberryPiに必要なSnapのビルドとリリースに必要なSnapcraftスナップとGitをインストールし、その他必要な設定を行っていきます。

####3.1 macaroonの作成
Snapストアにパスワードユーザー名を入力せずにログインするためにmacaronを作成します。このデモではci-test-masahiroをEdgeチャネルにリリースすることが目的なので、--snaps=ci-test-masahiroと--channels=edgeをオプションとして指定します。

```
/home/ubuntu/$ snapcraft export-login --snaps=ci-test-masahiro --channels=edge macaroon
Enter your Ubuntu One e-mail address and password.
If you do not have an Ubuntu One account, you can create one at https://snapcraft.io/account
Email: masahiro.nakagawa@canonical.com
Password: 
Second-factor auth: 275310

Login successfully exported to 'macaroon'. This can now be used with

    snapcraft login --with macaroon


to log in to this account with no password and have these capabilities:
snaps:       ['ci-test-masahiro']
channels:    ['edge']
permissions: ['package_access', 'package_manage', 'package_push', 'package_register', 'package_release', 'package_update']
expires:     2022-07-14T08:58:21.533211

This exported login is not encrypted. Do not commit it to version control!
```
これでmacaroonを作成できたので、次に必要な変更を加えていきます。

####3.2 Ubuntuフォルダのパーミッションを変更
Jobはgitlab-runnerというユーザーが実行するので、Ubuntuユーザーのホームディレクトリに作成されたmacaroonファイルにアクセスができるように、gitlab-runnerをUubntuグループに追加し、Ubuntuユーザーのホームティレクトリと作成されたmacaroonのパーミッションを変更を変更します

```
sudo  usermod -a -G ubuntu　gitlab-runner
chmod 750 ubuntu
chmod 770 macaron
```
####3.3 gitlab-runnersnapcraftコマンドを実行するさいにエラーが起きるこ場合

visudoを実行し、以下のラインを/etc/sudoersに追加します。

```
Gitlab-runner ALL=(ALL) NOPASSWD: ALL
```

#4.GitLab CI/CD　Pipelineを設定する
ここからは.gitlab-cy.ymlを作成、編集してPipelineを定義していきます。

ここで少し概念の説明をさせてください。Pipelineを構築する際にステージ,とジョブという2つの概念があります。ステージはジョブの集合で、同時に複数のステージを実行することはできません。逆に、ジョブは何らかのタスクで、同じステージのジョブは並列実行をすることが可能です。(Runnerが複数無い場合は、一つづつ実行されます)

ここではそれぞれ一つのジョブ(SnapApp､ReleaseSnap)で構成される2ステージ(buildsnap､ releasesnap)のPipelineを作っていきます。buildsnap ステージでSnapのビルドを行い、releasesnap ステージでSnapストアにリリースします。

####4.1 ステージの定義
2つのステージと各ステージの実行順を定義します。ここで記載した順番で各ステージが実行されます。

```
# .gitlab-ci.yml
stages:
  #defines two stages for snapping app and testing it
    - buildsnap
    - releasesnap   
```

####4.2 SnapAppジョブの定義
次にSnapApp Jobを定義していきます。このジョブは先程登録したrpi3runnerで実行され、Gitリポジトリーからコードをクローンし、Snapcraftコマンドを実行してSnapのビルドを行います。また、各ステージ毎に作成されたファイルは次のステージには持ち越されません。そのためここではArtifactを利用して、Snapパッケージをreleasesnapステージに持ち越します。

```
SnapApp:
  stage: buildsnap
  #specify specific runner to be used
  tags: 
    - rpi3runner 
  #define what to do
  script:
    # build snap package 
    - snapcraft --destructive-mode
       
  artifacts:
    paths:
      - ci*.snap
    expire_in: 1 day   
```

####4.2 ReleaseSnapジョブの定義
ここでは先程作成したmacaroonを利用しSnapストアにログインをし、buildsnap ステージで作成されたSnapファイルをストアにリリースします。

```
ReleaseSnap:
  stage: releasesnap
  #specify specific runner to be used
  tags: 
    - rpi3runner 
  #define what to do
  script:
  # login to snap store using credential
  - snapcraft login --with  /home/ubuntu/credential 
  # upload snap to edge 
  - snapcraft upload ci*.snap --release edge
```
####4.4 .gitlab-ci.ymlをPushする
.gitlab-ci.ymlがGitlabのリポジトリにPushされると自動的にパイプラインが実行され、buildsnapステージの実行が始まります。buildsnapステージに問題がなければ、次にreleasesnapステージが実行されSnapがリリースされます。

#5. Snapがリリースされたか確認してみる
RaspberryPiから以下のコマンドを実行すると、EdgeチャネルにSnapがリリースされていることが確認できます。(注意:SnapをビルドしたRunnerと同じアーキテクチャのマシンからコマンドを実行してください。コマンドを実行したアーキテクチャ用にリリースされたSnapを確認するので、アーキテクチャが異なる場合はEdgeチャネルになにもリリースされていないように見えてしまいます)

```
ubuntu@ubuntu:~$ uname --all
Linux ubuntu 5.11.0-1012-raspi #13-Ubuntu SMP PREEMPT Thu Jun 17 10:47:05 UTC 2021 armv7l armv7l armv7l GNU/Linux
ubuntu@ubuntu:~$ snap info ci-test-masahiro
name:      ci-test-masahiro
summary:   Single-line elevator pitch for your amazing snap
publisher: Masahiro Nakagawa (masahiro20200511)
store-url: https://snapcraft.io/ci-test-masahiro
license:   unset
description: |
  This is demo snap used to demonstrate how to utilize gitlab CI/CD.
  Gitlab CI/CD automatically builds snap and releases it to snap store edge channel.
  store.
snap-id: efvyUMD55NHxPoeNak0YAjfbUo2XcGfN
channels:
  latest/stable:    –                         
  latest/candidate: –                         
  latest/beta:      –                         
  latest/edge:      1.0 2021-07-15 (82) 102kB -
```

#6. まとめ
今回はGitLabのSpecific Runnerを利用してSnapを自動的にビルドし、Snapストアにリリースする方法をご紹介しました。今回は省きましたが、もちろんテストステージを定義し、Post refresh hookやUnit Testをテストステージで実行することもできます。必要に応じてPipelineの定義を拡張してみてください。


中川　雅裕　Canonical Japan (Iot Field Engineer)
Ubuntu Blog: https://jp.ubuntu.com/blog
Canonical Japan: https://jp.ubuntu.com/
Email: masahiro.nakagawa@canonical.com
Email:info-jp@canonical.com
Phone: 03-6205-3075