ระบบ CI สำหรับการพัฒนา iOS: การแปลง Intel เป็น ARM

เผยแพร่แล้ว: 2024-02-14

ในภูมิทัศน์ของเทคโนโลยีที่เปลี่ยนแปลงตลอดเวลา บริษัทต่างๆ จะต้องปรับตัวให้เข้ากับกระแสแห่งการเปลี่ยนแปลงเพื่อรักษาความเกี่ยวข้องและความสามารถในการแข่งขัน การเปลี่ยนแปลงอย่างหนึ่งที่ครอบงำโลกแห่งเทคโนโลยีอย่างรวดเร็วคือการเปลี่ยนจากสถาปัตยกรรม Intel x86_64 ไปเป็น สถาปัตยกรรม iOS ARM ซึ่ง มีตัวอย่างจากชิป Apple M1 ที่ล้ำสมัยของ Apple ในบริบทนี้ ระบบ CI สำหรับ iOS ได้กลายเป็นข้อพิจารณาที่สำคัญสำหรับบริษัทต่างๆ ที่เผชิญกับการเปลี่ยนแปลงนี้ เพื่อให้มั่นใจว่ากระบวนการพัฒนาและทดสอบซอฟต์แวร์ยังคงมีประสิทธิภาพและทันสมัยด้วยมาตรฐานทางเทคโนโลยีล่าสุด

Apple ประกาศชิป M1 เมื่อเกือบสามปีที่แล้ว และตั้งแต่นั้นเป็นต้นมา เป็นที่ชัดเจนว่าบริษัทจะยอมรับสถาปัตยกรรม ARM และเลิกรองรับซอฟต์แวร์ที่ใช้ Intel ในที่สุด เพื่อรักษาความเข้ากันได้ระหว่างสถาปัตยกรรม Apple ได้เปิดตัว Rosetta เวอร์ชันใหม่ ซึ่งเป็นเฟรมเวิร์กการแปลไบนารีที่เป็นกรรมสิทธิ์ของบริษัท ซึ่งได้รับการพิสูจน์แล้วว่าเชื่อถือได้ในอดีตระหว่างการเปลี่ยนแปลงสถาปัตยกรรมครั้งสำคัญจาก PowerPC มาเป็น Intel ในปี 2549 การเปลี่ยนแปลงยังคงดำเนินอยู่ และเราได้เห็นแล้วว่า Xcode สูญเสียการสนับสนุน Rosetta ในเวอร์ชัน 14.3

ที่ Miquido เราตระหนักถึงความจำเป็นในการย้ายจาก Intel ไปใช้ ARM เมื่อไม่กี่ปีก่อน เราเริ่มเตรียมการในกลางปี ​​2021 ในฐานะบริษัทซอฟต์แวร์ที่มีไคลเอนต์ แอป และโปรเจ็กต์หลายรายการที่กำลังดำเนินไปพร้อมๆ กัน เราเผชิญกับความท้าทายบางประการที่เราต้องเอาชนะ บทความนี้อาจเป็นแนวทางปฏิบัติของคุณได้หากบริษัทของคุณเผชิญกับสถานการณ์ที่คล้ายคลึงกัน สถานการณ์และวิธีแก้ปัญหาที่อธิบายไว้นั้นแสดงให้เห็นจากมุมมองการพัฒนา iOS แต่คุณอาจพบข้อมูลเชิงลึกที่เหมาะสมสำหรับเทคโนโลยีอื่นๆ เช่นกัน องค์ประกอบที่สำคัญของกลยุทธ์การเปลี่ยนแปลงของเราเกี่ยวข้องกับการทำให้แน่ใจว่าระบบ CI สำหรับ iOS ของเราได้รับการปรับให้เหมาะสมที่สุดสำหรับสถาปัตยกรรมใหม่ โดยเน้นถึงความสำคัญของ ระบบ CI สำหรับ iOS ในการรักษาขั้นตอนการทำงานที่มีประสิทธิภาพและผลลัพธ์คุณภาพสูงท่ามกลางการเปลี่ยนแปลงที่สำคัญดังกล่าว

ปัญหา: การย้ายสถาปัตยกรรม Intel ไปยัง iOS ARM

Apple เปิดตัวชิป M1 ในปี 2020

กระบวนการโยกย้ายแบ่งออกเป็นสองสาขาหลัก

1. แทนที่คอมพิวเตอร์ของนักพัฒนาที่ใช้ Intel ที่มีอยู่ด้วย M1 Macbooks ใหม่

กระบวนการนี้ควรจะค่อนข้างง่าย เรากำหนดนโยบายที่จะค่อยๆ แทนที่ Intel Macbooks ของผู้พัฒนาทั้งหมดภายในระยะเวลาสองปี ปัจจุบัน 95% ของพนักงานของเราใช้ Macbook ที่ใช้ ARM

อย่างไรก็ตาม เราพบกับความท้าทายที่ไม่คาดคิดในระหว่างกระบวนการนี้ ในกลางปี ​​2021 การขาดแคลน M1 Mac ทำให้กระบวนการเปลี่ยนทดแทนของเราช้าลง ภายในสิ้นปี 2021 เราสามารถเปลี่ยน Macbook ได้เพียงไม่กี่เครื่องจากเครื่องที่รออยู่เกือบ 200 เครื่อง เราคาดการณ์ว่าจะใช้เวลาประมาณสองปีในการแทนที่ Intel Macs ของบริษัททั้งหมดด้วย M1 Macbooks อย่างสมบูรณ์ รวมถึงวิศวกรที่ไม่ใช่ iOS ด้วย

โชคดีที่ Apple เปิดตัวชิป M1 Pro และ M2 ใหม่ ด้วยเหตุนี้ เราจึงเปลี่ยนโฟกัสจากการแทนที่ Intel ด้วย M1 Mac ไปเป็นการแทนที่ด้วยชิป M1 Pro และ M2

ซอฟต์แวร์ไม่พร้อมสำหรับสวิตช์ทำให้นักพัฒนาหงุดหงิด

วิศวกรกลุ่มแรกๆ ที่ได้รับ M1 Macbooks ใหม่มีช่วงเวลาที่ยากลำบาก เนื่องจากซอฟต์แวร์ส่วนใหญ่ยังไม่พร้อมที่จะเปลี่ยนไปใช้สถาปัตยกรรม iOS ARM ใหม่ของ Apple เครื่องมือของบุคคลที่สาม เช่น Rubygems และ Cocoapods ซึ่งเป็นเครื่องมือจัดการการพึ่งพาที่ต้องอาศัย Rubygems อื่นๆ จำนวนมากได้รับผลกระทบมากที่สุด เครื่องมือเหล่านี้บางส่วนไม่ได้รับการคอมไพล์สำหรับสถาปัตยกรรม iOS ARM ดังนั้นซอฟต์แวร์ส่วนใหญ่จึงต้องทำงานโดยใช้ Rosetta ทำให้เกิดปัญหาด้านประสิทธิภาพและความยุ่งยาก

อย่างไรก็ตาม ผู้สร้างซอฟต์แวร์พยายามแก้ไขปัญหาเหล่านี้ส่วนใหญ่เมื่อเกิดขึ้น ช่วงเวลาแห่งการพัฒนามาพร้อมกับการเปิดตัว Xcode 14.3 ซึ่งไม่รองรับ Rosetta อีกต่อไป นี่เป็นสัญญาณที่ชัดเจนสำหรับนักพัฒนาซอฟต์แวร์ทุกคนที่ Apple กำลังผลักดันให้มีการย้ายสถาปัตยกรรม Intel ไปใช้ iOS ARM สิ่งนี้บังคับให้นักพัฒนาซอฟต์แวร์บุคคลที่สามส่วนใหญ่ที่เคยใช้ Rosetta มาก่อนต้องย้ายซอฟต์แวร์ไปยัง ARM ในปัจจุบัน 99% ของซอฟต์แวร์บุคคลที่สามที่ใช้งานที่ Miquido ในแต่ละวันทำงานโดยไม่มี Rosetta

2. แทนที่ระบบ CI ของ Miquido สำหรับ iOS

