あくびのあくびしないブログ

エンジニアを目指す学生のブログ兼備忘録

golangのコードにCircleCIを適用してみた話

去年の秋から、ずっとお前を使ってみたかったんだよ。
めっちゃ今更CircleCIをいじってみたので備忘録的なものを書くぞ。

最初に、CircleCIのドキュメントは以下。

circleci.com

CircleCIとは

まずCIってなんだよ、ということにならないために勉強。。。

CIとは、継続的インテグレーション(Continuous Integration)の略。 ソフトウェアを健全な状態に保つことを目的とし、ビルド/テストを常に行うことで問題の発見を迅速に行う開発手法です。

メリットは、というと

チーム開発の生産性、効率、満足度が向上します。 コードにおける問題の迅速な発見と解決につながります。 不具合の少ない高品質なプロダクトの提供を実現します。

コードの品質保証に一役買ってくれる開発手法って感じですね。
メリットに、チーム開発の生産性や満足度の向上も挙げられているところを見るとモチベが上がる!

CircleCIとは、CIを行うためのツールの一つです。他にもGithub ActionsとかJenkinsとかあります。
CircleCIは他のツールと比べ、ビルド/テストの処理速度が早い、らしい。

やりたいこと

ローカルリポジトリからコードをpushしたらテストを実行するって感じにしたい。
継続的デリバリーの方はとりあえず保留。。。デプロイ周りの自動化もやりたいよね。

やったこと

JWTをつかった認証をgoで実装した時のコードを再利用します。
その時の記事が以下。宣伝ですね

GoでJWTを使って認証してみる - Qiita

本流がmasterブランチなので、ブランチ切っときます。developってお名前。無難。

めっちゃちょっとだけテストを書いて、ローカルでテストを通します。よしよし。 f:id:yawn_yawn_yawn:20200309212705p:plain

とりあえず通るテストを用意したので、CircleCIをいじっていきます。

githubアカウントでログインします。
ログインするとこんな感じの画面に。適用したいリポジトリの横のSetUpProjectからセットアップします。 f:id:yawn_yawn_yawn:20200309221509p:plain


セットアップの段階で、configの取得ができます。テンプレがあるのでサクサク。 f:id:yawn_yawn_yawn:20200309221538p:plain

StartBuildingからビルドかけてみます。 configファイルがないので、モーダルにて、どうする?って聞かれます。add configしました...。
けど結局ローカルに手動で設定ファイル作ったので、add manuallyで良かった気がしています。
ビルドはconfigファイル作って、リモートにpushしてからでも良いのではって思います。

次にdevelopブランチにCIのconfigファイルを作成します。
configファイルは、プロジェクトのルートディレクトリに、.circleci/config.ymlとなるように作る必要があります。
config.yamlって名前にするとつらい思いをします。

$ mkdir .circleci
$ touch config.yml


ちなみにconfigの中身はこんな感じ。めっちゃシンプル

# version circleCIのバーション
version: 2

# jobs CIのフローの実行単位を定義
jobs:
  # build workflowsを使わない場合に用意するジョブ
  # プッシュをトリガーとしてCIを実行する時のデフォルトエントリーポイント
  build:
    docker:
      - image: circleci/golang:1.12
    
    working_directory: /go/src/github.com/yawn-yawn-yawn/authJWT-go
    
    # steps 実行内容のリスト
    steps:
      - checkout

      - run: go get -v -t -d ./...
      - run: go test -v ./...

configファイルを書いたらリモートリポジトリにpushします。
するとCIが実行されますー!連携自体はこれで完了!!簡単!

masterにプルリク出して、そこでCIが通るとこんな感じになります。気分が良い。 f:id:yawn_yawn_yawn:20200309221221p:plain


テストなどにエラーがあったりするとこんな感じ。 f:id:yawn_yawn_yawn:20200309221254p:plain

ぐぬぬってなるのでCircleCIの方を確認しにいきます。 テストの結果が吐かれてるのでチェックしたり。 f:id:yawn_yawn_yawn:20200309221317p:plain

導入自体は以上な感じで、サクサクできます!導入コストが低いのめっちゃ助かる。

吐かれたエラーたち

configファイルがない

config.yamlにした場合。こんなエラーを吐かれました。

#!/bin/sh -eo pipefail
# No configuration was found in your project. Please refer to https://circleci.com/docs/2.0/ to get started with your configuration.
# 
# -------
# Warning: This configuration was auto-generated to show you the message above.
# Don't rerun this job. Rerunning will have no effect.
false
Exited with code exit status 1

いやconfigあるが???って気持ちになります。

go getがうまく通ってくれない。

#!/bin/bash -eo pipefail
go get -v -t -d ./...
package auth-jwt/handler: unrecognized import path "auth-jwt/handler" (import path does not begin with hostname)

この開発で最初、go mod init auth-jwtって感じで初期化していたのだけど、どうやらimportのpathはhostnameから書かねばならんらしい。
ということで、module名をgithub.com/yawn-yawn-yawn/authJWT-goに直したところちゃんと通りました。

