Share
mixi tech note #04
#技術書典9 に出典されたミクシィグループエンジニア有志による技術書です。
<< 目次 >>
第一章:Haskellで簡単にCLIツールを作るために
by 松原 信忠 GitHub @matsubara0507
第二章:TIPSTARのアーキテクチャとCloud Spannerとの付き合い方
by 川又 義照 Twitter @4cteru
第三章:機械学習とGCPで作るTIPSTARワイガヤ順位
by 津田 恭平 Twitter @canoefishing
第四章:Behavior TreeでゲームAIに取り組んで思う
by 田那辺 輝
第五章:Gitのindex treeとは結局なんなのか
by 藤田 朱門 Twitter @shumon_84
<< TECH NOTE 一覧 >>
mixi tech note #01
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-01
mixi tech note #02
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-02
mixi tech note #03
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-03
mixi tech note #04
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-04
mixi tech note #05
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-05
mixi tech note #06
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-06
mixi tech note #07
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-07
MIXI TECH NOTE #08
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-08
XFLAG Tech Note Vol.01
https://speakerdeck.com/mixi_engineers/xflag-tech-note-vol-dot-01
XFLAG Tech Note vol.02
https://speakerdeck.com/mixi_engineers/xflag-tech-note-vol-dot-02
MIXI ENGINEERS PRO
Transcript
72 slides
-
·͕͖͑ ຊॻʮmixi tech note #04ʯɺϛΫγΟάϧʔϓʹॴଐ͢Δ༗ࢤୡʹΑͬͯࣥචɾ੍࡞͞Εͨ ٕज़ॻͰ͢ɻ࣮ࡍͷϓϩμΫτ։ൃͷݱͰ༻͞Εٕͨज़ͷɺʑͷۀͷΒɺݸਓతʹௐ ͨ͜ͱͳͲɺࢥ͍ࢥ͍ʹࣥච͍ͯ͠·͢ɻͦͷͨΊɺ֤ষͦΕͧΕͰ͍݁ͯ͠Δ༰ʹͳ͍ͬͯ ·͢ͷͰɺ͖ͳষ͔Β͖ͳॱ൪Ͱָ͓͠Έ͍ͩ͘͞ɻ ·ͨɺຊॻɺϛΫγΟάϧʔϓʹ͋Δٕज़తݟΞΠσΞΛੵۃతʹڞ༗ɾެ։͍ͯ͘͜͠ͱ ͰɺੈͷதʹΑΓྑ͍αʔϏε͕ҲΕग़͢͜ͱΛئͬͯץߦ͞Ε͍ͯ·͢ɻܝࡌ͞Ε͍ͯΔใɺ
ࣥචऀࣗͷڥͰݕূࣥ͠ච͞ΕͨͷͰ͢ͷͰɺ͝ࢀߟʹ͞ΕΔࡍɺࣗ͝ͷͰஅ͠ ͝׆༻͍ͩ͘͞ɻͳ͓ɺจষදݱʹ͖ͭ·ͯ͠ɺࣥචऀࣗͷݴ༿Ͱ͑ͨ͘ɺϑϥϯΫͳදݱͱ ͳ͓ͬͯΓ·͢͜ͱ͝ཧղ͍͚ͨͩΕͱࢥ͍·͢ɻ CTO ࣨ DevRel άϧʔϓ Ұಉ ˗ຊॻʹؔ͢Δ͓͍߹Θͤઌ ɹ https://twitter.com/mixi_engineers ˗ϛΫγΟάϧʔϓʹ͍ͭͯ ɹ https://mixi.co.jp/ ˞ʠϛΫγΟʡ ɺ ʠmixiʡ ɺ ʠmixi ϩΰʡ ɺגࣜձࣾϛΫγΟͷඪ·ͨొඪͰ͢ɻ·ͨɺ֤ ࣾͷձ໊ࣾɺαʔϏεٴͼͷ໊শɺͦΕͧΕͷॴ༗͢Δඪ·ͨొඪͰ͢ɻ iii -
࣍ ·͕͖͑ iii ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ
1 1.1 mix ύοέʔδ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 ࡐɿstack-tpls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.3 Stack Template ͔Β࡞ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.4 ϦϙδτϦΛऩू . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.5 ऩू݁ՌΛอଘ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.6 ऴΘΓʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 19 2.1 ࢝Ίʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.2 ΞʔΩςΫνϟ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.3 Cloud Spanner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.4 ऴΘΓʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 31 3.1 ϫΠΨϠॱҐͱ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 3.2 ࡞͢Δ͜ͱʹͳͬͨഎܠ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3.3 ϫΠΨϠࢉग़ͷΞʔΩςΫνϟ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.4 ϫΠΨϠॱҐʹͳΔ·Ͱ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 3.5 ػցֶशͷಋೖʹ͍ͭͯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜͰࢥ͏ 45 4.1 ࢝Ίʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 4.2 σβΠϯύλʔϯͰΈΔ State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 4.3 Behavior Tree ΛઃܭʹऔΓೖΕΔ . . . . . . . . . . . . . . . . . . . . . . . . . . 50 4.4 ҧ͍Λཧղͯ͠׆༻͢Δ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 4.5 ϏδϡΞϥΠζͰ͖ͨͱͯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4.6 Behavior Tree ͕࢘Δͷ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 v -
࣍ 4.7 ݁ͼ . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 ୈ 5 ষ Git ͷ index tree ͱ݁ہͳΜͳͷ͔ 55 5.1 ࢝Ίʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 5.2 Git ͷ commit ʹ͍ͭͯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 5.3 index tree ࠩཧͰͳ͍ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 5.4 index tree ͷ࣮ମͱߏ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 5.5 index tree ͷಾͱͦͷ͑ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 5.6 ࠷ޙʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 ஶऀհ 63 vi -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞Δͨ Ίʹ චऀΑ͘ɺࢥ͍͍ͭͨπʔϧΛ Haskell
Ͱ࣮͠·͢ɻͦͷͱ͖ʹɺίΞͷϩδοΫҎ֎ΛͰ͖ Δ͚ͩ؆୯ʹ࣮͢ΔͨΊͷΛ͍ͯ͠·͢ɻCLI πʔϧΛ࡞Δͱ͖ʹඞཁʹͳΔίΞϩδοΫҎ ֎ͷ෦ͱ͍͏ͷɺͨͱ͑࣍ͷΑ͏ͳͷ͕ߟ͑ΒΕ·͢ɻ • ίϚϯυϥΠϯҾ • άϩʔόϧͳઃఆͷཧ • ϩΪϯάʢग़ྗʣ • ֎෦αʔϏεͱͷΓͱΓ • σʔλετΞʢ؆қతͳʣ චऀɺ͜ΕΒΛ؆୯ʹѻ͑ΔΑ͏ʹ͢Δ͘͠Έͱͯ͠ mix ύοέʔδͱ͍͏ͷΛࣗ࡞ͯ͠ར༻ ͍ͯ͠·͢ɻຊষͰɺ͜ͷ mix ύοέʔδΛར༻ͯ͠εςοϓόΠεςοϓʹ CLI πʔϧΛ࡞Δ ͜ͱͰɺCLI πʔϧΛ؆୯ʹ࡞ΔͨΊͷΛհ͠·͢ɻ·ͨɺͯ͢ͷιʔείʔυ GitHub ͷ matsubara0507/stack-tpls-demo*1 ϦϙδτϦʹ͋Γ·͢ɻ 1.1 mix ύοέʔδ ·ͣ mix ύοέʔδʹ͍ͭͯհ͠·͢*2ɻmix ύοέʔδͦΕࣗମɺ͋Δ 2 ͭͷύοέʔ δΛΈ߹Θ͚ͤͨͩͷ؆୯ͳϥούʔϑϨʔϜϫʔΫͰ͢ɻΈ߹Θͤͨύοέʔδ extensible ύοέʔδͱ rio ύοέʔδͰ͢ɻॱʹհ͠·͢ɻ *1 https://github.com/matsubara0507/stack-tpls-demo *2 https://github.com/matsubara0507/mix.hs 1 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.1 mix ύοέʔδ
extensible ύοέʔδ extensible*3 ύοέʔδ֦ுՄೳϨίʔυͷΑ͏ͳɺߴ֊ΧΠϯυͳσʔλܕΛ؆୯ʹѻ͑Δ ύοέʔδͰ͢ɻߴ֊ΧΠϯυʹ͍ͭͯ͜͜ͰׂѪ͠·͕͢ɺͨͱ͑࣍ͷΑ͏ͳ ToDo ܕͰ؆ ୯ʹྫΛࣔ͠·͢ɻ ී௨ʹ Haskell ͷߏจͰهड़ͨ͠ ToDo ܕ data ToDo = ToDo { todoID :: Int , title :: Text , done :: Bool } ͜ΕΛ extensible Λ͏͜ͱͰ࣍ͷΑ͏ʹهड़Ͱ͖·͢ɻ extensible ه๏ͷ ToDo ܕ import Data.Extensible type ToDo = Record ’[ "id" >: Int , "title" >: Text , "done" >: Bool ] ϑΟʔϧυʹΞΫηε͢Δ߹ person^.#name ͷΑ͏ʹͰ͖·͢*4ɻ͜Ε͚ͩͷ߹ɺΘ͟Θ ͟ extensible Λར༻͢Δར͋Γ·ͤΜɻextensible ه๏ʹΑΔϨίʔυܕͷେ͖ͳརͷҰͭ ɺϨίʔυϑΟʔϧυΛྻϦετߏͷΑ͏ʹڞ௨ͷॲཧΛద༻ͨ͠͏͑ͰΈࠐΊΔͰ ͢ɻͨͱ͑ɺϑΟʔϧυ໊ʢจࣈྻʣͷϦετΛߏங͢Δؔɺ֤ϑΟʔϧυΛίϚϯυϥΠϯ ҾͷΦϓγϣϯͱͯ͠ߏங͢ΔؔΛఆٛͰ͖·͢ɻ ·ͨɺextensible ͷϨίʔυه๏ type એݴʹͳΔͷͰܕΫϥεͷΠϯελϯεΛݸผʹએݴ͢ Δඞཁ͕ͳ͘ͳΔͱ͍͏ར͋Γ·͢ɻͪΖΜɺཪΛฦͤݸʑͷϨίʔυʹରͯ͠ݸผʹએݴ Ͱ͖ͳ͘ͳΔͱ͍͏ܽͱߟ͑ΒΕ·͕͢ɻ *3 https://hackage.haskell.org/package/extensible-0.7 *4 ^ . lens ܥ ύ ο έ ʔ δ ͷ ؔ Ͱ ͢ https://hackage.haskell.org/package/lens-4.18.1/docs/ Control-Lens-Getter.html#v:-94-. 2 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.1 mix ύοέʔδ
rio ύοέʔδ rio ύοέʔδ*5͍ΘΏΔ alt. Prelude ͳύοέʔδͰ͢ɻalt. Prelude ͳύοέʔδͱɺ Haskell ͷඪ४ύοέʔδʹ͋ͨΔ Prelude ϞδϡʔϧͷସʹͳΔύοέʔδͷ͜ͱΛࢦ͠·͢ɻ rio ͷ΄͔ʹ༗໊ͳͷͰɺclassy-prelude ύοέʔδ universum ύοέʔδͳͲ͕͋Γ·͕͢ɺ චऀͬͨ͜ͱ͕ͳ͍ͷͰൺֱͳͲΛ͜͜Ͱ͠·ͤΜɻPrelude Ϟδϡʔϧ҉తʹΠϯϙʔ τ͞Ε·͕͢ɺGHC ͷ NoImplicitPrelude ݴޠ֦ுΛ͏͜ͱͰ҉తʹΠϯϙʔτ͞ΕΔͷΛͱ ΊΔ͜ͱ͕Ͱ͖·͢ɻ͜ͷػೳΛΈ߹ΘͤΔ͜ͱͰ Prelude ϞδϡʔϧͷΘΓʹผͷඪ४ύο έʔδͷΑ͏ͳύοέʔδΛΠϯϙʔτ͠·͢ɻ rio ύοέʔδɺϏϧυπʔϧ Stack *6Λ։ൃ͍ͯ͠ΔνʔϜ͕࡞͠·ͨ͠ɻrio ύοέʔδ ReaderT ύλʔϯͱݺΕΔσβΠϯύλʔϯ*7Λϕʔεʹͯ͠ઃܭ͞Ε͓ͯΓɺ࣍ͷΑ͏ͳ͜ ͱΛ؆୯ʹهड़Ͱ͖·͢ɻ • άϩʔόϧͳઃఆͷཧ • ϩΪϯά rio ύοέʔδʹ RIO env ͱ͍͏ܕ͕͋Γ·͢ɻ͜ΕΛ IO ܕͷΘΓʹར༻͢Δ͜ͱͰʮά ϩʔόϧͳઃఆͷཧʯͱʮϩΪϯάʯΛ؆୯ʹهड़Ͱ͖ΔͷͰ͢ɻ rio Λར༻ͨ͠ϓϩάϥϜͷྫ import RIO main :: IO () main = do opt <- logOptionsHandle stdout False withLogFunc opt $ \logFunc -> runRIO (Env logFunc "Hoge") app data Env = Env { logFunc :: LogFunc -- ϩΪϯάͷͨΊʹඞཁ , getName :: Text } app :: RIO Env () app = do name <- asks getName logDebug $ display ("This is debug: " <> name) logInfo $ display ("This is info: " <> name) logWarn $ display ("This is warn: " <> name) logError $ display ("This is error: " <> name) *5 https://hackage.haskell.org/package/rio-0.1.18.0 *6 https://docs.haskellstack.org *7 ͜ ͜ Ͱ ͷ ղ આ ׂ Ѫ ͠ · ͢ ͕ ɺৄ ͠ ͘ https://www.fpcomplete.com/blog/2017/06/ readert-design-pattern Λࢀর͍ͯͩ͘͠͞ (rio ͷ࡞ऀͷهࣄͰ͢) 3 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.1 mix ύοέʔδ
ͪͳΈʹɺ͜ͷϓϩάϥϜͷ࣮ߦ݁Ռ࣍ͷΑ͏ʹͳΓ·͢ɻ $ stack runghc mix/sample/Main.hs 2020-09-07 22:33:49.378471: [debug] This is debug: Hoge @(mix/sample/Main.hs:23:3) 2020-09-07 22:33:49.381893: [info] This is info: Hoge @(mix/sample/Main.hs:24:3) 2020-09-07 22:33:49.381943: [warn] This is warn: Hoge @(mix/sample/Main.hs:25:3) 2020-09-07 22:33:49.382005: [error] This is error: Hoge @(mix/sample/Main.hs:26:3) extensible ͷຐ๏ rio ͷϩΪϯάඇৗʹศརͰ͕ͪ͢ΐͬͱ͕ͨܽ͠ 2 ͭ͋Γ·͢ɻ 1. ܕΫϥεͷΠϯελϯεએݴ͕ඞཁ 2. withLogFunc ͷར༻͕ಛघ ࣮ɺલड़ͨ͠ϓϩάϥϜΛϏϧυ͢ΔʹԼهͷΑ͏ͳܕΫϥεͷΠϯελϯεએݴ͕ඞཁ Ͱ͢ɻ rio ͷϩΪϯάΛ͢ΔͷʹඞཁͳΠϯελϯεએݴ instance HasLogFunc Env where logFuncL = lens logFunc (\x y -> x { logFunc = y }) ͜ΕͨͩͷϘΠϥʔςϯϓϨʔτͰ͢Ͷɻmix Ͱ RIO env ͷ env Λ extensible ͷϨίʔυ Ͱએݴ͢Δ͜ͱΛલఏʹ͢Δ͜ͱͰɺ͜ͷϘΠϥʔςϯϓϨʔτΛͳ͘͠·͢ɻલड़ͨ͠ϓϩάϥϜ ͷྫΛ mix Ͱهड़͢Δͱ࣍ͷΑ͏ʹͳΓ·͢ɻ mix Λར༻ͨ͠ϓϩάϥϜͷྫ import RIO import Data.Extensible import Mix import Mix.Plugin.Logger as MixLogger main :: IO () main = Mix.run plugin app where plugin :: Plugin () IO Env 4 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.1 mix ύοέʔδ
plugin = hsequence $ #logger <@=> MixLogger.buildPlugin logOpt <: #name <@=> pure "Hoge" <: nil logOpt = #handle @= stdout <: #verbose @= True <: nil type Env = Record ’[ "logger" >: MixLogger.LogFunc , "name" >: Text ] app :: IO () app = do name <- asks (view #name) logDebug $ display ("This is debug: " <> name) logInfo $ display ("This is info: " <> name) logWarn $ display ("This is warn: " <> name) logError $ display ("This is error: " <> name) withLogFunc ͷΘΓʹ Mix.run ͱ Plugin Λ͍·͢ɻ͜ͷ Plugin extensible ͷ͘͠Έ Λར༻͍ͯ͠·͢ɻextensible ͷϨίʔυͷརʮϨίʔυϑΟʔϧυΛྻϦετߏͷΑ͏ ʹڞ௨ͷॲཧΛద༻ͨ͠͏͑ͰΈࠐΊΔʯͱॻ͖·͕ͨ͠ɺ·͞ʹ͜ΕΛར༻͍ͯ͠·͢ʢhseq uence ͷ෦Ͱ͢ʣ ɻPlugin ͷத࣍ͷΑ͏ʹͳ͍ͬͯ·͢ɻ Plugin ܕͷத type Plugin a m env = ContT a m env run :: MonadIO m => Plugin a m env -> RIO env a -> m a run plugin act = (‘runRIO‘ act) ‘withPlugin‘ plugin withPlugin :: (env -> m a) -> Plugin a m env -> m a withPlugin = flip runContT -- MixLogger.buildPlugin Ͱར༻͍ͯ͠·͢ toPlugin :: ((env -> m a) -> m a) -> Plugin a m env toPlugin = ContT -- Data.Extensible ͷؔʢf ͕ Plugin a m Ͱ͢ʣ hsequence :: Applicative f => (Comp f h :* xs) -> f (h :* xs) ContT ͱ͍͏ͷܧଓͱݺΕΔͷͰ͢ɻৄׂ͘͠Ѫ͠·͕͢ɺwithLogFunc ͕·͞ʹܧଓ ʹͳ͍ͬͯ·͢ɻ withLogFunc ͱܧଓ withLogFunc :: MonadUnliftIO m => LogOptions -> (LogFunc -> m a) -> m a 5 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.1 mix ύοέʔδ
-- ܧଓ(Continuation)ͷϞφυτϥϯεϑΥʔϜ༷ͷܕ newtype ContT r m a = ContT { runContT :: ((a -> m r) -> m r) } (a -> m r) -> m r ͷ෦͕ (LogFunc -> m a) -> m a ͱҰக͠·͢Ͷɻޙड़͢Δ mix ͷ ϓϥάΠϯͯ͢͜ͷܧଓͱͯ͠දݱ͓ͯ͠Γɺhsequence ͰͦΕΒΛΈࠐΜͰ env Λ࡞Γ ·͢ɻ ϓϥάΠϯγεςϜ mix ύοέʔδͷ͋Δ matsubara0507/mix.hs ϦϙδτϦʹɺ͍͔ͭ͘ϓϥάΠϯΛҰॹʹஔ ͍ͯ͋Γ·͢ɻݱࡏʢ2020 9 ݄ʣ͋ΔͷҎԼͷ௨ΓͰ͢ɻ • rio ͷϩΪϯά (mix ʹೖ͍ͬͯΔ) • ઃఆ༻ϑΟʔϧυΛ࣋ͭ (mix ʹೖ͍ͬͯΔ) • GitHub API ͷΫϥΠΞϯτʢmix-plugin-githubʣ • Drone API ͷΫϥΠΞϯτʢmix-plugin-droneʣ • Shell ͷ࣮ߦʢmix-plugin-shellʣ • SQLiteʢmix-plugin-persistent-sqliteʣ • XDG σΟϨΫτϦͷΩϟογϡʢmix-plugin-xdgcacheʣ ͪͳΈʹɺ͜ͷϓϥάΠϯγεςϜʢͱ rio ͷϥούʔϑϨʔϜϫʔΫʣ tonatona ͱ͍͏ύο έʔδ*8ʹΠϯεύΠΞ͞Ε·ͨ͠ɻ ֤ύοέʔδͷόʔδϣϯͳͲ ຊʹೖΔલʹɺࠓճར༻ͨ͠ॲཧܥύοέʔδͷόʔδϣϯΛྻڍ͓͖ͯ͠·͢ɿ • GHCʢHaskell ͷॲཧܥʣ: 8.8.4 • StackʢHaskell ͷϏϧυπʔϧʣ: 2.3.1 • extensible : 0.8 • rio : 0.1.18.0 • aesonʢJSON Τϯίʔμɾσίʔμʣ: 1.4.7.1 • githashʢϓϩδΣΫτͷ.git Λղੳͯ͠ར༻Ͱ͖Δʣ: 0.1.4.0 • dotenvʢ.env ϑΝΠϧΛಡΈऔΔʣ: 0.8.0.6 *8 https://github.com/tonatona-project/tonatona 6 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.2 ࡐɿstack-tpls 1.2
ࡐɿstack-tpls ࠓճ stack-tpls ͱ͍͏ CLI πʔϧΛ࡞Γ·͢ɻ͜Ε GitHub ্ʹ͋Δ stack-templates ͱ͍ ͏ϦϙδτϦΛऩू͢ΔπʔϧͰ͢ɻ Stack Template Stack Template ͱɺHaskell ͷϏϧυπʔϧ Stack ͰॳظϑΝΠϧΛੜ͢Δʢstack new ί ϚϯυΛ࣮ߦʣͱ͖ʹੜ͢ΔϑΝΠϧͷͻͳܕΛΧελϚΠζͨ͠ςϯϓϨʔτͰ͢ɻίϚϯυ࣮ ߦ࣌ʹ࣍ͷΑ͏ʹࢦఆͰ͖·͢ɻ $ stack new project-name github:matsubara0507/mix-cli.hsfiles GitHubɾGitLabɾBitbucket ͷ͍ͣΕ͔ʹ stack-templates ͱ͍͏໊લͷϦϙδτϦΛ࡞Γɺͦ ͷϦϙδτϦʹ hsfiles ͱ͍͏֦ுࢠͷϑΝΠϧΛτοϓϨϕϧʹஔ͘͜ͱͰೝࣝ͞Ε·͢ʢ͜͜Ͱ .hsfiles ͷॻ͖ํࣗମׂѪ͠·͢ʣ ɻͨͱ͑ɺGitHub ্ʹ͋Δචऀͷ stack-templates Ϧϙ δτϦ࣍ͷΑ͏ʹͳ͍ͬͯ·͢ɻ ਤ 1.1: GitHub ͷ matsubara0507/stack-templates ϦϙδτϦ ຊߘͰ GitHub ্ͷ Stack Template ΛूΊΔػೳ͚ͩΛ࣮͠·͢ɻ 7 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.3 Stack Template
͔Β࡞ εςοϓόΠεςοϓ ࣍ͷεςοϓͰ࣮͍͖ͯ͠·͢ɻ 1. Stack Template ͔ΒϕʔεΛ࡞ 2. GitHub ͔ΒϦϙδτϦΛऩू͢Δ 3. ऩू݁ՌΛϩʔΧϧʹอଘ͢Δ ·ͣɺ Stack Template Λར༻ͯ͠ CLI πʔϧͷͻͳܕΛ࡞͠·͢ɻ࣍ʹɺ GitHub GraphQL API Λར༻ͯ͠ GitHub ্ʹ͋Δ Stack Template ϦϙδτϦΛूΊ·͢ɻGraphQL Λར༻͢Δ ͜ͱͰɺϦϙδτϦͱϦϙδτϦ্ʹ͋ΔϑΝΠϧΛ 1 ͭͷϦΫΤετͰूΊΔ͜ͱ͕Ͱ͖·͢ɻ GraphQL Ͱޮྑ͘ऩूͨ͠ͱͯ͠ɺίϚϯυΛͨͨͨ͘ͼʹϦΫΤετΛ͍͛ͯΔͱ͘ͳ Γ·͢ɻͦ͜Ͱ࠷ޙʹɺऩू݁ՌΛϩʔΧϧʹอଘ͢Δ͜ͱͰίϚϯυͷ࣮ߦΛߴԽ͠ɺGitHub API Λͬͯߋ৽͍ͨ͠߹ઐ༻ͷϑϥάΛ͢Α͏ʹ͠·͢ɻ 1.3 Stack Template ͔Β࡞ mix ύοέʔδΛར༻ͨ͠ CLI πʔϧΛ࡞Δ༻ʹ Stack Template Λ༻ҙ͍ͯ͠ΔͷͰɺͦΕΛ ར༻ͯ͠ stack new ͠·͢ɻ $ stack new stack-tpls-demo github:matsubara0507/mix-cli.hsfiles ࣮ stack new ͚ͨͩ͠Ͱɺstack build Ͱ͖·ͤΜɻ $ stack build ... [4 of 4] Compiling Main /path/to/stack-tpls-demo/app/Main.hs:3:29: error: parse error on input ʞ-ʟ | 3 | import Paths_stack-tpls-demo (version) | ^ .hsfiles ͷه๏Ͱɺ࡞͍ͨ͠ύοέʔδ໊ʢ͜͜Ͱ stack-tpls-demoʣΛςϯϓϨʔτʹຒΊ ࠐΉ͜ͱ͕Ͱ͖·͢ɻPaths_xxx ͱ͍͏ͷϓϩδΣΫτʹؔͯࣗ͠ಈͰੜ͞ΕΔϞδϡʔϧͰɺ ϓϩδΣΫτϑΝΠϧʹهࡌͨ͠όʔδϣϯͳͲΛऔಘͰ͖·͢ɻCLI πʔϧͷ --version Φϓ γϣϯͳͲʹ͏ͷͰ͕͢ɺxxx ͷ෦ʹ - ͕͑ͳ͍ͷͰ͢ɻ͔͠͠ɺຒΊࠐΉʹ - ͷ··Ͱ ͳ͍ͱ͍͚·ͤΜɻͳͷͰ - Λ _ ʹखͰஔ͖͑Δඞཁ͕͋Γ·͢ɻ 8 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.3 Stack Template
͔Β࡞ ଞʹɺsrc/Lib ͱͳ͍ͬͯΔϞδϡʔϧͷωʔϜεϖʔεΛม͑ͨΓɺgit init Λͨ͠Γ͢Δ ඞཁ͋Γ·͢ɻͦ͏ͯ͠Ϗϧυͨ݁͠Ռɺ࣍ͷΑ͏ͳ CLI πʔϧ͕ͬͦ͘͞Ͱ͖͕͋Γ·ͨ͠ɻ $ stack exec -- stack-tpls-demo not yet implement command. $ stack exec -- stack-tpls-demo --help stack-tpls-demo [options] [input-file] -h --help Show this help text --version Show version -v --verbose Enable verbose mode: verbosity level "debug" $ stack exec -- stack-tpls-demo --version Version 0.1.0, Git revision Tue Sep 1 08:17:16 2020 +0900 (1 commits) ίϚϯυϥΠϯҾ ίϚϯυϥΠϯҾͷΈཱͯʹ System.Console.GetOpt Λ͍·͢ɻ͜Εඪ४ʹΈࠐ ·Ε͍ͯΔίϚϯυϥΠϯҾΛղऍ͢ΔػೳͰ͢ɻextensible ύοέʔδʹ GetOpt Λ֦ுՄೳ ϨίʔυʹରԠͤͨ͞ͷ͕͋Γɺ࣮ࡍʹ͜ΕΛར༻͠·͢ɻ GetOpt ͷ֦ுՄೳϨίʔυ൛ withGetOpt :: MonadIO m => String -- usage Ͱදࣔ͞ΕΔ -> RecordOf (OptionDescr h) xs -- ίϚϯυϥΠϯҾͷઃఆ -> (RecordOf h xs -> [String] -> m a) -- ίϚϯυϥΠϯҾΛΒͬͯಈ࡞͍ͤͨؔ͞Λ͢ -> m a ͍ΘΏΔ usage ͬΆ͍ग़ྗίϚϯυϥΠϯҾ͕ؒҧ͍ͬͯΔͱ͖ʹදࣔ͞Ε·͢ɻͰ͖Ε --help Φϓγϣϯ͕༩͑ΒΕͨͱ͖ʹද͍ࣔͨ͠ͷͰɺusage ༻ͷจࣈྻΛΒ͑ΔΑ͏ʹ֦ு ͨ͠ϞϊΛ༻ҙ͠·ͨ͠ʢwithGetOpt’ʣ ɻ͜ΕΛར༻͠·͢ɻ Main ؔͱίϚϯυϥΠϯҾ -- Main.hs main :: IO () main = withGetOpt’ "[options] [input-file]" opts $ \r args usage -> do - MultiWayIf ͱ͍͏ݴޠ֦ு if | r ^. #help -> hPutBuilder stdout (fromString usage) | r ^. #version -> hPutBuilder stdout (Version.build version <> "\n") | otherwise -> runCmd r (listToMaybe args) where 9 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.3 Stack Template
͔Β࡞ opts = #help @= helpOpt <: #version @= versionOpt <: #verbose @= verboseOpt <: nil type Options = Record ’[ "help" >: Bool , "version" >: Bool , "verbose" >: Bool ] -- ͜ͷΑ͏ͳؔΛ֤ίϚϯυϥΠϯҾ༻ҙ͢Δ helpOpt :: OptDescr’ Bool helpOpt = optFlag [’h’] ["help"] "Show this help text" όʔδϣϯΦϓγϣϯ ίϛοτͳͲɺ Α͍ײ͡ʹόʔδϣϯͷใΛग़ྗ͢Δʹ githash ύοέʔδ*9Λར༻͠·͢ɻ import Data.Version (Version) -- ͜Εඪ४ύοέʔδʹ͋Δ import qualified Data.Version as Version import qualified GitHash build :: Version -> Builder build v = encodeUtf8Builder . fromString $ unwords [ "Version" , Version.showVersion v ++ "," , "Git revision" , GitHash.giCommitDate gi , "(" ++ show (GitHash.giCommitCount gi) ++ " commits)" ] where gi = $$(GitHash.tGitInfoCwd) ͜ͷ build ؔ Main ؔͰ Version.build version ͰݺΜͰ͍ΔͷͰ͢ɻҾͷ vers ion લड़ͨ͠ Paths_xxx Ͱऔಘͨ͠ͷͰɺϓϩδΣΫτͷόʔδϣϯใʹͳΓ·͢ɻ$$(Gi tHash.tGitInfoCwd) Template Haskell ͱ͍͏ Haskell ͷϝλϓϩάϥϛϯάΛར༻ͨ͠ͷ ͰɺϏϧυ࣌ʹ .git ͷใΛूΊͯຒΊࠐΈ·͢ɻ *9 https://hackage.haskell.org/package/githash 10 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.3 Stack Template
͔Β࡞ dotenv Λ͏ GitHub τʔΫϯΛདͷ··ίϚϯυͱͯ͠ೖྗ͢Δͷؾ͕Ҿ͚ΔͷͰɺϫʔΫσΟϨΫτϦ ϗʔϜσΟϨΫτϦʹ͋Δ .env ϑΝΠϧΛಡΈࠐΜͰͬͯ͘ΕΔΑ͏ʹͯ͠Έ·͢ɻ͜Εʹ dotenv ύοέʔδ*10Λ͍·͢ɻ͍ํ؆୯ͰɺMain ؔͷ಄ʹߦՃ͢Δ͚ͩͰ͢ɻ dotenv Λ͏ -- Main.hs main :: IO () main = withGetOpt’ "[options] [input-file]" opts $ \r args usage -> do -- ҎԼ3ߦʢ಄2ߦϗʔϜσΟϨΫτϦ༻ɺ࠷ޙϫʔΫσΟϨΫτϦ༻ʣ homeDir <- getHomeDirectory _ <- tryIO $ loadFile defaultConfig { configPath = [homeDir </> ".env"] } _ <- tryIO $ loadFile defaultConfig { configOverride = True } if | r ^. #help -> hPutBuilder stdout (fromString usage) ... ίϚϯυຊମ ࠷ޙʹίϚϯυຊମΛࡌ͓͖ͤͯ·͢ɻ͜ΕΛϕʔεʹͯ͠ɺঃʑʹ֦ு͞Ε͍͖ͯ·͢ɻͪͳΈ ʹɺ_path ҾʹίϚϯυͷΦϓγϣϯͳ͠ͷҾ͕དྷ·͢ɻࣗΑ͘ઃఆͷϑΝΠϧύεͳͲ ʹ͍·͕͢ɺࠓճར༻͠ͳ͍ͷͰແࢹ͍ͯ͠·͢ɻ GitHub ϓϥάΠϯΛΈࠐΉ runCmd :: Options -> Maybe FilePath -> IO () runCmd opts _path = do -- ϓϥάΠϯΛΈࠐΈ·͢ʢ·ͩ̍ͭͰ͕͢ʣ let plugin = hsequence $ #logger <@=> MixLogger.buildPlugin logOpts <: nil Mix.run plugin cmd where logOpts = #handle @= stdout <: #verbose @= (opts ^. #verbose) <: nil *10 https://hackage.haskell.org/package/dotenv 11 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.4 ϦϙδτϦΛऩू 1.4
ϦϙδτϦΛऩू ͍Α͍ΑίΞͷ࣮Λ࢝Ί·͢ɻ͔͠͠ɺຊߘͷతίΞͷ࣮෦Ͱͳ͍ͷͰదʹׂѪ͠ ·͢ɻ GraphQL API Λ࣮ߦ͢Δ GitHub GraphQL API *11Λར༻͢ΔͨΊʹ mix-plugin-github ϓϥάΠϯύοέʔδͷ Mix.P lugin.GitHub.GraphQL ϞδϡʔϧΛར༻͠·͢ɻ Mix.Plugin.GitHub.GraphQL ͷؔ module Mix.Plugin.GitHub.GraphQL where fetch :: (HasGitHubToken env, FromJSON a) => Text -> RIO E a 1 Ҿͷ Text ܕΫΤϦจࣈྻͰ͢ɻ೦ͳ͕Βɺྑͦ͞͏ͳ GitHub GraphQL API ΫϥΠ Ξϯτ͕ Haskell ʹ·ͩ͋Γ·ͤΜʢ2020 9 ݄ݱࡏʣ ɻͳͷͰ fetch ؔͰɺݺͼग़͠ଆ͕ Α͠ͳʹΫΤϦจࣈྻͰߏங͠ɺͦͷΫΤϦจࣈྻΛ POST ϦΫΤετʹؚΊΔͱ͜Ζ·ͰΛΓ ·͢ɻ·ͨɺGraphQL API Λݺͼग़͢ʹ GitHub ͷτʔΫϯ͕ඞཁͰ͢ɻHasGitHubToken en v RIO env ͷڥ env ͕ GitHub τʔΫϯΛ͍࣋ͬͯΔ͜ͱΛอূ͢ΔܕΫϥεͰ͢ɻ͜Εʹ ΑΓɺfetch ؔͰͷ HTTP ϦΫΤετͰ GitHub τʔΫϯΛར༻Ͱ͖·͢ɻ ͯ͞ɺ͜ͷؔΛ RIO Env ্Ͱ͏ʹίϚϯυຊମʹϓϥάΠϯΛΈࠐΈ·͠ΐ͏ɻ GitHub ϓϥάΠϯΛΈࠐΉ import qualified Mix.Plugin.GitHub as MixGitHub -- Env.hs type Env = Record ’[ "logger" >: MixLogger.LogFunc , "github" >: MixGitHub.Token -- Ճ ] -- Main.hs runCmd :: Options -> Maybe FilePath -> IO () runCmd opts _path = do gToken <- liftIO $ fromString <$> getEnv "GH_TOKEN" -- Ճ let plugin = hsequence $ #logger <@=> MixLogger.buildPlugin logOpts <: #github <@=> MixGitHub.buildPlugin gToken -- Ճ <: nil *11 https://docs.github.com/en/graphql 12 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.4 ϦϙδτϦΛऩू Mix.run
plugin cmd ࣮ࡍʹ͜ͷؔΛݺͼग़͍ͯ͠Δ෦࣍ͷΑ͏ʹͳΓ·͢ɻ fetch ؔΛݺͼग़͢ -- Cmd.hs fetchTplListFromGitHub :: [Repository] -> SearchOpts -> RIO Env [Repository] fetchTplListFromGitHub acc opts = do let query = searchRepositoryQuery "stack-templates in:name" opts logDebug $ "query: " <> display query resp <- GraphQL.fetch query :: RIO Env Response -- ίί let page = resp ^. #data ^. #search ^. #pageInfo repos = acc ++ map (view #node) (resp ^. #data ^. #search ^. #edges) opts’ = opts & #after ‘set‘ (page ^. #endCursor) if page ^. #hasNextPage then fetchTplListFromGitHub repos opts’ else pure repos searchRepositoryQuery :: Text -> SearchOpts -> Text searchRepositoryQuery query sOpt = ... type SearchOpts = Record ’[ "first" >: Int -- Կ݅औಘ͢Δ͔ , "after" >: Maybe Text -- ϖʔδωʔγϣϯͷઌ಄ ] searchRepositoryQuery ؔϦϙδτϦݕࡧ༻ͷΫΤϦͱϦϙδτϦݕࡧ༻ΦϓγϣϯΛࢦ ఆͯ͠ɺGraphQL ΫΤϦΛฦؔ͢Ͱ͢ɻ͜ΕΛͦͷ·· Mix.Plugin.GitHub.GraphQL.fetch ʹ͠·͢ɻ͜ͷͱ͖ʹɺܕऍ RIO Env Response ΛೖΕ͍ͯΔͷɺJSON Λσίʔυͨ͠ܕ ͕ఆ·Βͳ͍ͨΊͰ͢ɻResponse ܕɺ ࠓճͷ GraphQL ΫΤϦͷϨεϙϯε JSON Λ extensible ͷϨίʔυܕͰදݱͨ͠ͷͰɺͨͱ͑࣍ͷΑ͏ͳ JSON Λఆ͍ͯ͠·͢ɻ ࠓճͷ GraphQL ΫΤϦͷϨεϙϯεͱͯ͠ظ͢Δ JSON { "data" : { "search" : { "repositoryCount" : 1, "pageInfo" : { "endCursor" : null, "hasNextPage" : false }, "edges" : [ { "node" : { 13 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.4 ϦϙδτϦΛऩू "name"
: "stack-templates", "nameWithOwner" : "matsubara0507/stack-templates", "object" : { "tree" : { "entries" : [ {"name":"mix-cli.hsfiles", "type":"blob"}, ... ] } } } } ] } } } "pageInfo" ʹϖʔδωʔγϣϯͷใؚ͕·Ε͓ͯΓɺҰճͷϦΫΤετͰ্ݶ 100 ͔݅͠औ ಘͰ͖·ͤΜɻͰ͢ͷͰɺଓ͖͕͋Δ߹ "endCursor" Λ࣍ͷϦΫΤετʹͯ͠ϖʔδΛਐΊ ·͢ɻ"node" Ҏ͕߱ϦϙδτϦͷใʹͳΓ·͢ɻ .hsfiles Λྻڍ͢Δ ࠷ऴతʹཉ͍͠ͷ stack-templates ϦϙδτϦʹ͋Δ .hsfiles ͷใͰ͢ɻͰ͢ͷͰɺϦϙδτ ϦใΛར༻ͯ͠ .hsfiles Λྻڍ͠·͢ɻ ϦΫΤετ݁ՌΛϑΟϧλϦϯάͯ͠.hsfile Λྻڍ͢Δؔ mapHsfilesWithFilter :: [Repository] -> [Hsfiles] mapHsfilesWithFilter = mconcat . map fromGitHubRepository . filter isStackTemplates isStackTemplates :: Repository -> Bool isStackTemplates repo = repo ^. #name == "stack-templates" type Hsfiles = Record ’[ "name" >: Text , "owner" >: Text , "domain" >: Domain -- GitHub or GitLab or BitBucket ] fromGitHubRepository :: Repository -> [Hsfiles] fromGitHubRepository repo = for files $ \file -> #name @= (file ^. #name) <: #owner @= getOwner repo <: #domain @= GitHub <: nil where -- "object" nullable files :: Repository -> [TreeEntry] 14 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.4 ϦϙδτϦΛऩू files
= filter isHsfiles $ maybe [] (\obj -> obj ^. #tree ^. #entries) (repo ^. #object) -- blob ΦϒδΣΫτͰ͔ͭϑΝΠϧ໊͕ .hsfiles ͰऴΘ͍ͬͯΔ isHsfiles :: TreeEntry -> Bool isHsfiles ent = isBlob ent && Text.isSuffixOf ".hsfiles" (ent ^. #name) ·ͣɺϦϙδτϦ໊ʢ"name"ʣ͕ stack-templates ͔Ͳ͏͔ΛνΣοΫ͠·͢ɻ"object" ͕Ϧ ϙδτϦͷτοϓϨϕϧͷϑΝΠϧσΟϨΫτϦͷใʹͳΓɺ"type":"blob" ͳͲ͔Β͔Δ ͱ͓Γɺͨͩͷ git ΦϒδΣΫτͷใͰ͢ɻblob ΦϒδΣΫτͰ͋Γʢͭ·ΓσΟϨΫτϦ͡Ό ͳͯ͘ϑΝΠϧͰ͋Δ͜ͱʣ͔ͭɺϑΝΠϧ໊͕ .hsfiles ͰऴΘ͍ͬͯΔ߹ Stack Template ͷϑΝΠϧͰ͋Δͱஅ͍ͯ͠·͢ɻ ूΊͨใΛग़ྗ͢Δ ͜ΕΒͷؔΛΈ߹Θͤͯ .hsfiles ΛूΊͯɺ͞ΒʹͦΕΒͷใΛग़ྗ͠·͢ɻҎ্ͷؔΛ ࣍ͷΑ͏ʹఆٛ͠·ͨ͠ɻ .hsfiles ΛूΊͯग़ྗ͢Δؔ -- Cmd.hs cmd :: RIO Env () cmd = do logDebug "run: fetch hsfiles" tpls <- fetchTplList forM_ tpls $ \tpl -> logInfo (display $ toStackArg tpl) fetchTplList :: RIO Env [Hsfiles] fetchTplList = -- ࠓ·ͰͷؔΛΈ߹ΘͤΔ mapHsfilesWithFilter <$> fetchTplListFromGitHub [] sOpts where sOpts = #first @= 100 <: #after @= Nothing <: nil ͜Μͳ;͏ʹग़ྗ͞Ε·͢ɻ $ stack exec -- stack-tpls-demo github:matsubara0507/get-opt-cli.hsfiles github:matsubara0507/lib-extensible.hsfiles github:matsubara0507/mix-cli.hsfiles github:matsubara0507/optparse-applicative-cli.hsfiles ... 15 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.5 ऩू݁ՌΛอଘ 1.5
ऩू݁ՌΛอଘ ຖճ GitHub API Λͨͨ͘ͷίετͳͷͰجຊϩʔΧϧϑΝΠϧʹอଘͯ͠ɺߋ৽͍ͨ͠ ߹ઐ༻ͷϑϥάΛ͢Α͏ʹ͠·͢ɻϩʔΧϧϑΝΠϧͷΩϟογϡ XDG Base Directory Specification *12Ͱࢦఆ͞Ε͍ͯΔͷΛ͍·͢ɻHaskell ͷ߹ɺdirectory ύοέʔδʹ͋Δ g etXdgDirectory *13Λ͍·͢ʢrio ͷ߹ RIO.Directory Ϟδϡʔϧʹ͋Γ·͢ʣ ɻ ϑϥάΛ͚Δ ·ͣϑϥάΛՃ͠·͢ɻEnv ʹ with_update ͱ͍͏ϑΟʔϧυΛՃͯ͠ɺίϚϯυϥΠϯ Ҿʹ --update ϑϥάΛՃ͠·͢ɻͦͯ͠ɺ--update ͷΛ with_update ʹ͚ͩ͢Ͱ͢ɻ ߋ৽ϑϥάΛՃ͢Δ -- Env.hs type Env = Record ’[ "logger" >: MixLogger.LogFunc , "github" >: MixGitHub.Token , "with_update" >: Bool -- Ճ ] -- Main.hs main :: IO () main = withGetOpt’ "[options] [input-file]" opts $ \r args usage -> do ... where opts = #help @= helpOpt <: #version @= versionOpt <: #verbose @= verboseOpt <: #update @= updateOpt -- Ճ <: nil type Options = Record ’[ "help" >: Bool , "version" >: Bool , "verbose" >: Bool , "update" >: Bool -- Ճ ] updateOpt :: OptDescr’ Bool updateOpt = optFlag [] ["update"] "Update stack templates list in local cache" runCmd :: Options -> Maybe FilePath -> IO () runCmd opts _path = do *12 https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html *13 https://hackage.haskell.org/package/directory-1.3.6.1/docs/System-Directory.html#v: getHomeDirectory 16 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.5 ऩू݁ՌΛอଘ gToken
<- liftIO $ fromString <$> getEnv "GH_TOKEN" let plugin = hsequence $ #logger <@=> MixLogger.buildPlugin logOpts <: #github <@=> MixGitHub.buildPlugin gToken <: #with_update <@=> pure (opts ^. #update) -- Ճ <: nil Mix.run plugin cmd mix-plugin-xdgcache ͯ͞ɺΩϟογϡͷ෦Λ࡞Γ·͢ɻ࣮ mix-plugin-xdgcache ϓϥάΠϯύοέʔδΛར༻͢ Δͱ؆୯ʹ࣮Ͱ͖·͢ɻϓϥάΠϯΛ͏ʹ·ͣ Env Λ֦ு͠·͠ΐ͏ɻ mix-plugin-xdgcache ϓϥάΠϯΛΦϯʹ͢Δ import qualified Mix.Plugin.XdgCache as MixCache -- Env.hs type Env = Record ’[ "logger" >: MixLogger.LogFunc , "github" >: MixGitHub.Token , "xdgcache" >: MixCache.Config -- Ճ , "with_update" >: Bool ] -- Main.hs runCmd :: Options -> Maybe FilePath -> IO () runCmd opts _path = do gToken <- liftIO $ fromString <$> getEnv "GH_TOKEN" let plugin = hsequence $ #logger <@=> MixLogger.buildPlugin logOpts <: #github <@=> MixGitHub.buildPlugin gToken <: #xdgcache <@=> MixCache.buildPlugin "stack-tpls" -- Ճ <: #with_update <@=> pure (opts ^. #update) <: nil Mix.run plugin cmd ͜ΕͰ XDG σΟϨΫτϦͷΩϟογϡ෦ʢMac ͷ߹ ~/.cacheʣʹ stack-tpls ͱ͍͏ σΟϨΫτϦ͕࡞ΒΕͯɺstack-tpls-demo ͷΩϟογϡϑΝΠϧஔ͖ͱͯ͠ར༻͠·͢ɻ ͯ͞ɺ͋ͱΩϟογϡΛ͏Α͏ʹΞϓϦέʔγϣϯΛ֦ு͠·͢ɻ࣮͘͢͝؆୯Ͱ͢ɻ Ωϟογϡ͕͋ΕΩϟογϡΛ͍ɺͳ͚Ε࠷ޙʹΩϟογϡΛอଘ͢Δؔ withCacheOn Λ ͏͚ͩͰ͢ɻͦͯ͠ɺߋ৽ϑϥά͕ਅͷ߹ΩϟογϡΛফ͢ expireCache ؔΛ͑ྑ͍ ͷͰ͢ɻ 17 -
ୈ 1 ষ Haskell Ͱ؆୯ʹ CLI πʔϧΛ࡞ΔͨΊʹ 1.6 ऴΘΓʹ ΩϟογϡΛՃ͢Δ
-- Cmd.hs cmd :: RIO Env () cmd = do logDebug "run: fetch hsfiles" -- ҎԼͷߦΛՃʢϑϥάͷ༗ແͰΩϟογϡΛআʣ whenM (asks $ view #with_update) $ MixCache.expireCache "stack-teplates" -- ‘withCacheOn‘ "stack-teplates" ΛޙΖʹ͚ͭΔ͚ͩ tpls <- fetchTplList ‘withCacheOn‘ "stack-teplates" forM_ tpls $ \tpl -> logInfo (display $ toStackArg tpl) ͍͢͝؆୯Ͱ͢Ͷɻ 1.6 ऴΘΓʹ stack-tpls 2018 10 ݄͝Ζʢ 2 લʣʹ࡞Γ࢝ΊͨπʔϧͰ͢ɻstack new ͢Δͱ͖ʹί ϐʔ&ϖʔετͯ͑͠ΔʢຖճΕΔʣͷͰɺݸਓతʹׂͱॏๅ͍ͯ͠ΔπʔϧͰ͢ɻmix.hs ͦΕ͔Β͞Βʹ 1 ޙʹ࡞ͬͨύοέʔδͰ͢ɻͰ͢ͷͰɺmix.hs ޙ͔Β stack-tpls ʹಋೖ͠· ͨ͠ɻ͜ͷύοέʔδɺ࡞ͬͯҎ߱ຖճ͍ͬͯΔ͓ؾʹೖΓͰ͢ɻͱ͍͏Θ͚Ͱɺ͓ؾʹೖΓͷ հճͰͨ͠ɻ 18 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.1 ࢝Ίʹ
TIPSTAR ͱ TIPSTAR ͱ 3 ਓͷԋऀ͕νʔϜΛΈɺڝྠͷϨʔεΛ༧͍ͯ͠Δ༷ࢠΛϥΠϒ৴͢Δϛ ΫγΟͷαʔϏεͰ͢ɻνʔϜͷ༧ʹࢹௌऀ͔ͬͬͯಉ͡൪߸ʹౌ͚Δ͜ͱ͕Ͱ͖ΔͷͰɺڝ ྠʹৄ͘͠ͳͯͦ͘ͷ··͔ͬΔ͚ͩͰ༡Ϳ͜ͱ͕Ͱ͖ͯ͠·͍·͢ɻ͞Βʹݱ͚ۚͩͰͳ ͘ɺແྉίΠϯ͑ΔΑ͏ʹͳ͍ͬͯΔͷͰɺָ͠͞ͷ෯͕͍ͬͯ·͢ɻ ਤ 2.1: TIPSTAR TIPSTAR ͷࢦ͢ͱ͜Ζ ϛΫγΟʹͱͬͯެӦڝٕͱ͍͏ࣄۀྖҬ৽͍͠νϟϨϯδͰͨ͠ɻͳͥ͜ͷࣄۀྖҬΛબ͠ ͯαʔϏεΛ࡞ͬͨͷ͔ΘΕΔ͜ͱ͕ଟ͍ͷͰɺνʔϜͱͯ͠ࢦ͍ͯ͠Δ͜ͱʹ͍ͭͯ৮Ε͓ͯ ͖͍ͨͱࢥ͍·͢ɻ 19 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.1 ࢝Ίʹ
ϛΫγΟίϛϡχέʔγϣϯαʔϏεΛग़͠ɺՈ༑ͩͪͱϫΠϫΠָ͘͠༡ΜͰΒ͑ Δ͜ͱΛࢦ͍ͯ͠·͢ɻఏڙ͍ͯ͠ΔαʔϏεʹ SNSʮmixiʯ ɺεϚʔτϑΥϯΞϓϦʮϞϯε λʔετϥΠΫʯ ɺՈΞϧόϜʮΈͯͶʯͳͲؚ·Ε͓ͯΓɺ͝ଘ͡ͷํ͍Βͬ͠ΌΔ͔͠ Ε·ͤΜɻ SNSɺεϚʔτϑΥϯΞϓϦɺը૾ŋಈըڞ༗αʔϏεͱ͍ͬͨΑ͏ʹɺαʔϏεͷछྨҟͳΓ ·͕͢ɺίϛϡχέʔγϣϯΛ௨ͯ͠ਓʑͷੜ׆Λ๛͔ʹ͍ͨ͠ͱ͍͏͍ڞ௨͍ͯ͠·͢ɻՃ͑ ͯɺεϙʔπίϛϡχέʔγϣϯπʔϧͱͯ͠ඇৗʹڧྗͰ͢ɻڝྠΦϦϯϐοΫͷछͱͯ͠ KEIRIN ͱͯ͠࠾༻͞Ε͓ͯΓɺຊൃͷͱ͓ͯ͠Ζ͍εϙʔπͰ͢ɻ ڝྠͷ࣋ͭັྗʹΤϯλʔςΠϯϝϯτͷཁૉΛ߹ΘͤΔ͜ͱͰɺڧྗͳίϛϡχέʔγϣϯπʔ ϧͱͯ͠ఏڙͰ͖ΔͷͰͳ͍͔ͱߟ͑ɺTIPSTAR Λ௨ͯ͡ڝྠͷύʔηϓγϣϯνΣϯδΛى͜ ͦ͏ͱ͍ͯ͠·͢ɻ 20 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.2 ΞʔΩςΫνϟ
2.2 ΞʔΩςΫνϟ TIPSTAR Λࢧ͑ΔΞʔΩςΫνϟ ͯ͞ɺͦΜͳ TIPSTAR Λࢧ͑ΔΞʔΩςΫνϟͰ͕͢ɺͯ͢Λ͑Δͷ͍͠ͷͰॏཁͳϙ ΠϯτΛհ͠·͢ɻ ਤ 2.2: TIPSTAR ͷΞʔΩςΫνϟ (ུ֓) ࠾༻͍ͯ͠ΔΫϥυαʔϏεɾݴޠΛྻڍ͢ΔͱҎԼ͕ڍ͛ΒΕ·͢ɻ • GCP – GKE Λར༻͠ API ͳͲͷαʔϏεΛཧ – σʔλϕʔεʹ Cloud Spanner Λར༻ • AWS – ө૾৴෦Λఏڙ – Media Services Λར༻ͯ͠ HLS ͰϨʔεө૾ɾTIPSTAR ͷ Live ө૾Λ৴ • Golang – API ֤छπʔϧ go Λར༻ͯ͠։ൃ – echo + CleanArchitecture • CI/CD ʹ CircleCI + Cloud Build ࠓճ͜ͷதͷ Cloud Spanner Λར༻͢ΔͨΊͷऔΓΈʹϑΥʔΧε͠·͢ɻ 21 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.2 ΞʔΩςΫνϟ
Cloud Spanner ͱʁ GCP ͕ఏڙ͢ΔσʔλϕʔεαʔϏεͷҰͭͰߴ͍Մ༻ੑͱແஈ֊ͷεέʔϥϏϦςΟΛఏڙ͠ ͍ͯ·͢ɻσʔλϕʔεͷεέʔϦϯάඇৗʹ࿑ྗ͕ඞཁͱ͞ΕΔ࡞ۀͷҰͭͰ͕͢ϊʔυͷ ՃɺࣗಈγϟʔσΟϯάΛػೳͱͯ͠ఏڙ͍ͯ͠ΔͷͰ͔ͦ͜Βղ์͞ΕΔͷඇৗʹ͋Γ͕͍ͨͰ ͢ɻͨͩ͜͠ΕΒΛ࣮ݱ͢ΔͨΊʹ୯७ͳϨΠςϯγ Cloud SQL ʹྼΓ·͢͠ൺֱతίετ͕ ߴ͍αʔϏεͰ͢ɻ࣮ࡍʹར༻͢Δʹఆنඞཁͱ͞ΕΔཁ͔݅Βஅ͢Δඞཁ͕͋Γ·͢ɻ Cloud Spanner ͷಋೖ·Ͱͷي TIPSTAR ͦΕͳΓʹେ͖ͳنʹͳΔ͜ͱΛఆ͍ͯ͠Δ͜ͱͱߴՄ༻ੑͷඞཁੑͱεέʔϦ ϯάͷ࡞ۀ͔Βղ์͞Ε͍ͨ͜ͱ͔Β Cloud Spanner Λબ͠·ͨ͠ɻ ࣮ TIPSTAR ࠷ॳ͔Β Cloud Spanner Λར༻͍ͯͨ͠Θ͚Ͱ͋Γ·ͤΜͰͨ͠ɻॳظͷݕ ূஈ֊ͷϑΣʔζͰҧ͏σʔλϕʔεΛར༻͓ͯ͠Γ Cloud Spanner ʹશʹҠߦͨ͠ͷ։ൃ த൫ʙޙͰͨ͠ɻ࠷ॳظͷߏ Cloud SQL ͱ Datastore Λར༻͍ͯ͠·ͨ͠ɻ͜ΕαʔϏε ͷݕূʹेͳߏͰ͢ɻݕূޙɺϦϦʔεޙͷཁ݅ʹରԠ͢Δʹ͍͔ͭ͘ͷ՝͔Βมߋ͕ඞ ཁͱͳΓ·ͨ͠ɻ • ن͕େ͖͘ͳͬͯདྷͨ࣌ʹεέʔϧ͢ΔରԠ͕ඞཁʹͳΔ • Cloud SQL ϝϯςφϯε࣌ʹΛμϯλΠϜ͕ൃੜ੍͠ޚͰ͖Δൣғ͕ڱ͍*1 ಛʹμϯλΠϜʹ͍ͭͯඇৗʹ·͘͠ɺࢥ͍ͬͯҠߦʹ౿ΈΓ·ͨ͠ɻ ਤ 2.3: ࠷ॳظͷߏ ར༻͍ͯ͠ΔσʔλϕʔεΛมߋ͢Δʹ͕͔͔࣌ؒΔ͜ͱͱɺ͢ͰʹϓϩμΫτͷϢʔβʔςε τ৴ςετΛ࣮ࢪ͍ͯ͠Δঢ়گͷͳ͔ɺฒߦͯ͠ߏΛมߋ͢Δʹஈ֊తʹߦ͏ඞཁ͕͋Γ· ͨ͠ɻ *1 ࣥච࣌ͷঢ়گͰ͋Γݱࡏվળ͍ͯ͠ΔՄೳੑ͕͋Γ·͢ 22 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.2 ΞʔΩςΫνϟ
ίʔυͷमਖ਼ CleanArchitecture Λ࠾༻͍ͯͨ͠ͷͰ usecase ͳͲʹखΛೖΕͣσʔλϕʔε ΛมߋͰ͖Δ༧ଌ͍ͯ͠·͕ͨ͠ɺޙड़͢Δ ID ൃ൪ͷػߏੑೳݕূඞཁͳͨΊ͜ͷ෦͚ͩΛ ͯ͠ҠߦΛ։࢝͠·ͨ͠ɻ ਤ 2.4: Cloud Spanner ͷҠߦաظ ༧ଌͷ௨Γίʔυमਖ਼େنͳߏมߋΛߟ͑Δͱൺֱతগͳ͍ίετͰ࣮ݱͰ͖·ͨ͠ɻID ൃ൪ͷػߏେ෦ͷҠߦͱผͰߦ͍ɺ͍͔ͭ͘ͷύλʔϯΛࢼ͠ແࣄඞཁͱ͢ΔύϑΥʔϚϯε Λຬͨ͢͜ͱ͕Ͱ͖ɺΕͯશʹ Cloud Spanner ͷҠߦΛྃ͠·ͨ͠ɻ ਤ 2.5: ࠷ऴతͳߏ Ҡߦ͢Δʹඇৗʹίετ͕͔͔Γ·͕ͨ͠࠷ऴతͳߏʹΑΓɺ๊͍͑ͯͨଟ͘ͷ՝Λղফ ͠ɺಉ࣌ʹଟ͘ͷϝϦοτΛखʹೖΕΔ͜ͱ͕Ͱ͖·ͨ͠ɻ·ͨɺར༻σʔλϕʔεͷมԽʹରԠͰ ͖ͨ͜ͱͰ CleanArchitecture ͷԸܙΛड͚Δ͜ͱ͕Ͱ͖ͨͱࢥ͍·͢ɻ 23 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.3 Cloud
Spanner 2.3 Cloud Spanner Cloud Spanner ͱͷ͖߹͍ํ ඇৗʹେ͖ͳίετΛ͔͚ͯҠߦͨ͠ Cloud Spanner Ͱ͕͢ߏมߋͷͨΊʹಛ༗ͷ՝ʹର Ԡ͢Δඞཁ͕͋Γ·ͨ͠ɻ • ϗοτεϙοτΛආ͚ΔͨΊʹ UUID Λར༻ • ιʔτͰ͖Δ ULID ͷར༻ • γʔέϯγϟϧͳ൪߸Λൃ൪͢Δʹಠࣗͷ ID ൃ൪͕ඞཁ • Ұׅߋ৽্ݶͷྀ • ϦτϥΠʹର͢Δႈੑͷҙ ͜ΕΒͷରԠํ๏͢Ͱʹར༻͞Ε͍ͯΔํϕετϓϥΫςΟεͱͯ͠هࣄʹͳ͍ͬͯΔͷ ଟ͍ͷͰͦͪΒʹৡΔͱͯ͠ߏมߋ࣌ʹಛʹྗͯ͠ղܾ͢Δඞཁͷ͋ͬͨγʔέϯγϟϧͳ൪߸ Λൃ൪͢Δ IDSequencer ͱݺΜͰ͍ΔػߏΛղઆ͠·͢ɻ γϯϓϧͳ ID ൃ൪ γϯϓϧʹγʔέϯγϟϧͳ ID Λൃ൪͠Α͏ͱ͢Δͱ͜ͷΑ͏ʹͳΔͱࢥ͍·͢ɻ Ϧετ 2.1: simple-sequencer 1: package sequencer 2: 3: import ( 4: "context" 5: 6: "cloud.google.com/go/spanner" 7: ) 8: 9: type SimpleSequencer struct { 10: SequencerKey string 11: Client *spanner.Client 12: } 13: 14: func (s *SimpleSequencer) Next(ctx context.Context) (int64, error) { 15: var next int64 16: 17: _, err := s.Client.ReadWriteTransaction(ctx, func(ctx context.Context, 18: txn *spanner.ReadWriteTransaction) error { 19: var current int64 20: row, err := txn.ReadRow(ctx, "Sequences", 21: spanner.Key{s.SequencerKey}, []string{"Id"}) 22: 23: if err != nil { 24 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.3 Cloud
Spanner 24: return err 25: } 26: if err := row.Column(0, ¤t); err != nil { 27: return err 28: } 29: 30: next = current + 1 31: m := spanner.Update("Sequences", 32: []string{"Key", "Id"}, []interface{}{s.SequencerKey, next}) 33: 34: return txn.BufferWrite([]*spanner.Mutation{m}) 35: }) 36: 37: return next, err 38: } ݱࡏൃ൪ࡁΈͷ ID Λऔಘ͠Ճࢉͨ͠ ID Λར༻͢ΔγϯϓϧͳܗͰ͢ɻऔಘ͔Βߋ৽·ͰΛτϥ ϯβΫγϣϯͱ͍ͯ͠·͢ɻ͜ͷܗͰಉ࣌ʹ ID ͷൃ൪Λཁٻ͞Εͯ͠·͏ͱൃ൪ͷͨͼʹϩοΫ ͕ൃੜ͢ΔͷͰཁ݅࣍ୈͰύϑΥʔϚϯεͰ͕ൃੜ͢ΔՄೳੑ͕͋Γ·͢ɻ όονʹΑΔ ID ൃ൪ γϯϓϧͳ ID ൃ൪Ͱڝ߹͕ߴසͰൃੜ͢ΔͨΊɺͦͷճΛগͳ͘͢ΔΞϓϩʔνͱͯ͠ൃ ൪ࡁΈͷ ID Λߋ৽͢Δࡍʹࣄલʹେ͖͘Ճࢉ͢Δ͜ͱͰɺ·ͱΊͯ ID Λόονతʹൃ൪͢Δͱ͍ ͏ํ๏͕͋Γ·͢ɻ Ϧετ 2.2: batch-sequencer 1: package sequencer 2: 3: import ( 4: "context" 5: "sync" 6: 7: "cloud.google.com/go/spanner" 8: ) 9: 10: const BatchSequencerBatchSize = 10 11: 12: type BatchSequencer struct { 13: SequencerKey string 14: Client *spanner.Client 15: mu sync.Mutex 16: current int64 17: max int64 18: } 19: 20: func (s *BatchSequencer) Next(ctx context.Context) (int64, error) { 25 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.3 Cloud
Spanner 21: s.mu.Lock() 22: defer s.mu.Unlock() 23: 24: if s.current != 0 && s.current < s.max { 25: s.current += 1 26: return s.current, nil 27: } 28: 29: _, err := s.Client.ReadWriteTransaction(ctx, func(ctx context.Context, 30: txn *spanner.ReadWriteTransaction) error { 31: var current int64 32: row, err := txn.ReadRow(ctx, "Sequences", 33: spanner.Key{s.SequencerKey}, []string{"Id"}) 34: 35: if err != nil { 36: return err 37: } 38: if err := row.Column(0, ¤t); err != nil { 39: return err 40: } 41: 42: s.current = current + 1 43: s.max = current + BatchSequencerBatchSize 44: 45: m := spanner.Update("Sequences", 46: []string{"Key", "Id"}, []interface{}{s.SequencerKey, s.max}) 47: 48: return txn.BufferWrite([]*spanner.Mutation{m}) 49: }) 50: 51: if err != nil { 52: return 0, err 53: } 54: 55: return s.current, nil 56: } 57: γϯϓϧͳ ID ൃ൪ͱҟͳΓ·ͱΊͯൃ൪͢Δ ID ͕ރׇ͢Δ·Ͱ Cloud Spanner ͷΞ Ϋηε͕ෆཁͱͳΔͷͰҰ݅͋ͨΓͷ ID ൃߦʹ͔͔ΔύϑΥʔϚϯεͷվળ͕ݟࠐΊ·͢ɻ BatchSequencerBatchSize Λେ͖͘͢ΕΑΓଟ͘ͷ ID ΛҰʹൃߦͰ͖ΔͷͰ͞ΒʹύϑΥʔ Ϛϯε͕ཁٻ͞ΕΔ߹͜ͷࣈΛେ͖͘͢Δ͜ͱͰղܾͰ͖ΔՄೳੑ͕͋Γ·͢ɻ όονʹΑΔ ID ൃߦൃ൪ͨ͠൪߸͕ඈΜͰ͠·͏Ϊϟοϓ͕ൃੜ͢Δ͜ͱ͕͋Γ·͢ɻ͜Ε Ұʹൃߦ͢ΔόοναΠζΛେ͖͘͢Ε͢Δ΄Ͳൃੜ͢Δ൪߸͕૿͑ΔՄೳੑ͕͋Γ·͢ɻ Ϊϟοϓͷൃੜ֫ಘࡁΈ ID ͕شൃͨ͠߹ʹى͜Γߏ࣍ୈͰσϓϩΠͳͲͰ༰қʹൃੜ͠ ·͢ɻ TIPSTAR Ͱ༗ݶͷ͕ཁٻ͞Ε͍ͯΔͨΊΪϟοϓͷൃੜͳΔ͍͑ͨ͘Ͱ͢ɻ ΪϟοϓͷൃੜΛݮΒ͢ํ๏ͱͯ͠ 26 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.3 Cloud
Spanner • ൃ൪͢ΔόοναΠζΛখ͘͢͞Δ • ID ൃ൪ͷػߏʹؔͷͳ͍σϓϩΠ͕Өڹ͠ͳ͍ߏʹมߋ͢Δ ͳͲ͕ߟ͑ΒΕ·͕͢ TIPSTAR Ͱ࣍ʹհ͢Δํ๏Ͱ ID ͷൃ൪Λ࣮ݱ͍ͯ͠·͢ɻ γϟʔυΛར༻ͨ͠ ID ൃ൪ ͜͜·Ͱॻ͍͖ͯͨ͜ͱ࣮ GCP ͷυΩϡϝϯτʹهࡌ͞Ε͍ͯ·͢ͷͰΑΓৄ͘͠Γͨ ͍ํͪ͜ΒͷυΩϡϝϯτ*2Λࢀߟʹ͢Δͱྑ͍ͱࢥ͍·͢ɻ όονʹΑΔ ID ൃ൪ʹՃ͑ɺൃ൪ࡁΈ ID Λཧ͢ΔΩʔΛෳ࣋ͨͤͯࢄͤ͞Δ͜ͱͰڝ߹ ͷൃੜΛݮΒ͠·ͨ͠ɻ Ϧετ 2.3: shard-sequencer 1: package sequencer 2: 3: import ( 4: "context" 5: "fmt" 6: "math/rand" 7: "sync" 8: 9: "cloud.google.com/go/spanner" 10: ) 11: 12: const ShardSequencerShardSize = 10 13: const ShardSequencerBatchSize = 1 14: 15: type ShardSequencer struct { 16: SequencerKey string 17: Client *spanner.Client 18: mu sync.Mutex 19: current int64 20: max int64 21: } 22: 23: func (s *ShardSequencer) Next(ctx context.Context) (int64, error) { 24: s.mu.Lock() 25: defer s.mu.Unlock() 26: 27: if s.current != 0 && s.current < s.max { 28: s.current += 1 29: return s.current, nil 30: } 31: 32: shardNum := int64(rand.Intn(ShardSequencerShardSize) + 1) 33: _, err := s.Client.ReadWriteTransaction(ctx, func(ctx context.Context, 34: txn *spanner.ReadWriteTransaction) error { *2 https://cloud.google.com/solutions/sequence-generation-in-cloud-spanner?hl=ja 27 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.3 Cloud
Spanner 35: var current int64 36: shardKey := fmt.Sprintf("%s_%d", s.SequencerKey, shardNum) 37: row, err := txn.ReadRow(ctx, "Sequences", 38: spanner.Key{shardKey}, []string{"Id"}) 39: 40: if err != nil { 41: return err 42: } 43: if err := row.Column(0, ¤t); err != nil { 44: return err 45: } 46: 47: if current == 0 { 48: s.max = shardNum * ShardSequencerBatchSize 49: } else { 50: s.max = current + 51: (ShardSequencerShardSize * ShardSequencerBatchSize) 52: } 53: s.current = s.max - ShardSequencerBatchSize + 1 54: 55: m := spanner.Update("Sequences", 56: []string{"Key", "Id"}, []interface{}{shardKey, s.max}) 57: 58: return txn.BufferWrite([]*spanner.Mutation{m}) 59: }) 60: 61: if err != nil { 62: return 0, err 63: } 64: 65: return s.current, nil 66: } ඞཁͳγϟʔυ͚ͩඌޠΛՃ͢Δ͜ͱͰࢄΛ࣮ݱ͍ͯ͠·͢ɻ͜͜ͰόοναΠζΛ 1 ͱ͍ͯ͠·͕ͪ͢͜ΒΛௐ͢Δ͜ͱͰύϑΥʔϚϯεΛՔ͙͜ͱ͕Ͱ͖·͢ɻ͔͠͠όοναΠζ ؆୯ʹ૿ͤ·͕͢γϟʔυαΠζ؆୯ʹ૿ͤͳ͍ͷͰඞཁͳཁ݅ΛѲܾͯ͠ఆ͢Δඞཁ͕ ͋Γ·͢ɻ 28 -
ୈ 2 ষ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํ 2.4 ऴΘΓʹ
2.4 ऴΘΓʹ ࠓճ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ར༻͢ΔͨΊͷऔΓΈΛհ͠·ͨ͠ɻ ଞʹ͜͜Ͱհ͖͠Εͳ͔ͬͨΞʔΩςΫνϟʹؔ͢ΔࢼߦࡨޡϥΠϒಈըΛ҆ఆͯ͠৴ ͢ΔͨΊʹߦ͖ͬͯͨ͞·͟·ͳٕज़తͳνϟϨϯδ͕͋Γ·͢ɻ TIPSTAR ࠓޙେ͖͘ͳΔՄೳੑΛ࣋ͬͨαʔϏεͩͱࢥ͍·͢ɻαʔϏε͕εέʔϧ͍ͯ͘͠ ্Ͱ Cloud Spanner ͷҠߦʹڍ͛ͨ௨Γܧଓͯ͠ΞʔΩςΫνϟͷมߋ͕ඞཁʹͳͬͯ͘Δͱ ࢥ͍ͬͯ·͢ɻ ՔಇதͷαʔϏεʹର͢ΔΞʔΩςΫνϟͷมߋͱ͍͏ͷಛʹϦεΫ͕͋Γίετ͔͔Γ· ͕͢ɺܧଓͯ͠औΓΜͰ͍͖ɺνϟϨϯδ͠ଓ͚ɺࠓޙϛΫγΟͷܝ͛ΔϢʔβʔαϓϥΠζ ϑΝʔετΛ೦಄ʹΑΓΑ͍αʔϏεΛఏڙ͍͖ͯ͠·͢ɻ 29 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠ ΨϠॱҐ લষɺຢٛরͷ
TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ͖߹͍ํͰɺTIPSTAR ͷհ͕͋Γ·ͨ͠ɻຊষͰ TIPSTAR ͷಛͷ 1 ͭͰ͋ΔνʔϜͷΓ্͕ΓΛࣔ͢ϫΠΨ ϠॱҐͷΞʔΩςΫνϟΛհ͠·͢ɻࢲීஈ TIPSTAR ͷ API ։ൃΛ୲͓ͯ͠ΓɺϫΠΨϠ ॱҐ։ൃ୲ۀͷ 1 ͭͰͨ͠ɻࠓճϫΠΨϠॱҐͷհΛ͍ͨ͠ͱࢥͬͨཧ༝ɺաڈʹ 2 ճ mixi tech note*1*2ͰΓʷػցֶशͱ͍ͬͨςʔϚͰ Google ͷػցֶशϓϩμΫτͷհΛͨ͠ ͱ͓Γɺ͍͔ͭ͘ Google ͷػցֶशϓϩμΫτʹ৮Ε͖ͯ·͕ͨ͠ɺϦϦʔε༧ఆͷϓϩμΫτʹ ಋೖͨ͠ࣄྫΛհͨ͜͠ͱ͕͋Γ·ͤΜͰͨ͠ɻ࣮ۀͰऔΓೖΕ͍ͯΔࣄྫΛհ͍ͨ͠ͱࢥ ͍ɺࠓճϫΠΨϠॱҐͷհΛ͢Δ͜ͱʹ͠·ͨ͠ɻ 3.1 ϫΠΨϠॱҐͱ TIPSTAR ʹ֤ϧʔϜͷΓ্͕ΓͷΛද͢ϫΠΨϠॱҐͱ͍͏ͷ͕͍͟͝·͢ɻ TIPSTAR A νʔϜ͔Β C νʔϜͷ߹ܭ 3 νʔϜ͋ΓɺΓ্͕Γʹ߹Θͤͯ 1~3 ҐͷॱҐ͕ ͖·͢ɻ1 ҐͷνʔϜΞϓϦͷϗʔϜը໘ͰҰ൪্ʹҠಈ͢ΔͨΊΛूΊ͘͢ͳΓ·͢ɻ ϫΠΨϠॱҐ্͕͕Δ͜ͱʹΑΓɺࢹௌΛ্͛ͨΓɺ͔ͬΓϕοτΛͯ͘͠ΕΔਓ͕ଟ͘ͳΔ͜ ͱ͕ظͰ͖ΔͨΊ֤νʔϜʹͱͬͯॏཁͳͷ 1 ͭͱͳ͍ͬͯ·͢ɻ *1 mixi tech note #02ɿ https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-02 *2 mixi tech note #03ɿ https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-03 31 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.2 ࡞͢Δ͜ͱʹͳͬͨഎܠ
ਤ 3.1: ϫΠΨϠॱҐ͕̍ҐʹͳΔͱϗʔϜը໘্෦ʹදࣔ͞ΕΔ 3.2 ࡞͢Δ͜ͱʹͳͬͨഎܠ TIPSTAR ϓϩδΣΫτൃ͔ΒϦϦʔε·Ͱ 1 ΄Ͳظ͕͍ؒ͟͝·͕ͨ͠ɺ࢝Ί͔ΒϫΠ ΨϠॱҐͷߏ͕͋ͬͨΘ͚Ͱ͋Γ·ͤΜɻචऀ͕ϓϩδΣΫτʹࢀըͨ͠ͷ 2019 10 ݄͜ ΖͰ͕ͨ͠ɺ࣌·ͩϫΠΨϠॱҐͷػೳ։ൃͷҊ͕ͳ͘ɺҊ͕ग़ͨͷཌͷ 1 ݄ͷࡱӨςε τޙͰͨ͠ɻԿ͔ࡱӨςετΛ܁Γฦ͍ͯ͠Δ͏ͪʹΤϯλʔςΠϝϯτͷཁૉΛڧ͍ͨ͘͠ɺग़ ԋऀͷϞνϕʔγϣϯΛ͍͋͛ͨͱ͍ͬͨཧ༝͔ΒνʔϜͷΓ্͕ΓΛද͢ϫΠΨϠॱҐΛઃ͚ ͍ͨͱڧ͍ཁ͕͋Γ։ൃ͢Δ͜ͱʹͳΓ·ͨ͠ɻ·ͨɺϫΠΨϠॱҐʹԠͯ͡ϗʔϜը໘ͷදࣔॱ ͕มΘΔ͜ͱΛհ͠·͕ͨ͠ɺͪ͜ΒαʔϏεϦϦʔεޙʹೖͬͨػೳͰ͢ɻϦϦʔεॳνʔ ϜͷฒͼॱݻఆͩͬͨͷͰ͕͢ɺը໘্෦ʹදࣔ͞Ε͍ͯΔνʔϜʹࢹௌ͕ภΔ͕͋ͬͨͷ ͰϫΠΨϠॱҐʹԠͯ͡දࣔॱΛม͑ͯΈΔ͜ͱʹͳΓ·ͨ͠ɻ ϫΠΨϠॱҐ 2 νʔϜͰ࡞ TIPSTAR ͷϓϩδΣΫτͰɺػցֶशΛ͍ʮελʔτɺΓ˓˓पɺΰʔϧʯͱ͍ͬͨς ϩοϓ BGM ΛࣗಈͰϨʔεө૾ʹૠೖ͢Δ͘͠ΈΛ࡞͍ͯ͠ΔνʔϜ͕͋Γ·͢ɻϫΠΨϠ ॱҐͰ༻͢ΔείΞͷࢉग़ػցֶशΛख͕͚͍ͯΔνʔϜ͕࡞͠ɺචऀͷνʔϜͰϫΠΨϠ είΞΛ Cloud Pub/Sub ܦ༝Ͱड͚औΓɺΫϥΠΞϯτʹϫΠΨϠॱҐΛఏڙ͢Δ෦Λ୲͠· ͨ͠ɻ྆ํͷΞʔΩςΫνϟΛ؆୯ʹհ͍ͨ͠ͱࢥ͍·͢ɻ 32 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.3 ϫΠΨϠࢉग़ͷΞʔΩςΫνϟ
ਤ 3.2: ػցֶशνʔϜ͕։ൃͨ͠ςϩοϓɻΓʓʓपͳͲͷςϩοϓ͕ࣗಈͰදࣔ͞ΕΔ 3.3 ϫΠΨϠࢉग़ͷΞʔΩςΫνϟ ϫΠΨϠείΞͷࢉग़ʹ Cloud Pub/Subɺ Cloud Functionsɺ FFmpegɺ Cloud Schedulerɺ Cloud Vision APIɺCloud Natural Language APIɺSpeech-to-Text APIɺCloud SQL ͱଟ͘ͷ GCP ϓϩμΫτΛ༻͍ͯ͠·͢ɻ1 ຖʹ֤νʔϜͷ HLS ಈը͔Β੩ࢭըͱԻϑΝΠϧΛൈ͖ग़͠ ػցֶशͰϥϕϧείΞΛݕग़͠ Cloud SQL อଘ͠·͢ɻͦͯ͠ఆظతʹσʔλϕʔεʹอଘ ͨ͠είΞͷฏۉΛ TIPSTAR API ͷ GCP ϓϩδΣΫτͷ Cloud Pub/Sub ʹ Publish ͠·͢ɻ 33 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.3 ϫΠΨϠࢉग़ͷΞʔΩςΫνϟ
ਤ 3.3: ϫΠΨϠείΞͷࢉग़ 1 ຖʹ֤νʔϜͷ HLS ಈը͔Β੩ࢭըͱԻϑΝΠϧൈ͖ग़͠ ֤νʔϜͷνʔϜ ID ͱ HLS URL ݻఆͳͷͰ Scheduler ͷ message body ʹηοτ͠ɺఆظత ʹ Cloud Pub/Sub ͷ Topic ʹ Publish ͢ΔΑ͏εέδϡʔϦϯά͠·͢ // gcloudίϚϯυΛ༻͠Cloud SchedulerͷjobΛηοτ gcloud scheduler jobs create pubsub Room1-1min --schedule "*/1 * * * *" --topic "waigaya-collector-schedule" --message-body "{\"hls\":\"https://team1.m3u8\",\ʡteam_id\":\"1\"}" --description "Room1:every 1minʡ HLS ͔Β੩ࢭըͱԻϑΝΠϧͷநग़͠είΞΛग़͢ τϐοΫʹϝοηʔδ͕ Publish ͞ΕΔͱɺͦΕΛτϦΨʹ͍ͯ͠Δ Cloud Functions ͕ىಈ͠· ͢ɻClooud Functions Ͱ ffmpeg ίϚϯυΛ͍੩ࢭըͱԻϑΝΠϧͷநग़͠ Google Cloud Storage ʹอଘ͠·͢ɻ 34 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.3 ϫΠΨϠࢉग़ͷΞʔΩςΫνϟ
// Cloud Functions ͰffmpegίϚϯυΛ࣮ߦ࣮͠ߦ݁ՌΛGoogle Cloud Storageʹอଘ if err := exec.Command( "ffmpeg", "-i", h.URL, "-map", "0:0", "-r", "0.1", "-f", "image2", "-t", "59", fmt.Sprintf("%vimage_%v.jpg", h.tmpDir, "%05d"), "-map", "0:1", "-acodec", "pcm_s16le", "-ac", "1", "-ar", "16k", "-t", strconv.Itoa(periodTime), h.tmpDir+"audio.wav", ).Run(); err != nil { return err } if err := h.uploadGCS(); err != nil { return err } औΓग़ͨ͠੩ࢭը͔ΒɺGoogle Cloud Vision API Λ͍ϥϕϧͷݕग़Λߦ͍·͢ɻ // VisionAPIΛ͍إͷදΛݕग़ image, err := vision.NewImageFromReader(out) if err != nil { return score, err } // ը૾ϑΝΠϧͷإΛݕग़ faces, err := s.visionApi.DetectFaces(ctx, image, nil, 10) if err != nil { return score, err } for _, face := range faces { // faceΦϒδΣΫτ͔Βౖ͍ͬͯΔ͔ɺָ͠ΜͰ͍Δ͔ɺڻ͍͍ͯΔ͔ͳͲͷදͷείΞΛऔಘՄೳ } औΓग़ͨ͠ԻϑΝΠϧ Speech-to-Text API Λ͍ςΩετϑΝΠϧΛऔಘ͠·͢ɻͦͷςΩ ετϑΝΠϧΛ Cloud Natural Language API Λ༻͠ײੳΛߦ͍·͢ɻ 35 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.3 ϫΠΨϠࢉग़ͷΞʔΩςΫνϟ
// Speech-to-Text APIΛ͍ςΩετϑΝΠϧΛऔಘ speechClient, err := speech.NewClient(context.Background()) if err != nil { return nil, err } data, err := ioutil.ReadFile(h.tmpDir + "audio.wav") if err != nil { return nil, err } // ԻϑΝΠϧͷԻΛݕग़ resp, err := speechClient.Recognize(context.Background(), &speechpb.RecognizeRequest{ Config: &speechpb.RecognitionConfig{ Encoding: speechpb.RecognitionConfig_LINEAR16, SampleRateHertz: 16000, LanguageCode: "ja-JP", }, Audio: &speechpb.RecognitionAudio{ AudioSource: &speechpb.RecognitionAudio_Content{Content: data}, }, }) for _, result := range resp.Results { for _, alt := range result.Alternatives { //alt.TranscriptͰςΩετऔಘ } } // ςΩετΛCloud Natural Language APIΛͬͯײΛੳ req := languagepb.AnalyzeSentimentRequest{ Document: &languagepb.Document{ Source: &languagepb.Document_Content{ Content: text, }, Type: languagepb.Document_PLAIN_TEXT, }, EncodingType: languagepb.EncodingType_UTF8, } sentiment, err := s.AudioApi.AnalyzeSentiment(context.Background(), &req) ࠷ޙʹϥϕϧͱײੳͷ݁Ռ͔ΒϫΠΨϠείΞΛܾఆ͠ɺCloud SQL ʹϫΠΨϠείΞΛอ ଘ͠·͢ɻ 36 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.3 ϫΠΨϠࢉग़ͷΞʔΩςΫνϟ
Google Cloud Vision API ͱ Google ͕ఏڙ͍ͯ͠Δը૾ೝࣝ API*3Ͱɺը૾ΛΞοϓϩʔυ͢Δ͚ͩͰମͷϥϕϧݕग़ɺײ ͷೝɺςΩετੳ͕Ͱ͖·͢ɻֶशࡁϞσϧΛ༻͢ΔͷͰֶशΛ͢Δඞཁͳ͘ɺREST API RPC API ͕ఏڙ͞Ε͍ͯΔ΄͔ɺC#ɺGoɺJavaɺNode.jsɺPHPɺPythonɺRuby Λα ϙʔτͨ͠ΫϥΠΞϯτϥΠϒϥϦఏڙ͞Ε͍ͯ·͢ɻࠓճ Go ͷΫϥΠΞϯτϥΠϒϥϦΛ ༻͠·ͨ͠ɻ·ͨհϖʔδͰ Vision API ΛࢼͤΔσϞπʔϧ͕ఏڙ͞Ε͍ͯ·͢ɻڵຯ͕͋Δ ํͥͻҙͷը૾Ͱ͓ࢼ͍ͩ͘͠͞ɻ·ͨࠓճׂѪ͠·͕͢ɺಠࣗͷը૾ೝࣝΛ࡞Γ͍ͨਓͷͨ Ίʹ AutoML Vsion ͱݺΕΔϓϩμΫτ͋Γ·͢ɻͪ͜Βը૾ΛूΊͯϥϕϦϯά͢Δ͚ͩ ͰΧελϜϞσϧΛ࡞Ͱ͖ɺൺֱత༰қʹΦϦδφϧͷը૾ೝࣝ API Λ࡞Ε·͢ɻڵຯ͕͋Δํ ͪ͜Βͥͻ৮ͬͯΈ͍ͯͩ͘͞ɻ ਤ 3.4: Vision API ͷհϖʔδͰσϞ͕Ͱ͖Δ Cloud Natural Language API ͱ Cloud Natural Language API*4ςΩετΛੳ͢Δ API Ͱ͢ɻҙͷςΩετΛ API ʹΞο ϓϩʔυ͢ΔͱਓɺॴɺιʔγϟϧϝσΟΞʹදΕΔײͳͲΛੳͰ͖ӳޠຊޠؚΊଟ͘ͷ ݴޠΛαϙʔτ͍ͯ͠·͢*5ɻͪ͜Βͷ API Vision API ಉ༷ʹ REST API ΫϥΠΞϯτϥΠ *3 Vision APIɿ https://cloud.google.com/vision *4 Natural Language APIɿ https://cloud.google.com/natural-language *5 Language Supportɿ https://cloud.google.com/natural-language/docs/languages 37 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.3 ϫΠΨϠࢉग़ͷΞʔΩςΫνϟ
ϒϥϦ͕ఏڙ͞Ε͍ͯ·͢ɻNatural Language API σϞπʔϧ͕ఏڙ͞Ε͍ͯΔͷͰɺͥͻ͓ࢼ ͍ͩ͘͠͞ɻ ਤ 3.5: Natural Language API ͷհϖʔδͰσϞ͕Ͱ͖Δ AutoML Natural Language Λ༻͢ΕΦϦδφϧͷΧελϜϞσϧ࡞ՄೳͰ͢ɻ Speech-to-Text ͱ Speech-to-Text*6 Google ͕ఏڙ͢Δ API ͰԻϑΝΠϧΛΞοϓϩʔυ͢Δ͚ͩͰจࣈى͜ ͠Λͯ͘͠Ε·͢ɻGoogle ࠷৽ͷσΟʔϓϥʔχϯάͷχϡʔϥϧωοτϫʔΫΞϧΰϦζϜΛར ༻͓ͯ͠Γ 125 Ҏ্ͷݴޠํݴͷԻೝࣝʹରԠ͍ͯ͠·͢ɻͪ͜Β REST API ΫϥΠΞϯ τϥΠϒϥϦɺϒϥβ্ͰࢼͤΔσϞπʔϧ͕ఏڙ͞Ε͍ͯΔͷͰͥͻ͓ࢼ͍ͩ͘͠͞ɻ *6 Speech-to-Textɿ https://cloud.google.com/speech-to-text 38 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.3 ϫΠΨϠࢉग़ͷΞʔΩςΫνϟ
ਤ 3.6: Speech-to-Text Google Cloud Vision API Cloud Natural Language API Λ࠾༻ͨ͠ཧ༝ ϫΠΨϠείΞͷ࣮Λͨ͠അढ़ (@raibo) ʹ࠾༻ͨ͠ཧ༝ΛਘͶͨͱ͜ΖҎԼͷճ͕͟͝ ͍·ͨ͠ɻ • ར༻ํ๏͕؆୯Ͱ͋Γɺ͙͢ʹར༻ՄೳͰ͋ͬͨ͜ͱ – Cloud Speech-to-Text Λ GCP ͕ϑϧϚωʔδυͰఏڙ͞Ε͍ͯΔ͓͔͛Ͱɺػցֶश είΞϦϯά෦ 1,2 ӦۀఔͰͨ͠ – ࠓճͷཁ݅ͰɺϞσϧҰൠతͳͷͰेద༻Ͱ͖Δͱஅͨͨ͠Ίɺཁ݅ͱඇৗ ʹϚον͍ͯͨ͠ • Cloud Speech-to-Text ͷਫ਼͕ଞαʔϏεΑΓߴ͍ͱ͍͏ – ෳਓͷձ͔ͬ͠Γͱ͞ΕΔͨΊɺείΞϦϯά͕ඇৗʹ͍͢͠ • ը૾ਪͷਫ਼͕ඇৗʹߴ͘ɺମײͰײͷ Classification ΄ͱΜͲਓؒͷײ֮ͱͣΕ͍ͯ ͳ͔ͬͨ – ϫΠΨϠͷείΞͱͯ͠దͩͱஅͰ͖ͨ ϫΠΨϠείΞΛ API ϓϩδΣΫτʹ Publish Cloud SQL ʹอଘ͍ͯͨ͠ϫΠΨϠείΞͷฏۉΛఆظʹ TIPSTAR API ϓϩδΣΫτͷ Cloud Pub/Sub ͷτϐοΫʹ Publish ͠·͢ɻ 39 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.3 ϫΠΨϠࢉग़ͷΞʔΩςΫνϟ
ਤ 3.7: ϫΠΨϠείΞΛ TIPSTAR API ͷ GCP ϓϩδΣΫτʹ Publish // 30ʹҰϫΠΨϠείΞΛPublish͢ΔΑ͏ʹઃఆ gcloud scheduler jobs create pubsub WaigayaNotify --schedule "*/30 * * * *" --topic "waigaya-notify-schedule" --message-body "{\"room_numbers\":[1,2,3,4]}" --description "every 30min/notify" ্هͷ Cloud Pub/Sub Topic ʹϝοηʔδ͕ެ։͞ΕΔͱ Cloud Functions ͕ىಈ͠σʔλϕʔ ε͔ΒฏۉείΞΛࢉग़͠ TIPSTAR API ͷ Cloud Pub/Sub Topic ʹ Publish ͠·͢ // σʔλϕʔε͔ΒΛऔಘ scoreList, err := getAccumulatedWaigayaScore(payload.RoomNumbers) if err != nil { return err } // औಘͨ͠ΛPublish client, err := pubsub.NewClient(ctx, destProject) if err != nil { return err } jsn, err := json.Marshal(scoreList) 40 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.4 ϫΠΨϠॱҐʹͳΔ·Ͱ
if err != nil { return err } _, err = client.Topic(destTopic).Publish(ctx, &pubsub.Message{Data: []byte(jsn)}).Get(ctx) if err != nil { return err } fmt.Printf("Waigaya Notify done.\n") 3.4 ϫΠΨϠॱҐʹͳΔ·Ͱ ্هͷϫΠΨϠείΞ͕ Publish ͞ΕΔͱɺτϦΨʹ͍ͯ͠Δ Cloud Functions ͕ىಈ͠ϫΠΨ ϠείΞΛ Cloud Spanner ʹอଘ͠·͢ɻͦͯ͠ TIPSTAR API Ͱ Cloud Spanner ͔Β֤ϧʔ ϜͷϫΠΨϠείΞΛऔಘ͠ΫϥΠΞϯτʹϫΠΨϠॱҐΛฦ͠·͢ɻ ਤ 3.8: ϫΠΨϠείΞΛ Spanner ʹอଘ Cloud Pub/Sub ͱ Cloud Functions Λબͨ͠ཧ༝ Cloud Pub/Sub ͷτϐοΫΛ͏͜ͱʹΑͬͯɺࠓޙػցֶशͷ͘͠ΈΛେ͖͘มߋ͢Δ͜ͱʹ ͳͬͨͱͯ͠֎ଆ͚ͩมߋ͢Εൺֱత༰қʹΓସ͕͑Ͱ͖Δͱߟ͑ͨͨΊͰ͢ɻ 41 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.4 ϫΠΨϠॱҐʹͳΔ·Ͱ
Cloud Functions Λ͏ʹ͋ͨΓؾΛ͚ͨ͜ͱ Cloud Functions σϓϩΠ࣌ʹ࠷େϦτϥΠճΛࢦఆ͠ͳ͍߹λΠϜΞτΛઃ͚ͯແݶϦ τϥΠ͕ى͖ͳ͍Α͏ʹ͠·ͨ͠ if time.Now().After(meta.Timestamp.Add(expiration)) { Infof("event timeout: halting retries for expired event ’%q’", meta.EventID) return nil } ҟͳΔ։ൃڥʹίϚϯυ 1 ͭͰ Cloud Functions ΛσϓϩΠͰ͖ΔΑ͏ʹڥมͱ໊ؔΛ ͠·ͨ͠ɻ // σϓϩΠίϚϯυ gcloud functions deploy $(FUNCTION_NAME)$(STAGE) --project $(GCP_PROJECT) --region $(REGION) --runtime $(RUNTIME) --trigger-topic $(TRIGGER_TOPIC) --retry --set-env-vars DB_SPANNER_URI=$(DB_SPANNER_URI) // ֤ڥͷؔΛ༻ҙ͠ϝΠϯͷؔΛݺͿΑ͏ʹ͓ͯ͘͠ func UpdateWaigayaScorePrd(ctx context.Context, m PubSubMessage) error { return UpdateWaigayaScore(ctx, m) } func UpdateWaigayaScoreStg(ctx context.Context, m PubSubMessage) error { return UpdateWaigayaScore(ctx, m) } func UpdateWaigayaScoreDev(ctx context.Context, m PubSubMessage) error { return UpdateWaigayaScore(ctx, m) } func UpdateWaigayaScore(ctx context.Context, m PubSubMessage) error { // ͜͜ʹϝΠϯͷॲཧΛॻ͘ } 42 -
ୈ 3 ষ ػցֶशͱ GCP Ͱ࡞Δ TIPSTAR ϫΠΨϠॱҐ 3.5 ػցֶशͷಋೖʹ͍ͭͯ
3.5 ػցֶशͷಋೖʹ͍ͭͯ ػցֶशۙۙʹͳͬͨͷͱ͍͑ɺ࣮ࡍͷϓϩμΫτʹಋೖ͢ΔʹɺͦΕͳΓʹϋʔυ ϧ͕͋Δͱײ͡Δํଟ͍ͱࢥ͍·͢ɻ͔͠͠ࠓճͷΑ͏ʹɺGoogle ͕ؾܰʹػցֶशͷԸܙΛऔ ΓೖΕΒΕΔΑ͏ʹެ։ͨ͠ Google Cloud Vision API Google Cloud Natural Language API Λ༻͢ΕɺࣗΒػցֶशϞσϧΛ࡞͠ͳ͘ͱػցֶशΛ࣮ࡍͷϓϩμΫτʹಋೖ͢Δ͜ͱ ՄೳͰ͢ɻmixi tech note#2 Ͱհ͠·͕ͨ͠ɺػցֶशʹ͍͔ͭ͘Ϩϕϧ͕͋Γɺಋೖ͕ ͍͠ͷ΄Ͳࣗ༝͕ߴ͘ɺಋೖ͕༰қͳͷ΄Ͳࣗ༝͕͍͕͋Γ·͢ɻ͍͖ͳΓ͍͠ྖ ҬͷͷΛೖΕΔΑΓɺ·ͣϓϩμΫτʹಋೖ༷͠ࢠΛݟͯඞཁ͕͋Δͱײͨ͡ΒঃʑʹϨϕϧͷ ߴ͍ͷʹ͍͚ͯ͠Α͍ͱචऀߟ͑·͢ɻϓϩμΫτϦϦʔε࣌ߴͳػցֶशϞσϧΛ࡞ ͢Δ࣮࣌ؒɺϞσϧ࡞ʹඞཁͳσʔλेʹͦΖ͍ͬͯͳ͍͜ͱଟ͍ͱࢥ͍·͢͠ɺߴ ͳͷΛऔΓೖΕͯඞཁ͕ͳ͔ͬͨͱ͍͏அΛ͢Δ͜ͱʹͳΔ͔͠Εͳ͍͔ΒͰ͢ɻ·ͨಋೖʹ ͋ͨͬͯࠓճͷΑ͏ʹɺείΞͷड͚औΓʹ Cloud Pub/Sub Λར༻͍ͯ͠Εɺࠓޙߴͳػց ֶशʹΓସ͑Δ࣌༰қʹͰ͖Δ͜ͱ͕ظͰ͖ΔͷͰ͓קΊͰ͢ɻ 43 -
ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜ Ͱࢥ͏ 4.1
࢝Ίʹ ιϑτΣΞ։ൃͷϞσϧσβΠϯύλʔϯͳͲͳͲͷઃܭ͍ͨΜଟ͘ͷؾ͖Λ༩͑ͯ͘ ΕΔͷͰ͋Δɻ·ٕͨज़ڥ͕ਐ݄าͰ͋ͬͯઃܭͷணʹ͓͚Δఆ൪ύλʔϯʹ͍· ͩຯظݶͳ͍ͱݴ͑ΔͰ͠ΐ͏ɻࠓճઃܭͱͯ͠ͷࢹͰܝͷʮήʔϜ AIʯΛʮBehavior TreeʯͰߏங͢Δ͜ͱʹऔΓΈͭͭɺѻ͍ํΛݟग़͍͖͍ͯͨ͠ɻݕ౼͍ͯ͘͠ʹ͋ͨͬͯؔ࿈͢ Δख๏ͳͲަ͑ͯղઆ͢Δɻͦ͜ͰؔΘΓ͕ਂ͍ʮBehavior Treeʯ ʮState Machineʯͷ̎ͭͷղઆ Λத৺ʹ͍ͯ͘͠ɻ ʮBehavior Treeʯ։ൃπʔϧ UX ͱͯ͜͠ΕΒ͕༻Ͱ͖ΔΑ͏Έࠐ·Ε͍ͯΔ͜ͱ ͋Δ͕ɺͳΜͰͰ͖ͯ͠·͏໘ɺಛੑϞσϧͱͯ͠ྑ͘Ѳͯ͠తΛૂͬͯར༻͠ͳ͍ͱ ࠞཚ͕ى͜Γ͍͢ɻ ʮState Machineʯେੲ͔Β͋ΔϞσϧ͕ͩɺif จ swtch-case จ͕සग़ͯ͠ ঢ়ଶཧ͕͍͋·͍ͳίʔυͱͳͬͯ͠·͏Մೳੑ͕͋Δɻ ࠓճ͜ΕΒख๏Λ͋ΒͨΊͯݟͭΊ͠౿ऻͭͭ͠ɺήʔϜϓϩάϥϜͷதͰͷΩϟϥΫλΛ੍ޚ ͢ΔήʔϜ AI ͱͯ྆͠ऀΛ׆༻͢Δͱ͍͏͜ͱʹ͍ͭͯɺ࣮ફͯ͠Έͨ͜ͱΛৼΓฦΓͳ͕Βಡऀ ͷ͋ͳͨͱҰॹʹඥղ͖ɺ΄Μͷগ͠ཧղΛਂΊ͍͖͍ͯͨͱࢥ͏ɻͦͷͨΊຊষ࡞ͬͯΈͨܥͰ ͳ͘ɺ࡞Δ্Ͱҙͨ͜͠ͱͷৼΓฦΓͱ͍͏·ͱΊʹͳ͍ͬͯΔɻ 45 -
ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜͰࢥ͏ 4.1 ࢝Ίʹ
ͳ͓ࣥචʹ͋ͨΓࣄྫͱͳΔऔΓΈͷڥͱͯ͠ɺήʔϜ։ൃΤϯδϯͷ Unity ʹͯɺBehavior Tree ͕άϥϑΟΧϧʹߏஙͰ͖ͯήʔϜ੍ޚͰ͖ΔγεςϜʢҎ߱ɺҰൠԽͯ͠ʮϏδϡΞϧΤσΟ λʯͱ͢Δʣ͝ͱ։ൃͨ͠ͷ͕ͩɺຊষͷจ຺ͱͯ͠ຊ࣭తʹ։ൃΤϯδϯΘͳ͍ͷͰ͋Δɻ ͨͩͷͪʹड़͍ͯ͘ҙΛ౿·͑ɺ։ൃ͍͢͠Τϯδϯ͘͠ϓϥάΠϯԿ͔Λݟग़ͯ͠ ͍͚ͨͩͨΒ͍Ͱ͋Δɻ ຊষͷͶΒ͍ͱΰʔϧ ࠓճѻ͏ςʔϚɺBeahviour Tree ͱ State Machine Λཧղͯ͠ɺήʔϜ AI ΛΈཱͯΔͱ͖ߟ ͓͖͍͑ͯͨ͜ͱΛऔΓ্͛ͯ͑Δɻ͜ͷςʔϚ৽͍͠ͷͰͳͦ͘ͷҰ෦ίϯϐϡʔλ ͕ൃల͢Δͦͷલ͔ΒΘΕ͍ͯΔेͱ͍ݹ͞ΕͯਐԽΛଓ͚͍ͯΔͰ͋ΔɻͦͷՁͷ ࠶ೝࣝͱదͳ͍Ͳ͜ΖͷબʹͲ͔͜ͰߩݙͰ͖Εͱئ͍ɺຊষΛॻ͘ɻ ͦΕͰղઆʹਐΉɻ Behavior Tree Beahviour Tree ͱɺλεΫΛࢬ༿ͷΑ͏ʹ࿈Ͷෳࡶʹߏ͜ͱ͕Ͱ͖ΔɺߦಈͱҙࢥܾఆΛπ Ϧʔঢ়ʹΓԼ͛ͨϏδϡΞϧΠϝʔδͰ͋Δɻւ֎ͷ༗໊ϏσΦήʔϜ͔Β͜ͷख๏͕ൃ͠ϒʔ Ϝͱͳͬͨख๏Ͱ͋Δɻ ? ਤ 4.1: Behavior Tree πϦʔਤࣗମࢀߟʹॻ͍ͨదͳͷ͕͓͓ͩΑͦ͜͏͍͏ͷͰ͋Δɻҹϋςφه߸ศ ্͚͍ٓͯΔ͕ɺͦ͜ʹϧʔϧͷࢦఆ͕͋Δͱ͍͏ҙຯؚ͕·ΕΔɻ State Machine State Machine ͱɺΑΓݹ͔͘Β͋ΔৼΔ͍ͷϞσϧͰ͋Γɺσδλϧճ࿏ઃܭͱϓϩάϥϜ ͰΘΕ͓ͯΓɺ༗ݶݸͷঢ়ଶͷத͔Β̍ͭͷঢ়ଶΛऔΓɺ·ͨঢ়ଶΛભҠ͢Δ݅Λ͍࣋ͬͯΔɻ 46 -
ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜͰࢥ͏ 4.1 ࢝Ίʹ
1 2 3 OFF OFF ON ON ਤ 4.2: State Machine ϑϩʔਤదʹॻ͍ͨͷ͕ͩɺ͜͏͍͏ͷͰؒҧ͍ͳ͍ɻจࣈΛఴ͍͑ͯΔ෦͕͋Δ͕ ͦͷΑ͏ʹભҠ͕͍͍݅ͯΔͱ͍͏ҙຯͰ͋Δɻ ۙͯ͘ԕ͍ ͜ΕΛΈͯɺ͍͔͕ͩΖ͏͔ɻલऀ॥͍ͯ͠ͳ͍ʁ ͨ·ͨ·ͩɻޙऀઢ͕͙ʹΌ͙ʹΌ͠ ͍ͯΔʁ ͨ·ͨ·ͩɻ ͦ͏ɺ̎ͭඇৗʹࣅ͓ͯΓࠞಉ͍͢͠ͷͩɻ·ͨ͞Βʹ͕̎ͭ Behavior Tree ͷϏδϡΞϧΤ σΟλͰ֞ࠜͳ͘ར༻Ͱ͖ΔΑ͏ఏڙ͞Ε͍ͯΔɻ࣮ࡍͱͯ͠ɺBehavior Tree ͷߦಈΛͻͱͭ ͷ State ͱͰ͖ΔɻState Machine Ͱॴఆͷॲཧͷ࣮ߦΛ͢ΔͷͰʮߦಈʯͰ͖Δɻ ͔͠͠ɺతʹ͓͍ܾͯఆతʹҧ͏͜ͱ͕͋ΔɻState Machine ຊ࣭తʹडಈతͰ͋Γతͱ ͯ͠ड͚ɻBehavior Tree ߦಈͱ༁ͤΔΑ͏ʹೳಈతͰ͋ΔɻͦΕɺBehavior Tree ॱং ཱͯͨಓےͰҙࢥܾఆ͠ߦಈ͢ΔҙࢥͷΑ͏ͳͷ͕͋Δͷʹର͠ɺState Machine Ͱঢ়ଶఆٛͱ ͭͳ͕ΓΛࣔ͢෦͕த৺Ͱ͋Δͱ͍͏͜ͱɻతΛཧ͢ΔͱਤͷΑ͏ͳߏͱͳΔɻ State Machine Behavior Tree 状態A 状態B ああしたい Aだったら Bだったら 行動 A 行動 B OKだったら OKだったら ਤ 4.3: Behavior tree ͱ State Machine 47 -
ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜͰࢥ͏ 4.1 ࢝Ίʹ
ฒΔͱ̎ͭͷؔ·ΔͰʮૹ৴ػͱड৴ػʯ ɺ·ͨʮϦϞίϯͱஔʯ ɺ ʮͱମʯͱ͍͏ؔ ʹ͋Δͱݴ͑ΔͷͰͳ͍͔ɻ̍ͭͷπϦʔͰ͜ͷͲͪΒΛ͍ࠞͥͯͬͯΔέʔε͖ͬͱ ͋Δͱࢥ͏͕ɺ໌֬ʹผਤͰ͋Δͱͯ͠ղऍͨ͠΄͏͕ऩ·Γ͕Α͍ɻͦΕࢥߟ AI Λஔ͖͑Δ ࡍʹঢ়ଶཧ͝ͱೖΕସ͑ͣҙࢥ͚ͩஔ͖͕͑Ͱ͖Δ͜ͱΛҙຯ͢Δɻ͍ͭͰʹݴ͑ήʔϜΤϯ δϯͷ Unity ΛͬͨͨΊɺΩϟϥΫλϞʔγϣϯཧͷʮStateMachineʯͱ͍͏Ξχϝʔγϣϯ πϦʔΛѻ͏ͱ͢Ε͞Βʹ 1 ͭπϦʔ͕૿͑ɺࡾॏͷπϦʔΛѻ͏ૄ݁߹ཧΈ͍ͨͳͱ͍͏͜ͱ ʹͳΔʢ͖ͬͱΑ͍ҙຯͰʣ ɻͦͷ߹ʹɺͱਆܦճ࿏ͱମɺͱ͍͏͜ͱʹͳΔ͔ɻ 目的 行動 状態 Animation ਤ 4.4: ࡾॏπϦʔ ͋͞ɺݕ౼։࢝ ͦΕͰɺ͔͜͜Βগ͠۩ମతʹήʔϜͷঢ়گΛΠϝʔδ͠ͳ͕ΒऔΓ্͍͛ͯ͘ɻΠϝʔδ ʮΞΫγϣϯήʔϜʯ ɻ ͲΜͳͱ͖ΩϟϥΫλ͕ͲͷΑ͏ͳʮঢ়ଶʯΛͱΔͷ͔ɺࣗతʹʮߦಈʯΛͱΔ߹ʹͲͷΑ ͏ͳಈػɾҙࢥΛͬͯಈ͘ͷ͔ΛϞσϧʹরΒ͠ͳ͕Βߟ͑ͯղઆ͍͖͍ͯͨ͠ɻ プレイヤー エネミー エネミー ਤ 4.5: Πϝʔδ 48 -
ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜͰࢥ͏ 4.2 σβΠϯύλʔϯͰΈΔ
State 4.2 σβΠϯύλʔϯͰΈΔ State GoF Ͱ͓ೃછΈͷσβΠϯύλʔϯʹ State ύλʔϯ͕ଘࡏ͢ΔɺState ผʹੜΫϥεΛઃ͚ ঢ়ଶʹԠͯ͡ΫϥεΦϒδΣΫτΛΓସ͑ͯৼΔ͍Λม͑Δͱ͍͏ख๏Ͱ͋ΔɻৼΔ͍ʹؔ͠ ͯ State ຖʹΫϥε͚Ͱ͖ΔςΫχοΫͰ͋Δ͕ɺભҠ݅ʹؔͯ͠Ϟσϧͱͯ͠໌֬ʹΈࠐ ·Ε͍ͯΔͷͰͳ͍ɻState ύλʔϯͰ State நΫϥεͰৼΔ͍͝ͱͷ Method Λઃ͚ɺ State ผʹੜ࣮ͯ͠͠ɺ࣮ߦ࣌ݱࡏঢ়ଶʹ߹Θ࣮ͤͯߦΫϥεΦϒδΣΫτΛࠩ͠ସ͑Δɺͱ ͍͏ઃܭͱͳΔͷͰ͋Δɻ ࢲ͕Α͘͏ State ࣮ɺ·ͣը໘ભҠͷγεςϜ͕ڍ͛ΒΕΔɻը໘ભҠͰ State Λߟ͑Δͱ ύλʔϯཧղͱͯ͠ͱͯ߹க͍͢͠ͷͩɻͨͱ͑ View() ϝιουͰදࣔɺBack() ϝιουͰ ը໘Δॲཧɺͱ͍ͬͨڞ௨ͨ͠ݺͼग़͠ʹ͍ͭͯநؔͱͯ͠ઃఆ͠қ͘ɺState ࢦఆʹΑͬͯ ભҠݺͼग़͠ʹରͯ͠ͱ্ͯखʹରԠͰ͖Δɻ ࠓճήʔϜΩϟϥΫλͷৼΔ͍ʹରͯ͠நԽ࣮ͨ͠Λ͍ͨ͠ͷͰɺΩϟϥΫλͷ State ʹ ͍ͭͯߟ͍͑ͯ͘ɻ Attack Wait Damage Death Move Jump ਤ 4.6: State ͱભҠ݅ ਤͷΑ͏ʹࣗΩϟϥΫλͰ͋ΕԿ͔ͷΩʔʹԠͨ͡ڍಈ͔ɺ֎తͳύϥϝʔλཁҼʢμϝʔδͳ ͲʣΛड͚ͨͱ͖ʹ State ભҠ͕ى͜Δɻ ఢΩϟϥΫλͰಉ͡ϩδοΫͷ··Ωʔೖྗ෦Λผͷܦ࿏ͰࣗಈԽ͢Δɺͭ·Γຊষͷओ Ͱ͋ΔήʔϜ AI ʹΑͬͯಈ͔͢͜ͱΛΉɻͦΕͭ·ΓೖྗϞδϡʔϧͷஔ͖͕͑؆୯ʹ࣮ ݱͰ͖ΔΑ͏४උΛ͓͚ͯ͠ɺࣗΩϟϥͰఢΩϟϥͰͨ·ͨຯํ CPU Ωϟϥʢຯํ AIʣ ͰɺҰ؏ͨ͠ΩϟϥΫλ੍ޚ࣮͕Ͱ͖Δͱ͍͏ૂ͍ͱͳΔɻ͍ͯͬͨ͞ΜɺΩϟϥΫλঢ়ଶΛ State ΫϥεϝιουͰཧ͢ΔͱදͷΑ͏ͳͷ͕ڍ͛ΒΕΔɻ ˙جຊతͳΩϟϥΫλঢ়ଶ • ػ Wait() 49 -
ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜͰࢥ͏ 4.3 Behavior
Tree ΛઃܭʹऔΓೖΕΔ • Ҡಈ Move() • δϟϯϓ Jump() • ߈ܸ Attack() • μϝʔδ Damage() • ࢮ Death() ήʔϜͷಛੑʹԠͯ͡͞ΒʹछྨΛઃ͚͍͚ͯΔ͜ͱ༰қʹΠϝʔδͰ͖ΔͩΖ͏ɻ·ͨݟ͓ͯ ͔ΓͷΑ͏ʹɺ΄΅ಉ͡ State ໊ͰΩϟϥΫλϞʔγϣϯׂ͕ΓͯͰ͖Δͱݴ͑Δɻ State Machine ΛઃܭʹऔΓೖΕΔ State Machine ɺભҠ݅ͱৼΔ͍ʹؔ͢ΔϞσϧͰ͋Δ͕ɺιϑτΣΞ։ൃͷϑϨʔϜ ϫʔΫͱͯ͠Ͱͳ֓͘೦ઃܭͱͯ͠ͷར༻ͷํ͕ҰൠతͰɺৼΔ͍Λܭը͢ΔਤղࢿྉͰΑ͘ ʹ͢Δͷͱࢥ͏͕ɺίʔυͰѻ͏߹ʹ Switch-Case ʹཔΔ͜ͱʹͳΓ͕ͪͰ͋ΔɻۙͰ πϦʔঢ়ʹλεΫΛઃఆͰ͖ΔϏδϡΞϥΠζ͞Εͨ։ൃπʔϧ͕ྲྀߦͯͦ͠ΕΛ༻͍Δ߹ʹ ࢹ֮తʹར༻Ͱ͖ΔΑ͏ʹͳͬͨɻ ϏδϡΞϧΤσΟλڥલ్ͨ͠ Behavior Tree ͷΞϓϩʔνͱ΄΅ಉٛͳͷͰ·ͱΊͯ࣍અ ͔Βॻ͍͍͖͍ͯͨɻ ͔͠͠ͳ͕ΒϏδϡΞϧΤσΟλͰ͋ͬͨͱͯ͠ɺߏ͕ෳࡶʹංେԽ͢ΔͱཧͰ͖ͳ͘ͳΔ ڪΕ͕͋Γɺίʔυͱͯ͠ͲͷΑ͏ʹΞϓϩʔν͢Δ͖͔ͱ͍͏͜ͱߟ͍͖͍͑ͯͨɻ ංେԽڊେԽͨ͠ྫͱͯ͠ॳϏδϡΞϧΤσΟλཧ͞Ε͍ͯͨେنͳ։ൃձࣾͷϓϩδΣ ΫτͰ࠷ऴతʹൃಈભҠ͕݅ Excel දཧʹணͨ͠ͱ͍͏͜ͱ͕͋Δͱ͍͏ɻ͜ͷహܦݧ ্ɺ։ൃݱͰΑ͘ى͜Γ͏Δ͋Δ͋ΔͰɺଟػೳπʔϧઆ͔Β࢝·Γɺ݁ہҰ൪࣍ݩʢϩʔϨϕ ϧʣͳํ๏Ͱղܾ͞ΕΔ༷ࢠͱॏͳΔͱࢥ͑ͨɻͳ͓͜Εঢ়گ࣍ୈͰɺͲͪΒ͕ຊ࣭తʹྑ͍ͱ ͍͏͜ͱͰͳ͍ɻ ͳ͓ίʔυͰѻ͏߹ͷঢ়ଶભҠ݅ͷநԽɺ͋Δ͍ͱ͍͏໘Ͱྑ͍Ϟσϧͪΐ͏Ͳ Α͍ܗͷԿ͕͋ΔͩΖ͏͔ɻલ్༷ͨ͠ʹͳͬͨͷɺۙతͳɺ͋Δ͍Α͘ཧͰ͖Δ࣮ݱ ํ๏͕ҙ֎ͱ͍͜͠ͱ͕ݱΕ͍ͯΔͷͰͱࢥ͏ɻ 4.3 Behavior Tree ΛઃܭʹऔΓೖΕΔ ࣍ʹߟ͍͑ͯ͘ͷɺBehavior Tree ΛͲͷΑ͏ʹਾ͑Δͷ͔ͱ͍͏͜ͱɻతͱͯ͠؆қతʹ ཧͨ͠ΩϟϥΫλͷߦಈτϦΨ͜ͷΑ͏ͳߏʹͳΔɻ 50 -
ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜͰࢥ͏ 4.3 Behavior
Tree ΛઃܭʹऔΓೖΕΔ プレイヤーを探 す プレイヤーにダ メージを与える プレイヤーから 逃げる 条件 条件 条件 行動 行動 行動 走査 ਤ 4.7: ΩϟϥΫλͷߦಈτϦΨ ଓઌʹߦಈΛ༩͑Δͱ͍͏ͷͩɻ༩͑ํ࣍ୈͰߦಈ͕ঢ়ଶͦͷͷʹͳΓ͏ΔͷͰɺ͋Β͔ ͡Ίܭը͕ඞཁʹͳΔͩΖ͏ɻ Behavior Tree ͷϏδϡΞϧΤσΟλͬͯͲΜͳͷ ʢ˞ʮϏδϡΞϧΤσΟλʯͱҰൠԽͨ͠ݺশͱͯࣔͨ͠͠ͷͰಛఆͷࢦ͠·ͤΜʣલ ܝͨ͠௨Γ݅ͱಈ࡞͕ϏδϡΞϥΠζ͞ΕͨπϦʔঢ়ͷͷ͕ͩɺࢹ֮తʹΘ͔Γ͍͢͜ͱ͔Β ήʔϜσβΠφʔͳΓ͕Ϛεૢ࡞ͰσʔλΛ࡞͍ͯ͘͠πʔϧͱͯͬ͠ͺΒར༻͞Ε͍ͯΔɻ ͜ͷπϦʔʹొͨ͠ϧʔϧʹԊͬͯҙࢥܾఆ͠ɺొͨ͠ߦಈΛى͜͢ɻ Ұൠతʹ Behavior Tree ͷϊʔυछผ͜ͷ௨Γɻ ˙ҰൠతʹΑ͋͘Δϊʔυͷछྨ • Action ɹ࣮ࡍͷॲཧΛ࣮ߦʢDOʣ • Decorator ɹλεΫͷฦ٫ΛՃ݅͠ఆʹ͢ʢDOʣ • Conditional ɹ୯Ұ݅ఆʢIFʣ • Composite ɹෳࢠϊʔυΛ࣮ߦͯ͠ఆΛ͢ • Sequence ɹ AND ͷ͜ͱ • Selector ɹ OR ͷ͜ͱ • Service ɹࢦఆͨ͠සͰݺͼग़͢λεΫ • BlackBord ɹσʔλ ˞ׂͱ໊শͦΕͧΕͷγεςϜͰଟগΒ͖͕ͭ͋Δ ͜ͷΞϓϩʔνͷ໊ࢺͱ͍ͬͯաݴͰͳ͍ Unreal Engine 4 ͰͳΒɺΑΓͬͱଟ͘ͷϊʔυ छผ͕͋Δɻ ͜ΕΒΛचͭͳ͗ɺλίઢͯ͠ɺ݅ˠߦಈͷભҠΛߏங͍ͯ͘͠ͷͰ͋Δɻ ͜Ε͜ΕͰ͍ͨΜ͢Β͘͠࡞ۀ୲͕Ͱ͖ͯཧతͳͷ͕ͩɺ͍ࠐΜͰ͍͘ʹ͋ͨͬͯ ҙ͓͖͍ͯͨ͠ݒ೦͋Δɻͳ͓ɺΑ͋͘Δ֤छͷಈ࡞ൺֱผͷػձʹͱ͓ͬͯ͘ͱͯ͠ɺ࣍ 51 -
ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜͰࢥ͏ 4.4 ҧ͍Λཧղͯ͠׆༻͢Δ
ͷΑ͏ͳ෦ͰϓϩδΣΫτͷ͕ى͖ͳ͍༷ʹऔΓѻ͑ͨΒͱࢥ͏ɻ ˙Behavior Tree ͷϏδϡΞϧΤσΟλ্Ͱͷҙ • πϦʔ͕ڊେԽɾෳࡶԽ͍͢͠ • ڊେԽͨ͠ͱ͖݁ہίʔυͰ࣮͠͞ͳ͍ͱ࣮ߦίετ͕ߴ͍ • ͜ͷπϦʔσʔλΛߏஙͰ͖Δऀͱ͍͏ಛघεΩϧ͕ඞཁʹͳΔ • ݁ہΤϯδχΞ࡞ۀʹͬͯ͘ΔڪΕ • πϦʔσʔλͷଟਓฤूϚʔδʹিಥ͕ى͜Δͷ͕͋Δ • πϦʔσʔλͷ͚֎ࠩ͠͠ସ͕͑ޮ͔͘ • ඞͣ͠πϦʔঢ়͕ཧ͍͢͠ͱݶΒͳ͍ʢϚτϦοΫεදͰͳΒ؆ܿͳ߹ͳͲʣ ͍ॴΛྑ͘ݟఆΊ্ͯखʹ͖߹͍ͬͯ͘ඞཁ͕͋Δͷͩɻ 4.4 ҧ͍Λཧղͯ͠׆༻͢Δ ઌ΄Ͳड़ͨΑ͏ʹɺݟͨతʹ͜ͷ 2 ͭࣅ͍ͯΔ͔ɺશʹಉ͡Ͱ͋Δɻπʔϧͱͯ͠Ͳ ͪΒ࣮ݱͰ͖ͯ߹Θͬͯ͞ѻΘΕ͍ͯΔɻ͏ଆ͕ʮStateʯΛߏங͢Δͧͩͱ͔ʮBehaviorʯͩ ͧͱߟ͑Λ͍ͬͯͳ͍ͱ͏ΉʹͳΓ͍͢ɻͦΕͰΑ͍߹͋Δ͕ɺલ్ͨ͠Α͏ͳݒ೦Λ ͋Β͔͡Ίߟྀ͓ͯ͘͠ͷͰ͋Εɺ͜ͷҧ͍ͱ͍ॴΛཧղͯ͠औΓΜͰ͍͘͜ͱΛਪ͢Δɻ Ґஔ͚ɺBehavior Tree Ͱจࣈ௨ΓߦಈΛఆٛ͢Δ͜ͱʹ͚ɺState Machine Ͱঢ়ଶͱ ભҠ݅Λఆٛ͢Δ͜ͱʹ͚͍ͯΔɻͭ·Γʮߦಈʯʮൃಈ݅ʯόϦΤʔγϣϯΛ૿ͯ͠ ͍͘ʹ͋Γɺ ʮঢ়ଶʯෳࡶʹ͕͍ͬͯͯܥ౷ཱͯɺू͠ɺҰҙͰ͋Δ͖Ͱ͋Δͱཧ ͨ͠ɻ バリエーションを多くしたい 行動 状態 系統立てて整理し一意にしたい ਤ 4.8: ߦಈͱঢ়ଶͱભҠ݅ ͜ΕΛҙࣝ͠ͳ͍߹ɺ ʮߦಈʯΛ࣍ʑͱ૿ͨ͠ͱ͖ͷଓઌͷʮঢ়ଶʯɺͦͷઌʹͱΔҰ࿈ ͷ࣍ͷʮߦಈʯͱΛผͷϧʔτͱ͚Δ͘ɺ ʮঢ়ଶʯ͍ͭ͘ॏෳͯͦ͠ͷߦಈͷ͚ͩੜ͞ ΕΔڪΕ͕͋Δɻͨͱ͑͋Δঢ়گԼͰఢΩϟϥΫλ͕ϓϨΠϠʔΛݟ͚ͭΔʮߦಈʯͷ͍͔ͭ͘ͷ 52 -
ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜͰࢥ͏ 4.5 ϏδϡΞϥΠζͰ͖ͨͱͯ
தʹҠಈͱ͍͏ʮঢ়ଶʯؚ͕·ΕΔࣄʹͳΔɻ͜ͷঢ়ଶͰߦΘΕΔߦಈΛ Behavior ͷʮߦಈʯͱ ܾͯ͠Ίͨ߹͜Ε͜ͷҠಈલޙͷҰ࿈ͷྲྀΕΛټΜͩࡧఢͷҠಈͱ͍͏༻్͕ݶఆ͞Ε͓ͯΓ ࠶ར༻ੑ͕͘ந͕͍ɻରʹ State ͷʮঢ়ଶʯͱͯ͠ҠಈͷߦಈΛܾΊͨ߹Ͳͷঢ়ଶ͔ ΒͰ͋ΕͲΊಉ͡ߦಈ͕औΕΔͱͯ͠࠶ར༻ੑ͕ߴ͘ந͕ߴ͍ɻ ͜ͷΑ͏ͳ͜ͱ͔Βɺಉ݁͡ՌΛى͜͢ͷͱͯ͠Ͱ͋ͬͯҐஔ͚औΓճ͠ํ͕มΘͬͯ ͘Δɻ 索敵 待つ 見渡す 進む 振り向く 歩く 走る 見失う 待機 状態における行動 行動における状態 ਤ 4.9: Behavior Tree ͷߦಈͱ State Machine ͷঢ়ଶͷߦಈͷҧ͍ 4.5 ϏδϡΞϥΠζͰ͖ͨͱͯ ͦ͏͍ͬͨ࠶ར༻ੑཧ໘Λҙ͍ࣝͯ͘͠ͱɺͰ͖Δ͚ͩߴ͍࣍ݩͰநԽͨ͠ʮߦಈࢦࣔʯͱ ۩ମతͳʮݸผߦಈʯͱΛͯ͠อ͍ͬͯͳ͚ΕେྔͷѥछΛฏߦͰཧ͍͔ͯ͠ͳ͚ΕͳΒ ͳ͍͜ͱ͕༧ݟͰ͖ΔɻநԽͱ͍͑ɺͲͷ࣍ݩͰղऍͰ͖ΔͷͰ͋ͬͯɺཧͷ͠ํͦΕͧ ΕͰࣗ༝͕͋Γ͗͢Δɻ౷Ұ͍ͨ͠ͷ࣍ୈͰཧ͢Δͷ͕Α͍ͩΖ͏ͱࢥΘΕΔɻͨͱ͑ɺ 1. ఢΩϟϥΫλΛछผͰཧ͢Δͱͨ͠߹ʹɺछผ͝ͱʹڞ௨ϞσϧΛٕ࣋ͬͯߦಈঢ়ଶ ϨϕϧͰཧ͢ΔͷΛʮߦಈࢦࣔʯͱͨ͠ͱ͖ɺݸผΩϟϥΫλຖʹऔΓѻ͏࣌ؒڑͷ ҧ͍͋Δ͍߈ܸٕͳͲͷߦಈ༰ͷҧ͍ʹ͍ͭͯΛ۩ମతͳʮݸผߦಈʯͱ͚ͯͯ͠ཧ Ͱ͖Εཧ໘Ͱ͋ͱ͋ͱݒ೦͕গͳ͍ɻ 2. ͋Δ͍ɺཁૉͭ·ΓߦಈͷՌͨ͢త͝ͱʹநԽͨ͠Ϟδϡʔϧঢ়ʹͯ͠ɺ ʮݸผߦಈʯͱ ͯ͠ΩϟϥΫλ͝ͱʹΈཱͯΔߦಈࢦࣔΛͯ͠Α͍ɻߦಈϞδϡʔϧࢦఆπϦʔΛ࣋ͭɺ ͱ͍͏ܗʹͳΔɻ 3. ൃలϛοΫεͤͯ͞ɺΩϟϥछผ͝ͱʹڞ௨ͰߦಈϞδϡʔϧࢦఆπϦʔʢ2 ͷͷʣΛ࣋ͪɺ ϞδϡʔϧྲྀΕͷఆٛͰ࣮͋ͬͯߦ͢Δ༰Ωϟϥݸผߦಈࢦఆ ʢ1 ͷͷʣͱͯ͠Α ͍ɻ͜ͷ߹ Behavior Tree Ͱࡾͱ͍͏۩߹ʹͳΔɻ ͜͏͍ͬͨ͜ͱΛݕ౼͓ͯ͘͜͠ͱͰ࡞ۀͱཧํ๏͕ཧ͞ΕΔɻͭ·ΓɺϏδϡΞϥΠζ͞Ε 53 -
ୈ 4 ষ Behavior Tree ͰήʔϜ AI ʹऔΓΜͰࢥ͏ 4.6 Behavior
Tree ͕࢘Δͷ ͍ͯΔΤσΟλͰ͋ΕշదʹཧͰ͖ΔʙͷૣܭͰɺগͳ͘ͱ͋Β͔͡Ίܭը͠ͳ͍ͱ·ͬ ͍ͯ͘ਂΈ͕ਂ͍ͷͰͱߟ͍͑ͯΔɻ 4.6 Behavior Tree ͕࢘Δͷ Ͳ͜·Ͱ͕ରԠͰ͖ΔπʔϧڥͰऔΓΜͰ͍Δ͔ʹΑΔͱ͜Ζ͕ͩɺ͜ΕΒΛ౿·͑ͨํ๏ Λ͠औΔͱ͢Ε... • Behavior Tree Ͱଟతʹཧͯ͠Լʢݸผʣ͕ผϑΝΠϧʹͳ͍ͬͯΔ • Behavior Tree ͱ State Machine ͕ฒྻʹ͋Γ Behavior ͷߦಈͰ State τϦΨ͔͠ൃ͠ͳ͍ • ଟཧ͢Δͱ͖ͲͪΒ͔ͷ྆͘͠ํΛϏδϡΞϧπϦʔͰͳ͍ͷͰߏங͢Δ ͷ͍ͣΕ͔Ͱͳ͚Εɺཧ্ݫ͍͜͠ͱʹͳΔͷͰͳ͍͔ͱߟ͑Δɻͦ͏Ͱͳ͍߹ɺલ్͠ ͨ༧ݟ͙ͨ͢͠ʹൃੜ͢ΔͩΖ͏ɻͦͷ΄͔ʹɺͦͦϑΝΠϧԽ͞Εͳ͍ͳͲɺΑΓ ͕ى͜Γ͍͢ঢ়گʹۙ͘ͳ͍͔Λબఆͷࡍʹҙ͍ͨ͠ɻ 4.7 ݁ͼ ιϑτΣΞ։ൃɺ༰ͷൃలʹΑͬͯඞͣෳࡶԽ͠ɺ࠷ॳ͔Βૂ͍͑ͯͨΒΑ͔ͬͨͱࢥ͏͜ ͱ͕Ͳͷ࣌ى͜ΔɻͲ͜ʹࣗ༝Λઃ͚Δ͔ɺͲΜͳεΩϧͷਓ͕ؒରԠͰ͖ΔΑ͏ʹ͢Δ͔ɺͲ ͷΑ͏ʹߋ৽͢Δ͔Λߟྀͨ͠ઃܭͱ͍͏ͷΛ৺͕͚͍ͨɻ ϏδϡΞϧΤσΟλͷ׆༻ͳͲΛਐΊɺྔ࢈࣌ͷোน͕͍ํ͕࡞ۀޮԽͱ্࣭ʹد༩͢Δ ͜ͱ͔͍ͬͯΔɻಛੑΛཧղͯ͠͏·͖͘߹͍͚ͬͯͨΒɺίʔυ։ൃͱϊϯίʔυ։ൃΛద ͍͚ٓΒΕΔྑ͍ੈքʹͳΔͱߟ͍͑ͯΔɻ ͯ͞࠷ޙʹɺ͜ͷ։ൃख๏ͰऔΓΜͩৼΓฦΓͱͯ͠ Behavior Tree ͱ State Machine ͕Ͳ͏ ͋Εઃܭ໘Ͱྑ͍͔ΛࢲݟͰॻ͖௲ͬͨɻਓͦΕͧΕॏΜ͍ͨ͡෦͕ҟͳΔ͜ͱͱࢥ͏ͷͰɺࠓ ճड़ͨ͜ͱΑΓ΄͔ͷͰڧ͘ҙࣝ͠ͳ͚ΕͳΒͳ͍͜ͱ͕͋Δਓɺආ͚͍ͨͱड़ͨͱ͜ ΖΛޮՌతʹ׆༻Ͱ͖Δਓ͍Δ͔͠Εͳ͍ɻͦΜͳ࣌ɺͥͻͱͬ͘͡Γࢲʹڭ͍͑ͯͨͩ ͖ɺҙݟΛަΘ͍ͨ͠ͱئ͏ɻ 54 -
ୈ 5 ষ Git ͷ index tree ͱ݁ہͳΜͳͷ͔ 5.1 ࢝Ίʹ
ίτμϚϯࣄۀ෦ͰΫϥΠΞϯτΤϯδχΞͱαʔόαΠυΤϯδχΞΛ͍݉ͯ͠Δ౻ాͰ͢ɻ ಥવͰ͕͢ɺΈͳ͞Μ৽ೖࣾһʹ࣍ͷΑ͏ͳ࣭Λ͞Εͯ͑ΒΕ·͔͢ʁ • ͳۭͥσΟϨΫτϦ add Ͱ͖ͳ͍ͷ͔ • git status ͕ߴʹಈ࡞͢Δϫέ • ίϯϑϦΫτதʹͲ͏ͬͯ merge ઌͱ merge ݩΛ۠ผ͍ͯ͠Δͷ͔ ͑ΒΕͳ͔ͬͨਓɺຊষΛಡΊཧղͰ͖Δͱࢥ͍·͢ɻ ฐࣾͰ৽ଔΤϯδχΞࣾһ͚ͷٕज़ݚमͷ 1 ͭͱͯ͠ Git ݚमΛຖߦ͍ͬͯ·͢ɻࠓ ͷ Git ݚमࢲ͕ߨࢣΛ୲͍ͯͨ͠ͷͰ͕͢ɺݚम༰ΛݻΊΔաఔͰ Git ͷ index tree ʹ͍ͭ ͯਂ۷Γͯ͠ΈΔͱɺڵຯਂ͍ಛ͕ͨ͘͞Μ͋ͬͨͷͰɺຊষͰ Git ͷ index tree ʹ͍ͭͯͷ ͋Ε͜ΕΛհ͍ͨ͠ͱࢥ͍·͢ɻ 5.2 Git ͷ commit ʹ͍ͭͯ index tree ͷਂ͍Λ͢Δલʹɺgit commit ͷڍಈʹ͍͓ͭͯͬͯ͘ඞཁ͕͋ΔͨΊɺ؆୯ʹ ͓͞Β͍͓͖ͯ͠·͢ɻ $ git commit Λ࣮ߦ͢Δͱ.git/objects/ҎԼʹ commit object ͕ੜ͞Ε·͢ɻ commit object ɺҎԼͷใΛ࿈݁ͤͨ͞ͷΛɺzlib Ͱѹॖͯ͠࡞ΒΕ·͢ɻ • ࣝผࢠ (จࣈྻ"commit") • object ͷαΠζ 55 -
ୈ 5 ষ Git ͷ index tree ͱ݁ہͳΜͳͷ͔ 5.2 Git
ͷ commit ʹ͍ͭͯ • tree object*1ͷ SHA-1 • commit ͷ SHA-1 • committer ͷ໊લɺϝʔϧΞυϨεɺίϛοτ࣌ࠁ • author ͷ໊લɺϝʔϧΞυϨεɺίϛοτ࣌ࠁ • ίϛοτϝοηʔδ ѹॖલͷঢ়ଶͷ SHA-1 ͕ͦͷ·· commit ͷϋογϡʹͳΓ·͢ɻ·ͨɺcommit ϋογϡ ετϨʔδͷύεʹͳ͓ͬͯΓɺͨͱ͑ commit ϋογϡ͕ aff91e72719be31e58dd814a99 d8e6dc5103d731 ͷͱ͖ɺcommit ͷ࣮ମ.git/objects/af/f91e72719be31e58dd814a99d8e6 dc5103d731 ʹอଘ͞Ε͍ͯ·͢ɻ tree object ϦϙδτϦͷঢ়ଶΛද͓ͯ͠Γɺ؆қతͳϑΝΠϧγεςϜͷΑ͏ͳߏʹͳͬͯ ͍·͢ɻtree object ʹɺͦͷσΟϨΫτϦʹؚ·Ε͍ͯΔϑΝΠϧͷ͞·͟·ͳใ͕ྻڍ͞Ε ͍ͯ·͢ɻ • ࣝผࢠ (จࣈྻ"tree") • object ͷαΠζ • ϑΝΠϧͷଐੑ (ϑΝΠϧͷछྨɺύʔϛογϣϯ) • ϑΝΠϧ (σΟϨΫτϦͷ߹ͦͷσΟϨτϦͷ tree object) ͷ SHA-1 • ϑΝΠϧ໊ commit object ͱಉ͘͡ɺ͜ΕΒͷใΛ࿈݁ͤͯ͞औͬͨ SHA-1 ͕ετϨʔδͷύεʹͳΓɺ zlib Ͱѹॖͨ͠ͷΛอଘ͠·͢ɻtree object ͕ࢦ͍ͯ͠ΔϑΝΠϧͷ SHA-1 Git Ͱ blob ͱ ݺΕΔ object ͷϋογϡͰ͢ɻblob ϑΝΠϧͷ༰ͦͷͷΛ zlib Ͱѹॖͨ͠ͷͰɺ΄͔ ͷ object ͱಉ༷ʹอଘ͞Ε͍ͯ·͢ɻ ͦΕͧΕͷ object ͷதɺҎԼͷίϚϯυͰ؆୯ʹ֬ೝͰ͖ΔͨΊɺҰ͍ͯΈΔͱ͓͠ Ζ͍ͱࢥ͍·͢ɻ $ git cat-file -p <ϋογϡ> Git ͷ object ʹ͍ͭͯɺ ʮPro Git - 10. Git ͷଆʯ *2Ͱ͔Γ͘͢ղઆ͞Ε͍ͯΔͨΊɺ͞ Βʹৄ͘͠Γ͍ͨ߹ࢀর͍ͯͩ͘͠͞ɻ *1 ৄ͘͠ޙड़͠·͕͢ɺtree object ϦϙδτϦͷϑΝΠϧͷঢ়ଶΛอ͍࣋ͯ͠·͢ɻ *2 https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4- Git%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88 56 -
ୈ 5 ষ Git ͷ index tree ͱ݁ہͳΜͳͷ͔ 5.3 index
tree ࠩཧͰͳ͍ 5.3 index tree ࠩཧͰͳ͍ ৄ͘͠ޙड़͠·͕͢ɺindex tree ͷ࣮ git add ͳͲͷपลαϒίϚϯυͷڍಈ͔Βߟ͑Δ ͱɺ͋·ΓײతͰ͋Γ·ͤΜɻͦͷ݁Ռɺindex tree Α͘ޡղ͞Εͨ··ΘΕ͍ͯ·͢ɻ࠷ యܕతͳޡղͱ͍͑ɺindex tree ͕ࠩΛཧ͍ͯ͠ΔͱࢥΘΕ͍ͯΔ͜ͱͰ͠ΐ͏ɻgit add ͢Δࡍɺࠩͷ͋ΔϑΝΠϧ͚ͩΛࢦఆͨ͠Γɺ-p ΦϓγϣϯͰಛఆͷ͚ࠩͩΛ index tree ʹొ ͨ͠ΓͰ͖ΔͨΊɺ͍͍ͭͭ index tree working directory ͷࠩΛ͍࣋ͬͯΔͱࢥΘΕ͕ͪͰ ͕͢ɺ࣮ࡍཧ͍ͯ͠ΔͷࠩͰͳ͘ɺϑΝΠϧͦͷͷΛཧ͍ͯ͠·͢ɻ ϑΝΠϧͦͷͷΛཧ͢Δͱɺadd ͢Δͨͼʹͦͷ༰Ͱ blob Λੜ*3͠·͢ɻ ͦ͏ฉ͘ͱɺgit add ͕ϦϙδτϦͷͯ͢ͷϑΝΠϧͷ SHA-1 Λܭࢉͯ͠ zlib Ͱѹॖ͢Δͱ ͍͏ɺͱͯͭͳ͘ॏͨ͘ɺσΟεΫ༰ྔେྔʹফඅ͢ΔॲཧΛ͍ͯ͠ΔΑ͏ʹฉ͑͜·͢ɻ͔͠ ࣮͠ࡍɺฤू͍ͯ͠ͳ͍ϑΝΠϧʹؔͯ͠ɺવ blob ͷϋογϡಉ͡ʹͳΓɺ.git/objec ts/ҎԼʹಉ͡༰ͷϑΝΠϧ͕͢Ͱʹଘࡏ͢Δ͜ͱʹͳΔͨΊɺมߋ͕͋ͬͨϑΝΠϧҎ֎ zlib Ͱѹॖ͢Δॲཧল͔Ε৽ͨʹσΟεΫ༰ྔফඅ͠·ͤΜɻ ͱ͍͑ɺcommit ͢Δ·ͰʹϑΝΠϧΛमਖ਼ͯ͠ add Λ܁Γฦ͍ͯ͠Δͱɺແବͳ blob ͕૿͑ ͍͖ͯ·͢ɻ࣮ࡍʹ֬ೝͯ͠Έ·͠ΐ͏ɻ $ git init Initialized empty Git repository in /home/******/sample/.git/ $ touch hoge $ git add hoge $ echo "hoge" > hoge $ git add hoge $ git commit -m "first commit" [master (root-commit) c116179] first commit 1 file changed, 1 insertion(+) create mode 100644 hoge $ git fsck Checking object directories: 100% (256/256), done. dangling blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 git fsck Ͳͷ commit object ͔Βࢀর͞Ε͍ͯͳ͍ object Λ୳͢ίϚϯυͰɺdangling ͍ͯ͠Δ blob ͕ 1 ͭݟ͔ͭΓ·ͨ͠ɻdangling object ɺ୯ମͰେͨ͠αΠζͰͳͯ͘ɺ commit ͢Δؾ͕ͳͯ͘ৗతʹ add Λ͖ͨͨͳ͕Β࡞ۀΛਐΊΔਓɺ͔ͳΓͷྔͷ dangling object ͕ੜ͞Εͯ͠·͏ͨΊɺσΟεΫΛѹഭͯ͠͠·͍ͦ͏Ͱ͢ɻ ͔͠͠ɺ࣮ࡍʹ dangling object ʹΑͬͯࡍݶͳ͘ফඅ͞ΕΔ͜ͱ͋Γ·ͤΜɻGit ͕͜ͷΑ *3 add ͨ͠ͱ͖ʹ࡞ΒΕΔ object blob object ͷΈͰɺtree object ੜ͠·ͤΜɻ 57 -
ୈ 5 ষ Git ͷ index tree ͱ݁ہͳΜͳͷ͔ 5.4 index
tree ͷ࣮ମͱߏ ͏ͳϑΝΠϧΛআ͢ΔͨΊ*4ʹ git gc ͱ͍͏ίϚϯυΛఆظత*5ʹ࣮ߦ͢Δ͔ΒͰ͢ɻ 5.4 index tree ͷ࣮ମͱߏ index tree ͕Ͳ͏ׂ͍ͬͨΛ͍࣋ͬͯΔͷ͔Λ֬ೝͨ͠ͱ͜ΖͰɺ͔͜͜Β index tree ͷΑ Γ۩ମతͳϑΝΠϧϑΥʔϚοτͳͲʹ͍ͭͯհ͍͖ͯ͠·͢ɻ͜͜Λཧղ͢Δ͜ͱͰ index tree ʹ·ͭΘΔɺ͞·͟·ͳෆՄղͳڍಈཧղͰ͖ΔͣͰ͢ɻ ·ͣɺindex tree ͷ࣮ମ.git/index ʹ͋Γ·͢ɻindex tree ͷϑΝΠϧϑΥʔϚοτʹ͍ͭͯ ɺެࣜϦϙδτϦͷ technical document*6ʹৄࡉʹهࡌ͞Ε͍ͯ·͢ɻͬ͟ͱ·ͱΊΔͱɺindex tree 2 छྨͷϒϩοΫͷू߹ମͰɺઌ಄ʹ 12byte ͷϔομ͕͋Γɺଓ͍ͯՄมͷΤϯτϦ͕ొ ͞Ε͍ͯΔϑΝΠϧʹ͍͚ͩ͠ଓ͘ͱ͍͏ɺϦετ 5.1 ͷΑ͏ͳߏʹͳ͍ͬͯ·͢ɻ Ϧετ 5.1: index tree ͷϑΝΠϧϑΥʔϚοτͷ֓ཁ ------------------------------------------------------------------------------- | 12byte - Header ------------------------------------------------------------------------------- | variable length - Entry 0 ------------------------------------------------------------------------------- | variable length - Entry 1 ------------------------------------------------------------------------------- | variable length - Entry 2 ------------------------------------------------------------------------------- | ... ------------------------------------------------------------------------------- | variable length - Entry N ------------------------------------------------------------------------------- ࣍ʹɺ֤ϒϩοΫͷϑΥʔϚοτʹ͍ͭͯݟ͍͖ͯ·͢ɻͥͻ͓खݩͷ index tree Λ hexdump Ͱ ோΊͳ͕ΒಡΈਐΊͯΈ͍ͯͩ͘͞ɻ·ͣɺ ϔομͷϑΝΠϧϑΥʔϚοτϦετ 5.2 ͷ௨ΓͰ͢ɻ Ϧετ 5.2: ϔομͷϑΝΠϧϑΥʔϚοτ ------------------------------------------------------------------------------- | Header | 4byte - signature(’D’,’I’,’R’,’C’) | |=================================================================== | 12byte | 4byte - version number(2 or 3 or 4) | |=================================================================== | | 4byte - number of index entries ------------------------------------------------------------------------------- *4 Ճ͑ͯɺશӬଓσʔλߏʹͳ͍ͬͯͯαΠζͷ໘Ͱޮͷѱ͍ object ΛӬଓσʔλߏʹͯ͠ pack file ͱݺ ΕΔͷʹ·ͱΊΔ࡞ۀߦ͍·͢ɻ *5 ͍ͭ git gc ͕࣮ߦ͞ΕΔ͔ώϡʔϦεςΟΫεͰஅ͍ͯ͠·͢ɻৄ͘͠ https://git-scm.com/docs/git- gc#Documentation/git-gc.txt-gcauto Λࢀর͍ͯͩ͘͠͞ɻ *6 https://github.com/git/git/blob/master/Documentation/technical/index-format.txt 58 -
ୈ 5 ষ Git ͷ index tree ͱ݁ہͳΜͳͷ͔ 5.4 index
tree ͷ࣮ମͱߏ ઌ಄ 4byte ϚδοΫφϯόʔͰɺඞͣ ASCII จࣈͰ"DIRC"Ͱ͢ɻଓ͘ 4byte index tree ͷ όʔδϣϯΛද͢Ͱɺݱࡏͷ࠷৽൛Ͱ͋Δ Git2.28 Ͱ 2 ~ 4 ͷόʔδϣϯ͕ଘࡏ͠·͢ɻҙ ͓ͯ͘͠ඞཁ͕͋Γ·͕͢ɺindex tree ͷόʔδϣϯ༻͍ͯ͠Δ Git ͷόʔδϣϯʹΑͬͯݻ ఆ͞Ε͍ͯΔΘ͚Ͱ͋Γ·ͤΜɻgit update-index ͷಛघͳΦϓγϣϯΛ༻͢Δͱόʔδϣ ϯ͕มΘΓɺޙͷΤϯτϦͷߏ͕มԽ͠·͢ɻ؆୯ͷͨΊɺຊষͰσϑΥϧτόʔδϣϯͰ͋Δ όʔδϣϯ 2 Λղઆ͠·͢ɻ࠷ޙʹΤϯτϦͷ͕ 4byte Ͱೖ͍ͬͯ·͢ɻ1 ΤϯτϦ 1 ϑΝ ΠϧʹରԠ͍ͯ͠ΔͨΊɺΤϯτϦݱࡏͷϦϙδτϦʹଘࡏ͢ΔϑΝΠϧʹ͘͠ͳΓ·͢ (ͨͩ͠ɺσΟϨΫτϦআ͘)ɻ ΤϯτϦͷϑΝΠϧϑΥʔϚοτϦετ 5.3 ͷ௨ΓͰ͢ɻ Ϧετ 5.3: ΤϯτϦͷϑΝΠϧϑΥʔϚοτ ------------------------------------------------------------------------------- | Entry | 4byte - ctime second | |=================================================================== | | 4byte - ctime nano second | |=================================================================== | | 4byte - mtime second | |=================================================================== | | 4byte - mtime nano second | |=================================================================== | | 4byte - device ID | |=================================================================== | | 4byte - inode number | |=================================================================== | | 4byte - mode | 16bit - unused | | |================================================== | | | 4bit - object type | | | 0x8 = regular file | | | 0xA = symbolic link | | | 0xE = git link | | |================================================== | | | 3bit - unused | | |================================================== | | | 9bit - unix like file permission | |=================================================================== | | 4byte - file owner’s user ID | |=================================================================== | | 4byte - file owner’s group ID | |=================================================================== | | 4byte - file size | |=================================================================== | | 20byte - SHA-1 digest | |=================================================================== | | 2byte - flags | 1bit - assume-valid flag | | | see bellow a command more about this flag | | | $ git update-index --assume-unchanged | | |================================================== 59 -
ୈ 5 ষ Git ͷ index tree ͱ݁ہͳΜͳͷ͔ 5.5 index
tree ͷಾͱͦͷ͑ | | | 1bit - must be zero | | |================================================== | | | 2bit - conflict flag | | | 00 = no conflict | | | 01 = lowest common ancestor commit’s file | | | 10 = current commit’s file | | | 11 = another commit’s file | | |================================================== | | | 12bit - file name length | |=================================================================== | | $(file name length)byte - relative path from top level directory | |=================================================================== | | Fill in null bytes until file offset of next multiple of 8 ------------------------------------------------------------------------------- ઌ಄ 16byte ΛͬͯɺରͷϑΝΠϧͷ ctime(ϝλσʔλͷ࠷ऴߋ৽࣌ࠁ) ͱ mtime(σʔλͷ ࠷ऴߋ৽࣌ࠁ) Λอ͍࣋ͯ͠·͢ɻ ͦͷޙ 4byte ۠ΓͰɺσόΠε IDɺinode ൪߸ɺϞʔυ (ޙड़)ɺϢʔβʔ IDɺάϧʔϓ IDɺ ϑΝΠϧαΠζͱଓ͖·͢ɻ Ϟʔυɺ4byte ΛࡉΕʹͯ͠ɺ֤ϏοτʹׂΛ༩͍͑ͯ·͢ɻৄ͘͠Ϧετ 5.3 ͷ௨ΓͰ ͕͢ɺobject type ͕ 0xE ͷ߹ɺgit link ͱ͍͏͋·Γฉ͖ೃછΈͷͳ͍୯ޠ͕ొ͍ͯ͠· ͢ɻຊষͰৄ͘͠ղઆ͠·ͤΜ͕ɺ͜Ε submodule Λͬͨࡍʹࢦఆ͞ΕΔ object type Ͱ ͢ɻ༻සߴ͋͘Γ·ͤΜ͕ɺ಄ͷย۱ʹஔ͍͓ͯ͘ͱྑ͍͔͠Ε·ͤΜɻ ࣍ͷ 20byte ʹΑ͏͘ SHA-1 ͕ొ͠·͢ɻ͜ͷ SHA-1 ීஈ 16 ਐͰදه͞ΕΔϋογϡ Λ 160bit Ͱදݱͨ͠ͷͳͷͰҙ͍ͯͩ͘͠͞ɻ ͦͷ࣍ͷ 2byte Ϟʔυͱಉ༷ʹ֤ϏοτͰׂ͕͔Ε͍ͯ·͢ɻassume-valid flag ɺgi t update-index --assume-unchanged ͱ͍͏ίϚϯυΛ༻ͨ͠ͱ͖ʹηοτ͞Ε·͢ɻ͜ͷϑ ϥά͕ηοτ͞Ε͍ͯΔͱɺ͜ͷϑΝΠϧͷ༰Λमਖ਼ͯ͠ git status ͰϑΝΠϧʹมߋ͕͋Δ ͔νΣοΫ͠ͳ͘ͳΓ·͢ɻҰݟ͍ಓ͕ͳͦ͞͏ʹݟ͑·͕͢ɺେྔʹϑΝΠϧ͕͋Δ͕ɺcommit ʹؚΊΔ༧ఆ͕ͳ͍σΟϨΫτϦͳͲΛࢦఆ͓ͯ͘͠ͱɺgit status ͕ߴʹ࣮ߦͰ͖ΔΑ͏ʹͳ ΔͳͲͷϝϦοτ͕͋Γ·͢ɻconflict flag ͦͷ໊ͷ௨ΓɺίϯϑϦΫτதʹ༻͞ΕΔϑϥ άͰɺmerge ઌͷ commit ͷϑΝΠϧͳͷ͔ɺmerge ݩͷ commit ͷϑΝΠϧͳͷ͔ࣝผ͢ΔͨΊʹ ͍·͢ɻ࠷ޙͷ 12bit ϑΝΠϧ໊ͷ͞Ͱɺ࣍ʹԿόΠτಡΈࠐΊྑ͍͔͕͔Γ·͢ɻ ࠷ޙʹϑΝΠϧ໊ΛಡΈࠐΉͱɺ͜ͷΤϯτϦʹؚ·ΕΔใΛͯ͢͞Β͏͜ͱ͕Ͱ͖·͢ɻ ͦͯ࣍͠ͷΤϯτϦɺͦͷ࣍ͷ 8 ͷഒͷΞυϨε͔Β࢝·Γ·͢ɻ 5.5 index tree ͷಾͱͦͷ͑ index tree ͷߏΛৄ͘͠ݟ͍ͯͬͨ͜ͱͰɺ಄Ͱհͨ͠ɺindex tree ʹ·ͭΘΔ͞·͟·ͳ ಾʹ͑Δ͜ͱ͕Ͱ͖ΔΑ͏ʹͳΓ·ͨ͠ɻͦΕͰ 1 ͭͣͭݟ͍͖ͯ·͠ΐ͏ɻ 60 -
ୈ 5 ষ Git ͷ index tree ͱ݁ہͳΜͳͷ͔ 5.5 index
tree ͷಾͱͦͷ͑ Q. ͳۭͥσΟϨΫτϦ add Ͱ͖ͳ͍ͷ͔ Git ۭσΟϨΫτϦΛ࡞ͬͯ add Ͱ͖ͣ commit ʹؚΊΒΕͳ͍ͷͰɺ.gitkeep ͱ͍͏ۭ ϑΝΠϧΛ͓ͬͯΛ͢Α͏ͳ͜ͱ͕Α͋͘Γ·͕͢ɺͲ͏ۭͯͦͦ͠σΟϨΫτϦ add Ͱ͖ͳ͍ͷͰ͠ΐ͏͔ɻ ͜͜·ͰಡΜͩ͋ͳͨͳΒɺ͜ͷ࣭ʹ͑Δ͜ͱ͕Ͱ͖ΔΑ͏ʹͳ͍ͬͯΔ͔ͱࢥ͍·͢ɻGit ͕ۭσΟϨΫτϦΛ add Ͱ͖ͳ͍ͷɺindex tree ͕ (ڱٛͷ) ϑΝΠϧ͔͠ѻ͍ͬͯͳ͍͔ΒͰ͢ɻ tree ΦϒδΣΫτɺindex tree Λݩʹ commit ࣌ʹॳΊͯੜ͞ΕΔͷͰɺindex tree ͔Β࡞Δͷ ʹ index tree ʹϑΝΠϧ͔͠ొͰ͖ͳ͍Ҏ্ɺۭͷ tree ΦϒδΣΫτ͕ੜ͞ΕΔ͜ͱ͋Γ· ͤΜɻ Q. git status ͕ߴʹಈ࡞͢Δϫέ git status มߋ͕͋ͬͨϑΝΠϧΛྻڍͯ͘͠Ε·͢ɻ͜ͷڍಈΛφΠʔϒʹ࣮͠Α͏ͱ͢ ΔͱɺϦϙδτϦͷશϑΝΠϧΛࠪͯ͠ 1 ͭͣͭ SHA-1 Λൺֱ͍͖ͯ͠ɺSHA-1 ͕ҟͳͬͨ ϑΝΠϧΛྻڍ͠Α͏ͱ͢Δ͔ͱࢥ͍·͢ɻ͔͠͠ɺ͜ͷॲཧશϑΝΠϧͷ SHA-1 Λ࠶ܭࢉ͢Δ ͨΊɺ͔ͳΓॏͦ͏ʹݟ͑·͕͢ɺ࣮ࡍʹڊେͳϦϙδτϦͰ git status ඇৗʹߴʹ࣮ߦ Ͱ͖·͢ɻ ͜Ε index tree ͷߏΛಡΈղ͍ͨࠓͰઆ໌Ͱ͖·͢ͶɻSHA-1 Λܭࢉ͢Δલʹɺctimeɺ mtime Λൺֱͯ͠ߋ৽͕͔͋ͬͨͲ͏͔Λ֬ೝ͍ͯ͠Δ͔ΒͰ͢ɻצͷӶ͍ಡऀؾ͍͔ͨ͠ Ε·ͤΜ͕ɺҰݟ͢Δͱ Git Ͱཧ͢Δඞཁ͕ͳͦ͞͏ͳใ͕ɺindex tree ʹؚ·Ε͍ͯ·ͨ͠ ͶɻctimeɺmtimeɺσόΠε IDɺinode ൪߸ͱ͍ͬͨϑΝΠϧͷϝλใͰ͢ɻ࣮͜ΕΒͷใ git status Ͱ༻͢ΔͷͰͨ͠ɻ git status Λ࣮ߦ͢Δͱ͍͖ͳΓ SHA-1 Λൺֱ͢Δલʹɺctimeɺmtime Λൺֱͯ͠ɺߋ৽͋ͬ ͨ߹ʹ͚ͩ SHA-1 Λܭࢉ͍ͯ͠·͢ɻ௨ৗɺ1commit ͰେྔͷϑΝΠϧΛߋ৽͢Δ͜ͱͳ͔ͳ ͔ͳ͍ͨΊɺGit Λ͍ͬͯͯ git status ͕͍ͱײ͡Δ͜ͱ͕গͳ͍ͷ͏ͳ͚ͣ·͢Ͷɻ ·ͨɺctime ͱ mtime ͕ߋ৽͞Ε͍ͯͨ߹ɺindex tree ͷ ctime ͱ mtime Λߋ৽ͯ͠ϝλ ใ͚ͩมԽͨ͠ϑΝΠϧ͕େྔʹͨ·Βͳ͍Α͏ʹͳ͍ͬͯ·͢ɻ ͞Βʹɺ͔͜͜Βݴ͑Δ͜ͱͱͯ͠ɺgit status ͕ index tree ͷ ctime ͱ mtime Λมߋ͢ Δ͜ͱ͕͋ΔͷͰɺ ʮgit status ഁյతมߋΛՃ͑ΔίϚϯυͰ͋Δʯͱ͍͏ڻዼͷࣄ࣮͕͋Γ ·͢ɻ Q. ίϯϑϦΫτதʹͲ͏ͬͯ merge ݩͱ merge ઌΛ۠ผ͍ͯ͠Δͷ͔ Git ͰίϯϑϦΫτ͢ΔͱɺίϯϑϦΫτՕॴʹ merge ݩͱ merge ઌͦΕͧΕͷमਖ਼༰͕ࠩ͠ ࠐ·Ε·͢ɻ͜ͷͱ͖ɺίϯϑϦΫτղফͷҰͱͯ࣍͠ͷΑ͏ͳίϚϯυΛ͏͜ͱ͕͋Δ͔ͱࢥ ͍·͢ɻ 61 -
ୈ 5 ষ Git ͷ index tree ͱ݁ہͳΜͳͷ͔ 5.6 ࠷ޙʹ
$ git checkout --thirs $ git checkout --ours ͜ΕΒͷίϚϯυɺͦΕͧΕڧ੍తʹ merge ઌ·ͨ merge ݩͷঢ়ଶʹϑΝΠϧΛ্ॻ͖͢Δ ίϚϯυͰ͢ɻ͔͠͠ɺίϯϑϦΫτதવ merge commit Λ࡞͢ΔલͰ͢ͷͰɺmerge ݩ merge ઌͱ͍ͬͨใͲ͜Ͱ͍࣋ͬͯΔͷͰ͠ΐ͏͔ɻ ͜ͷ͑ index tree ͷߏʹ͋Γ·ͨ͠ɻindex tree ίϯϑϦΫτதɺmerge ઌͱ merge ݩͷϑΝΠϧΛ conflict flag Ͱ۠ผͯ͠ɺ྆ϑΝΠϧͱอ͍࣋ͯ͠Δ͔ΒͰͨ͠ɻcommit Λ ݟʹߦ͘ͷͰͳ͘ɺindex tree ͔Βରͷ blob ʹඈΔͱ͍͏ͷɺ͔ͳΓҙ֎Ͱ͢Ͷɻ 5.6 ࠷ޙʹ index tree Ԟ͕ਂ͍Ͱ͢Ͷɻࠓճॻ͖͖Ε·ͤΜͰ͕ͨ͠ɺindex tree ͷόʔδϣϯ 3 4 Ͱ ɺ͞Βʹ͓͠Ζ͍ػೳ͕Ճ͞Ε͍ͯ·͢ɻ චऀ Git ͕େ͖Ͱɺࣾ LT ձͰසൟʹ Git ͷΛ͍ͯ͠ΔͷͰɺࣾͰ͋Δఔ Git ͖ͱͯ͠ೝ͞Ε͍ͯΔͷͰ͕͢ɺ࣮ೖࣾલπʔϧͱͯ͠ͷ Git ͷ͍ํҎ্ͷ͜ͱશવΓ ·ͤΜͰͨ͠ɻ࣌ͷ৽ଔݚमͰɺGit ͷ෦ߏʹ͍ͭͯৄ͘͠ฉ͍ͯڵຯ͕༙͍ͨͷ͕࢝·ΓͰ ͨ͠ɻ Git ʹ index tree ͚ͩͰͳ͘ɺ·ͩ·͓ͩ͠Ζ͍͕ͨ͘͞Μ͍ͬͯΔͷͰɺগ͠Ͱଟ͘ ͷਓʹ Git ʹڵຯΛ࣋ͬͯΒ͑ͨΒͳͱࢥ͍ͬͯ·͢ɻ ·ͨɺ࣮ࡍͷݚमͰ͜ΕҎ্ʹೱͯ͘ৄࡉͳ Git ͷΛ͍ͯ͠ΔͷͰɺ͜͠ΕΛಡΜͰ Git ʹ ڵຯΛ࣋ͬͯ͘Εֶͨੜ͕͍ͨΒɺͥͻฐࣾͷ࠾༻Λड͚ͯΈ͍ͯͩ͘͞ʂ ͓͍ͪͯ͠·͢ɻ 62 -
ஶऀհ দݪ ৴ (ୈ 1 ষ୲, GitHub: matsubara0507) ॴଐϞϯεταʔόνʔϜͰ Ruby
Λॻ͍ͯΔɻϓϩάϥϛϯά͕͖Ͱɺීஈਪ͠ݴ ޠͷ Haskell Ͱ༡ΜͩΓɺ৽͍͠ϓϩάϥϛϯάݴޠΛษڧͨ͠Γ͍ͯ͠ΔɻHaskell-jp Elm-jp Ͱͪΐͪ͜ΐ͜׆ಈ͍ͯ͠Δɻ ຢ ٛর (ୈ 2 ষ୲, GitHub: m4cteru, Twitter: @4cteru) ϥΠϒಈըϓϩμΫτ։ൃࣨͰ TIPSTAR ͷ։ൃΛߦͳ͍ͬͯ·͢ɻΞτυΞ (ข͖Ր) ͕ ͖ͰΑ͘ΩϟϯϓՏݪʹग़͔͚ͯ༡ΜͰ͍·͢ɻCHUMS ͱ͍͏ΞτυΞϒϥϯυ ͕͖ɻ ా ګฏ (ୈ 3 ষ୲, GitHub,Medium,Qiita: flatfisher, Twitter: @canoefishing) TIPSTAR ͷαʔόʔαΠυΤϯδχΞɺGCP ͱ Go ݴޠΛͬͨ API ։ൃϓϩδΣΫτ ϦʔυΛ͍ͯ͠·͢ɻΓ͕झຯͳͷͰʮΓʷςΫϊϩδʔʯͱ͍ͬͨςʔϚʹऔΓΉͷ ͕͖Ͱ͢ɻ ాಹล ً (ୈ 4 ষ୲) ։ൃຊ෦ CTO ࣨॴଐɻΤϯδχΞͱͯ͠εϚϗήʔϜ։ൃͱӡӦɺͦΕ͔ΒऩӹੑήʔϜ ੑͱ UX ໘ͷΫϦΤΠςΟϒʹܞΘ͖ͬͯͨɻࠓϓϩάϥϛϯάڭҭʹ͍ͭͯͷֶशιϑ τͱΧϦΩϡϥϜΛࣗࣾͰ୯ಠͰ࡞Γɺதɾߴߍੜʹ͚ͯϓϩάϥϛϯάߨ࠲ͷߨࢣ͠ ͍ͯΔɻ ౻ా ग (ୈ 5 ষ୲, Github: shumoon84, Twitter: @shumon_84) ίτμϚϯࣄۀ෦Ͱ Unity ͱ Java Λॻ͍͍ͯΔ৽ଔ 2 ΤϯδχΞͰ͢ɻ͖ͳͷ ϘʔυήʔϜͱ Git ͱ Go Ͱ͢ɻ ԕ౻ ѯ ʢσβΠφʔ, noteɿ en_akaʣ σβΠφʔͱͯ͠ΞϓϦͷ UI ʗ UX σβΠϯΛϝΠϯʹ୲ɺ৽نࣄۀͷ্ཱͪ͛ʹઓ ͨ͠Γ͠·ͨ͠ɻࠓɺσβΠϯઓུࣨͰ৫ࢧԉ৽ଔ࠾༻ͷΠϕϯτاըͳͲΛߦͳͬͯ ͍·͢ɻ ਿా ֆඒ (੍࡞ਐߦ, GitHub: esugita, Twitter: @semisemi7) ݩʑΤϯδχΞͰɺαʔόαΠυ͔ΒεϚϗΞϓϦ·Ͱ෯͘ܦݧ͍ͯ͠·͕͢ɺࠓɺ DevRel ͱͯ͠ɺࣾ֎ͷٕज़ऀίϛϡχςΟͷࢧԉΛߦͬͨΓɺٕज़ใΛ͍ͯ͠·͢ɻ 63 -
mixi tech note #04 2020 9 ݄ 12 ɹॳ൛ୈ
1 ɹൃߦ ஶɹऀ גࣜձࣾϛΫγΟ ༗ࢤ ൃߦॴ גࣜձࣾϛΫγΟ ҹॴ ޫاը ɹ mixi, Inc.