การเปลี่ยน ระบบ iOS ที่บูรณาการอย่างต่อเนื่อง ที่ Miquido ได้รับการพิสูจน์แล้วว่าเป็นงานที่ซับซ้อนมากกว่าการเปลี่ยนเครื่องจักร ขั้นแรก โปรดดูที่โครงสร้างพื้นฐานของเราในตอนนั้น:

สถาปัตยกรรม CI ที่ Miquido ระบบ CI สำหรับการแปลง iOS

เรามีอินสแตนซ์ระบบคลาวด์ Gitlab และ Mac Mini ที่ใช้ Intel จำนวน 9 เครื่องเชื่อมต่ออยู่ เครื่องจักรเหล่านี้ทำหน้าที่เป็นตัวดำเนินการงาน และ Gitlab มีหน้าที่รับผิดชอบในการจัดการเรียบเรียง เมื่อใดก็ตามที่งาน CI ถูกจัดคิว Gitlab จะมอบหมายงานนั้นให้กับรันเนอร์ตัวแรกที่พร้อมใช้งานซึ่งตรงตามข้อกำหนดของโปรเจ็กต์ที่ระบุในไฟล์ gitlab-ci.yml Gitlab จะสร้างสคริปต์งานที่มีคำสั่ง build ตัวแปร เส้นทาง ฯลฯ ทั้งหมด จากนั้นสคริปต์นี้จะถูกย้ายไปยังรันเนอร์และดำเนินการบนเครื่องนั้น

แม้ว่าการตั้งค่านี้อาจดูแข็งแกร่ง แต่เราประสบปัญหาเกี่ยวกับการจำลองเสมือนเนื่องจากการรองรับโปรเซสเซอร์ Intel ที่ไม่ดี ด้วยเหตุนี้ เราจึงตัดสินใจที่จะไม่ใช้การจำลองเสมือน เช่น Docker และดำเนินงานบนเครื่องจริงด้วยตนเอง เราพยายามตั้งค่าโซลูชันที่มีประสิทธิภาพและเชื่อถือได้โดยใช้ Docker แต่ข้อจำกัดในการจำลองเสมือน เช่น การขาดการเร่งความเร็วของ GPU ส่งผลให้งานใช้เวลานานกว่าสองเท่าในการดำเนินการบนเครื่องจริง สิ่งนี้นำไปสู่ค่าใช้จ่ายที่เพิ่มขึ้นและคิวที่เต็มอย่างรวดเร็ว

เนื่องจาก macOS SLA เราจึงสามารถตั้งค่า VM สองเครื่องพร้อมกันได้เท่านั้น ดังนั้นเราจึงตัดสินใจที่จะขยายกลุ่มนักวิ่งจริงและตั้งค่าให้รันงาน Gitlab ได้โดยตรงบนระบบปฏิบัติการของพวกเขา อย่างไรก็ตาม วิธีการนี้มีข้อเสียบางประการเช่นกัน

ความท้าทายในกระบวนการสร้างและการจัดการนักวิ่ง

  1. ไม่มีการแยกบิลด์ภายนอกแซนด์บ็อกซ์ไดเร็กทอรีบิลด์

รันเนอร์ดำเนินการทุกบิลด์บนเครื่องจริง ซึ่งหมายความว่าบิลด์ไม่ได้แยกออกจากไดเร็กทอรีแซนด์บ็อกซ์ สิ่งนี้มีข้อดีและข้อเสีย ในด้านหนึ่ง เราสามารถใช้แคชของระบบเพื่อเร่งการสร้างได้เนื่องจากโปรเจ็กต์ส่วนใหญ่ใช้ชุดการขึ้นต่อกันของบุคคลที่สามชุดเดียวกัน

ในทางกลับกัน แคชจะไม่สามารถบำรุงรักษาได้เนื่องจากการเหลือจากโปรเจ็กต์หนึ่งอาจส่งผลต่อโปรเจ็กต์อื่นๆ ทุกโปรเจ็กต์ นี่เป็นสิ่งสำคัญอย่างยิ่งสำหรับแคชทั้งระบบ เนื่องจากรันเนอร์ตัวเดียวกันนั้นใช้สำหรับการพัฒนาทั้ง Flutter และ React Native โดยเฉพาะอย่างยิ่ง React Native จำเป็นต้องมีการขึ้นต่อกันจำนวนมากที่แคชผ่าน NPM

  1. เครื่องมือระบบที่อาจเกิดขึ้น

แม้ว่างานทั้งสองจะไม่ได้รับการดำเนินการด้วยสิทธิ์ sudo แต่ก็ยังเป็นไปได้ที่พวกเขาจะสามารถเข้าถึงระบบหรือเครื่องมือผู้ใช้บางอย่าง เช่น Ruby สิ่งนี้ก่อให้เกิดภัยคุกคามต่อการทำลายเครื่องมือเหล่านี้ โดยเฉพาะอย่างยิ่งเมื่อ macOS ใช้ Ruby สำหรับซอฟต์แวร์รุ่นเก่าบางรุ่น รวมถึงฟีเจอร์ Xcode รุ่นเก่าด้วย เวอร์ชันระบบของ Ruby ไม่ใช่สิ่งที่คุณต้องการจะยุ่งด้วย

อย่างไรก็ตาม การแนะนำ rbenv จะสร้างความซับซ้อนอีกชั้นหนึ่งให้จัดการ สิ่งสำคัญคือต้องทราบว่า Rubygems ได้รับการติดตั้งตามเวอร์ชัน Ruby และอัญมณีเหล่านี้บางส่วนต้องการ Ruby เวอร์ชันเฉพาะ เครื่องมือของบุคคลที่สามเกือบทั้งหมดที่เราใช้นั้นขึ้นอยู่กับ Ruby โดยมี Cocoapods และ Fastlane เป็นตัวแสดงหลัก

  1. การจัดการอัตลักษณ์การลงนาม

การจัดการข้อมูลประจำตัวของการลงนามหลายรายการจากบัญชีการพัฒนาลูกค้าต่างๆ อาจเป็นเรื่องที่ท้าทายเมื่อพูดถึงพวงกุญแจระบบของนักวิ่ง ข้อมูลระบุตัวตนในการลงนามเป็นข้อมูล ที่มีความละเอียดอ่อนสูง เนื่องจากช่วยให้เราสามารถออกแบบโค้ดแอปพลิเคชันได้ ทำให้มีความเสี่ยงต่อภัยคุกคามที่อาจเกิดขึ้น

เพื่อรับประกันความปลอดภัย ข้อมูลประจำตัวควรถูกแซนด์บ็อกซ์ทั่วทั้งโปรเจ็กต์และได้รับการปกป้อง อย่างไรก็ตาม กระบวนการนี้อาจกลายเป็นฝันร้ายเมื่อพิจารณาถึงความซับซ้อนที่เพิ่มขึ้นจาก macOS ในการใช้พวงกุญแจ

  1. ความท้าทายในสภาพแวดล้อมแบบหลายโครงการ

ไม่ใช่ทุกโปรเจ็กต์ที่ถูกสร้างขึ้นโดยใช้เครื่องมือเดียวกัน โดยเฉพาะ Xcode บางโปรเจ็กต์ โดยเฉพาะโปรเจ็กต์ที่อยู่ในระยะการสนับสนุน ได้รับการดูแลโดยใช้ Xcode เวอร์ชันล่าสุดที่โปรเจ็กต์ได้รับการพัฒนาด้วย ซึ่งหมายความว่าหากจำเป็นต้องมีงานใดๆ ในโครงการเหล่านั้น CI จะต้องสามารถสร้างมันขึ้นมาได้ เป็นผลให้นักวิ่งต้องรองรับ Xcode หลายเวอร์ชันพร้อมกัน ซึ่งทำให้จำนวนนักวิ่งสำหรับงานเฉพาะลดลงได้อย่างมีประสิทธิภาพ

5. ต้องใช้ความพยายามเป็นพิเศษ

การเปลี่ยนแปลงใดๆ ที่เกิดขึ้นกับนักวิ่ง เช่น การติดตั้งซอฟต์แวร์ จะต้องดำเนินการกับนักวิ่งทุกคนพร้อมกัน แม้ว่าเราจะมีเครื่องมืออัตโนมัติสำหรับสิ่งนี้ แต่ก็ต้องใช้ความพยายามเป็นพิเศษในการรักษาสคริปต์อัตโนมัติ