circleCIのconfigで指定したり、gomodなしで通ったりするんだろうかと思ってみたりした。検証せねば...

go testがつまった

ローカルで通ってたやん!ってなったけどそこじゃなかった。

#!/bin/bash -eo pipefail
go test -v ./...
# github.com/go-sql-driver/mysql
../../go-sql-driver/mysql/driver.go:88:33: undefined: driver.Connector
../../go-sql-driver/mysql/driver.go:99:49: undefined: driver.Connector

このissueみたところ、mysqlのドライバが、go1.9だとサポートできてないっぽい。。。
最初、configをそのままコピーしてたので、1.9だったんですよね...初めから開発環境と揃えろという話。

追加で少しだけ

configファイルにworkflowsを導入

workflowsはジョブを自動化するのに用います。 configファイルはこんな感じになる。

version: 2

jobs:
  test:
    docker:
      - image: circleci/golang:1.12
    working_directory: /go/src/github.com/yawn-yawn-yawn/authJWT-go
    steps:
      - checkout
      - run: go get -v -t -d ./...
      - run: go test -v ./...

workflows:
  version: 2
  testflow:
      jobs:
        - test

先ほどbuildで定義していたジョブを、testって名前に変更して定義します。 testジョブをworkflowsに組み込みます。めっちゃ簡単!
実行するジョブが一個だけだと恩恵がない気がする...複数になった時に管理しやすそうですね。

終わりに

導入手こずったーと思ったけど、思い返すとそこまでじゃないな。。。
CIやるからにはテスト書かなきゃーって気持ちになりますし、品質保証もできるならちゃんと導入しよって思います。
他CIツールとかも使って比べてみたいところです。

JavaScriptでモジュールを作ろうとして詰まった話

タイトル通り!jsでモジュールをつくろーっと思って色々詰まったので備忘録.

作りたいモジュールはこんな感じ.簡易化として今回は,index.htmlmain.jssub.jsで構成.

f:id:yawn_yawn_yawn:20191014104244j:plain

index.htmlでオブジェクトを作成(コンストラクタを呼び出すだけ).他処理は全部jsで行う.

requireが通らない

とりあえず以下のコードを書いてみた.

  • index.html
<html>
    <head>
        <script type="text/javascript" src="./src/bundle.js"></script>
    </head>
    <body>
        <script>
            var obj = new MainObject("new object!");
            console.log("html: ", obj);
            obj.callSub();
        </script>
    </body>
</html>


  • main.js
var subFunction = require("./sub.js");

function MainObject(str) {
    this.string = str;
    console.log("main string: ", this.string);
}

MainObject.prototype.callSub = function () {
    subFunction();
}

module.exports = MainObject;


  • sub.js
function subFunction(){
    console.log("sub call!");
}

module.exports = subFunction;

この状態だとブラウザでは,main.js:1 Uncaught ReferenceError: require is not definedという感じでエラーになります.
requireが使えないみたい.当然sub.jsに定義された関数は使えません.

なんでrequireが使えないのか?

requireはNodeのモジュールシステムで,モジュールをサーバサイドで使うための宣言らしい.なので,フロントでは使えない.

browserifyでビルドしてみる

browserifyって?

Nodeでrequireを使うのと同じように,requireを使ってコードをかけるようになるツール.
ビルドして依存関係を解決する感じです.

インストール

$ npm install -g browserifyでインストール

ビルド

$ browserify main.js -o bundle.jsで,main.jsでインポートされているモジュールを含めた,bundle.jsというファイルを生成します.
できたものはこんな感じ.

  • bundle.js
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
var subFunction = require("./sub.js");

function MainObject(str) {
    this.string = str;
    console.log("main string: ", this.string);
}

MainObject.prototype.callSub = function () {
    subFunction();
}

module.exports = MainObject;
},{"./sub.js":2}],2:[function(require,module,exports){
function subFunction(){
    console.log("sub call!");
}

module.exports = subFunction;
},{}]},{},[1]);

見覚えのあるコードも混ざってますね.
この状態で,index.htmlからbundle.jsを呼び出してみます.
変更箇所だけ.

  • index.html
<head>
    <!-- <script type="text/javascript" src="./src/main.js"></script> -->
    <script type="text/javascript" src="./src/bundle.js"></script>
</head>

すると,
index.html:8 Uncaught ReferenceError: MainObject is not definedっていうエラー.
MainObjectがないらしい.なんで?

ブラウザからモジュールが使えない理由

browserifyでビルドしても,フロントで使える仕様にはならないっぽい.
JSのモジュール定義って色々種類があるみたいなんですよね.AMDとか,CommonJSとか.初めて知った. それらを総括しているのがUMDっていうらしい.

AMD(Asynchronous Module Definition)
モジュールを非同期でロードしてくれる.依存関係の解決をビルドするとかじゃなく,実行時に行う. フロントでモジュールをまとめて読み込むときに使えそう.
requireJSっていうフレームワークがあるので,実装する時は使おう...
require()は使わない.

