อยากจะ Publish App ขึ้น F-Droid ต้องทำอะไรบ้าง

ช่วงนี้เจ้าของบล็อกมีเหตุที่ต้องเอาแอปตัวหนึ่งขึ้น F-Droid เลยเขียนบทความนี้เพื่อเล่าสู่กันอ่าน เผื่อว่ามีนักพัฒนาคนไหนต้องการส่งแอปขึ้น F-Droid เหมือนกัน

F-Droid คืออะไร?

สำหรับผู้ใช้แอนดรอยด์ทั่วไปเวลาจะโหลดแอปผ่าน App Store ซักเจ้าก็คงไม่พ้น Google Play เป็นหลัก และก็อาจจะมี App Store ทางเลือกของแต่ละแบรนด์ เช่น ถ้าใช้ของ Samsung ก็จะมี Galaxy Store เป็นอีกทางเลือก เป็นต้น

แต่ F-Droid นั้นจะเป็น App Store ที่ไม่ได้ขึ้นอยู่กับแบรนด์ใด เพราะเป็น Open Source App Store สำหรับแอนดรอยด์ที่มีจุดเด่นดังนี้

  • ผู้ใช้สามารถติดตั้งและดาวน์โหลดแอปโดยไม่ต้องลงทะเบียนและไม่มีการเก็บข้อมูลส่วนตัวของผู้ใช้
  • แอปทุกตัวใน F-Droid จะเป็น Open Source Project ทั้งหมดและจะต้องผ่านการตรวจสอบจากทีมงาน F-Droid ทุกครั้งไม่ว่าจะเป็นแอปใหม่หรืออัปเดตในภายหลังก็ตาม
F-Droid - Free and Open Source Android App Repository
F-Droid is an installable catalogue of FOSS (Free and Open Source Software) applications for the Android platform. The client makes it easy to browse, install, and keep track of updates on your device.

ไม่ได้เหมาะกับผู้ใช้แอนดรอยด์ทุกคน

การติดตั้ง F-Droid และแอปที่ดาวน์โหลดจาก F-Droid จะเป็นการติดตั้งแอปแบบ Sideload ที่ต้องดาวน์โหลดไฟล์ APK ลงในเครื่องเพื่อติดตั้งด้วยตัวเอง ดังนั้น F-Droid จึงไม่เหมาะกับผู้ใช้ทั่วไป แต่เหมาะกับผู้ใช้ที่มีความเชี่ยวชาญในการใช้งานแอนดรอยด์หรือที่เรียกกันว่า Superuser มากกว่า

เทียบไฟล์ APK จาก Source Code โดยตรง

F-Droid จะไม่ได้แค่รับไฟล์ APK จากนักพัฒนาไปตรวจสอบเท่านั้น แต่จะตรวจสอบด้วยว่าไฟล์ APK นั้นมาจาก Source Code ที่ Commit SHA นั้นจริงตามที่นักพัฒนาแจ้งไว้ด้วย

นั่นจึงทำให้มั่นใจได้ว่าแอปที่ดาวน์โหลดจาก F-Droid เป็นแอปที่มาจาก Source Code ที่เป็น Open Source Project ตัวนั้นจริง ๆ ไม่ใช่ไฟล์ APK ที่นักพัฒนาแอปใส่โค้ดอย่างอื่นเพิ่มเข้ามาในภายหลัง

มีตรวจสอบความถูกต้องและความปลอดภัย

F-Droid มี CI/CD ในระบบเพื่อตรวจสอบโค้ดภายในโปรเจค เช่นทำ Security & Privacy Analysis ด้วย VirusTotal และ Pithus เป็นต้น

สิ่งที่ต้องควรรู้และควรทำก่อน Submit App ขึ้น F-Droid

ในบทความนี้จะอ้างอิงจากโปรเจคที่อยู่บน GitHub เป็นหลัก ถ้าใช้ Git Hosting เจ้าอื่นก็อาจจะมีจุดที่แตกต่างกันเล็กน้อยนะ