โซลูชันโครงสร้างพื้นฐานที่ปรับแต่งตามความต้องการของลูกค้าที่หลากหลาย

Miquido คือบริษัทซอฟต์แวร์ที่ทำงานร่วมกับลูกค้าหลายรายที่มีความต้องการที่แตกต่างกัน เราปรับแต่งบริการของเราให้ตรงตามความต้องการเฉพาะของลูกค้าแต่ละราย เรามักจะโฮสต์ฐานโค้ดและโครงสร้างพื้นฐานที่จำเป็นสำหรับธุรกิจขนาดเล็กหรือสตาร์ทอัพ เนื่องจากอาจขาดทรัพยากรหรือความรู้ในการดูแลรักษา

ลูกค้าองค์กรมักจะมีโครงสร้างพื้นฐานของตนเองเพื่อโฮสต์โครงการของตน อย่างไรก็ตาม บางแห่งไม่มีความสามารถในการทำเช่นนั้นหรือมีภาระผูกพันตามข้อบังคับอุตสาหกรรมในการใช้โครงสร้างพื้นฐานของตน พวกเขาไม่ต้องการใช้บริการ SaaS ของบริษัทอื่น เช่น Xcode Cloud หรือ Codemagic พวกเขาต้องการโซลูชันที่เหมาะกับสถาปัตยกรรมที่มีอยู่แทน

เพื่อรองรับลูกค้าเหล่านี้ เรามักจะโฮสต์โปรเจ็กต์บนโครงสร้างพื้นฐานของเราหรือตั้งค่าการกำหนดค่า iOS แบบบูรณาการอย่างต่อเนื่องเดียวกันบนโครงสร้างพื้นฐานของพวกเขา อย่างไรก็ตาม เราใช้ความระมัดระวังเป็นพิเศษเมื่อต้องจัดการกับข้อมูลและไฟล์ที่ละเอียดอ่อน เช่น การลงนามข้อมูลระบุตัวตน

การใช้ประโยชน์จาก Fastlane เพื่อการจัดการบิลด์ที่มีประสิทธิภาพ

Fastlane มาเป็นเครื่องมือที่มีประโยชน์ ประกอบด้วยโมดูลต่างๆ ที่เรียกว่าการดำเนินการ ซึ่งช่วยปรับปรุงกระบวนการและแยกกระบวนการระหว่างไคลเอนต์ที่แตกต่างกัน หนึ่งในการดำเนินการเหล่านี้เรียกว่า การจับคู่ ช่วยรักษาเอกลักษณ์การลงนามการพัฒนาและการผลิต รวมถึงการจัดเตรียมโปรไฟล์ นอกจากนี้ยังทำงานในระดับระบบปฏิบัติการเพื่อแยกข้อมูลระบุตัวตนเหล่านั้นออกเป็นพวงกุญแจแยกต่างหากสำหรับเวลาบิวด์ และดำเนินการล้างข้อมูลหลังจากการบิลด์ ซึ่งมีประโยชน์เป็นพิเศษเนื่องจากเรารันบิลด์ทั้งหมดของเราบนเครื่องจริง

Fastlane: เครื่องมือพัฒนาอัตโนมัติ
เครดิตรูปภาพ: Fastlane

ในตอนแรกเราหันมาใช้ Fastlane ด้วยเหตุผลเฉพาะเจาะจง แต่พบว่ามีฟีเจอร์เพิ่มเติมที่อาจเป็นประโยชน์สำหรับเรา

  1. สร้างการอัปโหลดไปยัง Testflight

ในอดีต AppStoreConnect API ไม่ได้เปิดให้ใช้งานแบบสาธารณะสำหรับนักพัฒนา ซึ่งหมายความว่าวิธีเดียวที่จะอัปโหลดบิลด์ไปยัง Testflight คือผ่าน Xcode หรือโดยใช้ Fastlane Fastlane เป็นเครื่องมือที่คัดลอก ASC API โดยพื้นฐานแล้วเปลี่ยนให้เป็นการกระทำที่เรียกว่า นำร่อง อย่างไรก็ตาม วิธีการนี้มักจะใช้งานไม่ได้เมื่อมีการอัปเดต Xcode ครั้งถัดไป หากนักพัฒนาต้องการอัปโหลดบิลด์ของตนไปยัง Testflight โดยใช้บรรทัดคำสั่ง Fastlane คือตัวเลือกที่ดีที่สุด

  1. สลับระหว่างเวอร์ชัน Xcode ได้อย่างง่ายดาย

การมีอินสแตนซ์ Xcode มากกว่าหนึ่งรายการบนเครื่องเดียว จำเป็นต้องเลือก Xcode ที่จะใช้สำหรับบิลด์ น่าเสียดายที่ Apple ทำให้การสลับระหว่างเวอร์ชัน Xcode ไม่สะดวก คุณต้องใช้ "xcode-select" เพื่อดำเนินการดังกล่าว ซึ่งต้องใช้สิทธิ์ sudo เพิ่มเติม Fastlane ก็ครอบคลุมเรื่องนั้นเช่นกัน

  1. ยูทิลิตี้เพิ่มเติมสำหรับนักพัฒนา

Fastlane มียูทิลิตี้ที่มีประโยชน์อื่นๆ อีกมากมาย รวมถึงการกำหนดเวอร์ชันและความสามารถในการส่งผลการสร้างไปยัง webhooks

ข้อเสียของ Fastlane

การนำ Fastlane มาปรับใช้กับโครงการของเราเป็นเรื่องที่ดีและมั่นคง ดังนั้นเราจึงมุ่งไปในทิศทางนั้น เราใช้มันสำเร็จมาหลายปีแล้ว อย่างไรก็ตาม ในช่วงหลายปีที่ผ่านมา เราพบปัญหาบางประการ:

  1. Fastlane ต้องการความรู้ Ruby

Fastlane เป็นเครื่องมือที่เขียนด้วย Ruby และต้องใช้ความรู้ Ruby เป็นอย่างดีจึงจะใช้งานได้อย่างมีประสิทธิภาพ เมื่อมีจุดบกพร่องในการกำหนดค่า Fastlane ของคุณหรือในเครื่องมือ การแก้ไขจุดบกพร่องโดยใช้ irb หรือ pry อาจเป็นเรื่องที่ค่อนข้างท้าทาย

  1. ต้องอาศัยอัญมณีมากมาย

Fastlane ต้องใช้อัญมณีประมาณ 70 เม็ด เพื่อลดความเสี่ยงในการทำให้ระบบ Ruby พัง โปรเจ็กต์จึงใช้ Bundler Gem ในเครื่อง การดึงอัญมณีเหล่านี้มาทั้งหมดทำให้เกิดค่าใช้จ่ายจำนวนมาก

  1. ปัญหาระบบ Ruby และ Rubygems

ด้วยเหตุนี้ ปัญหาทั้งหมดเกี่ยวกับระบบ Ruby และ rubygems ที่กล่าวถึงก่อนหน้านี้ก็สามารถใช้ได้ที่นี่เช่นกัน

  1. ความซ้ำซ้อนสำหรับโครงการ Flutter

โปรเจ็กต์ Flutter ยังถูกบังคับให้ใช้การ จับคู่ fastlane เพียงเพื่อรักษาความเข้ากันได้กับโปรเจ็กต์ iOS และปกป้องพวงกุญแจของนักวิ่ง นั่นเป็นสิ่งที่ไม่จำเป็นอย่างไร้เหตุผล เนื่องจาก Flutter มีระบบการสร้างของตัวเองในตัว และค่าใช้จ่ายที่กล่าวถึงก่อนหน้านี้ถูกนำมาใช้เพื่อจัดการข้อมูลประจำตัวของการลงนามและการจัดเตรียมโปรไฟล์เท่านั้น

ปัญหาเหล่านี้ส่วนใหญ่ได้รับการแก้ไขในระหว่างดำเนินการ แต่เราต้องการโซลูชันที่มีประสิทธิภาพและเชื่อถือได้มากกว่านี้