CommonJS
主にサーバサイドを開発するための仕様.モジュールを,exportしてrequireで使用する.
browserifyでビルドするとこの仕様になるのかな.

UMD(Universal Model Identifinition)
AMDとCommonJSの総括.
UMDのモジュールはクライアント,サーバの両方で利用可能.

standaloneオプションを使ってみる

browserifyでビルドする際,standaloneオプションを付加します.

standaloneオプションってなんぞ?

モジュールの名前を指定して,UMDのモジュールを作成するためのオプションです.
フロントでもサーバサイドでも使えるモジュールを作成してくれるってことか.

今回は,main.jsからMainObjectっていう名前でモジュールを書き出してみる.ややこしいネーミングした感じが否めない.

$ browserify main.js -s MainObject -o bundle.js

これで,作成されたbundle.jsはこんな感じ.

  • bundle.js
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.MainObject = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
var subFunction = require("./sub.js");

function MainObject(str) {
    this.string = str;
    console.log("main string: ", this.string);
}

MainObject.prototype.callSub = function () {
    subFunction();
}

module.exports = MainObject;
},{"./sub.js":2}],2:[function(require,module,exports){
function subFunction(){
    console.log("sub call!");
}

module.exports = subFunction;
},{}]},{},[1])(1)
});

前のbundle.jsと似てるかもだけど,ちょこちょこ違いますね....

以上で,ちゃんと動いてくれます.
ブラウザとかのデベロッパーツールで確認してみると,コンソールがこんな感じになります.

main string:  new object!
html:  MainObject {string: "new object!"}
sub call!

htmlから,main,subのモジュールが使えてる.よしよし.いい感じですね.

おまけ:minify化を含めて自動化する

gulp + browserifyでビルドとminify化を一発でできるようにしてみました.

ちょっとmain.jsに追加している部分があります.

  • main.js
MainObject.prototype.count = function () {
    const names = [
        "Amy",
        "Basil",
        "Clara",
        "Desmond",
        "Ernest",
        "Fanny",
        "George",
    ]
    names.map(name => console.log(name.length));
}

gulpfileを作ります.大まかな流れは,ビルド -> トランスパイル -> minify化っていう感じ.

  • gulpfile.js
var gulp = require('gulp'),
    uglify = require('gulp-uglify'),
    source = require('vinyl-source-stream'),
    buffer = require('vinyl-buffer'),
    browserify = require('browserify'),
    babel = require('gulp-babel')

gulp.task("build", function() {
    return (
        browserify("./src/main.js", {standalone:"MainObject"})
        .bundle()
        .pipe(source("bundle.js"))
        .pipe(buffer())
        .pipe(babel({presets: ['@babel/env']}))
        .pipe(uglify())
        .pipe(gulp.dest("./src/"))
    );
});

なぜトランスパイルするのか?
ES6表記(アロー関数とかconstとか)を含んでいると,minify化する部分(uglify)でエラーがおこるっぽい.なので,トランスパイルする必要があります.うーん厄介. syntaxエラーを出されると少し落ち込む...

まとめ

js沼すぎ!!!モジュール作ってみようかなーと思い立ってから,動く形にするまで結構苦労した気がします.jsだけでフロントやるのもなかなか大変ですね.
向き合ったの初めてなんですけど,記述自体,アロー関数とか変数のスコープとか,なかなか曲者かな....

色々盛りすぎて底なし沼みたいになってませんこれ?
TypeScriptとかも気になってるんですけど,闇が深そう.マイペースに勉強していきます....

ここまで読んでいただき,ありがとうございます!

CA Tech Challenge 学生版ヒダッカソン -API編- に参加した話

タイトル通り,CyberAgentさんのインターン「CA Tech Challenge 学生版ヒダッカソン -API編-」に参加してきました!

f:id:yawn_yawn_yawn:20190924141104j:plain

www.cyberagent.co.jp

このインターンに参加した経緯

インターン以前に参加したインターンである「CA Tech Dojo サーバサイド(Go)編」にて参加権をいただいたため,参加させていただきました!
当時,無理だろうなーと思っていたのでめちゃめちゃ嬉しかったです...!

ハッカソンとなると,自分では力不足だろうなーと,すごく抵抗を持っていたので,「Challengeするぞー」という心持ちでした.(前日はめちゃめちゃ緊張してました.)

日々の話

1日目

会場について,Dojoで会った仲間や社員さんの顔がちらほらと見えたので少し落ち着けたと思いますw

ただ参加者全員が集まると,参加者がめちゃめちゃつよそうに見えました(小並感).
午前中に会社説明や競技の説明をしていただいて,お昼休憩でした.この時点で「あーこわーえー」って感じで.緊張やら不安やら色々感じていました.

開始の合図がすごくゆるーっとしていたので,ゆるーっと開発に入りました.まず環境構築がはちゃめちゃに時間がかかりました.Dockerに不慣れだったことや,開発時のpathが通っていなかったり.「これ今日開発まで行かないんじゃないか」くらいに不安になりました....