Git Repo ของ F-Droid ที่เกี่ยวข้อง

ขั้นตอนทั้งหมดของ F-Droid จะอยู่บน GitLab ดังนั้นนักพัฒนาจะต้องมี GitLab Account เพื่อเข้าไป Contribute Git Repo เหล่านี้ของ F-Droid

  • fdroid/fdroiddata [Data] – เปิด Merge Request (aka Pull Request) เพื่อส่งข้อมูลที่เรียกว่า Build Metadata สำหรับโปรเจคของเรา
  • fdroid/rfp [Requests For Packaging] – เปิด Issue เพื่อขอ Submit App ขึ้น F-Droid

เงื่อนไขของโปรเจคที่จะ Submit App ขึ้น F-Droid

  • โปรเจคจะต้องเป็น FLOSS (Free, Libre and Open Source Software)
  • Library ที่ใช้ก็จะต้องเป็น Free License ทั้งหมด
  • ไม่รองรับ Library ของ Google Play Services และ Firebase เพราะเป็น Non-free License
  • ไม่รองรับ Non-free Build Tools ใด ๆ  เช่น Oracle JDK
  • ต้องเป็น Public Project ที่สามารถเข้าถึงผ่าน git, hg, svn, หรือ bzr
  • ห้ามมีการดาวน์โหลดไฟล์จำพวก Executable Binary โดยไม่ได้รับอนุญาตจากผู้ใช้ ต้องเป็นแบบ Opt-in เท่านั้น

F-Droid มีการตรวจสอบว่าไฟล์ APK ถูก Build มาจาก Source Code ต้นทางจริงหรือไม่

F-Droid มีกระบวนการที่เรียกว่า Reproducible Builds ที่ทำให้ตรวจสอบได้ว่าไฟล์ APK ที่นักพัฒนาส่งเข้ามาเป็นไฟล์ที่ถูกสร้างมาจาก Commit SHA ตามที่แจ้งไว้

โดยใช้เทคนิคที่เรียกว่า APK Signature Copying ที่จะทำการ Build App จาก Commit SHA นั้น ๆ แล้วเทียบว่า APK Signature ตรงกับไฟล์ APK ของนักพัฒนาหรือไม่

Reproducible Builds | F-Droid - Free and Open Source Android App Repository
F-Droid supports reproducible builds ofapps, so that anyone can run the build process again and reproduce the sameAPK as the original release. This means th…

นั่นจึงทำให้ผู้ใช้มั่นใจได้ว่าไฟล์ APK ที่ดาวน์โหลดจาก F-Droid นั้นมาจาก Source Code ชุดนั้น, Tag นั้น, หรือ Commit SHA นั้นจริง ไม่ใช่ไฟล์ APK ที่แอบใส่โค้ดอื่นนอกเหนือจากที่เห็นใน Source Code ทีหลัง

เพิ่ม SHA256 ของ Gradle Wrapper ที่ใช้ในโปรเจค

เพราะใน CI ของ F-Droid จะตรวจสอบ Gradle Wrapper ที่ใช้ในโปรเจคโดยเทียบกับ SHA256 ที่กำหนดไว้ในโปรเจค และถ้านักพัฒนาไม่ได้กำหนด SHA256 ไว้ ก็จะถือว่าโปรเจคดังกล่าวใช้ Insecure Gradle Wrapper และโดนเตือนใน CI Report จาก F-Droid Bot

เพื่อเลี่ยงปัญหาดังกล่าว นักพัฒนาจะต้องเพิ่ม Properties ที่ชื่อว่า distributionSha256Sum ไว้ใน gradle/wrapper/gradle-wrapper.properties

# gradle/wrapper/gradle-wrapper.properties

distributionSha256Sum=<sha256_by_gradle_version>

โดย SHA256 ของ Gradle Wrapper สามารถดูได้ที่ Gradle distribution and wrapper JAR checksum reference [Gradle Build Tool]

