Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
CiecleCIでもくもく会を支える技術
Search
threetreeslight
August 23, 2019
Technology
0
45
CiecleCIでもくもく会を支える技術
2019-08-23-CiecleCIでもくもく会を支える技術
threetreeslight
August 23, 2019
Tweet
Share
More Decks by threetreeslight
See All by threetreeslight
Bottleneck is You
threetreeslight
0
82
Japan Office Society オフィスはスタートアップの成長を助長するのか?阻害するのか?
threetreeslight
0
97
スタートアップは見極められたくない
threetreeslight
0
35
VPoEの責務とは
threetreeslight
0
58
Ego vs higher self
threetreeslight
0
35
Performance Hack 101
threetreeslight
0
78
複数のスタートアップを 通して得た失敗と学び
threetreeslight
0
65
How to probe prometheus & grafana. What is helm
threetreeslight
0
29
Istio Traffic Management
threetreeslight
0
29
Other Decks in Technology
See All in Technology
B2B SaaSから見た最近のC#/.NETの進化
sansantech
PRO
0
750
ハイパーパラメータチューニングって何をしているの
toridori_dev
0
140
Application Development WG Intro at AppDeveloperCon
salaboy
0
180
【Startup CTO of the Year 2024 / Audience Award】アセンド取締役CTO 丹羽健
niwatakeru
0
990
Engineer Career Talk
lycorp_recruit_jp
0
160
初心者向けAWS Securityの勉強会mini Security-JAWSを9ヶ月ぐらい実施してきての近況
cmusudakeisuke
0
120
これまでの計測・開発・デプロイ方法全部見せます! / Findy ISUCON 2024-11-14
tohutohu
3
370
適材適所の技術選定 〜GraphQL・REST API・tRPC〜 / Optimal Technology Selection
kakehashi
1
170
Introduction to Works of ML Engineer in LY Corporation
lycorp_recruit_jp
0
110
なぜ今 AI Agent なのか _近藤憲児
kenjikondobai
4
1.4k
フルカイテン株式会社 採用資料
fullkaiten
0
40k
社内で最大の技術的負債のリファクタリングに取り組んだお話し
kidooonn
1
550
Featured
See All Featured
Docker and Python
trallard
40
3.1k
Raft: Consensus for Rubyists
vanstee
136
6.6k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
6
410
[RailsConf 2023] Rails as a piece of cake
palkan
52
4.9k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
665
120k
Fontdeck: Realign not Redesign
paulrobertlloyd
82
5.2k
Why Our Code Smells
bkeepers
PRO
334
57k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
A Philosophy of Restraint
colly
203
16k
Art, The Web, and Tiny UX
lynnandtonic
297
20k
Teambox: Starting and Learning
jrom
133
8.8k
Large-scale JavaScript Application Architecture
addyosmani
510
110k
Transcript
CiecleCI でもくもく会を ⽀える技術 CircleCI ユーザーコミュニティミートアップ on 2019-08-23 by @threetreeslight 1
/ 32
こんにちわ! 2 / 32
whoami NTT 系SIer (SE -> sales) ⾳楽系スタートアップ CTO メディア系スタートアップ 創業
国内EC 創業 越境系発送代⾏サービス 1 号エンジニア Repro 創業兼CTO -> VP of Engineering -> VP of PeopleOps Akira Miki @threetreeslight コードが書けるイベントおじさん 3 / 32
今⽇話すこと・話さないこと 話さないこと Repro でのCircleCI 活⽤の詳細 話すこと Repro とCircleCI 利⽤どころ ↑スポンサートークっぽいの⼀応⼤事↑
CircleCI でイベント開催を⾃動化している話 4 / 32
早速ですが スポンサートークいきます 5 / 32
What's Repro? 6 / 32
Customer Engagement Platform 7 / 32
Web/App MA tool Right message To the right person At
the right time 8 / 32
T2D3 cf. Tech Crunch - The SaaS Adventure 9 /
32
Traffic 毎⽇10 億を超えるイベントデータを処理 AI でユーザーを⾃動セグメント 毎⽇2 億近いプッシュなど施策を配信 10 / 32
そんなRepro での CircleCI 利⽤ 11 / 32
⾊々使っています 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
そんなハイトラフィック を楽しみにたい⽅へ 13 / 32
We are hiring 14 / 32
宣伝 終了 15 / 32
それでは本題 参ります 16 / 32
イベント 継続は⼤変 1. 企画するのが⼤変 2. 作成するのが⾯倒 3. 運営に気を使う 17 /
32
これを解決していく 18 / 32
企画するのが⼤変 もくもく会の内容を固定し、企画要素を削る 19 / 32
作成するのが⾯倒 オーガナイザーが複数いる場合は特に⾒合う。 => ⾃動化しましょう 20 / 32
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
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
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
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
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
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
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
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
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
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
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
訪れる幸せ 24 / 32
ちなみに circleci と⽇本からではconnpass server への network latency が異なるので( 致し⽅がない)sleep の微調整が必要
25 / 32
運営に気を使う timetable にあわせ、いちいちslack のreminder 設定 したり連絡したりするの⾟い。 => firebase function +
slash command で凌ぐ 26 / 32
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
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
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
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
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
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
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
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
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
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
訪れる幸せ 30 / 32
ちなみに community で使うfirebase の登録クレカ、どうす るか悩みますよね 31 / 32
まとめ 誰やるにならないように常に⾃動化 puppeteer の可能性無限⼤ slack のslash command の可能性無限⼤ CI にまかせていく
sheduled workflow 便利! orb 便利! network latency には気をつける 時間⾒つけてイベント⾃動作成が外でもできるよう package 化していこう 32 / 32