Share
MIXI TECH NOTE #12
#技術書典 17 に出典された、MIXI GROUP エンジニア有志による技術書です。
<< 目次 >>
1章:TIPSTAR における DWH 活用と開発チームとの連携
2章:Appium によるモバイルアプリ動作検証の自動化入門
3章:Cloud Spanner のしくみを理解する
4章:入門 Bzlmod
5章:バナー生成ツールでデザイナーを幸せにしたかった話
6章:運用入稿データを AI でチェックする仕組みを作った
7章:Obsidian プラグイン開発入門。簡易なベクトル検索を導入してみる
<< MIXI 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
MIXI TECH NOTE #09
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-09
MIXI TECH NOTE #10
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-10
MIXI TECH NOTE #11
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-11
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
110 slides
-
·͕͖͑ ຊॻʮMIXI TECH NOTE #12ʯɺMIXI GROUP ʹॴଐ͢Δ༗ࢤୡʹΑͬͯࣥචɾ੍࡞͞Εͨ ٕज़ॻͰ͢ɻMIXI Ͱ༷ʑͳٕज़Λ֤ͬͯαʔϏεΛ։ൃ͍ͯ͠·͢ɻຊॻͰɺMIXI ͷͦΕ
ͧΕͷ։ൃݱͰ׆ಈ͢ΔΤϯδχΞୡ͕ۀͰಘͨݟϊϋɺ·ͨɺݸਓతʹௐͨ͜ͱ ͳͲΛࢥ͍ࢥ͍ʹհ͍ͯ͠·͢ɻ֤ষͦΕͧΕͰٕज़ςʔϚ͕ҟͳΓɺ̍ষͰ͍݁ͯ͠Δ༰ʹ ͳ͍ͬͯ·͢ͷͰɺ͖ͳষ͔Β͖ͳॱ൪Ͱָ͓͠Έ͍ͩ͘͞ɻ ·ͨɺຊॻɺMIXI GROUP ʹ͋Δٕज़తݟΞΠσΞΛੵۃతʹڞ༗ɾެ։͍ͯ͘͜͠ͱ ͰɺੈͷதʹΑΓྑ͍αʔϏε͕ҲΕग़͢͜ͱΛئͬͯץߦ͞Ε͍ͯ·͢ɻܝࡌ͞Ε͍ͯΔใɺ ࣥචऀࣗͷڥͰݕূࣥ͠ච͞ΕͨͷͰ͢ͷͰɺ͝ࢀߟʹ͞ΕΔࡍɺࣗ͝ͷͰஅ͠ ͝׆༻͍ͩ͘͞ɻͳ͓ɺจষදݱʹ͖ͭ·ͯ͠ɺࣥචऀࣗͷݴ༿Ͱ͑ͨ͘ɺϑϥϯΫͳදݱͱ ͳ͓ͬͯΓ·͢͜ͱ͝ཧղ͍͚ͨͩΕͱࢥ͍·͢ɻ MIXI DEVELOPERS ༗ࢤҰಉ ˗ຊॻʹؔ͢Δ͓͍߹Θͤઌ ɹ https://x.com/mixi_engineers ˗ MIXI GROUP ʹ͍ͭͯ ɹ https://mixi.co.jp/ ˞ MIXI ͷ໊শɺ͜Εʹؔ࿈͢Δඪٴͼϩΰɺגࣜձࣾ MIXI ͷඪٴͼొඪͰ͢ɻ·ͨɺ ֤ࣾͷձ໊ࣾɺαʔϏεٴͼͷ໊শɺͦΕͧΕͷॴ༗͢Δඪ·ͨొඪͰ͢ɻ iii -
࣍ ·͕͖͑ iii ୈ 1 ষ TIPSTAR ʹ͓͚Δ DWH ׆༻ͱ։ൃνʔϜͱͷ࿈ܞ
1 1.1 DWH Λ࡞ͬͨཧ༝ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 DWH ͷߏͷৄࡉ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.3 DHW ΛͲ͏׆༻͍ͯ͠Δ͔ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4 DWH ΛͲ͏ҡ࣋͢Δ͔ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.5 ͦͯ͠ࠓ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 11 2.1 Appium ͷΞʔΩςΫνϟ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.2 Android ΞϓϦͷͨΊͷڥߏங . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.4 iOS ΞϓϦͷͨΊͷڥߏங . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 2.6 ͍͞͝ʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 ୈ 3 ষ Cloud Spanner ͷ͘͠ΈΛཧղ͢Δ 51 3.1 ·͕͖͑ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 3.2 ຊষʹ͍ͭͯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 3.3 Cloud SpannerɾNewSQL ͱ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 3.4 ਫฏεέʔϥϏϦςΟΛఏڙ͢ΔΞʔΩςΫνϟ . . . . . . . . . . . . . . . . . . . 52 3.5 ֎෦߹ੑΛࢧ͑Δ͘͠Έ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.6 ऴΘΓʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 3.7 ࢀߟ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 ୈ 4 ষ ೖ Bzlmod 59 4.1 αϯϓϧϓϩάϥϜ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.2 Bazel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.3 Bzlmod ΛͬͨΞϓϦέʔγϣϯ։ൃ . . . . . . . . . . . . . . . . . . . . . . . . 61 v -
࣍ 4.4 ͓͠·͍ . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 ୈ 5 ষ όφʔੜπʔϧͰσβΠφʔΛͤʹ͔ͨͬͨ͠ 69 5.1 ·͕͖͑ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.2 എܠ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.3 πʔϧͷ֓ཁ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.4 πʔϧͷٕज़બఆ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 5.5 πʔϧͷ։ൃ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 5.6 ఏڙͨ݁͠Ռ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 5.7 ࠓޙͷల . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 5.8 ͓·͚ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 5.9 ༻ͨ͠ AI ػೳʹ͍ͭͯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 5.10 ࣮ࡍʹ AI ੜͯ͠Έͨ݁Ռ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 5.11 ࠷ޙʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢ΔΈΛ࡞ͬͨ 79 6.1 ·͕͖͑ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 6.2 ՝ʹ͍ͭͯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 6.3 ։ൃʹ͍ͭͯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 6.4 ӡ༻࣌ʹݟ͔ͭͬͨ՝ͱରԠ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 6.5 ͋ͱ͕͖ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ 89 7.1 Obsidian ͱԿ͔ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 7.2 ࢲ͕ Obsidian ͕͖ͳཧ༝ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 7.3 ϓϥάΠϯ։ൃೖ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 7.4 Obsidian ʹϕΫτϧݕࡧΛಋೖͯ͠ΈΔ . . . . . . . . . . . . . . . . . . . . . . . 93 7.5 ऴΘΓʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 ஶऀհ 101 vi -
ୈ 1 ষ TIPSTAR ʹ͓͚Δ DWH ׆༻ͱ։ൃ νʔϜͱͷ࿈ܞ ͜ΜʹͪɺιʔγϟϧϕοςΟϯάࣄۀຊ෦ ։ൃࣨ
৽ن։ൃάϧʔϓͷߐാͰ͢ɻ ຊষͰɺ2023 ͝Ζʹ TIPSTAR *1 ʹج൫ͱͯ͠࡞ͨ͠ DHW (σʔλΣΞϋ ε) ͱɺDWH ͱϓϩμΫτͷܭଌʹ·ͭΘΔ։ൃνʔϜͱͷ࿈ܞϓϩηεʹ͍ͭͯհ͠·͢ɻ ͳ͓ຊষϓϩμΫτͰσʔλੳΛߦͬͯΑΓྑ͍αʔϏεͮ͘ΓΛ͍ͨ͠σʔλΞφϦετɾ ϓϥϯφʔɾ։ൃऀ͚ͯॻ͔Ε͍ͯ·͢ɻ 1.1 DWH Λ࡞ͬͨཧ༝ ·ͣલஈͱͯ͠ɺͳͥ TIPSTAR ʹ DWH Λ࡞Δ͜ͱʹͳͬͨͷ͔Λઆ໌͠·͢ɻ DWH ಋೖҎલͷσʔλநग़ DWH ಋೖલͷ TIPSTAR ͰɺܦཧػೳɾࢪࡦͷޮՌܭଌͷͨΊʹɺσʔλϕʔεͷόοΫ ΞοϓͳͲͷ͞·͟·ͳσʔλιʔε *2 *3 ͔ΒɺσʔλΞφϦετ։ൃνʔϜ͕ SQL ΫΤ ϦΛൃߦ͢Δ͜ͱͰσʔλநग़Λߦ͍ͬͯ·ͨ͠ɻ·ͨɺϏδωε৬Λ͡Ίͱ͢Δσʔλར༻ऀ ɺͦΕΒͷநग़σʔλΛ Google Spreadsheet *4 Looker *5 Λ׆༻ͯ͠ɺࢹ֮Խɾੳʹར༻ ͍ͯ͠·ͨ͠ɻ ͜ͷϑϩʔɺ४උͳ͠ͰͨͩͪʹσʔλΛநग़Ͱ͖ɺಛʹ KPI ͳͲͷఆ͕͍ٛ͋·͍ͳαʔ Ϗεॳظஈ֊Ͱ YAGNI *6 ͷ؍͔Β༗ޮͰͨ͠ɻ *1 ڝྠɾPIST6ɾΦʔτϨʔεͷωοτථ͕Ͱ͖Δڞ༡ܕεϙʔπϕοςΟϯάαʔϏε https://about.tipstar.com/ *2 https://cloud.google.com/bigquery *3 https://developers.google.com/analytics *4 https://developers.google.com/sheets *5 https://cloud.google.com/looker *6 You ain’t gonna need it. ͏͔Θ͔Βͳ͍ػೳՃ͠ͳ͍ํ͕ྑ͍ͱ͍͏ΤϯδχΞϦϯάͳͲͷͰ༻͍Β ΕΔݪଇͷҰͭ 1 -
ୈ 1 ষ TIPSTAR ʹ͓͚Δ DWH ׆༻ͱ։ൃνʔϜͱͷ࿈ܞ 1.1 DWH Λ࡞ͬͨཧ༝
՝ͱλεΫϑΥʔεൃ ͔͠͠ϓϩμΫτΛӡ༻͍ͯ͘͠தʹɺܦཧ্ͷਖ਼֬ͳΛࢉग़͢Δඞཁ͕͋Δ߹͕͋Γ· ͢ɻ·ͨɺαʔϏεશମͷػೳར༻τϨϯυͳͲͷதظతͳܭଌٻΊΒΕ·͢ɻ͞Βʹɺظɾ ͳͲͷఆظతʹඞཁͱͳΔෳࡶͳσʔλநग़ʹؔͯ͠ಉ༷Ͱ͢ɻ ͜ΕΒͷέʔεͰσʔλநग़ΫΤϦΛ৽ن࡞͢Δମ੍ͰɺͷͣΕ͕ੜ͡ΔՄೳੑ͕͋ Γ·͢ɻ͞ΒʹɺαʔϏε͕ظӡ༻͞Ε͍ͯ͘ʹͭΕΫΤϦ࡞ͷྦྷܭίετ͕ߴಅ͢Δ͜ͱ໌ Β͔ʹͳΓ·ͨ͠ɻ ਤ 1.1: DWH උҎલʹى͍ͬͯͨ͜σʔλநग़࣌ͷࠔΓ͝ͱ ͦ͜Ͱσʔλج൫උͱͯ͠ɺDWH Λ࡞͢ΔλεΫϑΥʔε͕݁͞Εɺ2023 Λத৺ʹ ҎԼͷΑ͏ͳ DWH ʹؔ͢Δ։ൃɾӡ༻Λ࣮ࢪ͠·ͨ͠ɻ 1. KPI Λ͡Ίͱ͢Δ֤छͷఆٛͷݟ͠ 2. σʔλநग़ͷج൫ͱͳΔ DWH ͷઃܭ͓Αͼߏங 3. ϏδωεଆΛத৺ͱͨ͠ DWH ͷՁͱར༻ํ๏ʹ͍ͭͯͷΨΠμϯεͷ࣮ࢪ 2 -
ୈ 1 ষ TIPSTAR ʹ͓͚Δ DWH ׆༻ͱ։ൃνʔϜͱͷ࿈ܞ 1.1 DWH Λ࡞ͬͨཧ༝
KPI Λ͡Ίͱ͢Δ֤छͷఆٛͷݟ͠ KPI πϦʔΛཧ͠ɺചΓ্͛ΞΫςΟϒϢʔβͳͲͷͷఆٛΛݟ͢͜ͱͰɺDWH ͱ KPI ͷରԠΛ࡞͠·ͨ͠ɻ ྫ͑ɺചΓ্͛ʹฦֹۚΛؚΉ͖͔ɺΞΫςΟϒϢʔβͱͯ͠ϩάΠϯϘʔφεͷड͚औΓ Λϕʔεͱͯ͠ܭ্͢Δ͔ͳͲͷৄࡉͳఆٛΛܾΊ·ͨ͠ɻ͜ΕʹΑΓɺσʔλநग़͝ͱʹಉ໊͡લ ͷ͕ҟͳΔࣄΛ͙͜ͱ͕ՄೳͱͳΓ·ͨ͠ɻ ֤छσʔλநग़ʹ͓͚ΔϕʔεͱͳΔ DWH ͷ࡞ DWH ଆͰ KPI Λతʹఆٛ͢ΔͷͰͳ͘ɺσΟϝϯγϣφϧϞσϦϯάΛϕʔεʹɺυ ϝΠϯతʹಠཱͨ͠ϑΝΫτɾσΟϝϯγϣϯςʔϒϧΛఆٛ͠·ͨ͠ɻ͜ΕʹΑΓɺσʔλΞφϦ ετ͕͞·͟·ͳཁ݅ͷσʔλநग़ʹ׆͔͢͜ͱ͕Ͱ͖ΔΑ͏ʹ͠·ͨ͠ɻ σΟϝϯγϣφϧϞσϦϯάͱɺं݊ͷߪೖֹۚͳͲͷΛϑΝΫτςʔϒϧɺं݊ʹඥͮ͘ ϨʔεใߪೖϢʔβʔͷใΛσΟϝϯγϣϯςʔϒϧͱͯ͠ఆٛ͢Δ͜ͱͰɺϑΝΫτͰ͋Δ Λ͞·͟·ͳࢹʢσΟϝϯγϣϯʣͰੳͰ͖ΔσʔλϞσϦϯάख๏Ͱ͢ɻ ͳ͓ɺTIPSTAR ʹ͓͚ΔചΓ্͛ KPI ं݊ߪೖֹۚΛϕʔεʹܭࢉ͞Ε·͕͢ɺചΓ্͛ͷ ઐ༻ςʔϒϧͱͯ͠ fc_sales Λఆٛ͢ΔΘΓʹɺं݊ߪೖςʔϒϧͱͯ͠ fc_purchase_tick ets Λఆٛ͠·ͨ͠ɻKPI ͷܭࢉ͜ͷςʔϒϧΛͱʹͨ͠ΫΤϦͰߦ͍·͢ɻ͜ͷΞϓϩʔν ΛऔΔཧ༝ɺKPI ͷݟ͕͠සൟʹߦΘΕΔҰํͰɺं݊ߪೖͱ͍͏ߦಈࣗମҰఆͰ͋ΔͨΊ Ͱ͢ɻ·ͨɺKPI ͷ Looker ͷ LookML Ͱཧɾӡ༻͞Ε͍ͯ·͢ɻ σʔλΞφϦετ͜Ε·ͰΞϓϦέʔγϣϯίʔυͱ֤छϩάΛಡΈͳ͕Β LookML Ͱ KPI Λఆ͍ٛͯ͠·͕ͨ͠ɺDWH Λඋ͢Δ͜ͱͰɺ͜ΕΒͷςʔϒϧΛ༻͍ͯ؆୯ʹ KPI Λఆٛɾ ௐͰ͖ΔΑ͏ʹͳΓ·ͨ͠ɻ ਤ 1.2: DWH Ͱ fc_purchase_tickets Λఆٛ͠ɺ۩ମతͳ KPI ͳͲͷ LookML ͳͲͰՃ ͯ͠ࢹ֮Խ͢Δ 3 -
ୈ 1 ষ TIPSTAR ʹ͓͚Δ DWH ׆༻ͱ։ൃνʔϜͱͷ࿈ܞ 1.1 DWH Λ࡞ͬͨཧ༝
ϏδωεଆΛத৺ͱͨ͠ DWH ͷՁͱར༻ํ๏ʹ͍ͭͯͷΨΠμϯεͷ࣮ࢪ DWH Λ׆༻ͯ͠Β͏ͨΊɺ·ͨࠓޙͷ DWH ͷӡ༻ʹ͍ͭͯͷཧղΛಘΔͨΊʹɺϏδωε ଆΛத৺ʹ DWH ͱͲͷΑ͏ͳͷͰɺαʔϏεͷҙࢥܾఆʹͲͷΑ͏ͳྑ͍ӨڹΛ༩͑Δ͜ͱ ͕Ͱ͖Δͷ͔ʹ͍ͭͯΨΠμϯεΛߦ͍·ͨ͠ɻ ·ͨɺϏδωεଆ͔ΒՃͰ DWH ͱͯ͠උͯ͠΄͍͠ΛώΞϦϯά͢Δ͜ͱͰɺϓϩμ Ϋτͷ DWH ͷՁΛߴΊ·ͨ͠ɻ ͨͱ͑ɺڝྠͷ֤։࠵ใͷσΟϝϯγϣϯςʔϒϧʹ։࠵࣌ؒଳ *7 ͷใΛՃ͢ΔͳͲͷ վળΛߦ͏͜ͱ͕Ͱ͖·ͨ͠ɻ *7 ڝྠͷϨʔε͕Ͳͷ࣌ؒଳʹ։࠵͞ΕΔ͔ͷ۠ɻͨͱ͑ϛουφΠτͰ͋Εਂʹ։࠵͞ΕΔͷΛࢦ͢ɻ 4 -
ୈ 1 ষ TIPSTAR ʹ͓͚Δ DWH ׆༻ͱ։ൃνʔϜͱͷ࿈ܞ 1.2 DWH ͷߏͷৄࡉ
1.2 DWH ͷߏͷৄࡉ TIPSTAR ʹɺσʔλϕʔεͷόοΫΞοϓΞϓϦέʔγϣϯͷ࣮ߦϩάɺGoogle Analytics ͳͲͷ͞·͟·ͳσʔλιʔεΛ BigQuery ܦ༝ͰΫΤϦͰ͖ΔΑ͏ʹͳ͍ͬͯ·͢ɻͦΕͧΕͷ σʔλιʔεܭଌ্ॏཁͳׂΛ୲͍ͬͯ·͢ɻͨͱ͑ɺDAU Λܭଌ͢Δ߹ʹΞϓϦ έʔγϣϯͷ࣮ߦϩάɺं݊ͷߪೖྔΛௐΔʹσʔλϕʔεͷόοΫΞοϓɺಛఆͷը໘ͷϢʔ βʔΞΫηεͷܭଌʹ Google Analytics ͱɺॏཁͳ͕ࡏ͍ͯ͠·͢ɻ ·ͨσʔλϕʔεͷόοΫΞοϓ͋͘·ͰΞϓϦέʔγϣϯ༻ʹνϡʔϯφοϓ͞ΕͨεΩʔϚ ͷίϐʔͰ͋ΓɺͲͷςʔϒϧΧϥϜʹͲͷΑ͏ͳσʔλ͕ೖ͍ͬͯΔ͔ΞϓϦέʔγϣϯͷιʔ είʔυΛಡ·ͳ͚Εղಡ͢Δ͜ͱͰ͖·ͤΜɻ ͜͏͍ͬͨࣄ͔ΒɺσʔλΞφϦετ͕ͨͩͪʹॊೈ͔ͭਖ਼֬ͳσʔλநग़Λ͢Δ͜ͱࠔͰ ͋ΓɺDWH ͷඋΛߦ͏͜ͱҰఆͷཧ͕͋Γ·ͨ͠ɻͦ͜Ͱɺ͋Δఔਝ͔ͭॊೈʹ DWH Λ ߏஙͰ͖Δٕज़Λબఆ͠ɺTIPSTAR Ͱ dbt *8 Λར༻ͯ͠σΟϝϯγϣφϧϞσϦϯάΛߦ͏͜ ͱͱ͠·ͨ͠ɻ σΟϝϯγϣφϧϞσϦϯάΛߦ͏͜ͱͰɺͨͱ͑ं݊ͷߪೖσʔλϢʔβʔͷߦಈϩάͱ ͍ͬͨΛ͋Δఔͷਖ਼نԽͷͱͰఆٛͰ͖Δ΄͔ɺΞφϦετ͕σʔλఆٛΛΑΓײతʹཧ ղͰ͖ΔΑ͏ʹͳΓ·ͨ͠ɻ ͞Βʹ dbt docs *9 Λ׆༻֤ͯ͠ςʔϒϧɾΧϥϜʹ͍ͭͯͷઆ໌Λߦ͏͜ͱͰɺΞϓϦέʔγϣ ϯίʔυΛಡ·ͣͱσʔλநग़͕ߦ͑ΔڥΛඋ͠·ͨ͠ɻ ਤ 1.3: DWH උҎ߱ɺσʔλΞφϦετΞϓϦέʔγϣϯίʔυͦͷपล͕ࣝͳ͘ͱ σʔλநग़͕ߦ͑ΔΑ͏ʹͳͬͨ *8 https://www.getdbt.com/ *9 https://docs.getdbt.com/docs/build/documentation 5 -
ୈ 1 ষ TIPSTAR ʹ͓͚Δ DWH ׆༻ͱ։ൃνʔϜͱͷ࿈ܞ 1.2 DWH ͷߏͷৄࡉ
۩ମతͳΠϝʔδΛ͔ͭΉͨΊɺͨͱ͑ҏ౦Թઘڝྠ *10 Ͱ 2024 10 ݄ 1 ʹߪೖ͞Εͨ 3 ࿈୯ं݊ͷ߹ܭߪೖֹۚΛܭࢉ͢Δͱͨ͠ࡍͷΫΤϦʹ͍ͭͯٞ͠·͢ *11ɻ DWH ԽҎલͰɺͷใౌ͚ࣜ ID Ͱཧ͞Ε͍ͯΔ΄͔ɺ͕ UTC Ͱཧ͞Ε͍ͯ Δ͜ͱ͔ΒɺΫΤϦ࡞࣌ʹࣄલ͕ࣝඞཁͰ͋ΓɺΫΤϦͷߏஙϛε͕ൃੜ͍͢͠ڥͰͨ͠ɻ Ϧετ 1.1: DWH ԽҎલͷΫΤϦ SELECT SUM(money) FROM user_keirin_tickets WHERE DATE(purchased_at, "Asia/Tokyo") = DATE("2024-10-01") AND ticket_state.return_type != "payback" AND velodrome_id = 37 AND bet_type = 1 ͜ͷΫΤϦɺDWH Խ͞Εͨ͜ͱͰҎԼͷΑ͏ʹॻ͖Լ͢͜ͱ͕Ͱ͖ΔΑ͏ʹͳΓ·ͨ͠ɻ Ϧετ 1.2: DWH ԽҎ߱ͷΫΤϦ SELECT SUM(fc_purchase_tickets.bet_money) FROM fc_purchase_tickets JOIN dm_races ON fc_purchase_tickets.race_id = dm_races.race_id JOIN dm_bet_types ON fc_purchase_tickets.bet_type = dm_bet_types.bet_type WHERE fc_purchase_tickets.purchase_date = DATE("2024-10-01") AND fc_purchase_tickets.race_type = "ڝྠ" AND fc_purchase_tickets.return_type != "ฦؐ" AND dm_races.velodrome_name = "ҏ౦" AND fc_purchase_tickets.bet_type_name = "ڝྠ:3࿈୯" *10 https://www.itokeirin.com/ *11 ࣮ࡍͷΫΤϦʹ͍ۙͷΛ༻ҙ͍ͯ͠·͕͢ɺ·ͬͨ͘ಉҰͷΫΤϦͰ͋Γ·ͤΜɻ 6 -
ୈ 1 ষ TIPSTAR ʹ͓͚Δ DWH ׆༻ͱ։ൃνʔϜͱͷ࿈ܞ 1.3 DHW ΛͲ͏׆༻͍ͯ͠Δ͔
1.3 DHW ΛͲ͏׆༻͍ͯ͠Δ͔ લઅͰհͨ͠Α͏ʹɺσʔλநग़ͷ࣭ͱͷ্Λࢦͯ͠σΟϝϯγϣφϧϞσϦϯά ͷݩɺDWH Λඋ͠·ͨ͠ɻ·ͨσʔλΞφϦετϏδωεαΠυͷڠྗΛ͍͖ͨͩͳ͕Βɺ DWH ͷ׆༻ମ੍ॱ࣍උ͞Ε͍ͯ·͢ɻ 2024 ࣌ͰɺDWH ҎԼͷ໘Ͱ׆༻͞Ε͍ͯ·͢ɻ • Looker ͱଓͯ͠ඇΤϯδχΞ͚ͷ GUI σʔλநग़ج൫ͱͯ͠ • σʔλΞφϦετͷΞυϗοΫͳෳࡶͳσʔλநग़ͷϕʔεͱͯ͠ • CSʢΧελϚʔαϙʔτʣͷෆఆظͳσʔλநग़ج൫ͱͯ͠ Looker ͱଓͯ͠ඇΤϯδχΞ͚ͷ GUI σʔλநग़ج൫ͱͯ͠ Looker ͰɺKPI πϦʔΛμογϡϘʔυͱͯ͠ࢹ֮Խ͠ɺఆظతʹ Slack νϟϯωϧʹߘ͢ Δ͜ͱͰɺ୭Ͱਖ਼͍͠ϓϩμΫτͷݱࡏͷঢ়گΛ֬ೝͰ͖ΔΑ͏ʹ͠·ͨ͠ɻ·ͨɺDWH ͷσʔ λʹิࣄ߲ΛՃ͑ͨ Looker Explore Λඋ͢Δ͜ͱͰɺKPI ͷৄࡉੳ͕༰қʹͰ͖ΔڥΛఏ ڙ͠·ͨ͠ɻ ͨͱ͑ɺTIPSTAR ͰಛఆͷڝྠͰͷं݊ߪೖʹରͯ͠ΩϟϯϖʔϯΛ࣮ࢪ͢Δ͜ͱ͕͋Γ ·͢ɻ͜ͷΩϟϯϖʔϯͷ݅ઃఆ࣮ࢪޙͷৼΓฦΓʹ DWH ͕׆༻͞Ε͍ͯ·͢ɻ Ϧετ 1.3: ं݊ߪೖͷ LookML (view) view: fc_purchase_tickets { view_label: "˒ं݊ߪೖใ" sql_table_name: ‘pjt.dwh.fc_purchase_tickets‘ ;; # ... measure: sales { group_label: "KPI" type: sum label: "Sales" description: "ं݊ߪೖ߹ܭֹۚ (ฦؐআ͔ΕΔ)" filters: [state: "-ฦؐ"] sql: "${bet_money}" ;; } } 7 -
ୈ 1 ষ TIPSTAR ʹ͓͚Δ DWH ׆༻ͱ։ൃνʔϜͱͷ࿈ܞ 1.3 DHW ΛͲ͏׆༻͍ͯ͠Δ͔
Ϧετ 1.4: ं݊ߪೖͷ LookML (explore) explore: fc_purchase_tickets { label: "˒ं݊ߪೖใ" join: dm_users { sql_on: ${fc_purchase_tickets.user_id}=${dm_users.user_id} ;; relationship: many_to_one type: inner } join: dm_races { sql_on: ${fc_purchase_tickets.race_id} = ${dm_races.race_id} ;; relationship: many_to_one type: left_outer } join: dm_bet_types { sql_on: ${fc_purchase_tickets.bet_type_id} = ${dm_bet_types.bet_type_id};; relationship: many_to_one type: left_outer } } σʔλΞφϦετͷΞυϗοΫͳෳࡶͳσʔλநग़ͷϕʔεͱͯ͠ σʔλੳͰɺੳΛਐΊΔ͏ͪʹෳࡶͳΫΤϦ͕ඞཁͱͳΔ͜ͱ͕͋Γ·͢ɻͨͱ͑ɺಛ ఆͷߦಈΛͨ͠Ϣʔβʔͷं݊ߪೖΛϢʔβʔͷαʔϏε։࢝͝ͱʹൺֱ͢Δͱ͍ͬͨཁ݅ Ͱ͢ɻ ͨͱ͑ɺ͋ΔಛఆͷߦಈΛͨ͠Ϣʔβʔͷं݊ߪೖΛɺϢʔβʔͷαʔϏε։࢝ͷηάϝ ϯτ͝ͱʹൺֱ͍ͨ͠ɺͱ͍ͬͨधཁɺͨͩͪʹ Looker Ͱදݱ͢Δ͜ͱ͕ࠔͰ͢ɻ DWH Λඋ͢Δલσʔλϕʔε͔ΒαϒΫΤϦΛநग़͢ΔͳͲͯ͠ɺඦߦͷΫΤϦΛߏ ͢Δඞཁ͕͋Γ·ͨ͠ɻ͔͠͠ɺDWH ͷඋޙɺ౷Ұ͞ΕͨϕʔεͷݩͰΫΤϦΛॻ͘͜ͱ͕ Ͱ͖ɺΫΤϦߦͷݮσʔλΞφϦετؒͷϨϏϡʔ࣌ؒͷॖ͕࣮ݱ͠·ͨ͠ɻ CS ͷෆఆظͳσʔλநग़ج൫ͱͯ͠ CS ۀܦཧۀʹಥൃతͳσʔλ֬ೝ͕ඞཁͱͳΔ͜ͱ͕͋Γ·͢ɻͨͱ͑ɺϢʔβʔͷ Ωϟϯϖʔϯͷઓঢ়گͷௐࠪɺ֎෦ͱͷܾࡁঢ়گͷ߹ੑͷνΣοΫͳͲͰ͢ɻ DWH Λར༻͢Δ͜ͱͰɺΫΤϦϛεΛݮΒ͠ɺΤϯδχΞͷΛݮͭͭ͠ɺநग़σʔλͷ ࣭Λ୲อͰ͖ΔΑ͏ʹͳΓ·ͨ͠ɻ 8 -
ୈ 1 ষ TIPSTAR ʹ͓͚Δ DWH ׆༻ͱ։ൃνʔϜͱͷ࿈ܞ 1.4 DWH ΛͲ͏ҡ࣋͢Δ͔
1.4 DWH ΛͲ͏ҡ࣋͢Δ͔ ͜͜·Ͱ DWH Λ࡞ͨ͠ཧ༝ͱͦͷϝϦοτΛհ͖ͯ͠·͕ͨ͠ɺDWH ࡞ͯ͠ऴΘΓͰ ͋Γ·ͤΜɻϓϩμΫτͷػೳ͕૿͑Δͨͼʹ DWH ࣗମͷߋ৽͕ඞཁͰ͋Γɺ։ൃνʔϜͷυ ϝΠϯ͕ࣝෆՄܽͰ͢ɻ ͔͠͠ɺDWH ͷଟ͘ͷϝϦοτϏδωεαΠυʹ͋Γɺ։ൃνʔϜʹతͳϝϦοτগ ͳ͍͔͠Ε·ͤΜɻ։ൃνʔϜ͕ DWH ͷඋͷࢹ֮ԽʹϝϦοτΛݟग़͘͢͠Έ࡞Γ͕ɺ DWH ͷҡ࣋ʹ͔ܽͤ·ͤΜɻ ։ൃνʔϜʹͱͬͯ DWH ͷϝϦοτ Ұؒ DWH Λ։ൃɾӡ༻͖ͯͨ͠தͰɺҎԼͷϝϦοτ͕ݟΒΕ·ͨ͠ɻ·ͨ͜ΕΒͷྫΛ ͑ΒΕΔΑ͏ͳϝϦοτΛ࡞Δ͜ͱ͕ݱࡏͷࣗͷ՝ͱͳ͍ͬͯ·͢ɻ ӡ༻ۀͷෛ୲ܰݮ CS ͷσʔλநग़ۀͳͲͷӡ༻ۀͷෛ୲ΛݮΒ͢͜ͱ͕Ͱ͖·͢ɻ ࣗͨͪͷࢪࡦͷݟ͑ΔԽ Ϣʔβʔͷ༻ঢ়گධՁΛࢹ֮Խ͠ɺࢪࡦͷޮՌΛ֬ೝͰ͖·͢ɻ AI ׆༻ AI Λར༻ͨ͠ػೳ։ൃੳɺ৽͍ٕ͠ज़ྖҬͷઓ͕ՄೳͰ͢ɻܗ͞ΕͨσʔλΛ ͏͜ͱͰɺAI ͷੑೳ্ʹد༩͠·͢ɻ 1.5 ͦͯ͠ࠓ 2024 10 ݄ݱࡏɺDWH ϓϩμΫτͷσʔλج൫ͱͯ͠ॱௐʹՔಇ͍ͯ͠·͢ɻ࠷ۙͰ͞Β ͳΔઓͱͯ͠ɺҎԼͷࢪࡦΛ࣮ࢪ͍ͯ͠·͢ɻ • dbt Looker ͷ։ൃΠϕϯτΛ։࠵͠ɺ։ൃνʔϜͷ DWH ҡ࣋ίετͷݮɺෑډͷԼ ʹઓ͍ͯ͠·͢ • CS ରԠͳͲͷఆৗӡ༻ۀʹ DWH ϕʔεͷੳج൫ΛΈࠐΜͰ͍͘͜ͱͰɺ։ൃνʔϜ ͷݮʹߩݙ͍ͯ͠·͢ • ։ൃνʔϜҎ֎ͷϝϯόʔͰ DWH ͷӡ༻͕Ͱ͖ΔΑ͏ʹɺϓϩμΫτίʔυʹؔ͢Δ ࣝΛਂΊɺΑΓྑ࣭ͳσʔλΛऔಘͰ͖ΔΑ͏ʹௐ͍ͯ͠·͢ ࠓޙɺGemini in BigQuery *12 ͳͲͷ৽ٕज़Λ׆༻͠ɺϓϩμΫτͷ؍ଌਫ਼Λ্ͤ͞ɺϢʔ βʔʹͱͬͯΑΓՁͷ͋ΔαʔϏεΛఏڙ͍͖͍ͯͨ͠ͱߟ͍͑ͯ·͢ɻ *12 https://cloud.google.com/gemini/docs/bigquery/overview 9 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕ ূͷࣗಈԽೖ ͜ΜʹͪɻΈͯͶϓϩμΫτ։ൃ෦ͷ sou ͱਃ͠·͢ɻ ʮՈΞϧόϜ
ΈͯͶʢҎԼɺΈͯͶʣ ʯ Ͱ iOSɺAndroid ΞϓϦͱαʔόͷ։ൃΛϝΠϯͷۀͱ͍ͯ͠·͢ɻ ΈͯͶଟݴޠʹରԠͨ͠ΞϓϦͱͳ͓ͬͯΓʑ৽ͨͳ༁จݴ͕Ճ͞Ε·͢ɻ༁จݴ ༁୲ґཔͨ͠ͷ͕ద༻͞ΕΔͨΊ࣭ͷߴ͍ͷͱͳ͍ͬͯ·͕͢ɺػೳՃʹࡍͯ͠ґ པΛߦ͏ܗͷͨΊՃ͞Εͨจݴ͕σβΠϯશମͷτϯϚφͱ߹͍ͬͯΔ͔ผͱͳΓ·͢ɻͦ͜Ͱ ը໘શମͰݟͨͱ͖ͷ࣭୲อ͢ΔͨΊ༁Λద༻ͨ͠ը໘શମͷνΣοΫ࡞ۀ༁ͱผʹߦͬ ͍ͯ·͢ʢ͜ΕΛࣾͰ LQA ͱݺΜͰ͍·͢ʣ ɻ LQA ࡞ۀ༁ΛߦͬͨશݴޠͰ֬ೝ͢Δඞཁ͕͋ΓɺΈͯͶͰࣥච࣌ͰຊޠΛআ͖ 7 ͭ ͷݴޠʹରԠ͍ͯ͠ΔͨΊը໘ҰͭͰ 7 ͭͷεΫϦʔϯγϣοτΛࡱΒͳͯ͘ͳΓ·ͤΜɻͦΕ ͨͩݴޠΛΓସ͑ΕΑ͍ͱ͍͏ͷͰͳ͘ɺ৽ͨʹՃͨ͠ػೳ͕ඞཁͱͳΔΑ͏ͳঢ়ଶΛ ࡞ΔͨΊʹҰ࣌తʹσʔλίʔυΛमਖ਼͢Δඞཁ͋Γ·͢ɻ ·ͨɺLQA Ҏ֎ʹίʔυϨϏϡʔ࣌ʹ͓͍ͯૣ͍ஈ֊Ͱෆ۩߹Λݟ͚ͭΔͨΊඞཁʹԠͯ͡Ϩ ϏϡΞʔಈ࡞ݕূΛߦ͓ͬͯΓɺಛʹαʔόαΠυͷมߋʹΑͬͯΞϓϦ্ͷৼΔ͍ݟͨʹ ӨڹΛ༩͑ΔΑ͏ͳमਖ਼ iOS ͱ Android ͷ྆ํͰ͕ى͖͍ͯͳ͍͔Λ֬ೝ͢ΔͨΊʹίετ ͷֻ͔Δ࡞ۀͱͳ͍ͬͯ·͢ʢ͕͋ͬͨ͠߹ʹίʔυͷमਖ਼ޙʹಈ࡞ݕূ͕ඞཁͰ͢ʣ ɻ ͜͏ͨ͠՝ʹର͠ͳΔ͘ίετΛֻ͚ͣʹղܾ͢Δखஈͱͯ͠චऀ Appium Ͱಈ࡞ݕূΛࣗ ಈԽ͢ΔπʔϧΛ࡞ͬͯΈ·ͨ͠ɻຊষͰͦ͜ͰಘͨܦݧΛݩʹϋϯζΦϯܗࣜͰ iOS/Android Ͱಈ࡞ݕূͷࣗಈԽΛ͢ΔͨΊͷखॱΛհ͠·͢ɻհ͢Δ༰ΈͯͶΞϓϦͷը໘ߏʹ߹Θ ͤͨͷͱͳ͍ͬͯ·͕͢ Appium ʹΑΔϞόΠϧΞϓϦͷࣗಈૢ࡞Λߦ͏͏͑ͰԿ͕ඞཁͰͲͷ Α͏ͳ͜ͱ͕Ͱ͖Δ͔ʹ͍ͭͯຊষͷ༰͔ΒټΈऔ͍͚ͬͯͨͩΔͷͰͳ͍͔ͱࢥ͍·͢ɻ 11 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ ຊষશମͷྲྀΕ࣍ͷͱ͓ΓͰ͢ɻ 1. Appium ͷհ 2.
Android ΞϓϦͷͨΊͷڥߏங 3. Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ 4. iOS ΞϓϦͷͨΊͷڥߏங 5. iOS ΞϓϦͰಈ࡞ݕূΛࣗಈԽ͢Δ ·ͨɺຊষͰߦ͏ಈ࡞ݕূ࣍ͷΑ͏ͳૢ࡞Λߦ͏ͷͱ͠·͢ɻ 1. ΞϓϦΛ্ཱͪ͛Δ 2. Ոઃఆը໘Λ։͘ 3. ݴޠઃఆ·ͰεΫϩʔϧ͠ݴޠΛΓସ͑Δ 4. ετΞը໘Λ։͘ 5. εΫϦʔϯγϣοτΛࡱΔ ҙࣄ߲ ຊষͰհ͢Δ༰ҎԼͷಈ࡞ڥͰߦ͍ͬͯ·͢ɻ ҟͳΔڥͰͷಈ࡞֬ೝ͍ͯ͠ͳ͍ͨΊ͝ཹҙ͍ͩ͘͞ɻ • : MacBook Pro • νοϓ: Apple M2 Max • ϝϞϦ: 64 GB • OS: macOS Sonoma 14.6.1 • Xcode: 15.4 (15F31d) • Android Studio: Jellyfish | 2023.3.1 Patch 2 ·ͨɺຊষͰհ͢ΔՌ࣍ͷπʔϧΛ༻͠·͢ɻ • Visual Studio Code • Bun v1.1.30 (ύοέʔδϚωʔδϟʔ) • Node v22.4.0 • Appium (Appium αʔό) • WebdriverIO (Appium ΫϥΠΞϯτ) • Appium Inspector (GUI Ξγελϯτπʔϧ) • iOS γϛϡϨʔλɺAndroid ΤϛϡϨʔλ • VCS: Git 12 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.1 Appium ͷΞʔΩςΫνϟ ຊষͰհ͢Δίʔυ GitHub
্ʹΦʔϓϯιʔεͱͯ͠ެ։͍ͯ͠·͢*1ɻιʔείʔυຊ ষͷྲྀΕʹԊͬͯίϛοτΛੵΜͰ͍ΔͷͰ్͠தͰ͕ੜͨ͡߹ͦͪΒ͔Βҙͷ࣌ͷ ίʔυΛ෮ݩͰ͖·͢ʢ࠷৽ͷίϛοτ*2͕ຊষͷܗͱͳ͍ͬͯ·͢ʣ ɻ ͦΕͰͬͦ͘͞ Appium ʹ͍ͭͯհ͍͖ͯ͠·͢ɻ 2.1 Appium ͷΞʔΩςΫνϟ Appium ϚϧνϓϥοτϑΥʔϜʹରԠͨ͠ E2E ςεςΟϯάϑϨʔϜϫʔΫͰ͢ɻ͞·͟· ͳϓϥοτϑΥʔϜʹରԠ͓ͯ͠ΓಛʹϞόΠϧΞϓϦͷྖҬʹ͓͍ͯࣥච࣌ͰσϑΝΫτελ ϯμʔυͱݴͬͯࠩ͠ࢧ͑ͳ͍Ͱ͠ΐ͏ɻৄ͍͠ಛυΩϡϝϯτʹৄ͘͠ࡌ͍ͬͯΔͷͰׂѪ ͍͖ͤͯͨͩ͞·͕͢ɺAppium Λཧղ͢Δ͏͑ͰΞʔΩςΫνϟͷશମ૾͕ѲͰ͖ΔͱϋϯζΦ ϯܗࣜͰԿΛ͍ͬͯΔͷ͔໎ࢠʹͳΓͮΒ͍͔ͱࢥ͍·͢ͷͰ͜͜Ͱ؆୯ʹհ͍͖ͤͯͨͩ͞ ·͢ɻ Appium ओʹʮAppium ΫϥΠΞϯτʯ ʮAppium αʔόʯ ʮAppium υϥΠόʯͷ 3 ͭͱʮσό ΠεʯͰߏ͞Ε·͢ɻͦΕͧΕͷׂҎԼͷͱ͓ΓͰ͢ɻ Appium ΫϥΠΞϯτ Appium ΫϥΠΞϯτ Appium αʔόͱ௨৴͠σόΠεΛૢ࡞͢ΔͨΊͷίʔυΛఏڙ͢Δ πʔϧͰ͢ɻAppium αʔόͦͷઌʹ͋ΔσόΠε͓Αͼૢ࡞ରͱͳΔΞϓϦͷଓใઃఆ Appium ΫϥΠΞϯτ্Ͱఆٛ͠·͢ɻAppium ΫϥΠΞϯτʹ Appium νʔϜ͕։ൃ͍ͯ͠ Δͷͱ༗ࢤ͕։ൃ͍ͯ͠Δͷɺ͞·͟·ͳݴޠͷ࣮͕͋ΓϢʔβʔ͕ࣗීஈ͍׳Ε͍ͯ ΔݴޠͰࣗಈԽ͢ΔͨΊͷεΫϦϓτΛॻ͚·͢ɻ·ͨɺ۩ମతͳૢ࡞ Appium ΫϥΠΞϯτʹ ΑΓநԽ͞Ε͍ͯΔͨΊ͋ΔఔϓϥοτϑΥʔϜʹґଘ͠ͳ͍ܗͰॲཧΛهड़Ͱ͖ΔΑ͏ʹͳͬ ͍ͯ·͢ɻ Appium αʔό Appium αʔό Node.js ͷ HTTP αʔόͰ Appium ΫϥΠΞϯτͱσόΠεͷதܧΛ୲ͬ ͓ͯΓɺAppium ΫϥΠΞϯτ͔Βड͚ͨϦΫΤετΛࢦఆ͞ΕͨσόΠε্Ͱ࣮ߦ͠ड͚औͬ ࣮ͨߦ݁ՌΛ Appium ΫϥΠΞϯτฦ٫͠·͢ɻσόΠε্ͰॲཧΛ͢ΔͨΊʹ࣍ʹհ͢Δ Appium υϥΠό͕ඞཁͱͳΓ·͢ɻ Appium υϥΠό Appium υϥΠό Appium ͕ఏڙ͢Δ֤ϞόΠϧϓϥοτϑΥʔϜʹಛԽͨ͠πʔϧͱͳͬͯ ͍·͢ɻAppium αʔό͔Βͷ௨৴Λड͚औΓநԽ͞Ε͍ͯͨ UI ૢ࡞ॲཧΛ֤ϓϥοτϑΥʔϜ *1 https://github.com/soudai-s/appium-example *2 https://github.com/soudai-s/appium-example/tree/119a0561c56e49ea1446ec45d230bde7a07b1d6b 13 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.2 Android ΞϓϦͷͨΊͷڥߏங Ͱߦ͏ͨΊͷॲཧʹม͠σόΠεΛૢ࡞͠·͢ɻ σόΠε σόΠε࣮ػɺ͘͠ΤϛϡϨʔλγϛϡϨʔλͷ͜ͱΛࢦ͠·͢ɻຊষͰ
Android Τ ϛϡϨʔλ͓Αͼ iOS γϛϡϨʔλΛ༻͠·͢ɻ͜ΕΒͷσόΠε͕ Appium υϥΠόܦ༝Ͱ Appium αʔόͱଓ͠σόΠε্Ͱ Appium ΫϥΠΞϯτͰॻ͍ͨॲཧ͕࣮ߦ͞Ε·͢ɻ ߏʹ͍ͭͯͷิ Appium Λಈ࡞ͤ͞ΔͨΊʹ͜ΕΒͷׂͦΕͧΕΛ୲͏πʔϧ͕ඞཁͱͳ͓ͬͯΓɺ·ͨɺ ׂʹΑͬͯπʔϧͷબࢶ͕ଟذʹΔͨΊށ͏͔͠Ε·ͤΜɻຊষͰ࣍Ҏ߱ͰॱΛ ͬͯԿΛηοτΞοϓ͍ͯ͠Δͷ͔Λৄ͘͠આ໌͠·͢ɻ 2.2 Android ΞϓϦͷͨΊͷڥߏங ·ͣηοτΞοϓ͕ൺֱత༰қͳ Android ͔ΒڥߏஙΛ͍͖ͯ͠·͢ɻ ϦϙδτϦͷηοτΞοϓ ͦΕͰ Android ΞϓϦͷͨΊͷڥߏஙΛߦ͍·͠ΐ͏ɻຊষͰචऀͷΈʹΑΓ Appium ΫϥΠΞϯτͷݴޠͱͯ͠ TypeScript Λ༻͠ɺ·ͨɺύοέʔδϚωʔδϟʔʹ Bun Λ͍· ͢ɻຊষͰجຊతͳػೳ͔͠Θͳ͍ͨΊ͓ΈͷύοέʔδϚωʔδϟʔ͕͋ΕͦͪΒΛ͝ར ༻͍͚ͨͩ·͢ɻ ·ͣϦϙδτϦΛ༻ҙ͠·͢ɻ bun init ࣍ʹύοέʔδΛཧ͠·͢ɻTypeScript peerDependencies ʹೖ͍ͬͯΔͨΊ devDepen- dencies ͱͯ͠ೖΕ͍ͯ͠·͢ɻ 14 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.2 Android ΞϓϦͷͨΊͷڥߏங bun remove @types/bun
typescript bun add -d typescript ts-node ·ͨɺࠓޙͷ։ൃΛݟਾ͑ϦϯλɺϑΥʔϚολΛ४උ͠·͢ɻຊষͰ͜ΕΒʹ Biome Λ͏ ͜ͱʹ͠·͢*3ɻ·ͨɺBiome Πϯετʔϧ࣌ʹ--exact Φϓγϣϯɺͭ·ΓΠϯετʔϧ࣌ͷ όʔδϣϯͰͱͲΊ͓ͯ͘͜ͱΛਪ͍ͯ͠·͢ɻͦͷͨΊຊষͰ࡞ۀ࣌ͷόʔδϣϯͷ··ͱͲ Ί͓ͯͨ͘ΊʹόʔδϣϯΛࢦఆ͠·͢ɻ bun add -d @biomejs/[email protected] Πϯετʔϧޙ Biome ͷઃఆΛՃ͠·͢ɻ biome.json { "$schema": "https://biomejs.dev/schemas/1.9.3/schema.json", "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }, "files": { "ignoreUnknown": false, "ignore": [] }, "formatter": { "attributePosition": "auto", "enabled": true, "formatWithErrors": false, "ignore": [], "indentStyle": "space", "indentWidth": 2, "lineEnding": "lf", "lineWidth": 80 }, "organizeImports": { "enabled": true }, "linter": { "enabled": true, "rules": { "recommended": true } *3 https://biomejs.dev/ja/ 15 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.2 Android ΞϓϦͷͨΊͷڥߏங }, "javascript": {
"formatter": { "arrowParentheses": "always", "bracketSameLine": false, "bracketSpacing": true, "jsxQuoteStyle": "double", "quoteProperties": "asNeeded", "semicolons": "always", "trailingCommas": "all" } }, "json": { "formatter": { "trailingCommas": "none" } } } npm εΫϦϓτՃ͠·͢ɻ package.json "devDependencies": { "@biomejs/biome": "1.9.3", "ts-node": "^10.9.2", "typescript": "^5.6.3" + }, + "scripts": { + "lint": "biome lint", + "format": "biome format", + "check": "biome check", + "lint:fix": "biome lint --write", + "format:fix": "biome format --write", + "check:fix": "biome check --write" } ϦϯτɺϑΥʔϚοτ IDE ͰϓϥάΠϯΛೖΕϑΝΠϧอଘ࣌ʹΦʔτϑΥʔϚοτ͢Δઃఆ ΛՃ͓ͯ͘͠ͱศརͰ͢ɻVisual Studio Code ͰϦϙδτϦݻ༗ͷઃఆਪͷ֦ுػೳΛఆ ٛͰ͖·͢ɻͬͦ͘͞Ճ͓͖ͯ͠·͠ΐ͏ɻ .vscode/settings.json { "editor.codeActionsOnSave": { "quickfix.biome": "explicit", "source.organizeImports.biome": "explicit" 16 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.2 Android ΞϓϦͷͨΊͷڥߏங }, "editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true } .vscode/extensions.json { "recommendations": ["biomejs.biome"] } ηοτΞοϓ͕ऴΘͬͨΒ Visual Studio Code ΛϦϩʔυ͠ index.ts ͷηϛίϩϯΛআͯ͠อ ଘͯ͠Έ·͠ΐ͏ɻ͏·͍͚͘ηϛίϩϯ͕Ճ͞ΕϑΝΠϧඌʹվߦ͕ೖΔͣͰ͢ɻ͜ΕͰ Visual Studio Code Λ͏ࡍϑΝΠϧฤूޙͷอଘ࣌ʹࣗಈͰमਖ਼͞ΕΔΑ͏ʹͳΓ·ͨ͠ɻ ͞Βʹ Visual Studio Code Ҏ֎Λ͍ͬͯͯमਖ਼͕ద༻͞ΕΔΑ͏ pre-commit ϑοΫͰࣗಈ मਖ਼͞ΕΔΑ͏ʹ͠·͠ΐ͏ɻGit ͷ Hook Λཧ͢ΔͨΊͷπʔϧͱͯ͠ Lefthook ΛΠϯετʔ ϧ͠·͢ɻ bun add -d lefthook@^1.7.18 Lefthook lefthook.yml ͱ͍͏ઃఆϑΝΠϧΛͬͯ Git ͷ Hook ΛཧͰ͖·͢ɻࠓճ pre-commit ϑοΫΛͬͯ biome Λ࣮ߦ͢ΔઃఆΛՃ͢ΔͨΊʹࣗಈ࡞͞Εͨ lefthook.yml Λ࣍ͷΑ͏ʹमਖ਼͠·͠ΐ͏ɻ lefthook.yml pre-commit: commands: biome: glob: "*.{js,ts}" run: bun check:fix --staged && git update-index --again Lefthook Ͱ{staged_files}ͱ͍͏ߏจͰεςʔδϯάʹ͋ΔϑΝΠϧΛϑΟϧλϦϯάͰ͖ ·͕͢ Biome ͷ--staged ΦϓγϣϯͰಉ͜͡ͱ͕ՄೳͰ͢ɻ͜ͷઃఆͰॏཁͳ࣮ߦεΫϦ ϓτͷ AND ԋࢉࢠͰ݁߹͍ͯ͠Δޙ෦Ͱ͢ɻ͜Ε͕ͳ͍ͱ Biome ʹΑΔमਖ਼͕ऴΘΔલʹί ϛοτ͞Εͯ͠·͏߹͕͋Γ·͢ɻ 17 -
ୈ2ষ AppiumʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.2 AndroidΞϓϦͷͨΊͷڥߏங Lefthook ͷઃఆ͕ऴΘͬͨΒ࣍ͷΑ͏ʹ IDE ΛΘͣ index.ts Λमਖ਼ͦ͠ͷ··ίϛοτͯ͠
Έ·͠ΐ͏ɻ echo "console.log(’Hello via Bun!’)" > index.ts git add index.ts git commit -m "Try Lefthook" ‷ᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷ‸ ᴹ lefthook v1.7.18 hook: pre-commit ᴹ ‹ᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷ› ᴺ biome > $ biome check --write --staged Checked 1 file in 1397 s. Fixed 1 file. ᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷᴷ summary: (done in 0.11 seconds) v biome [main c1c4cab] Try Lefthook 1 file changed, 1 insertion(+), 1 deletion(-) ແࣄ Lefthook ͕ಈ͖·ͨ͠ɻมߋ༰ΛݟΔͱ Biome ʹΑΔमਖ਼ֻ͕͔͍ͬͯΔͣͰ͢ɻ Appium ͷηοτΞοϓ ͜͜·Ͱͷ४උ͕Ͱ͖ͨͱ͜ΖͰ Appium αʔόΛΠϯετʔϧ͠·͢ɻ bun add -d appium ࣍ʹυϥΠόΛΠϯετʔϧ͠·͢ɻ Appium ͞·͟·ͳϓϥοτϑΥʔϜ্Ͱಈ࡞͠·͢ɻυϥΠόͷϦετ࣍ͷίϚϯυͰ֬ೝ Ͱ͖·͢ɻ npx appium driver list v Listing available drivers - uiautomator2 [not installed] - xcuitest [not installed] - espresso [not installed] - mac2 [not installed] - windows [not installed] - safari [not installed] - gecko [not installed] 18 -
ୈ2ষ AppiumʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.2 AndroidΞϓϦͷͨΊͷڥߏங - chromium [not installed] ͜͜Ͱ Android
ͷڥߏஙΛ͍ͨ͠ͷͰࣥච࣌Ͱ Android ༻ͷυϥΠόͱͯ͠ఏڙ͞Ε͍ͯ Δ UIAutomator2 ΛΠϯετʔϧ͠·͠ΐ͏ɻ npx appium driver install uiautomator2 υϥΠόͷΠϯετʔϧޙͦͷυϥΠόΛ༻ͨ͠ࡍʹ͓͚Δ࣮ߦڥ͕͍ͬͯΔ͔ΛஅͰ ͖·͢ʢͨͱ͑ UIAutomator2 Ͱ͋Ε Android ͚ͷ࣮ߦڥ͕͍ͬͯΔ͔அͯ͘͠Ε· ͢ʣ ɻWARN ͳͯ͘ྑ͍͕͋ΔͱΑΓྑ͍ͷɺError ඞཁͳͷͱͯ͠அ݁Ռ͕දࣔ͞Ε ·͢ɻError ͕ग़͍ͯΔ߹ਖ਼ৗʹಈ͔ͳ͍ͨΊஅ͢Δ͜ͱΛ͓קΊ͠·͢ɻUIAutomator2 ͷ ߹ appium driver doctor uiautomator2 ͰஅͰ͖·͢ɻ Ҏલ·Ͱಈ࡞ڥͷஅπʔϧͱͯ͠ఏڙ͞Ε͍ͯΔ Appium Doctor ͱ͍͏ͷ͕͋Γ· ͨ͠ɻnpm ্Ͱ appium-doctor ͱ@appium/doctor ͕͋Γ·͕͢લऀ͢ͰʹඇਪͰޙऀ Appium αʔό 2.4. θϩ͔ΒඇਪͰ͢ɻΘΓʹ༻͢ΔυϥΠόʢ·ͨϓϥάΠϯʣ͝ͱʹί Ϛϯυ͕ఏڙ͞Ε͍ͯ·͢ͷͰͦͪΒΛ͏ͱྑ͍Α͏Ͱ͢ɻ υϥΠόͷΠϯετʔϧ͕ྃͨ͠Β Appium ΫϥΠΞϯτΛΠϯετʔϧ͠·͠ΐ͏ɻຊষͰ Appium ΫϥΠΞϯτͱͯ͠ WebdriverIO Λ༻͠·͢ɻWebdriverIO ͷυΩϡϝϯτ*4Ͱ ςϯϓϨʔτ࡞༻ͷεΫϦϓτ͕հ͞Ε͍ͯ·͕ࣥ͢ච࣌ͰεΫϦϓτʹΑΓ࡞͞ΕΔς ϯϓϨʔτͰϒϥβ͚ͷςετͱͳͬͯ͠·͍ Appium ͷͨΊʹଟ͘ͷ࡞ۀ͕ඞཁͱͳͬͯ ͠·͍·͢ɻWebdriverIO ʹςϯϓϨʔτͷ΄͔ʹతʹ߹ΘͤͯରܗࣜͰηοτΞοϓΛ ਐΊΔ͜ͱ͕Ͱ͖Δ࣮ߦπʔϧ͕༻ҙ͞Ε͍ͯΔͷͰຊষͰͦͪΒΛ͍·͢ɻCLI ΛΠϯετʔ ϧ͠ରܗࣜͷ࣮ߦπʔϧΛݺͼग़͠·͢ɻ bun i -d @wdio/cli@~9.2.1 bunx wdio config v A project named "appium-example" was detected at "/path/to/repos/appium-example", correct? yes v A project named "appium-example" was detected at "/path/to/repos/appium-example", correct? yes v What type of testing would you like to do? E2E Testing - of Web or Mobile Applications v Where is your automation backend located? On my local machine v Which environment you would like to automate? Mobile - native, hybrid and mobile web apps, on And v Which mobile environment you’ld like to automate? Android - native, hybrid and mobile web apps, t > using UiAutomator2 (https://www.npmjs.com/package/appium-uiautomator2-driver) v Which framework do you want to use? Mocha (https://mochajs.org/) v Do you want to use Typescript to write tests? yes *4 https://webdriver.io/docs/gettingstarted 19 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.2 Android ΞϓϦͷͨΊͷڥߏங v Do you
want WebdriverIO to autogenerate some test files? no v Which reporter do you want to use? spec v Do you want to add a plugin to your test setup? v Would you like to include Visual Testing to your setup? For more information see https://webdrive v Do you want to add a service to your test setup? appium v Do you want me to run ‘bun install‘ yes v Continue with Appium setup using appium-installer (https://github.com/AppiumTestDistribution/appi ͜ΕʹΑΓ࣍ͷΑ͏ͳมߋ͕ࣗಈͰద༻͞Ε·͢ɻ • WebdriverIO Λ࣮ߦ͢ΔͨΊͷґଘͱεΫϦϓτίϚϯυͷՃ • WebdriverIO ͷૢ࡞ରΛࢦఆ͢ΔͨΊͷઃఆϑΝΠϧ (wdio.conf.ts) ͷՃ ࣍ʹ WebdriverIO ্ͰઃఆϑΝΠϧΦʔτϝʔγϣϯ༻ͷεΫϦϓτΛॻͨ͘Ίʹ TypeScript ͷઃఆϑΝΠϧ (tsconfig.json) Λ࣍ͷΑ͏ʹमਖ਼͠·͢ɻ΄΅ͯ͢ॻ͖͑ΔͷͰ diff Ͱͳ͘ ॻ͖͑ͨޙͷঢ়ଶΛࡌͤ·͢ɻ tsconfig.json { "compilerOptions": { "moduleResolution": "node", "module": "ESNext", "target": "es2022", "lib": [ "es2022" ], "types": [ "node", "@wdio/globals/types", "expect-webdriverio", "@wdio/mocha-framework", "@wdio/appium-service" ], "skipLibCheck": true, "noEmit": true, "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": [ "test", "wdio.conf.ts" ] } 20 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.2 Android ΞϓϦͷͨΊͷڥߏங ॾʑͷηοτΞοϓ͕ࡁΜͩΒ@wdio/cli Λ 8
ܥʹμϯάϨʔυ͠·͢ɻ͜Εࣥච࣌ʹ͓͍ ͯ 9 ܥͩͱςετϥϯφʔ࣮ߦ࣌ʹΤϥʔ͕ൃੜ͢ΔͨΊͰ͢ɻ bun i -d @wdio/cli@^8.40.6 ͜͜·Ͱͷ४උ͕Ͱ͖ΕηοτΞοϓྃ·Ͱ͏গ͠Ͱ͢ʂ ࠷ޙʹ WebdriverIO ্ͷεΫ ϦϓτͰ Appium αʔόΛհ͠σόΠε্Ͱ UIAutomator2 ʹΑΔૢ࡞ΛͰ͖ΔΑ͏ʹ͢ΔͨΊ ʹσόΠεΞϓϦɺαʔόͷઃఆΛ capabilities هड़͠·͢ɻͱ͍͍ͬͯͣΕڥม͔ ΒಡΈࠐ·ͤΔΑ͏ʹ͢Δ͚ͩͰ͢ɻ·ͨɺҰॹʹςετϥϯφʔ͕ಉ࣌ʹ্ཱ͕ͪΔ࠷େΛ maxInstances Ͱ੍ݶ͓͖ͯ͠·͠ΐ͏ɻ wdio.conf.ts - maxInstances: 10, + maxInstances: 1, ... capabilities: [{ // capabilities for local Appium web tests on an Android Emulator platformName: ’Android’, - browserName: ’Chrome’, - ’appium:deviceName’: ’Android GoogleAPI Emulator’, - ’appium:platformVersion’: ’12.0’, + ’appium:deviceName’: process.env.ANDROID_DEVICE_NAME, + ’appium:appPackage’: process.env.ANDROID_APP_PACKAGE, + ’appium:appActivity’: process.env.ANDROID_APP_ACTIVITY, ’appium:automationName’: ’UiAutomator2’ }], ͦͯ͠ WebdriverIO ͕ಡΈࠐΉςετϥϯφʔΛࢦఆ͠·͢ɻ wdio.conf.ts specs: [ + "./test/specs/**/*.ts", - // ToDo: define location for spec files here ], ·ͨɺࠓճͷతςετͰͳ͘ಈ࡞ݕূͰ͢ɻͰ͖Δָ͚ͩʹಈ࡞ݕূΛ͢ΔͨΊʹҎԼͷઃ ఆՃ͠·͢ɻ 21 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ wdio.conf.ts capabilities: [
{ // capabilities for local Appium web tests on an Android Emulator platformName: "Android", "appium:deviceName": process.env.ANDROID_DEVICE_NAME, "appium:appPackage": process.env.ANDROID_APP_PACKAGE, "appium:appActivity": process.env.ANDROID_APP_ACTIVITY, "appium:automationName": "UiAutomator2", + "appium:noReset": true, + "appium:autoGrantPermissions": true, }, ], appium:noReset ςετ࣮ߦ࣌ʹΞϓϦΛΫϦʔϯΠϯετʔϧ͠ͳ͍ͨΊͷઃఆͰ͢ɻtrue Λࢦఆ͢Δ͜ͱͰ͢ͰʹΞϓϦ͕Πϯετʔϧ͞Ε͍ͯͨ߹ʹΠϯετʔϧࡁΈͷΞϓϦΛ͏ Α͏ʹͳΓ·͢ɻappium:autoGrantPermissions ςετ࣮ߦ࣌ʹٻΊΒΕΔݖݶΛࣗಈͰঝೝ ͠·͢ɻ ͜ΕͰ४උྃͰ͢ʂ 2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ͔͜͜Β͍Α͍Αಈ࡞ݕূͷͨΊͷॲཧΛॻ͍͍͖ͯ·͕͢ɺͦͷલʹڥมʹಀͨ͠ύϥ ϝʔλΛࢦఆ͢Δඞཁ͕͋Γ·͢ɻadb ͰௐΔ͜ͱ͕Ͱ͖·͢ɻadb Android ։ൃͷͨΊͷ ηοτΞοϓͷաఔͰΠϯετʔϧ͞Ε͍ͯΔ͔ͱࢥ͍·͢ͷͰΠϯετʔϧखॱ͜͜ͰׂѪ͠ ·͢ɻ ·ͣσόΠε ID Λௐ·͠ΐ͏ɻAndroid ΤϛϡϨʔλ͕͋Β͔͡ΊΠϯετʔϧ͞Ε͍ͯΔঢ় ଶͰҎԼͷίϚϯυΛ࣮ߦ͢Δͱ࣍ͷΑ͏ʹ֬ೝ͕Ͱ͖·͢ɻ adb devices List of devices attached emulator-5554 device ͜ͷྫͰ emulator-5554 ͕σόΠε ID ͱͳΓ·͢ɻ ࣍ʹΤϛϡϨʔλ্ͰΠϯετʔϧ͞Ε͍ͯΔΞϓϦͷύοέʔδ໊Λௐ·͢ɻͪΖΜ͜ͷΑ ͏ͳ͜ͱΛ͠ͳ͔ͯ͘ΔͷͰ͋Εࢦఆ͢ΕΑ͍ͷͰ͜ͷ࡞ۀෆཁͰ͢ɻDEVICE_ID ෦ઌ΄Ͳͷૢ࡞Ͱऔಘ࣮ͨ͠ࡍͷσόΠε ID Λࢦఆ͠·͢ɻ 22 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ adb -s DEVICE_ID
shell pm list packages package: ͔ΒޙΖͷจࣈྻ͕ύοέʔδ໊ͱͳΓ·͢ɻ͍Ζ͍ΖͳΞϓϦ͕දࣔ͞Ε·͕͢͜ͷ த͔Β͕ࣗૢ࡞͍ͨ͠ΞϓϦΛ୳͠·͠ΐ͏ɻσόΠε ID ͱύοέʔδ໊͕͔ͬͨΒΞϓϦͷ ΤϯτϦϙΠϯτΛௐ·͢ɻͪ͜Β͢Ͱʹ͍ͬͯΕෆཁͰ͢ɻPACKAGE_NAME ࣮ࡍ ͷύοέʔδ໊Λࢦఆ͍ͯͩ͘͠͞ɻ adb -s DEVICE_ID shell dumpsys package PACKAGE_NAME ͍Ζ͍Ζͱදࣔ͞Ε·͕͢ android.intent.action.MAIN ʹ͋Δͷ͕ΤϯτϦϙΠϯτͰ͢ɻ ۩ମతʹ PACKAGE_NAME/ACTIVITY_NAME ͱ͍͏ϑΥʔϚοτͷ ACTIVITY_NAME ෦͕Τ ϯτϦϙΠϯτͱͳ͍ͬͯ·͢ɻ ͜͜·ͰʹಘͨใΛͦΕͧΕڥมʹࢦఆ͠·͢ɻ .env ANDROID_DEVICE_NAME=σόΠεID ANDROID_APP_PACKAGE=ύοέʔδ໊ ANDROID_APP_ACTIVITY=ΤϯτϦʔϙΠϯτ ͯ͞ɺ͜͜·Ͱ४උ͕Ͱ͖ΕεΫϦϓτ͑͋͞Εςετϥϯφʔ͕ಈ͖·͢ɻ࣮ࡍʹԿ͠ ͳ͍ςετΛॻ͍ͯࢼͯ͠Έ·͠ΐ͏ɻຊষͰςεςΟϯάϑϨʔϜϫʔΫʹ Mocha*5Λ༻͠ ͍ͯ·͕͢ Mocha ͷৄ͍͠ղઆ͜͜ͰׂѪ͠·͢ɻ͜͜Ͱಛච͖͕͋͢Δͱ͢ΕɺͦΕ ࣗͰબͨ͠ςετϥϯφʔͰීஈ͍׳ΕͨݴޠςεςΟϯάϑϨʔϜϫʔΫͰॲཧ͕ॻ͚ Δͱ͍͏Ͱ͢ɻ mkdir -p test/specs/ touch test/specs/e2e.ts *5 https://mochajs.org/ 23 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ test/specs/e2e.ts describe("Automation", ()
=> { it("takes screenshots in all languages", async () => {}); }); bun run wdio ͏·͍͚࣮͘ࡍʹΞϓϦಈ͔ͳ͍Ͱ͕͢ςετ͕ύεͨ͠ͱදࣔ͞ΕΔͣͰ͢ɻ ͯ͞ɺ͜͜·ͰΞϓϦΛಈ͔ͨ͢ΊͷηοτΞοϓͰ͕ͨ͠ɺ͔͜͜Β࣮ࡍͷΞϓϦΛૢ࡞͢ ΔͨΊͷεΫϦϓτΛॻ͘ඞཁ͕͋Γ·͢ɻ ΞϓϦ͞·͟·ͳύʔπͰߏ͞Ε͍ͯ·͕͢ɺAppium ܦ༝ͰΞϓϦΛૢ࡞͢ΔͨΊʹ UI ্ͰಛఆͷཁૉΛࣝผ͢ΔͨΊʹͲͷΑ͏ͳϩέʔλ͕͑Δ͔ௐͳ͚ΕͳΓ·ͤΜɻͦ͜Ͱ Appium Inspector ͱ͍͏ͷΛ͍·͢ɻ Appium Inspector Appium Inspector ΞϓϦͰཁૉ͝ͱʹ༻ՄೳͳϩέʔλͱͦͷϩέʔλͰཁૉΛࣝผ͢Δͨ ΊͷΛௐΔ͜ͱͷͰ͖Δ GUI πʔϧͰɺతʹݴ͑ Appium ΫϥΠΞϯτʹศརͳ GUI ͕ ͍ͨͷͰ͢ɻGUI Λհͯ͠ΞϓϦΛૢ࡞ͨ͠Γϩέʔλࣝผ͢ΔͨΊͷΛௐΔ͜ͱ͕Ͱ ͖ΔͨΊ Appium Λ͏্Ͱ΄΅͔ܽͤͳ͍πʔϧͱݴ͑·͢ɻWeb ্Ͱ࣮ߦͰ͖ΔαΠτެ։ ͞Ε͍ͯ·͕͢ຊষͰσεΫτοϓΞϓϦΛΠϯετʔϧ͍ͯ͠·͢*6ɻ Πϯετʔϧ͕ྃͨ͠Β্ཱͬͦͪ͛͘͞·͢ɻ ্ཱͪ͛Δͱ Capability Builder ͷλϒ͕දࣔ͞Εͨঢ়ଶͱͳ͍ͬͯ·͢ɻ͖͞΄Ͳڥม ઃఆؚͨ͠Ίͨ Capability ͷઃఆΛͦͷ··ࢦఆ͢Ε OK Ͱ͢ɻ *6 https://appium.github.io/appium-inspector/latest/quickstart/installation/ 24 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ਤ 2.1: Appium
Inspector ઃఆྃޙɺStart Session ϘλϯΛԡͤಈ͖·͢ɻ͕ɺͦͷલʹ Appium αʔό্ཱ͚ͩͪ͛ ͓͖ͯ·͠ΐ͏ɻAppium αʔό࣍ͷίϚϯυͰ্ཱͪ͛Δ͜ͱ͕ՄೳͰ͢ɻ bunx appium server αʔό্ཱ͕͕ͪͬͨΒ Appium Inspector ͷ Start Session ϘλϯΛԡͯ͠Έ͍ͯͩ͘͞ɻ͏· ͍͚͘ΤϛϡϨʔλͰࠓදࣔ͞Ε͍ͯΔը໘͕ Appium Inspector ্Ͱදࣔ͞Ε͍ͯΔ͜ͱ͕ ͔Δ͔ͱࢥ͍·͢ɻ 25 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ਤ 2.2: Android
ΤϛϡϨʔλ ͜ͷঢ়ଶͰը໘ࠨʹදࣔ͞Ε͍ͯΔΤϛϡϨʔλ্Λλοϓ͢Δͱλοϓͨ͠ཁૉΛಛఆ͢ΔͨΊ ʹ༻Ͱ͖Δϩέʔλͱࣝผ͢ΔͨΊͷͷηοτΛௐΔ͜ͱ͕Ͱ͖·͢ɻ·ͨɺΤϛϡϨʔλ্ ෦ʹ͋ΔͰࣔͨ͠ϘλϯΛΦϯʹͨ͠ঢ়ଶͰλοϓεϫΠϓͳͲΤϛϡϨʔλ Appium Inspector Λଓͨ͠··ը໘ૢ࡞͕Ͱ͖ΔͨΊҰʹෳͷը໘ʹ͋ΔཁૉΛௐΔ͜ͱ͕Մೳ Ͱ͢ɻ 26 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ Ͱ࣮࣍ࡍʹతͷΞϓϦΛ্ཱͪ͛ͨঢ়ଶͰݟͯΈ·͠ΐ͏*7ɻͦͯ͠తͷૢ࡞Λߦ͏ͨΊ ʹը໘Լ෦ʹදࣔ͞ΕΔλϒͷதͰҰ൪ӈʹ͋ΔՈઃఆλϒΛ Appium
Inspector ্Ͱλοϓ͠ϩ έʔλΛ֬ೝ͠·͢ɻ ਤ 2.3: ΈͯͶ Android ΞϓϦͷΞϧόϜը໘ λοϓ͢Δͱը૾ͷ௨Γ Appium Inspector ը໘ӈଆʹબͨ͠ཁૉʹؔ͢Δใ͕දࣔ͞Ε· ͢ɻ͜ͷ͏ͪͰғͬͨ෦͕ϩέʔλʹؔ͢Δใͱͳ͓ͬͯΓɺ͜ΕΒ͕ςετϥϯφʔͰཁ ૉΛಛఆ͢ΔͨΊͷબࢶͱͳ͍ͬͯ·͢ɻ͍ͣΕͰͳ͘ཁૉΛಛఆͰ͖·͕͢ Accessibility ID ଟݴޠରԠ͍ͯ͠ΔΞϓϦͩͱݴޠ͝ͱʹ͕ҟͳΓ͍ͮΒ͍͔͠Ε·ͤΜɻ· ͨɺXPath ཁૉ͕૬ରతʹද͞Ε͍ͯΔ͜ͱ͋ΔͨΊҙ͕ඞཁͰ͢ɻ࡞ΓʹΑͬͯΞϓϦ ͷσʔλঢ়ଶʹΑͬͯҟͳΔͱͳΔ͜ͱ͕͋Γ·͢ɻAndroid UIAutomator ͦͷ໊ͷ௨Γ UIAutomator ͷ UiSelector Λ༻ͯ͠ཁૉΛಛఆ͢ΔͨΊͷίϚϯυΛ࣮ߦ͠·͢ɻID ࠷ແ ͳબࢶͱͳΓ·͢ɻAndroid ͷ߹ɺPACKAGE_NAME:id/IDENTIFIER_STRING ͱ͍͏ϑΥʔ Ϛοτͱͳ͍ͬͯΔͨΊ IDENTIFIER_STRING ෦ͷઌ಄ʹ#Λ͚ͯࢦఆ͠·͢ɻ *7 ˞ ࢽ໘ͷ߹্ɺ։ൃ༻ͷΈͯͶΞϓϦΛΠϯετʔϧ͠ΞΧϯτͷηοτΞοϓ͕ࡁΜͰ͍ΔલఏͰਐΊ·͢ɻಉ ͡Α͏ʹਐΊΔʹΈͯͶΞϓϦΛΠϯετʔϧ͠ηοτΞοϓΛࡁ·ͤΔඞཁ͕͋Γ·͢ɻ 27 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ͨͱ͑ઃఆΞϓϦͷτοϓը໘ʹදࣔ͞ΕΔΞόλʔͷ ID
com.Android.settings.id/ac count_avatar ͱͳ͓ͬͯΓɺ͜ͷ߹#account_avatar ͕ ID ͱͳΓ·͢ɻ ਤ 2.4: Android ͷઃఆΞϓϦ ͳ͓ɺ࡞ΓʹΑͬͯ༻Ͱ͖Δϩέʔλ͕গͳ͘ɺ·͍͠બࢶ͕ͳ͍ͨΊʹଥڠΛͤ͟ΔΛ ಘͳ͍߹͋ΔͰ͠ΐ͏ɻͦͷ߹ಉ͡ૢ࡞͕ҟͳΔঢ়ଶͰ࠶ݱͰ͖Δ͔܁Γฦ֬͠ೝ͓ͯ͠ ͘ͱ҆৺Ͱ͢ʢ҆ఆ͠ͳ͍߹Կ͔͠Βͷ͕ඞཁʹͳΔ͔͠Ε·ͤΜʣ ɻ ಈ࡞ݕূͷࣗಈԽ ͦΕͰௐͨ݁ՌΛͬͦ͘͞ίʔυʹө͠·͢ɻ࣍ͷΑ͏ʹॻ͖͑·͢ɻ test/specs/e2e.ts describe("Automation", () => { it("takes screenshots in all languages", async () => { await $(process.env.ANDROID_SETTING_TAB_ID).click(); }); }); 28 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ $ WebdriverIO ͕ఏڙ͍ͯ͠ΔίϚϯυ*8ͱͳ͓ͬͯΓҾʹ͞ΕͨจࣈྻͰࣝผՄೳͳཁ
ૉΛฦ͠·͢ɻͦͯ͠ฦ͞Εͨཁૉʹରͯ͠ݺͼग़͍ͯ͠Δ click ίϚϯυ*9ϞόΠϧΞϓϦͰ λοϓʹ૬͠·͢ɻ ্هͷྫͰ$ίϚϯυͷҾʹΘ͟Θ͟ڥมΛ༻͍ͯ͠·͕͢ಛผͳཧ༝͕ͳ͚Ε จࣈྻΛهड़ͨ͠ํ͕͔Γ͍͔͢ͱࢥ͍·͢ɻ Ͱ͜ͷίʔυΛಈ͔ͯ͠Έ·͢ɻAppium Inspector ͷͨΊʹ্ཱ͓͍ͪ͛ͯͨ Appium αʔ όͦͷ··ىಈ͍ͯͯ͠མͱ͍ͯͯ͠ͳ͘ಈ͖·͢ɻ bun run wdio ͯ͞ɺ͜ΕͰՈઃఆը໘Λ։͚·ͨ͠ɻ࣍Ոઃఆը໘্ʹ͋ΔݴޠઃఆΛλοϓ͠ݴޠΛ Γସ͑Δඞཁ͕͋Γ·͢ɻ͔͠͠ݴޠઃఆՈઃఆը໘ͷϑΝʔετϏϡʔͰදࣔ͞Ε͍ͯ·ͤ Μɻ͜ͷঢ়ଶͰϩέʔλΛ༻ͯ͠ཁૉΛಛఆͰ͖ͳ͍ͨΊεΫϩʔϧΛ͢Δඞཁ͕͋Γ·͢ɻͦ ͜ͰεΫϩʔϧ͢ΔͨΊͷίʔυΛՃ͠·͢ɻ ͜ͷ··ίʔυΛॻ͖ਐΊͯΑ͍Ͱ͕͢ຊষͰ WebdriverIO ͕ਪ͍ͯ͠ΔϖʔδΦϒδΣ ΫτύλʔϯΛ౿ऻͯ͠Έ·͢ɻৄ͘͠υΩϡϝϯτ*10Λࢀর͍ͩ͘͞ɻ ·ͣ test σΟϨΫτϦͷԼʹ pageobjects σΟϨΫτϦΛ࡞Γ·͢ɻͦͯ͠શϖʔδڞ௨ͷॲཧ Λهड़͢Δ page ΫϥεΛఆٛ͢ΔͨΊʹ page.ts Λ࡞͠·͢ɻ mkdir test/pageobjects touch test/pageobjects/page.ts ͦͯ͠ઌ΄Ͳॻ͍ͨॲཧ page.ts Ҡಈͤ͞·͠ΐ͏ɻ test/pageobjects/page.ts export abstract class Page { async goToSetting() { await this.settingTab.click(); } private get settingTab() { return $(process.env.ANDROID_SETTING_TAB_ID); } *8 https://webdriver.io/docs/api/browser/$ *9 https://webdriver.io/docs/api/element/click/ *10 https://webdriver.io/docs/pageobjects/ 29 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ } தۭͰ͕͢ΞϧόϜը໘ͷϖʔδΦϒδΣΫτ༻ҙ͠·͢ɻ test/pageobjects/album.page.ts
import { Page } from "./page.ts"; class AlbumPage extends Page {} export const albumPage = new AlbumPage(); ͦͯ͠ςετΛॻ͖͑·͢ɻ test/specs/e2e.ts import { albumPage } from "../pageobjects/album.page.ts"; describe("Automation", () => { it("takes screenshots in all languages", async () => { await albumPage.goToSetting(); }); }); ͰεΫϩʔϧ͢ΔॲཧΛॻ͍͍͖ͯ·͢ɻεΫϩʔϧશը໘Ͱ༻͢ΔͷͰ Page Ϋϥεʹ࣮ ͠·͢ɻ test/pageobjects/page.ts protected async scrollDownTo(text: string, maxAttempts = 10) { // 1 const scrollableElementId = await $( "android=new UiScrollable(new UiSelector().scrollable(true))", ).elementId; // 2 let attempt = 1; while (true) { if (maxAttempts < attempt) { throw Error( ‘ࢦఆͨ͠จࣈྻͰಛఆՄೳͳཁૉ͕ݟ͔ͭΓ·ͤΜͰͨ͠ɻจࣈྻ: ${text}‘, ); } if (attempt <= 3) { await driver.execute("mobile: scrollGesture", { 30 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ elementId: scrollableElementId, direction:
"down", percent: 10, }); } else { // 3 const direction = attempt % 2 === 0 ? "up" : "down"; await driver.execute("mobile: scrollGesture", { elementId: scrollableElementId, direction, percent: 10, }); } // 4 await new Promise((resolve) => setTimeout(resolve, 1000)); // 5 if (await this.elementBy(text).isDisplayed()) { break; } attempt++; } } protected elementBy(text: string) { return $(‘android=new UiSelector().textMatches("${text}")‘); } ͜Εͬ͘͟Γղઆ͢Δͱཁૉ͕ݟ͔ͭΔ·ͰεΫϩʔϧμϯ͢Δॲཧͱͳ͍ͬͯ·͢ɻ1 ͱί ϝϯτʹهड़͍ͯ͠Δ෦ Android Ͱ࣮֬ʹεΫϩʔϧ͢ΔͨΊʹ UiScrollable ΦϒδΣΫτΛ ੜ͍ͯ͠Δॲཧͱͳ͍ͬͯ·͢ɻ͜ΕΛεΫϩʔϧδΣενϟʹεΫϩʔϧՄೳͳཁૉͱͯ͢͠ ͜ͱͰ Android ͰεΫϩʔϧ͕࣮֬ʹॲཧ͞Ε·͢ɻ2 ϧʔϓͱͳ͓ͬͯΓཁૉ͕ݟ͔ͭΔ·Ͱ εΫϩʔϧΛ܁Γฦ͢ॲཧͰ͢ɻ3 ཁૉΛ௨Γա͗ͯ͠·͍ͬͯΔՄೳੑΛՃຯ͠εΫϩʔϧΛߦ ͖དྷ͍ͤͯ͞·͢ɻ4 Appium ্Ͱཁૉ͕ݕग़ՄೳʹͳΔલʹཁૉݕग़ॲཧΛͯ͠͠·Θͳ͍Α ͏ʹҰఆ࣌ؒͷԆΛೖΕ͍ͯ·͢ɻ5 ৽ͨʹՃͨ͠ཁૉݕग़ॲཧΛͬͯεΫϩʔϧΛதஅ͞ ͤΔॲཧͰ͢ɻAndroid Ͱ UiSelector ͷ textMatches*11Λ͏͜ͱͰࢦఆͨ͠จࣈྻʹϚον͢ ΔจࣈྻΛ࣋ͭཁૉΛ୳ͤ·͢ɻ εΫϩʔϧ͢ΔॲཧΛॻ͍ͨΒͲ͜·ͰεΫϩʔϧ͢ΕΑ͍͔Λಛఆ͠·͢ɻAppium Inspec- tor Ͱௐ·͠ΐ͏ɻ *11 https://developer.android.com/reference/androidx/test/uiautomator/UiSelector#textMatches(java.lang.String) 31 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ਤ 2.5: ݴޠઃఆ
͜ͷը૾ͰݴޠઃఆͷηϧΛද͢ཁૉͰཁૉΛಛఆ͢Δ͜ͱ͕ࠔͩͬͨͨΊηϧͷதʹ͋Δจ ࣈྻΛௐ͍ͯ·͢ɻ͜ͷΑ͏ʹ͋ΔཁૉΛಛఆ͢Δͷ͕͍͠߹Ͱ Appium Inspector ্Ͱࢦ ఆͰ͖ΔཁૉͰ͋ΕͦͪΒͰସՄೳͰ͢ɻ ͯ͞ɺ͜ͷཁૉΛλοϓ͢ΕݴޠΓସΘΔͷͰ͠ΐ͏͔ɻAppium Inspector ্Ͱૢ࡞Λ Γସ࣮͑ͯࡍʹಈ͔ͯ͠Έ·͢ɻ͢Δͱ࣍ݴޠબ༻ͷϞʔμϧ͕දࣔ͞ΕΔ͜ͱ͕͔Γ·͢ɻ ࠶ཁૉݕग़ʹૢ࡞ΛΓସ͑ɺ͜͜Ͱϑϥϯεޠʹ͢ΔͨΊͷϩέʔλͱΛ֬ೝ͠·͢ɻ 32 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ਤ 2.6: ݴޠબϞʔμϧ
ͦͯ͠·ͨૢ࡞ΛΓସ͑ͯϑϥϯεޠΛλοϓͯ͠Έ·͠ΐ͏ɻ͢Δͱࠓ֬ೝμΠΞϩά͕ දࣔ͞Ε·͢ɻ ਤ 2.7: ݴޠΓସ͑֬ೝμΠΞϩά μΠΞϩά্ͷ OK ϘλϯͷϩέʔλͱΛ֬ೝ͠ OK ϘλϯΛԡ͠·͢ɻ͜ΕͰݴޠ͕Γସ ΘΔ͜ͱ͕֬ೝͰ͖·ͨ͠ɻ 33 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ͦΕͰ͜͜·Ͱͷૢ࡞ΛࣗಈԽ͠·͢ɻ·ͣՈઃఆը໘ͷϖʔδΦϒδΣΫτΛՃ͠·͢ɻ test/pageobjects/setting.page.ts import
{ Page } from "./page.ts"; class SettingPage extends Page { async switchToFrench() { await this.scrollDownTo(process.env.ANDROID_LANGUAGE_SETTING_TEXT); await this.languageSetting.click(); await this.frenchOnLanguageModal.click(); await this.okButtonOnDialog.click(); } private get languageSetting() { return this.elementBy(process.env.ANDROID_LANGUAGE_SETTING_TEXT); } private get frenchOnLanguageModal() { return this.elementBy(process.env.FRENCH_TEXT); } } export const settingPage = new SettingPage(); ͯ͞ɺ͜͜Ͱઌ΄Ͳ Page Ϋϥεʹ࣮ͨ͠ scrollDownTo elementBy Λݺͼग़͍ͯ͠·͕͢ Ҿͷܕ͕߹Θͳ͍ͨΊ IDE Λ͍ͬͯΔ߹Τϥʔ͕දࣔ͞Ε͍ͯΔ͔ͱࢥ͍·͢ɻͦ͜Ͱڥ มʹಠࣗͷܕΛఆٛ͠·͢ɻ env.d.ts declare global { namespace NodeJS { interface ProcessEnv { readonly ANDROID_DEVICE_NAME: string; readonly ANDROID_APP_PACKAGE: string; readonly ANDROID_APP_ACTIVITY: string; readonly ANDROID_SETTING_TAB_ID: string; readonly ANDROID_LANGUAGE_SETTING_TEXT: string; readonly ANDROID_OK_BUTTON_XPATH: string; readonly FRENCH_TEXT: string; } } } export {}; 34 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ͦͯ͜͠ͷܕ͕ಡΈࠐ·ΕΔΑ͏ʹ tsconfig.json ͷ
include ΦϓγϣϯΛ࣍ͷΑ͏ʹमਖ਼͠·͢ɻ tsconfig.json "include": ["env.d.ts", "test", "wdio.conf.ts"] ͜ͷঢ়ଶͰ IDE ΛϦϩʔυ͢ΕಠࣗʹՃͨ͠ڥม͕ఆٛͨ͠ܕͰղऍ͞Ε·͢ɻ SettingPage Ϋϥεͷ switchToFrench ͰݺΜͰ͍Δ okButtonOnDialog Page Ϋϥεʹఆٛ ͠·͢ɻ test/pageobjects/page.ts protected get okButtonOnDialog() { return $(process.env.ANDROID_OK_BUTTON_XPATH); } ͦͯ͜͜͠·ͰͷॲཧΛςετʹө͠·͢ɻ test/specs/e2e.ts import { albumPage } from "../pageobjects/album.page.ts"; import { settingPage } from "../pageobjects/setting.page.ts"; describe("Automation", () => { it("takes screenshots in all languages", async () => { await albumPage.goToSetting(); await settingPage.switchToFrench(); }); }); .env Λ։͍ͯΕͣʹڥมΛઃఆͨ͋͠ͱɺΞϓϦΛΞϧόϜը໘ʹͨ͠ঢ়ଶͰςετΛ ࣮ߦ͠·͢ɻ͢ΔͱՈઃఆλϒΛλοϓ͠ઃఆը໘Ͱը໘Λݴޠઃఆ·ͰεΫϩʔϧ͠ݴޠઃఆΛ λοϓͯ͠ϑϥϯεޠΓସ͑Δૢ࡞͕֬ೝͰ͖ΔΑ͏ʹͳΓ·ͨ͠ɻ ଓ͚ͯετΞը໘Λ։͘ॲཧΛՃ͠·͢ɻ͜Ε·Ͱಉ༷ʹ Appium inspector Λͬͯཁૉ͕ࣝ ผՄೳͳϩέʔλͱΛ֬ೝͦ͠ΕΛίʔυʹөͤ͞·͢ɻ env.d.ts + readonly ANDROID_STORE_TAB_ID: string; 35 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ test/pageobjects/page.ts export abstract
class Page { + async goToStore() { + await this.storeTab.click(); + } ... + private get storeTab() { + return $(process.env.ANDROID_STORE_TAB_ID); + } } test/specs/e2e.ts import { albumPage } from "../pageobjects/album.page.ts"; import { settingPage } from "../pageobjects/setting.page.ts"; describe("Automation", () => { it("takes screenshots in all languages", async () => { await albumPage.goToSetting(); await settingPage.switchToFrench(); + await albumPage.goToStore(); }); }); ετΞը໘Λ։͍ͨΒεΫϦʔϯγϣοτΛอଘ͠·͢ɻWebdriverIO ʹεΫϦʔϯγϣοτ Λอଘ͢ΔͨΊͷίϚϯυ༻ҙ͞Ε͍ͯ·͢ͷͰͦͪΒΛ͍·͢ɻը໘ભҠॲཧͷޙͩͱඳը ͕ؒʹ߹Θͳ͍ͨΊεϦʔϓॲཧΛϝιουԽ͠ݺͼग़͠·͢ɻ·ͨɺ͍ͭͰʹεΫϩʔϧதͷε Ϧʔϓॲཧॻ͖͑·͢ɻ test/pageobjects/page.ts + async saveScreenshot(language: string) { + await this.sleep(); + await driver.saveScreenshot( + ‘./tmp/${this.platform.toLowerCase()}-${language}.png‘, + ); + } + + protected get platform() { + return driver.isAndroid ? "Android" : "iOS"; + } + protected async sleep(ms = 1000) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } ... 36 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ2.3 Android Ͱಈ࡞ݕূΛࣗಈԽ͢Δ - await new
Promise((resolve) => setTimeout(resolve, 1000)); + await this.sleep(); εΫϦʔϯγϣοτͰอଘ͢ΔϑΝΠϧͷ໊લʹݴޠͷ΄͔ɺϓϥοτϑΥʔϜࢦఆ͢Δ͜ͱ Ͱݴޠ x ϓϥοτϑΥʔϜͰεΫϦʔϯγϣοτΛอଘͰ͖ΔΑ͏ʹ͍ͯ͠·͢ɻϓϥοτϑΥʔ Ϝͷ driver.capabilities ܦ༝Ͱ wdio.conf.ts Ͱఆٛͨ͠ΛऔಘͰ͖·͕͢ܕΞαʔγϣ ϯ͠ͳ͚ΕͳΓ·ͤΜɻࠓճͷΑ͏ʹ 2 ͭͷϓϥοτϑΥʔϜͷΈͰ͋Ε isAndroid ِ͕ͱͳ Δ߹ iOS ͱͯ͠͠·ͬͯͳ͍ͨΊ৽ͨʹՃͨ͠ platform ͰͦͷΑ͏ͳॲཧͱ͍ͯ͠ ·͢ɻ Ͱ࠷ޙʹςετʹεΫϦʔϯγϣοτΛอଘ͢ΔॲཧΛՃͯ͠ಈ࡞Λ֬ೝ͠·͢ɻετΞը໘ Ͱૢ࡞Λߦ͏ͨΊ StorePage Ϋϥε༻ҙ͓͖ͯ͠·͢ɻ test/pageobjects/store.page.ts import { Page } from "./page.ts"; class StorePage extends Page {} export const storePage = new StorePage(); test/specs/e2e.ts import { albumPage } from "../pageobjects/album.page.ts"; import { settingPage } from "../pageobjects/setting.page.ts"; +import { storePage } from "../pageobjects/store.page.ts"; describe("Automation", () => { it("takes screenshots in all languages", async () => { await albumPage.goToSetting(); await settingPage.switchToFrench(); await albumPage.goToStore(); + await storePage.saveScreenshot("french"); }); }); ࣮ߦલʹ.gitignore Ͱ tmp σΟϨΫτϦΛ࡞͠ Git ͷର͔Βআ֎͓͖ͯ͠·͠ΐ͏ɻ mkdir tmp echo tmp >> .gitignore bun run wdio 37 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.4 iOS ΞϓϦͷͨΊͷڥߏங ͏·͘ಈ͍͍ͯΕ tmp/android-french.png
ʹϑϥϯεޠͰ։͍ͨετΞը໘ͷεΫϦʔϯ γϣοτ͕อଘ͞Ε·͢ɻ ͜ΕͰ Android ͱ͠·͢ɻ 2.4 iOS ΞϓϦͷͨΊͷڥߏங iOS ͰϦϙδτϦͷ४උ࡞ۀͪΖΜͷ͜ͱ Appium αʔό͢ͰʹΠϯετʔϧࡁΈͳͷ Ͱ Appium υϥΠόΛΠϯετʔϧ͠·͢ɻ bunx appium driver install xcuitest ͜ͷͱ͖ύοέʔδϚωʔδϟʔϢʔβʔͷڥʹ͔͔ΘΒͣ npm Ͱ࣮ߦ͞Ε·͢ɻ͜͠ ͍ͷͰ bun Ͱ࠶Πϯετʔϧ͠ package-lock.json ʹ͍ͭͯআ͓͖ͯ͠·͠ΐ͏ɻ bun i rm package-lock.json υϥΠόͷηοτΞοϓ iOS ͰͷΦʔτϝʔγϣϯʹ Android ʹͳ͍खॱ͕ඞཁʹͳΓ·͢ɻυΩϡϝϯτ*12ʹ͋Δ ͱ͓ΓͰ͕͢ຊষͰղઆ͍ͨ͠ͱࢥ͍·͢ɻ ·ͣ࣍ͷίϚϯυΛ࣮ߦ͠·͢ɻ *12 https://appium.github.io/appium-xcuitest-driver/7.27/guides/run-preinstalled-wda/#using-xcode 38 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.4 iOS ΞϓϦͷͨΊͷڥߏங bunx appium
driver run xcuitest open-wda ͢Δͱ Xcode ͕։͖·͢ɻXcode Ͱ WebDriverAgentRunner ͷεΩʔϜΛબ͠·͢ɻ ਤ 2.8: WebDriverAgentRunner Λબ ͦͯ͠ςετΛ࣮ߦ͠·͢ɻ ਤ 2.9: Xcode ্ͰςετΛ࣮ߦ ͢Δͱ Xcode ্ʹωοτϫʔΫड৴ଓͷڐμΠΞϩά͕දࣔ͞Ε·͢ɻ͜ΕΛڐՄ͢Δ͜ͱ Ͱ iOS γϛϡϨʔλͱ Appium αʔό͕௨৴Ͱ͖·͢ɻ 39 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ਤ 2.10:
ωοτϫʔΫड৴ଓͷڐμΠΞϩά ͜ΕͰ४උ͕͍·ͨ͠ɻ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ iOS Ͱ Android ಉ༷ʹ·ͣσόΠεΞϓϦͷใ͕ඞཁʹͳΓ·͢ɻAppium Λಈ͔͍ͨ͠ γϛϡϨʔλΛىಈ͍ͯ͠Ε࣍ͷίϚϯυͰ UDID ͷ֬ೝ͕Ͱ͖·͢ɻUDID Λ֬ೝͨ͠Β 2 ߦ ͷίϚϯυͰϞσϧͱ OS όʔδϣϯௐ·͠ΐ͏ɻϞσϧ໊γϛϡϨʔλͷ߹ Simulator ͱ͍͏จࣈྻͷखલ෦ɺOS όʔδϣϯ࠷ॳͷׅހͷͰ͢ɻతͷΞϓϦͷόϯυϧ ID ͕ ͔Βͳ͍߹ɺ͋Β͔͡ΊγϛϡϨʔλʹΠϯετʔϧ͓ͯ͘͜͠ͱͰ 3 ߦͷίϚϯυͰ֬ೝͰ ͖·͢ɻ xcrun simctl list devices | grep ’Booted’ xcrun xctrace list devices | grep UDID xcrun simctl listapps UDID ͜͜·Ͱʹ֬ೝͨ͠ΛͦΕͧΕڥมʹࢦఆ͠·͢ɻ .env +IOS_DEVICE_NAME=Ϟσϧ໊ +IOS_PLATFORM_VERSION=OSόʔδϣϯ +IOS_UNIQUE_DEVICE_IDENTIFIER=UDID +IOS_BUNDLE_IDENTIFIER=όϯυϧID ͦͯ͠ wdio.conf.ts ͷ capabilities ʹ৽ͨʹ iOS ༻ͷઃఆΛՃ͠·͢ɻAndroid ͷઃఆͷલ ʹՃ͢ΔΑ͏ʹ͠·͠ΐ͏ɻcapabilities ࣍ͷΑ͏ʹͳΓ·͢ɻ 40 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ wdio.conf.ts capabilities:
[ { platformName: "iOS", "appium:automationName": "XCUITest", "appium:deviceName": process.env.IOS_DEVICE_NAME, "appium:platformVersion": process.env.IOS_PLATFORM_VERSION, "appium:udid": process.env.IOS_UNIQUE_DEVICE_IDENTIFIER, "appium:bundleId": process.env.IOS_BUNDLE_IDENTIFIER, "appium:noReset": true, "appium:autoGrantPermissions": true, "appium:showXcodeLog": true, }, { // capabilities for local Appium web tests on an Android Emulator platformName: "Android", "appium:deviceName": process.env.ANDROID_DEVICE_NAME, "appium:appPackage": process.env.ANDROID_APP_PACKAGE, "appium:appActivity": process.env.ANDROID_APP_ACTIVITY, "appium:automationName": "UiAutomator2", "appium:noReset": true, "appium:autoGrantPermissions": true, }, ], ܕఆٛϑΝΠϧΕͣߋ৽͓͖ͯ͠·͠ΐ͏ɻ env.d.ts declare global { namespace NodeJS { interface ProcessEnv { readonly ANDROID_DEVICE_NAME: string; readonly ANDROID_APP_PACKAGE: string; readonly ANDROID_APP_ACTIVITY: string; readonly IOS_DEVICE_NAME: string; readonly IOS_PLATFORM_VERSION: string; readonly IOS_UNIQUE_DEVICE_IDENTIFIER: string; readonly IOS_BUNDLE_IDENTIFIER: string; readonly ANDROID_STORE_TAB_ID: string; readonly ANDROID_SETTING_TAB_ID: string; readonly ANDROID_LANGUAGE_SETTING_TEXT: string; readonly ANDROID_OK_BUTTON_XPATH: string; readonly FRENCH_TEXT: string; } } } 41 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ export {};
͜ͷঢ়ଶͰγϛϡϨʔλ্ͰతͷΞϓϦΛ্ཱͪ͛ͨ··εΫϦϓτΛΒͤͯΈ·͢ɻઃఆʹ ͕ͳ͚Εඪ४ग़ྗ্Ͱॲཧ͕ਖ਼ৗʹಈ͍͍ͯΔ͜ͱ͕֬ೝͰ͖ΔͣͰ͢ʢωοτϫʔΫड৴ ଓͷڐʹ͍ͭͯ࠶ٻΊΒΕΔ߹͕͋Γ·͕ͦ͢ͷ߹࠶ڐՄΛ͍ͯͩ͘͠͞ʣ ɻ ͯ͞ɺ͜͜·Ͱ iOS ͷଓใ͕͔Γ·ͨ͠ɻͦͯ͠ iOS Android ͱϩέʔλͷछྨ͕ ҟͳΔͨΊ͜ͷ··Ͱಈ͖·ͤΜɻ࠶ Appium Inspector ͷग़൪Ͱ͢ɻAppium Inspector Λཱ ্ͪ͛ͨΒࠓ Capability Builder Λ্ॻ͖͍ͯͩ͘͠͞ɻ্ॻ͖͢Δલʹը໘ӈԼͷ Save As... ϘλϯΛԡ͢ͱࠓͷઃఆʹ໊લΛ͚ͯอଘͰ͖ Saved Capability Sets ͔Βอଘͨ͠ઃఆΛ෮ݩ͢ Δ͜ͱՄೳͰ͢ɻඞཁʹԠͯ͡อଘ͓ͯ͘͠ͱศརͰ͢ɻ ଓใΛ্ॻ͖ͨ͠Β Appium αʔό্ཱ͕͕͍ͪͬͯΔ͔֬ೝ͠ຬΛ࣋ͯ͠ Start Session Λ ԡ͠·͠ΐ͏ɻ্ཱ͕͍ͪͬͯͳ͚Ε࠶ҎԼͷίϚϯυͰ্ཱ͓͍͍ͪ͛ͯͯͩ͘͞ɻ bunx appium server ແࣄ্ཱ͕ͪΓ·ͨ͠ɻ ਤ 2.11: iOS γϛϡϨʔλ 42 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ಈ࡞ݕূͷࣗಈԽ ͦΕͰ
Android ಉ༷ʹ·ͣ࣍ͷΛͦΕͧΕௐ·͢ɻ • Ոઃఆλϒ • ݴޠબ iOS Android ͱϩέʔλͷछྨ͕Ұ෦ҟͳΓ·͢ɻݻ༗ͷϩέʔλʹ Android ಉ༷ʹυΩϡ ϝϯτͷϦϯΫ͕ซه͞Ε͍ͯΔͷͰ͍͚ʹ͍ͭͯͦͪΒΛ͝ࢀর͍ͩ͘͞ɻ ͬͦ͘͞ௐͨ݁ՌΛϖʔδΦϒδΣΫτө͠·͢ɻ͜ͷͱ͖ɺϩέʔλ͕ҟͳΔ͜ͱʹΑΔ ͕ඞཁͱͳΓ·͢ɻ·ͨɺ͜ͷ··ͰεΫϩʔϧʹ͍ͭͯಈ͔ͳ͍ͨΊมߋ͕ඞཁͱͳΓ· ͢ɻiOS Android ͱҧ͍εΫϩʔϧͷڍಈ͕҆ఆ͍ͯ͠ΔͨΊཁૉΛ௨Γա͗Δ͜ͱߟྀ͠· ͤΜɻ·ͱΊΔͱ࣍ͷΑ͏ʹͳΓ·͢ɻ ˞ ࢽ໘ͷ߹্ɺมߋͨ͠෦ͷΈൈਮ͍ͯ͠·͢ɻ·ͨɺҎ߱ڥมͷͷઃఆܕఆ ٛϑΝΠϧͷهड़ͱಉ࣌ʹߦΘΕͨͷͱͯ͠ѻ͍·͢ɻࢽ໘্Ͱলུ͞Ε͍ͯ·͕͢ຊষΛࢀ ߟʹ࣮ࡍʹखΛಈ͔͢ࡍʹΕͣʹઃఆ͠·͠ΐ͏ʢΦʔϓϯιʔεʹ͢Δඞཁ͕ͳ͚Εڥม Ͱͳ͘هड़ͯ͠͠·͏ํ͕ྑ͍͔͠Ε·ͤΜʣ ɻ env.d.ts readonly IOS_SETTING_TAB_ID: string; readonly IOS_LANGUAGE_SETTING_TEXT: string; test/pageobjects/page.ts protected async scrollDownTo(text: string, maxAttempts = 10) { if (driver.isAndroid) { const scrollableElementId = await $( "android=new UiScrollable(new UiSelector().scrollable(true))", ).elementId; let attempt = 1; while (true) { if (maxAttempts < attempt) { throw Error( ‘ࢦఆͨ͠จࣈྻͰಛఆՄೳͳཁૉ͕ݟ͔ͭΓ·ͤΜͰͨ͠ɻจࣈྻ: ${text}‘, ); } if (attempt <= 3) { await driver.execute("mobile: scrollGesture", { elementId: scrollableElementId, direction: "down", percent: 1, }); 43 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ } else
{ // 4ճҎ߱తͷElementΛ௨Γա͗ͯ࠷Լ෦౸ୡ͍ͯ͠ΔՄೳੑ͕ߴ͍ͨΊަޓʹ্Լ͞ ͤΔ const direction = attempt % 2 === 0 ? "up" : "down"; await driver.execute("mobile: scrollGesture", { elementId: scrollableElementId, direction, percent: 1, }); } // εΫϩʔϧޙͩͱඳը͕ؒʹ߹Θͣfalse͕ฦΔ߹͕͋ΔͨΊεϦʔϓΛೖΕΔ await this.sleep(); if (await this.elementBy(text).isDisplayed()) { break; } attempt++; } } else { const predicateString = this.predicateStringWith(text); driver.executeScript("mobile: scroll", [ { direction: "down", predicateString }, ]); } } protected elementBy(text: string) { return $( driver.isAndroid ? ‘android=new UiSelector().textMatches("${text}")‘ : ‘-ios predicate string: ${this.predicateStringWith(text)}‘, ); } protected predicateStringWith(text: string) { return ‘name = "${text}" OR label = "${text}" OR value = "${text}"‘; } private get settingTab() { return $( driver.isAndroid ? process.env.ANDROID_SETTING_TAB_ID : process.env.IOS_SETTING_TAB_ID, ); } ·ͣ settingTab ʹ͍ͭͯͲͪΒ ID ϩέʔλͩͬͨͨΊγϯϓϧͳมߋͱͳΓ·ͨ͠ɻҰํ Ͱ elementBy ʹ͍ͭͯϩέʔλͷ߹͚Λߦ͍ͬͯ·͢ɻεΫϩʔϧ͜Ε·ͰͷॲཧΛ͢ ͯ Android ༻ͱͯ͠৽ͨʹՃ͠·͕ͨͪ͜͠Βൺֱతγϯϓϧͳ࣮ͱͳ͍ͬͯ·͢ɻ SettingPage ࣍ͷͱ͓Γݴޠબ෦ͷΛ߹͚͍ͯ͠·͢ɻelementBy Ͱϩέʔλͷ 44 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ߹͚ߦΘΕ͍ͯΔͨΊ࣮ࡍʹҟͳΔϩέʔλʹҟͳΔ͕ઃఆ͞Ε͍ͯ·͢ɻ test/pageobjects/setting.page.ts
private get languageSetting() { return this.elementBy( driver.isAndroid ? process.env.ANDROID_LANGUAGE_SETTING_TEXT : process.env.IOS_LANGUAGE_SETTING_TEXT, ); } ͜ͷঢ়ଶͰಈ͔ͯ͠Έ·͠ΐ͏ɻ͢ΔͱࣗಈςετͷεΫϦϓτม͑ͳͯ͘ݴޠબ·Ͱ͏ ·͘ಈ͖·͕͢ݴޠબޙͷಈ͖͕ iOS ΞϓϦͱ Android ΞϓϦʹΑͬͯҟͳΔͨΊʹ iOS ్த Ͱࢭ·ͬͯ͠·͍·͢ɻ͜Ε iOS ͰݴޠબΛԡ͢ͱઃఆΞϓϦʹ͋ΔΞϓϦͷݸผઃఆͰ ݴޠΛΓସ͑ΔΑ͏ͳ࣮ʹͳ͍ͬͯΔͨΊͰ͢ɻ ਤ 2.12: iOS Ͱͷݴޠઃఆը໘ ݱࡏͷઃఆͷ߹ɺAppium ͰϑΥΞάϥϯυʹͳ͍ͬͯΔΞϓϦʹରͯ͠ૢ࡞͕ߦΘΕ· ͢ɻ͍ʹݴޠઃఆΛԡͨ͠ޙઃఆΞϓϦ͕ϑΥΞάϥϯυͱͳΔͨΊଓ͚ͯૢ࡞͢Δ͜ͱ ՄೳͰ͢ɻͦ͜Ͱ͜ͷ··ઃఆΞϓϦΛૢ࡞ͯ͠͠·͍·͠ΐ͏ɻ ઃఆΞϓϦόϯυϧ ID ͕ҟͳΔͨΊ Appium Inspector ͰࠓͷઃఆͩͱௐΔ͜ͱ͕Ͱ͖· ͤΜɻࣥච࣌ͰઃఆΞϓϦͷόϯυϧ ID com.Apple.Preferences ͱͳ͍ͬͯΔͨΊόϯ υϧ ID ্͚ͩॻ͖͢Ε͙͢ʹ֬ೝͰ͖·͢ɻ্ॻ͖ͨ͠Βͬͦ͘͞ Appium Inspector Λ্ཱͪ ͛ͯϩέʔλͱΛ֬ೝͦ͠ΕΛઃఆ͠·͢ɻ֬ೝ͕Ͱ͖ͨΒઃఆΞϓϦͷૢ࡞ SettingPage Ϋ ϥεʹهड़ͯ͠͠·͍·͠ΐ͏ɻ݁Ռɺ࣍ͷΑ͏ʹͳΓ·͢ɻ 45 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ env.d.ts readonly
IOS_PREFERRED_LANGUAGE_TEXT: string; test/pageobjects/setting.page.ts async switchToFrench() { await this.scrollDownTo(process.env.ANDROID_LANGUAGE_SETTING_TEXT); await this.languageSetting.click(); - await this.frenchOnLanguageModal.click(); - await this.okButtonOnDialog.click(); + if (driver.isAndroid) { + await this.frenchOption.click(); + await this.okButtonOnDialog.click(); + } else { + await this.preferredLanguageOnPreference.click(); + await this.frenchOption.click(); + } } - private get frenchOnLanguageModal() { + private get frenchOption() { return this.elementBy(process.env.FRENCH_TEXT); } + + private get preferredLanguageOnPreference() { + return this.elementBy(process.env.IOS_PREFERRED_LANGUAGE_TEXT); + } ·ͣ switchToFrench Ͱ߹͚Λ͍ͯ͠·͢ɻͱ͍ͬͯ Android ͷॲཧม͍͑ͯ·ͤΜɻ1 ɺAndroid iOS ϑϥϯεޠͷબࢶϩέʔλ͢ͷ·ͬͨ͘ಉ͡ͱͳ͍ͬͯ·͢ɻ ͳͷͰ໋໊Λ Android ʹಛԽ͍ͯͨ͠ frenchOnLanguageModal ͔Β൚༻తͳ frenchOption ͱ͍ ͏໊લʹมߋ͍ͯ͠·͢ɻͦͯ͠ iOS ͰઃఆΞϓϦભҠޙʹ࠶ݴޠઃఆͷ߲Λબ͢Δඞཁ ͕͋ΔͨΊ৽ͨͳॲཧΛՃ͍ͯ͠·͢ɻͦͷޙɺઃఆΞϓϦͰݴޠͷબࢶ͕දࣔ͞ΕΔͨΊ Android ಉ༷ʹϑϥϯεޠΛબ͠·͢ɻAndroid ͱҧ͍બޙ֬ೝμΠΞϩά͕දࣔ͞Εͳ ͍ͨΊॲཧ͜ΕͰऴΘΓɺͱ͍ͨ͠ͱ͜ΖͰ͕͢ 1 ͕͋Γ·͢ɻ ·ͣ͜͜·ͰҰ࣮ߦͯ͠Έ·͠ΐ͏ɻ 46 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ bun run
wdio ͢Δͱͳ͘ಈ͘ͷͷઃఆΞϓϦͰݴޠબޙʹॲཧ͕ࢭ·ͬͯ͠·͍·͢ɻ͜Εݴޠબ Λͯ͠ΈͯͶΞϓϦʹΔॲཧ͕ͳ͍ͨΊͰ͢ɻͰ͢ͷͰɺiOS Ͱ͞ΒʹΈͯͶΞϓϦʹ͢ॲ ཧՃ͠·͢ɻ test/pageobjects/page.ts protected async launchIosApp() { await driver.executeScript("mobile: launchApp", [ { bundleId: process.env.IOS_BUNDLE_IDENTIFIER }, ]); this.sleep(500); } test/pageobjects/setting.page.ts async switchToFrench() { await this.scrollDownTo(process.env.ANDROID_LANGUAGE_SETTING_TEXT); await this.languageSetting.click(); await this.frenchOnLanguageModal.click(); await this.okButtonOnDialog.click(); if (driver.isAndroid) { await this.frenchOption.click(); await this.okButtonOnDialog.click(); } else { await this.preferredLanguageOnPreference.click(); await this.frenchOption.click(); + await this.launchIosApp(); } } ͜ΕͰແࣄʹϑϥϯεޠΓସ͑Δ͜ͱ͕Ͱ͖·ͨ͠ɻ 47 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ਤ 2.13:
ϑϥϯεޠͷΞϧόϜը໘ ݴޠΓସ͕͑Ͱ͖ͨΒ͋ͱετΞը໘ભҠ͠εΫϦʔϯγϣοτΛࡱΔ͚ͩͰ͢ɻͦ͜Ͱλ ϒҰཡʹ͋ΔετΞλϒΛௐ·͢ɻઌ΄Ͳ·ͰઃఆΞϓϦΛௐΔͨΊʹ Appium Inspector ͷ ઃఆΛ্ॻ͖͍ͯͨ͠ͷͰઃఆΛͯ͠࠶࣮ߦ֬͠ೝ͠ɺiOS ༻ʹϖʔδΦϒδΣΫτө͠· ͠ΐ͏ɻ env.d.ts + readonly IOS_STORE_TAB_ID: string; test/pageobjects/page.ts private get storeTab() { - return $(process.env.ANDROID_STORE_TAB_ID); + return $( + driver.isAndroid + ? process.env.ANDROID_STORE_TAB_ID + : process.env.IOS_STORE_TAB_ID, + ); } ແࣄετΞը໘Λ։͘͜ͱ͕Ͱ͖·ͨ͠ɻ 48 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.5 iOS Ͱಈ࡞ݕূΛࣗಈԽ͢Δ ਤ 2.14:
iOS ͷϑϥϯεޠ൛ετΞը໘ ͦΕͰ্͛ʹεΫϦʔϯγϣοτͷॲཧͰ͢ɻͱݴ͍͍ͨͱ͜ΖͰ͕࣮͢εΫϦʔϯγϣο τʹ͍ͭͯ߹͚ඞཁ͋Γ·ͤΜɻ͢ͰʹϑΝΠϧ໊ʹ͍ͭͯϓϥοτϑΥʔϜͷҧ͍Λҙ ࣝͨ͠ॲཧͱͳ͍ͬͯ·͢ͷͰ࣮ݱ࣌Ͱ tmp σΟϨΫτϦΛݟΔͱ iOS ൛ͷεΫϦʔϯγϣο τ͕อଘ͞Ε͍ͯΔ͜ͱ͕͔Γ·͢ɻͨͩɺiOS ͷํ͕ඳըʹֻ͕͔࣌ؒΔͷ͔·ͩϖʔδͷҰ෦ ͕දࣔ͞Ε͍ͯ·ͤΜͶɻগ͠εϦʔϓ͢ΔॲཧΛ৳ͯ͠࠶࣮ߦͯ͠Έ·͠ΐ͏ɻ bun run wdio ͪΌΜͱը໘શମ͕ඳը͞Εͨঢ়ଶͰอଘ͞Ε͍ͯΔ͜ͱ͕֬ೝͰ͖·ͨ͠ɻ ਤ 2.15: iOS ͷϑϥϯεޠ൛ετΞը໘εΫϦʔϯγϣοτ ͱ͜ΖͰ͜͜·Ͱ࣮ߦ͍ͯ͠Δͱ iOS ͕ऴΘͬͨޙʹ Android ͕ಈ͖࢝ΊΔ͜ͱ͕͔Γ·͢ɻ 49 -
ୈ 2 ষ Appium ʹΑΔϞόΠϧΞϓϦಈ࡞ݕূͷࣗಈԽೖ 2.6 ͍͞͝ʹ ࣮ capabilities ʹ͋Δઃఆͷ͚ͩΠϯελϯε͕ಈ͘Α͏ʹͳ͍ͬͯ·͢ɻͦͯ͠ಉ࣌ʹىಈͰ
͖Δ͕ maxInstances ͱͳ͍ͬͯ·͢ɻ͜Ε͕ 1 ͩͬͨͷͰฒྻʹಈ͔ͣྻʹىಈ͢Δಈ࡞ͱͳ Γ݁Ռͱͯ͠ capabilities ͷઌ಄ʹ͋Δ iOS ͕ಈ͍͍͚ͯͨͩͰ iOS ͕ऴΘͬͨͷͰ࣍ʹ Android ͕ɺͱͳ͍ͬͯͨͱ͍͏Θ͚Ͱ͢ɻAppium αʔό WebdriverIO ͷ࣮ߦϩάΛ֬ೝ͢Δ͏͑Ͱ ߹͕ྑ͔ͬͨͷͰ͕͢ɺͲͪΒͰ͖ͨࠓͱͳͬͯྻͰಈֻ͔͍͕͔ͯͯ࣌ؒ͠Δ͚ͩͰ͢ ͶɻͳͷͰ maxInstances Λ 2 ʹมߋ͠·͠ΐ͏ɻ͢Δͱ Android ͱ iOS Λಉ࣌ʹಈ͔͢͜ͱ͕Ͱ ͖·͢ɻ wdio.conf.ts - maxInstances: 1, + maxInstances: 2, ຊষͰհ͢Δ༰Ҏ্ʹͳΓ·͢ɻ 2.6 ͍͞͝ʹ ຊষͰ Appium ͷಋೖ͔Β࣮ࡍʹಈ͔͢෦Λհ͍͖ͤͯͨͩ͞·ͨ͠ɻ ࣮ࡍʹߦ͍ͬͯΔಈ࡞ݕূ༻ͷίʔυ͔ΒݟΔͱຊষͰհͰ͖ͨͷ΄ΜͷҰ෦ͱͳ͓ͬͯΓ͍ Ζ͍ΖͱΓͳ͍෦͋ΔΜͰ͕͢ɺͦΕͰͳΜͱͳ͘ Appium ͰࣗಈԽ͢ΔͨΊͷงғؾ ͔ͭΊͨͷͰͳ͍Ͱ͠ΐ͏͔ɻ චऀ Appium ͰτΠϧͷࣗಈԽΛ͠Α͏ͱࢥཱ͍ͬͨͱ͖ɺશମͷΞʔΩςΫνϟͷཧղͱ iOS ͷηοτΞοϓʹ͖᪴·ͨ͠ɻͦ͜Ͱࣗͷ಄ͷཧ݉ͶͯචΛࣥͬͯΈͨ࣍ୈͰ͕ͨ͠ɺͦͷա ఔͰಘΒΕͨࣝࠓޙ׆͔ͤͦ͏Ͱ͢͠ԿΑΓָ͍࣌ؒ͠Ͱͨ͠ɻϓϩάϥϛϯάΛ࢝Ί͔ͨ Γͷ͜Ζ͕ࣗॻ͍ͨॲཧ͕࣮ࡍʹಈ͘ͱ͜ΖΛݟΔͷ͕͓͠Ζ͔ͬͨͷͰ͕͢ɺAppium Ͱ ͦΕ͕ΞϓϦ্ͰߦΘΕΔͱ͍͏ͰචऀʹͱͬͯϢχʔΫͳମݧͰɺ࠶ͼ͔ͭͯͷΑ͏ͳϫΫ ϫΫ͢Δؾ࣋ͪΛऔΓͨ͠ؾ͕͠·͢ɻ Appium ͱͱςεςΟϯάϑϨʔϜϫʔΫͰ͢ͷͰ·ͣಈ࡞ݕূ͔Β࢝Ίͯιʔείʔυ Λҭ͍ͯ͘͜ͱͰ E2E ςετͷڥΛඋ͢Δ͜ͱՄೳͰ͢ɻ͠·ͩࣗ͝ͷීஈͷ։ൃͷத Ͱ E2E ςετͷڥ͕͍ͬͯͳ͍Α͏Ͱ͋Εɺ·ͣτΠϧͷݮΛඪʹ Appium Λͬͨ ಈ࡞ݕূ͔Β࢝ΊͯΈΔͷ͍͔͕Ͱ͠ΐ͏͔ɻ ຊষΛಡΜͰ 1 ਓͰ Appium Λ৮ͬͯΈΑ͏ͱࢥ͍͚ͬͯͨͩͨͳΒ͍Ͱ͢ɻ 50 -
ୈ 3 ষ Cloud Spanner ͷ͘͠ΈΛཧղ͢Δ 3.1 ·͕͖͑ ॳΊ·ͯ͠ɻ2023 ৽ଔͱͯ͠ೖࣾͨ͠एদͰ͢ɻX
ͰͷΞΧϯτ໊ @Jaksho500 Ͱ͢ɻ ࢲΤϯδχΞͱͯ͠ɺTIPSTAR*1 ͱ͍͏αʔϏεͷ։ൃʹܞΘ͍ͬͯ·͢ɻ TIPSTAR ͷٕज़తͳಛͷ 1 ͭͱͯ͠ɺDB ʹ Cloud Spanner*2 ʢҎԼɺSpanner ʣΛར༻͠ ͍ͯΔ͕ڍ͛ΒΕ·͢ɻTIPSTAR ʹδϣΠϯͯ͠ॳΊͯ Spanner Λ৮Γ·͕ͨ͠ɺաڈʹ༻ ܦݧ͕͋ͬͨϦϨʔγϣφϧσʔλϕʔεʢҎԼɺRDBMS ʣ NoSQL ͱҟͳΔಠಛͷ༻ײ ͕͋Γ·ͨ͠ɻSpanner ͷجૅͱͳΔߟ͑ํΞʔΩςΫνϟɺGoogle ʹΑͬͯจͱͯ͠ެ։ ͞Ε͍ͯ·͢ɻࢲ͜ͷจΛ͍ͣΕಡΈ͍ͨͱߟ͍͑ͯ·ͨ͠ɻจΛಡΉ͜ͱͰɺීஈۀͰߦ ͏εΩʔϚઃܭΫΤϦͷνϡʔχϯάʹ͍ͭͯɺΑΓਂ͍ཧղ͕ಘΒΕΔͷͰͳ͍͔ͱظͯ͠ ͍ͨͷͰ͢ɻ͔͠͠ɺͳ͔ͳ͔࣌ؒΛ֬อͰ͖ͣೖ͔ࣾΒ 1 ͕ܦաͯ͠͠·͍·ͨ͠ɻͦ͜Ͱࠓ ճٕज़ॻయ 17 ΛకۦಈʹɺจΛಡΈਐΊΔ͜ͱʹ͠·ͨ͠ɻ 3.2 ຊষʹ͍ͭͯ ຊষͰɺ Google ͕ެ։͍ͯ͠Δ ʮ Cloud Spanner: Googleʟ s Globally Distributed Database*3ʯ ͱ͍͏จΛಡΜͰཧղͨ͠༰Λ·ͱΊ·͢ɻதͰɺSpanner ͕ਫฏεέʔϥϏϦςΟͱ֎෦ ߹ੑͷ྆ํΛಛͱͯ࣋ͪ͠߹Θ͍ͤͯΔ͘͠ΈʹϑΥʔΧε͠ɺҎԼͷΑ͏ͳΓޱͰհ͍ͯ͠ ͖·͢ɻ *1 TIPSTARʢςΟοϓελʔʣެࣜαϙʔταΠτ: https://about.tipstar.com *2 Spanner: ৗ ࣌ Ք ಇ Ͱ ɺ࣮ ࣭ త ʹ ແ ੍ ݶ ͷ ε έ ʔ Ϧ ϯ ά Λ උ ͑ ͨ σ ʔ λ ϕ ʔ ε | Google Cloud https://cloud.google.com/spanner?hl=ja *3 Spanner: Google’s Globally-Distributed Database: https://research.google/pubs/spanner-googles-globally- distributed-database-2/ 51 -
ୈ 3 ষ Cloud Spanner ͷ͘͠ΈΛཧղ͢Δ 3.3 Cloud SpannerɾNewSQL ͱ
• ਫฏεέʔϥϏϦςΟΛఏڙ͢Δ Cloud Spanner ͷΞʔΩςΫνϟ • Cloud Spanner ͷ֎෦߹ੑΛࢧ͑Δ͘͠Έ ཧղΛਂԽ͢ΔͨΊʹɺݱࡏ Google ͕ఏڙ͍ͯ͠Δ Spanner ͷυΩϡϝϯτ΄͔ͷํ͕ެ։ ͞Ε͍ͯΔղઆࢀߟʹ͠·ͨ͠ɻࢀߟʹ͍͍ͤͯͨͩͨ͞هࣄɾGoogle ͷυΩϡϝϯτɺ࠷ޙ ʹ·ͱΊͯհ͍͖ͤͯͨͩ͞·͢ɻ ͞·͟·ͳใΛͱʹཧղΛਐΊͨͨΊɺ෦తʹจͰهࡌ͞Ε͍ͯΔ༰ΑΓ࠷৽ͷใ Λհͯ͠͠·͍ͬͯΔՄೳੑ͕͋Γ·͢ɻ·ͨɺޡͬͨ༰Λհͯ͠͠·͍ͬͯΔ෦͋Δ͔ ͠Ε·ͤΜɻେͳ৺ͰຊষΛಡΈਐΊ͍͚ͯͨͩΕ͍Ͱ͢ɻ 3.3 Cloud SpannerɾNewSQL ͱ Cloud Spanner Spanner ͱɺGoogle ͕ఏڙ͢ΔϑϧϚωʔδυܕͷϦϨʔγϣφϧσʔλϕʔεαʔϏεͰɺ NewSQL ͱݺΕΔσʔλϕʔεͷ 1 ͭͰ͢ɻ NewSQL NewSQL ͱɺैདྷͷ RDBMS ͕࣋ͭ ACID ಛੑ SQL ͷڧྗͳΫΤϦػೳΛҡ࣋͠ͳ͕Βɺ NoSQL ͕ಘҙͱ͢ΔεέʔϥϏϦςΟͱύϑΥʔϚϯεͷ՝Λղܾ͢ΔͨΊʹੜ·Εͨσʔλ ϕʔεٕज़Ͱ͢ɻ۩ମతʹҎԼͷΑ͏ͳಛ͕͋Γ·͢ɻ • SQL αϙʔτ: SQL Λ༻ͨ͠ΫΤϦ͕ՄೳͰɺैདྷͷ RDBMS ͱಉ༷ͷૢ࡞ײΛ࣋ͪ ·͢ɻ • εέʔϥϏϦςΟ: NoSQL σʔλϕʔε͕ಘҙͱ͢ΔਫฏεέʔϥϏϦςΟʢσʔλΛଟ͘ ͷϊʔυʹࢄͯ֨͠ೲ͠ɺෛՙࢄΛߦ͏ʣ͕ՄೳͰ͢ɻ • ڧ߹ੑͷఏڙ: ෳϊʔυؒͰͷσʔλͷҰ؏ੑ͕֬อ͞Ε·͢ɻ • ACID τϥϯβΫγϣϯͷαϙʔτ: τϥϯβΫγϣϯॲཧʹ͓͍ͯڧྗͳҰ؏ੑͱ৴པੑ ͕อূ͞Ε͍ͯ·͢ɻ 3.4 ਫฏεέʔϥϏϦςΟΛఏڙ͢ΔΞʔΩςΫνϟ Spanner κʔϯͷηοτͱͯ͠ฤ͞Ε͓ͯΓɺݱࡏʢ2024/10 ݄ʣબͰ͖Δ Spanner ͷ࠷ খߏɺγϯάϧϦʔδϣϯͰ 3 ͭͷκʔϯʹϨϓϦΧΛ࣋ͭߏͰ͢*4ɻκʔϯͷηοτɺ σʔλΛϨϓϦέʔγϣϯͰ͖ΔϩέʔγϣϯͷηοτͰ͋Γ·͢ɻ֤κʔϯʹ 1 ͭͷκʔϯϚ *4 Cloud Spanner ͷ Ϛ ϧ ν Ϧ ʔ δ ϣ ϯ ߏ ʹ ͭ ͍ ͯ ཧ ղ ͢ Δ | Google Cloud ެ ࣜ ϒ ϩ ά: https://cloud.google.com/blog/ja/topics/developers-practitioners/demystifying-cloud-spanner-multi- region-configurations 52 -
ୈ 3 ষ Cloud Spanner ͷ͘͠ΈΛཧղ͢Δ 3.4 ਫฏεέʔϥϏϦςΟΛఏڙ͢ΔΞʔΩςΫνϟ ελͱඦ͔ΒઍͷεύϯαʔόɺϩέʔγϣϯϓϩΩγ͕ଘࡏ͠·͢ɻ ʢຊষ
5 અʹκʔϯͷશମ ૾Λࣔ͢ਤΛܝࡌ͍ͯ͠·͢ɻઌʹݟ͍͍ͯͨͩͨํ͕ཧղ͕ਐΉ͔͠Ε·ͤΜɻ ʣ ͦΕͧΕׂΛҎԼʹࣔ͠·͢ɻ • κʔϯϚελ ʢ Zonemaster ʣ ɿκʔϯͷσʔλཧΛߦ͍·͢ɻσʔλͷஔεύϯαʔό ͷλϒϨοτ (ׂ͞Εͨσʔλ) ͷׂΓͯΛཧ͢ΔͨΊͷத৺తͳׂΛՌͨ͠·͢ɻ • εύϯαʔόʢ Spanserver ʣ ɿෳͷλϒϨοτΛཧ͠·͢ɻ • ϩέʔγϣϯϓϩΩγʢ Location Proxy ʣ ɿΫϥΠΞϯτͷσʔλϦΫΤετΛదͳεύ ϯαʔόʹϧʔςΟϯά͢ΔͨΊɺޮతʹσʔλΞΫηεΛॲཧ͠·͢ɻ Spanner ͷσʔλϞσϧͱεύϯαʔόʹ͍ͭͯઆ໌͠·͢ɻ σʔλϞσϧ Spanner ͷσʔλϞσϧͱ࣮ʹ͍ͭͯհ͠·͢ɻ Spanner ɺσʔλϞσϧͱͯ͠εΩʔϚఆٛ͞ΕͨηϛϦϨʔγϣφϧςʔϒϧɺΫΤϦݴޠɺ ͓Αͼ൚༻τϥϯβΫγϣϯΛΞϓϦέʔγϣϯʹఏڙ͠·͢ɻ࣮ͷ؍ͰɺσΟϨΫτϦͱݺ ΕΔόέοτநԽΛαϙʔτ͍ͯ͠·͢ɻσΟϨΫτϦɺ͋ΔಛఆͷΩʔൣғʹରԠ͢Δσʔ λΛάϧʔϓԽͨ͠ͷͰ͢ɻσΟϨΫτϦΛ୯Ґʹσʔλׂ͕͞Ε͍ͯΔͨΊɺσΟϨΫτϦͷ αϙʔτʹΑͬͯΞϓϦέʔγϣϯɺΩʔͷબʹΑͬͯؔ࿈͢Δσʔλ͕ཧతʹۙ͘ʹอଘ͞ ΕΔΑ͏੍ޚ͕ՄೳʹͳΓ·͢ɻ Spanner ɺετϨʔδγεςϜͱͯ͠ Colossus*5 ͱ͍͏ࢄϑΝΠϧγεςϜΛར༻͠σʔλ ϞσϧΛ࣮͍ͯ͠·͢ɻσΟϨΫτϦͷσʔλ̍ͭҎ্ͷλϒϨοτͱ͍͏σʔλߏʹׂ͞ ΕɺColossus ʹཧ͞Ε·͢ɻͳ͓ɺλϒϨοτʹෳͷσΟϨΫτϦͷσʔλ͕֨ೲ͞ΕΔ ߹͋Γ·͢ɻλϒϨοτɺҎԼͷΑ͏ͳϚοϐϯάσʔλΛอ͍࣋ͯ͠·͢ɻ ( key:string, timestamp:int64 ) ˠ string Spanner λΠϜελϯϓΛσʔλʹׂΓ͓ͯͯΓɺλϒϨοτσʔλͷΩʔͱλΠϜελ ϯϓͷΈ߹ΘͤͰཧ͞Ε·͢ɻColossus ্ͰλϒϨοτɺλΠϜελϯϓΛΩʔʹෳόʔ δϣϯཧ͞Ε͍ͯΔͨΊɺkey-value ετΞͷΑ͏ʹݟ͑·͕͢Ϛϧνόʔδϣϯσʔλϕʔ εʹ͍ۙͰ͢ɻσʔλ͕ཧతʹه͞ΕΔ·ͰͷྲྀΕΛ؆୯ʹҎԼͷਤ 3.1 ʹࣔ͠·͢ɻຊདྷɺ σΟϨΫτϦΛεύϯαʔόʹׂΓͯΔκʔϯϚελɺσΟϨΫτϦɺλϒϨοτͷཧతͳҠಈ Λ࣮ߦ͢ΔόοΫάϥϯυλεΫɺཧతͳཧΛߦ͏εύϯαʔόͳͲ͕ଘࡏ͍ͯ͠·͢ɻ *5 A peek behind Colossus, Googleʟs file system | Google Cloud Blog: https://cloud.google.com/blog/products/storage-data-transfer/a-peek-behind-colossus-googles-file- system?hl=en 53 -
ୈ 3 ষ Cloud Spanner ͷ͘͠ΈΛཧղ͢Δ 3.4 ਫฏεέʔϥϏϦςΟΛఏڙ͢ΔΞʔΩςΫνϟ ਤ 3.1:
Spanner ͷσʔλϞσϧ σʔλͷಡΈऔΓ Spanner ʹ͓͚ΔηϛϦϨʔγϣφϧςʔϒϧɺओΩʔྻ͔ΒඇओΩʔྻͷϚοϐϯάΛఆ ͍ٛͯ͠·͢ɻςʔϒϧͷใ͔ΒɺλϒϨοτͷΩʔͱ Colossus ্ͷσʔλ͔ΒྻͷϚοϐϯ ά͕ಘΒΕ·͢ɻσʔλಡΈऔΓͷࡍʹɺςʔϒϧͱλΠϜελϯϓΛࢦఆ͢Δ͜ͱͰదͳλϒ Ϩοτ͔ΒσʔλΛಡΈऔΔ͜ͱ͕ՄೳʹͳΓ·͢ɻ σΟϨΫτϦͷσʔλྔͷ૿Ճ σΟϨΫτϦɺσʔλͷϩέʔγϣϯͷ୯ҐͰ͢ɻσΟϨΫτϦͷͯ͢ͷσʔλɺಉ͡Ϩ ϓϦέʔγϣϯߏΛ࣋ͪ·͢ɻޙड़͢Δ paxos άϧʔϓؒͰσʔλΛҠಈ͢ΔͱɺσΟϨΫτϦ ͝ͱʹҠಈ͠·͢ɻSpanner ɺσΟϨΫτϦ͕େ͖͘ͳΓ͗͢ΔͱɺσΟϨΫτϦΛෳͷϑϥά ϝϯτͱ͍͏୯Ґʹׂ͠ɺσʔλͷཧΛෳͷεύϯαʔόʹࢄ͠·͢ɻϑϥάϝϯτ B πϦʔϥΠΫͳߏͰཧ͞Ε·͢ɻσΟϨΫτϦ·Ͱͷ B πϦʔߏͱɺσΟϨΫτϦʢϑϥά ϝϯτʣͷ B πϦʔߏͱ͍͏ 2 ஈ֊ͷ֊తͳσʔλཧ͕ߦΘΕ͍ͯΔͱࢲཧղ͍ͯ͠·͢ɻ εύϯαʔό ֤εύϯαʔόɺઌड़ͨ͠λϒϨοτ 100ʙ1000 ݸͷॲཧΛ୲͠·͢ɻϨϓϦέʔγϣϯΛ ࣮ݱ͢ΔͨΊɺεύϯαʔό֤λϒϨοτ͝ͱʹ paxos εςʔτϚγϯΛ࣮͍ͯ͠·͢ɻκʔ ϯ͝ͱʹಉ͡λϒϨοτͷϨϓϦΧΛཧ͢Δεύϯαʔό͕ଘࡏ͠ɺޓ͍ʹ paxos Λར༻ͯ͠߹ ҙܗΛߦ͍ɺϨϓϦέʔγϣϯΛ࣮ݱ͠·͢ɻ 54 -
ୈ 3 ষ Cloud Spanner ͷ͘͠ΈΛཧղ͢Δ 3.5 ֎෦߹ੑΛࢧ͑Δ͘͠Έ 3.5 ֎෦߹ੑΛࢧ͑Δ͘͠Έ
ࢲ͕จΛಡΉதͰײͨ͡ͷɺ Spanner ͷಛੑͰ͋Δʮ֎෦߹ੑʯͷॏཁੑͰ͢ɻਫฏεέʔ ϧ͢ΔࢄγεςϜ্Ͱ֎෦߹ੑ͕࣮ݱ͍ͯ͠Δ͘͠Έ͍ͭͯࢲͷཧղΛ౿·͑ͭͭհ͠·͢ɻ ֎෦߹ੑʢ External Consistenc ʣɺࢄσʔλϕʔεγεςϜʹ͓͚Δ࠷ݫີͳ߹ੑ ϨϕϧͰ͢ɻෳͷτϥϯβΫγϣϯΛ࣮ߦͨ͠߹ɺॱংΛਖ਼֬ʹө͢Δ͜ͱΛอূ͠·͢ɻͭ ·Γɺ͋ΔτϥϯβΫγϣϯ A ͕τϥϯβΫγϣϯ B ΑΓઌʹίϛοτ͞Εͨ߹ɺࢄ͞Εͨ σʔλʹͲ͔͜ΒΞΫηεͯ͠ಉ݁͡Ռ͕ಘΒΕΔ͜ͱΛอূ͢ΔಛੑͰ͢ɻ֎෦߹ੑΛ࣮ݱ͢ ΔͨΊɺTrueTime API ɺpaxos ɺτϥϯβΫγϣϯʹ͍ͭͯ৮ΕΔඞཁ͕͋Γ·͢ɻ TrueTime API TrueTime API ɺࢄγεςϜͷͯ͢ͷϊʔυʹରͯ͠ݫີͳ࣌ؒใΛఏڙ͠·͢ɻ TrueTime ࣌ࠁΛ୯ҰͷͰͳ͘ɺ্ݶͱԼݶΛ࣋ͭ࣌ؒൣғʢ TTinterval ʣͱͯ͠දݱ͠· ͢ɻ͜ͷ࣌ؒൣғɺTrueTime Λऔಘͨ͠ʢ TT.Now Λݺͼग़ͨ͠ʣ࣌ؒΛؚΉ͜ͱ͕อূ͞Ε ͍ͯ·͢ɻ TrueTime ͕࣌ؒൣғͱͯ࣌ؒ͠ใΛఏڙ͢Δͷɺαʔόؒͷ࣌ࠁͷζϨΛߟྀ͢ΔͨΊͰ ͢ɻTrueTime ɺσʔληϯλʔ͝ͱʹଘࡏ͢ΔλΠϜϚελϚγϯηοτʢ GPS ࣌ܭͱݪࢠ࣌ ܭͷηοτʣͱɺϚγϯ͝ͱʹଘࡏ͢ΔλΠϜεϨʔϒσʔϞϯʹΑ࣮ͬͯ͞Ε͍ͯ·͢ɻλΠϜ εϨʔϒσʔϞϯɺࣗͷλΠϜϚελϚγϯηοτΛఆظతʹࢀর͠ɺαʔόͷϩʔΧϧʹଘࡏ ͢ΔγεςϜΫϩοΫͱಉظͤ͞·͢ɻ TrueTime ͷ࣌ؒൣғɺ ʢ[ t-Џ, t+ Џ ]ʣͷΑ͏ʹද͞Εɺt ͕ʮݱࡏ࣌ࠁͷਪఆʯͰ͋Γɺ TrueTime ͕γεςϜΫϩοΫΛͱʹࢉग़͓͓ͨ͠Αͦͷ࣌ࠁͰ͢ɻЏ ʮෆ࣮֬ੑͷൣғʯͰ ͋ΓɺΫϩοΫʹର͢ΔζϨෆਖ਼֬͞Λද͠·͢ɻЏͷൣғʹ࣮ࡍͷਖ਼֬ͳ࣌ࠁ͕ଘࡏ͍ͯ͠Δ ͱਪఆͰ͖·͢ɻЏ ͷɺ֤ϙʔϦϯάͷ֤ײ֮ʹ͓͍ͯ̍ʙ̓ϛϦඵͷؒͰมԽ͠·͢ɻ΄ ͱΜͲͷ߹ Џ ̐ϛϦඵͰϙʔϦϯάͷײ֮ 30 ඵͰ͢ɻ͜ͷΑ͏ʹࢄγεςϜؒͷΫϩο ΫͷζϨΛߟྀ࣌ؒ͠ൣғΛฦ͢͜ͱͰɺαʔόؒͰτϥϯβΫγϣϯͷ࣮ߦॱংσʔλͷҰ؏ੑ Λอূ͢ΔͨΊͷൺֱ͕ՄೳʹͳΓ·͢ɻ Paxos paxos ɺࢄγεςϜͰಈ࡞͢ΔίϯϐϡʔλͲ͏͕͠ɺ߹ҙܗΛߦ͏ͨΊͷϓϩτίϧʢΞ ϧΰϦζϜʣͰ͢ɻࢄγεςϜͰɺෳͷαʔό͕ڠྗͯ͠σʔλΛॲཧ͠·͕͢ɺͦΕΒ͕ಉ ܾ͡ఆʢ߹ҙʣʹࢸΔඞཁ͕͋Γ·͢ɻpaxos ɺωοτϫʔΫͷԆαʔόো͕ൃੜͨ͠߹ ͰɺҰ؏ͨ͠߹ҙΛಘΔ͜ͱ͕Ͱ͖Δ͘͠ΈͰ͢ɻ paxos ͰɺࢄγεςϜͷαʔό͕ɺಛఆͷఏҊʹରͯ͠߹ҙΛಘΔͨΊʹ 3 ͭͷׂΛՌͨ ͠·͢ɻ 55 -
ୈ 3 ষ Cloud Spanner ͷ͘͠ΈΛཧղ͢Δ 3.5 ֎෦߹ੑΛࢧ͑Δ͘͠Έ • ϓϩϙʔβʢ
Proposer ʣ: ఏҊΛߦ͏ׂͰ͢ɻ ʮ͜ͷσʔλΛ৽͍͠ਖ਼͍͠σʔλͱͯ͠࠾ ༻͠Α͏ʯͱ͍͏ఏҊΛ΄͔ͷαʔόʹૹ৴͠·͢ɻ • ΞΫηϓλʢ Acceptor ʣ: ఏҊΛड͚औΓɺ߹ҙ͢Δ͔Ͳ͏͔Λܾఆ͢ΔׂͰ͢ɻෳͷ ΞΫηϓλ͕ಉҙ͢ΕɺఏҊ͕ঝೝ͞Ε·͢ɻ • ϥʔφʔʢ Learner ʣ: ߹ҙܗͷϓϩηεʹࢀՃͤͣɺ߹ҙཱ͕ͨ͠ఏҊʢܾఆʣΛֶ ͼɺγεςϜશମʹө͠·͢ɻ Spanner γϯάϧϦʔδϣϯߏͷ߹ɺ࠷Ͱ̏ͭͷϨϓϦΧΛҟͳΔκʔϯʹ࣋ͪ·͢ɻ ͜ͷ 3 ͭͷϨϓϦΧΛཧ͢ΔεύϯαʔόͰ paxos άϧʔϓΛߏ͠·͢ɻ͏͕ͪ̍ͭϦʔμʔ ͱͳΓɺΓ 2 ͭϑΥϩϫʔͱͳΓ·͢ɻϦʔμʔϓϩϙʔβͱͯ͠΄͔ͷϨϓϦΧʹσʔλͷ ߋ৽ΛఏҊ͠ɺ΄͔ͷϨϓϦΧʢ͜ͷ߹ɺΞΫηϓλʣ͔Βಉҙ͞Εͨ߹ʹσʔλΛߋ৽͠· ͢ɻ߹ҙܗʹϑΥϩϫʔͷաͷ߹ҙ͕ඞཁͰɺ3 ͭͷεύϯαʔόͰ paxos άϧʔϓΛߏ ͍ͯ͠Δ߹ 2 ͭͷϑΥϩϫʔ͔Βͷ߹ҙ͕ඞཁʹͳΓ·͢ɻաͷ߹ҙ͕ಘΒΕΔͱϦʔμʔ ϑΥϩϫʔʹ߹ҙΛ௨͠ɺσʔλʹө͠·͢ɻ ຊষͷୈ 4 અʹͯհͨ͠ Spanner ͷΞʔΩςΫνϟͱຊઅͷ paxos ΛؚΊͯશମతͳΠϝʔδ ΛҎԼʹࣔ͠·͢ɻ ਤ 3.2: Spanner ͷΞʔΩςΫνϟ τϥϯβΫγϣϯ Spanner ɺߴ͍εέʔϥϏϦςΟͱڧ͍σʔλ߹ੑΛཱ྆͢ΔͨΊʹɺಠࣗͷτϥϯβΫγϣ ϯॲཧΛఏڙ͍ͯ͠·͢ɻલड़ͷ֎෦߹ੑ TrueTime APIɺpaxos ϓϩτίϧͱΈ߹ΘͤΔ ͜ͱͰɺࢄڥͰҰ؏ੑͷ͋Δσʔλૢ࡞͕Մೳͱͳ͍ͬͯ·͢ɻSpanner ͕αϙʔτ͢Δτϥ ϯβΫγϣϯʹ͍ͭͯհ͠·͢ɻ 56 -
ୈ 3 ষ Cloud Spanner ͷ͘͠ΈΛཧղ͢Δ 3.5 ֎෦߹ੑΛࢧ͑Δ͘͠Έ จͰɺҎԼͷτϥϯβΫγϣϯͷ෦ಈ࡞ʹ͍ͭͯड़ΒΕ͍ͯ·ͨ͠ɻ •
Read-Write Transactionɿݫີͳ̎ϑΣʔζϩοΫͱ paxos Λར༻ͨ͠ॻ͖ࠐΈૢ࡞ • Snapshot Transactionɿॻ͖ࠐΈͷͳ͍τϥϯβΫγϣϯͰɺγεςϜ͕બΜͩλΠϜελϯ ϓͰ࣮ߦ͞ΕɺϩοΫΛ༻ͤͣʹσʔλΛಡΈऔΔૢ࡞ ͳ͓ɺGoogle ͕ެ։͍ͯ͠Δ Spanner ͷτϥϯβΫγϣϯʹ͍ͭͯհ͢ΔυΩϡϝϯτ*6Λݟ Δͱɺݱࡏ Spanner ҎԼͷτϥϯβΫγϣϯΛαϙʔτ͍ͯ͠ΔΑ͏Ͱ͢ɻ • ಡΈऔΓ / ॻ͖ࠐΈτϥϯβΫγϣϯʢRead-write transactionsʣ • ಡΈऔΓઐ༻τϥϯβΫγϣϯʢRead-only transactionsʣ • ύʔςΟγϣϯԽ DML τϥϯβΫγϣϯʢPartitioned DML transactionsʣ ࠓճจͰհ͞Ε͍ͯͨτϥϯβΫγϣϯʹϑΥʔΧε͠·͢ɻ Read-Write Transaction ॻ͖ࠐΈΛߦ͏τϥϯβΫγϣϯͰݫີͳ̎ϑΣʔζϩοΫΛ༻͍ͯ͠·͢ɻ·ͣτϥϯβ ΫγϣϯͰඞཁ͕͋ΔσʔλͷϩοΫΛऔಘ͠·͢ɻͯ͢ͷϩοΫ͕औಘ͞ΕΔͱɺϩοΫղ์· ͰλΠϜελϯϓͷׂΓ͕ͯՄೳʹͳΓ·͢ɻΫΤϦʹै͍ɺඞཁʹԠͯ͡σʔλͷಡΈऔΓͱม ߋΛߦ͍·͢ɻSpanner Ͱ̎ϑΣʔζίϛοτΛ࠾༻͓ͯ͠Γɺσʔλͷૢ࡞͕ྃ͢Δͱίϛο τ४උʹೖΓ·͢ɻίϛοτ४උ͕Ͱ͖Δͱ TrueTime API ͔Β࣌ؒൣғͷऔಘɺpaxos ʹΑΔ߹ ҙܗΛߦ͍·͢ɻ߹ҙ͕ಘΒΕΔͱίϛοτΛߦ͍ɺϩοΫΛղ์͠·͢ɻίϛοτΛߦ͏ࡍʹ औಘͨ࣌ؒ͠ൣғͷ࠷େʢ࠷ϩʔΧϧΫϩοΫͷζϨ͕େ͖͔ͬͨ߹ʹߟ͑͏Δ͍࣌ࠁʣ͕ ܦա͢Δ·Ͱίϛοτͷ࣮ࢪΛػ͠·͢ɻ࣌ؒൣғͷ࠷େ·Ͱػ͢Δ͜ͱͰɺ࣮֬ʹ΄͔ͷτ ϥϯβΫγϣϯૢ࡞ͱॏෳ͕ൃੜ͠ͳ͍͜ͱΛอূ͠·͢ɻίϛοτ͞ΕΔ·ͰɺτϥϯβΫγϣ ϯʹରͯ͠λΠϜελϯϓׂ͕ΓͯΒΕΔ͜ͱͳ͘ɺผͷτϥϯβΫγϣϯʹಡΈऔΒΕΔ͜ͱ ͋Γ·ͤΜɻ Snapshot Transaction Spanner ɺτϥϯβΫγϣϯσʔλʹλΠϜελϯϓׂ͕ΓͯΒΕ͓ͯΓτϥϯβΫγϣ ϯͰಡΈऔΔσʔλࢄͯ͠อଘ͞Ε͍ͯΔͨΊɺͦΕͧΕͷσʔλΛ࣋ͭϊʔυ͝ͱʹσʔλ ͷಡΈࠐΈରͱ͢ΔεφοϓγϣοτͷλΠϜελϯϓΛࢦఆ͢Δඞཁ͕͋Γ·͢ɻରͱͳΔ σʔλ͕ 1 ͭͷ paxos άϧʔϓʹଘࡏ͢Δ߹ɺ࠷ޙʹ ίϛοτ͞Εͨॻ͖ࠐΈͷλΠϜελϯϓ ΛͱʹεφοϓγϣοτͷಡΈࠐΈΛߦ͍·͢ɻσʔλ͕ෳͷ paxos άϧʔϓʹΘͨΔ߹ɺ TrueTime API ͔Β ݱࡏͷ࣌ࠁʹ͓͚Δ࠷େͷՄೳͳλΠϜελϯϓΛऔಘ֤͠ paxos άϧʔϓ͔ ΒεφοϓγϣοτͷಡΈࠐΈΛߦ͍·͢ɻ *6 A peek behind Colossus, Googleʟs file system | Google Cloud Blog: https://cloud.google.com/spanner/docs/transactions?hl=ja#read-only_transactions 57 -
ୈ 3 ষ Cloud Spanner ͷ͘͠ΈΛཧղ͢Δ 3.6 ऴΘΓʹ 3.6 ऴΘΓʹ
ຊষͰɺGoogle ͕ެ։͍ͯ͠Δʮ Cloud Spanner: Googleʟs Globally Distributed Database ʯΛࢲ͕ಡΈɺ༰ʹ͍ͭͯ·ͱΊ·ͨ͠ɻSpanner ɺ͕ࣗܞΘ͍ͬͯΔϓϩμΫτͰར༻ͯ͠ ͍ΔͨΊɺ͋Δఔͷ෦తͳ͘͠Έʹରͯࣝ͠Λ࣋ͪ߹Θ͍ͤͯΔͭΓͰ͕ͨ͠ɺॳΊͯ Δ͜ͱ͕ଟ͘ Spanner ʹର͢Δཧղ͕ਂ·Γ·ͨ͠ɻ ಛʹɺຊষ 4 અͰ৮ΕͨΞϓϦέʔγϣϯͰѻ͏σʔλ͕ͲͷΑ͏ʹཧతʹཧ͞Ε͍ͯΔͷ͔ ֶͼʹͳΓ·ͨ͠ɻීஈεΩʔϚΠϯσοΫεΛઃܭ͢ΔࡍʹɺϗοτεϙοτͷճආͩͬͨΓ ςʔϒϧؒͷΠϯλϦʔϒʹؔͯ͠ࢥߟΛ८Βͤ·͕͢ɺࠓޙΑΓೲಘײΛ࣋ͬͯऔΓΊΔͱײ ͡·ͨ͠ɻ·ͨɺSpanner ͷΞʔΩςΫνϟ·ͬͨ͘Βͳ͔ͬͨͨΊڵຯਂ͔ͬͨͰ͢ɻ Google ຊষͰऔΓ্͛ͨจҎ֎ʹɺSpanner BigtableɺBigQuery ͷจΛެ։ͯ͠ ͍·͢ɻͥͻؾʹͳͬͨํಡΜͰΈ͍͔͕ͯͰ͠ΐ͏͔ʁ Spanner ͕։ൃ͞Εͨഎܠʹɺ Bigtable ͱ MegaStore ͕͍ؔͯ͠Δͱͷ͜ͱͰɺࢲ࣍ Bigtable ͷจΛಡΜͰΈ͍ͨͱࢥ ͍·͢ɻ 3.7 ࢀߟ จΛಡΈਐΊΔதͰࢀߟʹ͍͍ͤͯͨͩͨ͞هࣄΛհ͍͖ͤͯͨͩ͞·͢ɻ • Spanner*7 • Google ͷ Spanner ʹؔ͢Δจͷ༁ 1/6*8 • Google ͷ Spanner ʹؔ͢Δจͷ༁ 2/6*9 • Google ͷ Spanner ʹؔ͢Δจͷ༁ 3/6*10 • Spanner: TrueTime ͱ֎෦߹ੑ*11 • Cloud Spanner ͕֎෦߹ੑΛఏڙ͢Δཧ༝*12 *7 Spanner #ϙΤϜ - Qiita@: https://qiita.com/kumagi/items/7dbb0e2a76484f6c522b *8 Google ͷ Spanner ʹؔ͢Δจͷ༁ 1/6 - 451 Unavailable For Legal Reasons: https://blog.game- programmer.jp/entry/2017/02/28/141653 *9 Google ͷ Spanner ʹؔ͢Δจͷ༁ 2/6 - 451 Unavailable For Legal Reasons: https://blog.game- programmer.jp/entry/2017/03/02/201647 *10 Google ͷ Spanner ʹؔ͢Δจͷ༁ 3/6 - 451 Unavailable For Legal Reasons: https://blog.game- programmer.jp/entry/2017/03/03/194051 *11 Spanner: TrueTime ͱ ֎ ෦ ߹ ੑ: https://cloud.google.com/spanner/docs/true-time-external- consistency?hl=ja#2 *12 Cloud Spanner ͕֎෦߹ੑΛఏڙ͢Δཧ༝: https://cloud.google.com/blog/ja/products/gcp/why-you- should-pick-strong-consistency-whenever-possible 58 -
ୈ 4 ষ ೖ Bzlmod ຊߘͰɺϏϧυπʔϧʮBazel*1ʯʹόʔδϣϯ 5 ͔Βࢼݧతʹಋೖ͞Ε࢝Ίͨɺ֎෦ґଘύο έʔδͷཧγεςϜ Bzlmod
ʹ͍ͭͯհ͠·͢ɻ ͱͱ Bazel Ͱɺ֎෦ґଘύοέʔδΛཧ͢Δͷʹ WORKSPACE ϑΝΠϧΛར༻͍ͯ͠·͠ ͨɻ͜ͷϑΝΠϧʹɺ͍͍ͨ֎෦ύοέʔδΛΠϯϙʔτ͢ΔΑ͏ͳίʔυΛྻڍ͢ΔΑ͏ͳΠ ϝʔδͰ͢ɻ͜ͷํ๏ͰɺґଘύοέʔδؒͷґଘؔΛ͏·͘ղܾͯ͘͠Εͳ͍ͨΊɺґଘύο έʔδͷόʔδϣϯΛ্͛Δ͚ͩͰϏϧυͰ͖ͳ͘ͳͬͨΓͯ͠·ͨ͠ɻͦΕΛղܾ͢ΔͨΊʹొ ͨ͠ͷ͕ Bzlmod Ͱ͢ɻBzlmod όʔδϣϯ 5 ͔Βࢼݧతʹಋೖ͞Εɺ࣍ͷϝδϟʔόʔδϣϯ ΞοϓͰ͋Δόʔδϣϯ 8 Ͱඪ४ʹͳΓɺόʔδϣϯ 9 ͔Β WORKSPACE ϑΝΠϧΛར༻ͨ͠ैདྷ ͷํ๏͕আ͞ΕΔ༧ఆͰ͢ɻ ͦ͜ͰຊߘͰɺ2024 10 ݄࣌Ͱͷ҆ఆόʔδϣϯͰ͋Δ Bazel όʔδϣϯ 7 Λར༻ͨ͠Ξ ϓϦέʔγϣϯΛ࡞Γͳ͕ΒɺBzlmod ʹ͍ͭͯհ͠·͢ɻ 4.1 αϯϓϧϓϩάϥϜ ຊʹೖΔલʹɺαϯϓϧϓϩάϥϜʹ͍ͭͯհ͠·͢ɻࠓճɺGo ͱ Elm Λ༻͍ͨ؆қత ͳ Web ΞϓϦέʔγϣϯΛαϯϓϧίʔυͱ͍ͯ͠·͢ɻεςοϓόΠεςοϓʹ֦ு͢Δ͜ͱ ͰɺঃʑʹొਓΛ૿͠ͳ͕Β Bazlmod ʹ͍ͭͯհ͠·͢ɻ 1. HTML ϑΝΠϧΛฦ͚ͩ͢ͷ Go ΞϓϦέʔγϣϯ 2. Echo Λͬͨ Go ΞϓϦέʔγϣϯ 3. Bazel ΛͬͯίϯςφΠϝʔδΛ࡞Δ 4. Elm Λͬͨ Web ϖʔδΛฦ͢ 5. ͓·͚ɿ OpenAPI Λͬͨগ͠ϦονͳΞϓϦέʔγϣϯ GitHub ϦϙδτϦ matsubara0507/sample-go-bzlmod Ͱ͢*2ɻ *1 https://bazel.build/ *2 https://github.com/matsubara0507/sample-go-bzlmod 59 -
ୈ 4 ষ ೖ Bzlmod 4.2 Bazel 4.2 Bazel ·ͣ࠷ॳʹɺBazel
ʹ͍ͭͯհ͠·͢ɻBazel ࣍ͷΑ͏ͳಛΛ࣋ͭϏϧυπʔϧͰ͢ɻ • ϏϧυΛಠࣗͷαϯυϘοΫεڥͷதͰߦ͏ • ґଘؔΛ໌ه͢ΔͨΊΩϟογϡޮ͕ྑ͍ • Starlark ͱ͍͏ Python αϒηοτͳݴޠͰ֦ுՄೳ ґଘ͕ؔ໌ه͞Ε͍ͯΔ͜ͱʹΑͬͯɺมߋʹରͯ͠࠷খݶͷϏϧυͰՌΛߏஙͰ͖ͨΓɺ ฒྻ࣮ߦʹΑͬͯߴԽͨ͠ΓͰ͖·͢ɻཪΛฦ͢ͱɺ͜ͷґଘؔΛ໌ه͢Δͷ͕͍ͨΜͰ͢ ͕ɺύοέʔδʹΑͬͯࣗಈతʹґଘؔΛ໌هͨ͠෦Λੜͯ͘͠Ε·͢ɻ ֎෦ύοέʔδ ຊߘͷओͰ͋Δɺ֎෦ύοέʔδΛ͏͜ͱͰɺ؆୯ʹ͞·͟·ͳϓϩάϥϜͷϏϧυςε τͷ࣮ߦΛಉ͡ΠϯλϑΣʔεͰߦ͏͜ͱ͕Ͱ͖·͢ɻ֎෦ύοέʔδʹɺͨͱ͑࣍ͷΑ͏ͳ ͷ͕͋Γ·͢ɻ • bazelbuild/rules_go : Go ϓϩάϥϜͷϏϧυςετͳͲ*3 • bazel-contrib/rules_oci : OCI ίϯςφΠϝʔδͷϏϧυϓογϡͳͲ*4 • kczulko/rules_elm : Elm ϓϩάϥϜͷϏϧυςετͳͲ*5 • matsubara0507/rules_openapi : openapi-generator-cli Λ͑ΔΑ͏ʹ͢Δ*6 αʔυύʔςΟؚΊͯ͞·͟·ͳύοέʔδ͕͋Γ·͢ɻ ߏཁૉɾ༻ޠ Bazel ͷߏཁૉ༻ޠʹ͍ͭͯ؆୯ʹઆ໌͠·͢ɻ·ͣɺBazel Ͱ GitHub ϦϙδτϦͰཧ ͢ΔΑ͏ͳ୯ҐΛϫʔΫεϖʔεͱݺͼ·͢ɻ·ͨɺϏϧυ͢ΔͨΊͷઃఆ MODULE.bazel ͱ BU ILD.bazel ͷ 2 छྨͷϑΝΠϧهड़͍͖ͯ͠·͢ɻ • MODULE.bazel ɺϫʔΫεϖʔεͷϧʔτσΟϨΫτϦʹҰ͚ͭͩஔ͖·͢ɻґଘύοέʔ δॲཧܥɺར༻͢ΔϕʔεΠϝʔδͳͲϏϧυʹඞཁͳϦιʔεʹؔ͢ΔઃఆΛهड़͠ ·͢ɻ • BUILD.bazel ɺϫʔΫεϖʔεͷσΟϨΫτϦ 1 ͭͣͭஔ͘͜ͱ͕Ͱ͖·͢ɻͦͷσΟ ϨΫτϦͰϏϧυ͢ΔͷࣗମͷઃఆΛهड़͠·͢ɻ *3 https://github.com/bazelbuild/rules_go *4 https://github.com/bazel-contrib/rules_oci *5 https://github.com/kczulko/rules_elm *6 https://github.com/matsubara0507/rules_openapi 60 -
ୈ 4 ষ ೖ Bzlmod 4.3 Bzlmod ΛͬͨΞϓϦέʔγϣϯ։ൃ ྆ϑΝΠϧͰهड़͢ΔؔɾϝιουͷΑ͏ͳͷΛɺBazel Ͱϧʔϧͱݺͼ·͢ɻBUILD.b
azel Ͱهड़ͨ͠ϧʔϧɺbazel build ίϚϯυͰϏϧυ͕͞Ε·͢ɻ·ͨɺόΠφϦϑΝΠϧ ͷΑ͏ʹɺϏϧυͯ͠Ͱ͖ͨՌ͕࣮ߦՄೳͳ߹ bazel run ίϚϯυͰ࣮ߦ͢Δ͜ͱͰ͖ ·͢ɻ 4.3 Bzlmod ΛͬͨΞϓϦέʔγϣϯ։ൃ લड़ͨ͠௨ΓɺεςοϓόΠεςοϓʹ Go ΞϓϦέʔγϣϯΛ֦ு͍͖ͯ͠·͢ɻ·ͣɺґଘ ύοέʔδͳͲཧ͢Δ MODULE.bazel Λஔ͠·͢ɻ MODULE.bazel module( name = "sample-go-bzlmod", version = "1.0.0", ) ͜ͷϑΝΠϧʹه͍͖ͯ͠·͢ɻͪͳΈʹɺBazel όʔδϣϯ 5ʙ7 Ͱ Bzlmod Λ͏ʹ -- enable_bzlmod Φϓγϣϯ͕ඞཁͰ͢ɻ·ͨɺBazel Ͱ .bazelrc ϑΝΠϧΛར༻͢Δ͜ͱͰɺ σϑΥϧτͰ༩͢ΔΦϓγϣϯͳͲΛઃఆͰ͖·͢ɻ .bazelrc common --enable_bzlmod STEP1: HTML ϑΝΠϧΛฦ͚ͩ͢ͷ Go ΞϓϦέʔγϣϯ ·ͣɺHTML ϑΝΠϧΛฦ͚ͩ͢ͷΞϓϦέʔγϣϯͰ͢ɻBazel Ͱ Go ΛϏϧυ͢Δʹɺ rules_go ύοέʔδΛ͍·͢ɻ MODULE.bazel module( name = "sample-go-bzlmod", version = "1.0.0", ) bazel_dep(name = "rules_go", version = "0.50.1") bazel_dep(name = "gazelle", version = "0.39.1") go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk") go_sdk.download(version = "1.23.1") 61 -
ୈ 4 ষ ೖ Bzlmod 4.3 Bzlmod ΛͬͨΞϓϦέʔγϣϯ։ൃ bazel_dep ͰґଘύοέʔδΛهड़͍ͯ͠·͢ɻར༻Մೳͳύοέʔδ
Bazel Central Registry ͰௐΔ͜ͱ͕Ͱ͖·͢*7ɻgazelle ɺrules_go ʹؔ͢ΔϧʔϧΛࣗಈੜͯ͘͠ΕΔศརπʔϧ Ͱ͢*8ɻ࠷ޙͷ 2 ߦͰɺBazel Ͱ Go ίϯύΠϥΛར༻͢Δ͜ͱΛએݴ͍ͯ͠·͢ɻ લड़ͨ͠௨ΓɺMODULE.bazel ʹɺϏϧυ࣌ʹར༻͢ΔϦιʔεΛએݴ͢Δඞཁ͕͋Γ·͢ɻͦ ͷϦιʔεʹɺGo ίϯύΠϥͷΑ͏ͳॲཧܥؚ·ΕΔͨΊɺgo_sdk.download Ͱએݴ͍ͯ͠ ΔͷͰ͢ɻ͜͜Ͱએݴͨ͠ Go ίϯύΠϥɺ࣍ͷΑ͏ͳܗͰ࣮ߦͰ͖·͢ɻ % bazelisk run @rules_go//go -- mod init github.com/matsubara0507/sample-go-bzlmod INFO: Analyzed target @@rules_go~//go:go (7 packages loaded, 11370 targets configured). INFO: Found 1 target... Target @@rules_go~//go/tools/go_bin_runner:go_bin_runner up-to-date: bazel-bin/external/rules_go~/go/tools/go_bin_runner/bin/go INFO: Elapsed time: 1.030s, Critical Path: 0.03s INFO: 1 process: 1 internal. INFO: Build completed successfully, 1 total action INFO: Running command line: bazel-bin/.../bin/go mod init ... go: creating new go.mod: module github.com/matsubara0507/sample-go-bzlmod main.go go:embed ͢Δ index.html Λهड़ͨ͠Βɺ࣍ʹ BUILD.bazel ϑΝΠϧΛ༻ҙ͠· ͢ɻBUILD.bazel ʹɺGo όΠφϦΛϏϧυ͢ΔͨΊͷੜϧʔϧΛهड़͠·͢ɻGo όΠφϦͷ ੜϧʔϧɺrules_go ͷ go_binary ϧʔϧΛ͍·͕͢ɺgazelle Λ͑ࣗಈతʹੜͯ͠ ͘Ε·͢ɻ BUILD.bazel load("@gazelle//:def.bzl", "gazelle") gazelle(name = "gazelle") ͜ͷঢ়ଶͰ bazel run //:gazelle Λ࣮ߦ͢ΔͱɺBUILD.bazel ϑΝΠϧʹ main.go ͷத Λݩʹͯ͠ go_binary ͳͲ͕ੜ͞Ε·͢ɻ BUILD.bazel load("@gazelle//:def.bzl", "gazelle") load("@rules_go//go:def.bzl", "go_binary", "go_library") gazelle(name = "gazelle") go_library( *7 https://registry.bazel.build/ *8 https://github.com/bazelbuild/bazel-gazelle/ 62 -
ୈ 4 ষ ೖ Bzlmod 4.3 Bzlmod ΛͬͨΞϓϦέʔγϣϯ։ൃ name =
"sample-go-bzlmod_lib", srcs = ["main.go"], embedsrcs = ["index.html"], importpath = "github.com/matsubara0507/sample-go-bzlmod", visibility = ["//visibility:private"], ) go_binary( name = "sample-go-bzlmod", embed = [":sample-go-bzlmod_lib"], visibility = ["//visibility:public"], ) bazel build //:sample-go-bzlmod ͰϏϧυΛɺbazel run //:sample-go-bzlmod ͰϏϧ υ͔Β࣮ߦΛͰ͖·͢ɻ STEP2: Echo Λͬͨ Go ΞϓϦέʔγϣϯ ࣍ʹ Go ͷ֎෦ύοέʔδΛ Bazel Ͱ͍·͢ɻmain.go ΛɺEcho*9 Λͬͨܗʹॻ͖͑ͯɺ bazel run @rules_go//go -- mod tidy Λ࣮ߦͯ͠ go.mod Λߋ৽͠·͢ɻGo ͷґଘύοέʔ δΛ Bazel Ͱѻ͏ʹɺgo mod vendor Λ͏͔ɺMODULE.bazel ʹهड़͢Δඞཁ͕͋Γ·͢ɻ͜ ͜Ͱޙऀͷํ๏ΛऔΓ·͕͢ɺgazelle Λ͏͜ͱͰɺ΄ͱΜͲΛ go.mod ͔ΒੜͰ͖·͢ɻ MODULE.bazel module( name = "sample-go-bzlmod", version = "1.0.0", ) ... go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") go_deps.from_file(go_mod = "//:go.mod") ͜ͷঢ়ଶͰ bazel mod tidy Λ࣮ߦ͢Δͱɺgo.mod Ͱએݴ͞Ε͍ͯΔґଘύοέʔδΛ MODUL E.bazel ॻ͖Ճ͑ͯ͘Ε·͢ɻͦͷޙɺbazel run //:gazelle Λ࠶࣮ߦ͢Δͱ BUILD.bazel ͕ߋ৽͞Εͯɺbazel run //:sample-go-bzlmod ͕࣮ߦͰ͖·͢ɻ *9 https://echo.labstack.com/ 63 -
ୈ 4 ষ ೖ Bzlmod 4.3 Bzlmod ΛͬͨΞϓϦέʔγϣϯ։ൃ STEP3: Bazel
ΛͬͯίϯςφΠϝʔδΛ࡞Δ Bazel Ͱ rules_oci ͱ rules_pkg Λ͏͜ͱͰɺγϯϓϧͳίϯςφΠϝʔδΛ؆୯ʹ࡞Δ͜ ͱ͕Ͱ͖·͢ɻ·ͣɺґଘ͢Δ֎෦ύοέʔδͱɺϕʔεΠϝʔδͷऔಘΛ MODULE.bazel ه ड़͠·͢ɻ MODULE.bazel module( name = "sample-go-bzlmod", version = "1.0.0", ) ... bazel_dep(name = "rules_oci", version = "2.0.0") bazel_dep(name = "rules_pkg", version = "1.0.1") oci = use_extension("@rules_oci//oci:extensions.bzl", "oci") oci.pull( name = "distroless_base", image = "gcr.io/distroless/base-debian12", platforms = ["linux/amd64"], tag = "latest", ) distroless ͱ͍͏ͷɺGoogle ͕ఏڙ͍ͯ͠ΔίϯύΫτͳίϯςφΠϝʔδͰɺshell ύο έʔδϚωʔδϟʔ͢Βؚ·Ε·ͤΜ*10ɻ͜ͷঢ়ଶͰ·ͨ bazel mod tidy Λ࣮ߦͯ͠ɺMODULE. bazel Λߋ৽͓͖ͯ͠·͢ɻ͋ͱɺBUILD.bazel ࡞͢ΔίϯςφΠϝʔδͷϧʔϧΛهड़͠ ·͢ɻ BUILD.bazel ... load("@rules_pkg//pkg:tar.bzl", "pkg_tar") load("@rules_oci//oci:defs.bzl", "oci_image", "oci_load") pkg_tar( name = "tar", srcs = [":sample-go-bzlmod"], ) oci_image( name = "image", *10 https://github.com/GoogleContainerTools/distroless 64 -
ୈ 4 ষ ೖ Bzlmod 4.3 Bzlmod ΛͬͨΞϓϦέʔγϣϯ։ൃ base =
"@distroless_base", tars = [":tar"], entrypoint = ["/sample-go-bzlmod"], ) oci_load( name = "load", image = ":image", repo_tags = ["sample-go-bzlmod:latest"], ) bazel build //:image Ͱ OCI ίϯςφΠϝʔδͷϏϧυΛɺbazel run //:load Ͱ OCI ί ϯςφΠϝʔδΛಡΈࠐΜͰ Docker ΠϝʔδͷϏϧυΛ͠·͢ɻ % bazelisk run --platforms=@rules_go//go/toolchain:linux_amd64 //:load ... INFO: Running command line: bazel-bin/load.sh Loaded image: sample-go-bzlmod:latest % docker run --rm -p 8080:8080 sample-go-bzlmod:latest ... ͪͳΈʹɺBazel Λར༻ͯ͠ Mac Ͱ Linux ༻ͷ Go όΠφϦΛϏϧυ͢Δʹ --platforms=@ rules_go//go/toolchain:linux_amd64 Φϓγϣϯ͕ඞཁͰ͢ɻ STEP4: Elm Λͬͨ Web ϖʔδΛฦ͢ Elm ΛϏϧυ͢Δʹɺrules_elm Λར༻͠·͢ɻ MODULE.bazel module( name = "sample-go-bzlmod", version = "1.0.0", ) ... bazel_dep(name = "rules_elm", version = "1.0.1") elm = use_extension("@rules_elm//elm:extensions.bzl", "elm") elm.repository( name = "elm_package_elm_core", sha256 = "6e37b11c88c89a68d19d0c7625f1ef39ed70c59e443def95e4de98d6748c80a7", strip_prefix = "core-1.0.5", 65 -
ୈ 4 ষ ೖ Bzlmod 4.3 Bzlmod ΛͬͨΞϓϦέʔγϣϯ։ൃ urls =
["https://github.com/elm/core/archive/1.0.5.tar.gz"], ) ... rules_elm ʹɺgazelle ͷΑ͏ʹ elm.json ϑΝΠϧ*11͔ΒґଘύοέʔδΛੜ͢Δπʔϧ͕ ͋Γ·ͤΜɻͰ͢ͷͰɺࣗͰґଘ͍ͯ͠Δ Elm ͷ֎෦ύοέʔδΛྻڍ͢Δඞཁ͕͋Γ·͢ɻ ·ͨɺBazel ͰϏϧυ͍͢͠Α͏ʹɺ࣍ͷΑ͏ͳσΟϨΫτϦߏมߋ͠·͢ɻ sample-go-bzlmod/ |- front/ | |- BUILD.bazel | |- embed.go | |- index.html | \- Main.elm |- BUILD.bazel |- go.mod |- main.go \- MODULE.bazel BUILD.bazel MODULE.bazel ͱҟͳΓɺσΟϨΫτϦ͝ͱஔͰ͖·͢ɻrules_go ͷ߹ ɺGo ύοέʔδ͝ͱʹஔ͢ΔΑ͏ʹͳ͍ͬͯ·͢ɻfront ύοέʔδɺindex.html ͱ Elm Ͱੜͨ͠ JavaScript Λ go:embed ͢Δ͚ͩͷͷͰ͢ɻgazelle Λར༻ͭͭ͠ɺ࣍ͷΑ͏ͳ BUIL D.bazel ͕͋Γ·͢ɻ front/BUILD.bazel load("@rules_elm//elm:defs.bzl", "elm_binary") load("@rules_go//go:def.bzl", "go_library") elm_binary( name = "main", main = "Main.elm", deps = [ "@elm_package_elm_html", ], ) go_library( name = "front", srcs = ["embed.go"], embedsrcs = [ ":main", # keep "index.html", *11 Elm ϓϩδΣΫτͷઃఆϑΝΠϧͰ͢ɻґଘ͢Δ Elm ͷ֎෦ύοέʔδͳͲྻڍ͞Ε͍ͯ·͢ɻ 66 -
ୈ 4 ষ ೖ Bzlmod 4.3 Bzlmod ΛͬͨΞϓϦέʔγϣϯ։ൃ ], importpath
= "github.com/matsubara0507/sample-go-bzlmod/front", visibility = ["//visibility:public"], ) rules_elm ͷ elm_binary Λͬͯ͜ͷΑ͏ʹهड़͢ΔͱɺMain.elm ͔Β main.js ͕ੜ͞Ε ·͢ɻgazelle ɺੜ͞Εͨ JavaScript ϑΝΠϧʹґଘ͍ͯ͠Δ͜ͱΛೝࣝͰ͖ͳ͍ͷͰɺembe dsrcs ͷ ":main" ͷ෦ࣗͰهड़͢Δඞཁ͕͋Γ·͢ɻͪͳΈʹɺkeep ͱίϝϯτ͓ͯ͘͠ ͱɺgazelle ͕ͦͷߦΛফ͞ͳ͘ͳΓ·͢ɻmain.go ଆͷมߋɺ͜͜ͰׂѪ͠·͢ɻ ͜ΕͰࠓ·Ͱͱಉ͡Α͏ʹɺGo όΠφϦίϯςφΠϝʔδΛ Bazel ͰϏϧυ͢ΔͱɺElm ͷ ίϯύΠϧͬͯ Elm Ͱੜͨ͠ JavaScript ʹґଘͨ͠ΞϓϦέʔγϣϯ͕ߏங͞Ε·͢ɻ ͓·͚ɿ OpenAPI Λͬͨগ͠ϦονͳΞϓϦέʔγϣϯ OpenAPI ΛͬͨίʔυΨϥοͱมΘͬͯ͠·͏ͨΊɺࡉ͔͘ݴٴ͠·ͤΜ*12ɻBzlmod ݻ ༗ͷͱͯ͠ɺࣗ࡞͍ͯ͠Δ rules_openapi ͕ Bazel Central Registry ʹͳ͍ͱ͍͏ͷ͕͋Γ· ͢ɻͦ͜Ͱ͜͜ͰɺBazel Central Registry ʹͳ͍ύοέʔδΛ Bzlmod Ͱѻ͏ํ๏ʹ͍ͭͯઆ ໌͠·͢ɻ ·ͣલఏ݅ͱͯ͠ɺύοέʔδ͕ Bzlmod ʹରԠ͍ͯ͠ͳ͍ͱ͍͚·ͤΜʢ͕͢͞ʹ͜͜Ͱ ׂѪ͠·͢ʣ ɻBazel Central Registry bazelbuild/bazel-central-registry ͱ͍͏ GitHub Ϧϙδ τϦͰཧ͞Ε͍ͯ·͢*13ɻϦϙδτϦͷ modules σΟϨΫτϦʹɺϨδετϦ͕ѻ͍ͬͯΔύο έʔδͷઃఆ͕ॻ͔Ε͍ͯΔͷͰɺϑΥʔϚοτΛਅࣅͯ͠ PR Λग़ͤϨδετϦʹՃ͞Ε· ͢ɻϑΥʔϚοτʹ͍ͭͯɺbazel-central-registry ϦϙδτϦͷ docs ʹҰԠ͋Γ·͢ʢΘ͔Γʹ ͍͘Ͱ͕͢ʣ ɻ·ͨɺbazel-contrib/publish-to-bcr ͱ͍͏ͷΛ͏͜ͱͰࣗಈతʹߋ৽ PR Λ ͛ͯ͘ΕΔͦ͏Ͱ͢ɻ ਓʹΑͬͯɺ Bazel Central Registry ʹ PR Λग़ͨ͘͠ͳ͍߹͋Δͱࢥ͍·͢ɻͦͷ߹ɺ bazel-central-registry ϦϙδτϦΛϑΥʔΫͯ͠ɺΦϓγϣϯͰϨδετϦΛࢦఆ͢Δ͜ͱͰղܾͰ ͖·͢ɻͨͱ͑ɺrules_openapi ΛՃͨ͠ϨδετϦΛ matsubara0507/bazel-central-registry ϦϙδτϦͷ rules_openapi ϒϥϯνʹ༻ҙ͠·ͨ͠*14ɻ͜ΕΛ͏ʹɺ--registroy Φϓγϣ ϯͰࢦఆ͢Δ͚ͩͰ͢ɻ .bazelrc common --registry=\ https://raw.githubusercontent.com/matsubara0507/bazel-central-registry/rules_openapi *12 ίʔυ͕ΨϥοͱมΘΔͨΊɺopenapi ϒϥϯνʹ͚ͯ·͢ https://github.com/matsubara0507/sample-g o-bzlmod/tree/openapi *13 https://github.com/bazelbuild/bazel-central-registry *14 https://github.com/matsubara0507/bazel-central-registry/tree/rules_openapi 67 -
ୈ 4 ষ ೖ Bzlmod 4.4 ͓͠·͍ github.com Ͱͳ͘ɺraw.githubusercontent.com Λ͏ʹҙ͍ͯͩ͘͠͞ɻҰൠతͳ
ϓϩάϥϛϯάݴޠͱҧͬͯগ͠खؒͰ͕͢ɺ͜ΕͰಠࣗύοέʔδΛ Bzlmod Ͱ͏͜ͱ͕Ͱ͖ ·͢ɻ 4.4 ͓͠·͍ Bazel ۀͰ͓ͬͯΓ*15ɺύοέʔδͷΞοϓσʔτͰύοέʔδؒͷґଘόʔδϣϯ͕ ่ΕͯɺಾͷΤϥʔ͕ى͜Δ͜ͱ͕͋͠͠Γ·ͨ͠ɻͦͷͨΊɺBzlmod Bazel Ϣʔβʔʹ ͱͬͯɺ͓ͦΒ͘೦ئͷػೳͰ͢ɻ͜ΕͰ Bazel ͷ೦ͳͱ͜Ζ͕ݮͬͨͷͰɺ͜ΕΛػʹ Bazel ΛͬͯΈ͍ͯͩ͘͞ɻ *15 ͨͩ͠ɺۀͷํ·ͩ Bzlmod Λ͍͑ͯ·ͤΜɻ͜ΕΛػʹશཧղͰ͖ͯΑ͔ͬͨͰ͢ɻ 68 -
ୈ 5 ষ όφʔੜπʔϧͰσβΠφʔΛͤʹ ͔ͨͬͨ͠ 5.1 ·͕͖͑ ॳΊ·ͯ͠ɺιʔγϟϧϕοςΟϯάࣄۀຊ෦ TIPSTAR ࣄۀ෦։ൃάϧʔϓͷࢁ୩ͱਃ͠·͢ɻ
ຊষͰɺ؆қతͳόφʔੜπʔϧΛ༻͍ͯɺσβΠφʔͷ՝ղܾΛࢦͨ࣌͠ͷ͓Λͤͯ͞ ͍͖ͨͩ·͢ɻ 5.2 എܠ ·ͣॳΊʹɺͳͥσβΠφʔͷ՝ղܾΛࢼΈͨͷ͔Λઆ໌͠·͢ɻTIPSTAR ͰɺαʔϏεͷ ࢪࡦʹ߹ΘͤͯɺόφʔΛ࡞͢Δػձ͕ଟ͋͘Γ·͢ɻͦͷଟ͘ɺσβΠφʔͷํʑ͕όφʔ ࡞ʹඅ࣌ؒ͢ଟ͘ͳΓ·͢ɻͻͱ͑ʹόφʔΛ࡞͢Δ࡞ۀͱݴͬͯେ͖͘ 2 छྨ͋Γ· ͢ɻҰͭɺ৽نʹθϩ͔ΒσβΠϯ͢ΔͷͰ͢ɻ͏Ұͭ͢ͰʹςϯϓϨʔτ͕ଘࡏ͢Δό φʔͷจࣈ͚ͩΛมߋ͢ΔΑ͏ͳ؆қతͳͷ͕͋Γ·͢ɻࠓճɺޙऀͷ؆қతͳόφʔ࡞ ʹϑΥʔΧε͠ɺσβΠφʔͷํʑ͕ΑΓޮతʹ࡞ۀΛਐΊΒΕΔΑ͏ͳπʔϧΛ࡞͢Δ͜ͱ ʹ͠·ͨ͠ɻػցతʹͰ͖Δ࡞ۀΛ؆қԽ͢Δ͜ͱʹΑΓɺ୯ௐͳ࡞ۀ࣌ؒΛݮ͠ɺσβΠφʔͷ ํʑ͕ΑΓΫΦϦςΟͷߴ͍σβΠϯʹऔΓΉ࣌ؒΛ֬อͰ͖Δͱߟ͔͑ͨΒͰ͢ɻ 5.3 πʔϧͷ֓ཁ ͜ͷܭը͋͘·Ͱ࣮ݧతͳͷͰͨ͠ɻσβΠφʔͷํʑ͕ͲͷΑ͏ͳπʔϧΛٻΊ͍ͯΔͷ ͔ɺ·ͨͲͷΑ͏ͳػೳ͕ඞཁͳͷ͔ΛΔͨΊʹɺ·ͣ؆қతͳόφʔੜπʔϧΛ࡞͢Δ͜ ͱʹ͠·ͨ͠ɻπʔϧͷཁ݅ɺҎԼͷ௨ΓͰ͢ɻ • ܾ·ͬͨςϯϓϨʔτͷόφʔΛछྨબఆ͠ɺͦΕΒͷܾ·ͬͨՕॴͷจࣈࣈΛ ؆୯ʹมߋͰ͖Δ • όφʔͷੜɺೖྗͨ͠༰Λݩʹը૾Λੜ͢Δ͜ͱͰߦ͏ 69 -
ୈ 5 ষ όφʔੜπʔϧͰσβΠφʔΛͤʹ͔ͨͬͨ͠ 5.4 πʔϧͷٕज़બఆ • ͦΕΒͷมߋΛϒϥβ্Ͱߦ͏͜ͱ͕Ͱ͖Δ • ੜ͞Εͨը૾ɺͦͷͰμϯϩʔυͰ͖Δ
5.4 πʔϧͷٕज़બఆ πʔϧͷ։ൃɺαʔόνʔϜͷϝϯόʔͰߦ͏ͨΊɺॳ Go ݴޠͷ API Ͱ OGP Λੜ͢ ΔҊΛݕ౼͍ͯ͠·ͨ͠ɻ͔͠͠ɺσβΠφʔͷํʑ͕͍͍͢Α͏ʹɺϒϥβ্Ͱૢ࡞Ͱ͖Δ Α͏ʹ͢Δ͜ͱ͕ॏཁͰɺͦͷͨΊʹϑϩϯτΤϯυͷ։ൃඞཁʹͳΓ·͢ɻͦ͜ʹɺ͕͔͔ Δ͜ͱ͕༧͞Ε·ͨ͠ɻࠓճ࣮ݧతʹπʔϧΛ༻ҙ͠ɺσβΠφʔͷํʑʹ͍ͬͯͨͩ͘͜ͱ Ͱɺඞཁͳػೳ͍উखΛΔ͜ͱΛతͱ͍ͯͨ͠ҝɺ͏গ͠؆қతʹ։ൃͰ͖ͳ͍͔ͱߟ͑ ·ͨ͠ɻͦ͜Ͱɺࠓճ *1vercel/satori Λ༻͍ͯίετͰ։ൃΛਐΊΔ͜ͱʹ͠·ͨ͠ɻsatori Vercel ͕ఏڙ͢ΔΦʔϓϯιʔεͷϥΠϒϥϦͰ͢ɻsatori ɺReact ίϯϙʔωϯτΛͬͯ SVG ΛੜͰ͖ΔͨΊɺಈతͳάϥϑΟοΫεΧελϜը૾ͷੜʹඇৗʹศརͰ͢ɻsatori Λ ༻͍Δ͜ͱͰɺϒϥβ্Ͱը૾Λ֬ೝͰ͖ΔڥΛ؆୯ʹߏஙͰ͖·͢ɻαʔόνʔϜͷཧ্ɺ Go ݴޠͷ API Ͱ༻ҙͨ͠ํ͕͋ͱ͋ͱͷཧָ͕Ͱ͋ΔͷͰ͕͢ɺલड़ͨ͠௨Γࠓճ͋͘·Ͱ ػೳΛ༻ҙ͢Δ͜ͱͰͲͷΑ͏ͳޮՌ͕ಘΒΕΔͷ͔ΛΔ͜ͱ͕తͰ͋ΔͨΊɺ͜ͷΑ͏ͳબఆ Λߦ͍·ͨ͠ɻ 5.5 πʔϧͷ։ൃ satori Λ༻͍ͨ͜ͱͰ։ൃࣗମ͙͢ʹਐΊΔ͜ͱ͕Ͱ͖·ͨ͠ɻ࣮ࣗମγϯϓϧͳઃܭͰɺ ΫΤϦύϥϝʔλʹੜ͍ͨ͠όφʔͷࢦఆมߋՕॴͷใΛࡌͤΔ͜ͱͰɺͦͷͰը૾Λੜ ͠ϒϥβ্Ͱ֬ೝͰ͖ΔΑ͏ʹͳ͍ͬͯ·͢ɻՄม෦ʹؔͯ͠ɺΫΤϦύϥϝʔλͷ༰ʹ Ԋͬͯɺ෦ͱͳΔը૾Λܾ·ͬͨॴʹஔ͢Δͱ͍͏ߏͰ͢ɻαʔό Google ͷ CloudRun ʹߏங͠ɺϒϥβදࣔ png ʹมͨ͠ը૾Λදࣔ͢ΔΑ͏ʹ͠·ͨ͠ɻ༻ऀϒϥβ্͔ Βը૾Λอଘ͢Δ͚ͩͰੜ͞ΕͨόφʔΛखݩʹอଘ͢Δ͚ͩͷγϯϓϧͳػߏͰ͢ɻϦονͳػ ೳԿ͚ͭͣʹɺඞཁ࠷ݶͷػೳͰఏڙ͢Δ͜ͱͰɺͰ͖Δ͚ͩૣ࣮͘ΛੵΉ͜ͱΛࢦ͠· ͨ͠ɻ *1 vercel/satoriɿ https://github.com/vercel/satori 70 -
ୈ 5 ষ όφʔੜπʔϧͰσβΠφʔΛͤʹ͔ͨͬͨ͠ 5.5 πʔϧͷ։ൃ ਤ 5.1: Tool ͷը૾
࣮ํඇৗʹγϯϓϧʹͨ͠ࣄͰɺ͙͢ʹ࣮ऴΘΔ͔ͱߟ͍͑ͯ·͕ͨ͠ɺۤ࿑͢ΔϙΠ ϯτผʹ͋Γ·ͨ͠ɻ πʔϧʹదԠ͢Δόφʔͷબఆ ಄Ͱͨ͠௨Γɺࠓճͷπʔϧ͋͘·ͰςϯϓϨʔτ͕ܾ·͍ͬͯΔόφʔͷΈ͕ରͱͳ ΓɺͰ͖Δ͜ͱมߋՄೳՕॴ͋Β͔͡ΊܾΊ͓ͯ͘ඞཁ͕͋Γ·͢ɻͦͷͨΊɺͲͷΑ͏ͳό φʔΛબఆ͢Δ͔͕ॏཁͰͨ͠ɻద༻͢Δ͜ͱͰݮʹͭͳ͕ΔόφʔͲΕ͔ɺͲ͜·ͰՄม ʹ͢Δ͔ɺՄมͷൣғͳͲΛاըσβΠφʔͱ͠߹͍ͳ͕Βܾఆ͍͖ͯ͠·͢ɻ࣌ظঢ়گʹ Αͬͯ։࠵͞ΕΔΠϕϯτ͕มΘΓɺͦΕʹ߹Θͤͯ༻͞ΕΔόφʔมΘΔͷͰɺ͜ͷબఆʹ ࢥͬͨΑΓ࣌ؒΛඅ݁͢ՌʹͳΓ·ͨ͠ɻ༻ස͕ߴ͘ޮՌ͕ಘΒΕΔόφʔͲΕ͔Λ͠ ߹ͬͨ݁Ռɺॳճ 6 छྨͷόφʔΛબఆ͠·ͨ͠ɻ ϑΥϯτͷ ͱͱจࣈࣈΓग़͠ը૾ΛΘͣʹɺϓϩάϥϜ্ͷϑΥϯτͰ࠶ݱͨ͠ͷΛό φʔ্ʹஔ͢Δ༧ఆͰͨ͠ɻ͔͠͠ɺόφʔʹ༻͞Ε͍ͯΔϑΥϯτ͕ಛघͰɺͦΕΛϓϩάϥ Ϝ্Ͱ࠶ݱ͢Δ͜ͱ͕͍͠ͷ͋Γ·ͨ͠ɻݱঢ়༻͞Ε͍ͯΔόφʔΛ࡞Ͱ͖ΔΑ͏ʹͨ͠ ͔ͬͨͨΊɺ͜ͷ Tool ͷͨΊʹ৽͘͠όφʔσβΠϯͷ࡞Γ͠ආ͚·ͨ͠ɻͦ͜Ͱɺจࣈ 71 -
ୈ 5 ষ όφʔੜπʔϧͰσβΠφʔΛͤʹ͔ͨͬͨ͠ 5.6 ఏڙͨ݁͠Ռ ࣈΛΓग़͠ը૾ͱͯ͠༻ҙ͢Δ͜ͱʹ͠·ͨ͠ɻͦΕʹΑΓ࡞ۀྔ͕૿͑ɺ൚༻ੑʹ͚ܽͯ͠·͏ ܽʹͳΓ·ͨ͠ɻ ϨΠΞτʹ·͞ΕΔ Πϕϯτͷ༰Λܝࡌ͢ΔόφʔʹͳΔҝɺֹۚϥϯΩϯάͷॱҐͳͲܻ͕มΘΔͷ͋
ΓɺͦΕΒͷද่ࣔΕͷରԠͳͲඞཁͰͨ͠ɻͦΕΒΛߟྀͯ͠ɺࣈͷΓग़͠ը૾ 1 ܻͷ ࣈ͝ͱʹ༻ҙͤͣʹ 10ɺ50ɺ100ɺ1000 ͳͲɺ༻͢ΔՄೳੑ͕͋Δࣈͷը૾Λ༻ҙ͠ɺͦΕ ͧΕͷαΠζΛௐ͢Δ͜ͱͰද่ࣔΕΛ͙Α͏ʹ͠·ͨ͠ɻ·ͨɺՄมՕॴܾ·͍ͬͯΔͱ ͍͑ɺਓ͕࡞ͬͨͷઈົͳϨΠΞτͷௐ͕͞Ε͍ͯ·͢ɻීஈදࣔͷόϥϯεจࣈͷӄ ӨͳͲσβΠφʔʹΑͬͯௐ͞Ε͍ͯΔͨΊɺͦΕΛՄมը૾ͰͲ͜·Ͱҧײͳ͘࠶ݱͰ͖Δ ͷ͔Λݕ౼͢Δඞཁ͕͋Γ·ͨ͠ɻ্࣮ CSS Λ༻͍ͨඍௐ͕ඞཁͰɺͪ͜Β࣌ؒΛཁ͠· ͨ͠ɻ ӡ༻खॱͷཧ ݱঢ়اը͔ΒσβΠφʔʹόφʔͷ࡞ґཔΛ͢ΔϑϩʔͰ͕ͨ͠ɺࠓճͷπʔϧΛ͏͜ͱ Ͱɺ࠷ऴతʹσβΠφʔґཔ͢Δ͜ͱͳ͘ɺاըͷํʑ͕ࣗୡͰόφʔΛ࡞Ͱ͖ΔࣄΛ ࢦ͠·ͨ͠ɻͦ͏͢Δ͜ͱͰɺ੍࡞ϑϩʔ͕ॖ͞Εݮʹͭͳ͕͍ͬͯ͘ͱߟ͑·ͨ͠ɻͨͩ ͠ɺӡ༻্࡞ͨ͠όφʔΛ GitHub ͷ PR ͱͯ͠ग़͢ඞཁ͕͋Δҝɺاը͕ͦΕΒΛߦ͑ΔΑ ͏ʹखॱΛཧ͢ΔඞཁͳͲ͋Γɺͦͷ͋ͨΓ͠߹͍ͳ͕Βཧ͍͖ͯ͠·ͨ͠ɻ 5.6 ఏڙͨ݁͠Ռ ͍Ζ͍Ζͳௐͷ݁ՌແࣄʹπʔϧΛ࡞͠ɺ͋ͱاըɺσβΠφʔʹͬͯΒ͏͚ͩͱ͍͏ ஈ֊·Ͱ͚͗ͭ͜·ͨ͠ɻͦͷ݁ՌΛݩʹϒϥογϡΞοϓ͍ͯ͘͠༧ఆͰ͕ͨ͠ɺ࠷ޙʹܾఆతͳ ՝͕ൃੜ͠·ͨ͠ɻͦΕɺࣄۀͷঢ়گͷมԽʹΑΓࢪࡦ༰มΘΓɺࠓճ༻ҙͨ͠όφʔ͕ෆ ཁʹͳͬͨࣄͰ͢ɻͦͷͨΊɺπʔϧͷӡ༻ʹࢸΒͣɺ݁Ռͱͯ͠σβΠφʔͷ՝ղܾʹͭͳ ͕Βͳ͍ͱ͍͏೦ͳ݁ՌʹऴΘΓ·ͨ͠ɻࠓճఏڙ·Ͱ༧ΑΓ͕͔͔࣌ؒͬͯ͠·ͬͨ͜ͱ ͋ΓɺλΠϛϯά͕ѱ͔ͬͨͷ͋Γ·͢ɻ͔͠͠ɺࠜຊతͳ՝ͱͯ͠ɺTool ʹ൚༻ੑΛ࣋ͨ ͤΔ͜ͱ͕Ͱ͖ͳ͔ͬͨࣄɺࢪࡦʹେ͖͘ࠨӈ͞Εͳ͍σβΠϯͷ౷ҰԽͳͲΛઌʹਤΕͳ͔ͬͨ ࣄ͕ڍ͛ΒΕ·͢ɻݱࡏࢪࡦधཁʹΑͬͯόφʔσβΠϯͷधཁ͕େ͖͘มΘΔͨΊɺͦͷ͋ͨ ΓΛཧ͢Δ͜ͱ͕ઌܾͰ͋ͬͨͱߟ͍͑ͯ·͢ɻ 5.7 ࠓޙͷల ࠓճͷ݁ՌͰ՝͕ݟ͑·͕ͨ͠ɺࠓݱࡏόφʔͷσβΠϯͷ౷ҰԽΛਤΔಈ͖͕ਐΜͰ͍· ͢ɻͦͷͨΊɺπʔϧͷ࠶ఏڙΛݕ౼͍ͯ͠·͢ɻ࣍ͦ͜ɺ͜ͷπʔϧΛ࣮ࡍʹӡ༻Ͱ͑ΔΑ͏ ʹ͍ͨ͠ͱߟ͍͑ͯ·͢ɻ 72 -
ୈ 5 ষ όφʔੜπʔϧͰσβΠφʔΛͤʹ͔ͨͬͨ͠ 5.8 ͓·͚ 5.8 ͓·͚ ࣮ࠓճͷόφʔ࡞πʔϧʹ͏Ұஈ֊্ͷߏ͕͋Γ·ͨ͠ɻ͜ͷ݅ɺܰ͘ݕূͨ͠ఔ Ͱຊ֨࢝ಈͰ͖ͳ͔ͬͨͷͰɺ͓·͚ఔʹհ͍͖ͤͯͨͩ͞·͢ɻ
ͦͷߏͱ͍͏ͷɺςϯϓϨʔτ͕ܾ·ͬͨόφʔΛվม͢ΔͷͰͳ͘ɺόφʔͷσβΠϯͦ ͷͷΛ AI ʹΑͬͯࣗಈੜ͢Δͱ͍͏ͷͰ͢ɻҊͱͯ͠ҎԼͷ 2 ஈ֊Ͱߏ͍ͯ͠·ͨ͠ɻ ·ͣɺ ʮσβΠϯͷཁૉΛ AI ʹֶशͤ͞ɺͦΕΛݩʹόφʔσβΠϯΛੜ͢Δʯͱ͍͏ͷͰ ͢ɻ͜Εɺ΄΅ਓͷखΛՃ͑ͳͯ͘എܠͱͳΔσβΠϯΛ AI ʹΑͬͯੜ͢Δͱ͍͏ͷͰ͢ɻ ͜Ε͕Ͱ͖ΕɺσβΠφʔͷํʑ΄΅จࣈࣈͷஔͷΈʹઐ೦Ͱ͖ΔΑ͏ʹͳΓɺΑΓޮ తʹ࡞ۀΛਐΊΔ͜ͱ͕Ͱ͖·͢ɻ͔͠͠ɺAI Ͱੜ͞Εͨը૾๏తͳ؍ͷ՝ͳͲ͋Δҝɺ ͦͷؚΊͯ͏ҰͭͷҊͱͯ͠ʮੜ͞ΕͨσβΠϯΛΠϝʔδ mock ͱͯ͠༻Ͱ͖ΔΑ͏ʹ ͢Δʯͱ͍͏Ҋݕ౼͍ͯ͠·ͨ͠ɻσβΠϯҊΛύλʔϯͷ mock Λ༻͍ͯاըͱσβΠφʔͰ ͍ٞͯ͠ΔͷͰ͕͢ɺ͜ͷ mock Λ AI ʹΑͬͯੜ͢Δͱ͍͏ͷͰ͢ɻͦ͏͢Δ͜ͱͰɺ੍࡞ ϑϩʔ͕ॖ͞Εݮʹͭͳ͕͍ͬͯ͘ͱߟ͑·ͨ͠ɻ͔݁Βݴ͏ͱɺͪ͜Βͷߏ࣮ݱ· Ͱ͍࣋ͬͯ͘ͷ͕͍͠ͱஅ͠ɺࠓճ࣮ʹࢸΓ·ͤΜͰͨ͠ɻ͔͠͠ɺͦͷ༰Λগ͚ͩ͠ հͰ͖Εͱࢥ͍ɺຊষͷ࠷ޙʹهࡌ͍͖ͤͯͨͩ͞·ͨ͠ɻ 5.9 ༻ͨ͠ AI ػೳʹ͍ͭͯ ࠓճ Adobe Firefly Λ༻͍ͯ AI ʹΑΔόφʔੜΛࢼͯ͠Έ·ͨ͠ɻFirefly ʹ ʮੜృΓͭ Ϳ͠ʯͱʮςΩετ͔Βը૾ੜʯͱ͍͏ػೳ͕͋Γ·͢ɻ •ʮੜృΓͭͿ͠ʯը૾ͷಛఆͷ෦Λબ͠ɺͦͷ෦Λ AI ͕ࣗಈతʹੜͨ͠ίϯςϯ πͰృΓͭͿ͢͜ͱ͕Ͱ͖·͢ɻ •ʮςΩετ͔Βը૾ੜʯςΩετͷࢦࣔʹ͕͍ͨ͠ɺ৽͍͠ը૾Λθϩ͔ΒੜͰ͖·͢ɻ 5.10 ࣮ࡍʹ AI ੜͯ͠Έͨ݁Ռ ݕূͱͯ͠ϕʔεͱͳΔόφʔΛ༻ҙ͠ɺͦͷόφʔΛͱʹ AI ʹΑΔόφʔੜΛࢼͯ͠Έ· ͨ͠ɻ 73 -
ୈ 5 ষ όφʔੜπʔϧͰσβΠφʔΛͤʹ͔ͨͬͨ͠ 5.10 ࣮ࡍʹ AI ੜͯ͠Έͨ݁Ռ ਤ 5.2:
ϕʔεͱͳΔը૾ ·ͣɺ ʮੜృΓͭͿ͠ʯͰഎܠΛมߋͯ͠Έ·͢ɻϕʔεը૾ͷϝΠϯͷʮணॱ༧ʯͱ͍͏ จࣈΛ͠എܠΛબ͠·͢ɻ ਤ 5.3: എܠΛબ ͨͱ͑ΦʔϩϥͷΑ͏ͳഎܠΛࢦఆ͢ΔͱҎԼͷΑ͏ʹͳΓ·͢ɻ 74 -
ୈ 5 ষ όφʔੜπʔϧͰσβΠφʔΛͤʹ͔ͨͬͨ͠ 5.10 ࣮ࡍʹ AI ੜͯ͠Έͨ݁Ռ ਤ 5.4:
എܠΛมߋ ͜ͷػೳ͑ͦ͏Ͱ͋ͬͨͷͰ͕͢ɺ݁ہϕʔεͱͳΔσβΠϯΛ༻ҙ͢Δඞཁ͕͋ͬͨҝɺ ࠓճ࠾༻͠·ͤΜͰͨ͠ɻ·ͨɺࢥ͍௨ΓͷσβΠϯΛੜ͢Δͷ͕͘͠ɺࠓޙͷలͱͯ͠ ͍͠ͱஅ͠·ͨ͠ɻ ࣍ʹɺ ʮςΩετ͔Βը૾ੜʯͰը૾Λੜͯ͠Έ·ͨ͠ɻ͔݁Βݴ͏ͱɺͪ͜ΒͰࢥ͍௨ ΓͷσβΠϯΛੜ͢Δͷ͕͘͠ɺ࣮ӡ༻͍ͯ͘͠ʹ͍͠ͱஅ͠·ͨ͠ɻ ҎԼʮ৭ͷงғؾͰɺ༿ͬͺ͕ͳ͍Πϝʔδɻؐݩ͕͋Δ͜ͱɾָͦ͠͏ͳงғؾͰ͋Δ ͜ͱɾ͓ಘͰ͋Δ͜ͱɾ͓͕͔ۚΔײ͕͡ग़͍ͯΔΠϝʔδʯͱ͍͏ϓϩϯϓτͰੜͨ͠ྫʹͳ Γ·͢ɻ৭Λࢦఆ͢Δͱ͕࿈͞ΕΔ͜ͱ͕ଟ͍Α͏Ͱɺ༿ͬͺ͕ੜ͞ΕΔͷΛͱΊΔͷ Ұۤ࿑Ͱͨ͠ɻ 75 -
ୈ 5 ষ όφʔੜπʔϧͰσβΠφʔΛͤʹ͔ͨͬͨ͠ 5.10 ࣮ࡍʹ AI ੜͯ͠Έͨ݁Ռ ਤ 5.5:
ςΩετ͔Βը૾ੜ ҎԼͷը૾ͷੜΛͱΊΔͨΊʹʮ๐ʯͱݴ͏ Word ΛՃͯ͠Έͨ݁Ռɻ 76 -
ୈ 5 ষ όφʔੜπʔϧͰσβΠφʔΛͤʹ͔ͨͬͨ͠ 5.11 ࠷ޙʹ ਤ 5.6: ςΩετ͔Βը૾ੜͦͷ 2
จࣈ่Ε͍ͯͨΓɺΠϝʔδͨ͠௨ΓͷσβΠϯͱఔԕ͍݁ՌʹͳΓ·ͨ͠ɻϓϩϯϓτΛਫ਼ ࠪ͢Δ͜ͱͰɺΑΓྑ͍݁Ռ͕ಘΒΕΔ͔͠Ε·ͤΜ͕ɺࠓճݕূͨ͠தͰͦͷਫ਼ʹࢸΓ· ͤΜͰͨ͠ɻ·ͨɺΠϝʔδ௨ΓͷσβΠϯΛੜ͢Δͷ͕͍ͨ͠Ίɺ࣮ӡ༻ʹ͍࣋ͬͯ͘ʹ՝ ଟ͍ͱײ͡·ͨ͠ɻࠓճਂ͘ݕূͰ͖͍ͯ·ͤΜ͕ɺϓϩϯϓτͷਫ਼ࠪΛߦ͍ɺѻ͍ํΛ ͢Δ͜ͱͰɺΑΓྑ͍݁Ռ͕ಘΒΕΔ͔͠Ε·ͤΜɻͦ͏ͳΕɺ͔ͨ͠͠Β࣮ӡ༻ͷల ͕Δ͔͠Ε·ͤΜͶɻ 5.11 ࠷ޙʹ ࠷ޙʹͳΓ·͕͢ɺຊষͰ՝ղܾΛࢼΈͨࡍʹࢥΘ͵͕֞ؒݟ͑ͨ͜ͱΛ͓͠·ͨ͠ɻ ࠓճಋೖʹࢸΓ·ͤΜͰ͕ͨ͠ɺͦͷܦݧΛ׆͔͠ɺ࣍ʹͭͳ͍͛ͯ͘͜ͱ͕େͩͱײ͡·͠ ͨɻ͜ͷऔΓΈͰݟ͑ͨ՝ΛղܾͰ͖Εɺ·ͨ࣍ͷεςοϓਐΉ͜ͱ͕Ͱ͖ɺ͍ͣΕϓϩ μΫτ։ൃશମͷޮԽʹͭͳ͕͍ͬͯ͘ͷͩͱࢥ͍·͢ɻ͍༰ʹͳΓ·͕͢ɺগ͠ͰΈͳ͞ ΜͷࢀߟʹͳΕ͍Ͱ͢ɻ 77 -
ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢Δ ΈΛ࡞ͬͨ 6.1 ·͕͖͑ ॳΊ·ͯ͠ɺࠓճॳΊٕͯज़ॻయͰࣥච͢Δ͜ͱʹͳΓ·ͨ͠ɺιʔγϟϧϕοςΟϯάࣄۀຊ෦
TIPSTAR ࣄۀ෦։ൃάϧʔϓͷࢁԼͱਃ͠·͢ɻ ຊষͰɺࢲୡ͕ʑ։ൃɾӡ༻Λߦ͍ͬͯΔʮTIPSTAR*1ʯͱ͍͏αʔϏεʹ͓͍ͯɺӡ༻վ ળͱ AI Λಋೖ͍ͨ͠ͱ͍͏ٕज़తͳνϟϨϯδΛΈ߹ΘͤͯɺݮʹऔΓΜͩΛ͍ͯ͠ ͖͍ͨͱࢥ͍·͢ɻ ݮͷओͳ༰ͱͯ͠ GitHub Actions ͷͱɺAI ଆͷʹͳ͍ͬͯ·͢ɻAI ํ໘ʹ ։ൃຊ෦ͷٶ͞Μʹ࣮ʹೖ͍͖ͬͯͨͩ·ͨ͠ɻ·ͨࠓճهࡌ͢Δ༰ʹ͍ͭͯ͝ڠྗ͍ͨͩ ͖·ͨ͠ɻ͜ͷΛआΓͯޚྱਃ্͛͠·͢ɻ 6.2 ՝ʹ͍ͭͯ TIPSTAR Ͱʑ͞·͟·ͳΩϟϯϖʔϯࢪࡦ͕։࠵͞Ε͍ͯ·͢ɻ *1 TIPSTARʢςΟοϓελʔʣެࣜαϙʔταΠτ: https://about.tipstar.com 79 -
ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢ΔΈΛ࡞ͬͨ 6.2 ՝ʹ͍ͭͯ ਤ 6.1:
͋Δͷϛογϣϯը໘ ྫͱ্ͯ͠هͷΑ͏ͳछྨʹͳΓ·͕͢ɺ΄͔ʹ͞·͟·ͳύλʔϯ͕͋ΓɺͦΕΒඇΤϯ δχΞͰ͋ΔӡӦνʔϜʹͯΩϟϯϖʔϯࢪࡦΛݕ౼ͨ͠͏͑ͰϚελσʔλͱͯ͠σʔλΛ࡞͠ ೖߘ͍ͯ͠·͢ɻ σʔλ GitHub Λͬͯཧ͞Ε͓ͯΓɺมߋ Pull Request ʹ͢Δ͜ͱͰ༰ͷ֬ೝόʔ δϣϯཧΛߦ͍ͬͯ·͢ɻ ͜ΕΒͷೖྗσʔλɺTIPSTAR ͷ QA νʔϜʹͯΩϟϯϖʔϯࢪࡦͷ༷ॻΛνΣοΫ͠ͳ ͕Βɺ༷Λຬͨ͢Ϛελσʔλͳͷ͔ͱ͍͏؍ͳͲͰσʔλͷνΣοΫ͕ߦΘΕ͍ͯ·͢ɻ νΣοΫʹ͔͔Δͱͯ͠ຖि 2-3 ఔൃੜ͓ͯ͠ΓɺͦͷΛݮ͍ͨ͠ͱ͍͏՝͕͋ Γ·ͨ͠ɻಉ͜͡Ζɺੈͷதతʹ AI ΛۀʹͬͯΈͨܥͷهࣄηογϣϯ૿͑࢝Ίɺػӡͷ ߴ·Γ͋ͬͨ͜ͱ͔ΒɺAI Λ׆༻ͯ͠ΈΑ͏ͱߟ͑औΓΈΛ࢝Ί·ͨ͠ɻ 80 -
ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢ΔΈΛ࡞ͬͨ 6.3 ։ൃʹ͍ͭͯ 6.3 ։ൃʹ͍ͭͯ
σʔλߏͱνΣοΫํ๏ͷઃܭ ΩϟϯϖʔϯࢪࡦͷϚελσʔλʹ͍͔ͭ͘छྨ͋ΔͷͰ͕͢ɺେ·͔ͳσʔλͷߏͱͯ͠ ҎԼͷΑ͏ͳ CSV ͷܗʹͳ͍ͬͯ·͢ɻ ਤ 6.2: σʔλͷΠϝʔδ ͜ͷΑ͏ͳσʔλΛ࣮ࡍͷΞϓϦͷํ͔ΒݟΔͱɺҎԼͷΑ͏ͳܗͱͳΓ·͢ɻ 81 -
ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢ΔΈΛ࡞ͬͨ 6.3 ։ൃʹ͍ͭͯ ਤ 6.3:
͋Δͷ͋Δϛογϣϯ ࢪࡦͷ༰ʢdescriptionʣͱɺͦΕΛߏ͢ΔͨΊͷύϥϝʔλʢΞϓϦέʔγϣϯϩδοΫʹؔ ΘΔͷʣ͕ 5-6 ݸ͋ͬͯ 1 ͭͷࢪࡦσʔλͱͳ͍ͬͯ·͢ɻ ͜ͷΑ͏ͳσʔλʹରͯ͠ɺࠓճ 2 छྨͷνΣοΫΛऔΓೖΕͯΈΑ͏ͱͳΓ·ͨ͠ɻ1 ͭ descsription ͷ༰Λਖ਼ղͱͯ͠ɺͦΕΛຬͨ͢ύϥϝʔλ͕ͦΕͧΕͷ߲ʹ͖ͭదʹೖ͍ͬͯ Δͷ͔ͱ͍͏ͷʢ߹ੑνΣοΫʣ ɺ2 ֹͭۚදهه߸ͷ͍ํͳͲ͕ QA νʔϜͰνΣο Ϋ͍ͯ͠ΔϧʔϧʹԊ͍ͬͯΔ͔ͱ͍͏ͷʢจষߍਖ਼νΣοΫʣͰ͢ɻ ·ͨɺઌ΄Ͳॻ͍͍ͯͨΑ͏ʹϚελσʔλ GitHub Ͱ Pull Request ʹΑΔࠩཧΛͯ͠ ͍ΔͷͰɺ GitHub Actions ্Ͱಈ࡞͢ΔνΣοΫ͢ΔπʔϧΛ Go Ͱ࡞͠ AI ఆʹ͔͚ͯ NG ͷͷ͕͋ΕɺPull Request ʹରͯ͠ίϝϯτΛߘ͢ΔΑ͏ʹ͠·ͨ͠ɻ 82 -
ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢ΔΈΛ࡞ͬͨ 6.3 ։ൃʹ͍ͭͯ AI ଆͷ
͔͜͜ΒνΣοΫʹ͏ AI γεςϜͷ༰Ͱ͢ɻ ֓ཁͱγεςϜߏ Goole Cloud ͷ Cloud Run functions *2 ্ʹϓϩϯϓτੜ֤छॲཧΛ࣮͠ɺνΣοΫʹ ͏ AI ʹ Azure OpenAI Service Λ༻͠ɺGPT-4 ϞσϧΛར༻͍ͯ͠·͢ɻ͢ͳΘͪ LLM ʹ ΑΔνΣοΫΛߦ͍ͬͯ·͢ɻ ࣍ʹ֤νΣοΫ͝ͱͷϓϩϯϓτͳͲʹ͍ͭͯͷઆ໌Ͱ͢ɻ ߹ੑνΣοΫ CSV ͷ֤ߦ͝ͱʹཁૉͷͱ description ͕߹͍ͬͯΔ͔Λ֬ೝ͠·͢ɻ description Λࣄલʹఆٛͨ͠ ׂจࣈ ʹΑͬͯ݅ύʔτͱใुύʔτʹׂͯ͠ɺύϥϝʔλ ͷछྨʹΑͬͯɺͲͪΒ͔ͷύʔτ͔ඞཁͳํͷΈͷೖྗʹ༩͑Δ͜ͱͰɺGPT-4 ͷར༻ྉۚΛઅ ͭͭ͠ਫ਼্Λૂ͍ͬͯ·͢ɻ Ϧετ 6.1: ׂจࣈ SPLIT_WORDS = [’͢Δͱ’, ’ͤ͞Δͱ’, ’ͨΒͳ͔ͬͨΒ’, ’͠Α͏’] Ϧετ 6.2: ରจষྫ ຊݶఆͰɺTIPϚωʔͰ͞Βʹ1Ϩʔεʢ߹ܭ23Ϩʔεʣं݊Λߪೖ͢Δͱɺ ʮۚ՟(1)֫ಘνϟϯεʯͷ நબʹ1ճઓ͢Δ͜ͱ͕ग़དྷ·͢ʂ ͜ͷநબͰɺ ʮۚ՟(1)ʯ1ຕɺ ʮϓϥΠζϙΠϯτʯ50Pͷ͍ͣΕ͔Λ֫ಘ͢Δ͜ͱ͕ग़དྷ·͢ʂ ઌఔͷׂจࣈͰׂ͢ΔͱҎԼͷΑ͏ʹͳΓ·͢ɻ Ϧετ 6.3: ݅ύʔτ ຊݶఆͰɺTIPϚωʔͰ͞Βʹ1Ϩʔεʢ߹ܭ23Ϩʔεʣं݊Λߪೖ͢Δͱ *2 https://cloud.google.com/functions?hl=ja 83 -
ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢ΔΈΛ࡞ͬͨ 6.3 ։ൃʹ͍ͭͯ Ϧετ 6.4:
ใुύʔτ ɺ ʮۚ՟(1)֫ಘνϟϯεʯͷநબʹ1ճઓ͢Δ͜ͱ͕ग़དྷ·͢ʂ ͜ͷநબͰɺ ʮۚ՟(1)ʯ1ຕɺ ʮϓϥΠζϙΠϯτʯ50Pͷ͍ͣΕ͔Λ֫ಘ͢Δ͜ͱ͕ग़དྷ·͢ʂ ϓϩϯϓτ ҎԼ༻͍ͯ͠ΔϓϩϯϓτͷྫͰ͢ɻ Ϧετ 6.5: ϓϩϯϓτͷྫ આ໌จͷ༰ͱʮλʔήοτจ:ʯͷ༰͕ؒҧ͍ͬͯͳ͍͔Ͳ͏͔Λʮਖ਼͍͠ʯ͔ʮਖ਼͘͠ͳ͍ʯͰڭ͑ͯͩ͘ ͍͞ɻ ղϑΥʔϚοτ: ਖ਼͍͠ or ਖ਼͘͠ͳ͍_ਖ਼͘͠ͳ͍߹ͷΈ؆ܿʹͦ͏ߟ͑ͨཧ༝ λʔήοτจ: ڝ໊ٕ=શڝٕͰ,TIPϚωʔͰߪೖͨ͠Ϩʔεͷ આ໌จ: ຊݶఆͰɺTIPϚωʔͰ͞Βʹ1Ϩʔεʢ߹ܭ23Ϩʔεʣं݊Λߪೖ͢Δͱ ࣮ࡍ͜Ε͚ͩͩͱޡఆ͕ଟ͘ग़ͯ͠·͏ͨΊɺఆͷํΛৄࡉʹઆ໌ͨ͠ΓྫΛ༻͍Δ͜ͱ ͰޡఆΛݮΒ͍ͯ͠·͢ɻ จষߍਖ਼νΣοΫ จষߍਖ਼νΣοΫɺࣄલʹఆٛͨ͠ϧʔϧͱ description Λϓϩϯϓτͱͯͯؒ͠͠ҧ͍Օॴ Λࢦఠͯ͠Β͍·͢ɻ ͔͠͠ɺݱঢ়ֹۚه߸ͷఆ͕ۤखͳΑ͏Ͱޡఆ͕ݟΒΕΔͨΊɺֹۚه߸पΓҰ෦ϧʔ ϧϕʔεͰఆΛͨ͠Γɺֹۚ෦ΛΓग़ͯ͠ϓϩϯϓτԽ͢ΔͳͲ͍ͯͯ͠͠·͢ɻ ఆͷ GPT-4 ͷฦʹ༳Β͕͗͋Γɺඞͣ͠ຖճਖ਼͍͠ఆΛͯ͘͠ΕΔΘ͚Ͱ͋Γ·ͤΜɻࠓ ճͷλεΫͰɺ΄ͱΜͲͷೖྗ͕ਖ਼͍͠ͱ͍͏എܠ͋Γɺෳճ࿈ଓͰ ‘ਖ਼͘͠ͳ͍ ‘ ఆ͕ग़ ͨ߹ͷΈ NG Λฦ͍ͯ͠·͢ɻ ίετΛ֎ࢹ͢ΕɺಉҰೖྗͰෳճճΛͤͯ͞ɺશһҰக͘͠ଟܾΛऔΔํ๏ߟ ͑ΒΕ·͢ɻ BERT Λͬͨݕূ Azure OpenAPI Service Λར༻͢ΔલʹɺBERT ͷϑΝΠϯνϡʔχϯάΛ༻͍ͯಉ༷ͷ νΣοΫ͕Ͱ͖ͳ͍͔ݕূߦ͍·ͨ͠ɻ BERT ͷࣄલֶशࡁΈϞσϧΛ TIPSTAR ͷաڈͷࢪࡦσʔλͰϑΝΠϯνϡʔχϯάͯ͠ྨ Ϟσϧʹ͠ɺdescription ͔Β֤छύϥϝʔλΛ༧ଌͯ͠ΈΔͱ͍͏ͷͰ͢ɻݕূͱͯ͠ɺ 84 -
ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢ΔΈΛ࡞ͬͨ 6.4 ӡ༻࣌ʹݟ͔ͭͬͨ՝ͱରԠ ͋Δύϥϝʔλͷछྨ͕ଟ͘ɺ͜ΕΛ͍ͬͨΜසग़ (N>=100)
ͷ 11 Ϋϥεʹ͚ͯɺͦΕΒ ͷύϥϝʔλͷΈΛ͍ݕূ͠·ͨ͠ɻ ݁Ռɺ͜ͷύϥϝʔλͷΫϥεਖ਼ 25% ఔͱͳΓɺϥϯμϜΑΓྑ͍ͷͷɺ࣮༻Ͱ͖ Δਫ਼ʹࢸΒͳ͍ͱ͍͏͜ͱʹͳΓ Azure OpenAPI Service Λར༻͢Δ͜ͱʹ͠·ͨ͠ɻ 6.4 ӡ༻࣌ʹݟ͔ͭͬͨ՝ͱରԠ ͔͜͜Β࡞͍͍ͬͯͨͩͨ AI Λ͍νΣοΫΛ͔͚ΔͨΊͷɺGitHub Actions ଆͷ༰ʹ ͳ͍͖ͬͯ·͢ɻ GitHub Actions ͔Βͷ࿈ܞ ༻ҙ͍͍ͯͨͩͨ͠ CloudFunctions ͷΤϯυϙΠϯτʹରͯ͠ɺPull Request τϦΨͰɺΩϟ ϯϖʔϯࢪࡦͷϚελσʔλ্ͷࢪࡦͷ༰ʢdescriptionʣͱͦΕΛߏ͢ΔͨΊͷύϥϝʔλ 5-6 ݸΛ͚ͭͯϦΫΤετΛ͛·͢ɻ νΣοΫ͕ߦΘΕɺϨεϙϯεͱͯ͠ύϥϝʔλͷ߲͝ͱʹ OK or NGɺNG ͷ߹ͦ͏ அͨ͠ཧ༝ฦͬͯ͘ΔͨΊɺNG ͷΈΛरͬͯίϝϯτ͢ΔܗͰ͢ɻ ࣍ʹɺຊ֨తͳಋೖ·Ͱʹۤ࿑ͨ͠ͱ͜Ζͱͨ͠ͱ͜ΖΛ·ͱΊ·͢ɻओʹνΣοΫʹ͔͔Δ ॴཁ࣌ؒͷ͕͋Γ·ͨ͠ɻ Ճ͞Εͨߦ͚ͩͷऔಘ ΩϟϯϖʔϯࢪࡦͷϚελσʔλաڈͷσʔλ͕͍ͭ͘ೖ͍ͬͯ·͢ɻ ຖճաڈͷσʔλΛνΣοΫ͍ͯ͠ΔͱνΣοΫରߦ͕ͱΜͰͳ͍͜ͱʹͳͬͯ͠·͏ͷͰɺ ࠩͱͯ͠ग़͍ͯΔߦ͚ͩΛ͏·͘औΓग़͢ඞཁ͕͋Γ·ͨ͠ɻ Go ͷνΣοΫπʔϧʹ͔͚Δલʹɺ GitHub Actions ͷ workflow ͔ΒγΣϧεΫϦϓτΛ࣮ߦ ͢ΔΑ͏ʹͯ͠ɺ·ͣͬ͘͟ΓͱՃ͚ࠩͩΛऔಘ͢ΔΑ͏ʹ͠·ͨ͠ɻ Ϧετ 6.6: origin/BASE ͱ head branch Ͱͷൺֱ४උ git cat-file blob origin/"${ GitHub_BASE_REF}":"${item}" > "${TMPDIR}"/base_tmp git cat-file blob "${CHECK_BRANCH}":"${item}" > "${TMPDIR}"/merge_tmp Ϧετ 6.7: ઌ΄ͲͷϑΝΠϧؒͷࠩνΣοΫ diff -u "${TMPDIR}"/base_tmp "${TMPDIR}"/merge_tmp \ | grep ^+ | grep -v ’+++’ | sed s/^+// >> "${TMPDIR}"/add_diff ͜Ε͕ۭͰͳ͚ΕԿ͔͠ΒՃࠩ͋Γͱ͍͏͜ͱͰɺνΣοΫʹ͔͚͍ͨͱ͍͏༰Ͱ͢ɻ 85 -
ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢ΔΈΛ࡞ͬͨ 6.4 ӡ༻࣌ʹݟ͔ͭͬͨ՝ͱରԠ ͔͠͠ɺ͜ͷ··Ͱ৽نʹՃ͞Εͨߦͳͷ͔ɺطଘͷߦʹରͯ͠ͷมߋͳͷ͔அ͕͖ͭ·ͤ ΜͰͨ͠ɻطଘͷߦʹ͍ͭͯ࣌ؒΛ৯͏͚ͩͳͷͰແࢹ͢Δͷ͕ཧͰ͢ɻಋೖͨͯ͠ͷ͜Ζͦ
ΕΛ͓ͯ͠Βͣରߦ͕େʹͳΓ͕͔͔࣌ؒͬͯ͠·͏ঢ়گʹͳͬͯ͠·͍·ͨ͠ɻ ͦ͜Ͱɺaswinkarthik/csvdiff*3 ͱ͍͏ Go ͷύοέʔδΛ͍ɺϑΝΠϧͷத͔Β add ͞Εͨ ߦ͚ͩΛநग़͢ΔΑ͏ʹ͢Δ͜ͱͰνΣοΫରߦΛݮΒ͢͜ͱ͕Ͱ͖·ͨ͠ɻ ฒྻ࣮ߦ ผͷͱͯ͠ɺAzure OpenAPI Service ଆͰ͕͔͔࣌ؒͬͯ͠·͏ͨΊʹɺ1 ݅ͷνΣοΫʹ ͖ͭ 1 ఔ͕͔͔࣌ؒΔ͜ͱ͕͋Γ·ͨ͠ɻઌఔͷରԠ͚ͩͩͱγϯϓϧʹՃ͞Εͨߦ͕ଟ͘ɺ νΣοΫ͢Δྔ͕େʹͳΔͱ͕͔͔࣌ؒΔͰҰॹͩͬͨͨΊɺྃ·Ͱ 1 ࣌ؒऑͦΕҎ্ ͔͔Δ͜ͱ͕͋Γ·ͨ͠ɻ ͜ΕΛղܾ͢ΔͨΊʹ GitHub Actions ͷ matrix ػೳΛͬͯฒྻ࣮ߦ͕Ͱ͖ΔΑ͏͢Δ͜ ͱʹ͠·ͨ͠ɻ πʔϧ͔ΒνΣοΫ͢Δํ๏νΣοΫ͍ͨ͠ ID ΛೖΕΔܗʹ͍ͯͨ͠ͷͰ͕͢ɺ͜͜ʹ͢Ҿ ΛγΣϧͳͲͰؤுͬͯՃ͠ɺ ʰ ʢઌ΄ͲͷʣՃߦ ID நग़ϑΝΠϧ࡞ -> split Ͱಛఆͷ͝ ͱʹׂ -> ׂͨ͠ϑΝΠϧ PATH Λޙଓͷ job ͷ matrix ༻ʹ json ܗࣜʹมʱ ͱ͢ΔΑ͏ ʹ͠·ͨ͠ɻ ۩ମతͳίʔυͯ͠ҎԼͷܗͰɺjson ΦϒδΣΫτͱͯ͠ matrix ʹͤɺͦͷͿΜฒྻͰಈ ͍ͯ͘Ε·͢ɻ Ϧετ 6.8: origin/BASE ͱ head branch Ͱͷൺֱ४උ # ରͱͳΔCSVͷϦετΛ matrix job ʹ͢ chunk ʹׂ͢Δॲཧ zendan-job: outputs: targets: ${{ steps.get.outputs.targets }} steps: - name: લॲཧ id: get run: | # ྻ TARGETS=() # ׂ͞ΕͨϑΝΠϧΛ֨ೲ͢ΔσΟϨΫτϦΛ࡞ mkdir split_files # ࠩϑΝΠϧ͕͋Ε༰Λׂͯ͠อଘ split -l [NUM] --numeric-suffixes \ --additional-suffix=.csv [FILE_NAME] split_files/part_"${file}". # ׂ͞ΕͨϑΝΠϧͷϦετΛ࡞ TARGETS=($(ls split_files/*)) # matrix༻ʹjsonܗࣜʹม targets=$(printf ’%s\n’ "${TARGETS[@]}" | jq -R . | jq -s -c .) # ฒྻνΣοΫͤ͞Δॲཧ *3 csvdiff: https://github.com/aswinkarthik/csvdiff 86 -
ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢ΔΈΛ࡞ͬͨ 6.5 ͋ͱ͕͖ koudan-job: needs:
- zendan-job strategy: matrix: target_file: ${{ fromJSON(needs.zendan.outputs.targets) }} ·ͨগ͠Ͱݟ͍͢Α͏ʹͱɺϑΝΠϧׂ job ͱνΣοΫΛ͔͚Δ job Λׂͨ͠ͷͰɺ ׂͨ͠ϑΝΠϧ܊ artifact Λ͍ɺޙଓͷ job ʹҾ͖͢Α͏ʹ͍ͯ͠·͢ɻ ʰগ͠Ͱݟ͍͢Α͏ʹʱͱࢥͬͯ࣌͜͏͍ͯͨ͠ͷͰ͕͢ɺࠓৼΓฦͬͯݟ͍ͯΔͱ job Λׂͨ͜͠ͱʹΑͬͯෳࡶ͕͞૿͍ͯ͠ΔΑ͏ʹࢥ͑ͯ͠·͍·͢ɻ ͨͩɺͻͱ·ͣ͜ͷରԠͰνΣοΫͷฒྻԽ͕Ͱ͖ɺ10 ҎԼͳͲͷڐ༰Ͱ͖Δ࣌ؒͰશମత ʹॲཧͰ͖ΔΑ͏ʹͳΓ·ͨ͠ɻ ࢦఠࡁΈύϥϝʔλͷΩϟογϡ ࠷ޙಋೖͨ͋͠ͱͷ՝Ͱɺϋϧγωʔγϣϯͨ͠߹ͷରࡦʹ͍ͭͯͰ͢ɻ νΣοΫͨ݁͠Ռ Pull Request ʹίϝϯτ͢ΔܗͳͷͰϋϧγωʔγϣϯ͕ى͖͍͢σʔλ ͷ߹ʹίϝϯτ͕૿͑ͯ͠·͍ Pull Request ࣗମ͕ݟͮΒ͘ͳͬͨΓɺ௨͕ແବʹඈΜͩΓ ͯ͠͠·͍͋·Γྑ͘ͳ͍ঢ়ଶʹͳͬͯ͠·͍ͬͯ·ͨ͠ɻ ·ͨɺPull Request Ͱӡ༻͍ͯ͠Δ߹ɺमਖ਼ίϛοτ͕ੵ·ΕΔ͜ͱ͕͋Γ·͕͢ɺίϛοτ ͕ੵ·ΕΔͨͼʹ base branch ͱͷՃࠩΛநग़ͯ͠νΣοΫ͕ಈ͘ͱ͍͏ڍಈͷͨΊɺಉ͡Օ ॴͷಉ͡Α͏ͳίϝϯτ͕૿͑ͯ͠·͏ͱ͍͏ซͤͯ͋Γ·ͨ͠ɻ ͜Εʹର͢Δमਖ਼ͱͯ͠ɺνΣοΫࡁΈύϥϝʔλϑΝΠϧͷΑ͏ͳͷΛઃ͚Δ͜ͱʹ͠· ͨ͠ɻ ৄࡉͱͯ͠ɺGo ͷπʔϧଆͰνΣοΫ NG ͱͳͬͨ߹ʹɺdescription Ҏ֎ͷύϥϝʔλ͔Β ϋογϡΛੜ͠ɺͦͷ݁ՌΛϑΝΠϧʹग़ྗͯ͠ artifact ʹอଘ͠·͢ɻ࣍ճ࣮ߦ࣌ʹ ͦͷ artifact ͔ΒϑΝΠϧΛऔಘ͠ɺ࠶ Go ͷπʔϧʹ͠·͢ɻϋογϡ͕ҟͳ͍ͬͯΔ߹ʹͷ ΈνΣοΫΛߦ͏Α͏ʹ͠·ͨ͠ɻ ͜͏͢Δ͜ͱͰɺ͢ͰʹࢦఠࡁΈͷߦ͕ίϛοτͷͨͼʹίϝϯτ͞ΕΔ͜ͱແ͘͢͜ͱ͕Ͱ͖ ·ͨ͠ɻ 6.5 ͋ͱ͕͖ ͜͏ͯ͠ಋೖͨ͠ AI ΛͬͨσʔλνΣοΫͰ͕͢ɺ͠Β͘ӡ༻ͯ͠ΈΔͱ AI ͷӡ༻ͱ͍͏ Ͱଞʹ՝͋Γͦ͏ͩͱ͍͏͜ͱ͕͔͖ͬͯ·ͨ͠ɻ ·ͣɺӡ༻࣌ʹݟ͔ͭͬͨ՝ͱରԠ ͷઅͰ৮ΕͨΑ͏ʹɺLLM ͷਫ਼ෆʹΑΔͷͳͷ͔ ϓϩϯϓτʹվળͷ༨͕͋Δͷ͔ɺϋϧγωʔγϣϯͷൃੜස͕ߴ͍ͱ͍͏Ͱ͢ɻࢪࡦͷσʔ λύλʔϯʹਵ࣌Ξοϓσʔτ͕ೖΔ͜ͱͰɺ৽ͨͳจষύλʔϯ͕ಥવೖͬͯ͘Δ߹͕͋Γɺ 87 -
ୈ 6 ষ ӡ༻ೖߘσʔλΛ AI ͰνΣοΫ͢ΔΈΛ࡞ͬͨ 6.5 ͋ͱ͕͖ ͦͷࡍʹϋϧγωʔγϣϯى͖ͯ͠·͍·͢ɻ͜ͷʹ͍ͭͯ࣌ંϑΟʔυόοΫΛ͍͖ͨͩ ͭͭϓϩϯϓτͷௐΛٶ͞Μͱݕ౼͠ͳ͕Βमਖ਼͍ͯ͠·͢ɻ
ͨͩͦΕͰͲ͏ͯ͠ޙखޙखײ͕͋Γɺ͜ͷ͋ͨΓϓϩϯϓτͷ༰ௐΛզʑͷํͰͰ ͖ΔΑ͏ʹͨ͠Γɺ৽͍͠ύλʔϯͷࣄલͷ४උͰ͖ΔΑ͏ʹ͢ΔͳͲɺQA νʔϜʹڠྗ͍ͨͩ ͍ͨΓ͠ͳ͕Βਫ਼Λ্͛ΒΕΔӡ༻ํ๏ͳ͍͔ͳͱݕ౼࢝͠Ί͍ͯΔஈ֊Ͱ͢ɻ ·ͨɺLLM ʹΑΔνΣοΫΛผͷख๏ʹஔ͖͑ͨΓɺ͘͠͏গ͠νΣοΫ͢Δείʔϓ ΛߜͬͯΈͨΓͱɺAI ଆͷ͍ํʹ͍͔ͭ͘ݟͤΔ෦͋ΔΑ͏ʹײ͍ͯ͡ΔͷͰɺ։ൃ ຊ෦ͷํʹ૬ஊͭͭ͠ಓʹվળ͍͚ͯ͠Εͳͱࢥ͍ͬͯ·͢ɻ ຊষΛಡΜͰ͍͍ͨͩͨํʹগ͠Ͱʹͨͭใ͕͋Ε͍Ͱ͢ɻ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ 88 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳ ϕΫτϧݕࡧΛಋೖͯ͠ΈΔ ιʔγϟϧϕοςΟϯάࣄۀຊ෦ TIPSTAR ࣄۀ෦։ൃ G
ͷాͰ͢ɻීஈ TIPSTAR ͷ։ൃ νʔϜͷϚωʔδϟʔΛ͍ͯ͠·͢ɻࠓճৗతʹѪ༻͍ͯ͠ΔϊʔτΞϓϦ Obsidian ͷϓϥά Πϯ։ൃʹઓͯ͠ΈͨͷͰɺͦͷମݧΛڞ༗͍ͨ͠ͱࢥ͍·͢ɻObsidian ීஈͷ։ൃۀʹ͓ ͚ΔϝϞΞΠσΞཧʹ׆༻͍ͯ͠ΔπʔϧͰ͋ΓɺͦͷΧελϚΠζੑͷߴ͞ʹඇৗʹऒ͔Ε ͍ͯ·͢ɻຊষͰͦΜͳ Obsidian ͷັྗࢲ͕࣮ࡍʹऔΓΜͩϓϥάΠϯ։ൃͷྲྀΕΛհ͠ ·͢ɻ 7.1 Obsidian ͱԿ͔ Obsidian *1 ɺγϯϓϧͰ͋Γͳ͕ΒڧྗͳϊʔτཧΞϓϦέʔγϣϯͰ͢ɻϚʔΫμϯܗ ࣜͰͷςΩετ࡞ΛجຊʹɺϢʔβʔࣗͷϊʔτΛࣗ༝ʹཧ͠ɺϦϯΫΛுΓ८ΒͤΔ͜ͱ ͰࣝΞΠσΞΛମܥతʹཧͰ͖·͢ɻ ͞ΒʹɺGitɺiCloudɺGoogle Drive ͳͲͷ֎෦αʔϏεΛͬͯϦϞʔτʹσʔλΛόοΫΞο ϓͨ͠ΓɺෳͷؒͰಉظͤͨ͞Γ͢Δ͜ͱ༰қͰ͢ɻ༗ྉαʔϏεͰ͋Δ Obsidian Sync ʹՃೖ͢ΕɺࣗಈతʹؒͷσʔλಉظόοΫΞοϓ͕ՄೳʹͳΔͨΊɺ͞Βʹศརʹ༻Ͱ ͖·͢ɻ ॊೈͰܰྔͳΞϓϦέʔγϣϯ Obsidian ͷ࠷େͷಛͷҰͭɺͦͷॊೈੑͰ͢ɻObsidian ɺϢʔβʔ͕ࣗ༝ʹϓϥάΠϯΛ Ճͯ͠ػೳΛ֦ுͰ͖ΔΞϓϦέʔγϣϯͰ͢ɻެࣜͷϓϥάΠϯҎ֎ʹɺίϛϡχςΟ͕࡞ ͨ͠ଟ͘ͷϓϥάΠϯ͕͋ΓɺͦΕΒΛΈ߹ΘͤΔ͜ͱͰࣗͷχʔζʹ߹ͬͨΧελϚΠζ͕ ՄೳͰ͢ɻ ϓϥάΠϯΛೖΕ͗͢Δͱಈ࡞͕ॏ͘ͳΔ͜ͱ͕͋ΔͨΊɺҙ͕ඞཁͰ͢ɻదͳͷϓϥάΠ *1 Obsidian https://obsidian.md/ 89 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ 7.2 ࢲ͕ Obsidian ͕͖ͳཧ༝ ϯΛબͼɺObsidian
ͷύϑΥʔϚϯεΛҡ࣋͢Δ͜ͱ͕ॏཁͰ͢ɻ อݿʢVaultʣͷ֓೦ Obsidian ɺϊʔτΛʮอݿʢVaultʣ ʯͱ͍͏୯ҐͰཧ͠·͢ɻอݿͱɺϊʔτϑΝ Πϧͷू·ΓͰ͋ΓɺObsidian ͰҰͭͷ࡞ۀۭؒΛҙຯ͠·͢ɻอݿɺ༻్͝ͱʹશʹ ͚Δ͜ͱ͕Ͱ͖ΔͨΊɺࣄ༻ͱϓϥΠϕʔτ༻ͰϊʔτΛ͚ΔͳͲɺෳͷอݿΛ͍͚Δ ͜ͱͰޮతͳཧ͕ՄೳͰ͢ɻ͜ΕʹΑΓɺϓϩδΣΫτ͝ͱʹཧ͞ΕͨڥΛߏஙͰ͖·͢ɻ 7.2 ࢲ͕ Obsidian ͕͖ͳཧ༝ ࢲ 10 Ҏ্ʹΘͨΓ Evernote Λ͍ͬͯͯ՝͍ۚͯ͠·ͨ͠ɻ͔͠͠ɺΞϓϦέʔγϣϯ ͷෆ۩߹ύϑΥʔϚϯεͷ͕ଓ͖ɺ࣍ʹ͏ϊʔτΞϓϦΛ୳͠·ͨ͠ɻ͕ࣗΉϊʔτΞ ϓϦΛٻΊɺϦαʔν͍ͯͨ͠ͱ͜Ζ Obsidian Λݟ͚ͭ·ͨ͠ɻ ϊʔτΞϓϦʹΜͰ͍Δͱ͜ΖɺMarkdown ܗࣜͰϊʔτΛ࡞Ͱ͖Δ͜ͱͱɺGit ͰόοΫ ΞοϓཧͰ͖Δ͜ͱͰ͢ɻ͜ͷػೳ͕ඇৗʹॏཁͰɺӡӦձ͕ࣾෆ҆ఆʹͳͬͨΓαʔϏε͕ऴྃ ͨ͠߹ͰɺࣗͷϊʔτΛεϜʔζʹ΄͔ͷϓϥοτϑΥʔϜʹҠͰ͖·͢ɻ ·ͨ Obsidian ͰɺอݿʢVaultʣʹΑͬͯ͞·͟·ͳ༻్Ͱ͍͚͕Ͱ͖·͢ɻiOSɺ AndroidɺMac ͱ͍ͬͨෳͷϓϥοτϑΥʔϜʹରԠ͓ͯ͠ΓɺؒͷಉظػೳٻΊ͍ͯ·͠ ͨɻಉظࢲʹͱͬͯඞਢͩͬͨͷͰɺ͙͢ʹ Obsidian Syncʢ༗ྉʣʹՃೖ͠ɺؒͷಉظշ దʹߦ͍͑ͯ·͢ɻ 10 Ҏ্ͷϊʔτ͕͋ͬͨͷͰ࣌ؒΛཁͨ͠ͷͷɺEvernote ͔ΒͷσʔλҠ͕؆୯ʹߦ͑· ͨ͠ɻ ·ͱΊΔͱɺαʔϏεʹґଘ͠ͳ͍ Markdown ͰͷσʔλཧɺϞόΠϧͱσεΫτοϓΞϓϦ ͕͋Δɺ֤σόΠεؒͰಉظ͕Ͱ͖ΔɺϓϥάΠϯʹΑΔΧελϚΠζੑ͕ߴࣗ͘ΦϦδφϧͷ ϊʔτΞϓϦ͕࡞ΕΔͱ͍͏͕ɺࢲ͕ Obsidian Λ͏࠷େͷཧ༝Ͱ͢ɻ 7.3 ϓϥάΠϯ։ൃೖ Obsidian ɺϢʔβʔͷχʔζʹԠͯ͡ػೳΛΧελϚΠζͰ͖ΔϓϥάΠϯػೳ͕͋Γ·͢ɻ ϓϥάΠϯʹɺେ͖͚ͯ͘ίΞϓϥάΠϯͱίϛϡχςΟϓϥάΠϯͷ 2 छྨ͕͋Γ·͢ɻ ίΞϓϥάΠϯɺObsidian ຊମʹඪ४Ͱࡌ͞Ε͍ͯΔެࣜϓϥάΠϯͰ͢ɻϝϞػೳόο ΫϦϯΫදࣔɺλάཧͳͲɺجຊతͳϊʔτཧͷػೳΛ֦ு͢ΔͨΊͷͷ͕ଟ͘ɺϢʔβʔ͕ ༗ޮɾແޮΛ؆୯ʹΓସ͕͑Ͱ͖·͢ɻରͯ͠ίϛϡχςΟϓϥάΠϯɺϢʔβʔ͕ࣗ༝ʹ։ ൃɾެ։͍ͯ͠ΔͷͰ͋ΓɺObsidian ͷػೳΛ͞Βʹ֦ுͰ͖·͢ɻͨͱ͑ɺλεΫཧεέ δϡʔϦϯάɺσʔλՄࢹԽͷػೳͳͲಛఆͷχʔζʹରԠͨ͠ଟछଟ༷ͳϓϥάΠϯ͕͋Γ·͢ɻ 90 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ7.3 ϓϥάΠϯ։ൃೖ Obsidian ͷϓϥάΠϯ։ൃڥ Obsidian ͷϓϥάΠϯ
JavaScript Ͱ؆୯ʹ࡞Ͱ͖·͢ɻ • Node.js: ϓϥάΠϯͷϏϧυґଘؔͷཧʹඞཁͰ͢ɻ • JavaScript: Obsidian ͷϓϥάΠϯɺJavaScript Λͬͯ։ൃ͠·͢ɻ • Obsidian API: Obsidian ͷػೳΛϓϥάΠϯͱ֦ͯ͠ு͢ΔͨΊʹ API Λ͍·͢ɻ ϓϥάΠϯϓϩδΣΫτͷߏ Obsidian ͷϓϥάΠϯɺಛఆͷσΟϨΫτϦߏΛ࣋ͭϓϩδΣΫτͱͯ͠։ൃ͞Ε·͢ɻϓ ϩδΣΫτͷجຊߏҎԼͷ௨ΓͰ͢ɻ • manifest.json: ϓϥάΠϯͷใʢόʔδϣϯɺIDɺґଘؔͳͲʣΛఆٛ͠·͢ɻ • main.js: ϓϥάΠϯͷओͳϩδοΫ͕ॻ͔ΕΔϑΝΠϧͰ͢ɻObsidian API Λͬͯɺ͞· ͟·ͳػೳΛ࣮͠·͢ɻ • styles.css: ϓϥάΠϯͷ UI ʹελΠϧΛՃ͢ΔͨΊͷϑΝΠϧʢඞཁͳ߹ʣ ɻ ͜ΕΒͷϑΝΠϧ͕࠷ݶඞཁͱͳΓɺσΟϨΫτϦߏΛ͑Δ͜ͱͰϓϥάΠϯ͕ Obsidian Ͱೝࣝ͞Εɺಈ࡞͠·͢ɻ ։ൃ༻ͷઃఆͱσόοάํ๏ ։ൃதͷϓϥάΠϯΛ Obsidian Ͱಈ࡞ͤ͞ΔͨΊʹɺObsidian ͷ։ൃऀϞʔυΛ༗ޮʹ͢Δඞ ཁ͕͋Γ·͢ɻ͜ΕʹΑΓɺϩʔΧϧͰ࡞ͨ͠ϓϥάΠϯΛςετͰ͖ɺมߋΛϦΞϧλΠϜͰ өͤ͞Δ͜ͱ͕Ͱ͖·͢ɻσόοάɺϒϥβͷ։ൃऀπʔϧΛར༻͠ɺJavaScript ͷίϯιʔϧ ϩάΛͬͯ֬ೝ͠·͢ɻΤϥʔνΣοΫߦ͍ͳ͕Βɺ҆ఆͨ͠ϓϥάΠϯ։ൃΛਐΊΒΕ·͢ɻ ॳΊͯͷϓϥάΠϯʮHello WorldʯΛ࡞ͯ͠ΈΔ ϓϥάΠϯ։ൃͷ࠷ॳͷεςοϓͱͯ͠ɺ؆୯ͳʮHello WorldʯϓϥάΠϯΛ࡞͠·͢ɻ͜ͷ ϓϥάΠϯɺObsidian Λ։͍ͨࡍʹʮHello Worldʯͱ͍͏ϝοηʔδ͕දࣔ͞ΕΔγϯϓϧͳ ͷͰ͢ɻ main.js module.exports = class MyPlugin extends require(’obsidian’).Plugin { onload() { console.log(’HelloWorld! onload’); } 91 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ7.3 ϓϥάΠϯ։ൃೖ onunload() { console.log(’HelloWorld! onunload’);
} }; ͜ͷίʔυͰ onload ϝιουͰϓϥάΠϯ͕ಡΈࠐ·ΕͨࡍʹʮHelloWorld! onloadʯͱ͍͏ ϝοηʔδΛίϯιʔϧʹදࣔ͠ɺ onunload ϝιουͰϓϥάΠϯ͕ແޮԽ͞Εͨࡍʹ ʮHelloWorld! onunloadʯͱ͍͏ϝοηʔδΛදࣔ͠·͢ɻ·ͣɺ͜͏͍ͬͨίʔυͰ։ൃڥ͕͍ͬͯΔ͔ͷ ֬ೝΛ͠·͢ɻ manifest.json { "id": "hello-world-plugin", "name": "Hello World Plugin", "version": "1.0.0", "minAppVersion": "0.12.0", "description": "A simple Hello World plugin for Obsidian.", "author": "Kyohei Tsuda", "authorUrl": "https://your-website.com", "isDesktopOnly": false } • id: ϓϥάΠϯͷҰҙͷࣝผࢠɻ • name: ϓϥάΠϯͷද໊ࣔɻ • version: ϓϥάΠϯͷόʔδϣϯɻ • minAppVersion: ϓϥάΠϯ͕αϙʔτ͢Δ Obsidian ͷ࠷খόʔδϣϯɻ • description: ϓϥάΠϯͷઆ໌ɻ • author: ։ൃऀͷ໊લɻ • authorUrl: ։ൃऀͷ Web αΠτʢҙʣ ɻ • isDesktopOnly: σεΫτοϓ൛ͷΈͰಈ࡞͢Δ߹ true ʹઃఆ͠·͢ɻ σΟϨΫτϦߏ ΞϓϦέʔγϣϯσΟϨΫτϦʹ͋Δ .obsidian/plugins/ ʹ֨ೲ͠·͢ɻ directory hello-world-plugin/ ᴹ 92 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ 7.4 Obsidian ʹϕΫτϧݕࡧΛಋೖͯ͠ΈΔ ᵓᴷᴷ manifest.json
ᵓᴷᴷ main.js ᵋᴷᴷ styles.css ࣮ߦํ๏ σΟϨΫτϦʹηοτͨ͠ΒɺίϛϡχςΟϓϥάΠϯͷઃఆ͔ΒϓϥάΠϯͷ࠶ಡΈࠐΈΛ͢Δ ͱϓϥάΠϯҰཡʹදࣔ͞Ε·͢ɻ͋ͱΦϯʗΦϑΛ࣮ߦ͢Ε onload()ɺonunload() ͕ݺΕ ·͢ɻ ਤ 7.1: ίϛϡχςΟϓϥάΠϯ 7.4 Obsidian ʹϕΫτϧݕࡧΛಋೖͯ͠ΈΔ ϕΫτϧݕࡧͱԿ͔ ϕΫτϧݕࡧɺैདྷͷΩʔϫʔυϕʔεͷݕࡧͱҟͳΓɺσʔλΛϕΫτϧۭؒʹϚοϐϯά ͠ɺͦͷྨࣅΛܭࢉͯ͠ݕࡧ݁ՌΛฦ͢ख๏Ͱ͢ɻςΩετσʔλΛϕΫτϧԽʢʹมʣ͠ɺ ͦΕΒͷϕΫτϧͲ͏͠ͷڑ֯Λجʹؔ࿈ੑΛஅ͠·͢ɻ͜Εಛʹɺҙຯతʹྨࣅͨ͠ ༰Λݟ͚͍ͭͨͱ͖ʹཱͪ·͢ɻ طଘͷίϛϡχςΟϓϥάΠϯͱࣗ࡞ͷཧ༝ Obsidian ʹ͢ͰʹίϛϡχςΟʹΑͬͯ࡞ΒΕͨϕΫτϧݕࡧͷϓϥάΠϯ͕ଘࡏ͠·͢ɻ͜ ΕΛ༻͢Δ͜ͱͰɺ؆୯ʹϕΫτϧݕࡧΛಋೖͰ͖·͢ɻ͔͠͠ࠓճ͋͑ͯࣗ࡞ΛࢼΈͨཧ༝ɺ ϓϥάΠϯ։ൃΛ௨ͯ͠ϕΫτϧݕࡧͷ͘͠ΈΛֶͼɺࣗͷಛఆͷཁ݅ʹԠͨ͡ΧελϚΠζΛࢪ ͔͔ͨͬͨ͠ΒͰ͢ɻ 93 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ 7.4 Obsidian ʹϕΫτϧݕࡧΛಋೖͯ͠ΈΔ ϕΫτϧݕࡧͷ֓೦ ϕΫτϧݕࡧͰɺ֤υΩϡϝϯτใΛϕΫτϧۭؒʹϓϩοτ͠ɺͦΕΒͷྨࣅΛܭࢉ͠
ͯؔ࿈ੑΛஅ͠·͢ɻैདྷͷΩʔϫʔυݕࡧͰɺ୯ޠ͕Ұக͍ͯ͠Δ͔Ͳ͏͔͚ͩʹґଘ͠·͢ ͕ɺϕΫτϧݕࡧͰɺҙຯతͳྨࣅੑίϯςΩετߟྀ͞Ε·͢ɻ ͨͱ͑ɺ ʮΓʯͱʮΞτυΞʯΩʔϫʔυݕࡧͰผͱͯ͠ѻΘΕ·͕͢ɺϕΫτϧݕ ࡧͰͦΕΒ͕ࣅͨ֓೦ͱͯ͠ͱΒ͑ΒΕɺؔ࿈͢Δ݁Ռ͕ಘΒΕΔՄೳੑ͕ߴ·Γ·͢ɻ ༻͢ΔϥΠϒϥϦͷબఆ ϕΫτϧݕࡧͷ࣮ʹɺ͞·͟·ͳϥΠϒϥϦ͕͋Γ·͢ɻࠓճɺHaystack ͱ͍͏ϥΠϒϥ ϦΛ༻͠·͢ɻHaystack ɺυΩϡϝϯτݕࡧ QA γεςϜʹ͓͍ͯΘΕΔڧྗͳπʔϧͰɺ ϕΫτϧݕࡧͷػೳඋ͍͑ͯ·͢ɻଞʹɺFaiss Annoy ͳͲͷϕΫτϧݕࡧ༻ͷϥΠϒϥϦ͕ ͋Γ·͕͢ɺࠓճ؆ศͰػೳ͕๛ͳ Haystack Λબఆ͠·ͨ͠ɻ ϩʔΧϧڥͰͷઃఆͱ࣮४උ Haystack ΛΠϯετʔϧͯ͠ɺObsidian ͱ࿈ܞͰ͖ΔΑ͏ʹϩʔΧϧڥΛ४උ͠·͢ɻಉ࣌ʹ ґଘؔͷ͋ΔϥΠϒϥϦΠϯετʔϧ͠·͢ɻ˞ Python 3 ܥͷ։ൃڥ͕ඞཁͰ͕͢ɺڥߏ ஙͷઆ໌ׂѪ͠·͢ɻ $ pip install ’farm-haystack[inference]’ torch sentence-transformers fugashi ipadic ࣍ʹɺPython Ͱ API ΛηοτΞοϓ͠ɺObsidian ͷϊʔτσʔλΛऔಘɾॲཧ͢Δ४උΛߦ͍ ·͢ɻObsidian ͷϊʔτ௨ৗɺMarkdown ܗࣜͰอଘ͞Ε͍ͯΔͨΊɺ͜ΕΛςΩετσʔλͱ ͯ͠ѻ͍·͢ɻ ϕΫτϧݕࡧػೳͷ࣮ farm-haystack Λ༻ͯ͠ϕΫτϧݕࡧΛߦ͏؆୯ͳ Python εΫϦϓτΛ࡞͠·͢ɻ͜ΕΛޙ Ͱ JavaScript ͔Β࣮ߦ͠·͢ɻϢʔβʔ͕ೖྗͨ͠ΫΤϦͱࣄલʹొ͞Εͨຊޠͷϊʔτσʔ λΛϕΫτϧۭؒʹม͠ɺྨࣅʹج͍ͮͯ࠷ؔ࿈ੑͷߴ͍υΩϡϝϯτΛݕࡧ͠·͢ɻ͜ͷϓ ϩηεͰɺจॻͷ༰Λతʹදݱ͢ΔͨΊͷຒΊࠐΈϞσϧΛ༻͠·͢ɻݕࡧϕΫτϧؒ ͷڑΛجʹߦΘΕɺҙຯతʹ͍ۙจॻΛޮՌతʹ୳͠ग़ͤ·͢ɻ 94 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ 7.4 Obsidian ʹϕΫτϧݕࡧΛಋೖͯ͠ΈΔ main.py from
haystack.document_stores import InMemoryDocumentStore from haystack.nodes import EmbeddingRetriever from haystack.pipelines import DocumentSearchPipeline import logging logging.basicConfig(level=logging.DEBUG) def run_haystack_query(query): # ຒΊࠐΈϞσϧͷ໊લΛࢦఆ͠ɺGPUΛ༻͠ͳ͍ઃఆ model_name = ’distiluse-base-multilingual-cased-v2’ # υΩϡϝϯτετΞͷηοτΞοϓʢembedding_dimΛ512ʹઃఆʣ document_store = InMemoryDocumentStore(embedding_dim=512) # αϯϓϧͷຊޠϊʔτσʔλΛ༻ҙ documents = [ {"content": "ࢲ ٳ ʹ ւ Γ Λ ָ ͠ Μ Ͱ ͍ · ͢ ɻಛ ʹ େ Λ Δ ͷ ͕ ͖ Ͱ ͢ɻ", "meta": {"source": "note1.md"}}, {"content": "࠷ۙొࢁʹઓ͓ͯ͠ΓɺࣗવͷதͰա͕࣌ؒ͢͝ϦϑϨογϡʹͳΓ· ͢ɻ", "meta": {"source": "note2.md"}}, {"content": "ࢲ Ω ϟ ϯ ϓ ͕ झ ຯ Ͱ ɺ༑ ୡ ͱ Ұ ॹ ʹ ࢁ Ͱ ς ϯ τ Λ ு Γ · ͢ɻ", "meta": {"source": "note3.md"}}, {"content": "ಡ ॻ ࢲ ͷ େ ͖ ͳ झ ຯ ͷ Ұ ͭ Ͱ ɺಛ ʹ ϛ ε ς Ϧ ʔ খ આ Λ Μ Ͱ ͍ · ͢ɻ", "meta": {"source": "note4.md"}} ] document_store.write_documents(documents) # EmbeddingRetrieverΛ༻ͯ͠ϦτϦʔόʔΛઃఆ retriever = EmbeddingRetriever( document_store=document_store, embedding_model=model_name, model_format=’sentence_transformers’, use_gpu=False ) # ຒΊࠐΈΛߋ৽ document_store.update_embeddings(retriever) # DocumentSearchPipelineΛ༻ͯ͠ݕࡧΫΤϦͷ࣮ߦ pipeline = DocumentSearchPipeline(retriever=retriever) # ΫΤϦ࣮ߦ results = pipeline.run(query=query) return results if __name__ == "__main__": query = "Γʹ͍ͭͯ" results = run_haystack_query(query) if results: print(results) 95 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ 7.4 Obsidian ʹϕΫτϧݕࡧΛಋೖͯ͠ΈΔ ຊޠΛѻ͑Δ distiluse-base-multilingual-cased-v2
Ϟσϧ 512 ࣍ݩͷຒΊࠐΈϕΫτϧΛੜ ͢ΔͨΊɺembedding_dim=512 Λࢦఆͯ͠·͢ɻ ࣮ߦ݁Ռ ʮΓʹ͍ͭͯʯͱ͍͏ݕࡧΛ͢Δͱɺ͜ͷΑ͏ͳ݁Ռ͕ฦ͖ͬͯ·͢ɻ͜ͷதͰ note1.md ͕ ࠷ core ͕ߴ͍͜ͱ͕͔Γ·͢ɻcontext ϥΠϒϥϦͷઃఆɺϞσϧΛมߋ͢Δ͜ͱͰɺΑΓߴ ਫ਼ͳνϡʔχϯά͕ՄೳͰ͢ɻ result { "documents": [ { "content": "ࢲٳʹւΓΛָ͠ΜͰ͍·͢ɻಛʹେΛΔͷ͕͖Ͱ͢ɻ", "score": 0.5008297958802435, "meta": { "source": "note1.md" }, }, { "content": "ࢲΩϟϯϓ͕झຯͰɺ༑ୡͱҰॹʹࢁͰςϯτΛுΓ·͢ɻ", "score": 0.5001496666252616, "meta": { "source": "note3.md" }, }, { "content": "ಡॻࢲͷେ͖ͳझຯͷҰͭͰɺಛʹϛεςϦʔখઆΛΜͰ͍·͢ɻ", "score": 0.500142795001931, "meta": { "source": "note4.md" }, }, { "content": "࠷ۙొࢁʹઓ͓ͯ͠ΓɺࣗવͷதͰա͕࣌ؒ͢͝ϦϑϨογϡʹͳΓ· ͢ɻ", "score": 0.5001341874679, "meta": { "source": "note2.md" }, } ], "root_node": "Query", "params": {}, "query": "Γʹ͍ͭͯ", "node_id": "Retriever" } 96 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ 7.4 Obsidian ʹϕΫτϧݕࡧΛಋೖͯ͠ΈΔ Obsidian ͔ΒϕΫτϧݕࡧΛ࣮ߦ
Obsidian ͔Β Python Λݺͼग़͢͜ͱ͕Ͱ͖ͳ͍ͨΊɺflask Λ͍؆୯ͳαʔόΛཱͯ· ͢ɻࠓճઌ΄ͲͷιʔείʔυΛͦͷ··ྲྀ༻͠·͢ɻ࠷Ϛον͢ΔυΩϡϝϯτΛฦ͠·͢ɻ αʔόίʔυগʑ͍ͨΊׂѪ͠·͕͢ɺҎԼͷΑ͏ͳ࣮ߦ݁Ռ͕ฦͬͯ͘ΔϩʔΧϧαʔόΛཱ ͯ·͢ɻ $ curl -X POST http://localhost:5000/search \ -H "Content-Type: application/json" \ -d ’{"query": "Γʹ͍ͭͯ"}’ {"best_source":"note1.md"} ࣍ʹ Obsidian ଆͷϓϥάΠϯΛ࡞͠·͢ɻઌ΄Ͳ࡞ͨ͠ hello-world-plugin ʹ࣮͠·͢ɻ main.js const { Plugin, requestUrl, Notice, FuzzySuggestModal } = require(’obsidian’); module.exports = class MyPlugin extends Plugin { async onload() { console.log(’MyPlugin loaded’); this.addCommand({ id: ’search-and-open-note’, name: ’Search and Open Note’, callback: () => { new QueryModal(this.app, async (query) => { try { const response = await requestUrl({ url: ’http://localhost:5000/search’, method: ’POST’, contentType: ’application/json’, body: JSON.stringify({ query: query }), }); const bestSource = response.json.best_source; const file = this.app.vault.getAbstractFileByPath(bestSource); if (file && file instanceof require(’obsidian’).TFile) { this.app.workspace.getLeaf().openFile(file); } else { new Notice(‘ϑΝΠϧ͕ݟ͔ͭΓ·ͤΜ: ${bestSource}‘); } } catch (error) { 97 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ 7.4 Obsidian ʹϕΫτϧݕࡧΛಋೖͯ͠ΈΔ console.error(error); new
Notice(’αʔόͷϦΫΤετʹࣦഊ͠·ͨ͠ɻ’); } }).open(); }, }); } onunload() { console.log(’MyPlugin unloaded’); } }; class QueryModal extends require(’obsidian’).Modal { constructor(app, onSubmit) { super(app); this.onSubmit = onSubmit; } onOpen() { const { contentEl } = this; contentEl.createEl(’h2’, { text: ’ݕࡧΫΤϦΛೖྗ͍ͯͩ͘͠͞’ }); const inputEl = contentEl.createEl(’input’, { type: ’text’ }); inputEl.focus(); inputEl.addEventListener(’keydown’, (event) => { if (event.key === ’Enter’) { this.close(); this.onSubmit(inputEl.value); } }); } onClose() { const { contentEl } = this; contentEl.empty(); } } manifest.json ໊લͷΈมߋ͠·ͨ͠ɻ͜͏͢Δ͜ͱͰϓϥάΠϯΛଟগѻ͍͘͢ͳΓ·͢ɻ 98 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ 7.4 Obsidian ʹϕΫτϧݕࡧΛಋೖͯ͠ΈΔ manifest.json {
"name": "Search and Open Note", } ϓϥάΠϯͷ࣮ߦ Obsidian ্ͰʲCommand + PʳΛ࣮ߦ͢ΔͱɺίϚϯυύϨοτ͕ग़ͯ͘ΔͷͰͦ͜Ͱ ʮSearchʯͱೖྗ͢Εɺࠓճ࡞ͨ͠ϓϥάΠϯ͕Ͱ͖ͯ·͢ɻ ਤ 7.2: ίϚϯυύϨοτ ࠓճಡॻͱೖྗ࣮͠ߦ͠·͢ɻϩʔΧϧαʔόʹΞΫηε͞ΕϕΫτϧݕࡧ͕࣮ߦ͞Ε·͢ɻ ਤ 7.3: Search and Open Note ͷ࣮ߦ ࣮ߦ͕ྃ͢Δͱ֘ͷϊʔτ͕։͖·ͨ͠ɻ ਤ 7.4: note4 ͕։͘ ͞ΒͳΔ֦ுΞΠσΞ ϕΫτϧݕࡧͷ݁ՌΛҰཡදࣔʹ͠ɺϢʔβʔ͕બͰ͖ΔΑ͏ʹվྑ͢Δ༨͕͋Γͦ͏Ͱ͢ɻ ·ͨࠓճͷαϯϓϧͩͱΞϓϦέʔγϣϯʹϊʔτͷใΛೖΕ͍ͯͨͷͰඇৗʹඇޮͰ͢ɻ ࢲ Obsidian ϊʔτͯ͢ GitHub ʹόοΫΞοϓ͞Ε͍ͯ·͢ɻϦϞʔταʔόΛཱͯࣗಈత ʹ GitHub ͔ΒϊʔτΛऔΓࠐΈɺϕΫτϧݕࡧ͕Ͱ͖ΔΑ͏ʹ͢Εɺ͞ΒʹศརͳͷʹͳΔ ͣͰ͢ɻ 99 -
ୈ 7 ষ Obsidian ϓϥάΠϯ։ൃೖɻ؆қͳϕΫτϧݕࡧΛಋೖͯ͠ΈΔ 7.5 ऴΘΓʹ 7.5 ऴΘΓʹ ຊষͰɺϓϥάΠϯ։ൃΛ௨ͯ͡ಘͨϕΫτϧݕࡧͷݟͱɺࠓޙͷ֦ுΞΠσΞʹ͍ͭͯ·ͱ
Ί·ͨ͠ɻ࠷ۙɺLLMʢେنݴޠϞσϧʣϕΫτϧݕࡧɺͦͯͦ͠ΕΒΛ׆༻ͨ͠ػցֶशٕज़ ʹ৮ΕΔػձ͕૿͑ɺΑΓਂ͘ཧղ͢ΔͨΊʹɺීஈ͍ͬͯΔ Obsidian ΛͬͯϓϥάΠϯ࡞Γ ʹઓ͠·ͨ͠ɻObsidian ୯ͳΔϊʔτΞϓϦʹͱͲ·Βͣɺ͍ํ࣍ୈͰඇৗʹߴͳใ ཧݕࡧػೳΛ࣮ݱͰ͖Δ͜ͱ͕͔Γ·ͨ͠ɻಛʹɺϕΫτϧݕࡧͷಋೖʹΑΓɺେͳใͷத ͔Βؔ࿈ੑͷߴ͍σʔλΛޮతʹநग़Ͱ͖ɺࠓޙͷԠ༻ͷՄೳੑ͕͍ͬͯ·͢ɻͨͱ͑ࢲͷ झຯͰ͋ΔΓͳͲಛఆͷτϐοΫʹؔ࿈ͨ͠ใΛ͖Ε͍ʹཧ͠ɺޮతʹݕࡧͰ͖ΔΦϦδφ ϧͷυΩϡϝϯτγεςϜΛ࡞Γ͍ͨͱ͍͏ඪݟ͖͑ͯ·ͨ͠ɻ Obsidian ɺ͍ํ࣍ୈͰແݶͷՄೳੑΛൿΊͨπʔϧͰ͋Γɺࢲʹͱͬͯͳͯ͘ͳΒͳ͍ ଘࡏͱͳ͍ͬͯ·͢ɻ͜Ε͔Βࣗͷχʔζʹ߹ͬͨΧελϚΠζ৽͍ٕ͠ज़ͷಋೖΛଓ͚ɺ Obsidian Λ͞ΒʹਐԽ͍͖͍ͤͯͨ͞ͱࢥ͍·͢ɻ 100 -
ஶऀհ ߐാ ࠸ (ୈ 1 ষ୲, GitHub: @MokkeMeguru) ιʔγϟϧϕοςΟϯάࣄۀຊ෦ ։ൃࣨ
৽ن։ൃάϧʔϓͷ։ൃͰ͢ɻओʹ TIPSTAR ͷ։ൃʹैࣄ͓ͯ͠Γɺ࠷ۙͰ iOS ։ൃͱαʔϏεશൠͷӡ༻Λத৺ʹ׆ಈ͍ͯ͠·͢ɻ ॳԻϛΫͷԻήʔָ͍͠Ͱ͢ͶɺXcode ͷϏϧυͪͷ࣌ؒͬͺΒ͜ΕͰ͕࣌ؒ௵ͤ ·͢ɻ ࠤʑా େ (ୈ 2 ষ୲, GitHub: soudai-s, X: @soudai_s) ΈͯͶϓϩμΫτ։ൃ෦ͰࣸਅϓϦϯτྖҬΛத৺ʹωΠςΟϒΞϓϦ (iOSɺAndroid) ͱ αʔό (Ruby on Rails) ͷ։ൃΛ͍ͯ͠·͢ɻεϊϘʔͱϦθϩ͕͖ɻ एদ ৎਓ (ୈ 3 ষ୲, GitHub: @take-2405 X: @Jaksho500) 23 ৽ଔͱͯ͠ೖࣾ͠ɺTIPSTAR ͱ͍͏αʔϏεͷ։ൃɾӡ༻Λߦ͍ͬͯ·͢ɻ࠷ۙӳ ޠͷษڧʹΛೖΕ͍ͯ·͢ɻ দݪ ৴ (ୈ 4 ষ୲) ॴଐϞϯεταʔόνʔϜͰ Ruby Go Λॻ͍ͯΔɻϓϩάϥϛϯά͕͖Ͱɺͨ·ʹ ਪ͠ݴޠͷ Haskell Ͱ༡ΜͩΓ͍ͯ͠Δɻ ࢁ୩ ༏հ (ୈ 5 ষ୲) ιʔγϟϧϕοςΟϯάࣄۀຊ෦ TIPSTAR ࣄۀ෦ ։ൃάϧʔϓͰαʔόʔνʔϜͷϦʔ μʔΛ͍ͯ͠·͢ɻࡢ 3 ਓͷࢠڙ͕ੜ·Εɺʑҭࣇʹฃಆ͓ͯ͠Γ·͢ɻ࠷ۙଉࢠͷ ۭखʹՈͰத͍ͯ͠·͢ɻ ࢁԼ কޗ (ୈ 6 ষ୲) ιʔγϟϧϕοςΟϯάࣄۀຊ෦ TIPSTAR ࣄۀ෦։ൃάϧʔϓॴଐɻ͜͜࠷ۙඇΤϯδ χΞ͚ͷۀվળʹ͏։ൃ͕ଟΊͰ͢ɻ ా ګฏ (ୈ 7 ষ୲ GitHub: @flatfisher) ιʔγϟϧϕοςΟϯάࣄۀຊ෦ TIPSTAR ࣄۀ෦ ։ൃάϧʔϓ ϚωʔδϟʔɻTIPSTAR ։ൃνʔϜͷϚωδϝϯτ͍ͯ͠·͢ɻٳ΄΅ΓΛ͍ͯ͠·͢ɻ 101 -
MIXI TECH NOTE #12 2024 11 ݄ 2 ɹॳ൛ୈ
1 ɹൃߦ ஶɹऀ גࣜձࣾ MIXI ༗ࢤ ൃߦॴ גࣜձࣾ MIXI ҹॴ ޫاը ɹ MIXI