Gradle | Gradle distribution and wrapper JAR checksum reference
List of distribution and wrapper JAR SHA-256 checksums for all released versions of Gradle.

ยกตัวอย่างเช่น โปรเจคที่ใช้ Gradle 8.9 ก็จะต้องกำหนด SHA256 ไว้แบบนี้

# gradle/wrapper/gradle-wrapper.properties

distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab

และใช้ Gradle Command ในโปรเจคเพื่ออัปเดต Gradle Wrapper ให้เป็นเวอร์ชันเดียวกับที่กำหนดไว้

./gradlew wrapper --gradle-version 8.9 \
  --gradle-distribution-sha256-sum d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab

เตรียม Upstream Metadata สำหรับ F-Droid ไว้ในโปรเจค

เพื่อให้ F-Droid สามารถนำข้อมูลพื้นฐานเกี่ยวกับแอปไปแสดงในหน้าดาวน์โหลดได้ เช่น ชื่อแอป, คำอธิบายเกี่ยวกับแอป, หรือภาพตัวอย่างแอป เป็นต้น นักพัฒนาจะต้องเตรียมข้อมูลที่ F-Droid เรียกว่า Upstream Metadata ไว้ในโปรเจค

# App name
<project>/metadata/<locale>/title.txt

# Short description (30-50 characters, no trailing dot)
<project>/metadata/<locale>/short_description.txt

# Full description
<project>/metadata/<locale>/full_description.txt

# App icon
<project>/metadata/<locale>/images/icon.png

# Feature Graphic
<project>/metadata/<locale>/images/featureGraphic.png

# Screenshot
<project>/metadata/<locale>/images/phoneScreenshots/<file_1>.png
<project>/metadata/<locale>/images/phoneScreenshots/<file_2>.jpg

# Changelogs
<project>/metadata/<locale>/changelogs/<version_code>.txt
  • <locale> จะขึ้นอยู่กับภาษาที่ต้องการให้รองรับ เช่น en-US หรือ th เป็นต้น
  • ไฟล์ภาพรองรับได้ทั้ง png, jpg, และ jpeg แต่ไม่รองรับ webp

โดยข้อมูลเหล่านี้บน Google Play จะเรียกว่า Store Listing นั่นเอง ดังนั้นนักพัฒนาสามารถใช้ข้อมูลเดียวกับที่ใช้บน Google Play ได้เช่นกัน

ถ้าไม่มั่นใจก็สามารถดูตัวอย่าง Upstream Metadata ได้จาก fdroid/fdroidclient [GitLab]

metadata · master · F-Droid / Client · GitLab
Android client application

สร้าง Tag และ Release สำหรับเวอร์ชันแอปที่ต้องการ Submit

โดยเลขเวอร์ชันสำหรับ Tag และ Release จะต้องขึ้นต้นด้วย v เสมอ (เช่น v1.0.0) และสร้าง Release ที่มีไฟล์ APK ที่ Build มาจากโค้ดของ Tag นั้น ๆ ด้วย

โดยทีมงาน F-Droid จะตรวจสอบไฟล์ APK ใน Release ด้วยว่าเป็นไฟล์ที่มาจาก Commit SHA ของ Tag นั้นด้วยหรือไม่

เตรียม SHA256 ของไฟล์ APK

ในขั้นตอนการ Submit App จะมีการใส่ SHA256 ของไฟล์ APK เพื่อให้ F-Droid ตรวจสอบความถูกต้อง ซึ่งจะได้มาจากคำสั่ง

apksigner verify --print-certs <apk_file_path> | grep SHA-256

โดยจะได้ผลลัพธ์ออกมาประมาณนี้

Signer #1 certificate SHA-256 digest: 1111222233334444555566667777888899990000111122223333444455556666

ให้เก็บ SHA256 ไว้เพื่อใช้ตอนการสร้าง Build Metadata ในภายหลัง

เตรียม URL ของไฟล์ APK