แนวคิด: การปรับใช้เครื่องมือบูรณาการอย่างต่อเนื่องแบบใหม่ที่แข็งแกร่งยิ่งขึ้นสำหรับ iOS

ข่าวดีก็คือ Apple สามารถควบคุมสถาปัตยกรรมชิปได้อย่างสมบูรณ์ และได้พัฒนาเฟรมเวิร์กการจำลองเสมือนใหม่สำหรับ macOS เฟรมเวิร์กนี้ช่วยให้ผู้ใช้สามารถสร้าง กำหนดค่า และรันเครื่องเสมือน Linux หรือ macOS ที่เริ่มต้นอย่างรวดเร็วและกำหนดลักษณะเฉพาะด้วยประสิทธิภาพที่เหมือนเนทีฟ – และฉันก็หมายถึงเหมือนเนทีฟจริงๆ

ซึ่งดูมีแนวโน้มดีและอาจเป็นรากฐานสำคัญของเครื่องมือบูรณาการอย่างต่อเนื่องใหม่สำหรับ iOS อย่างไรก็ตาม มันเป็นเพียงส่วนหนึ่งของวิธีแก้ปัญหาที่สมบูรณ์เท่านั้น การมีเครื่องมือการจัดการ VM ทำให้เราจำเป็นต้องมีบางอย่างที่สามารถใช้เฟรมเวิร์กนั้นในการประสานงานกับ Gitlab runners ของเราได้

เมื่อเป็นเช่นนั้น ปัญหาส่วนใหญ่ของเราเกี่ยวกับประสิทธิภาพการจำลองเสมือนที่ไม่ดีก็จะล้าสมัยไป นอกจากนี้ยังช่วยให้เราแก้ไขปัญหาส่วนใหญ่ที่เราตั้งใจจะแก้ไขด้วย Fastlane โดยอัตโนมัติ

การพัฒนาโซลูชันที่ปรับแต่งสำหรับการจัดการข้อมูลระบุตัวตนการเซ็นชื่อตามความต้องการ

เรามีปัญหาสุดท้ายที่ต้องแก้ไข – การลงนามการจัดการข้อมูลประจำตัว เราไม่ต้องการใช้ Fastlane สำหรับสิ่งนี้ เนื่องจากมันดูมากเกินไปสำหรับความต้องการของเรา แต่เรากลับมองหาโซลูชันที่เหมาะกับความต้องการของเรามากขึ้น ความต้องการของเราตรงไปตรงมา: กระบวนการจัดการข้อมูลระบุตัวตนต้องทำตามความต้องการ เฉพาะเวลาสร้าง โดยไม่ต้องติดตั้งข้อมูลประจำตัวใดๆ ไว้ล่วงหน้าบนพวงกุญแจ และเข้ากันได้กับเครื่องใดๆ ก็ตามที่มันจะทำงาน

ปัญหาการกระจายและการขาด AppstoreConnect API ที่เสถียรกลายเป็นเรื่องล้าสมัยเมื่อ Apple เปิดตัว `altool` ซึ่งอนุญาตให้มีการสื่อสารระหว่างผู้ใช้และ ASC

เราก็เลยมีไอเดียและต้องหาทางเชื่อมโยงทั้งสามด้านเข้าด้วยกัน:

  1. ค้นหาวิธีใช้เฟรมเวิร์ก Virtualization ของ Apple
  2. ทำให้มันใช้งานได้กับนักวิ่ง Gitlab
  3. ค้นหาโซลูชันสำหรับการลงนามการจัดการข้อมูลประจำตัวในโปรเจ็กต์และรันเนอร์หลายรายการ

แนวทางแก้ไข: ภาพรวมแนวทางของเรา (รวมเครื่องมือ)

เราเริ่มค้นหาวิธีแก้ไขปัญหาเพื่อแก้ไขปัญหาทั้งหมดที่กล่าวถึงข้างต้น

  1. การใช้กรอบงาน Virtualization ของ Apple

สำหรับอุปสรรคแรก เราพบวิธีแก้ปัญหาอย่างรวดเร็ว: เราเจอเครื่องมือทาร์ตของ Cirrus Labs ตั้งแต่วินาทีแรก เรารู้ว่านี่จะเป็นทางเลือกของเรา

ข้อได้เปรียบที่สำคัญที่สุดของการใช้เครื่องมือทาร์ตที่นำเสนอโดย Cirrus Lab คือ:

  • ความเป็นไปได้ในการสร้าง vms จากอิมเมจ .ipsw แบบ raw
  • ความเป็นไปได้ในการสร้าง vms โดยใช้เทมเพลตที่แพ็กไว้ล่วงหน้า (โดยติดตั้งเครื่องมืออรรถประโยชน์บางอย่าง เช่น Brew หรือ Xcode) มีอยู่ในหน้า Cirrus Labs GitHub
  • เครื่องมือ Tart ใช้ Packer เพื่อรองรับการสร้างภาพแบบไดนามิก
  • เครื่องมือ Tart รองรับทั้งอิมเมจ Linux และ MacOS
  • เครื่องมือนี้ใช้คุณสมบัติที่โดดเด่นของระบบไฟล์ APFS ซึ่งช่วยให้สามารถทำซ้ำไฟล์ได้โดยไม่ต้องสำรองพื้นที่ดิสก์จริงๆ ด้วยวิธีนี้ คุณไม่จำเป็นต้องจัดสรรพื้นที่ดิสก์เป็น 3 เท่าของขนาดภาพต้นฉบับ คุณต้องการพื้นที่ดิสก์เพียงพอสำหรับรูปภาพต้นฉบับ ในขณะที่โคลนใช้เฉพาะพื้นที่ที่มีความแตกต่างระหว่างโคลนกับรูปภาพต้นฉบับ สิ่งนี้มีประโยชน์อย่างเหลือเชื่อ โดยเฉพาะอย่างยิ่งเมื่อรูปภาพของ macOS มักจะมีขนาดค่อนข้างใหญ่

ตัวอย่างเช่น อิมเมจ macOS Ventura ที่ใช้งานได้พร้อม Xcode และยูทิลิตี้อื่นๆ ที่ติดตั้งต้องใช้พื้นที่ดิสก์ขั้นต่ำ 60GB ในสถานการณ์ปกติ อิมเมจและโคลนสองตัวจะใช้พื้นที่ดิสก์สูงสุด 180GB ซึ่งถือเป็นปริมาณที่มีนัยสำคัญ และนี่เป็นเพียงจุดเริ่มต้น เนื่องจากคุณอาจต้องการมีอิมเมจต้นฉบับมากกว่าหนึ่งอิมเมจ หรือติดตั้ง Xcode หลายเวอร์ชันบน VM เดียว ซึ่งจะเพิ่มขนาดได้อีก

  • เครื่องมือนี้ช่วยให้สามารถจัดการที่อยู่ IP สำหรับ VM ดั้งเดิมและโคลนได้ ช่วยให้ SSH เข้าถึง VM ได้
  • ความสามารถในการติดตั้งข้ามไดเร็กทอรีระหว่างเครื่องโฮสต์และ VM
  • เครื่องมือนี้ใช้งานง่ายและมี CLI ที่ง่ายมาก

แทบจะไม่มีสิ่งใดที่เครื่องมือนี้ขาดในแง่ของการใช้งานสำหรับการจัดการ VM แทบไม่มีอะไรเลย ยกเว้นสิ่งหนึ่ง: แม้ว่าจะมีแนวโน้มที่ดี แต่ปลั๊กอิน Packer เพื่อสร้างภาพได้ทันทีนั้นใช้เวลานานเกินไป ดังนั้นเราจึงตัดสินใจที่จะไม่ใช้สิ่งนั้น

เราลองทาร์ตแล้วและมันก็ได้ผลอย่างน่าอัศจรรย์ ประสิทธิภาพการทำงานก็เหมือนเจ้าของภาษา และการจัดการก็ทำได้ง่าย

หลังจากผสมผสานทาร์ตเข้ากับผลลัพธ์ที่น่าประทับใจได้สำเร็จ ต่อไปเราจะมุ่งเน้นไปที่การจัดการกับความท้าทายอื่นๆ

  1. หาวิธีรวมทาร์ตเข้ากับนักวิ่ง Gitlab