苦労して環境構築を終えて,機能の実装に入りました.
まずはじめに,この機能がないと他が進まないかもと言われていた機能を実装しました.クッキーを使う機能だったため,調べながら,またメンターさんに質問しながらひたすらコードを書いてました.レスポンスの形式や何を実装するのかなど,仕様書の読み込みの大切さを改めて実感しました...!
一つ機能ができると,関連している機能の実装が30分足らずでできたため,一人で感動してました...!

次の機能の実装に入り,SQLを構成していたら1日目が終わりました.
1日目からものすごい得点を取ってる方がいて,本気ですごいと思いましたし,自分がまだまだ力不足だなと感じました.

初日を終えて,本当に時間との戦いだなーと思いつつ,「自分のできることを全力でやりきろう」と自分の中で納得していました.

1日目夜

夜は私を含め4人で集まって作業していました.気持ち的には寝ないでもいいかなと思う部分もあり....APIの実装や,環境を整えたりと相談などしながら進めていました.
途中で解散して,寝ようと思ったのですが,色々考えはじめたら寝ることができなくて,結局1時間半ほどしか寝ませんでした.

2日目

午前中は仕様書の修正点や前日の振り返りを少し行なった後,社員さんからLTがありました.
Dojo期間中に開催された「サーバーサイド祭り」にて聞いた話とほぼ同じ内容でしたが,改めて仕事や各事業の特徴について理解できたと思います.

午後からは再び開発.この日は本当にひたすら機能の実装を行なっていました.
初日の夜にやったことのデバッグが思った以上に大変でした.
複数の機能を同時にテストしたので,どこにバグがあるのか発見することにすごく時間がかかってしまった....良い方法を事前に考えておくべきだったなと思います.
また,ある機能の実装で,どうしても納得したい部分があったので,そこに結構時間を使ってしまったのが失敗だったなと思います.
実装が簡単な部分の見極めが甘かった...(戒め).
ラスト1時間で機能を2つほど実装できたので,時間配分がすごく大切だったなと感じました.
これについては,ハッカソンでこそ学べることだなと思いました...!

終了2分前のテスト待ち人数がものすごかった...ほぼ全員いたのでは...?

終了後に,仲間と色々話す時間があり,そこで課題について話し合ったりしました.みんなそれぞれに反省点があったので,色々吸収させていただきました.

最後にメンターさんから個別にフィードバックをいただけました.
改善点と良かった点,両方ともしっかり見ていてくださっていて嬉しかったです.つよくなれるよう精進したいと思います!(葛巻さんありがとうございます!)

最後に

ハッカソンであったこともあり,2日間緊張しっぱなしでした.後半になって一周回って楽しくなってきていた気はします.

良かった点もあれば,反省点もたくさん見つかったハッカソンでした!自分がまだまだ成長できるなと実感できる良い機会になったと思います!

f:id:yawn_yawn_yawn:20190924140547j:plain
2日間お世話になりました!ありがとうございました!!

gulp を使って Sass を気持ちよく書く

ブログでなにやるかーと唸ってましたが,「備忘録を貯めておけばいいじゃん」と思ったので.

n番煎じでも気にしないメンタルを持とう...

今回は,Sassのコンパイルをgulpで自動化しようーって話です.

Sassとは?

Sass(サース)とは,CSSを変数や関数を用いて書くことができるCSSの拡張言語です.

Sass(サース、英: Syntactically Awesome Stylesheets)は、ハンプトン・キャトリンが設計しネイサン・バイゼンバウムが開発したスタイルシート言語である。

wikioedia

CSSセレクターをネスト(入れ子)したり,複数のSassファイルから1つのCSSファイルを作成したりと様々なことができます.

公式サイトがすごくおしゃれ...

gulpとは?

gulp(ガルプ)とは,タスクを自動化できるタスクランナーです.

ファイルの変更を監視して,変更があったら何らかの処理を自動で実行するみたいなことができます.

公式サイトがすごく赤い.

同系統のもので,Grunt(グラント)があります.gulpより前にあったタスクランナーで,gulpより記述が難しいらしい. 使ったことがないので何とも言えない....

スクランナーとは?

スクランナーとは,タスク(処理,課題etc...)を自動化してくれるツールのことです.
タスクの具体例は,Sassのコンパイルだったり,画像圧縮,CSSのminifyなど.


事前準備

gulpを使う前に使うツールのインストールや作業環境を作ります.

Node.jsのインストール

まずNode.jsのインストールを行います. Node.jsの公式サイトからインストールします.

Macの場合はHomeberwを使って環境を作ることも可能です. Homebrewを用いた方法は以下の記事がすごく丁寧でわかりやすかったです.

qiita.com

作業用ディレクトリを作成

適当な場所にディレクトリとSassファイルを作ります.

ツリーは以下のようにしておきます.
今回はhatena-gulpディレクトリ以下で作業します.

hatena-gulp/
├── css
└── sass
    └── sample.scss

ちなみに,sample.scssはこんな感じ.めっちゃてきとうに.