เพื่อให้ F-Droid เข้าถึงไฟล์ APK ที่นักพัฒนาทำ App Signing มาตรวจสอบ จึงต้องใส่ URL ของไฟล์ดังกล่าวในตอนที่สร้าง Build Metadata ด้วย และถ้านักพัฒนาใช้ GitHub เหมือนกัน ไฟล์ APK ที่อยู่ใน Release ของแต่ละ Tag จะมี URL ดังนี้

https://github.com/<username>/<repo>/releases/download/<release>/<filename>.apk

เนื่องจาก <release> จะเปลี่ยนไปตามเลขเวอร์ชันของแต่ละ Tag ทำให้ F-Droid กำหนดว่า URL ส่วนใดที่เป็นเลขเวอร์ชันให้แทนที่ด้วย v%v แทน ยกตัวอย่างเช่น

https://github.com/akexorcist/sfl/releases/download/v%v/app-release.apk

ขั้นตอนการ Submit App ขึ้น F-Droid

เมื่อเตรียมข้อมูลและโปรเจคสำหรับแอปของเราพร้อมแล้ว ขั้นตอนต่อไปก็คือการเปิด Issue และเปิด Merge Request สำหรับ Build Metadata ใน Git Repository ของ F-Droid

Merge Request ของ GitLab ก็คือ Pull Request ใน GitHub น่ะแหละ

Fork Repository สำหรับ Build Metadata

เข้าไปที่ fdroid/fdroiddata เพื่อ Fork Repository มาไว้ใน GitLab ของตัวเองแล้วสร้าง Branch ขึ้นมาเพื่อเพิ่ม Build Metadata ของแอปที่ต้องการ Submit  (แนะนำให้สร้าง Branch แยกออกมาจาก master)

สร้างไฟล์ yml ใน metadata โดยใช้ชื่อไฟล์เป็น Package Name ของแอป ยกตัวอย่างเช่น metadata/com.akexorcist.sleepingforless.yml แล้วทำการเพิ่มข้อมูลเข้าไปใน Build Metadata โดยอ้างอิงข้อมูลจากไฟล์ที่อยู่ใน templates/build-gradle.yml หรือจะอิงจากตัวอย่างข้างล่างนี้ก็ได้

# metadata/<package_name>.yml

Categories:
  - <app_categories>
License: <software_license>
AuthorName: <name>
AuthorEmail: <email>
AuthorWebSite: <website>
SourceCode: <source_code_url>
IssueTracker: <issue_tracker_url>

AutoName: <app_name>

RepoType: git
Repo: <git_url>
Binaries: <apk_file_url>

Builds:
  - versionName: <app_version_name>
    versionCode: <app_version_code>
    commit: <tag>
    subdir: app
    gradle:
      - yes

AllowedAPKSigningKeys: <apk_signing_keys>

AutoUpdateMode: Version
UpdateCheckMode: Tags
CurrentVersion: <app_version_name>
CurrentVersionCode: <app_version_code>
  • Categories – หมวดหมู่ของแอป สามารถดูได้จาก Categories [Build Metadata Reference] (กำหนดได้มากกว่า 1 หมวดหมู่)
  • License – Software License ของ Source Code
  • AuthorName – ชื่อ
  • AuthorEmail – อีเมล
  • SourceCode – URL ของ Source Code
  • IssueTracker – URL ของ Issue Tracker
  • AutoName – ชื่อแอป
  • Repo – URL ของ Source Code ที่ลงท้ายด้วย .git
  • Binaries – URL ของไฟล์ APK ของ Tag นั้น (ที่ให้เตรียมไว้ในตอนแรก)
  • Version Name – Version Name ของแอป เช่น 1.0.0
  • Version Code – Version Code ของแอปที่เป็น Incremental Version Number
  • Commit – ชื่อ Tag ของเวอร์ชันนั้น เช่น v1.0.0
  • AllowedAPKSigningKeys – SHA256 ของไฟล์ APK (ที่ให้เตรียมไว้ในตอนแรก)
  • CurrentVersion – Version Name ของแอป
  • CurrentVersionCode – Version Code ของแอป