หลังจากแก้ไขปัญหาแรกแล้ว เราก็เผชิญกับคำถามว่าจะรวมทาร์ตเข้ากับ Gitlab runners ได้อย่างไร

เริ่มต้นด้วยการอธิบายว่านักวิ่ง Gitlab ทำอะไรได้บ้าง:

แผนภาพแบบง่ายของการมอบหมายงาน Gitlab ระบบ CI สำหรับ iOS

เราจำเป็นต้องรวมปริศนาเพิ่มเติมลงในไดอะแกรม ซึ่งเกี่ยวข้องกับการจัดสรรงานจากรันเนอร์โฮสต์ไปยัง VM งาน GitLab เป็นเชลล์สคริปต์ที่เก็บตัวแปรที่สำคัญ รายการ PATH และคำสั่ง

วัตถุประสงค์ของเราคือการถ่ายโอนสคริปต์นี้ไปยัง VM และเรียกใช้

อย่างไรก็ตาม งานนี้พิสูจน์แล้วว่ามีความท้าทายมากกว่าที่เราคิดไว้ในตอนแรก

นักวิ่ง

ตัวเรียกใช้งาน Gitlab มาตรฐาน เช่น Docker หรือ SSH นั้นติดตั้งง่ายและไม่ต้องการการกำหนดค่าเลย อย่างไรก็ตาม เราต้องการการควบคุมการกำหนดค่าที่มากขึ้น ซึ่งทำให้เราต้องสำรวจตัวดำเนินการแบบกำหนดเองที่ GitLab มอบให้

ตัวดำเนินการแบบกำหนดเองเป็นตัวเลือกที่ยอดเยี่ยมสำหรับการกำหนดค่าที่ไม่ได้มาตรฐาน เนื่องจากแต่ละขั้นตอนการรันเนอร์ (จัดเตรียม ดำเนินการ และล้างข้อมูล) ได้รับการอธิบายในรูปแบบของเชลล์สคริปต์ สิ่งเดียวที่ขาดหายไปคือเครื่องมือบรรทัดคำสั่งที่สามารถทำงานที่เราต้องการและดำเนินการในสคริปต์การกำหนดค่านักวิ่ง

ปัจจุบันมีเครื่องมือสองสามอย่างที่สามารถทำเช่นนั้นได้ ตัวอย่างเช่น CirrusLabs Gitlab tart executor เครื่องมือนี้คือสิ่งที่เรากำลังมองหาในขณะนั้น อย่างไรก็ตาม มันยังไม่มี และหลังจากทำการวิจัย เราไม่พบเครื่องมือใด ๆ ที่จะช่วยให้เราบรรลุภารกิจของเราได้

การเขียนโซลูชันของตัวเอง

เนื่องจากเราไม่สามารถหาวิธีแก้ปัญหาที่สมบูรณ์แบบได้ เราจึงเขียนวิธีแก้ปัญหาขึ้นมาเอง เราเป็นวิศวกรไงล่ะ! แนวคิดนี้ดูแข็งแกร่ง และเรามีเครื่องมือที่จำเป็นทั้งหมด ดังนั้นเราจึงดำเนินการพัฒนาต่อ

เราได้เลือกใช้ Swift และไลบรารีโอเพ่นซอร์สสองสามรายการที่ Apple จัดเตรียมให้ ได้แก่ Swift Argument Parser เพื่อจัดการการดำเนินการบรรทัดคำสั่งและ Swift NIO เพื่อจัดการการเชื่อมต่อ SSH กับ VM เราเริ่มการพัฒนา และภายในสองสามวัน เราก็ได้รับต้นแบบการทำงานชิ้นแรกของเครื่องมือซึ่งในที่สุดก็พัฒนาเป็น MQVMRunner

โครงสร้างพื้นฐาน iOS CI: MQVMRunner

ในระดับสูง เครื่องมือจะทำงานดังนี้:

  1. (เตรียมขั้นตอน)
    1. อ่านตัวแปรที่ให้ไว้ใน gitlab-ci.yml (ชื่อรูปภาพและตัวแปรเพิ่มเติม)
    2. เลือกฐาน VM ที่ร้องขอ
    3. โคลนฐาน VM ที่ร้องขอ
    4. ตั้งค่าไดเร็กทอรีแบบ cross-mount และคัดลอกสคริปต์งาน Gitlab ลงไป โดยตั้งค่าสิทธิ์ที่จำเป็น
    5. เรียกใช้โคลนและตรวจสอบการเชื่อมต่อ SSH
    6. ตั้งค่าการขึ้นต่อกันที่จำเป็น (เช่น เวอร์ชัน Xcode) หากจำเป็น
  2. (ดำเนินการขั้นตอน)
    1. รันงาน Gitlab โดยเรียกใช้สคริปต์จากไดเร็กทอรีแบบ cross-mount บนโคลน VM ที่เตรียมไว้ผ่าน SSH
  3. (ขั้นตอนการล้างข้อมูล)
    1. ลบภาพโคลน

ความท้าทายในการพัฒนา

ในระหว่างการพัฒนา เราพบปัญหาหลายประการ ซึ่งทำให้การทำงานไม่ราบรื่นเท่าที่เราต้องการ

  1. การจัดการที่อยู่ IP

การจัดการที่อยู่ IP ถือเป็นงานสำคัญที่ต้องจัดการด้วยความระมัดระวัง ในตัวต้นแบบ การจัดการ SSH ถูกนำมาใช้โดยใช้คำสั่งเชลล์ SSH โดยตรงและแบบฮาร์ดโค้ด อย่างไรก็ตาม ในกรณีของเชลล์ที่ไม่โต้ตอบ แนะนำให้ตรวจสอบสิทธิ์คีย์ นอกจากนี้ ขอแนะนำให้เพิ่มโฮสต์ลงในไฟล์known_hostsเพื่อหลีกเลี่ยงการหยุดชะงัก อย่างไรก็ตาม เนื่องจากการจัดการแบบไดนามิกของที่อยู่ IP ของเครื่องเสมือน จึงมีความเป็นไปได้ที่จะเพิ่มรายการเป็นสองเท่าสำหรับ IP หนึ่งๆ ซึ่งนำไปสู่ข้อผิดพลาด ดังนั้นเราจึงจำเป็นต้องกำหนดknown_hostsแบบไดนามิกสำหรับงานเฉพาะเพื่อป้องกันปัญหาดังกล่าว

  1. โซลูชัน Pure Swift

เมื่อพิจารณาถึงสิ่งนั้น และความจริงที่ว่าคำสั่งเชลล์แบบฮาร์ดโค้ดในโค้ด Swift นั้นไม่ได้สวยงามนัก เราจึงคิดว่าคงจะดีถ้าใช้ไลบรารี Swift โดยเฉพาะ และตัดสินใจเลือกใช้ Swift NIO เราได้แก้ไขปัญหาบางอย่างแล้ว แต่ในขณะเดียวกัน ก็ได้แนะนำรายการใหม่สองสามรายการ เช่น – บางครั้งบันทึกที่วางไว้บน stdout จะถูกถ่ายโอน *หลังจาก* ช่อง SSH ถูกยกเลิกเนื่องจากการดำเนินการคำสั่งเสร็จสิ้น – และในขณะที่เรากำลังพิจารณาจาก ผลลัพธ์ในการทำงานต่อไป การดำเนินการล้มเหลวแบบสุ่ม

  1. การเลือกเวอร์ชัน Xcode

เนื่องจากปลั๊กอิน Packer ไม่ใช่ตัวเลือกสำหรับการสร้างอิมเมจแบบไดนามิกเนื่องจากการสิ้นเปลืองเวลา เราจึงตัดสินใจใช้ฐาน VM เดียวที่มี Xcode หลายเวอร์ชันติดตั้งไว้ล่วงหน้า เราต้องหาวิธีให้นักพัฒนาระบุเวอร์ชัน Xcode ที่พวกเขาต้องการใน gitlab-ci.yml และเราก็ได้สร้างตัวแปรแบบกำหนดเองขึ้นมาเพื่อใช้ในโปรเจ็กต์ต่างๆ จากนั้น MQVMRunner จะดำเนินการ `xcode-select` บน VM ที่โคลนเพื่อตั้งค่าเวอร์ชัน Xcode ที่เกี่ยวข้อง

