Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
CiecleCIでもくもく会を支える技術
Search
threetreeslight
August 23, 2019
Technology
70
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
CiecleCIでもくもく会を支える技術
2019-08-23-CiecleCIでもくもく会を支える技術
threetreeslight
August 23, 2019
More Decks by threetreeslight
See All by threetreeslight
実録 採用一投入魂
threetreeslight
0
49
Bottleneck is You
threetreeslight
0
140
Japan Office Society オフィスはスタートアップの成長を助長するのか?阻害するのか?
threetreeslight
0
140
スタートアップは見極められたくない
threetreeslight
0
65
VPoEの責務とは
threetreeslight
0
100
Ego vs higher self
threetreeslight
0
53
Performance Hack 101
threetreeslight
0
120
複数のスタートアップを 通して得た失敗と学び
threetreeslight
0
95
How to probe prometheus & grafana. What is helm
threetreeslight
0
69
Other Decks in Technology
See All in Technology
セルフサービスのオブザーバビリティ基盤をOpenTelemetryで作る / Building a Self-Service Observability Platform with OpenTelemetry
ymotongpoo
2
280
Argo CDとAtlantisで実現するインフラ管理のセルフサービス化──小規模SREチームで支えるプラットフォーム
cassius7
0
160
【ゲームメーカーズスクランブル2026】『Shadowverse: Worlds Beyond』UIとアニメーションで実現する最高のユーザー体験を叶えるプロトタイピング
cygames
PRO
1
500
Claude Codeを「使うほど育つ」AI秘書にするノウハウ
minorun365
PRO
33
32k
AIに書かせて、プラットフォームで縛る ― EKSプラットフォームで実践した責任境界と権限設計
elmodev09
0
560
AI 時代のスタートアップエコシステ厶から考究する技術的負債との向き合い方
m3m0r7
PRO
3
2.7k
[Kiro Meetup #7] Kiro Crew Dive Deep
konippi
0
350
人にやさしく、AIにやさしく、書き手を選ばないIaCのガードレール再考 / Rethinking IaC Guardrails for Humans and AI Alike
kohbis
5
1.2k
「大丈夫そう?」をObservabilityで確かめる
mrmtsu
0
160
AIエージェントの権限管理 3: Agentic RAG の Fine grained access control 編
ren8k
1
270
AIエージェントを最高のパートナーに育てる方法|評価と判断軸を育てる5つのステップ
koichiaoki
1
160
白金鉱業Meetup Vol.25 アウトカムが二値のデータに対するCausal Impact
brainpadpr
0
310
Featured
See All Featured
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
680
30 Presentation Tips
portentint
PRO
1
410
Un-Boring Meetings
codingconduct
0
430
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
470
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
35
2.8k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.7k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
770
Become a Pro
speakerdeck
PRO
31
6.3k
Building Applications with DynamoDB
mza
96
7.2k
Fireside Chat
paigeccino
43
4k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.9k
Git: the NoSQL Database
bkeepers
PRO
432
67k
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