เมื่อเตรียมเสร็จแล้วก็ให้ Commit แล้ว Push ขึ้น GitLab เพื่อทดสอบว่า CI Pipeline สามารถทำงานได้ถูกต้องทั้งหมดหรือไม่ (CI Pipeline จะเริ่มทำงานทันทีที่ Push ขึ้น Upstream)

โดยนักพัฒนาจะต้องรอให้ CI Pipeline ผ่านทั้งหมดก่อนถึงจะไปขั้นตอนต่อไปได้ ถ้ามีปัญหาใน CI Job ใด ๆ ก็ควรเข้าไปตรวจสอบการทำงานของ Log และแก้ปัญหาให้เรียบร้อยก่อน

สร้าง Issue สำหรับ Request For Packaging (RFP)

เข้าไปที่ Issue ของ fdroid/rfp เพื่อสร้าง Issue

Issues · F-Droid / Requests For Packaging · GitLab
Request apps etc. to be packaged, build and distributed via F-Droid’s mainline repository.

สร้าง Issue โดยกำหนดหัวข้อ Issue ว่า "New app: <ชื่อแอป>" แล้วให้เลือก Type เป็น "Issue" ส่วน Description ให้เลือกจาก Template ที่ชื่อว่า "Minimal"

โดย Template ดังกล่าวจะมีรายละเอียดที่นักพัฒนาต้องกรอกข้อมูลดังนี้

  • ยืนยันว่าแอปอยู่ภายใต้เงื่อนไขของ Inclusion Policy
  • ยืนยันว่าแอปไม่เคยอยู่ใน Repository ของ F-Droid และไม่เคยสร้าง Issue มาก่อน
  • ยืนยันว่าได้รับการยินยอมจากเจ้าของแอป
  • ได้บริจาคเงินเพื่อสนับสนุน F-Droid หรือไม่ (ไม่บังคับ)
  • URL ของ Source Code
  • Software License ที่ใช้
  • หมวดหมู่ของแอป (Category) แบบเดียวกับที่กำหนดไว้ใน Build Metadata
  • คำอธิบายเกี่ยวกับแอปโดยย่อ (Summary)
  • คำอธิบายเกี่ยวกับแอปแบบเต็ม (Description)
ถ้าไม่มั่นใจในหัวข้อไหน สามารถดูตัวอย่าง Issue ของเจ้าของบล็อกได้ที่ New app: Ruam Mij [RFP]

สร้าง Merge Request สำหรับ Build Metadata

เข้าไปที่ Merge Request ของ fdroid/data เพื่อสร้าง Merge Request จาก Branch ที่นักพัฒนาได้เตรียม Build Metadata ไว้แล้ว

Merge requests · F-Droid / Data · GitLab
Data for the main F-Droid repository at https://f-droid.org

สร้าง Merge Request โดยกำหนดหัวข้อว่า "New app: <ชื่อแอป>" (เหมือนกับตอนเปิด Issue ใน RFP) แล้วให้เลือก Description ให้เลือกจาก Template ที่ชื่อว่า "App Inclusion"