และอีกมากมายอีกมากมาย

ปรับปรุงการย้ายโปรเจ็กต์และการผสานรวมอย่างต่อเนื่องสำหรับเวิร์กโฟลว์ iOS กับ Mac Studios

เราได้ตั้งค่านั้นบน Mac Studios ใหม่สองแห่ง และเริ่มโยกย้ายโปรเจ็กต์ เราต้องการทำให้กระบวนการโยกย้ายสำหรับนักพัฒนาของเราโปร่งใสที่สุดเท่าที่จะเป็นไปได้ เราไม่สามารถทำให้มันราบรื่นได้ทั้งหมด แต่ในที่สุด เราก็มาถึงจุดที่พวกเขาต้องทำสองสามอย่างใน gitlab-ci.yml:

  • แท็กของนักวิ่ง: ใช้ Mac Studios แทน Intel
  • ชื่อของอิมเมจ: พารามิเตอร์ทางเลือก แนะนำสำหรับความเข้ากันได้ในอนาคต ในกรณีที่เราต้องการ VM ฐานมากกว่าหนึ่งรายการ ในตอนนี้ ค่าเริ่มต้นจะเป็น VM ฐานเดียวที่เรามีเสมอ
  • เวอร์ชันของ Xcode: พารามิเตอร์ทางเลือก; หากไม่ได้ระบุไว้ ระบบจะใช้เวอร์ชันใหม่ล่าสุดที่มีอยู่

เครื่องมือนี้ได้รับการตอบรับเบื้องต้นที่ดีมาก ดังนั้นเราจึงตัดสินใจทำให้เป็นโอเพ่นซอร์ส เราได้เพิ่มสคริปต์การติดตั้งเพื่อตั้งค่า Gitlab Custom Runner รวมถึงการดำเนินการและตัวแปรที่จำเป็นทั้งหมด เมื่อใช้เครื่องมือของเรา คุณสามารถตั้งค่า GitLab runner ของคุณเองได้ในเวลาไม่กี่นาที สิ่งเดียวที่คุณต้องการคือ tart และ VM พื้นฐานที่งานจะดำเนินการ

การบูรณาการอย่างต่อเนื่องขั้นสุดท้ายสำหรับโครงสร้าง iOS มีลักษณะดังนี้:

โครงสร้างพื้นฐาน CI สุดท้าย: MQVMRunner

3. โซลูชั่นสำหรับการจัดการข้อมูลประจำตัวที่มีประสิทธิภาพ

เรากำลังดิ้นรนเพื่อค้นหาโซลูชันที่มีประสิทธิภาพในการจัดการข้อมูลประจำตัวที่ลงนามของลูกค้า นี่เป็นเรื่องท้าทายอย่างยิ่ง เนื่องจากการลงนามข้อมูลประจำตัวถือเป็นข้อมูลที่เป็นความลับสูงซึ่งไม่ควรเก็บไว้ในที่ที่ไม่ปลอดภัยเป็นเวลานานเกินความจำเป็น

นอกจากนี้ เราต้องการโหลดข้อมูลระบุตัวตนเหล่านี้ในช่วงเวลาที่สร้างเท่านั้น โดยไม่ต้องใช้โซลูชันข้ามโครงการ ซึ่งหมายความว่าไม่ควรเข้าถึงข้อมูลประจำตัวภายนอกแซนด์บ็อกซ์ของแอป (หรือบิลด์) เราได้แก้ไขปัญหาหลังนี้แล้วโดยการเปลี่ยนไปใช้ VM อย่างไรก็ตาม เรายังต้องหาทางจัดเก็บและโหลดข้อมูลระบุตัวตนการลงนามลงใน VM เฉพาะในช่วงเวลาที่สร้างเท่านั้น

ปัญหากับการแข่งขัน Fastlane

ในขณะนั้น เรายังคงใช้การจับคู่ Fastlane ซึ่งจัดเก็บข้อมูลประจำตัวที่เข้ารหัสและการจัดเตรียมไว้ในพื้นที่เก็บข้อมูลแยกต่างหาก โหลดในระหว่างกระบวนการสร้างลงในอินสแตนซ์พวงกุญแจแยกต่างหาก และลบอินสแตนซ์นั้นออกหลังจากการสร้าง

วิธีนี้ดูเหมือนสะดวก แต่มีปัญหาบางประการ:

  • ต้องใช้การตั้งค่า Fastlane ทั้งหมดจึงจะทำงานได้

Fastlane คือ Rubygem และปัญหาทั้งหมดที่ระบุไว้ในบทแรกจะมีผลใช้ที่นี่

  • เช็คเอาท์พื้นที่เก็บข้อมูล ณ เวลาที่สร้าง

เราเก็บข้อมูลประจำตัวของเราไว้ในพื้นที่เก็บข้อมูลแยกต่างหากซึ่งมีการตรวจสอบระหว่างกระบวนการสร้างมากกว่ากระบวนการตั้งค่า ซึ่งหมายความว่าเราต้องสร้างการเข้าถึงที่แยกจากกันไปยังพื้นที่เก็บข้อมูลประจำตัว ไม่ใช่แค่สำหรับ Gitlab เท่านั้น แต่สำหรับนักวิ่งที่เฉพาะเจาะจง คล้ายกับวิธีที่เราจะจัดการกับการพึ่งพาส่วนตัวของบุคคลที่สาม

  • ยากที่จะจัดการนอกการแข่งขัน

หากคุณใช้ Match เพื่อจัดการข้อมูลประจำตัวหรือการจัดเตรียม แทบไม่จำเป็นต้องมีการแทรกแซงด้วยตนเองเลย การแก้ไข ถอดรหัส และเข้ารหัสโปรไฟล์ด้วยตนเอง เพื่อให้รายการที่ตรงกันยังคงใช้งานได้ในภายหลังนั้นเป็นเรื่องที่น่าเบื่อและใช้เวลานาน การใช้ Fastlane เพื่อดำเนินการตามกระบวนการนี้มักจะส่งผลให้ต้องลบการตั้งค่าการจัดเตรียมแอปพลิเคชันออกโดยสิ้นเชิงและสร้างการตั้งค่าใหม่

  • ยากนิดหน่อยที่จะแก้ไขข้อบกพร่อง

ในกรณีที่มีปัญหาในการเซ็นโค้ด คุณอาจพบว่าเป็นการยากที่จะระบุข้อมูลประจำตัวและการจัดเตรียมที่ตรงกันที่เพิ่งติดตั้ง เนื่องจากคุณจะต้องถอดรหัสก่อน

  • ข้อกังวลด้านความปลอดภัย

จับคู่บัญชีนักพัฒนาซอฟต์แวร์ที่เข้าถึงได้โดยใช้ข้อมูลประจำตัวที่ให้ไว้เพื่อทำการเปลี่ยนแปลงในนามของพวกเขา แม้ว่า Fastlane จะเป็นโอเพ่นซอร์ส แต่ลูกค้าบางรายปฏิเสธเนื่องจากปัญหาด้านความปลอดภัย

  • สุดท้ายแต่ไม่ท้ายสุด การกำจัด Match จะช่วยขจัดอุปสรรคที่ใหญ่ที่สุดในการกำจัด Fastlane ออกไปโดยสิ้นเชิง

ข้อกำหนดเบื้องต้นของเรามีดังนี้:

  • โหลดจำเป็นต้องลงนามในข้อมูลระบุตัวตนจากสถานที่ที่ปลอดภัย โดยควรอยู่ในรูปแบบที่ไม่ใช่ข้อความธรรมดา และวางไว้ในพวงกุญแจ
  • ข้อมูลประจำตัวนั้นควรสามารถเข้าถึงได้โดย Xcode
  • รหัสผ่านประจำตัว ชื่อพวงกุญแจ และตัวแปรรหัสผ่านพวงกุญแจควรตั้งค่าได้เพื่อวัตถุประสงค์ในการแก้ไขจุดบกพร่อง