$blue: #00f;
$red: #f00;
.thing{
    color: $blue;
    .red{
        background-color: $red;
    }
}

最後にhatena-gulpディレクトリに移動しておきます.

使用するモジュールのインストール

npmの初期化

初期化します.package.jsonが作成されます.

$ npm init -y

-yオプションをつけると,package.jsonが自動で作成されます.

オプション無しだと,パッケージの名前や説明などを対話形式で設定できます.

初期状態のpackage.jsonはこんな感じ.

{
  "name": "hatena-gulp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

モジュールのインストール

使用するモジュールをインストールします.
今回はgulpgulp-sassというモジュールを使います.

// gulp グローバルインストール
$ npm install gulp -g

// gulp ローカルインストール
$ npm install gulp --save-dev

// gulp-sass ローカルインストール
$ npm install gulp-sass --save-dev

グローバルと開発用(ローカルインストール)の違いとは

グローバルインストールを行うと,コマンドを実行する際実行ファイルまでのpathを省略することができます.また,どこのディレクトリでもコマンドの実行が可能になります.今回はgulpコマンドを使用するため,グローバルインストールを行いました.

ローカルインストールを行うと,package.jsonと同じ階層にnode_modulesというディレクトリが作成されます.この中にインストールしたモジュールが入ります.ローカルインストールした場合,pathが通っていないためコマンドの実行ができません.


最後にpackage.jsonscriptsに追記します.こんな感じになります.

{
  "name": "hatena-gulp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "gulp": "gulp"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "gulp": "^4.0.2",
    "gulp-sass": "^4.0.2"
  }
}


この状態でのツリーはこんな感じ.

hatena-gulp
├── css/
├── sass/
│    └── sample.scss
├── node_modules/
│    └── 色々...
├── package.json
└── package-lock.json


実際に動かしてみる

いよいよgulpの登場です.
まずコンパイルから,そのあとに自動化をやっていきます.

コンパイル

gulpでは,タスクをgulpfile.jsに記述していきます.

では,早速gulpfile.jsにタスクを書いていきます.package.jsonと同じ階層にgulpfile.jsを作成して,以下のように書きます.

// モジュールの読み込み
var gulp = require("gulp");
var sass = require("gulp-sass");

// タスクを作成
// gulp.task("タスク名", 実行される処理)
gulp.task("sass", function(){
    return (
        gulp.src("sass/**/*.scss") // 取得するファイル
        .pipe(sass({outputStyle:"expanded"})) // コンパイル時のオプション
        .pipe(gulp.dest("./css")) // 保存先
    );
});


それから,作業用ディレクトリ(hatena-gulpディレクトリ)に移動して,以下のコマンドを実行します.

$ gulp sass

実行後,cssディレクトリ以下にsample.cssというCSSファイルができていると思います.

これで,コンパイルするというタスクの作成ができました.

コンパイルの自動化

今の状態だと,コンパイルが必要になるたびに自分でコマンドを叩かなくてはいけません.

そこを,ファイルを監視して,変更があったら自動でコンパイルしてくれるようにします.

gulpfile.jsにタスクを書いていきます.以下を追記します.

// タスクを作成
// gulp.watch(”監視するファイル”, 実行される処理)
gulp.task("sass-watch", function(){
    return gulp.watch("sass/**/*.scss", function(){
        return (
            gulp.src("sass/**/*.scss")
            .pipe(sass({outputStyle: "expanded"}).on("error", sass.logError))
            .pipe(gulp.dest("./css"))
        );
    });
});


追記後,以下コマンドを実行することで,ファイルの監視をしてくれます.

$ gulp sass-watch

sample.scssファイルを適当に書き換えて保存すると,自動でコンパイルが実行され,CSSファイルが更新ていると思います.

終わりに

私が個人でwebアプリケーションを作っている時に,CSSを生で書いていました....色の変更や数値の変更の際,「変数とか無いのかよー」と思いながら書いていたので...もっと早く知るべきだったなあーと思います.

少しでも「コンパイルとか面倒くさそうだしなあー」と思っている方の助けになればと思います.

ここまで読んでいただき,ありがとうございます!

CA Tech Dojo サーバーサイド(Go)編 に参加してつよくなった話

www.cyberagent.co.jp

初めての投稿がこれとかめっちゃドキドキする. タイトル通り,CyberAgentさんのインターン「CA Tech Dojo サーバーサイド(Go)編」に参加してきました!

f:id:yawn_yawn_yawn:20190902171016j:plain

結論からいうと.

本当にすべてが最高でした...!

思い出なんて安いものじゃなくて,これは力にしないとダメだって本気で思えるインターンでした. この熱量を言葉に残しておきたいと思って書いてます.読んでくださる方の心に少しでも残るものがあればと思います.

言葉にしたいことが多過ぎてすごく長くなってしまったので...気が向いた時に読んでいただけると幸いです...!もはやブログというか,感謝の連続なので...お許しください.

私がこのインターンに参加した経緯

