tag:crieit.net,2005:https://crieit.net/feed Crieitの最近の投稿 - Crieit Crieitの最近の投稿 2020-12-04T00:13:51+09:00 https://crieit.net/feed tag:crieit.net,2005:PublicArticle/16255 2020-12-04T00:13:51+09:00 2020-12-04T00:13:51+09:00 https://crieit.net/posts/ansible-settings-vsftpd-and-apache-20201202 Ansible を使って FTPユーザの作成と Apache の仮想サイトの設定をする <p>Ansible を使って設定を行う例として、 Linuxユーザを作成してFTPユーザとし、 Apache の仮想サイトを作成する部分をやってみたいと思います。</p> <h2 id="前提"><a href="#%E5%89%8D%E6%8F%90">前提</a></h2> <p>対象サーバの LAMP は<a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2020/07/09/centos7-wordpress-move/">CentOS7.5 + Apache + PHP + MySQL サーバを構築し、WordPressサイトを引っ越す(2018/11/20)</a>のうち</p> <ul> <li>1.初期設定 ~ 4.リポジトリ追加</li> <li>5.vsftpd のうち <ul> <li>5.1.インストール, 5.2.設定</li> </ul></li> <li>6.Apache ~ 11.Webmin</li> <li>12.Apache仮想サイトのうち <ul> <li>12.1.ダミーサイトの作成</li> </ul></li> <li>16.SSH</li> </ul> <p>が既に設定済みの状態のサーバとします(13.Let’s Encrypt ~ 15.WP-CLI は対象外)。</p> <p>また、今回 Ansible で設定するのは</p> <ul> <li>5.vsftpd のうち <ul> <li>5.3.ユーザ設定</li> <li>5.4.ユーザ作成・設定</li> </ul></li> <li>12.Apache仮想サイトのうち <ul> <li>12.2. 仮想サイト作成</li> </ul></li> </ul> <p>の部分を想定しています。</p> <h2 id="設定"><a href="#%E8%A8%AD%E5%AE%9A">設定</a></h2> <h3 id="ディレクトリ階層"><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E9%9A%8E%E5%B1%A4">ディレクトリ階層</a></h3> <pre><code class="bash">PROJECT_ROOT/ ├ workspace/ // データ永続化領域 │ ├ entrypoint.sh // Dockerコンテナ起動時に実行するシェルスクリプト │ └ ansible/ // Ansible 用ディレクトリ │ ├ targets/ // リモートホストの情報を収めるディレクトリ │ │ └ hosts // リモートサーバの一覧 (今回は1つのみ) │ │ │ ├ tasks/ // Ansible のタスクを収めるディレクトリ │ │ ├ add_user.yml // Linuxユーザの作成 │ │ ├ vsftpd_user_settings.yml // vsftpd の設定 │ │ └ apache_settings.yml // Apache 仮想サイトの設定 │ │ │ ├ templates/ // Apache 仮想サイト設定のテンプレートを収めるディレクトリ │ │ └ vhost.conf.j2 // Apache 仮想サイト設定のテンプレート │ │ │ ├ vars/ // 各種パラメータ変数の設定を収めるディレクトリ │ │ └ param_vars.yml // 各種パラメータ変数 │ │ │ └ main.yml // Ansible の playbook │ ├ docker-compose.yml // Docker Compose 設定ファイル └ Dockerfile // Dockerfile </code></pre> <p><a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2020/10/22/build_docker_container_as_ansible_controller/">Ansibleコントロールノード を Dockerコンテナ でビルドし、リモートサーバに公開鍵認証でSSH接続してインストール済みのパッケージの一覧を取得するまで</a>や<a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2020/11/06/ansible_execute_yum_update/">Ansible を使って yum update を実行する</a>の記事の構造をベースにカスタマイズ。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/philotic_moby_test/tree/yum_update">arm-band/philotic_moby_test at yum_update</a></li> </ul> <p>一揃いを Github に置きました。</p> <h3 id="Dockerfile, docker-compose.yml, entrypoint.sh. ansible/targets/hosts"><a href="#Dockerfile%2C+docker-compose.yml%2C+entrypoint.sh.+ansible%2Ftargets%2Fhosts">Dockerfile, docker-compose.yml, entrypoint.sh. ansible/targets/hosts</a></h3> <p>この辺りはそのまま。 <code>ansible/targets/hosts</code> はグループ名を変更していますが、対となる <code>ansible/main.yml</code> の <code>hosts</code> の指定を変えれば良いだけなので些事。</p> <h3 id="ansible/vars/param_vars.yml"><a href="#ansible%2Fvars%2Fparam_vars.yml">ansible/vars/param_vars.yml</a></h3> <pre><code class="yaml">username: USERNAME password: Password1234 rootdirectory: sample_site domain: www.sample.jp ipaddress: 192.0.2.1 portnum: 80 </code></pre> <p>まずは今回の新顔の変数から。当初は末尾の「備考1: コマンドライン引数を変数に代入する」のように引数でコマンドラインから直接渡すことを考えていましたが、上記の通り6つもあるとそれだけでコマンドが長くなってしまい可読性が下がるので最終的に止めました。</p> <p>代わりに変数を1つのファイルにまとめて、コマンド自体の長さは変わらないように調整しました。</p> <p>この場合、以下の <code>ansible/main.yml</code> で触れますが <code>vars_files: ./vars/param_vars.yml</code> という一行で変数ファイルの読み込みができ、かつそれ以降は <code>vars</code> キーで変数としてそのまま使用できるので導入はわりとすんなり行きました。</p> <h3 id="ansible/main.yml"><a href="#ansible%2Fmain.yml">ansible/main.yml</a></h3> <pre><code class="yaml">- name: Settings vsftpd and httpd become: yes become_user: ADMIN_USER become_method: su hosts: - add_vhost_servers vars_files: ./vars/param_vars.yml tasks: - name: Add Linux User include_tasks: ./tasks/add_user.yml vars: user: "<span>{</span><span>{</span> username <span>}</span><span>}</span>" passwd: "<span>{</span><span>{</span> password <span>}</span><span>}</span>" - name: Settings vsftpd user_list and user_conf include_tasks: ./tasks/vsftpd_user_settings.yml vars: user: "<span>{</span><span>{</span> username <span>}</span><span>}</span>" directory: "<span>{</span><span>{</span> rootdirectory <span>}</span><span>}</span>" - name: Setup Apache conffile include_tasks: ./tasks/apache_settings.yml vars: directory: "<span>{</span><span>{</span> rootdirectory <span>}</span><span>}</span>" url: "<span>{</span><span>{</span> domain <span>}</span><span>}</span>" ip: "<span>{</span><span>{</span> ipaddress <span>}</span><span>}</span>" portnumber: "<span>{</span><span>{</span> portnum <span>}</span><span>}</span>" </code></pre> <p>タスクのメイン部分。基本は前回と同様で、 <code>tasks</code> で指定する各種タスクが置き換わった形です。変数については上記の通り。</p> <h3 id="ansible/tasks/add_user.yml"><a href="#ansible%2Ftasks%2Fadd_user.yml">ansible/tasks/add_user.yml</a></h3> <pre><code class="yaml">- name: Add user, setting password and groups user: name: "<span>{</span><span>{</span> username <span>}</span><span>}</span>" password: "<span>{</span><span>{</span> password | password_hash('sha512') <span>}</span><span>}</span>" groups: apache append: yes </code></pre> <p><code>ansible/main.yml</code> から渡ってきた各変数が使用される(ここでは <code>username</code>, <code>password</code>)、という形です。</p> <p>一点注意なのはパスワードはそのままだと平文で保存されてしまいますが、実際に認証する際はハッシュ値に変換されるので不一致となってしまいます。そこで、<strong>保存時にハッシュ値に変換</strong>するように <code>password_hash('sha512')</code> を噛ませています。</p> <h3 id="ansible/tasks/vsftpd_user_settings.yml"><a href="#ansible%2Ftasks%2Fvsftpd_user_settings.yml">ansible/tasks/vsftpd_user_settings.yml</a></h3> <pre><code class="yaml">- name: Settings vsftpd user_list blockinfile: path: /etc/vsftpd/user_list create: yes insertafter: EOF marker: "# {mark} ANSIBLE basic setup: <span>{</span><span>{</span> username <span>}</span><span>}</span>" block: "<span>{</span><span>{</span> username <span>}</span><span>}</span>" - name: Settings vsftpd user_conf blockinfile: path: "/etc/vsftpd/user_conf/<span>{</span><span>{</span> username <span>}</span><span>}</span>" create: yes insertafter: EOF marker: "# {mark} ANSIBLE basic setup: <span>{</span><span>{</span> username <span>}</span><span>}</span>" block: "local_root=/var/www/<span>{</span><span>{</span> rootdirectory <span>}</span><span>}</span>" - name: Restart vsftpd systemd: name: vsftpd.service state: restarted daemon_reload: yes </code></pre> <p>次は vsftpd の設定。 <code>user_list</code> にユーザとして登録し、 <code>user_conf</code> ディレクトリの下にユーザ名のファイルを作成、そこに <code>local_root</code> の指定を記述する、という内容です。</p> <p>ここでも一点注意。</p> <p>設定ファイルへの書き込みとして <code>blockinfile</code> を使用しているのですが、 <strong><code>marker</code> の値がユニークでない</strong>と、 <code>loop</code> で繰り返し処理をしたり、今回のケースだと異なる仮想サイトの設定を行ったり(Ansible を2回実行する)した場合に<strong>後勝ちで上書きされてしまう</strong>こと。</p> <p>今回のケースでは <code>user_list</code> に2回目の変数のユーザしか残らなくなってしまいます。そのため、 <strong><code>marker</code> の最後にユーザ名を混ぜることでコメント文が実行の度にユニークとなる</strong>ようにしました。</p> <h3 id="ansible/tasks/apache_settings.yml"><a href="#ansible%2Ftasks%2Fapache_settings.yml">ansible/tasks/apache_settings.yml</a></h3> <pre><code class="yaml">- name: mkdir root file: path: "/var/www/<span>{</span><span>{</span> rootdirectory <span>}</span><span>}</span>" state: directory owner: ADMIN_USER group: ADMIN_USER mode: 0755 recurse: no - name: mkdir web file: path: "/var/www/<span>{</span><span>{</span> rootdirectory <span>}</span><span>}</span>/web" state: directory owner: apache group: apache mode: 0775 recurse: no - name: Setup Apache conffile template: src: ../templates/vhost.conf.j2 dest: "/etc/httpd/conf.d/<span>{</span><span>{</span> rootdirectory <span>}</span><span>}</span>.conf" mode: '0644' - name: Restart httpd systemd: name: httpd.service state: reloaded daemon_reload: yes </code></pre> <p>続いて Apache 仮想サイトの設定。ディレクトリを作ったり権限・所有者設定したり。</p> <p>最後の <code>Setup Apache conffile</code> の処理では jinja2 のテンプレートから仮想サイト用の conf ファイルを作成しています。</p> <h3 id="ansible/templates/vhost.conf.j2"><a href="#ansible%2Ftemplates%2Fvhost.conf.j2">ansible/templates/vhost.conf.j2</a></h3> <pre><code class="jinja2"><VirtualHost <span>{</span><span>{</span> ipaddress <span>}</span><span>}</span>:<span>{</span><span>{</span> portnum <span>}</span><span>}</span>> DocumentRoot "/var/www/<span>{</span><span>{</span> rootdirectory <span>}</span><span>}</span>/web" ServerName <span>{</span><span>{</span> domain <span>}</span><span>}</span> {% if domain | regex_search("^www\.", ignorecase=True) %} ServerAlias <span>{</span><span>{</span> domain | regex_replace("^www\.(.*)$", '\\1') <span>}</span><span>}</span> {% endif %} ScriptAlias /cgi-bin/ /var/www/<span>{</span><span>{</span> rootdirectory <span>}</span><span>}</span>/web/cgi-bin/ {% if domain | regex_search("^www\.", ignorecase=True) %} RewriteEngine on RewriteCond %{HTTP_HOST} <span>{</span><span>{</span> domain | regex_replace("^www\.(.*)$", '^\\1$') | regex_replace("\.", '\.') <span>}</span><span>}</span> RewriteRule ^(.*)$ http://<span>{</span><span>{</span> domain <span>}</span><span>}</span>$1 [R=301,L] {% endif %} <Directory "/var/www/<span>{</span><span>{</span> rootdirectory <span>}</span><span>}</span>/web"> allow from all AllowOverride All Options FollowSymLinks Require all granted </Directory> </VirtualHost> </code></pre> <p>大体設定ファイルの内容ですが、各種変数を使用しています。</p> <p>また、ドメイン(変数 <code>domain</code> )が <code>www.</code> 始まりの場合はサーバ名を <code>www.example.jp</code> のように <code>www.</code> 付きとし、同時に <code>ServerAlias</code> で <code>example.jp</code> と <code>www.</code> なしもフォローするような条件分岐(<code>if</code>, <code>regex_search</code>)を挟みました。</p> <p>併せて、 <code>www.</code> なしのURLにアクセスした場合に <code>www.</code> ありにリダイレクトする設定も、変数 <code>domain</code> を正規表現置換(<code>regex_replace</code>)で一括で入れるようにしました。</p> <p>例えば、 <code>domain: www.example.jp</code> とした場合は以下のような設定ファイルが作成されます。</p> <pre><code><VirtualHost 192.0.2.1:80> DocumentRoot "/var/www/example/web" ServerName www.example.jp ServerAlias example.jp ScriptAlias /cgi-bin/ /var/www/example/web/cgi-bin/ RewriteEngine on RewriteCond %{HTTP_HOST} ^example\.jp$ RewriteRule ^(.*)$ http://www.example.jp$1 [R=301,L] <Directory "/var/www/example/web"> allow from all AllowOverride All Options FollowSymLinks Require all granted </Directory> </VirtualHost> </code></pre> <p>一方、 <code>domain: form.example.jp</code> のようなケースだと以下。</p> <pre><code><VirtualHost 192.0.2.1:80> DocumentRoot "/var/www/form_example/web" ServerName form.example.jp ScriptAlias /cgi-bin/ /var/www/form_example/web/cgi-bin/ <Directory "/var/www/form_example/web"> allow from all AllowOverride All Options FollowSymLinks Require all granted </Directory> </VirtualHost> </code></pre> <p>上記の通り、 <code>ServerAlias</code> と <code>Rewrite</code> 系がありません。</p> <h2 id="動作確認"><a href="#%E5%8B%95%E4%BD%9C%E7%A2%BA%E8%AA%8D">動作確認</a></h2> <p>以上のような構成で動作検証。</p> <pre><code class="bash"># ansible-playbook -i /workspace/ansible/targets/hosts /workspace/ansible/main.yml -u SSH_REMOTEUSER --private-key=&quot;/root/.ssh/PRIVATE_KEY&quot; -K BECOME password: PLAY [Settings vsftpd and httpd] ********************************************************************************** TASK [Gathering Facts] ********************************************************************************************ok: [192.0.2.1] TASK [Add Linux User] *********************************************************************************************included: /workspace/ansible/tasks/add_user.yml for 192.0.2.1 TASK [Add user, setting password and groups] **********************************************************************changed: [192.0.2.1] TASK [Settings vsftpd user_list and user_conf] ********************************************************************included: /workspace/ansible/tasks/vsftpd_user_settings.yml for 192.0.2.1 TASK [Settings vsftpd user_list] **********************************************************************************changed: [192.0.2.1] TASK [Settings vsftpd user_conf] **********************************************************************************changed: [192.0.2.1] TASK [Restart vsftpd] *********************************************************************************************changed: [192.0.2.1] TASK [Setup Apache conffile] **************************************************************************************included: /workspace/ansible/tasks/apache_settings.yml for 192.0.2.1 TASK [mkdir root] *************************************************************************************************changed: [192.0.2.1] TASK [mkdir web] **************************************************************************************************changed: [192.0.2.1] TASK [Setup Apache conffile] **************************************************************************************changed: [192.0.2.1] TASK [Restart httpd] **********************************************************************************************changed: [192.0.2.1] PLAY RECAP ********************************************************************************************************192.0.2.1 : ok=12 changed=8 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 </code></pre> <p>OKです。</p> <p>仮想サイトにアクセスして Apache のデフォルトページが表示される、FTPでログインできる、ファイルアップロードなどの操作ができる、といった動作を一通りできることを確認できました。</p> <h3 id="備考1: コマンドライン引数を変数に代入する"><a href="#%E5%82%99%E8%80%831%3A+%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3%E5%BC%95%E6%95%B0%E3%82%92%E5%A4%89%E6%95%B0%E3%81%AB%E4%BB%A3%E5%85%A5%E3%81%99%E3%82%8B">備考1: コマンドライン引数を変数に代入する</a></h3> <p>例えば、以下のような <code>main.yml</code> を用意します。</p> <pre><code class="yaml">- name: Get packages from hosts and do yum update become: yes become_user: ADMIN_USER become_method: su hosts: - update_servers tasks: - name: Get packages from hosts before update include_tasks: ./tasks/get_packages.yml - name: Output packages from hosts before update include_tasks: ./tasks/output_packages.yml vars: flag: "<span>{</span><span>{</span> before <span>}</span><span>}</span>" - name: Do yum update include_tasks: ./tasks/yum_update.yml - name: Get packages from hosts after update include_tasks: ./tasks/get_packages.yml - name: Output packages from hosts after update include_tasks: ./tasks/output_packages.yml vars: flag: "<span>{</span><span>{</span> after <span>}</span><span>}</span>" </code></pre> <p><a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2020/11/06/ansible_execute_yum_update/">Ansible を使って yum update を実行する</a> の <code>main.yml</code> のうち、 <code>vars</code> の部分を少し編集したものです。</p> <p>これで以下のように playbook を実行。</p> <pre><code class="bash"># ansible-playbook -i /workspace/ansible/targets/hosts /workspace/ansible/main.yml -u SSH_REMOTEUSER --private-key=&quot;/root/.ssh/PRIVATE_KEY&quot; -K -e &quot;before=before2 after=after22&quot; </code></pre> <p>すると、 <code>workspace/192.0.2.1_packages_before2</code> と <code>workspace/192.0.2.1_packages_after22</code> が生成され、コマンドラインの引数が変数として使用されたことが分かりました。</p> <p>当初はこれの延長線を考えていましたが、冒頭の通り6つも引数があると長くなってしまうので最終的には不採用としました。</p> <p>ただし、これはこれで引数の使い方として参考になりそうなのでメモとして残しておきます。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="Ansible"><a href="#Ansible">Ansible</a></h3> <h4 id="引数、変数"><a href="#%E5%BC%95%E6%95%B0%E3%80%81%E5%A4%89%E6%95%B0">引数、変数</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/YumaInaura/items/6e05d87858bd647d11ab">ansible コマンドでモジュール引数 ( パラメータ ) を複数渡す方法 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://docs.ansible.com/ansible/2.9_ja/user_guide/modules_intro.html">モジュールの概要 - Ansible Documentation</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://docs.ansible.com/ansible/2.9_ja/user_guide/playbooks_variables.html">変数の使用 - Ansible Documentation</a></li> </ul> <h4 id="変数"><a href="#%E5%A4%89%E6%95%B0">変数</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/c_u/items/e91ca023fbb124fec34a">Ansible: vars_files を使用した loop処理 - Qiita</a></li> </ul> <p><code>vars_files</code> を採用。</p> <h4 id="ユーザ作成"><a href="#%E3%83%A6%E3%83%BC%E3%82%B6%E4%BD%9C%E6%88%90">ユーザ作成</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.netassist.ne.jp/blog/?p=156">Ansibleでユーザ追加 | Netassist Blog</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/moiwa/items/bab0f4c8d0dbf361afa4">Ansible ~userモジュール~ - Qiita</a></li> </ul> <h4 id="パスワード"><a href="#%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89">パスワード</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.netassist.ne.jp/blog/?p=967">Ansibleでユーザパスワード設定 | Netassist Blog</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sicksixrock66/items/474068167de9c8454319">適当にansibleでrootパスワードを変えようとしたらアクセスできなくなった件 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://teratail.com/questions/263760">Linux - Ansibleでパスワード変更の際に、パスワードポリシーを遵守させたい|teratail</a></li> </ul> <h4 id="Facts"><a href="#Facts">Facts</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://nwengblog.com/ansible-facts/">Ansible:Factsについて(Ansible 2.9.6) | matsublog</a></li> </ul> <h4 id="設定に追記"><a href="#%E8%A8%AD%E5%AE%9A%E3%81%AB%E8%BF%BD%E8%A8%98">設定に追記</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/YuukiMiyoshi/items/f1b773f65792ce0e9101">【Ansible】そのCopy、Blockinfileの方が良いかも? - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/nakacya/items/a624f6710c3ca0edc8a9">Ansible Blockinfile の tips - Qiita</a></li> </ul> <h4 id="blockfile"><a href="#blockfile">blockfile</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://dekitakotono.blogspot.com/2019/05/blockinfile.html">blockinfile モジュール-φ(.. ) のメモ</a></li> </ul> <blockquote> <p>複数のテキストブロックを追加する場合、マーカーラインをユニークにしないとテキストブロックが上書きされる</p> </blockquote> <p><code>marker</code> の文字列はユニークになるようにすること。</p> <h4 id="systemd"><a href="#systemd">systemd</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://tnamao.hatenablog.com/entry/2016/12/03/014742">ansible で systemd 配下のサービスを扱う - 日記</a></li> <li><a target="_blank" rel="nofollow noopener" href="http://pj-doaa.hatenablog.com/entry/2017/09/05/120849">AnsibleのModule:systemd - ときどきAnsible日記</a></li> </ul> <h4 id="ディレクトリ作成"><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E4%BD%9C%E6%88%90">ディレクトリ作成</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/hnakamur/items/b5a17d8cb289432014d5">[Ansible] ディレクトリが無かったら作成する - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://blog.adachin.me/archives/2781">[ansible]ディレクトリ作成はfileモジュールで! ? ADACHIN SERVER LABO</a></li> </ul> <h4 id="httpd"><a href="#httpd">httpd</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://dev.classmethod.jp/articles/tutorial-ansible-with-role/">Role を使ったAnsibleのチュートリアル | Developers.IO</a></li> </ul> <h4 id="jinja2, if"><a href="#jinja2%2C+if">jinja2, if</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yteraoka/items/7119d4e1e2f8faddfb64">Ansible の Template 機能の紹介 - Qiita</a></li> </ul> <h4 id="jinja2, regrex"><a href="#jinja2%2C+regrex">jinja2, regrex</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html">Using filters to manipulate data - Ansible Documentation</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/30413616/using-regex-in-jinja-2-for-ansible-playbooks">python - using regex in jinja 2 for ansible playbooks - Stack Overflow</a></li> </ul> <h3 id="usermod"><a href="#usermod">usermod</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.atmarkit.co.jp/ait/articles/1612/14/news022.html">【 usermod 】コマンド――ユーザーアカウントの情報を変更する:Linux基本コマンドTips(73) - @IT</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16253 2020-12-03T15:23:54+09:00 2020-12-03T15:28:22+09:00 https://crieit.net/posts/COBOL-1-5fc8847acbffe COBOLの基本構文その1 <p>この記事は COBOL アドベントカレンダー2020 3日目の記事です。</p> <ul> <li>COBOL Advent Calendar 2020 <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2020/cobol">https://qiita.com/advent-calendar/2020/cobol</a></li> </ul></li> </ul> <p>当分は COBOL についての基本的な話をする予定なので、<br /> 多くの部分で wikipedia などの記述とカブると思います。</p> <h2 id="TL;DR"><a href="#TL%3BDR">TL;DR</a></h2> <ul> <li>主な出典:</li> <li>COBOL | Wikipedia <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/COBOL">https://ja.wikipedia.org/wiki/COBOL</a></li> </ul></li> <li>サルでもわかるCOBOL入門 | ひよこグミ <ul> <li><a target="_blank" rel="nofollow noopener" href="http://www16.plala.or.jp/hiyokogumi/top.html">http://www16.plala.or.jp/hiyokogumi/top.html</a></li> </ul></li> <li>ILE COBOL 解説書 | IBM Knowledge Center <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.ibm.com/support/knowledgecenter/ja/ssw_ibm_i_73/rzasb/rzasbmain.htm">https://www.ibm.com/support/knowledgecenter/ja/ssw_ibm_i_73/rzasb/rzasbmain.htm</a></li> </ul></li> </ul> <h2 id="COBOL の基本構文について"><a href="#COBOL+%E3%81%AE%E5%9F%BA%E6%9C%AC%E6%A7%8B%E6%96%87%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">COBOL の基本構文について</a></h2> <ul> <li>COBOLってなぁに?その2 <ul> <li><a href="https://crieit.net/posts/COBOL-2">https://crieit.net/posts/COBOL-2</a></li> </ul></li> </ul> <p>では、COBOL プログラムの第一歩として<br /> 1行ごとの書き方についてお話しました。<br /> 最後に出てきたコード例はこんな感じでしたね。</p> <p>例:</p> <pre><code>000100**************************** 000200* ここはコメント 000300**************************** 000400 IDENTIFICATION DIVISION. 000500 PROGRAM-ID. COBOL_SAMPLE01. 000600 </code></pre> <p>今回はここで出てきた<br /> <code>IDENTIFICATION DIVISION.</code> などの<br /> COBOL の基本構文について触れていきましょう。</p> <h2 id="COBOLのプログラム 4つのDIVISION"><a href="#COBOL%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0+4%E3%81%A4%E3%81%AEDIVISION">COBOLのプログラム 4つのDIVISION</a></h2> <p>COBOL プログラムの基本構造として、<br /> 以下に挙げる 4つのDIVISION を順に記述していきます。<br /> COBOL プログラムを構成する基礎となる部分なので、順に追っていきましょう。</p> <h3 id="IDENTIFICATION DIVISION :見出し部"><a href="#IDENTIFICATION+DIVISION+%EF%BC%9A%E8%A6%8B%E5%87%BA%E3%81%97%E9%83%A8">IDENTIFICATION DIVISION :見出し部</a></h3> <p>COBOL プログラムの一番最初に記述する DIVISION です。<br /> 見出し部では必須の項目として <code>PROGRAM-ID</code> を記述します。<br /> COBOL プログラムでは、ファイル名などではなくこの <code>PROGRAM-ID</code> を用いてプログラムを区別するため、<br /> 他のプログラムと被らない <strong>一意なプログラムID</strong> を指定する必要があります。<br /> プログラム名は英字で始まる英数字で構成される文字列を指定します。</p> <p>また、見出し部では <code>PROGRAM-ID</code> の他に任意の項目として</p> <ul> <li>AUTHOR <ul> <li>作成者</li> </ul></li> <li>DATE-WRITTEN <ul> <li>作成日</li> </ul></li> <li>INSTALLATION <ul> <li>設置環境</li> </ul></li> </ul> <p>などがあります。<br /> これら任意の項目は現在のCOBOLでは廃止とされており、<br /> 新たにCOBOLプログラムを書くときには記述すべきではないとされていますが、<br /> <code>AUTHOR</code> や <code>DATE-WRITTEN</code> に関しては引き続き使われているような気がします(要出典)。</p> <p>例:</p> <pre><code>000100**************************** 000200* ここはコメント 000300**************************** 000400 IDENTIFICATION DIVISION. 000500 PROGRAM-ID. COBOL_SAMPLE01. 000600 AUTHOR. JUNYA KITAYAMA. 000700 DATE-WRITTEN. 12/03/2020. 000800 </code></pre> <h3 id="ENVIRONMENT DIVISION:環境部"><a href="#ENVIRONMENT+DIVISION%EF%BC%9A%E7%92%B0%E5%A2%83%E9%83%A8">ENVIRONMENT DIVISION:環境部</a></h3> <p>COBOL プログラムの2番目に記述する DIVISION です。<br /> ソース作成マシン名、利用ファイルの定義など、実行に必要な環境を記述します。<br /> あまりありませんが、もし仮に入出力を全く行なわないプログラムであれば省略可能となります。</p> <p>ENVIRONMENT DIVISION は 大きく<br /> CONFIGURATION SECTION = 構成節 と INPUT-OUTPUT SECTION 入出力節の2つからなります。</p> <h4 id="CONFIGURATION SECTION:構成節"><a href="#CONFIGURATION+SECTION%EF%BC%9A%E6%A7%8B%E6%88%90%E7%AF%80">CONFIGURATION SECTION:構成節</a></h4> <p>プログラムの作成・実行環境を提示する節です。<br /> 以下の3つの段落があります。</p> <ul> <li><code>SOURCE-COMPUTER</code> 段落 <ul> <li>ソース・プログラムをコンパイルするコンピューターを記述します。</li> <li>構文検査は行われますが、実行そのものに影響はありません。</li> <li><code>IBM-I</code> と記述されるものを多く見かけます。慣例的な表現でしょうか。</li> </ul></li> <li><code>OBJECT-COMPUTER</code> 段落 <ul> <li>オブジェクト・プログラムが実行されるシステムを指定します。</li> <li>構文検査は行われますが、実行そのものに影響はありません。</li> <li>こちらも <code>IBM-I</code> と記述されるものを多く見かけます。慣例的な表現でしょうか。</li> </ul></li> <li><code>SPECIAL-NAMES</code> 段落 <ul> <li><code>CONFIGURATION SECTION</code> における主要な記述となります。</li> <li>以下のような指定を行います。</li> <li>IBM 指定の環境名とユーザーが定義した簡略名の関連づけ</li> <li>通貨符号のための、単一または複数の通貨文字ストリングおよび置換文字を指定</li> <li>PICTURE 文節および数字リテラル内のコンマと小数点の機能が交換されることを指定</li> </ul></li> </ul> <h4 id="INPUT-OUTPUT SECTION:入出力節"><a href="#INPUT-OUTPUT+SECTION%EF%BC%9A%E5%85%A5%E5%87%BA%E5%8A%9B%E7%AF%80">INPUT-OUTPUT SECTION:入出力節</a></h4> <p>プログラム上でファイルやプリンターなどを利用する場合には、必須になる項目。<br /> ファイルを扱わない COBOL プログラムはほとんど存在しないため、<br /> ここの節が環境部ではメインの記述となります。</p> <ul> <li><code>FILE-CONTROL</code> 段落 <ul> <li>ファイルに名前を付け、<code>SELECT</code> 文節を記述することでァイルやプリンターなどの割り当てを記述します。</li> </ul></li> <li><code>I-O-CONTROL</code> 段落 <ul> <li>さまざまなファイルが共用する記憶域を指定します。</li> <li>COBOL プログラムではオプショナルな指定となっており、現在使うことはほぼないようです。</li> </ul></li> </ul> <h3 id="環境部のまとめ"><a href="#%E7%92%B0%E5%A2%83%E9%83%A8%E3%81%AE%E3%81%BE%E3%81%A8%E3%82%81">環境部のまとめ</a></h3> <p>標準出力に出力をプリントするような COBOL プログラムの場合、このような指定になります。<br /> (早速 <code>INPUT-OUTPUT SECTION</code> を指定しない例外的なコードになってしまいましたがご容赦ください)</p> <p>例:</p> <pre><code>000100**************************** 000200* ここはコメント 000300**************************** 000400 IDENTIFICATION DIVISION. 000500 PROGRAM-ID. COBOL_SAMPLE01. 000600 AUTHOR. JUNYA KITAYAMA. 000700 DATE-WRITTEN. 12/03/2020. 000800 ENVIRONMENT DIVISION. 000900 CONFIGURATION SECTION. 001000 SOURCE-COMPUTER. IBM-I. 001100 OBJECT-COMPUTER. IBM-I. 001200 SPECIAL-NAMES. 001300 CONSOLE IS CONSL. </code></pre> <h3 id="次回に続く"><a href="#%E6%AC%A1%E5%9B%9E%E3%81%AB%E7%B6%9A%E3%81%8F">次回に続く</a></h3> <p>今回は COBOL 基本構文である 4つの DIVISION の中から<br /> <code>IDENTIFICATION DIVISION</code> と <code>ENVIRONMENT DIVISION</code> についてお話しました。<br /> 次回は残り2つの DIVISION についてお話します。</p> 北山淳也 tag:crieit.net,2005:PublicArticle/16252 2020-12-03T07:00:09+09:00 2020-12-03T07:00:09+09:00 https://crieit.net/posts/Unity3D Unity3D上でマウスポインタの位置のグリッド座標を簡単に取得する <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>いつもお世話になっております。<br /> この度アドベントカレンダーに参加したいなと思いまして、<br /> <a href="https://crieit.net/advent-calendars/2020/crieit">なんでも Advent Calendar 2020</a>に記事を投稿することにしました。<br /> 普段はnoteやTwitterで活動しております。</p> <p>さて、今回のテーマは「Unity3D上でマウスカーソルの位置のグリッド座標を得られるようにする」というものですが、言葉だけつらつら並べてもわかりにくいのでサンプル画像を用意しました。最終的にこんな感じで選択できるようになります。</p> <p><a href="https://crieit.now.sh/upload_images/cd4a538e455b1b9f8f63504745e403815fc64e03e0852.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cd4a538e455b1b9f8f63504745e403815fc64e03e0852.gif?mw=700" alt="グリッド座標取得サンプル" /></a></p> <p>この画像のようにマウスカーソルの位置の座標を光らせられるようにしましょう。<br /> (キャラクター移動方法などは省略致します。ご了承下さい。また地形に高低差がある場合、この方法では上手くいかないと思います)</p> <h1 id="使用する機能"><a href="#%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E6%A9%9F%E8%83%BD">使用する機能</a></h1> <p>上記を実現する方法としてパッと思いつくのはPlaneやCubeを敷き詰めて全てにコライダーコンポーネントをアタッチし、マウスカーソルが当たっているかどうかを都度計算するという方法ですが、これはコストがかかり過ぎるので実用向きではありません。</p> <p>ならばどうするかと言うと、Unityが標準でサポートしている<strong>Grid</strong>と<strong>Tilemap</strong>を使います。<br /> これはUnity2Dでメインに使われている機能になります。普通はその名の通りタイル(マップチップ)を敷き詰めてフィールドを表現する機能なのですが、これを活用することで3D上でも簡単にグリッド座標を取得することができるようになります。</p> <h1 id="大まかな手順"><a href="#%E5%A4%A7%E3%81%BE%E3%81%8B%E3%81%AA%E6%89%8B%E9%A0%86">大まかな手順</a></h1> <p>大まかには以下の手順で進めていきます。</p> <ul> <li><strong>PlaneとGrid、Tilemapオブジェクトの用意</strong></li> <li>Plane上にカーソルが乗っている時、光らせる為の小さいPlane(=SelectPlane)を表示</li> <li>Plane上からカーソルが外れた際、SelectPlaneを非表示にする</li> <li><strong>RaycastでPlane上のマウスカーソルのある位置を取得する</strong></li> <li><strong>その地点をTilemapのWorldToCellメソッドでグリッド座標に変換</strong></li> <li>更にCellToWorldメソッドでもう一度ワールド座標に変換し、SelectPlaneのpositionに代入</li> </ul> <p>とりあえず以上になります。なおグリッド座標を単純に取得したいだけなら太字部分だけを行えばOKです。</p> <h1 id="実際の手順"><a href="#%E5%AE%9F%E9%9A%9B%E3%81%AE%E6%89%8B%E9%A0%86">実際の手順</a></h1> <p>それでは行って参りましょう。</p> <h2 id="PlaneとGrid、Tilemapオブジェクトの用意"><a href="#Plane%E3%81%A8Grid%E3%80%81Tilemap%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E7%94%A8%E6%84%8F">PlaneとGrid、Tilemapオブジェクトの用意</a></h2> <p>まずは空のGameObjectを一つ作成し、その子要素に「3Dオブジェクト」-「平面」を1枚用意します。大きさや位置などは任意で変更して下さい。<br /> 更にGameObjectの階層下に「2Dオブジェクト」-「タイルマップ」を作成します。<br /> (光らせたい方は1グリッドのサイズ/10の大きさで平面をもう一つ作成します=SelectPlane<br /> 任意でマテリアルを変更しておきましょう。位置のY座標を0.01程度上げておき、MeshColliderコンポーネントを外しておきます。また、通常は非表示にしておくといいです)<br /> するとヒエラルキータブはこんな感じになるはずです。</p> <p><a href="https://crieit.now.sh/upload_images/78ca287e8ef30417ef8f6487671401eb5fc66556759b3.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/78ca287e8ef30417ef8f6487671401eb5fc66556759b3.png?mw=700" alt="ヒエラルキータブ" /></a></p> <p>そしてGridオブジェクトとTilemapオブジェクトを以下のように変更して下さい。</p> <p><a href="https://crieit.now.sh/upload_images/16c3c47de13cedc0a77e8b35a1a7fac65fc663a7ddd70.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/16c3c47de13cedc0a77e8b35a1a7fac65fc663a7ddd70.png?mw=700" alt="Gridオブジェクト" /></a></p> <p><a href="https://crieit.now.sh/upload_images/856929aa9995b605b3375464c29bb29f5fc663e0d87e1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/856929aa9995b605b3375464c29bb29f5fc663e0d87e1.png?mw=700" alt="Tilemapオブジェクト" /></a></p> <p>一応全て載せていますが、基本はGridコンポーネントのセルスウィズルを「<strong>XZY</strong>」にすることと、Tilemapコンポーネントの向きを「<strong>XZ</strong>」にすることが大事です。</p> <p>あとは任意ですがカメラの向きも変えておくと確認しやすいかもしれません。</p> <p>これでオブジェクト側の用意は終わりました。</p> <h2 id="SelectPlaneの表示/非表示"><a href="#SelectPlane%E3%81%AE%E8%A1%A8%E7%A4%BA%2F%E9%9D%9E%E8%A1%A8%E7%A4%BA">SelectPlaneの表示/非表示</a></h2> <p>それではスクリプトを書いていきます。まずは手始めにSelectPlaneの表示と非表示から。<br /> OnGridPointerスクリプトを作成して下さい。そこに以下のように記述します。</p> <pre><code>using UnityEngine; public class OnGridPointer : MonoBehaviour { [SerializeField] private GameObject selectPlane; private void OnMouseEnter() { selectPlane.SetActive(true); } private void OnMouseExit() { selectPlane.SetActive(false); } } </code></pre> <p>ビルドしたらPlaneにアタッチし、Select PlaneにSelectPlaneオブジェクトを設定しておきます。<br /> 実行してみます。以下のようにマウスカーソルが上に乗ったらSelectPlaneオブジェクトが表示され、外れたら非表示になればOKです。</p> <p><a href="https://crieit.now.sh/upload_images/43e295b50c83690e1ac8075d03e6f5675fc669b655454.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/43e295b50c83690e1ac8075d03e6f5675fc669b655454.gif?mw=700" alt="SelectPlaneの表示・非表示" /></a></p> <h2 id="マウスカーソルのあるPlane上の位置の取得"><a href="#%E3%83%9E%E3%82%A6%E3%82%B9%E3%82%AB%E3%83%BC%E3%82%BD%E3%83%AB%E3%81%AE%E3%81%82%E3%82%8BPlane%E4%B8%8A%E3%81%AE%E4%BD%8D%E7%BD%AE%E3%81%AE%E5%8F%96%E5%BE%97">マウスカーソルのあるPlane上の位置の取得</a></h2> <p>マウスカーソルのあるPlane上の位置はRaycastを飛ばすことで取得します。<br /> OnGridPointerクラスを以下のように変更します。</p> <pre><code>// メソッドを追加 private void OnEnter() { if (Camera.main == null) return; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { selectPlane.transform.position = hit.point + new Vector3(0, 0.01f, 0); } } private void OnMouseOver() { OnEnter(); } // メソッドを変更 private void OnMouseEnter() { selectPlane.SetActive(true); OnEnter(); } </code></pre> <p>試しにSelectPlaneオブジェクトの位置を変更することで実際に取得できているか確認できるようにしています。<br /> また、そのままだとPlaneオブジェクトと重なってしまってSelectPlaneオブジェクトが埋もれてしまう為、若干上に座標をずらしています。Y座標に0.01足しているのはその為です。<br /> 実行してみます。以下のようにマウスカーソル位置にSelectPlaneオブジェクトが移動すると思います。</p> <p><a href="https://crieit.now.sh/upload_images/77e8992c128b2f4fa9a6ec969a9d5c5f5fc67ad3ecbf0.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/77e8992c128b2f4fa9a6ec969a9d5c5f5fc67ad3ecbf0.gif?mw=700" alt="マウスカーソルのPlane上の位置" /></a></p> <h2 id="グリッド座標を取得し、その位置に移動"><a href="#%E3%82%B0%E3%83%AA%E3%83%83%E3%83%89%E5%BA%A7%E6%A8%99%E3%82%92%E5%8F%96%E5%BE%97%E3%81%97%E3%80%81%E3%81%9D%E3%81%AE%E4%BD%8D%E7%BD%AE%E3%81%AB%E7%A7%BB%E5%8B%95">グリッド座標を取得し、その位置に移動</a></h2> <p>最後にグリッド座標を取得し、SelectPlaneオブジェクトをその位置に移動させましょう。<br /> OnGridPointerクラスを以下のように変更します。</p> <pre><code>// スクリプトの最初に以下を追記 using UnityEngine.Tilemaps; // パラメーターを追加 [SerializeField] private Tilemap tilemap; // メソッドを変更 private void OnEnter() { if (Camera.main == null) return; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { Vector3Int gridPos = tilemap.WorldToCell(hit.point); Vector3 complementPos = new Vector3(tilemap.cellSize.x / 2, 0.01f, tilemap.cellSize.y / 2); Vector3 worldPos = tilemap.CellToWorld(gridPos) + complementPos; selectPlane.transform.position = worldPos; } } </code></pre> <p>ビルドしたら、コンポーネントのTIlemapにはTilemapオブジェクトを設定して下さい。</p> <p>一度グリッド座標に直してから再度ワールド座標に直すことで、位置を正規化しています。<br /> ただしこれで取得できるのはあくまでグリッドの左上の座標の為、1グリッドのサイズ(=tilemap.cellSize)/2を足して中央にずらす必要があります。<br /> 実行してみます。以下のようにグリッド座標単位でSelectPlaneオブジェクトが移動できればOKです。</p> <p><a href="https://crieit.now.sh/upload_images/90687487aceb0989193ade5f6ceb61585fc68206bbb55.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/90687487aceb0989193ade5f6ceb61585fc68206bbb55.gif?mw=700" alt="グリッド座標移動" /></a></p> <h1 id="終わりに"><a href="#%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB">終わりに</a></h1> <p>Unity3D上でマウスカーソルのある位置のグリッド座標を取得できました。これを応用すれば最初の画像のようにキャラクターをその位置に移動させたりといったことが可能になると思います。RPGやローグライクゲームなどにいかがでしょうか。</p> <p>最後に今回の参加について。もともとネタがなかったのでアドベントカレンダーには参加しないつもりだったんですが、もしかしたらこのネタはいいかもしれないと思い結局参加してしまいました。<br /> 検索しても出てこなかったので多分二番煎じではないと......ないといいですね。<br /> (もしネタ被りしていたらすみません......!)</p> <p>ここまでお付き合い下さり、ありがとうございました。</p> <h1 id="おまけ:最小のコード"><a href="#%E3%81%8A%E3%81%BE%E3%81%91%EF%BC%9A%E6%9C%80%E5%B0%8F%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89">おまけ:最小のコード</a></h1> <p>もしSelectPlaneを使わず、グリッド座標のみを取得したい場合、コードは以下のようになると思います。</p> <pre><code>using UnityEngine; using UnityEngine.Tilemaps; public class OnGridPointer : MonoBehaviour { [SerializeField] private Tilemap tilemap; public Vector3Int GetGridPointer() { if (Camera.main == null) return new Vector3Int(); Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { Vector3Int gridPos = tilemap.WorldToCell(hit.point); return gridPos; } return new Vector3Int(); } } </code></pre> <p>ご査収下さい。</p> みにに tag:crieit.net,2005:PublicArticle/16251 2020-12-02T23:56:15+09:00 2020-12-03T00:05:49+09:00 https://crieit.net/posts/COBOL-2 COBOLってなぁに?その2 <p>この記事は COBOL アドベントカレンダー2020 2日目の記事です。</p> <ul> <li>COBOL Advent Calendar 2020 <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2020/cobol">https://qiita.com/advent-calendar/2020/cobol</a></li> </ul></li> </ul> <p>当分は COBOL についての基本的な話をする予定なので、<br /> 多くの部分で wikipedia などの記述とカブると思います。</p> <h2 id="TL;DR"><a href="#TL%3BDR">TL;DR</a></h2> <ul> <li>COBOL | Wikipedia <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/COBOL">https://ja.wikipedia.org/wiki/COBOL</a></li> </ul></li> </ul> <h2 id="COBOL プログラムの前提となる思想"><a href="#COBOL+%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AE%E5%89%8D%E6%8F%90%E3%81%A8%E3%81%AA%E3%82%8B%E6%80%9D%E6%83%B3">COBOL プログラムの前提となる思想</a></h2> <ul> <li>COBOLってなぁに?その1 <ul> <li><a href="https://crieit.net/posts/COBOL-1">https://crieit.net/posts/COBOL-1</a></li> </ul></li> </ul> <p>でも触れましたが、<br /> COBOL は「理系ではない事務員や役人でも扱えるように作られ」ました。<br /> これは、COBOLの言語仕様の根本として考えられたものなので、<br /> これからCOBOLの言語仕様や構造について触れていく時にたびたび登場する思想です。</p> <p>Ruby on Rails の精神として "Convention over configuration" = 設定より規約 というものがありますが<br /> 設定などによって柔軟な記述ができるようにしておくよりも、<br /> 規約を定義しその規約に則って記述することで開発者の考えることの負担を少なくすることができます。</p> <ul> <li>Convention over Configuration | Ruby on Rails <ul> <li><a target="_blank" rel="nofollow noopener" href="https://rubyonrails.org/doctrine/#convention-over-configuration">https://rubyonrails.org/doctrine/#convention-over-configuration</a></li> </ul></li> </ul> <p>COBOL も「理系ではない事務員や役人でも扱えるように作られ」ているため、<br /> 現在のプログラミング言語では柔軟に記述できる部分も<br /> 見方によっては不便とも取れるほど書き方が決まっている部分があり、<br /> そのことにより理系の知識や詳しい言語仕様の把握をせずともCOBOLプログラムが書けるように<br /> 考えられているのです。</p> <p>例えば C# コードの Hello World として</p> <pre><code class="cs">using System; public class Hello{ public static void Main(){ Console.WriteLine("hello world!"); } } </code></pre> <p>このようなコードがある時、</p> <pre><code class="cs">public static void Main(){ Console.WriteLine("hello world!"); } </code></pre> <p>は Hello クラスの1行目で定義されていようが<br /> 様々なプロパティやメソッドなどの後に定義されていようが<br /> どこで定義されてあっても(どこに書いてあっても)デフォルトのエントリポイントとして機能しますが、<br /> COBOL では後述の基本構造として定義された順で記述する必要があります。<br /> これにより、「定義されている基本構造」に則った書き方で書くことで<br /> COBOLプログラミングを行う上で考えることを少なくしているのです。</p> <h2 id="COBOL プログラムの1行ごとの書き方"><a href="#COBOL+%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AE1%E8%A1%8C%E3%81%94%E3%81%A8%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9">COBOL プログラムの1行ごとの書き方</a></h2> <p>では早速 COBOL プログラムの構文定義に触れていきましょう。</p> <p>まず、今回は COBOL プログラムで定義されている1行ごとのルールについてお話します。<br /> 多くのプログラミング言語ではタブの数や予約語について書き順は柔軟であることが多いですが、<br /> COBOL では1行ごとの書き方についても定義があります。<br /> (Python などではインデントでブロックを定義するので似たような考え方とも言えますね)</p> <ul> <li>インデント | Python 3.9.1rc1 ドキュメント <ul> <li><a target="_blank" rel="nofollow noopener" href="https://docs.python.org/ja/3/reference/lexical_analysis.html#indentation">https://docs.python.org/ja/3/reference/lexical_analysis.html#indentation</a></li> </ul></li> </ul> <p>また、COBOLプログラムの1行ごとのルールが決まっている理由は<br /> COBOLプログラムは昔パンチカードという紙に記述していたところにも起因しています。<br /> コンピュータに直接テキストファイルとしてプログラミングを行う現在のプログラミング言語と異なり、<br /> パンチカードという紙にコードを記述していたがために<br /> 行ごとのルールが存在しないと簡単に記述量超過(ホールドオーバー)が起きてしまったんですね。</p> <p>なお、以下に出てくる「n列目」という記載は<br /> COBOL プログラムにおける領域の単位となりますが、<br /> 少なくとも7列目までは「7文字目」までと同一と考えてもらって問題ありません。</p> <h3 id="1列目〜6列目 シーケンシャル番号"><a href="#1%E5%88%97%E7%9B%AE%E3%80%9C6%E5%88%97%E7%9B%AE+%E3%82%B7%E3%83%BC%E3%82%B1%E3%83%B3%E3%82%B7%E3%83%A3%E3%83%AB%E7%95%AA%E5%8F%B7">1列目〜6列目 シーケンシャル番号</a></h3> <p>COBOL プログラムにおける<br /> 各行の識別を行うためのシーケンシャル番号を6桁で記述します。<br /> 現代のプログラミング言語ではエラーが発生した時に<br /> プログラムファイルの何行目でエラーですよ、などというエラー出力があるのが一般的ですが、<br /> COBOL が考えられた当時はそのような親切なものはなく、自前で用意していました。<br /> そのため、途中でプログラムが停止した時などにどの位置かを理解しやすくなるために<br /> 各行の頭にシーケンシャル番号を記述したのです。<br /> シーケンシャル番号自体の記述ルールはありません。</p> <p>例:</p> <pre><code>000001 000002 000003 000004 000005 </code></pre> <p>ただし、1ずつ加算するシーケンシャル番号の場合は<br /> プログラムの修正などで途中行に追記が発生した場合、上や下の行と同じ番号を振るなど<br /> 破綻したシーケンシャル番号の記述が発生してしまうので、<br /> 最初にプログラムを書くときは慣例として10桁や100桁ごとのシーケンシャル番号を振ることが多いようです。</p> <p>例:</p> <pre><code>000100 000200 000300 000400 000500 000600 </code></pre> <p>なお、この記述は大変冗長であるため<br /> 昨今のCOBOLコンパイラでは不要とされています。<br /> いきなり最初から今は不要、みたいな定義になってしまいましたね……。</p> <h3 id="7列目 標識"><a href="#7%E5%88%97%E7%9B%AE+%E6%A8%99%E8%AD%98">7列目 標識</a></h3> <p>COBOL プログラムの7文字目はコードの標識を記述する部分となります。<br /> 多くの場合はスペースを記述しますが、<code>*</code> を記述することでその行はコメント行となります。<br /> また、<code>-</code> を記述すると前の行の単語またはリテラルが継続という扱いになります。</p> <p>例:</p> <pre><code>000100**************************** 000200* ここはコメント 000300**************************** 000400 000500 000600 </code></pre> <h3 id="8列目〜11列目、12列目〜72列目 コード記述領域"><a href="#8%E5%88%97%E7%9B%AE%E3%80%9C11%E5%88%97%E7%9B%AE%E3%80%8112%E5%88%97%E7%9B%AE%E3%80%9C72%E5%88%97%E7%9B%AE+%E3%82%B3%E3%83%BC%E3%83%89%E8%A8%98%E8%BF%B0%E9%A0%98%E5%9F%9F">8列目〜11列目、12列目〜72列目 コード記述領域</a></h3> <p>COBOL プログラムを記述します。</p> <p>例:</p> <pre><code>000100**************************** 000200* ここはコメント 000300**************************** 000400 IDENTIFICATION DIVISION. 000500 PROGRAM-ID. COBOL_SAMPLE01. 000600 </code></pre> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>今回は COBOL の1行ごとの記述方法についてお話しました。<br /> サンプルの最後にはいよいよ COBOL プログラムが出て来ましたね。<br /> 明日は、最後のサンプルに書いた <code>IDENTIFICATION DIVISION</code> など<br /> COBOL の基本構文について触れていきましょう。</p> 北山淳也 tag:crieit.net,2005:PublicArticle/16250 2020-12-02T23:50:23+09:00 2020-12-02T23:53:04+09:00 https://crieit.net/posts/bootstrap-paint-in-variable-20201202 Dart Sass (@use, @forward 使用)で Bootstrap 4 の変数やマップを上書きする <p>Dart Sass (<code>@use</code>, <code>@forward</code> 使用)で Bootstrap 4 の変数・マップを上書きしたくなったので実験してみました。</p> <h2 id="今まで (node-sass / LibSass)"><a href="#%E4%BB%8A%E3%81%BE%E3%81%A7+%28node-sass+%2F+LibSass%29">今まで (node-sass / LibSass)</a></h2> <h3 id="前提"><a href="#%E5%89%8D%E6%8F%90">前提</a></h3> <pre><code class="bash"> / └ src/ ├ html/ │ └ index.html │ └ scss/ ├ assets/ │ └ bootstrap/ │ │ └ bootstrap/ │ │ └ 略 │ └ bootstrap.scss │ ├ foundation/ │ ├ _index.scss │ ├ _mixin.scss │ └ _variables.scss │ ├ layout/ │ ├ _l-footer.scss │ ├ _l-header.scss │ └ _l-main.scss │ ├ object/ │ ├ component/ │ │ └ 略 │ ├ project/ │ │ └ 略 │ └ utility/ │ └ 略 │ └ index.scss </code></pre> <p>今回の検証で使用するプロジェクトのディレクトリ構造は、このような状態とします。</p> <h3 id="src/html/index.html"><a href="#src%2Fhtml%2Findex.html">src/html/index.html</a></h3> <pre><code class="html"><div class="mt-4"> <a href="#" class="btn btn-primary m-3">プライマリーボタン</a><!-- プライマリーカラーのボタン --> <a href="#" class="btn btn-main m-3">メインボタン</a><!-- デフォルトにはないボタン --> </div> </code></pre> <p>例として、こんな HTML があったとして。</p> <h3 id="src/scss/foundation/_scss_variable.scss"><a href="#src%2Fscss%2Ffoundation%2F_scss_variable.scss">src/scss/foundation/_scss_variable.scss</a></h3> <pre><code class="scss">$theme-colors: ( /* 上書き */ primary: $own-color, /* デフォルトにないカラーの追加 */ main: $own-main-color, ); </code></pre> <p>マップの定義を上書きするコードを書きます。</p> <h3 id="src/scss/foundation/_index.scss"><a href="#src%2Fscss%2Ffoundation%2F_index.scss">src/scss/foundation/_index.scss</a></h3> <pre><code class="scss">@import "variables"; //変数(Bootstrap の変数上書きのコードあり) @import "mixin"; @import "../assets/bootstrap/bootstrap"; //bootstrap </code></pre> <p>同じ <code>foundation</code> の中に読み込み用の Scss を用意します。</p> <p>注意する点としては、「 Bootstrap の Scss を読み込むより前に、 Bootstrap の変数(マップ)を上書きするための自前の定義が書かれた Scss を読み込む」ということ。</p> <h3 id="src/scss/index.scss"><a href="#src%2Fscss%2Findex.scss">src/scss/index.scss</a></h3> <pre><code class="scss">@import "./foundation/index"; //読み込み </code></pre> <p>最後に、実際に <code>index.css</code> にコンパイルされる <code>src/scss/index.scss</code> で <code>src/scss/foundation/_index.scss</code> を読み込みます。</p> <p>今までは、これで変数の上書きができました。</p> <h2>Dart Sass での検証1 (失敗 / 単純に <code>@import</code> を <code>@use</code>, <code>@forward</code> に書き換え)</h2> <p>さて、ここで単純に今まで <code>@import</code> で記述していたのを <code>@use</code>, <code>@forward</code> に書き換えてみます。</p> <h3 id="src/scss/foundation/_scss_variable.scss"><a href="#src%2Fscss%2Ffoundation%2F_scss_variable.scss">src/scss/foundation/_scss_variable.scss</a></h3> <pre><code class="scss">$theme-colors: ( /* 上書き */ primary: $own-color, /* デフォルトにないカラーの追加 */ main: $own-main-color, ); </code></pre> <p>上書きしたい変数(マップ)の定義はそのまま。</p> <h3 id="src/scss/foundation/_index.scss"><a href="#src%2Fscss%2Ffoundation%2F_index.scss">src/scss/foundation/_index.scss</a></h3> <pre><code class="scss">@forward "scss_variables"; //変数(Bootstrap の変数上書きのコードあり) @forward "mixin"; @forward "../assets/bootstrap/bootstrap"; //bootstrap </code></pre> <p>今度は <code>@import</code> ではなく、 <code>@forward</code> に書き換えます。</p> <h3 id="src/scss/layout/_l-header.scss"><a href="#src%2Fscss%2Flayout%2F_l-header.scss">src/scss/layout/_l-header.scss</a></h3> <pre><code class="scss">@use "../foundation" as f; .l-header { .navbar-brand { &, &:link, &:visited { color: f.$own-main-color; } &:hover, &:active, &:focus { color: f.$own-main-color_l; } } } // 略 </code></pre> <p>実際に <code>src/scss/foundation/_index.scss</code> を読み込んで使用する Scss で <code>@use</code> による読み込みを行います。</p> <h3 id="src/scss/index.scss"><a href="#src%2Fscss%2Findex.scss">src/scss/index.scss</a></h3> <pre><code class="scss">@use "layout/l-header"; //_l-header.scss の中で @use を使って src/scss/foundation/_index.scss を読み込み、使用 @use "layout/l-main"; @use "layout/l-footer"; </code></pre> <p>こんな形に書き換えます。</p> <p>流れとしては、「<code>src/scss/index.scss</code> -(<code>@use</code>)-> <code>src/scss/layout/_l-header.scss</code> -(<code>@forward</code>)-> <code>src/scss/foundation/_index.scss</code>」という関係。</p> <p>この状態で Dart Sass によるコンパイルを実行すると、<code>$theme-colors</code> の変数名が重複しているのでエラーになってしまいます。</p> <pre><code class="bash">Error: src/scss/foundation/_index.scss Error: Two forwarded modules both define a variable named $theme-colors. ╷ 3 │ @forward "variables"; │ ━━━━━━━━━━━━━━━━━━━━ original @forward ... │ 5 │ @forward "../assets/bootstrap/bootstrap"; //bootstrap │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ new @forward ╵ src/scss/foundation/_index.scss 5:1 @use src/scss/layout/_l-header.scss 3:1 @use src/scss/index.scss 8:1 root stylesheet </code></pre> <p>回避するためにはいくつかの方法が考えられますが、 Bootstrap 4 の <code>@import</code> を書き換えていくのは大変な上に既存ライブラリに手を入れるのはできれば避けたいので今回は不採用。</p> <p>他の方法としては <code>with</code> を使う方法が考えられます。</p> <p>ただし、これもいくつか工夫が必要です。</p> <h2>Dart Sass での検証2 (失敗 / <code>@foward</code> ~ <code>with</code>)</h2> <p>安直に <code>with</code> を使おうかと考えましたが、構文的に <code>with</code> は <code>@use</code> でしか使えません。</p> <p>そのため、以下のような書き換えは不可です。</p> <h3 id="src/scss/foundation/_index.scss"><a href="#src%2Fscss%2Ffoundation%2F_index.scss">src/scss/foundation/_index.scss</a></h3> <pre><code class="scss">@forward "../assets/bootstrap/bootstrap" with ( /* with の中では use で読み込んだファイルのスコープは使えない */ $theme-colors: ( /* 上書き */ primary: #333, /* デフォルトにないカラーの追加 */ main: red, ), ); //bootstrap </code></pre> <h2 id="Dart Sass での検証3 (失敗 / ファイルスコープ)"><a href="#Dart+Sass+%E3%81%A7%E3%81%AE%E6%A4%9C%E8%A8%BC3+%28%E5%A4%B1%E6%95%97+%2F+%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97%29">Dart Sass での検証3 (失敗 / ファイルスコープ)</a></h2> <p><code>@use</code> とセットで使うということが分かったので、今度は <code>@use</code> で読み込んでいる場所で <code>with</code> を付けたそうと思いました。</p> <h3 id="src/scss/layout/_l-header.scss"><a href="#src%2Fscss%2Flayout%2F_l-header.scss">src/scss/layout/_l-header.scss</a></h3> <pre><code class="scss">@use "../foundation" as f with ( $theme-colors: ( /* 上書き */ primary: f.$own-color, /* デフォルトにないカラーの追加 */ main: f.$own-main-color, ), ); .l-header { .navbar-brand { &, &:link, &:visited { color: f.$own-main-color; } &:hover, &:active, &:focus { color: f.$own-main-color_l; } } } // 略 </code></pre> <p>ただし、このような既述をすると以下のエラーが発生します。</p> <pre><code class="bash">Error: src/scss/layout/_l-header.scss Error: There is no module with the namespace "f". ╷ 7 │ primary: f.$own-color, │ ^^^^^^^^^^^^^ ╵ src/scss/layout/_l-header.scss 7:18 @use src/scss/index.scss 8:1 root stylesheet </code></pre> <p><code>with</code> の中では、その直前で既述した名前空間はまだ使えません。</p> <p>「独自に定義したメインカラーでプライマリーカラーを上書きしたい」というケースが多いと思われるので、 <code>with</code> で上書きするときにメインカラーが変数で指定できないのは困ります。</p> <p>そこで、独自定義の変数読み込みと Bootstrap 4 の読み込み部分を分けることにしました。</p> <h2 id="最終形"><a href="#%E6%9C%80%E7%B5%82%E5%BD%A2">最終形</a></h2> <h3 id="ディレクトリ構造"><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E9%80%A0">ディレクトリ構造</a></h3> <pre><code class="bash"> / └ src/ ├ html/ │ └ index.html │ └ scss/ ├ assets/ │ └ bootstrap/ │ │ └ bootstrap/ │ │ └ 略 │ └ bootstrap.scss │ ├ foundation/ │ ├ _index.scss │ ├ _mixin.scss │ └ _variables.scss │ ├ global/ │ └ _index.scss # 追加 │ ├ layout/ │ ├ _l-footer.scss │ ├ _l-header.scss │ └ _l-main.scss │ ├ object/ │ ├ component/ │ │ └ 略 │ ├ project/ │ │ └ 略 │ └ utility/ │ └ 略 │ └ index.scss </code></pre> <h3 id="src/scss/foundation/_scss_variable.scss"><a href="#src%2Fscss%2Ffoundation%2F_scss_variable.scss">src/scss/foundation/_scss_variable.scss</a></h3> <p>先程まであった上書き用のコードは削除。</p> <h3 id="src/scss/foundation/_index.scss"><a href="#src%2Fscss%2Ffoundation%2F_index.scss">src/scss/foundation/_index.scss</a></h3> <pre><code class="scss">@forward "variables"; @forward "mixin"; </code></pre> <p>Bootstrap 4 を読み込む <code>@forward</code> を削除。</p> <h3 id="src/scss/global/_index.scss"><a href="#src%2Fscss%2Fglobal%2F_index.scss">src/scss/global/_index.scss</a></h3> <pre><code class="scss">@forward "../assets/bootstrap/bootstrap"; //bootstrap </code></pre> <p>Bootstrap 4 を読み込む部分を切り出したのを、 <code>src/scss/global/_index.scss</code> とします。</p> <h3 id="src/scss/layout/_l-header.scss"><a href="#src%2Fscss%2Flayout%2F_l-header.scss">src/scss/layout/_l-header.scss</a></h3> <pre><code class="scss">@use "../foundation" as f; @use "../global" as g with ( $theme-colors: ( /* 上書き */ primary: f.$own-color, /* デフォルトにないカラーの追加 */ main: f.$own-main-color, ), ); .l-header { .navbar-brand { &, &:link, &:visited { color: f.$own-main-color; } &:hover, &:active, &:focus { color: f.$own-main-color_l; } } } // 略 </code></pre> <p>実際に使う部分。変数は <code>foundation</code>, Bootstrap 4 は <code>global</code> で読み込み、 <code>foundation</code> を先に読み込むことで、 <code>with</code> の中で使用できるようにしました。</p> <h3 id="src/scss/index.scss"><a href="#src%2Fscss%2Findex.scss">src/scss/index.scss</a></h3> <pre><code class="scss">@use "layout/l-header"; @use "layout/l-main"; @use "layout/l-footer"; </code></pre> <p><code>index.css</code> になる部分は読み込みだけ。</p> <p>この形にすることでようやく当初意図していた形にすることができました。</p> <h2 id="備考1"><a href="#%E5%82%99%E8%80%831">備考1</a></h2> <p>今回の方法では Bootstrap 4 の中身は一切触れない方向で実装しました。そのため、 Bootstrap 4 の中は以前として <code>@important</code> で読み込まれており、変数はグローバルスコープに定義されるようです。</p> <p>そのため、今回は <code>src/scss/layout/_l-header.scss</code> でしか <code>with</code> を使用していませんが……</p> <p><a href="https://crieit.now.sh/upload_images/3415ce329faf1c572815489dd4a732555fc7a9865a365.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3415ce329faf1c572815489dd4a732555fc7a9865a365.jpg?mw=700" alt="デフォルト状態のサンプル" /></a></p> <p>デフォルト状態ではこのような状態になります(プライマリーカラーがデフォルト、右側はデフォルト状態では存在しない <code>btn-main</code> クラスが付与されているため背景色なし)。</p> <p><a href="https://crieit.now.sh/upload_images/bf9bc07305297db9738d5598f4d950bd5fc7a98f8206f.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/bf9bc07305297db9738d5598f4d950bd5fc7a98f8206f.jpg?mw=700" alt="適用状態のサンプル" /></a></p> <p><code>src/scss/layout/_l-header.scss</code> に <code>with</code> を適用しただけで、メイン部分のボタンも影響を受けます。今回の場合は「全体で色を変更したい」のでこれで良いのですが、 <code>@use</code> や <code>@forward</code> の位置付けからすると役割を発揮できていない状態なので微妙なところ。</p> <p>今後、 Bootstrap が <code>@use</code>, <code>@forward</code> に変更した場合にこの辺りの挙動は変わると思われます。</p> <p>※ちなみに、v5.0.0-alpha3 でもまだ <code>@import</code> でした。</p> <h2 id="備考2"><a href="#%E5%82%99%E8%80%832">備考2</a></h2> <p>備考1と関連しますが、グローバルスコープに定義されるということは……</p> <h3 id="src/scss/layout/_l-header.scss"><a href="#src%2Fscss%2Flayout%2F_l-header.scss">src/scss/layout/_l-header.scss</a></h3> <pre><code class="scss">@use "../foundation" as f; @use "../global" as g; .l-header { .navbar-brand { &, &:link, &:visited { color: f.$own-main-color; } &:hover, &:active, &:focus { color: f.$own-main-color_l; } } } // 略 </code></pre> <p><code>src/scss/layout/_l-header.scss</code> は普通に <code>src/scss/global/_index.scss</code> を読み込み……</p> <h3 id="src/scss/layout/_l-main.scss"><a href="#src%2Fscss%2Flayout%2F_l-main.scss">src/scss/layout/_l-main.scss</a></h3> <pre><code class="scss">@use "../foundation" as f; @use "../global" as g with ( $theme-colors: ( /* 上書き */ primary: f.$own-color, /* デフォルトにないカラーの追加 */ main: f.$own-main-color, ), ); .l-main { background-color: f.$own-bg-color; color: f.$own-color; .btn-main { color: f.$own-color; } } // 略 </code></pre> <p><code>src/scss/layout/_l-header.scss</code> の後に読み込まれる <code>src/scss/layout/_l-main.scss</code> で <code>with</code> を使用した場合、以下のエラーが発生します。一度読み込んだモジュールが <code>with</code> 使用で再度読み込まれている、という旨のエラーですね。</p> <pre><code class="bash">Error: This module was already loaded, so it can't be configured using "with". ┌──> src/scss/layout/_l-main.scss 4 │ ┌ @use "../global" as g with ( 5 │ │ $theme-colors: ( 6 │ │ /* 上書き */ 7 │ │ primary: f.$own-color, 8 │ │ /* デフォルトにないカラーの追加 */ 9 │ │ main: f.$own-main-color, 10│ │ ), 11│ │ ); │ └─^ new load ╵ ┌──> src/scss/layout/_l-header.scss 4 │ @use "../global" as g; │ ━━━━━━━━━━━━━━━━━━━━━ original load ╵ src/scss/layout/_l-main.scss 4:1 @use src/scss/index.scss 9:1 root stylesheet Error: src/scss/layout/_l-main.scss Error: This module was already loaded, so it can't be configured using "with". ┌──> src/scss/layout/_l-main.scss 4 │ ┌ @use "../global" as g with ( 5 │ │ $theme-colors: ( 6 │ │ /* 上書き */ 7 │ │ primary: f.$own-color, 8 │ │ /* デフォルトにないカラーの追加 */ 9 │ │ main: f.$own-main-color, 10│ │ ), 11│ │ ); │ └─^ new load ╵ ┌──> src/scss/layout/_l-header.scss 4 │ @use "../global" as g; │ ━━━━━━━━━━━━━━━━━━━━━ original load ╵ src/scss/layout/_l-main.scss 4:1 @use src/scss/index.scss 9:1 root stylesheet </code></pre> <p>そのため、 <code>with</code> を使用するならば全体を通して <code>src/scss/global/_index.scss</code> が最初に <code>@use</code> で読み込まれる部分に既述する必要があります。</p> <hr /> <p>以上、知らないと全体的に嵌まり所が多い感じだったのでメモしておきます。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="with"><a href="#with">with</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.boel.co.jp/tips/vol114/">Sassの新しいモジュールシステム | BOEL Inc. | ブランディング&デザインファーム</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://kojika17.com/2020/05/next-generation-sass-module-system.html">Sassを@importから@useに置き換えるための手引き | Web Design KOJIKA17</a></li> </ul> <h3 id="今までのやり方 (参考)"><a href="#%E4%BB%8A%E3%81%BE%E3%81%A7%E3%81%AE%E3%82%84%E3%82%8A%E6%96%B9+%28%E5%8F%82%E8%80%83%29">今までのやり方 (参考)</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://homupedia.com/bootstrap4-how-to-customize-theme.html">Bootstrap4のテーマをカスタマイズする3つの簡単な方法 | ホムペディア</a></li> </ul> <h3 id="Bootstrap (v5.0.0-alpha3)"><a href="#Bootstrap+%28v5.0.0-alpha3%29">Bootstrap (v5.0.0-alpha3)</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/twbs/bootstrap/tree/v5.0.0-alpha3">twbs/bootstrap at v5.0.0-alpha3</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16249 2020-12-02T07:00:10+09:00 2020-12-02T14:27:24+09:00 https://crieit.net/posts/Qiita-5fc6bcea9e658 [供養] Qiita の記事に投げ銭サービスを仕込むハックでやらかした話 <p>人生はじめての Advent Calendar に挑戦です!今年は<strong>挑戦を恐れず色々やってきました</strong>が、その中で「あー、やっちった~」的なものを1つご紹介して私の中で供養しようかと思います。</p> <p>※ なお、<strong>具体的なやり方は記載しません</strong>が、やり方を想像できる方もいらっしゃると思います。<strong>真似なさらないようにお願いします</strong>🙇‍</p> <h2 id="序章"><a href="#%E5%BA%8F%E7%AB%A0">序章</a></h2> <p><code>ブログを収益化できる投げ銭サービス</code>というものが世の中にはあって、それを使って「何か面白いことできないかな~」と純粋な気持ちで考えていました。「自分のブログには既に広告を貼ってるし、あー Qiita か・・・」</p> <p>(この時の私は『Qiita で投げ銭するのは多分グレーだろうな~あとで利用規約確認するとして、とりあえず技術的に実現できるのか試してみよう』などと思っていました)</p> <p><strong>一旦それぞれの仕様をまとめてみます</strong></p> <ul> <li>投げ銭サービスは iframe タグと script タグを実行できれば使える<br /> (自分のブログに iframe, script タグを張り付けることを想定している)</li> <li>Qiita には CodePen を埋め込める</li> </ul> <p>この2つの情報があったので、もはや試さずにはいられませんでした。ええ、<strong>Qiita 上で投げ銭のボタンを表示することに成功</strong>してしまいました👺</p> <h2 id="やらかし (〃´∪`〃)ゞ"><a href="#%E3%82%84%E3%82%89%E3%81%8B%E3%81%97+%28%E3%80%83%C2%B4%E2%88%AA%EF%BD%80%E3%80%83%29%E3%82%9E">やらかし (〃´∪`〃)ゞ</a></h2> <p>『Qiita 投げ銭』というタイトルで具体的な方法を記載したうえで Qiita に投稿しました。<br /> その数分後に1通のメールが届くことになります。内容は Qiita でコメントされたと。コメントの内容は・・・</p> <blockquote> <p>Qiitaの利用規約違反になりませんか?</p> </blockquote> <p>と。(この時コメントくださった方。本当にありがとうございました)<br /> <strong>記事を一旦削除</strong>した上で利用規約を再度確認してみました。以下に該当部分を抜粋します。</p> <p><a target="_blank" rel="nofollow noopener" href="https://qiita.com/terms">https://qiita.com/terms</a></p> <blockquote> <p>第三章:禁止事項・本ユーザー資格の停止について<br /> 第10条(禁止事項)<br /> 3. 登録ユーザーは、本サービスを利用するに際し、以下のような本サービス利用上不適切な行為を行ってはなりません。<br /> (4)客観的には<strong>事実上、広告・宣伝や商用を目的とした</strong>勧誘と認められる行為(検索サイト最適化又はアフィリエイトを目的とする投稿行為を含むがこれに限らない)。ただし、当社が本サービス上で別途定める場合はその限りではありません。</p> </blockquote> <p>なるほどよくわからん。『事実上…商用を目的とした…』に当たりそうなような当たらなそうな・・・</p> <h2 id="運営さんに問い合わせ"><a href="#%E9%81%8B%E5%96%B6%E3%81%95%E3%82%93%E3%81%AB%E5%95%8F%E3%81%84%E5%90%88%E3%82%8F%E3%81%9B">運営さんに問い合わせ</a></h2> <p>わかないことは聴くのが一番早いので、<strong>Qiita 運営に問合せ</strong>てみました。<br /> 以下送った内容です。</p> <blockquote> <p><strong>Qiita の記事内に CodePen を利用して投げ銭サービスを組み込むことは規約違反でしょうか?</strong></p> <p>規約を確認する限り判断できかねるため Qiita 運営の判断を仰ぎたいと思いました。</p> <p>参考までに以下の URL で実際の動作を確認できるように「限定公開中」にしております。問題があればすぐに削除いたします。<br /> https://qiita.com/alclimb/private/********</p> <p>(以下、対象規約部分を抜粋)<br /> 第三章:禁止事項・本ユーザー資格の停止について<br /> 第10条(禁止事項)<br /> 3. 登録ユーザーは、本サービスを利用するに際し、以下のような本サービス利用上不適切な行為を行ってはなりません。<br /> (4)客観的には事実上、広告・宣伝や商用を目的とした勧誘と認められる行為(検索サイト最適化又はアフィリエイトを目的とする投稿行為を含むがこれに限らない)。ただし、当社が本サービス上で別途定める場合はその限りではありません。</p> <p>--<br /> 以上です。お手数ですがよろしくお願いいたします。</p> </blockquote> <p>夕方 18:40 頃に問い合わせして、40 分後の 19:20 頃には <strong>Qiita サポートから回答が来てました</strong>。(はやっ)<br /> <strong>回答の一部のみ引用</strong>すると、</p> <blockquote> <p>今後状況が変わる可能性も0ではありませんが、少なくとも現時点では利用規約違反となります。</p> </blockquote> <p>とのことでした。はい<strong>利用規約違反</strong>でした。申し訳ございませんでしたー<br /> 今後状況が変わる可能性も0ではないとの事なので今後に期待したいです。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <ul> <li>Qiita で投げ銭は技術的に可能だけど利用規約的に NG</li> <li>Qiita サポートは回答が早くユーザーとしては嬉しかった。個人開発としても見習いたい部分</li> <li>失敗は何らかの形で供養するとセルフ反省会にもなるのでおススメ</li> </ul> <p>きっと来年もいっぱい失敗します。でも『とにかくやる!』で精進していきたいと思います✨</p> <hr /> <p>明日の Advent Calendar 3日目は <a href="https://crieit.net/users/frabbit_mbp">みにに</a> さんです!よろしくお願いします</p> hikaru🐧 tag:crieit.net,2005:PublicArticle/16248 2020-12-01T23:54:56+09:00 2020-12-01T23:54:56+09:00 https://crieit.net/posts/COBOL-1 COBOLってなぁに?その1 <p>この記事は COBOL アドベントカレンダー2020 1日目の記事です。</p> <ul> <li>COBOL Advent Calendar 2020 <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2020/cobol">https://qiita.com/advent-calendar/2020/cobol</a></li> </ul></li> </ul> <p>当分は COBOL についての基本的な話をする予定なので、<br /> 多くの部分で wikipedia などの記述とカブると思います。</p> <h2 id="TL;DR"><a href="#TL%3BDR">TL;DR</a></h2> <ul> <li>COBOL | Wikipedia <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/COBOL">https://ja.wikipedia.org/wiki/COBOL</a></li> </ul></li> </ul> <h2 id="COBOL とは?"><a href="#COBOL+%E3%81%A8%E3%81%AF%EF%BC%9F">COBOL とは?</a></h2> <p>COBOL とはプログラミング言語の1つです。<br /> "Common Business Oriented Language" = 共通 事務処理向けの 言語 という名のように、<br /> 開発年といわれている 1959年当時にコンピューターで事務処理をするための<br /> 専用言語として登場したプログラミング言語です。<br /> アメリカ国防総省主導で開発されました。</p> <h2 id="事務処理向けとは"><a href="#%E4%BA%8B%E5%8B%99%E5%87%A6%E7%90%86%E5%90%91%E3%81%91%E3%81%A8%E3%81%AF">事務処理向けとは</a></h2> <p>「事務処理向け」というのはどういうことかというと、<br /> COBOLが持っている機能を挙げることで少し理解することができます。</p> <ul> <li>ファイルの読み込み</li> <li>データの順次処理</li> <li>データのソート</li> <li>データをまとめる処理(マージ)</li> <li>大量のデータのファイルへ順次書き込み</li> <li>帳票の作成</li> <li>印刷</li> <li>データを10進数で扱うことによる簡易で正確な金額計算</li> </ul> <p>現代のモダンなプログラミング言語はもちろんこれらの言語機能を有しており、<br /> COBOLが「事務処理向け」であると言われてもピンと来ないかもしれません。<br /> しかし、1959年当時はこのような機能を揃えた言語は少なかったのでしょう。<br /> (そもそもプログラミング言語の歴史を紐解くと、<br />  1959年当時は現在よりも遥かにプログラミング言語の種類自体が少ない状況にありました)</p> <p>また、事務処理向けとしてCOBOLの目指したものとして言語仕様を強く方向付けたものとして</p> <ul> <li>理系ではない事務員や役人でも扱えるように作られた</li> </ul> <p>ということがあります。</p> <h2 id="理系ではない事務員や役人でも扱えるように とは"><a href="#%E7%90%86%E7%B3%BB%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84%E4%BA%8B%E5%8B%99%E5%93%A1%E3%82%84%E5%BD%B9%E4%BA%BA%E3%81%A7%E3%82%82%E6%89%B1%E3%81%88%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB+%E3%81%A8%E3%81%AF">理系ではない事務員や役人でも扱えるように とは</a></h2> <p>日本語ネイティブ話者であれば、日本語で記述できるプログラミング言語である<br /> 「なでしこ」や「ひまわり」と合わせて見てみるとイメージしやすいのですが、<br /> 自然言語に近い記法(COBOL の場合は英語)で記述できるというのは、<br /> それだけで記述のハードルが下がるものです。</p> <p>また、ブロックを組み合わせるようにして開発する「Scratch」を使ってみるとわかるのですが、<br /> 数学やアルゴリズムの知識を持ち合わせていなくても、<br /> 構文上の約束事さえ覚えて記述することができればプログラムを組むことは可能です。</p> <p>COBOLは、</p> <ul> <li>自然言語に近い記法で記述できる</li> <li>数学やアルゴリズムの知識を持ち合わせていなくても記述できる</li> </ul> <p>という点を抑えることで「理系ではない事務員や役人でも扱えるように」考えられています。</p> <p>では実際にどういう言語記述をしていくのか?<br /> 明日以降はもう少しCOBOLでの記述も合わせて、見ていきましょう。</p> <h2 id="参考リンク"><a href="#%E5%8F%82%E8%80%83%E3%83%AA%E3%83%B3%E3%82%AF">参考リンク</a></h2> <ul> <li>日本語プログラミング言語 なでしこ <ul> <li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/top/">https://nadesi.com/top/</a></li> </ul></li> <li>日本語プログラミング言語 ひまわり <ul> <li><a target="_blank" rel="nofollow noopener" href="https://kujirahand.com/himawari/">https://kujirahand.com/himawari/</a></li> </ul></li> <li>プログラミング言語 Scratch(スクラッチ) <ul> <li><a target="_blank" rel="nofollow noopener" href="https://scratch.mit.edu/">https://scratch.mit.edu/</a></li> </ul></li> </ul> 北山淳也 tag:crieit.net,2005:PublicArticle/16247 2020-12-01T15:38:37+09:00 2020-12-01T15:38:37+09:00 https://crieit.net/posts/cb016f5196a5c0cbc01932301054fbd1 ランキング動画の作り方 <p><a href="https://crieit.now.sh/upload_images/525398b35fcb4fe3cac3c3e909b1f62f5fc5e2e9d24b1.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/525398b35fcb4fe3cac3c3e909b1f62f5fc5e2e9d24b1.gif?mw=700" alt="image" /></a></p> <p><a target="_blank" rel="nofollow noopener" href="https://dnjiro.hatenablog.com/entry/ranking">このランキング動画の作り方の記事を投稿</a></p> Danjiro Daiwa tag:crieit.net,2005:PublicArticle/16246 2020-12-01T07:53:54+09:00 2020-12-01T07:53:54+09:00 https://crieit.net/posts/44254b982415d997f38677b11db20fc2 今年の総括。スマホアプリに力を入れたこととか。 <p>年末ということで今年やったことの総括的なものです。今年はサービスとアプリを13個ほど作りました。うち6つはイベントで作ったものやネタで作った小さなものです。それ以外のちゃんと作ろうと思って作ったもののうち、Webサービスは2つ、スマホアプリは5つでした。ということで今年はスマホアプリ開発を再開し、そちらに力を入れた年でした。</p> <h2 id="アプリ作りを再開した理由"><a href="#%E3%82%A2%E3%83%97%E3%83%AA%E4%BD%9C%E3%82%8A%E3%82%92%E5%86%8D%E9%96%8B%E3%81%97%E3%81%9F%E7%90%86%E7%94%B1">アプリ作りを再開した理由</a></h2> <p>アプリ作りを再開した理由はいくつかあります。</p> <h3 id="良い開発ツールが整ってきた"><a href="#%E8%89%AF%E3%81%84%E9%96%8B%E7%99%BA%E3%83%84%E3%83%BC%E3%83%AB%E3%81%8C%E6%95%B4%E3%81%A3%E3%81%A6%E3%81%8D%E3%81%9F">良い開発ツールが整ってきた</a></h3> <p>アプリを作るのに実用的な開発ツールが整ってきたことが一つの理由です。ゲームにはライセンス料なしのGodot、それ以外にはFlutterを使うことでクロスプラットフォームのスマホアプリを作ることができます。どちらも非常に開発しやすく、実際に今年6つのアプリをAndroid, iOS用の両方にリリースできました。</p> <p>初めてゲームを作ったのはもういつかわからないのですが、日本でauの実用的なAndroidのスマホが発売されたあたりでした。ゲームエンジンもなく、OpenGLで作っていました。当時はマーケティングもASOも何のノウハウも分からず、開発が大変だったのにもかかわらず全く利用されませんでした。</p> <p>最後に作ったのは多分5年前くらいで、cocos2dxを使ってJavaScriptでクロスプラットフォームで作ることができるようになっていました。とはいえまだまだネイティブでガリガリ書く必要があったり、よくわからないエラーでアプリが落ちる様になってしまったりと、気楽という感じではありませんでした。</p> <p>ここに来てようやく十分なものが整ってきたな、という感じがします。</p> <h3 id="Webより稼げそうな気がした"><a href="#Web%E3%82%88%E3%82%8A%E7%A8%BC%E3%81%92%E3%81%9D%E3%81%86%E3%81%AA%E6%B0%97%E3%81%8C%E3%81%97%E3%81%9F">Webより稼げそうな気がした</a></h3> <p>Webをずっとやってきましたが、未だに不労所得でウハウハなんて状況にはなっていません。Twitterの個人開発者で稼いでいる人を見ると、大体がスマホアプリ開発者です。もちろんその方たちのアプリのクオリティやノウハウが素晴らしいことであることは色々聞く限り間違いはないのですが、そもそもの稼げるようになるまでの敷居がWebサービスは高すぎるのでは、という気にもなってきました。</p> <p>アプリは広告以外にも課金機能も用意されていますし、広告もゲームであればリワード広告という手段もあります。</p> <h3 id="スマホアプリは重要だと思った"><a href="#%E3%82%B9%E3%83%9E%E3%83%9B%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AF%E9%87%8D%E8%A6%81%E3%81%A0%E3%81%A8%E6%80%9D%E3%81%A3%E3%81%9F">スマホアプリは重要だと思った</a></h3> <p>単純にスマホアプリは重要だなと思うようになったためです。僕のようにプログラミングを生業としている人からしたらPCでWebを色々利用して色んなことを行い、それをあって当然の基盤として生活することが当たり前です。</p> <p>しかし時代が変わっていくに連れ、ブログも全部スマホで書くよ、とか、Twitterなんてやってないよ、とか、既に話を聞く限りでも僕にとっては信じられない常識の中にいる人達が多く増えてきています。スマホはtiktokとLINEしかやってないなんて人も多いでしょう。すでにブラウザなんてよくわからないしだいたい全部スマホアプリでやってるよ、という人たちも増え続けていることでしょう。Webアプリとして作っておけば誰でも使えるし大丈夫、という時代は終わりつつあるような気がしています。スマホに必要な機能をアプリで追加してそれを使えば済む、という世代が増えていくのではないかと感じます。というか既にかなり多そうにも思います。</p> <p>実際にはそうではない可能性があっても、Webだから便利なことと、アプリだから便利なことがやはりあると感じます。Webだけでなくアプリを提供してアプリのことを知っていくのは重要なことだというのは間違いないと感じるようになりました。思いついた何かしらの作りたいものがWeb向けかアプリ向けかをちゃんと判断していったほうが良いと思いました。</p> <h2 id="そもそもなぜアプリづくりをやめていたのか"><a href="#%E3%81%9D%E3%82%82%E3%81%9D%E3%82%82%E3%81%AA%E3%81%9C%E3%82%A2%E3%83%97%E3%83%AA%E3%81%A5%E3%81%8F%E3%82%8A%E3%82%92%E3%82%84%E3%82%81%E3%81%A6%E3%81%84%E3%81%9F%E3%81%AE%E3%81%8B">そもそもなぜアプリづくりをやめていたのか</a></h2> <p>前述の通り、開発環境がつらかったというのがまず一つです。</p> <p>あとはスマホアプリはSDKやストアがガンガンアップデートされていくため、これはきっと作ってもいつか使えなくなるな……という気持ちが強まったためです。100個作ったとしても多分追従して全部をアップデートしていくことは時間がなくて無理だと思いました。売れているならともかく、ほとんどは売れないアプリになるでしょうし。</p> <p>しかも時間がたつにつれ、ソースコードを失う恐怖もありました。Webアプリケーションであれば最悪サーバーからダウンロードしてきて復旧すればよいのですが、アプリはビルドされていますのでソースを失うともう復旧は不可能です。作り直すしかありません。GitHubも昔は使っていなかったりプライベートリポジトリが有料だったりで使っていなかったため、今より全てを失う可能性は高かったです。cocos2d-jsなどはどこをignoreしてどこをコミットすればいいのか非常に分かりづらかったので、リポジトリに保存していたとしても復旧は困難そうに思います。アプリづくりをしながらこういう喪失感を容易に想像できていたので、それもやめてしまった原因でした。</p> <h2 id="今年やってみてどうだったか"><a href="#%E4%BB%8A%E5%B9%B4%E3%82%84%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%A6%E3%81%A9%E3%81%86%E3%81%A0%E3%81%A3%E3%81%9F%E3%81%8B">今年やってみてどうだったか</a></h2> <p>今のところは全然です。ほとんどインストールされないアプリがほとんどです。一つだけぼちぼち成長してるアプリがありますが、収益的には全然です。ゲームもストア上の数が多すぎるのか、リリース時に一瞬たくさん使われただけであとは全然です。</p> <p>ただ元々すぐ収益につながるようなことになるとは思っていないので、とにかく今年は種まきと、あとは実際にやってみて情報収集と勉強のためにやっている部分が大きいです。そのためまだまだ来年は何をどうやっていこうか、というところを考えることも楽しみです。</p> <p>ただやはり前述の通り開発しやすくなった、というのは僕だけではなくあらゆる人にとってそうであり、多分新しいアプリが出てくる量はかなり以前より増えているのではないかと思います。Twitterなどでもちらちら、昔はアプリを見つけてもらいやすかったけど今は辛い、という実体験の情報なども見かけたりしました。今流れている情報を鵜呑みにせず、今後の状況をしっかり見ていく必要はあると思いました。これはWebもなのかもしれませんが。何にしろこっちが良さそうだからこっち、やっぱあっちがよさそうだからあっち、みたいに、何も考えずにひょいひょい適当クオリティでやっていくのは厳しいだろうなというのは間違いなさそうに思います。</p> <h2 id="来年からはどうしていくか"><a href="#%E6%9D%A5%E5%B9%B4%E3%81%8B%E3%82%89%E3%81%AF%E3%81%A9%E3%81%86%E3%81%97%E3%81%A6%E3%81%84%E3%81%8F%E3%81%8B">来年からはどうしていくか</a></h2> <p>作りたいものがWebサービスにもスマホアプリにもあります。そのあたりは何も考えず、作りたいもので、役に立ちそうなものをどんどんまた作っていったり改良していったりしたいと思います。あとはSide QuestでVRゲームをリリースしたいなとも思います。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>とにかくやる!</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/16245 2020-11-30T01:25:12+09:00 2020-11-30T01:25:12+09:00 https://crieit.net/posts/README このブログのREADME的なもの <h1 id="はじめまして"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%BE%E3%81%97%E3%81%A6">はじめまして</a></h1> <p>これから、わたくしずずまるが、このブログに書いていきたいこと、お前ってどういうレベルの人間?というのをかいていきます。<br /> READMEみたいな感覚です。</p> <h2 id="ずずまるとは"><a href="#%E3%81%9A%E3%81%9A%E3%81%BE%E3%82%8B%E3%81%A8%E3%81%AF">ずずまるとは</a></h2> <p>SIerのへっぽこC#er二年目。<br /> 人手不足すぎてなぜかPLっぽいことをやってる。タスクはまわっていない。<br /> アウトプットが大事って偉い人がいうのでブログとか始めた</p> <h2 id="主な環境"><a href="#%E4%B8%BB%E3%81%AA%E7%92%B0%E5%A2%83">主な環境</a></h2> <ul> <li>Windows10 Pro</li> <li>VisualStudio2019</li> <li>VScode</li> <li>Git bash</li> <li>Git hub</li> </ul> <h2 id="このブログで書いていきたいトピック"><a href="#%E3%81%93%E3%81%AE%E3%83%96%E3%83%AD%E3%82%B0%E3%81%A7%E6%9B%B8%E3%81%84%E3%81%A6%E3%81%84%E3%81%8D%E3%81%9F%E3%81%84%E3%83%88%E3%83%94%E3%83%83%E3%82%AF">このブログで書いていきたいトピック</a></h2> <ul> <li>C#の学習記録</li> <li>salesforth学習記録</li> <li>かじりたてPython</li> <li>その他雑多なことは<a target="_blank" rel="nofollow noopener" href="https://note.com/wanamaru">note</a></li> </ul> <h2 id="興味はあるけど手を出せてないもの"><a href="#%E8%88%88%E5%91%B3%E3%81%AF%E3%81%82%E3%82%8B%E3%81%91%E3%81%A9%E6%89%8B%E3%82%92%E5%87%BA%E3%81%9B%E3%81%A6%E3%81%AA%E3%81%84%E3%82%82%E3%81%AE">興味はあるけど手を出せてないもの</a></h2> <ul> <li>ラズパイ</li> <li>PHP</li> <li>AWS</li> <li>GoogleAPIを利用する開発</li> </ul> ずずまる@ばぶばぶSE tag:crieit.net,2005:PublicArticle/16243 2020-11-28T07:27:30+09:00 2020-11-28T07:27:30+09:00 https://crieit.net/posts/Cyber-Muse - Cyber Muse - 音楽投稿アプリをリリースしました!(個人開発) <h1 id="初めに"><a href="#%E5%88%9D%E3%82%81%E3%81%AB">初めに</a></h1> <p>音楽投稿アプリを開発しました!</p> <p>ウェブサイト:<a target="_blank" rel="nofollow noopener" href="https://cyber-muse.com">https://cyber-muse.com</a></p> <p>Apple Store:<a target="_blank" rel="nofollow noopener" href="https://apps.apple.com/app/id1511131064">https://apps.apple.com/app/id1511131064</a></p> <p>Google Play: <a target="_blank" rel="nofollow noopener" href="https://play.google.com/store/apps/details?id=com.front_music">https://play.google.com/store/apps/details?id=com.front_music</a></p> <p>開発言語は<a target="_blank" rel="nofollow noopener" href="https://ja.reactjs.org/">React</a>と<a target="_blank" rel="nofollow noopener" href="https://reactnative.dev/">React Native</a>を使用しています。</p> <p>開発から運用まで全て個人で行っています。<del>開発から運用まで大変だったことなど色々書いていきたいと思います。</del></p> <h1 id="React &amp; React Nativeについての所感"><a href="#React+%26amp%3B+React+Native%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%81%AE%E6%89%80%E6%84%9F">React & React Nativeについての所感</a></h1> <p>気づけばReactを書き始めてから1年ぐらいがたってしまいました。Reactを学ぶきっかけとなったのはアルバイトです。それまではフロントエンドについてはあまり興味がなく、<a target="_blank" rel="nofollow noopener" href="https://www.python.jp/">Python</a>をよく書いていました。バイト先ではReact(JS)がメインだったので面接に受かってから学び始めました。バイトの頻度は週二回1日8時間くらいでした。</p> <h3 id="React"><a href="#React">React</a></h3> <p>Reactは個人的にすごく書きやすい。個人的にhtml内のscriptの中やUIとは独立したjsファイルにjsを書くのが嫌いだったので。見にくくないですか?<br /> Reactでつまづくとすれば、それはstateという概念だ。hooks周辺を理解するのは難しい。<br /> useEffectについてひたすら調べまくりました。正直日本語でこれ以上に分かりやすい記事は他にないと思う。<br /> <a target="_blank" rel="nofollow noopener" href="https://iqkui.com/ja/a-complete-guide-to-useeffect/">https://iqkui.com/ja/a-complete-guide-to-useeffect/</a><br /> あとは例外的ではあるがファイルアップロード関係かな。バイト先では<a target="_blank" rel="nofollow noopener" href="https://graphql.org/">GraphQL</a>というバックエンドとフロントを繋ぐものを使用していたのだが、これに使えるファイルアップローダーの無さといったらもう。そもそもGraphQLを通してファイルアップロードするのもどうかと思うんだが。バックエンドで<a target="_blank" rel="nofollow noopener" href="https://v1.prisma.io/docs/1.34/get-started/01-setting-up-prisma-new-database-JAVASCRIPT-a002/">Prisma</a>とか<a target="_blank" rel="nofollow noopener" href="https://www.docker.com/">Docker</a>とか使ってたからしゃあなしか?<br /> 今回バックエンドについては特にかくつもりないので割愛します。<br /> とまぁこんな感じでReactに慣れてきたらReact Nativeも書くようになりました。</p> <h3 id="React Native"><a href="#React+Native">React Native</a></h3> <p>React Nativeで一番大変なのはコードを書く以前の準備だと思います。モバイルアプリの開発はSimulatorをたてた状態で行わないといけませんが、慣れないうちはこれが大変です。iOSで言えばpodあたりやXcodeの設定をしっかり把握していないとビルドが失敗します。バイト先はアプリの運用をしていたので、既に完成されているものをビルドしなければいけません。ビルド時間は5~10分くらいかかっていました。ですので、慣れていないと後少し!!というところでビルドが失敗したりしてドツボにハマったりします。<br />  という感じで僕のモバイルアプリ開発の始めは苦しむことが多かったですが、今はiOSもAndroidもできているのでいいかなという感じです。<br />  そう、これがReact Nativeの利点だと僕は思う。ネイティブ言語でiOSとAndroidの開発をしようと思ったら、swiftやjavaの両方を学ばなければいけないからだ。React Nativeを使えば、多少の差があることは確かだがUIでいじる必要があることはほとんどない。ビルドの仕方とリリースの仕方を学べばそれでいい。多少の差に言及しておくとそれはアプリ内でファイル操作をする時だ。写真を選択したり、アップロードしたり。知らないとバグって、は?ってなる。 </p> <p>最後にReact NativeのUIについて述べる。UIはオープンソースのコンポーネントが色々あって便利だと思う。が、React Nativeのバージョンの違いとかで役に立たなかったりするやつがあるから注意。githubのReleasesのlatestが2年前とかのやつには手を出さないのが無難だ。あとは、cssのflexの有り難みがすごくわかる。キーワードはflexDirection, justifyContent, alignItemsだ。この3つがあれば画面が綺麗に整う。</p> <p>所感の割りには色々書きすぎたと思うが以上がReact Nativeに関する正直な感想だ。</p> <h1 id="ウェブサイトのアニメーションについて"><a href="#%E3%82%A6%E3%82%A7%E3%83%96%E3%82%B5%E3%82%A4%E3%83%88%E3%81%AE%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">ウェブサイトのアニメーションについて</a></h1> <p>実は最近ウェブサイトをアップデートしてアニメーションを取り入れた。<br /> <a target="_blank" rel="nofollow noopener" href="https://cyber-muse.com">https://cyber-muse.com</a><br /> このウェブサイトのアニメーションは全て<a target="_blank" rel="nofollow noopener" href=""https://www.react-spring.io/">react-spring</a>というモジュールを使用している。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.react-spring.io/">https://www.react-spring.io/</a><br /> このモジュールにスクロールの制御を加えると僕が作ったウェブサイトぐらいは作れるという感じで紹介しておこう。</p> <h1 id="終わりに"><a href="#%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB">終わりに</a></h1> <p>初めの文章に取り消しせんが付いている通りです。<br /> ReactとReact Nativeについて述べただけになってしまいました。</p> <p><strong>React</strong>と<strong>React Native</strong>を使えばこういうウェブサイトやアプリが作れる!!という参考になればいいと思います。</p> <p>ウェブサイト:<a target="_blank" rel="nofollow noopener" href="https://cyber-muse.com">https://cyber-muse.com</a><br /> Apple Store: <a target="_blank" rel="nofollow noopener" href="https://apps.apple.com/app/id1511131064">https://apps.apple.com/app/id1511131064</a><br /> Google Play: <a target="_blank" rel="nofollow noopener" href="https://play.google.com/store/apps/details?id=com.front_music">https://play.google.com/store/apps/details?id=com.front_music</a></p> Cyber Muse tag:crieit.net,2005:PublicArticle/16242 2020-11-27T23:04:34+09:00 2020-11-27T23:04:34+09:00 https://crieit.net/posts/Next-js-Bootstrap-Sticky-Footer Next.jsでBootstrapのSticky Footerを利用する <p>BootstrapにはSticky Footerのデモがあります。</p> <p><a target="_blank" rel="nofollow noopener" href="https://v5.getbootstrap.jp/docs/5.0/examples/sticky-footer/">https://v5.getbootstrap.jp/docs/5.0/examples/sticky-footer/</a></p> <p>昔はCSSを使っていましたが、現在はBootstrapのユーティリティを利用してクラス指定だけで実現しています。そのため適切にクラスを指定していけばよいだけなのですが、Next.jsのようなフレームワークの場合は各HTMLタグがデフォルトでは編集できなかったり、配置が決まっていたりするためなかなか思うように行きません。</p> <p>実際に対応した方法を書いておきます。</p> <h2 id="_document.jsを作成する"><a href="#_document.js%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">_document.jsを作成する</a></h2> <p>まずは全体のHTMLを編集するための _document.js というファイルを作ります。TypeScriptの場合は _document.tsx です。_app.js と同様、pagesの直下に配置します。</p> <p>中身は公式で解説しているとおりです。</p> <p><a target="_blank" rel="nofollow noopener" href="https://nextjs.org/docs/advanced-features/custom-document">Advanced Features: Custom <code>Document</code> | Next.js</a></p> <h2 id="Sticky Footer用のクラスを指定していく"><a href="#Sticky+Footer%E7%94%A8%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%A6%E3%81%84%E3%81%8F">Sticky Footer用のクラスを指定していく</a></h2> <p>次にどんどんクラスを指定していきます。まずはhtmlとbodyタグに高さ100%のクラス <code>h-100</code> を指定します。 _document.js 内です。</p> <pre><code class="jsx"> return ( <Html lang="ja" className="h-100"> <Head /> <body className="h-100"> <Main /> <NextScript /> </body> </Html> ) </code></pre> <p>次にフッター以外のメインコンテンツを下記のタグで囲みます。ここからは _document.js ではなく各ページもしくは共通レイアウトに設定します。</p> <pre><code class="html"> <main className="flex-shrink-0"> メインコンテンツ </main> </code></pre> <p>そしてその下にSticky Footerを配置します。</p> <pre><code class="html"><footer className="footer mt-auto py-3 bg-light"> <div className="container"> <span className="text-muted">フッターのコンテンツをここに置きます。</span> </div> </footer> </code></pre> <h2 id="Next.js用の対応"><a href="#Next.js%E7%94%A8%E3%81%AE%E5%AF%BE%E5%BF%9C">Next.js用の対応</a></h2> <p>これで一通りBootstrapのデモと同じ設定は完了です。しかし、Next.jsの場合は下記のように <code>__next</code> というidのdivが全体を囲んでしまっています。そのためデモと同様にbodyに <code>d-flex flex-column</code> を指定しても全てそのdivに反映されてしまい、メインコンテンツとフッターが正常に動作しません。そのためbodyは <code>h-100</code> だけにしていました。</p> <p><a href="https://crieit.now.sh/upload_images/6e2af66c610d88bc766649f72032893a5fc106aea8470.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6e2af66c610d88bc766649f72032893a5fc106aea8470.png?mw=700" alt="" /></a></p> <p>そのため、ここだけはCSSで指定してあげます。</p> <pre><code class="css">#__next { display: flex; flex-direction: column; height: 100%; } </code></pre> <p>自動挿入されている要素ですのでidなど、仕様が変わったら適宜調整が必要です。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/16241 2020-11-27T12:20:55+09:00 2020-11-27T12:23:06+09:00 https://crieit.net/posts/Crieit-5fc0709756a13 Crieitに数式を書くテスト <p>Crieitに数式を書くテスト。CodeCogsという外部サービスを利用する。</p> <ul> <li>リンク有り</li> </ul> <p><a target="_blank" rel="nofollow noopener" href="https://www.codecogs.com/eqnedit.php?latex=d=\frac{|ax_0&plus;by_0&plus;c|}{\sqrt{a^2&plus;b^2<span>}</span><span>}</span>" target="_blank"><img src="https://latex.codecogs.com/svg.latex?d=\frac{|ax_0&plus;by_0&plus;c|}{\sqrt{a^2&plus;b^2<span>}</span><span>}</span>" title="d=\frac{|ax_0+by_0+c|}{\sqrt{a^2+b^2<span>}</span><span>}</span>" /></a></p> <ul> <li>リンク無し</li> </ul> <p><img src="https://latex.codecogs.com/svg.latex?d=\frac{|ax_0&plus;by_0&plus;c|}{\sqrt{a^2&plus;b^2<span>}</span><span>}</span>" title="d=\frac{|ax_0+by_0+c|}{\sqrt{a^2+b^2<span>}</span><span>}</span>"></p> <p>URL: https://www.codecogs.com/eqnedit.php</p> konvsoup tag:crieit.net,2005:PublicArticle/16240 2020-11-26T18:56:31+09:00 2020-11-26T20:55:08+09:00 https://crieit.net/posts/VSCode-WSL-CMake-GoogleTest-C VSCode+WSL+CMake+GoogleTestでC++プログラミング環境を構築 <p>Windows10で掲題の環境を作成したのでメモ<br /> サクッとやりたかった<br /> 次の機会にはサクッとできるはず</p> <h4 id="①まずは↓を見ながらVSCode&WSLでC++を使えるようにする"><a href="#%E2%91%A0%E3%81%BE%E3%81%9A%E3%81%AF%E2%86%93%E3%82%92%E8%A6%8B%E3%81%AA%E3%81%8C%E3%82%89VSCode%EF%BC%86WSL%E3%81%A7C%2B%2B%E3%82%92%E4%BD%BF%E3%81%88%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B">①まずは↓を見ながらVSCode&WSLでC++を使えるようにする</a></h4> <p><a target="_blank" rel="nofollow noopener" href="https://ynucpc.github.io/blog/2020/03/24/howtowsl-vscode/">WSL+VSCODEで競技プログラミング環境構築</a></p> <p>途中、</p> <blockquote> <p>拡張機能がインストールできたら上部のメニューから「ファイル」->「フォルダーを開く」<br /> から<strong>好きなフォルダー</strong>を開いてください。</p> </blockquote> <p>と書かれているが、自分は好きなフォルダーじゃなくて、<br /> <strong>WSLのrootfsディレクトリ以下</strong>を指定した<br /> 具体的にはUbuntu18.04なら以下</p> <pre><code>C:\Users\[ユーザー名]\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs </code></pre> <p>本当にマイドキュメントとか好きなフォルダにしたら、<br /> gtest.hをincludeするときとかの補完機能がなんかアレとか、CMakelists.txtを書く時に全然アレとか、<br /> とにかく無用な苦労をしたので・・・(だから「競技プログラミング」って書いてあったのかな)</p> <h4 id="②cmakeのインストール"><a href="#%E2%91%A1cmake%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">②cmakeのインストール</a></h4> <pre><code>sudo apt-get install cmake </code></pre> <p>おわり</p> <h4 id="③GoogleTestのインストール"><a href="#%E2%91%A2GoogleTest%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">③GoogleTestのインストール</a></h4> <pre><code>cd git clone https://github.com/google/googletest.git cd googletest mkdir build cd build cmake .. sudo make install </code></pre> <p>ググると色んなインストール方法が出てくるが、自分が苦労なくできたのはこれだけだった<br /> 他はヒューマンのエラーが入る余地がありすぎる<br /> 特に最後のmake installはmakeだけやって手作業で.aとか.hをコピーしてる記事が多くてなんで?? ってなった</p> <h4 id="④C++のコード書く"><a href="#%E2%91%A3C%2B%2B%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E6%9B%B8%E3%81%8F">④C++のコード書く</a></h4> <p>さすがに省略</p> <h4 id="⑤GoogleTest使ってみたソースのCMakeLists.txt"><a href="#%E2%91%A4GoogleTest%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AECMakeLists.txt">⑤GoogleTest使ってみたソースのCMakeLists.txt</a></h4> <pre><code>cmake_minimum_required(VERSION 3.0) project("hello_gtest") add_executable(hello_gtest main.cpp) target_link_libraries(hello_gtest PRIVATE gtest gmock pthread) </code></pre> <p>①②③あたりを上に書いた通りにやっておけば、includepathの指定とか特に何も書かないで<br /> 上の内容でビルド&実行できると思う<br /> お疲れさまでした。次の⑥は読まなくていいです</p> <h4 id="⑥■■■■■■■■■■"><a href="#%E2%91%A5%E2%96%A0%E2%96%A0%E2%96%A0%E2%96%A0%E2%96%A0%E2%96%A0%E2%96%A0%E2%96%A0%E2%96%A0%E2%96%A0">⑥■■■■■■■■■■</a></h4> <p>仕事でどうしてもわかんないとこがあったが職場にいられる時間があまりなく、<br /> しかたない、家でC++環境構築してやってみるか・・男の子なら自宅WindowsPCで<br /> CやC++のコンパイル環境があるべきだよな・・・<br /> と思ってとりかかったら、上記の環境設定だけで終わってしまった</p> <p>何も考えず仮想PCにlinux入れてssh接続でやる方法ならむしろ確実だったが<br /> 「めんどい&重い&時間かかる」<br /> というのはわかっていたので、他に軽い方法ないかとググったらMinGWを使う方法がいっぱいでてきた<br /> このMinGWが無駄にした時間のほぼ全て<br /> 最初のapt-getに相当する部分でpacmanとか打ち込まされる時点で嫌な予感がして、<br /> 騙されたかなと思ってそれでもやってみたら騙された<br /> MinGWってなんで今も存在してるんだろう・・・</p> <p>WSLはすごい楽でした<br /> ありがとうMicroSoft</p> uskz tag:crieit.net,2005:PublicArticle/16239 2020-11-26T18:36:53+09:00 2020-11-26T18:36:53+09:00 https://crieit.net/posts/LINE-DEVELOPER-DAY LINE DEVELOPER DAY参加記録 <p>「LINE DEVELOPER DAY 2020」2日目に参加しました。<br /> 聞いたセッションのメモ書き程度の投稿です。<br /> 知識不足や聞き落としで間違ったことを書いているかもしれないので悪しからず。</p> <h2 id="Kubernetesを用いたPull Request駆動E2Eテスト"><a href="#Kubernetes%E3%82%92%E7%94%A8%E3%81%84%E3%81%9FPull+Request%E9%A7%86%E5%8B%95E2E%E3%83%86%E3%82%B9%E3%83%88">Kubernetesを用いたPull Request駆動E2Eテスト</a></h2> <p>同時に同じ機能に修正が入ると、E2Eテストがコケる。<br /> サーバにデプロイしないとE2Eテストを実行できないので開発者へのフィードバックが遅い。</p> <p>k8sを使ってE2Eのテストをユニットテストと同レベルで実行できるようにした話。</p> <h3 id="SETとは"><a href="#SET%E3%81%A8%E3%81%AF">SETとは</a></h3> <p>Software Engineer in Test<br /> 自動テストに関わるフレームワークやパッケージを組み立てるのがメイン業務。<br /> E2Eだけではない<br /> DX(Developer eXperience)を高めて、その結果として製品の品質を高めることが目的。</p> <p>Test Automation Engineerが製品品質をメイン目的にするため、役割として住み分けされている。</p> <h3 id="k8s活用"><a href="#k8s%E6%B4%BB%E7%94%A8">k8s活用</a></h3> <p>E2Eテスト実行にはアプリのサーバデプロイまで必要。<br /> ならばプルリクを起点にk8s上にサーバを構築してしまう。<br /> プルリクごとに独立したNameSpaceが立ち上がるため、他の変更やテスト実行に影響されない。</p> <p>実施した結果、元々の課題改善だけでなく、レビューにも効果が!<br /> ローカルで立ち上げて実行していると、ローカル環境でしか再現しない問題が出た時、フィードバックしずらい。<br /> k8s環境に移したら、そこで再現して見せられるのが良い。</p> <h3 id="感想"><a href="#%E6%84%9F%E6%83%B3">感想</a></h3> <p>自分がk8s初心者なのとスピーカーのテンポが早めだったので、付いていけない部分が多かった。<br /> かなり凄いことを実現しているので、資料が公開されたら改めて確認したい。<br /> 以前は色んな構築や課題が発生していた試験環境準備が、DevOpsとコンテナ技術の発展でどんどん進化しているなーと思いました。</p> <h2 id="LINEのテスト自動化システムがどのように動いているのか"><a href="#LINE%E3%81%AE%E3%83%86%E3%82%B9%E3%83%88%E8%87%AA%E5%8B%95%E5%8C%96%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%8C%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E5%8B%95%E3%81%84%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B">LINEのテスト自動化システムがどのように動いているのか</a></h2> <h3 id="ドキュメンテーション"><a href="#%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">ドキュメンテーション</a></h3> <p>テスト自動化においてもドキュメンテーションは重要。</p> <p>Page Objec Patternを採用しているので、スクリーンごとにページを作成し、<br /> スクリーンショット、プロパティ(ページの要素)、アクション(イベント)をまとめている。</p> <p>このページはMarkdownで書いて簡単にデプロイできる。</p> <h3 id="モニタリング"><a href="#%E3%83%A2%E3%83%8B%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0">モニタリング</a></h3> <p>LINEには世界中に開発のためのハードウェアがある。<br /> サーバの故障や不具合で試験が失敗した時、監視できるよう、<br /> ElasticserchにFilebeatとMetricbeatでデータを収集して、Grafanaでダッシュボード表示している。</p> <h3 id="レポーティング"><a href="#%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0">レポーティング</a></h3> <p>様々なLINEサービスのテスト結果をどうまとめるか。<br /> eggplantやappiumのデータをElasticsearchに収集し、Pythonでデータを成型、Veu.jsでWebに表示している。</p> <h3 id="Bot"><a href="#Bot">Bot</a></h3> <p>LINEとSlackを利用している。<br /> コミュニケーションツールだけでなく自動ツールの通知にも活用している。</p> <h3 id="感想"><a href="#%E6%84%9F%E6%83%B3">感想</a></h3> <p>ドキュメンテーションはアジャイル開発だといかに手軽に必要十分な情報をまとめていくかが重要になるので、<br /> フレームワークを作って更新していっているのは魅力的だなーと感じました。</p> <h2 id="The CI/CD Automation make happiness from small pieces"><a href="#The+CI%2FCD+Automation+make+happiness+from+small+pieces">The CI/CD Automation make happiness from small pieces</a></h2> <h3 id="リリース自動化"><a href="#%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9%E8%87%AA%E5%8B%95%E5%8C%96">リリース自動化</a></h3> <p>リリースと言えば、デプロイを行い、問題なければリリースステータスが更新され、Gitタグが切られ、リソースを収集、マーケットにアーティファクトがアップロードされればリリース…と多くのタスクがあり、抜けが発生しやすく、また各フローが終了するまでチームが緊張状態になっていた。</p> <p>そこで自動化を進めた。</p> <p>ネットワークが遮断されている環境でもリリースステータスを取得できる仕組みを作り、リリース結果がフィードバックされるようになった。<br /> これによって、CIタスクを増やしても実施が漏れない・負担が増えない環境になった。</p> <h3 id="感想"><a href="#%E6%84%9F%E6%83%B3">感想</a></h3> <p>扱っている内容自体はCI/CDを地道にやったよ!という話なので、もう少し突っ込んで実現時に直面した課題とその対処策を聞きたかったなと。</p> <h2 id="How to quickly develop static pages in LINE"><a href="#How+to+quickly+develop+static+pages+in+LINE">How to quickly develop static pages in LINE</a></h2> <p>静的コンテンツを1日で開発・デプロイできるようになった話。</p> <p>動的コンテンツはキャッシュ利用やサーバ間通信など考えることが多く、重くなりがちなため、静的ページへのニーズが増えている。<br /> そこで、多くのページを作るためのリソース不足が課題となった。<br /> 静的コンテンツ作成には具体的に以下のようなタスクが発生する。<br /> - デザイン<br /> - JavaScriptコーディング<br /> - Minify(難読化)<br /> - 多言語化<br /> - Webサーバへのデプロイ<br /> - ドメイン設定</p> <p>→多くは繰り返し作業なので、自動化できるのでは?と考えた。</p> <h3 id="共通作業の自動化"><a href="#%E5%85%B1%E9%80%9A%E4%BD%9C%E6%A5%AD%E3%81%AE%E8%87%AA%E5%8B%95%E5%8C%96">共通作業の自動化</a></h3> <p>Node.jsでCLIツールを作成し、init, dev, build, deployといったコマンドで実行できるようにした。<br /> が、ビルド時に開発者のローカル環境の影響を受けることが問題になったので、<br /> Web画面(Dashboard)からGitHubのレポジトリを参照できるように変更した。</p> <h3 id="コンテンツ作成"><a href="#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E4%BD%9C%E6%88%90">コンテンツ作成</a></h3> <p>コンテンツ作成はサービスごとに異なり、共通化が難しい。<br /> →JAMStackを利用することにした。<br /> - JavaScript<br /> - APIs<br /> - Markup</p> <p>Headless CMSをDockerコンテナ上に立て、Dashboard画面からGUI画面を呼び出せるようにした。<br /> これにより、コンテンツ作成・更新時にエンジニアがソースコードを操作する必要がなくなった。<br /> さらに画面コンポーネントを定義しておき、コンテンツ作成を素早く行えるようにした。</p> <h3 id="感想"><a href="#%E6%84%9F%E6%83%B3">感想</a></h3> <p>色んなサービスを扱っているLINEだからこそ独自フレームワーク化・自動化が生きるのかなと思った。<br /> MkDocsなど、単純なページを生成するフレームワークは既にあるので、それをパイプラインに乗せるだけで手軽に静的コンテンツの自動更新はできる。<br /> が、サービスごとに凝ったコンテンツを作りたければ、このようなビルドが必要になるのだなと。</p> <h2 id="総括"><a href="#%E7%B7%8F%E6%8B%AC">総括</a></h2> <p>LINE DEVELOPER DAY初参加でしたが、予め登録していたセッションが近づくとLINEに通知されたり、ライブ画面の横でLINEスタンプ風のリアクションが送れたり、LINEさんならではの意匠がこらされていて面白かったです。<br /> セッション内容としては、10~20分の短いセッションも多く、ちょっと駆け足な印象も否めず。<br /> もう少しじっくり聞きたかったなーと思います。<br /> 次回予告が出ているセッションもあるので、続報を待ちます。</p> <p>本日は参加させていただき、ありがとうございました!</p> kareyama_r tag:crieit.net,2005:PublicArticle/16238 2020-11-25T23:44:58+09:00 2020-11-25T23:44:58+09:00 https://crieit.net/posts/chrome-background-property-and-border-radius-stretch-image-20201125 Chrome系ブラウザで background-position, attachment, size, そして border-radius を指定すると、特定の画面サイズ条件のときにスクロールで背景画像が引き伸ばされる現象のメモ <h2 id="条件"><a href="#%E6%9D%A1%E4%BB%B6">条件</a></h2> <h3 id="1. ページコンテンツ"><a href="#1.+%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84">1. ページコンテンツ</a></h3> <p>css で以下の条件を満たすページを作ります。</p> <ul> <li><code>background-image</code>: 背景画像をカバーとして設置</li> <li><code>background-position-y</code>: <code>bottom</code> に設定</li> <li><code>background-attachment</code>: <code>fixed</code> に設定</li> <li><code>background-size</code>: <code>cover</code> に設定</li> <li><code>border-radius</code>: 角丸になるように指定(例: <code>15px</code>)</li> </ul> <h3 id="2. ブラウザ"><a href="#2.+%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6">2. ブラウザ</a></h3> <p>このページを、 Chrome 系のブラウザで表示させます。例えば以下。</p> <ul> <li>Chrome: <code>87.0.4280.66</code></li> <li>Edge: <code>87.0.664.47</code></li> <li>Vivaldi: <code>3.4.2066.106</code></li> </ul> <p>Firefox では発生しませんでした。 Safari は未検証です。</p> <h3 id="3. ユーザアクション"><a href="#3.+%E3%83%A6%E3%83%BC%E3%82%B6%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3">3. ユーザアクション</a></h3> <p>以上の条件を満たす状態でページをスクロールします。すると……</p> <h2 id="デモ"><a href="#%E3%83%87%E3%83%A2">デモ</a></h2> <p>以下にデモページを用意しました。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://scrunchy56.starfree.jp/inspect_vampire_stretch/">Home - Vampire Stretch</a></li> </ul> <h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2> <p><a href="https://crieit.now.sh/upload_images/3a4ba3f3e62a9a32b4a901e5fb2b04c65fbe6c8a658cb.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3a4ba3f3e62a9a32b4a901e5fb2b04c65fbe6c8a658cb.jpg?mw=700" alt="サンプルページの表示(まだスクロールせず)" /></a></p> <p>こんなページをデモで作成しました。このページをスクロールすると……</p> <p><a href="https://crieit.now.sh/upload_images/62db714cff8ee5048c7e055db642ad785fbe6c9754250.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/62db714cff8ee5048c7e055db642ad785fbe6c9754250.jpg?mw=700" alt="画像の一部分(特に文字)が縦に引き伸ばされる" /></a></p> <p>……画像の一部分、サンプルだと「Vampire」の文字が縦に引き伸ばされました。</p> <p><a href="https://crieit.now.sh/upload_images/5ae87e554a331c09e82a359c1eeaf7a75fbe6ca3e8410.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5ae87e554a331c09e82a359c1eeaf7a75fbe6ca3e8410.jpg?mw=700" alt="画面の比率を変えるとよりひどく引き伸ばされる" /></a></p> <p>開発者ツールを画面下に表示させて横長の画面サイズにするとより顕著に現象が確認できます。</p> <h2 id="抑制の方法"><a href="#%E6%8A%91%E5%88%B6%E3%81%AE%E6%96%B9%E6%B3%95">抑制の方法</a></h2> <p>さすがにこれは……ということで、抑制する方法を検証しました。</p> <p>現象は、上述のように Chrome でも画面サイズによって出たり出なかったりします。例えば、開発者ツールを下ではなく右に表示させると現象が消えた、といった具合に。</p> <p>とはいえ、レスポンシブなサイトで画面サイズを限定するのは難しいと思います。</p> <p>そこで開発者ツールでcssの適用を付け外しした結果、<strong>以下のいずれか1つを実施</strong>すれば現象が発生しなくなることが確認できました。</p> <ul> <li><code>background-position-y</code>: 指定しない( <code>top</code> に設定する) <ul> <li>手元の環境では、 <code>center</code> は画像コンテンツの高さ等によって現象が出てしまうこともありました</li> </ul></li> <li><code>background-attachment</code>: <code>fixed</code> 以外に設定する</li> <li><code>background-size</code>: <code>cover</code> 以外に設定する</li> <li><code>border-radius</code>: 指定しない( <code>0</code> に設定する)</li> </ul> <p>裏を返せば、これら4つの条件を同時に満たさない限りは現象は発生しない、ということになります。</p> <p><a href="https://crieit.now.sh/upload_images/5c5b2c54ae4e2b754ceb9c49445911465fbe6cafe5c97.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5c5b2c54ae4e2b754ceb9c49445911465fbe6cafe5c97.jpg?mw=700" alt="background-position-y の指定を無効化した場合" /></a></p> <p><a href="https://crieit.now.sh/upload_images/828581555b0f9956dad988f5fd7a05a65fbe6cbae7753.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/828581555b0f9956dad988f5fd7a05a65fbe6cbae7753.jpg?mw=700" alt="background-attachment の指定を無効化した場合" /></a></p> <p><a href="https://crieit.now.sh/upload_images/3fcc3b91812efa34f6a02a951d41dc2b5fbe6cc6e455e.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3fcc3b91812efa34f6a02a951d41dc2b5fbe6cc6e455e.jpg?mw=700" alt="background-size の指定を無効化した場合" /></a></p> <p><a href="https://crieit.now.sh/upload_images/c6526480497d29e068827e0fd76c6a745fbe6cd19b020.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c6526480497d29e068827e0fd76c6a745fbe6cd19b020.jpg?mw=700" alt="border-radius の指定を無効化した場合" /></a></p> <h2 id="備考"><a href="#%E5%82%99%E8%80%83">備考</a></h2> <ul> <li>以前は気が付かなかった</li> <li>Firefox では発生しなかった</li> <li>条件があまりにも限定的である</li> </ul> <p>以上から、もしかすると Chrome(Chromium) のバグの可能性もあるのでは、と思っているのですが、検索しても全然ヒットしないので正直よく分かりません。</p> <p>いずれにしても、「こんな現象がある」ということでメモしておきます。</p> arm-band tag:crieit.net,2005:PublicArticle/16237 2020-11-25T17:55:46+09:00 2020-11-25T17:55:46+09:00 https://crieit.net/posts/c-c-doxygen c,c++のコメントメモ(doxygenを使用) <p>この記事ははてなをメインにQrunch(閉鎖済み)、Crieitにクロス投稿しています</p> <h4 id="環境"><a href="#%E7%92%B0%E5%A2%83">環境</a></h4> <p>VisualStudio2017</p> <p>VisualStudio2019</p> <p>VSCode</p> <h4 id="Doxygenで生成したドキュメントのどこで表示されるかわからない"><a href="#Doxygen%E3%81%A7%E7%94%9F%E6%88%90%E3%81%97%E3%81%9F%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%81%AE%E3%81%A9%E3%81%93%E3%81%A7%E8%A1%A8%E7%A4%BA%E3%81%95%E3%82%8C%E3%82%8B%E3%81%8B%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA%E3%81%84">Doxygenで生成したドキュメントのどこで表示されるかわからない</a></h4> <p>変数等の@briefは詳解の前の簡単な説明と詳解の後のところに表示。<br /> @detailsは詳解のところに表示される。</p> <h4 id="コメントの書く場所をどうすべきか"><a href="#%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88%E3%81%AE%E6%9B%B8%E3%81%8F%E5%A0%B4%E6%89%80%E3%82%92%E3%81%A9%E3%81%86%E3%81%99%E3%81%B9%E3%81%8D%E3%81%8B">コメントの書く場所をどうすべきか</a></h4> <p>VisualStudioではcppに書いたときInteliSenseが機能しなかったので<br /> ヘッダーに書くのが良さそうです。<br /> ただテンプレート関数をクラス内で多用するとコメントと合わせかなり行数が増えるので注意が必要です。<br /> <del>どうにかしたい・・・</del></p> <h4 id="前置簡易コメント"><a href="#%E5%89%8D%E7%BD%AE%E7%B0%A1%E6%98%93%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">前置簡易コメント</a></h4> <pre><code>/// コメント /// @detailsと同じ扱いに int i; /// コメント </code></pre> <p>このコメントはvisualstudioで使うとxmlコメントの書き方に被るみたいで<br /> InteliSense(変数や関数を選んだときに表示されるやつ)に表示されません。<br /> なので以下を使用しました。</p> <pre><code>//! コメント //!@brief 説明 </code></pre> <h4 id="後置コメント"><a href="#%E5%BE%8C%E7%BD%AE%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">後置コメント</a></h4> <pre><code>int a;///<コメント </code></pre> <p>これもvisualstudioでは「XMLコメントに無効なXMLが含まれます」と表示されるので</p> <pre><code>int a//!<コメント </code></pre> <p>を使用しました。<br /> あと、Visual studioのc++用のxmlコメントは拡張機能で使用可能みたいです。</p> <p><a target="_blank" rel="nofollow noopener" href="http://cercopes-z.com/Doxygen/list-command-dxy.html">[VisualStudio]C++でもXMLドキュメントコメントを自動挿入したい!!</a></p> <h4 id="各ファイルのコメント"><a href="#%E5%90%84%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">各ファイルのコメント</a></h4> <p>一番始めにいります。<br /> 最低限この2行あれば十分っぽいです。</p> <pre><code>/*! @file 何も書かなくてもいい @brief このファイルの説明を書く ファイル一覧のところに出力されます */ </code></pre> <h4 id="変数、マクロのコメント"><a href="#%E5%A4%89%E6%95%B0%E3%80%81%E3%83%9E%E3%82%AF%E3%83%AD%E3%81%AE%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">変数、マクロのコメント</a></h4> <pre><code>int hp;//!@brief コメント //!@brief     コメント //!@details   コメント int mp; </code></pre> <p>基本後置コメントがスマートでいいのですが複数行に対応してないみたいなので<br /> 複数行に書く場合は前置のほうがいいです。</p> <h4 id="関数のコメント"><a href="#%E9%96%A2%E6%95%B0%E3%81%AE%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">関数のコメント</a></h4> <pre><code>/*! @brief 関数の要約 @param 引数名 引数の詳細引数の個数分かく @return 戻り値の詳細 @details 関数の細かい説明 */ </code></pre> <h4 id="クラス、列挙体へのコメント"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E3%80%81%E5%88%97%E6%8C%99%E4%BD%93%E3%81%B8%E3%81%AE%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">クラス、列挙体へのコメント</a></h4> <pre><code>/*! @brief 要約 クラスなら一覧にも書かれます @details 細かい説明 */ </code></pre> <h4 id="便利なコメント"><a href="#%E4%BE%BF%E5%88%A9%E3%81%AA%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">便利なコメント</a></h4> <div class="table-responsive"><table> <thead> <tr> <th>@todo</th> <th>書くとTODOリストが自動で作成されめっちゃ便利!</th> </tr> </thead> <tbody> <tr> <td>@see</td> <td>参照したい関数、変数を書ける</td> </tr> <tr> <td>@note</td> <td>メモを書ける</td> </tr> <tr> <td>@n</td> <td>文の途中で改行してくれます。</td> </tr> <tr> <td>@retval</td> <td>@returnの代わりに使用 戻り値ごとの細かい説明を書ける。</td> </tr> </tbody> </table></div> <h4 id="参考にしたサイト"><a href="#%E5%8F%82%E8%80%83%E3%81%AB%E3%81%97%E3%81%9F%E3%82%B5%E3%82%A4%E3%83%88">参考にしたサイト</a></h4> <p><a target="_blank" rel="nofollow noopener" href="http://cercopes-z.com/Doxygen/list-command-dxy.html">Doxygen コマンド一覧</a><br /> <a target="_blank" rel="nofollow noopener" href="https://teratail.com/questions/69989">C++で///を使ったコメント - teratail</a></p> <h4 id="VSCodeの拡張機能(2020-11-25追記)"><a href="#VSCode%E3%81%AE%E6%8B%A1%E5%BC%B5%E6%A9%9F%E8%83%BD%282020-11-25%E8%BF%BD%E8%A8%98%29">VSCodeの拡張機能(2020-11-25追記)</a></h4> <p>VSCodeにはDoxygen形式のコメントを自動生成してくれるとても便利な拡張機能があります<br /> <a target="_blank" rel="nofollow noopener" href="https://marketplace.visualstudio.com/items?itemName=cschlosser.doxdocgen">Doxygen Documentation Generator</a><br /> 関数やクラスの前、ファイルの先頭で/**と改行だけで必要な内容を生成できるのでとてもおすすめです。</p> jupi tag:crieit.net,2005:PublicArticle/16236 2020-11-24T23:34:09+09:00 2020-11-24T23:34:09+09:00 https://crieit.net/posts/gulp4-webpack5-terser-webpack-plugin-error-resolutions-20201124 続・ Gulp 4 + Webpack 5 を試す (resolutions 使用) <p>以前、<a href="https://crieit.net/posts/gulp4-webpack5-terser-webpack-plugin-error-20201020">Gulp 4 + Webpack 5 を試す ( 未完 / terser-webpack-plugin で TypeError: Cannot read property ‘javascript’ of undefined エラーになる)</a>の記事で Gulp 4 + Webpack 5 の実験をしましたが、その続きです。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p><a href="https://crieit.net/posts/backstopjs-test-20201122">BackstopJS を試す (Error: Failed to launch the browser process! エラー発生→ puppeteer のバージョンを指定して解決)</a>で <code>package.json</code> に <code>resolutions</code> を記述して Yarn で内部依存パッケージのバージョンを強制的に変更する手法を取りましたが、同様の手法が使えるのではないか、と思った次第です。</p> <h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2> <h3 id="package.json"><a href="#package.json">package.json</a></h3> <p>```json:package.json<br /> // 略<br /> "devDependencies": {<br /> // 略<br /> "webpack": "^5.6.0",<br /> "webpack-stream": "^6.1.1",<br /> "terser-webpack-plugin": "^5.0.3",<br /> // 略<br /> },<br /> "resolutions": {<br /> "webpack": "^5.6.0"<br /> },<br /> // 略</p> <pre><code><br />上述のように `resolutions` で Webpack のバージョンを指定。 ### gulp/tasks/js.js `gulp/tasks/js.js` は前回のまま。 ### webpack.config.js `webpack.config.js` は source map のための設定を追加した以外はそのままです。 ```javascript:webpack.config.js const _ = require('./gulp/plugin'); const dir = require('./gulp/dir'); const mode = () => { return process.env.DEV_MODE === 'dev' ? 'development' : 'production'; }; const modeFlag = () => { return process.env.DEV_MODE === 'dev' ? false : true; }; const entry = () => { const entries = _.glob .sync( '**/*.js', { ignore: [ '_plugins/**' ], cwd: dir.src.js } ) .map(function (key) { return [key, _.path.resolve(dir.src.js, key)]; }); return Object.fromEntries(entries) }; const configs = { mode: mode(), entry: entry(), output: { filename: '[name]' }, optimization: { minimizer: [ new _.webpackTerser({ extractComments: 'some', terserOptions: { compress: { drop_console: modeFlag(), }, }, }), ], } }; if (process.env.DEV_MODE === 'dev') { // 追加 configs.devtool = 'inline-source-map'; } module.exports = configs; </code></pre> <p>これで <code>yarn restart</code> などすると</p> <pre><code class="bash">$ gulp # 略 [hh:ii:ss] asset app.js 226 KiB [emitted] [minimized] (name: app.js) 1 related asset webpack 5.6.0 compiled successfully [hh:ii:ss] Finished 'jsBuild' after 20 s </code></pre> <p>動きました!</p> <hr /> <p>後々は <code>resolutions</code> なしでも動くようにしたいですが、ひとまず動く形になったのでメモしておきます。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="Yarn の reasolutions"><a href="#Yarn+%E3%81%AE+reasolutions">Yarn の reasolutions</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://nju33.com/yarn/resolutions%20%E3%81%A7%E4%BE%9D%E5%AD%98%E3%81%AE%E4%BE%9D%E5%AD%98%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B">yarn: resolutions で依存の依存のバージョンを指定する - nju33</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16235 2020-11-24T23:28:18+09:00 2020-11-24T23:28:18+09:00 https://crieit.net/posts/gulp4-webpack5-terser-webpack-plugin-error-20201020 Gulp 4 + Webpack 5 を試す ( 未完 / terser-webpack-plugin で TypeError: Cannot read property 'javascript' of undefined エラーになる) <p>以前、<a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2020/10/06/browserify-babelify-gulp-transpile-ie/">browserify + babelify + Gulp で IE11対応を試す</a>の記事で browserify + babelify を試しましたが、今回は Webpack に挑んでみます。</p> <p>ただし、プレーンな Gulp 環境ではなく、 <a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/kiribi_ususama/tree/8a06ef719725236c26337a71bb2b1f9b64ef0900">Ususama</a> で。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <h3 id="package.json"><a href="#package.json">package.json</a></h3> <p>```json:package.json<br /> // 略<br /> "devDependencies": {<br /> // 略<br /> "glob": "7.1.6",<br /> "webpack": "^5.1.3",<br /> "webpack-stream": "^6.1.0",<br /> "terser-webpack-plugin": "^5.0.0",<br /> // 略<br /> }<br /> // 略</p> <pre><code><br />`gulp-uglify-es` と `gulp-concat` を除き、代わりに `webpack` 本体と Gulp で Webpack を使用するために必要な `webpack-stream` 、そして minifier の terser の Webpack 用プラグインである `terser-webpack-plugin` を追加。 ### gulp/tasks/js.js ```javascript:js.js const gulp = require('gulp'); const plumber = require('gulp-plumber'); const notify = require('gulp-notify'); const webpack = require('webpack'); const webpackStream = require('webpack-stream'); const rename = require('gulp-rename'); const dir = { dist: { js: './dist/js' } }; const webpackConfig = require('../../weppack.config'); const jsBuild = () => { return _.webpackStream(webpackConfig) .pipe(_.plumber({ errorHandler: _.notify.onError({ message: 'Error: <%= error.message %>', title: 'jsLibBuild' }) })) .pipe(_.rename((path) => { path.basename += '.min' path.extname = '.js' })) .pipe(_.gulp.dest(dir.dist.js)); }; module.exports = jsBuild; </code></pre> <h3 id="webpack.config.js"><a href="#webpack.config.js">webpack.config.js</a></h3> <p>```javascript:js.js<br /> const webpack = require('webpack');<br /> const webpackTerser = require('terser-webpack-plugin');<br /> const path = require('path');<br /> const glob = require('glob');<br /> const dotenv = require('dotenv').config();<br /> const dir = {<br /> src: {<br /> js: './src/js'<br /> }<br /> };</p> <p>const mode = () => {<br /> return process.env.DEV_MODE === 'dev' ? 'development' : 'production';<br /> };<br /> const modeFlag = () => {<br /> return process.env.DEV_MODE === 'dev' ? false : true;<br /> };<br /> const entry = () => {<br /> const entries = _.glob<br /> .sync(<br /> '<strong>/*.js',<br /> {<br /> ignore: [<br /> '_plugins/</strong>'<br /> ],<br /> cwd: dir.src.js<br /> }<br /> )<br /> .map(function (key) {<br /> return [key, _.path.resolve(dir.src.js, key)];<br /> });<br /> return Object.fromEntries(entries)<br /> };<br /> module.exports = {<br /> mode: mode(),<br /> entry: entry(),<br /> output: {<br /> filename: '[name]'<br /> },<br /> optimization: {<br /> minimizer: [<br /> new _.webpackTerser({<br /> extractComments: 'some',<br /> terserOptions: {<br /> compress: {<br /> drop_console: modeFlag(),<br /> },<br /> },<br /> }),<br /> ],<br /> }<br /> };</p> <pre><code><br />いくつかの記事を参考にしながらタスクを組みます。 自分でカスタマイズした部分は以下。 - `.env` で `dev`, `demo`, `prod` のモードを切り替えているので、その部分を `process.env.DEV_MODE` で振り分け - Webpack の設定の `mode` と `terser-webpack-plugin` の `drop_console` のフラグが関係しています - 複数の `.js` ファイルをエントリポイントにしたかったのでその部分は[webpackのentryファイルを複数指定、globパッケージの使い方 \- Qiita](https://qiita.com/masato_makino/items/7130bbe408ca929e7f0d)を参考に - 最終的なファイル名は `XXX.min.js` の形にしたかったので `gulp-rename` を通しました ## jQuery の扱い タスク自体は上記のやり方で走ることが確認できました( `DEV_MODE=dev` )。 次は現時点ではまだ jQuery を使用しているので、 jQuery をどう読み込ませるかが課題ですが、以下のようにして動作することを確認しました。 ### app.js ```javascript:app.js import $ from 'jquery'; import 'jquery.easing/jquery.easing'; $(() => { /* 処理 */ }); </code></pre> <h3 id="sitesearch.js"><a href="#sitesearch.js">sitesearch.js</a></h3> <p>```javascript:sitesearch.js<br /> import $ from 'jquery';<br /> import List from 'list.js';</p> <p>//サイト内検索<br /> export default () => {<br /> const options = {<br /> valueNames: ['searchTitle', 'searchText'],<br /> };<br /> const searchList = new List('listSearch', options);<br /> //hits<br /> searchList.on('searchComplete', function (a) {<br /> $('#hits').text(a.matchingItems.length);<br /> });<br /> };</p> <pre><code><br />サイト内検索で [List.js](https://listjs.com) を使用しているのですが、これについては[How am I suppose to import list\.js with es6 and webpack ? · Issue \#559 · javve/list\.js](https://github.com/javve/list.js/issues/559)の Issues の方法で解決しました。 ここまでは比較的順調でした。 しかし、間も無く壁に突き当たることになります……。 ## TypeError: Cannot read property 'javascript' of undefined エラー `DEV_MODE=dev` で動作することは確認できたので、 `DEV_MODE=prod` に切り替えました。 すると、以下のエラーが出てしまいました。 </code></pre> <p>TypeError: Cannot read property 'javascript' of undefined<br /> at PATH\TO\PROJECT\node_modules\terser-webpack-plugin\dist\index.js:366:38<br /> ```</p> <p><code>DEV_MODE=dev</code> に戻すと先ほどと同じように問題なく動作。上記でこのフラグが関係するのは <code>mode</code> と <code>terser-webpack-plugin</code> の <code>drop_console</code> の2箇所なので、そのどちらかだろうとアタリを付けます。</p> <p>試しに <code>drop_console</code> を常に <code>false</code> としましたが、 <code>DEV_MODE=prod</code> でエラーは再現しました。</p> <p>となると、 <code>mode</code> の方ということになります。</p> <p>ここでエラー文で検索すると、以下の Issues を発見。 <code>terser-webpack-plugin</code> 本家のリポジトリです。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a></li> </ul> <p>発生個所も含めてエラー文が同じです。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335#issuecomment-709997726">TypeError: Cannot read property 'javascript' of undefined · Issue #335 · webpack-contrib/terser-webpack-plugin</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335#issuecomment-710004332">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a></li> </ul> <p>別の方のコメントを見ると、原因は以下の模様。</p> <ul> <li>terser-webpack-plugin 5 は Webpack 4 とは互換性がない</li> <li><code>webpack-stream</code> の内部で使用している Webpack がバージョン 4 系</li> </ul> <blockquote> <p>yep, we are working on it, release with fix will be today/tomorrow</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335#issuecomment-710032380">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a> (2020/10/16日 22:04 JST)</p> </blockquote> <p>わりとタイムリーなものを踏んでしまったようなので、 fixed されるのを待つ感じですかね……。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h2 id="Gulp + Webpack"><a href="#Gulp+%2B+Webpack">Gulp + Webpack</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tonkotsuboy_com/items/2d4f3862e6d05dc0bea1">Gulpで始めるwebpack 4入門 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/am10/items/2516fa04def815195ffe">gulp + webpack + babelをつかってみた - Qiita</a></li> </ul> <h3 id="webpack-stream"><a href="#webpack-stream">webpack-stream</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/webpack-stream">webpack-stream - npm</a></li> </ul> <h2 id="Webpack, 複数エントリポイントとoutput"><a href="#Webpack%2C+%E8%A4%87%E6%95%B0%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%81%A8output">Webpack, 複数エントリポイントとoutput</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/masato_makino/items/7130bbe408ca929e7f0d">webpackのentryファイルを複数指定、globパッケージの使い方 - Qiita</a></li> </ul> <h2 id="List.js import"><a href="#List.js+import">List.js import</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/javve/list.js/issues/559">How am I suppose to import list.js with es6 and webpack ? · Issue #559 · javve/list.js</a></li> </ul> <h2 id="terser-webpack-plugin"><a href="#terser-webpack-plugin">terser-webpack-plugin</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16234 2020-11-23T14:24:47+09:00 2020-11-25T01:32:44+09:00 https://crieit.net/posts/Among-Us-Discord-bot-AutoMuteUs-Discord-is-rate-limiting-me Among Us 用超便利 Discord bot “AutoMuteUs” をセルフホストした (公式推奨簡単版) <p><strong>注: この記事は、 2020年11月23日~25日 時点の話だよ。 状況は今後も刻々と変わる可能性が高いよ。</strong></p> <p>今年の夏あたりから、日本でも爆発的なブームが続いている、 <strong>宇宙人狼</strong> こと <strong>Among Us</strong>。</p> <p>Discord でボイスチャットしながらやるのが面白いのだけれど、 ゲームの仕様上、ミュート管理が結構面倒だ。</p> <p>そこに、 Among Us のゲーム画面に同期してミュート管理を超いい感じに自動で行ってくれる Discord bot が存在する。</p> <p><strong>それが <a target="_blank" rel="nofollow noopener" href="https://github.com/denverquane/automuteus">AutoMuteUs</a> だ。</strong></p> <p>このツールは、 誰か一人が Windows Steam 版 Among Us のゲームと一緒にキャプチャープログラムを立ち上げ、 公式の Discord Bot と連携させるだけで超お手軽に使える…<br /> <strong>…はずだった。</strong></p> <p>11月下旬現在、 AutoMuteUs 公式の手順に従って、 bot の <code>.au new</code> コマンドでゲームを作成しようとすると、<strong>以下のような悲しいメッセージを目にするだろう。</strong></p> <p><a href="https://crieit.now.sh/upload_images/c5d20f926b8515b20d82a4895eb57cc65fbb466f47b1c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c5d20f926b8515b20d82a4895eb57cc65fbb466f47b1c.png?mw=700" alt="among-us-bot-limit-000.png" /></a></p> <blockquote> <p>I'm very sorry, but Discord is rate-limiting me and I cannot accept any new games right now :frowning:<br /> Please try again in a few minutes.</p> </blockquote> <p>Discord のレート制限に引っかかってるって。<br /> Among Us は人気すぎるし仕方ないね!</p> <p>そして、仮にゲームの作成が無事にできたとしても、 bot のミュート変更が遅れたり、変更しそびれたりと不安定だったり…</p> <p><strong>…何とかしたい。</strong></p> <h2 id="ちょっと古い v2.4.3 をセルフホストするのが推奨"><a href="#%E3%81%A1%E3%82%87%E3%81%A3%E3%81%A8%E5%8F%A4%E3%81%84+v2.4.3+%E3%82%92%E3%82%BB%E3%83%AB%E3%83%95%E3%83%9B%E3%82%B9%E3%83%88%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8C%E6%8E%A8%E5%A5%A8">ちょっと古い v2.4.3 をセルフホストするのが推奨</a></h2> <p>2020/11/14 時点の <a target="_blank" rel="nofollow noopener" href="https://discord.gg/ZkqZSWF">AutoMuteUs の 公式 Discord サーバー</a> では、 このレート制限について以下のようにアナウンスされている。</p> <blockquote> <p>Soup 2020/11/14</p> <p>Hey all,</p> <p>We know the Official Bot has been having some serious issues lately, and we're sorry it keeps going down.<br /> We're looking at it right now and coming up with solutions, but we're fairly certain that the Bot is hitting the GLOBAL bot rate limit, and this is causing the outages (IP gets banned for 10 minutes upon hitting the limit). The bot is distributed across multiple IPs, but we need clever solutions to make sure we address the problem fully.</p> <p>It certainly makes sense that this would be the culprit, given that Discord has no "mass mute" API request option, and thus every single game is using ~10 requests to mute players, additional requests to update the status message, etc.</p> <p>We're developing solutions right now, but it might take some time. If you have some critical games coming up, we recommend self-hosting with version 2.4.3 while we get things ironed out with the official bot. The video detailing how to self-host version 2.4.3 is here: <a target="_blank" rel="nofollow noopener" href="https://www.youtube.com/watch?v=LUptOv5ohNc">https://www.youtube.com/watch?v=LUptOv5ohNc</a>, and the release is here: <a target="_blank" rel="nofollow noopener" href="https://github.com/denverquane/automuteus/releases/tag/2.4.3">https://github.com/denverquane/automuteus/releases/tag/2.4.3</a></p> <p>Thanks everyone for the patience, love you guys :kissing_heart:</p> </blockquote> <p>ざっくりいうと、</p> <ul> <li>公式ボットが Discord のレート制限に引っかかっているが、これは根本的な仕組みの問題。</li> <li>完全な対処には複雑な解決策が必要なので、解決には時間がかかるかもしれない。</li> <li>プレイしたい大事なゲームがあるなら、バージョン2.4.3でセルフホストすることをお勧めする。</li> </ul> <p>という感じ。</p> <p>AutoMuteUs はめっちゃアクティブに開発が進んでおり、 2020/11/14 の時点ですでに <a target="_blank" rel="nofollow noopener" href="https://github.com/denverquane/automuteus/releases/tag/4.0.3">バージョン 4.0.3</a> までリリースされているのだが、 敢えて割と古い 2.4.3 の利用が推奨されている。<br /> これはおそらく、 3.0.0 以降のバージョンのセルフホストでは、 docker が必須になっているなど、導入の敷居がかなり高くなっているためだろう。</p> <p>技術力に自信があるなら最新版を使ってもかまわないのだが、 とにかくすぐに遊びたいだけなら、 おとなしく 2.4.3 を使っておくのがよい。<br /> (2.4.4 も存在するけど、 x86 版 Windows で動かす分には差が無いようだ)</p> <p>古いバージョンでも、 bot の機能は十二分に便利… というか、主要な機能では最新版と差が無い。</p> <p>但し、あくまで古いバージョンのままであるという点は注意したい。<br /> 例えば、安定性は最新版に及ばないかも知れないし、 近くリリースされるらしい Among Us の(日本語を含めた)多言語対応などではうまく動かないかもしれない。<br /> (後者は 後述の AmongUsCapture が吸収してくれそうではあるけど。)</p> <h2 id="v2.4.3 の導入手順"><a href="#v2.4.3+%E3%81%AE%E5%B0%8E%E5%85%A5%E6%89%8B%E9%A0%86">v2.4.3 の導入手順</a></h2> <p>ということで、 v2.4.3 のセルフホストの導入手順について簡単に紹介してみようと思う。</p> <p>… といっても、 上記の <a target="_blank" rel="nofollow noopener" href="https://www.youtube.com/watch?v=LUptOv5ohNc">https://www.youtube.com/watch?v=LUptOv5ohNc</a> の動画をなぞっているだけだが。<br /> (英語の動画ではあるけど、 YouTube の自動字幕&自動翻訳で割と簡単に理解できる内容だと思う)</p> <p>実施にあたっては <strong>自己責任</strong> でお願いします。<br /> 状況が日々刻々とかわる話題なので、 記事のコメントで質問されても、答えるつもりはないので悪しからず。<br /> 他の方が回答してくれるのは大歓迎ですけどもね。</p> <p>後述するプリコンパイル済みの exe ファイルが信用できないと感じる場合は、ソースコードの内容を確認し、自身で exe をビルドすると良いだろう。<br /> 私は面倒くさかったので、プリコンパイル済みの exe 使うけど。</p> <h3 id="1. Among Us の 公式 Steam版 を用意する"><a href="#1.+Among+Us+%E3%81%AE+%E5%85%AC%E5%BC%8F+Steam%E7%89%88+%E3%82%92%E7%94%A8%E6%84%8F%E3%81%99%E3%82%8B">1. Among Us の 公式 Steam版 を用意する</a></h3> <p>まず、 <strong><a target="_blank" rel="nofollow noopener" href="https://store.steampowered.com/app/945360/Among_Us/">Among Us の Steam版</a> を買おう。</strong><br /> Andoroid版 や iOS版 の Among Us では AutoMuteUs は動作ないので。</p> <p>なお、 プレイヤーのうち誰か一人だけが AutoMuteUs を動かしていれば良く、 クロスプラットフォームで対戦もできるので、 全員 Steam版 である必要は無い。</p> <h3 id="2. GitHub から automuteus_windows と AmongUsCapture を DL"><a href="#2.+GitHub+%E3%81%8B%E3%82%89+automuteus_windows+%E3%81%A8+AmongUsCapture+%E3%82%92+DL">2. GitHub から automuteus_windows と AmongUsCapture を DL</a></h3> <p>開発者の GitHub リポジトリのリリースページから、以下の二つのファイルを、適当なフォルダに DL しよう。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/denverquane/automuteus/releases/tag/2.4.3">AutoMuteUs バージョン2.4.3</a> の <code>automuteus_windows.exe</code></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/denverquane/amonguscapture/releases/">AmongUsCapture 最新版</a> の <code>AmongUsCapture.exe</code> <ul> <li><code>AmongUsCapture.zip</code> をダウンロードして解凍した中に入っている</li> </ul></li> </ul> <p>一度アプリを起動したら保存フォルダを移動しないほうが良いので、どこか固定のフォルダを決めてそこへ移動させておくとよい。</p> <p>ネットワークからダウンロードした exe ファイルは ZoneId が付与されていて、実行時に Windowns による制限がかかるので、 それぞれの exe ファイルを右クリックして「ブロックの解除」をしておこう。</p> <p>なお、 AmongUsCapture の Readme には、 <a target="_blank" rel="nofollow noopener" href="https://dotnet.microsoft.com/download/dotnet-core/3.1">.NET Core 3.1 系ランタイム</a> のインストールが別途必要と書かれているが、 AmongUsCapture 2.4 系以上であれば不要だと思う。 多分。<br /> そもそも、 2.4.5 以降 TargetFramework が <code>net5.0-windows</code> に設定されてて、 .NET 5 のランタイムで動いてるし。 ランタイムは exe に同梱されているから別途インストールは不要なはず。</p> <h3 id="3. Discord の bot を作成する"><a href="#3.+Discord+%E3%81%AE+bot+%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">3. Discord の bot を作成する</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://discord.com/developers/applications">https://discord.com/developers/applications</a></p> <p>上記の Discord の開発者ページに移動して、 bot を作成しよう。</p> <p><a href="https://crieit.now.sh/upload_images/7207ec3a30a86188ccd1b5232d4fcbd45fbb46a3f0a16.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/7207ec3a30a86188ccd1b5232d4fcbd45fbb46a3f0a16.png?mw=700" alt="among-us-bot-limit-010.png" /></a><br /> <a href="https://crieit.now.sh/upload_images/35c8cad24ebb45cc15d40d1ff30e36035fbb46ad824dd.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/35c8cad24ebb45cc15d40d1ff30e36035fbb46ad824dd.png?mw=700" alt="among-us-bot-limit-011.png" /></a></p> <p>まず、 New Application ボタンから Discord アプリを作成して、 Bot タブから Add Bot をクリックして、 bot を作成する。</p> <p><a href="https://crieit.now.sh/upload_images/0c9a38322a22aa0ac9444f0518eb80b85fbb46c4b91bb.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0c9a38322a22aa0ac9444f0518eb80b85fbb46c4b91bb.png?mw=700" alt="among-us-bot-limit-012.png" /></a></p> <p>PUBLIC BOT のスイッチを OFF にして、 変更点を保存する。</p> <p><a href="https://crieit.now.sh/upload_images/8e5c4222fe1edb4fbb3d7196b0db26a45fbb46cb22d1e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8e5c4222fe1edb4fbb3d7196b0db26a45fbb46cb22d1e.png?mw=700" alt="among-us-bot-limit-013.png" /></a></p> <p>Token の Copy ボタンをクリックして、 bot のトークンコードをメモ帳など適当なところに控えておく。</p> <p><a href="https://crieit.now.sh/upload_images/4fa6a340f3ff5ecfca740551312fc49b5fbb46d1f0ac1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/4fa6a340f3ff5ecfca740551312fc49b5fbb46d1f0ac1.png?mw=700" alt="among-us-bot-limit-014.png" /></a></p> <p>OAuth タブに移動して、<br /> ① <strong>SCOPES から bot をチェック</strong><br /> ② <strong>BOT PERMISSIONS から Administrator をチェック</strong><br /> ③ <strong>Copy ボタンで 認可 リンクをコピー</strong><br /> の順に実行。</p> <p>そして ③ でコピーしたリンクをブラウザで開き、 Discord にログインしてどのサーバーに bot を追加するかを選択する。</p> <p>…とここまでで、 bot の作成は完了。</p> <h3 id="4. AutoMuteUs の設定"><a href="#4.+AutoMuteUs+%E3%81%AE%E8%A8%AD%E5%AE%9A">4. AutoMuteUs の設定</a></h3> <p>先ほどダウンロードした <code>automuteus_windows.exe</code> を一度起動させよう。</p> <p>すると、以下のようなエラーが表示され、 10秒後にプログラムが勝手に終了するはずだ。</p> <blockquote> <p>Program exited with the following error:<br /> no DISCORD_BOT_TOKEN provided<br /> This window will automatically terminate in 10 seconds</p> </blockquote> <p>すると、 その代わりに同じフォルダに <code>config.txt</code> というファイルが作成される。</p> <p>そのファイルを メモ帳 などで開くと</p> <pre><code class="plain">DISCORD_BOT_TOKEN= </code></pre> <p>と書かれているはずだ。</p> <p>その <code>=</code> の後ろに、 <strong>(3.) で控えておいた、 bot のトークンコードを貼り付けて保存しよう。</strong></p> <p>改めて <code>automuteus_windows.exe</code> を起動すれば、 今度は "Bot is now running." などと表示されるはずだ。</p> <h3 id="5. AmongUsCapture をリンクさせる"><a href="#5.+AmongUsCapture+%E3%82%92%E3%83%AA%E3%83%B3%E3%82%AF%E3%81%95%E3%81%9B%E3%82%8B">5. AmongUsCapture をリンクさせる</a></h3> <p>Steam版 Among Us を起動した状態で、 先ほどダウンロードした <code>AmongUsCapture.exe</code> を起動させる。</p> <p>AmongUsCapture が Among Us のプログラムを見つけて反応するはずだ。</p> <p>その状態で、 <strong>Discord の bot が入っているテキストチャンネル</strong> に、以下のコマンドを入力する。<br /> (コマンドと言っても、ドットから始まるメッセージを入力するだけだが)</p> <pre><code class="plain">.au new </code></pre> <p>すると、 BOT から以下のような <strong>ダイレクトメッセージ</strong> が届く。<br /> (サーバー内のチャンネルのメッセージではないので注意)</p> <p><a href="https://crieit.now.sh/upload_images/c82bc55fe701094e443ec0d4f4be746c5fbb47049c534.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c82bc55fe701094e443ec0d4f4be746c5fbb47049c534.png?mw=700" alt="among-us-bot-limit-030.png" /></a></p> <p>ここに書かれている <code>aucapture://</code> で始まるリンクをクリックすると、 AmongUsCapture にフォーカスが移って、 連係動作が開始される。</p> <p>ちなみに、 上手く AmongUsCapture が反応しない場合、 このメッセージ右下の URL, Code の内容を、 手動で AmongUsCapture へ入力しても OK だ。</p> <p><a href="https://crieit.now.sh/upload_images/93dff2af4f8df263402119df9f821a165fbb4714ccbd9.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/93dff2af4f8df263402119df9f821a165fbb4714ccbd9.png?mw=700" alt="among-us-bot-limit-031.png" /></a><br /> <a href="https://crieit.now.sh/upload_images/82da208766e5bb5af0f6f4870ea00ed65fbb47194601d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/82da208766e5bb5af0f6f4870ea00ed65fbb47194601d.png?mw=700" alt="among-us-bot-limit-032.png" /></a></p> <p>これで、全ての準備は完了だ。</p> <h2 id="遊び方"><a href="#%E9%81%8A%E3%81%B3%E6%96%B9">遊び方</a></h2> <p>Among Us で部屋を立て、参加者に部屋に入ってもらう。</p> <p>すると、 Among Us 上のプレイヤー名とキャラクターの色が、 チャンネルのテキストメッセージに表示される。</p> <p><a href="https://crieit.now.sh/upload_images/f8263442a5d4752e3f87ecfc2636a7855fbb474986113.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f8263442a5d4752e3f87ecfc2636a7855fbb474986113.png?mw=700" alt="among-us-bot-limit-040.png" /></a></p> <p>各プレイヤーが Discord 上で、 <strong>自分の色と同じリアクションの絵文字</strong> をクリックすると、 Among Us のプレイヤーと Discord ユーザーがリンクされる。</p> <p>この状態でゲームを開始すると、 プレイヤーの生存状態や会議のシーンにあわせて、自動的にボイスとスピーカーのミュートが変化するようになる。</p> <p>なお、ゲーム内で死んだプレイヤーは、 探索中にミュートが解除される一方で、死んでいないプレイヤーは探索中はスピーカーもミュートになって聞こえない状態となる。<br /> このため、死者同士の <strong>いわゆる霊界チャット</strong> も行える。</p> <p>その他、細かい使い方は、 bot の <code>.au help</code> コマンドなどで確認して欲しい。</p> <p>例えば、 bot が task と discuss のステージ変更を取りこぼしてしまった場合も、 <code>.au force</code> コマンドを使って手動でステータスを変更することも可能だ。</p> <p>以上が簡単な紹介だ。</p> <p><strong>快適な Among Us ライフを!</strong></p> <h2 id="メモ"><a href="#%E3%83%A1%E3%83%A2">メモ</a></h2> <p>AutoMuteUs とは直接関係ないけど、 気づいたことなど。</p> <ul> <li>Among Us は Alt+Enter で フルスクリーンとウィンドウを切り替えられる。 AmongUsCapture はどちらでも OK。</li> <li>Among Us のプレイヤー名と、 Discord の名前は揃っていた方がわかりやすい。 Discord の名前はサーバー毎に「ニックネーム」として名前が変更できるので、 Discord の名前を Among Us の名前に揃えておくと良い。<br /> <a href="https://crieit.now.sh/upload_images/9607fd0e0ddeab82616189e149426dae5fbb475d218df.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9607fd0e0ddeab82616189e149426dae5fbb475d218df.png?mw=700" alt="among-us-bot-limit-041.png" /></a></li> <li>ストアで購入できる Among Us だけでなく、 AutoMuteUs のほうも <a target="_blank" rel="nofollow noopener" href="https://www.patreon.com/automuteus">https://www.patreon.com/automuteus</a> にて $1/月~ 課金できるぞ。 $5/月 以上支援すれば 公式bot の 優先ミュートbotが貰えて快適にプレイできるはずだ。 公式bot の維持にはだいぶコストがかかっていることが明らかなので、気に入ったら支援してあげよう。</li> </ul> advanceboy