Match มีทุกสิ่งที่เราต้องการ แต่การนำ Fastlane มาใช้เพียงเพื่อใช้งาน Match ดูเหมือนจะเป็นการใช้ความพยายามมากเกินไป โดยเฉพาะอย่างยิ่งสำหรับโซลูชันข้ามแพลตฟอร์มที่มีระบบการสร้างของตัวเอง เราต้องการบางสิ่งที่คล้ายกับ Match แต่ไม่มีภาระ Ruby ที่หนักหน่วง มันก็ยังแบกอยู่

การสร้างโซลูชันของตนเอง

ดังนั้นเราจึงคิดว่า – มาเขียนกันเถอะ! เราทำอย่างนั้นด้วย MQVMRunner ดังนั้นเราจึงทำที่นี่ได้เช่นกัน นอกจากนี้เรายังเลือก Swift ให้ทำเช่นนั้น เนื่องจากเราสามารถรับ API ที่จำเป็นจำนวนมากได้ฟรีโดยใช้เฟรมเวิร์กความปลอดภัยของ Apple

แน่นอนว่ามันไม่ได้ราบรื่นอย่างที่คาดไว้เช่นกัน

  • มีกรอบการรักษาความปลอดภัยอยู่

กลยุทธ์ที่ง่ายที่สุดคือการเรียกคำสั่ง bash เช่นเดียวกับที่ Fastlane ทำ อย่างไรก็ตาม การมีกรอบการทำงานด้านความปลอดภัยพร้อมใช้งาน เราคิดว่าการใช้เพื่อการพัฒนาจะสวยงามกว่า

  • ขาดประสบการณ์.

เราไม่ค่อยมีประสบการณ์กับเฟรมเวิร์กการรักษาความปลอดภัยสำหรับ macOS และปรากฏว่ามันแตกต่างอย่างมากจากสิ่งที่เราคุ้นเคยบน iOS สิ่งนี้ส่งผลเสียต่อเราในหลายกรณีที่เราไม่ทราบถึงข้อจำกัดของ macOS หรือสันนิษฐานว่ามันทำงานเหมือนกับบน iOS ซึ่งสมมติฐานส่วนใหญ่นั้นผิด

  • เอกสารแย่มาก

เอกสารประกอบของ Apple Security Framework กล่าวอย่างสุภาพและถ่อมตัว มันเป็น API ที่เก่ามากซึ่งย้อนกลับไปถึง OSX เวอร์ชันแรก และบางครั้งเรารู้สึกว่ายังไม่ได้รับการอัปเดตตั้งแต่นั้นมา โค้ดจำนวนมากไม่ได้รับการบันทึกไว้ แต่เราคาดหวังว่ามันทำงานอย่างไรโดยการอ่านซอร์สโค้ด โชคดีสำหรับเรา มันเป็นโอเพ่นซอร์ส

  • การเสื่อมสภาพโดยไม่มีการทดแทน

ส่วนที่ดีของกรอบงานนี้เลิกใช้แล้ว Apple กำลังพยายามเปลี่ยนจากพวงกุญแจ "สไตล์ macOS" ทั่วไป (พวงกุญแจหลายอันที่เข้าถึงได้ด้วยรหัสผ่าน) และใช้พวงกุญแจ "สไตล์ iOS" (พวงกุญแจเดี่ยวที่ซิงโครไนซ์ผ่าน iCloud) ดังนั้นพวกเขาจึงเลิกใช้มันใน macOS Yosemite ย้อนกลับไปในปี 2014 แต่ในช่วงเก้าปีที่ผ่านมายังไม่มีการทดแทนใดๆ เลย ดังนั้น API เดียวที่พร้อมใช้งานสำหรับเราในตอนนี้จึงเลิกใช้แล้วเพราะยังไม่มี API ใหม่

เราสันนิษฐานว่าข้อมูลประจำตัวการลงนามสามารถจัดเก็บเป็นสตริงที่เข้ารหัส base64 ในตัวแปร Gitlab ต่อโครงการ มีความปลอดภัยตามโปรเจ็กต์ และหากตั้งค่าเป็นตัวแปรที่มาสก์ ก็สามารถอ่านและแสดงในบันทึกบิลด์ในรูปแบบที่ไม่ใช่ข้อความธรรมดาได้

ดังนั้นเราจึงมีข้อมูลประจำตัว เราแค่ต้องใส่มันเข้าไปในพวงกุญแจเท่านั้น การใช้ Security API หลังจากพยายามไม่กี่ครั้งและพยายามอ่านเอกสารประกอบกรอบความปลอดภัย เราก็ได้เตรียมต้นแบบของบางสิ่งที่ต่อมากลายเป็น MQSwiftSign

การเรียนรู้ระบบความปลอดภัย macOS แต่เป็นวิธีที่ยาก

เราต้องทำความเข้าใจอย่างลึกซึ้งว่าพวงกุญแจ macOS ทำงานอย่างไรเพื่อพัฒนาเครื่องมือของเรา สิ่งนี้เกี่ยวข้องกับการค้นคว้าวิธีที่พวงกุญแจจัดการรายการ การเข้าถึงและการอนุญาต และโครงสร้างของข้อมูลพวงกุญแจ ตัวอย่างเช่น เราค้นพบว่าพวงกุญแจเป็นไฟล์ macOS เดียวที่ระบบปฏิบัติการละเว้นชุด ACL นอกจากนี้ เราได้เรียนรู้ว่า ACL ในรายการพวงกุญแจเฉพาะเป็นไฟล์ข้อความธรรมดาที่บันทึกไว้ในไฟล์พวงกุญแจ เราเผชิญกับความท้าทายหลายประการตลอดเส้นทาง แต่เราก็ได้เรียนรู้มากมายเช่นกัน

ความท้าทายสำคัญประการหนึ่งที่เราพบคือการแจ้งเตือน เครื่องมือของเราได้รับการออกแบบมาให้ทำงานบนระบบ CI iOS เป็นหลัก ซึ่งหมายความว่าจะต้องไม่มีการโต้ตอบ เราไม่สามารถขอให้ผู้ใช้ยืนยันรหัสผ่านบน CI ได้

อย่างไรก็ตาม ระบบความปลอดภัยของ macOS ได้รับการออกแบบมาอย่างดี ทำให้ไม่สามารถแก้ไขหรืออ่านข้อมูลที่เป็นความลับ รวมถึงข้อมูลระบุตัวตนในการลงนาม โดยไม่ได้รับอนุญาตจากผู้ใช้อย่างชัดแจ้ง หากต้องการเข้าถึงทรัพยากรโดยไม่ต้องยืนยัน โปรแกรมที่เข้าถึงจะต้องรวมอยู่ในรายการควบคุมการเข้าถึงของทรัพยากร นี่เป็นข้อกำหนดที่เข้มงวดซึ่งไม่มีโปรแกรมใดสามารถทำลายได้ แม้แต่โปรแกรมของ Apple ที่มาพร้อมกับระบบก็ตาม หากโปรแกรมใดๆ จำเป็นต้องอ่านหรือแก้ไขรายการพวงกุญแจ ผู้ใช้จะต้องระบุรหัสผ่านพวงกุญแจเพื่อปลดล็อค และเพิ่มลงใน ACL ของรายการ (ทางเลือก)

เอาชนะความท้าทายในการอนุญาตของผู้ใช้