個人的にwebアプリケーションを作っていて,就職はweb系のことができる企業がいいなーと軽い感じでした. ある時に,参加しようとしていた合同説明会で,CAの公聴会が開催されると決まって通知が来ました.

CAってアメーバの?あそこか??って,最初は半信半疑というか. 私自身,アメーバのユーザで,私が小学生,中学生の頃にインターネットリテラシーを学んだあそこか!?っていう気持ちでした.

そこで真っ先に予約とって,公聴会を聴きにいきました.そこで完全に落ちたと言うか. 若手が成長できる土壌がすごく整っていること,軸の強さ,幅の広さにひたすら感動しました. ここで働けたら,めちゃめちゃ成長できるじゃん!って思ってからがすごく早かったです.

速攻でインターンを探して,そこでサーバサイドを見つけた時は本当に運命を感じました...! 絶対ここは通るんだって気持ちで,ESを書いて,ポートフォリオをサクッと作ってみて.ES通った時は,は???まじ???うそ???え???と,まあ混乱しました.

次に面接がありまして.もともと面接はあまり得意ではなかったのですが,ここまできたら受かってやるって気持ちでいっぱいでした....プレゼンはあまり緊張しない私ですが,面接は結構緊張しました.初のビデオ面接でしたし.そのわりに,「覚えた言葉は絶対喋らない,自分の思ったこと,伝えたいことで勝負しよう」という謎の強気メンタルで挑みました.

始まってから,まず画面の威圧感().面接担当の人事さん(我らがゴッティさん)がすごくつよい()方で!一瞬おぅまじかって思いました.そこからすぐに,よっしゃー喋るぞーー!っていうノリで面接しました.

話すだけ話して,その場で合格と言ってもらえた時は,嬉しすぎて震えました...!通話が切れた後のはしゃぎ方がひたすらすさまじかった記憶があります....インターン時に「面接で,よく喋る子だなーと思った」と仰っていただけたので,割と普段通りに話すことができてたのかなーと思いました.

毎日の話

1日目

実はめちゃめちゃ緊張しました.技術的に強い人が多いし,自分は歯が立たないんじゃないかとか,片道1時間半で通いなので,疲れで保たないんじゃないかとか.

全くそんなことはなかった.

インターンでもそうだったのですが,チームの人がきた瞬間,もしくは喋りかけることができるタイミングでは,自分からガンガン話すようにしていたので,開始直後からめちゃめちゃうるさかったと思います(すみません).周りがそれにノッてきてくれて始まる前から超仲良しになっていました...Bチームのメンバー,本当にありがとう!

社員さんの紹介や,2週間一緒に頑張る仲間たちの自己紹介がすごく多様で.不安がどっかいきました.

午後からハンズオンでの演習で,チームで補い合いながら演習をして,課題発表の後,開発開始. 課題がゲームAPIの開発で,あまりやったことのない毛色の課題でした.全員が開発する基礎課題と,個人でアイディアを出して開発する応用課題に別れていて,これは最終形が面白くなりそうだなーと思っていました.

夜は人事さんの粋な計らいもあって,いきなり仲間の10人ほどで飲み会に!初日から飲み会かよ!!って.自宅に着いたのが0時30分とかでした.

2日目

この日は1日開発してました! 午前中はチームメンバーに色々と教えてました.もともと教える側に回ることが多かったので,教えることで得られる気づきもあって,自分はここがわかってないのかと認識できました.

午後は自分の開発をひたすら続けていました.作業記録をどうやってとるかすごく悩んでた爪痕があって懐かしさが....

このインターンでは毎日,日報という形で自分の作業や課題を残すということを行なっていたのですが,2日目にして,日報の書き方のコツの指導をしていただきました.

2日目から日報の書き方がガラッと変わっていきました.仲間の日報を読むのが楽しくなりました...!

夜はまた,4人で飲みにいきました!そして帰宅が0時30分とかでした!遅い!

f:id:yawn_yawn_yawn:20190902165015j:plain
いつみても「アホやろこいつら」と思ってしまう

3日目

この日も基本的に開発をしてました. どういうエラーが出るかとかをまとめて,しっかり記録してを繰り返していました.

この辺りからもうレビューの話が出ていて,嘘やろ早すぎやろ?って感じてました.

焦ったらいいものはできないから,というメンタルで,自分にできることを120%やるんだって思って作業してました....みんな強すぎやろ.

この日は人事さんとメンターさんと飲み会でした!仲間のこととか,技術のこととかアホな話とか,たくさんしてました!

4日目

この日は結構,開発で苦戦しました.

なかなか納得できる形にできなかったので,メンターさんに質問を投げてみました.答えじゃなくヒントとして応じてくださって,理解と納得を同時に得ることができました!本当にベストな回答をしてくださる!と感動していました.見習っていきたい.

また,この日はメンターさんがワークという名の勉強会を開いてくださいました.実務では何が大切で,どんな問題が起きてなど,実務に本当に近いレベルで話をしていただきました. 私がこのインターンで開発したAPIの,応用課題の基盤はこの勉強会だったと思います...! そのくらい心に残った話でした!

