$30 off During Our Annual Pro Sale. View Details »

CiecleCIでもくもく会を支える技術

 CiecleCIでもくもく会を支える技術

2019-08-23-CiecleCIでもくもく会を支える技術

threetreeslight

August 23, 2019
Tweet

More Decks by threetreeslight

Other Decks in Technology

Transcript

  1. CiecleCI
    でもくもく会を
    ⽀える技術
    CircleCI
    ユーザーコミュニティミートアップ on 2019-08-23
    by @threetreeslight
    1 / 32

    View Slide

  2. こんにちわ!
    2 / 32

    View Slide

  3. whoami
    NTT
    系SIer (SE -> sales)
    ⾳楽系スタートアップ CTO
    メディア系スタートアップ 創業
    国内EC
    創業
    越境系発送代⾏サービス 1
    号エンジニア
    Repro
    創業兼CTO
    -> VP of Engineering
    -> VP of PeopleOps
    Akira Miki
    @threetreeslight
    コードが書けるイベントおじさん
    3 / 32

    View Slide

  4. 今⽇話すこと・話さないこと
    話さないこと
    Repro
    でのCircleCI
    活⽤の詳細
    話すこと
    Repro
    とCircleCI
    利⽤どころ
    ↑スポンサートークっぽいの⼀応⼤事↑
    CircleCI
    でイベント開催を⾃動化している話
    4 / 32

    View Slide

  5. 早速ですが
    スポンサートークいきます
    5 / 32

    View Slide

  6. What's
    Repro?
    6 / 32

    View Slide

  7. Customer
    Engagement Platform
    7 / 32

    View Slide

  8. Web/App MA tool
    Right message
    To the right person
    At the right time
    8 / 32

    View Slide

  9. T2D3
    cf. Tech Crunch - The SaaS Adventure
    9 / 32

    View Slide

  10. Traffic
    毎⽇10
    億を超えるイベントデータを処理
    AI
    でユーザーを⾃動セグメント
    毎⽇2
    億近いプッシュなど施策を配信
    10 / 32

    View Slide

  11. そんなRepro
    での
    CircleCI
    利⽤
    11 / 32

    View Slide

  12. ⾊々使っています
    Kick to build Image on image build server
    Kick to test on EC2 cluster and collect artifacts
    Attach github label about risk
    Run plan and apply Terraform
    12 / 32

    View Slide

  13. そんなハイトラフィック
    を楽しみにたい⽅へ
    13 / 32

    View Slide

  14. We are hiring
    14 / 32

    View Slide

  15. 宣伝
    終了
    15 / 32

    View Slide

  16. それでは本題
    参ります
    16 / 32

    View Slide

  17. イベント
    継続は⼤変
    1.
    企画するのが⼤変
    2.
    作成するのが⾯倒
    3.
    運営に気を使う
    17 / 32

    View Slide

  18. これを解決していく
    18 / 32

    View Slide

  19. 企画するのが⼤変
    もくもく会の内容を固定し、企画要素を削る
    19 / 32

    View Slide

  20. 作成するのが⾯倒
    オーガナイザーが複数いる場合は特に⾒合う。
    =>
    ⾃動化しましょう
    20 / 32

    View Slide

  21. Setup Config
    01 const moment = require('moment');
    02 const { EventDirCreator } = require('./event_dir_creator.
    03 const { Slack } = require('./slack.js');
    04 const { ConnpassEventCreator } = require('./connpass_even
    05
    06 Slack.setup(process.env.SLACK_API_TOKEN);
    07
    08 // TODO: replase oauth api token
    09 EventDirCreator.setup(process.env.GITHUB_API_TOKEN);
    10
    11 const nextEventNum = EventDirCreator.getNextEventNum();
    12
    13 EventDirCreator.createDirWithNum(nextEventNum);
    14 EventDirCreator.createPullRequestWithNum(nextEventNum);
    21 / 32

    View Slide

  22. Setup Config
    16 const eventDate = moment().day('Saturday').add(21, 'd').f
    09 EventDirCreator.setup(process.env.GITHUB_API_TOKEN);
    10
    11 const nextEventNum = EventDirCreator.getNextEventNum();
    12
    13 EventDirCreator.createDirWithNum(nextEventNum);
    14 EventDirCreator.createPullRequestWithNum(nextEventNum);
    15
    17 const connpassEventSettings = {
    18 group: 'shinjuku-moku',
    19 title: `Shinjuku Mokumoku Programming #${nextEventNum}`
    20 subTitle: 'The Art of Mokumoku Programming',
    21 startDate: eventDate,
    22 startTime: '11:00',
    23 endDate: eventDate
    3W
    後の開催⽇を指定
    21 / 32

    View Slide

  23. Setup Config
    18 group: 'shinjuku-moku',
    19 title: `Shinjuku Mokumoku Programming #${nextEventNum}`
    20 subTitle: 'The Art of Mokumoku Programming',
    21 startDate: eventDate,
    22 startTime: '11:00',
    23 endDate: eventDate,
    24 endTime: '18:00',
    14 EventDirCreator.createPullRequestWithNum(nextEventNum);
    15
    16 const eventDate = moment().day('Saturday').add(21, 'd').f
    17 const connpassEventSettings = {
    25 participation: [
    26 {
    27 attendeeType: '
    ⾏きます', maxAttendees: 10, payDoor:
    イベントのメタ情報を突っ込む
    21 / 32

    View Slide

  24. Setup Config
    53 {
    54 required: true,
    55 title: '
    本もくもく会は、⾃⼰紹介と当⽇やることを記述したPul
    56 answerType: 'radiobutton',
    57 options: ['
    問題なく遂⾏できる', '
    やったことないので当⽇まで
    58 },
    47 {
    48 required: true,
    49 title: '
    本もくもく会は、slack
    でcommunication
    を取ります。
    50 answerType: 'radiobutton',
    51 options: ['
    問題なく遂⾏できる', '
    やったことないので当⽇まで
    52 },
    59 {
    60 required: true,
    61 title: '
    もくもくして得た学びや気付き、成果をもくもく会終了時
    ⺠度の担保を⽬的としたアンケートも作る
    21 / 32

    View Slide

  25. Setup Config
    66 descriptionPath: `${__dirname}/../connpass.md`,
    58 },
    59 {
    60 required: true,
    61 title: '
    もくもくして得た学びや気付き、成果をもくもく会終了時
    62 answerType: 'radiobutton',
    63 options: ['
    ノープロブレム', 'LT
    初⼼者ですががんばります']
    64 },
    65 ],
    67 preview: true,
    68 };
    69
    70 (async () => {
    71 const eventUrl = await ConnpassEventCreator.create(conn
    イベント本⽂の位置を指定
    21 / 32

    View Slide

  26. Automatically Create event with Puppeteer
    01 const puppeteer = require('puppeteer');
    02 const fs = require('fs');
    03
    04 const logger = console;
    05
    06 const ConnpassEventCreator = {};
    07
    08 ConnpassEventCreator.getDescription = path => fs.readFile
    09
    10 ConnpassEventCreator.create = async (settings) => {
    11 /* eslint-disable no-await-in-loop */
    12
    13 const user = process.env.CONNPASS_USER;
    14 const password = process.env.CONNPASS_PASSWORD;
    22 / 32

    View Slide

  27. Automatically Create event with Puppeteer
    23 await page.goto('https://connpass.com/login/');
    24 const loginAreaSelector = '#login_form';
    25 await page.type(`${loginAreaSelector} input[name="usern
    26 await page.type(`${loginAreaSelector} input[name="passw
    27 await page.click(`${loginAreaSelector} button[type="sub
    28 await page.waitForSelector('.EventCreate');
    29
    19
    20 // --------------------------------------------------
    21 // Login to connpass
    22 // --------------------------------------------------
    30 // --------------------------------------------------
    31 // Move to group page and create event
    32 // --------------------------------------------------
    33 if (typeof (settings group) === 'string') {
    めちゃめちゃがんばる
    22 / 32

    View Slide

  28. Automatically Create event with Puppeteer
    78 // delete exist value
    79 await page.$eval(`${timeAreaSelector} input[name="end_d
    80 await page.keyboard.press('Backspace');
    81 // input data
    82 await page.type(`${timeAreaSelector} input[name="end_da
    83 // Unforcus from input form to close datepitcker
    84 await page.click(`${timeAreaSelector} th`);
    85 // Wait to close datepicker animation. Datepicker overw
    86 await page.waitFor(500);
    73 // wait for fill end date automatically
    74 await page.waitFor(500);
    75
    76 // focus input form
    77 await page.click(`${timeAreaSelector} input[name="end_d
    87
    アニメーションに殺意を覚える
    22 / 32

    View Slide

  29. Config circleci
    01 # Javascript Node CircleCI 2.0 configuration file
    02 #
    03 # Check https://circleci.com/docs/2.0/language-javascript
    04 #
    05 version: 2.1
    06
    07 orbs:
    08 puppeteer: threetreeslight/[email protected]
    09
    10 jobs:
    11 build:
    12 docker:
    13 - image: circleci/node:8
    14 steps:
    23 / 32

    View Slide

  30. Config circleci
    07 orbs:
    08 puppeteer: threetreeslight/[email protected]
    01 # Javascript Node CircleCI 2.0 configuration file
    02 #
    03 # Check https://circleci.com/docs/2.0/language-javascript
    04 #
    05 version: 2.1
    06
    09
    10 jobs:
    11 build:
    12 docker:
    13 - image: circleci/node:8
    14 steps:
    puppeteer install orb
    作って
    23 / 32

    View Slide

  31. Config circleci
    96 nightly:
    97 triggers:
    98 - schedule:
    99 # Run evey friday
    100 cron: "0 0 * * 5"
    101 filters:
    102 branches:
    103 only:
    104 - master
    91 requires:
    92 - build
    93 filters:
    94 branches:
    95 only: master
    105 j b
    nightly build
    する
    23 / 32

    View Slide

  32. 訪れる幸せ
    24 / 32

    View Slide

  33. ちなみに
    circleci
    と⽇本からではconnpass server
    への
    network latency
    が異なるので(
    致し⽅がない)sleep
    の微調整が必要
    25 / 32

    View Slide

  34. 運営に気を使う
    timetable
    にあわせ、いちいちslack
    のreminder
    設定
    したり連絡したりするの⾟い。
    => firebase function + slash command
    で凌ぐ
    26 / 32

    View Slide

  35. slack remind and command
    01 // Prepare for event start
    02 //
    03 // 1. Create vol-xx channel
    04 // 1. Set lunch and due reminder
    05 // 1. Set lunch poller
    06 // 1. Set announce event channel to general
    07
    08 const logger = console;
    09 const { Slack } = require('./slack.js');
    10
    11 const Preparation = {};
    12
    13 Preparation.start = async (slackToken, num) => {
    14 Slack.setup(slackToken);
    27 / 32

    View Slide

  36. slack remind and command
    15 const currentChannelName = `vol-${num}`;
    16
    17 logger.info(`channel name is ${currentChannelName}`);
    18
    19 await Slack.create_channel(currentChannelName);
    10
    11 const Preparation = {};
    12
    13 Preparation.start = async (slackToken, num) => {
    14 Slack.setup(slackToken);
    20 const channelId = await Slack.get_channel_id(currentCha
    21
    22 // Event channel announce
    23 const generalId = await Slack.get_channel_id('general')
    24
    開催回のchannel
    作って
    27 / 32

    View Slide

  37. slack remind and command
    25 // for introduction
    26 Slack.message(generalId, `
    今⽇のshinjuku mokumoku slack
    27 Slack.message(channelId, 'wifi: \nhttps://gitpitch.com/
    28 Slack.command(channelId, '/remind', `<#${channelId}> \n
    19 await Slack.create_channel(currentChannelName);
    20 const channelId = await Slack.get_channel_id(currentCha
    21
    22 // Event channel announce
    23 const generalId = await Slack.get_channel_id('general')
    24
    29 "@channel
    わからないことがあるときはまず以下を参照しましょう :poi
    30 \n
    31
    イベントページ: https://shinjuku-moku.connpass.com/\n
    32 introduction
    資料: https://gitpitch.com/shinjuku-mokumoku/s
    33 \
    通知を設定
    27 / 32

    View Slide

  38. slack remind and command
    34 *:warning: Attention :warning:*\n
    35 -
    会場IP
    からのスクレイピング・クローリングコードの実⾏は⽌めてくださ
    36 -
    本イベントは[
    アンチハラスメントポリシー](http://25.ruby.or.jp
    37 -
    どなたでもblog
    などにあげられるよう写真撮影を許可していますので、そ
    38 -
    途中退出される場合は、**PR
    に**
    今⽇の成果をお出しください\n
    28 Slack.command(channelId, /remind , <#${channelId}> \n
    29 "@channel
    わからないことがあるときはまず以下を参照しましょう :poi
    30 \n
    31
    イベントページ: https://shinjuku-moku.connpass.com/\n
    32 introduction
    資料: https://gitpitch.com/shinjuku-mokumoku/s
    33 \n
    39 " at 11:30`);
    40
    41 // Lunch
    42 Sl k d( h lId '/ ll' '"
    昼⾷どこらへんが好き?
    諸注意も再連絡しつつ
    27 / 32

    View Slide

  39. slack remind and command
    41 // Lunch
    42 Slack.command(channelId, '/poll', '"
    昼⾷どこらへんが好き?
    34 *:warning: Attention :warning:*\n
    35 -
    会場IP
    からのスクレイピング・クローリングコードの実⾏は⽌めてくださ
    36 -
    本イベントは[
    アンチハラスメントポリシー](http://25.ruby.or.jp
    37 -
    どなたでもblog
    などにあげられるよう写真撮影を許可していますので、そ
    38 -
    途中退出される場合は、**PR
    に**
    今⽇の成果をお出しください\n
    39 " at 11:30`);
    40
    43 Slack.message(channelId, '
    ランチリスト: \nhttps://github.
    44 Slack.command(channelId, '/remind', `<#${channelId}> "
    45 @channel
    もうすぐlunch
    です。ランチアンケートへの回答しましょう!\
    46
    ランチリスト: \n
    47 https://github.com/shinjuku-mokumoku/shinjuku-mokumoku/bl
    公開されていないSlack API
    を使ってコマンドを叩き
    27 / 32

    View Slide

  40. slack remind and command
    54 // checkout
    55 Slack.command(channelId, '/remind', `<#${channelId}> "
    56 @channel checkout
    まであと1h
    です!成果のまとめなどしていきましょう
    57
    発表は *1.5-3 min +
    質問 0-2min / person*
    です!
    58 " at 16:00`);
    59 Slack.command(channelId, '/remind', `<#${channelId}> "
    60 @channel checkout
    の10min
    前です!\n
    49 Slack.command(channelId, '/remind', `<#${channelId}> "@
    50
    51 // check templature
    52 Slack.command(channelId, '/remind', `<#${channelId}> "

    53
    61
    今⽇の成果項を更新しshinjuku-mokumoku
    へPR
    をお願いします :muscle
    62
    発表ではchrome cast
    を使います。
    終了のリマインドまで⼊れる
    27 / 32

    View Slide

  41. deploy firebase
    01 const { execSync } = require('child_process');
    02 const Octokit = require('@octokit/rest');
    03
    04 const octokit = new Octokit();
    05 const logger = console;
    06
    07 const owner = 'shinjuku-mokumoku';
    08 const repo = 'shinjuku-mokumoku';
    09
    10 const pullRequestNum = async () => {
    11 if (process.env.CIRCLE_PR_NUMBER) {
    12 return process.env.CIRCLE_PR_NUMBER;
    13 }
    14
    28 / 32

    View Slide

  42. deploy firebase
    27 const hasFunctionDiff = async () => {
    28 const num = await pullRequestNum();
    29 logger.debug(`Pull Request is https://github.com/shinju
    30
    31 const res = await octokit.pulls.listFiles({ owner, repo
    32 const functionFileNames = res.data.map(files => files.f
    33 logger.debug(`Change Files: ${functionFileNames.join(',
    34
    35 return functionFileNames.length > 0;
    36 };
    p [ ] ;
    25 };
    26
    37
    38 const deploy = async () => {
    変更があるときだけfirebase functions
    にdeploy
    されるようにしておき
    28 / 32

    View Slide

  43. build and deploy
    01 # Javascript Node CircleCI 2.0 configuration file
    02 #
    03 # Check https://circleci.com/docs/2.0/language-javascript
    04 #
    05 version: 2.1
    06
    07 orbs:
    08 puppeteer: threetreeslight/[email protected]
    09
    10 jobs:
    11 build:
    12 docker:
    13 - image: circleci/node:8
    14 steps:
    29 / 32

    View Slide

  44. build and deploy
    61 - run:
    62 name: Deploy Master to Firebase
    63 command: npm --prefix functions run deploy
    g
    55 - functions_npm_modules-
    56 - run: npm --prefix functions install
    57 - save_cache:
    58 paths:
    59 - functions/node_modules
    60 key: functions_npm_modules-{{ checksum "functio
    64
    65 event:
    66 docker:
    67 - image: circleci/node:8
    68 steps:
    build & deploy
    していく
    29 / 32

    View Slide

  45. 訪れる幸せ
    30 / 32

    View Slide

  46. ちなみに
    community
    で使うfirebase
    の登録クレカ、どうす
    るか悩みますよね
    31 / 32

    View Slide

  47. まとめ
    誰やるにならないように常に⾃動化
    puppeteer
    の可能性無限⼤
    slack
    のslash command
    の可能性無限⼤
    CI
    にまかせていく
    sheduled workflow
    便利! orb
    便利!
    network latency
    には気をつける
    時間⾒つけてイベント⾃動作成が外でもできるよう
    package
    化していこう
    32 / 32

    View Slide