ดังนั้นเราจึงต้องหาทางให้ Xcode เข้าถึงข้อมูลประจำตัวที่ตั้งค่าโดยพวงกุญแจของเราโดยไม่ต้องขออนุญาตจากผู้ใช้โดยใช้การแจ้งรหัสผ่าน ในการทำเช่นนั้น เราสามารถเปลี่ยนรายการควบคุมการเข้าถึงของรายการได้ แต่ต้องได้รับอนุญาตจากผู้ใช้ด้วย และแน่นอนว่าต้องได้รับอนุญาตด้วย ไม่อย่างนั้นจะบ่อนทำลายจุดที่มี ACL ทั้งหมด เราพยายามที่จะเลี่ยงการป้องกันนั้น - เราพยายามที่จะบรรลุผลเช่นเดียวกับคำสั่ง `ชุดความปลอดภัยคีย์-พาร์ติชัน-รายการ'

หลังจากเจาะลึกเอกสารประกอบของเฟรมเวิร์กแล้ว เราไม่พบ API ใดๆ ที่อนุญาตให้แก้ไข ACL โดยไม่ต้องแจ้งให้ผู้ใช้ระบุรหัสผ่าน สิ่งที่ใกล้เคียงที่สุดที่เราพบคือ `SecKeychainItemSetAccess` ซึ่งจะทริกเกอร์การแจ้งเตือน UI ทุกครั้ง จากนั้น เราก็ดำดิ่งลงไปอีก แต่คราวนี้ เข้าไปในเอกสารที่ดีที่สุด ซึ่งก็คือซอร์สโค้ดเอง Apple นำไปใช้อย่างไร?

ปรากฎว่าพวกเขากำลังใช้ API ส่วนตัวตามที่คาดไว้ วิธีการที่เรียกว่า `SecKeychainItemSetAccessWithPassword` โดยพื้นฐานแล้วจะเหมือนกับ `SecKeychainItemSetAccess` แต่แทนที่จะแจ้งให้ผู้ใช้ใส่รหัสผ่าน รหัสผ่านจะถูกจัดเตรียมเป็นอาร์กิวเมนต์ของฟังก์ชัน แน่นอนว่า ในฐานะ API ส่วนตัว API จึงไม่อยู่ในรายการในเอกสารประกอบ แต่ Apple ขาดเอกสารประกอบสำหรับ API ดังกล่าว ราวกับว่าพวกเขาไม่สามารถคิดที่จะสร้างแอปสำหรับการใช้งานส่วนบุคคลหรือองค์กรได้ เนื่องจากเครื่องมือนี้มีไว้เพื่อใช้ภายในเท่านั้น เราจึงไม่ลังเลที่จะใช้ API ส่วนตัว สิ่งเดียวที่ต้องทำคือเชื่อมโยงวิธี C เข้ากับ Swift

เอาชนะความท้าทายในการอนุญาตของผู้ใช้

ดังนั้น ขั้นตอนการทำงานขั้นสุดท้ายของต้นแบบจึงเป็นดังนี้:

  1. สร้างพวงกุญแจที่ปลดล็อคชั่วคราวโดยปิดการล็อคอัตโนมัติ
  2. รับและถอดรหัสข้อมูลประจำตัวการลงนามที่เข้ารหัส base64 จากตัวแปรสภาพแวดล้อม (ผ่านโดย Gitlab)
  3. นำเข้าข้อมูลระบุตัวตนไปยังพวงกุญแจที่สร้างขึ้น
  4. ตั้งค่าตัวเลือกการเข้าถึงที่เหมาะสมสำหรับข้อมูลระบุตัวตนที่นำเข้า เพื่อให้ Xcode และเครื่องมืออื่นๆ สามารถอ่านข้อมูลดังกล่าวสำหรับการออกแบบโค้ดได้

การอัพเกรดเพิ่มเติม

ต้นแบบทำงานได้ดี ดังนั้นเราจึงระบุคุณลักษณะเพิ่มเติมบางประการที่เราต้องการเพิ่มลงในเครื่องมือ เป้าหมายของเราคือการแทนที่ช่องทางด่วนในที่สุด เราได้ดำเนินการ "จับคู่" แล้ว อย่างไรก็ตาม fastlane ยังคงนำเสนอคุณสมบัติอันมีค่าสองประการที่เรายังไม่มี ได้แก่ การจัดเตรียมการติดตั้งโปรไฟล์และการสร้างexport.plist

การติดตั้งโปรไฟล์การจัดเตรียม

การติดตั้งโปรไฟล์การจัดเตรียมนั้นค่อนข้างตรงไปตรงมา โดยแบ่งเป็นการแยก UUID โปรไฟล์และคัดลอกไฟล์ไปที่ `~/Library/MobileDevice/Provisioning Profiles/` โดยมี UUID เป็นชื่อไฟล์ และนั่นก็เพียงพอแล้วสำหรับ Xcode ที่จะมองเห็นได้อย่างถูกต้อง การเพิ่มปลั๊กอินง่ายๆ ลงในเครื่องมือของเราเพื่อวนซ้ำไดเร็กทอรีที่ให้มานั้นไม่ใช่เรื่องยากเลย และทำอย่างนั้นกับไฟล์ .mobileprovision ทุกไฟล์ที่พบในภายใน

การสร้าง Export.plist

อย่างไรก็ตาม การสร้างexport.plistนั้นยุ่งยากกว่าเล็กน้อย ในการสร้างไฟล์ IPA ที่เหมาะสม Xcode กำหนดให้ผู้ใช้จัดเตรียมไฟล์ plist พร้อมข้อมูลเฉพาะที่รวบรวมจากแหล่งต่างๆ เช่น ไฟล์โปรเจ็กต์ ไฟล์ plist การให้สิทธิ์ การตั้งค่าพื้นที่ทำงาน ฯลฯ สาเหตุที่ Xcode สามารถรวบรวมข้อมูลนั้นผ่านวิซาร์ดการแจกจ่ายเท่านั้น แต่ไม่สามารถรวบรวมได้ ฉันไม่รู้จัก CLI ผ่าน CLI อย่างไรก็ตาม เราต้องรวบรวมโดยใช้ Swift API โดยมีเพียงการอ้างอิงโปรเจ็กต์/พื้นที่ทำงาน และความรู้เพียงเล็กน้อยเกี่ยวกับวิธีการสร้างไฟล์โปรเจ็กต์ Xcode

ผลลัพธ์ดีเกินคาด ดังนั้นเราจึงตัดสินใจเพิ่มเป็นปลั๊กอินอื่นในเครื่องมือของเรา นอกจากนี้เรายังเผยแพร่เป็นโปรเจ็กต์โอเพ่นซอร์สสำหรับผู้ชมในวงกว้างอีกด้วย ปัจจุบัน MQSwiftSign เป็นเครื่องมืออเนกประสงค์ที่สามารถนำมาใช้แทนการดำเนินการ Fastlane พื้นฐานที่จำเป็นสำหรับการสร้างและเผยแพร่แอปพลิเคชัน iOS ของคุณได้สำเร็จ และเราใช้มันในทุกโปรเจ็กต์ของเราใน Miquido

ความคิดสุดท้าย: ความสำเร็จ

การเปลี่ยนจากสถาปัตยกรรม Intel ไปใช้ สถาปัตยกรรม iOS ARM ถือเป็นงานที่ท้าทาย เราเผชิญกับอุปสรรคมากมายและใช้เวลาอย่างมากในการพัฒนาเครื่องมือเนื่องจากขาดเอกสารประกอบ อย่างไรก็ตาม ในที่สุดเราก็สร้างระบบที่แข็งแกร่งขึ้นมา:

  • นักวิ่งสองคนที่ต้องจัดการแทนที่จะเป็นเก้าคน
  • การใช้งานซอฟต์แวร์ที่อยู่ภายใต้การควบคุมของเราทั้งหมด โดยไม่มีค่าใช้จ่ายใดๆ มากมายในรูปแบบของ Rubygems เราสามารถกำจัด Fastlane หรือซอฟต์แวร์บุคคลที่สามใดๆ ในการกำหนดค่าบิลด์ของเราได้
  • ความรู้และความเข้าใจมากมายในสิ่งที่เรามักไม่ใส่ใจ เช่น ความปลอดภัยของระบบ macOS และเฟรมเวิร์กความปลอดภัย โครงสร้างโปรเจ็กต์ Xcode จริง และอื่นๆ อีกมากมาย

ฉันยินดีที่จะสนับสนุนคุณ – หากคุณกำลังดิ้นรนกับการตั้งค่า GitLab runner สำหรับ iOS builds ให้ลองใช้ MQVMRunner ของเรา หากคุณต้องการความช่วยเหลือในการสร้างและเผยแพร่แอปของคุณโดยใช้เครื่องมือชิ้นเดียว และไม่ต้องการพึ่งพา Rubygems ลองใช้ MQSwiftSign ใช้งานได้สำหรับฉันอาจใช้ได้ผลสำหรับคุณด้วย!