この辺りから自分でも謎の拒食が始まって,めっちゃ色々な人に心配されました...今ではあれ本当なんだったの?ってくらい食べてます.

5日目

折り返しの日!基礎課題がなんとか1週目に終わって,この日から応用課題のことを考え始めました.

自分の記録に「ユーザのいかなる入力も信用してはならない」って書いてあって面白い. gitについて,branchのことがやっとわかり始めてきて,gitって面白いなーと思い始めた頃です.まさかあんな大作を作るとは思っていなかった(後述).

午後にメンターさんとの面談がありました.このインターンを通して何を身につけるか,どう振る舞うかを修正できた面談でした.本当に的確なアドバイスをしていただいたと思います.本当にありがとうございます!

5.5日目 - サーバーサイド祭り!-

インターンとは別でイベントに参加させていただきました! ここでも本当に様々な仲間と出会えて,みんな強いな...!と思いながら楽しみました.

実際の仕事のことなどを聞くことができる機会はそうそうないことだと思うので,一字一句逃さないように聞いていました!貴重な機会を作ってくださってありがとうございます.

夜は,まあ飲みに行きました. 一次会でとんでも暴露をしたので,とんでも空気にしてしまった感じはありますが,めちゃめちゃ楽しかったです!聞いてくれた仲間たち,本当にありがとうございます!

二次会でめちゃめちゃ盛り上がって,終電は逃しました!どこでオールするかで色々悩んでもらえました.むしろ私よりみんなが悩んでいてちょっと不思議な気分でした....

6日目

席がシャッフルしての初日! 仲間も応用課題に入っているだけあって,やっていることが結構ばらばらでした.

人に聞きつつ自分の知見も広げるぞーと思っていたのですが,私のやっていることをすでにやっていた人がなかなかに少なくて.逆に教えられる知識を蓄えられるならそれもありか!と前向きに捉えて開発していました.

だからこそ最後のフィードバックタイムがすごく面白く感じました.自分のやっていることをどれだけ人に伝えることができるか,理解してもらえるかの挑戦だったと思います.

また,この日,人生初のコードレビューをしていただきました!マージのミスなどで一部,自分でもあれ?と思う場所があって.まだまだ伸び代があるんだなーと再実感しました...!

レビュー自体がとても丁寧で,学びに繋がる部分がとても多かったです.コードレビューって本当に大切なんだなと実感しました.レビューできる側になりたい...精進せねば.

この日の夜が女子会でした!インターン仲間で唯一の同性と,人事の方と内定者バイトの方で飲み会.すごくおしゃれなお店に連れて行っていただきました!ピザが本当に美味しかったです.

f:id:yawn_yawn_yawn:20190902165440j:plain

7日目

午前中はレビュー箇所の修正に当てていました.丁寧なレビューをいただいたので丁寧に直そうと思って修正していました.

午後からはRedisと仲良くしつつ,仲間に教える時間も多かったです. この辺りから,自分からも応用課題についてのしっかりした共有ができるようになってきて,アウトプットが楽しくてしょうがありませんでした!

また,だいぶ大きな事件になっていた,awsの障害について勉強会を開いていただきました.すごくタイムリーな話題でしたが,資料,説明ともとても綺麗にまとまっていて,良いインプットになりました!ありがとうございます!

夜は人事の方やメンターの方と飲み会でした!ゴッティさんのめちゃめちゃアツい人生であったり,このインターンの企画の裏にある思いだったり,すごく濃い話をしていただけました!

8日目

せっかく通勤時間が長いし,電車酔いもほぼしないみたいだし.

ということで,迷惑だろうなと考えつつ電車内で作業してました.主に設計をでしたが,ここまでの開発で,設計が開発の基盤だということが身にしみていたこともありまして.2時間近く考え込んでいました!

CAについてから早速実装.前日までにRedisの基本的な部分を実装できていたこともあり,すごくスムーズに開発を進めることができました!

残りの時間は,次にやりたい課題もすでに見つかっていたので,仲間たちから意見をもらいつつ,パッケージの選定やテストをしていました.

また,お昼前に,社内見学をさせていただきました...!どのフロアも活気があって,雰囲気がとても明るくて素敵でした...!集中部屋とオフィスの雰囲気の違いがすごかった. カフェでホットチョコをいただきました.めっちゃ美味しかった...!

f:id:yawn_yawn_yawn:20190902161837j:plain

夜は別のインターンで東京に来ている,大学の先輩と飲みました.先輩もすごく楽しそうに話をしてくださいました.誘ってくださってありがとうございます!

9日目

この日は実質開発最終日だったので,めちゃめちゃ気合い入れました. 自分のやっていることになんとか一区切りつけようって思いでした.

色々とデプロイを忘れていたことをハッと思い出してすぐ確認することから始めてました.デプロイで動くかはすごく大切な部分なので...!

その後,前日夜にテストしておいたパッケージの導入を行いました.わからないことを聞きに来てくれる仲間もいて,アウトプット楽しいーにまにまという感じになっていました.