โดย Template ดังกล่าวจะมีรายละเอียดที่นักพัฒนาต้องกรอกข้อมูลดังนี้

  • ยืนยันว่าแอปอยู่ภายใต้เงื่อนไขของ Inclusion Policy
  • ยืนยันว่าได้รับการยินยอมจากเจ้าของแอป
  • ยืนยันว่าข้อมูล Build Metadata และ RFP ถูก Reference จาก Merge Request นี้
  • ยืนยันว่า CI Pipeline ของ Build Metadata ผ่านทั้งหมดแล้ว
  • ยืนยันว่าใน Source Code มีข้อมูลของ Upstream Metadata เรียบร้อยแล้ว
  • ยืนยันว่าใน Source Code ได้ทำ Release ที่อ้างอิงจาก Tag ตามเลขเวอร์ชันเรียบร้อยแล้ว
  • (ไม่จำเป็น) ถ้ามี External Repository ในโปรเจค ได้เปลี่ยนจาก srclibs เป็น Git Submodule เรียบร้อยแล้ว
  • (ไม่จำเป็น) ยินยอมให้ F-Droid สามารถทำ Reproducible Build ได้
  • (ไม่จำเป็น) ให้ทำเป็น Multiple APKs สำหรับแอปที่มี Native Code เพื่อให้ไฟล์ APK มีขนาดเล็กลง
  • ให้ใส่เลข Issue ของ RPF ไว้ในช่องที่ Template ได้เตรียมไว้ เช่น "Closes rfp#1234"
  • เลขของ fdroiddata ไม่ต้องกำหนด สามารถลบออกได้เลย
  • Label ของ Merge Request จะถูกกำหนดเป็น "New App" เตรียมไว้ให้แล้ว ไม่ต้องกำหนดอะไรเพิ่ม
ถ้าไม่มั่นใจในหัวข้อไหน สามารถดูตัวอย่าง Issue ของเจ้าของบล็อกได้ที่ New app: Ruam Mij [Build Metadata]

รอการรีวิวจาก F-Droid

หลังจากที่สร้าง Issue ใน fdroid/rfp และ Merge Request ใน fdroid/data เสร็จเรียบร้อยแล้ว CI Pipeline ของ fdroid/data ก็จะทำงานทันทีเพื่อตรวจสอบว่า Build Metadata ของนักพัฒนาทำงานได้ถูกต้องครบถ้วน และในระหว่างนั้นก็จะมี F-Droid Bot มาแนบ Report ใน Comment ของ Merge Request นั้น

นอกจาก F-Droid Bot ก็จะมีทีมงานของ F-Droid เข้ามารีวิวและดูผลลัพธ์ของ CI และอาจจะเปิด Thread เพื่อให้นักพัฒนาแก้ไขปัญหาตามคำแนะนำด้วย

โดยจุดสังเกตสำคัญในระหว่างนี้ก็คือการใส่ Label ของทีมงานที่จะบ่งบอกว่า Merge Request ของเราอยู่ในสถานะใด ถ้า Label เป็น waiting-for-upstream คือทีมงานรอการแก้ไขหรือคำตอบจากเรา

เมื่อขั้นตอนทั้งหมดผ่านไปได้ด้วยดี ทีมงาน F-Droid จะใส่ Label ว่า reproducible-builds กับ review-requested ก็แปลว่า Merge Request นั้นพร้อมจะถูก Merge เข้าไปใน Main Branch ของ fdroid/data แล้วนั่นเอง

สามารถดูตัวอย่าง Merge Request ของเจ้าของบล็อกได้ที่ New App: Ruam Mij [Merge Request]

และเมื่อ Merge Request ถูก Merge เรียบร้อย Issue ของ RFP ที่เปิดไว้ก็จะถูกปิดไปพร้อมกันด้วย ถือว่าเป็นอันเสร็จสิ้นขั้นตอนการ Submit App ขึ้น F-Droid แล้ว

รอจนกว่าแอปจะโผล่อยู่บนหน้าเว็ปหรือแอป F-Droid

F-Droid จะไม่ได้นำแอปของนักพัฒนาขึ้นในระบบให้ทันที เหมือนกับ App Store เจ้าอื่น เพราะระบบของ F-Droid จะมี Build Cycle หรือรอบในการเพิ่มแอปเข้าไปในระบบที่ไม่ได้ถี่มาก ซึ่งจะใช้เวลา 3-7 วัน โดยอ้างอิงจาก Genera FAQ [F-Droid Wiki]

FAQ · Wiki · F-Droid / wiki · GitLab
Wiki for all things free software, Android, and of course F-Droid

และเมื่อระบบนำแอปของเราขึ้นไปอยู่บนหน้าเว็ปหรือแอปของ F-Droid เป็นที่เรียบร้อยแล้ว ก็ขอแสดงความยินดีด้วยครับ 🎉🎉