夜は人事さんやメンターさんへのサプライズのために買い物に行ってきましたー.喜ばせるというか楽しませるというか,本気で感謝している気持ちがすごく強かったです.(Cチームの愛が凄まじかったです.)

10日目!最終日!

午前中の,割とギリギリにバグを見つけて急いで修正してました.本物の業務ってこんな感じなのかなーと思いつつ.なんとか滑り込みで直し切りました.よかった.

午後からはチームでのコードレビューでした.自分がする側にたつチャンスもそうそうないので,突っ込めるところはどんどん突っ込んでいくぞという意気込みでした.このインターンでいただいたレビューにめっちゃ影響受けていると思います.ありがとうございます.

最終のプレゼンでは,お世話になった人事さん,メンターさんがたくさん来てくださって,前に立つのが少し嬉しかったです...!

3分という短い時間で,2週間の成果と感謝と成長をどこまで伝えられたかはわかりませんが,少しでも伝わっていたなら幸いです.(懇親会でプレゼンを褒めていただいた時の嬉しさが半端じゃなかったです.さらに伸ばしていきたい)

仲間のプレゼンもすごく面白くて,それぞれ目の付け所がやっぱりすごいな!と感じました.みんなすごく成長してる!って実感がありました.

f:id:yawn_yawn_yawn:20190903171314j:plain

また,プレゼン後に「CA Tech Challenge」の参加権獲得者の発表と,インターンでの表彰がありました.

www.cyberagent.co.jp

何よりもびっくりしたことが...

「CA Tech Challenge」の参加権獲得者に,自分の名前が!!!

まさか呼んでいただけるとは全く思っていなかったので,おいおいマジか!という気持ちでした!むしろ今もです.表彰されている仲間が本当にみんな凄い人たちで,自分も近づけるように精進せねば.と思いっぱなしでした.

最後にチームのメンターさんとの面談があり.今回のインターンのこと,今後のことや課題など,本当に貴重なアドバイスをいただけました!本当に最後まで感謝の気持ちでいっぱいです!

そして懇親会!もちろん終電はない!

一次会でまさかの人事さん,メンターさんからサプライズがあって,すごく感動しました!ほんとうにありがとうございます...家宝にします!

f:id:yawn_yawn_yawn:20190902161306j:plain

二次会ではこちらからサプライズ.メンターさんが笑ってくださっていたのがめちゃめちゃ嬉しかったです!隣で飲んでいたテーブルの方々がノッてきてくれて,なぜか誕生日のお祝いになっていたのがハイライトでした.

三次会は本当にとんでもメンバーだったなと思います.だんだん部屋がメルトダウンしていく様子が本当に面白かった.時間が過ぎていくのがとても惜しかったです!また会いたい!ゴッティさん,本当に遅く(早く?)までありがとうございました!

日々のまとめとして

めちゃめちゃ長くなっていて自分でも引いている.

前述しましたが,インターンでは毎日日報を書いていました.そこに,メンターさんが毎日コメントしてくれるのが,モチベーションに直結していました.

成長を感じてくれているのが自分だけではないということがすごく励みになっていました.ほんとうにありがとうございます.

また,自分でもアホほど体力が続いたなーと思います.仲間やメンターさんから元気を吸い取っていた疑惑があるレベルで.ほぼ毎日飲みに行ったり外食に付き合ってくれた仲間たちに感謝です.本当にありがとう!

後日談

私のgitの理解が本当に浅くて,なかなか辛い思いをしました....ブランチってどのタイミングで切ればいいねんとか,マージ怖すぎやろとか.

同じように悩んでる仲間がいて,「ゼミでも使うし,これを機にgitの資料作るかーw」って言ったら,「いいやん,ぜひ!」ってのってくれた仲間がいまして. おっと需要があるのか?って思ってサクサク作ってしまいました!

出来上がってみたらスライド94枚!誰が読むねん!って長さです.噛み砕くだけ砕いて,知識だけで終わらせないようにしたらこうなってしまった.

共有したその日に一通りやってくれた仲間がいて,レビューもすごく丁寧で.嬉しさで寝れないレベルでした!ありがとう!

アウトプットから得られる知識の多さに改めて感動しました.曖昧なまま教えるなんて絶対にできないからこそ大量のインプットが生まれてくる実感がありました.

その後でこのブログというか,感想というか感謝を書いてます.このインターンを通して開発に対する考え方がガラッと変わりました.

アウトプットの大切さを肌で感じられたとしみじみ感じます. 次はRedisのアウトプットに繋げる!ぞ!

最後に

こんなに一緒にいたいと思える仲間ができたのは生まれて初めてです...!

当日は全然泣かなかったのに,後になってめっちゃ泣きました.すごく寂しい.全力で走り抜けた,本当に終わってほしくない2週間でした.

最速で成長を実感できたと思います!

最高の仲間と,最高の環境で,一緒に過ごせて本当に最高でした!

ありがとうございました!

f:id:yawn_yawn_yawn:20190902161245j:plain
2週間お世話になりました!ありがとうございました!!