ย้ายข้อมูล Firebase Extensions ไปยัง Cloud Functions

คู่มือนี้จะแสดงวิธีย้ายข้อมูลส่วนขยายจากสภาพแวดล้อม Firebase Extensions ที่เลิกใช้งานแล้วไปยังฟังก์ชันที่ผู้ใช้ติดตั้งและทำให้ใช้งานได้ในฐานของโค้ด Cloud Functions สำหรับ Firebase (รุ่นที่ 2) ของตนเอง

ซึ่งเป็นเส้นทางการย้ายข้อมูลที่แนะนำ Firebase จะเก็บรักษารายการส่วนขยายที่มี npm ที่เทียบเท่าอย่างเป็นทางการ คู่มือนี้จะแนะนำคุณเกี่ยวกับการสร้างส่วนขยายของคุณเอง

ตลอดทั้งคู่มือนี้ เราจะใช้ส่วนขยาย Stream Cloud Firestore to BigQuery (firestore-bigquery-export) เป็นตัวอย่างในการอธิบาย แต่ละส่วนจะลงท้ายด้วยตัวอย่างที่ใช้ได้จริงซึ่งแสดงให้เห็นว่าส่วนขยายนั้นมีลักษณะอย่างไรก่อนการย้ายข้อมูล และมีลักษณะอย่างไรหลังจากนั้นในฐานะแพ็กเกจ @firebase-function-kits/firestore-bigquery-export

ลงชื่อสมัครใช้เพื่อรับข้อมูลเพิ่มเติมและความช่วยเหลือเกี่ยวกับการย้ายข้อมูลส่วนขยาย

หากมีคำถามเกี่ยวกับวิธีย้ายข้อมูลจาก Firebase Extensions โปรดติดต่อเราที่ firebase-extensions-migrator-support-external@google.com นอกจากนี้ เราจะส่งอีเมลถึงกลุ่มนี้เมื่อเราอัปเดตคู่มือพร้อมข้อมูลเพิ่มเติมเกี่ยวกับการแพ็กเกจ การทดสอบ และการเผยแพร่ฟังก์ชันรุ่นที่ 2

หากต้องการเข้าร่วมกลุ่มนี้ ให้ส่งข้อความไปที่ firebase-extensions-migrator-support-external+subscribe@google.com ซึ่งจะตอบกลับด้วยอีเมลคำขอเป็นสมาชิก คุณต้องตอบกลับอีเมลนั้น ไม่ใช่คลิกปุ่ม "เข้าร่วมกลุ่มนี้"

ก่อนเริ่มต้น

หากต้องการย้ายข้อมูลนี้ให้เสร็จสมบูรณ์ตามที่ระบุไว้ คุณจะต้องใช้ฟีเจอร์ต่อไปนี้ของ Cloud Functions

  • การกำหนดค่าที่มีพารามิเตอร์ พารามิเตอร์แต่ละรายการที่คุณประกาศใน extension.yaml จะกลายเป็นพารามิเตอร์ที่กำหนดในโค้ดแพ็กเกจ

  • บทบาท IAM แบบประกาศและ API ที่จำเป็น แต่ละบทบาทที่คุณประกาศใน extension.yaml จะกลายเป็นการเรียก requiresRole(...) และแต่ละ API จะกลายเป็นการเรียก requiresAPI(...) ในโค้ดแพ็กเกจของคุณ เมื่อติดตั้งใช้งาน Firebase CLI จะให้บทบาทที่ประกาศไว้แก่บัญชีบริการรันไทม์ที่มีการจัดการ และเปิดใช้ API ที่ประกาศไว้ในนามของคุณ

  • เหตุการณ์ในวงจรสำหรับฐานของโค้ด Cloud Functions ตอนนี้ฐานของโค้ด Cloud Functions รองรับเหตุการณ์ในวงจรที่คล้ายกับ Firebase Extensions แล้ว ประกาศการตั้งค่าเวลาติดตั้งและเวลาอัปเดตด้วยฮุกวงจร afterFirstDeploy(...) และ afterRedeploy(...) ซึ่งจะแทนที่ lifecycleEvents ที่คุณประกาศใน extension.yaml

ย้ายข้อมูลแหล่งที่มา Firebase Extensions ไปยังฟังก์ชันรุ่นที่ 2

(ไม่บังคับ) การย้ายข้อมูลอัตโนมัติด้วยทักษะ Agent ของ Firebase

คุณสามารถทำให้ขั้นตอนที่ 1 ถึง 8 (การจัดทำรายการทรัพยากร, ทริกเกอร์การอัปเกรด, การแปลงพารามิเตอร์และข้อมูลลับ, IAM แบบประกาศ, Lifecycle Hook และการสร้าง README ของแพ็กเกจ) เป็นแบบอัตโนมัติได้โดยใช้ความสามารถของ AI Agent extension-to-functions-codebase อย่างเป็นทางการ

ติดตั้งทักษะ

หากคุณหรือผู้ช่วยเขียนโค้ด AI (Gemini ใน Firebase, Cursor, Claude Code, GitHub Copilot) ยังไม่ได้ติดตั้งทักษะ ให้เรียกใช้คำสั่งต่อไปนี้โดยใช้ CLI ของทักษะ

npx skills add firebase/agent-skills --skill extension-to-functions-codebase

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

"โปรดย้ายข้อมูล Firebase Extensions นี้ไปยังแพ็กเกจ Function Kit รุ่นที่ 2 ที่เผยแพร่ได้โดยทำตามวิธีการในทักษะ extension-to-functions-codebase"

1. ตรวจสอบส่วนขยาย

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

ตรวจสอบแต่ละรายการต่อไปนี้และจดบันทึกสิ่งที่คุณพบ

  • extension.yaml ซึ่งประกาศพารามิเตอร์, ฟังก์ชัน, เหตุการณ์, บทบาท IAM, API ที่จำเป็น, ข้อมูลลับ และฮุกวงจร

  • functions/ ซึ่งมีโค้ดฟังก์ชัน ทรัพยากร Dependency การกำหนดค่าบิลด์ ทริกเกอร์ และฟังก์ชันคิวงาน

  • README.md, PREINSTALL.md และ POSTINSTALL.md ซึ่งมีขั้นตอนการตั้งค่า คำเตือน และหมายเหตุสำหรับการเรียกเก็บเงิน

  • scripts/ ซึ่งประกอบด้วยยูทิลิตีการนำเข้า การทดแทนโฆษณา IAM การซ่อมแซม หรือการย้ายข้อมูล และเครื่องมืออื่นๆ ที่คุณจัดส่งพร้อมกับส่วนขยาย

จากนั้นให้ตัดสินใจว่าจะวางแต่ละรายการใน extension.yaml ไว้ที่ใดในแพ็กเกจ npm ดังนี้

ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery

การอ่าน firestore-bigquery-export/extension.yaml และ functions/ จะสร้างพื้นที่โฆษณานี้

ใน extension.yaml จำนวน/มูลค่า ตำแหน่งที่ข้อมูลของคุณ
params 25 (COLLECTION_PATH, DATASET_ID, TABLE_ID, DATASET_LOCATION, VIEW_TYPE, …) พารามิเตอร์ Cloud Functions (ขั้นตอนที่ 4)
apis bigquery.googleapis.com requiresAPI(...) (ขั้นตอนที่ 6)
roles bigquery.dataEditor, datastore.user, bigquery.user requiresRole(...) (ขั้นตอนที่ 6)
resources ทริกเกอร์เหตุการณ์ 1 รายการ (fsexportbigquery) + ฟังก์ชันคิวงาน (initBigQuerySync, setupBigQuerySync) ฟังก์ชันแพ็กเกจที่ส่งออก (ขั้นตอนที่ 3)
lifecycleEvents onInstall → initBigQuerySync; onUpdate / onConfigure → setupBigQuerySync afterFirstDeploy / afterRedeploy (ขั้นตอนที่ 7)
รหัสอินสแตนซ์ ไม่ได้ใช้ (ไม่มีการอ่าน EXT_INSTANCE_ID) ไม่มีข้อมูลที่จะย้าย
scripts/ import/ (โฆษณาทดแทน), gen-schema-view/ เก็บไว้เป็นสคริปต์ (อยู่นอกขอบเขตที่นี่)

การวิเคราะห์ ส่วนขยายไม่ได้ประกาศพารามิเตอร์ type: secret จึงไม่มีอะไรให้ย้ายข้อมูลสำหรับข้อมูลลับในขั้นตอนที่ 4 ทริกเกอร์เหตุการณ์เป็นรุ่นที่ 2 อยู่แล้ว มีเพียงฟังก์ชันคิวงานเท่านั้นที่ยังคงเป็นรุ่นที่ 1 (เกี่ยวข้องในขั้นตอนที่ 3)

2. อัปเดต package.json

อัปเดตไฟล์ package.json ของส่วนขยาย หากคุณกำลังย้ายข้อมูลส่วนขยายเดียว นี่อาจเป็น package.json รูท หากคุณกำลังย้ายข้อมูลส่วนขยายจำนวนมากในที่เก็บเดียว ให้สร้างแพ็กเกจของตัวเองสำหรับส่วนขยายแต่ละรายการ

เวอร์ชัน SDK ขั้นต่ำ: ประกาศ firebase-functions >= 7.4.0 และ firebase-admin >= 14.2.0 เป็นทรัพยากร Dependency ประกาศเวอร์ชันของ firebase-functions เป็นทรัพยากร Dependency แบบเพียร์ด้วย เพื่อให้โปรเจ็กต์ Cloud Functions ของผู้ใช้มี SDK เวอร์ชันเดียวกับที่ใช้เขียนไลบรารีของคุณ

{
  "name": "<package-name>",
  "version": "1.0.0",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "exports": {
    ".": {
      "types": "./lib/index.d.ts",
      "default": "./lib/index.js"
    }
  },
  "engines": {
    "node": "22"
  },
  "peerDependencies": {
    "firebase-functions": "^7.4.0"
  },
  "dependencies": {
    "firebase-functions": "^7.4.0",
    "firebase-admin": "^14.2.0"
  }
}

ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery

ก่อน functions/package.json ของส่วนขยายเป็นแบบส่วนตัว โดยระบุรหัสส่วนขยายและประกาศ firebase-functions เป็นทรัพยากร Dependency โดยตรง ดังนี้

{
  "name": "firestore-bigquery-export",
  "main": "lib/index.js",
  "private": true,
  "dependencies": {
    "@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
    "firebase-admin": "^14.2.0",
    "firebase-functions": "^6.3.2"
  }
}

หลัง แพ็กเกจที่เผยแพร่ได้: ชื่อที่กำหนดขอบเขต, แมป exports และ firebase-functions ย้ายไปที่ peerDependencies:

{
  "name": "@firebase-function-kits/firestore-bigquery-export",
  "version": "0.1.0",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "exports": {
    ".": {
      "types": "./lib/index.d.ts",
      "default": "./lib/index.js"
    }
  },
  "engines": {
    "node": "22"
  },
  "peerDependencies": {
    "firebase-functions": "^7.4.0"
  },
  "dependencies": {
    "@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
    "firebase-admin": "^14.2.0",
    "firebase-functions": "^7.4.0"
  }
}

3. อัปเกรดฟังก์ชันจากรุ่นที่ 1 เป็นรุ่นที่ 2

หากส่วนขยายยังคงส่งออกฟังก์ชันรุ่นที่ 1 ให้แปลงทริกเกอร์แต่ละรายการเป็นฟังก์ชันรุ่นที่ 2 ที่เทียบเท่า นำเข้าจากโมดูล firebase-functions/... และส่งการตั้งค่ารันไทม์ในตัวเลือกทริกเกอร์

ดูคู่มือการอัปเกรด Cloud Functions รุ่นที่ 2 โดยเฉพาะอย่างยิ่ง คุณสามารถลดความพยายามในการเขียนใหม่ได้โดยใช้การแยกโครงสร้างเหตุการณ์ที่แพตช์แล้วของรุ่นที่ 2 และหลีกเลี่ยงการเขียนตรรกะของฟังก์ชันใหม่ เนื่องจาก SDK รุ่นที่ 2 จะแสดงพารามิเตอร์ v1 เป็นฟิลด์ในออบเจ็กต์เหตุการณ์ ซึ่งช่วยให้คุณใช้พารามิเตอร์ที่แยกโครงสร้าง/มีชื่อได้ และคงตรรกะทางธุรกิจไว้โดยไม่มีการเปลี่ยนแปลง

ก่อน รุ่นที่ 1:

import * as functions from "firebase-functions/v1";

export const sync = functions.firestore
  .document("{collectionId}/{documentId}")
  .onWrite(async (change, context) => {
    await handleWrite(change.before, change.after, context.params);
  });

หลัง รุ่นที่ 2:

import { onDocumentWritten } from "firebase-functions/firestore";

export const syncV2 = onDocumentWritten(
  { document: "{collectionId}/{documentId}" },
  async ({ change, context }) =>
    await handleWrite(change.before, change.after, context.params)
);

ดูการเปรียบเทียบเวอร์ชัน Cloud Functions เพื่อดูรายการความแตกต่างทั้งหมดระหว่างฟังก์ชันรุ่นที่ 1 และรุ่นที่ 2

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

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

ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery

defineString("DATABASE_REGION", {
  label: "Firestore Instance Location",
  description:
    "Where is the Firestore database located? You can check your current database location at https://console.cloud.google.com/firestore/databases. The functions in this kit deploy to the Cloud Run region closest to this location.",
  input: select({
    "Multi-region (Europe - Belgium and Netherlands)": "eur3",
    "Multi-region (United States)": "nam5",
    "Multi-region (Iowa, North Virginia, and Oklahoma)": "nam7",
    "Iowa (us-central1)": "us-central1",
    // More locations...
  })
});

// Firestore multi-region locations are not Cloud Run regions; deploying a
// function to one hard-fails, so they map to a region inside the multi-region.
const MULTI_REGION_TO_FUNCTION_REGION: Record<string, string> = {
  nam5: "us-central1",
  nam7: "us-central1",
  eur3: "europe-west1",
};

/**
 * Maps a Firestore database location to the Cloud Run region the functions
 * should deploy to. The lookup is case-insensitive and ignores surrounding
 * whitespace, as the CLI's own region handling is. Regional locations pass
 * through lowercased; an unset or blank location returns `undefined`, meaning
 * the functions declare no region.
 */
export function firestoreLocationToFunctionRegion(
  location: string | undefined
): string | undefined {
  const normalized = location?.trim().toLowerCase();
  if (!normalized) {
    return undefined;
  }
  return MULTI_REGION_TO_FUNCTION_REGION[normalized] ?? normalized;
}

const functionRegion = firestoreLocationToFunctionRegion(
  process.env.DATABASE_REGION
);

export const fsexportbigquery = onDocumentWritten(
  {
    region: functionRegion,
    // Other configuration
  },
  (event) => handleDocumentWrite(event, getHandlerContext())
);

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

4. แปลงพารามิเตอร์และข้อมูลลับของส่วนขยาย

พารามิเตอร์

พารามิเตอร์แต่ละรายการที่คุณประกาศใน extension.yaml จะกลายเป็นพารามิเตอร์ Cloud Functions

แปลงการอ่านสภาพแวดล้อมโดยตรง:

const collectionPath = process.env.COLLECTION_PATH;

เป็นพารามิเตอร์ Cloud Functions:

import { defineString } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";

const collectionPath = defineString("COLLECTION_PATH");

// Pass the param directly when used as a placeholder (e.g. trigger path)
export const sync = onDocumentWritten(
  { document: collectionPath },
  async (event) => {
    // Call .value() to read the string inside a handler
    const path = collectionPath.value();
    await handleWrite(path, event);
  }
);

ใช้ collectionPath.value() เพื่ออ่านสตริงภายในเครื่องจัดการ ใช้ collectionPath โดยตรงในตำแหน่งที่คาดว่าจะมีตัวยึดตำแหน่ง เช่น เส้นทางทริกเกอร์ฟังก์ชัน

Firebase CLI จะค้นหาพารามิเตอร์และอ่านค่าจาก .env, .env.<projectId> หรือแจ้งผู้ใช้ในระหว่างการติดตั้งใช้งาน ใช้ชื่อพารามิเตอร์เดิมเพื่อให้ค่าจากการติดตั้งที่มีอยู่ยังคงอยู่

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

ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery

ก่อน พารามิเตอร์ที่ประกาศใน extension.yaml ซึ่งอ่านเป็นตัวแปรสภาพแวดล้อมดิบใน config.ts:

# extension.yaml
- param: COLLECTION_PATH
  label: Collection path
  type: string
  required: true
// functions/src/config.ts
collectionPath: process.env.COLLECTION_PATH,

หลัง defineString 1 รายการ โดย CLI จะค้นพบและอ่านจาก .env ดังนี้

// src/config.ts
import { defineString } from "firebase-functions/params";

collectionPath: defineString("COLLECTION_PATH", {
  label: "Collection path",
  // We now support "nonEmpty: true" to ensure a value other than the empty string
  // is entered, analogous to "required: true" in extension.yaml
  input: { text: { nonEmpty: true } }
}),

ชื่อพารามิเตอร์ไม่มีการเปลี่ยนแปลง ดังนั้น .env ที่มีอยู่จะยังคงทำงานต่อไป

รหัสอินสแตนซ์

ส่วนขยายจะอ่านรหัสอินสแตนซ์จาก EXT_INSTANCE_ID ซึ่งแทรกโดยรันไทม์ของส่วนขยาย ชุดฟังก์ชันจะอ่านรหัสอินสแตนซ์จาก FIREBASE_KIT_INSTANCE_ID ซึ่ง Firebase CLI จะตั้งค่าสำหรับอินสแตนซ์ชุดแต่ละรายการเป็นคีย์ของอินสแตนซ์ในแมป instances ใน firebase.json CLI จะระบุค่านี้ในระหว่างการค้นหาเวลาทำให้ใช้งานได้ ในโปรแกรมจำลอง และฟังก์ชันที่ทำให้ใช้งานได้

รหัสอินสแตนซ์ไม่ใช่พารามิเตอร์ จึงไม่ต้องประกาศด้วย defineString อันที่จริงแล้ว FIREBASE_... เป็นคำนำหน้าที่สงวนไว้ในไฟล์ .env ดังนั้นผู้ใช้จึงไม่สามารถตั้งค่าหรือลบล้างได้ ค่าที่ CLI แทรกจะไม่ปรากฏในระบบ params อ่านได้โดยตรงจากสภาพแวดล้อม ดังนี้

// Before
const instanceId = process.env.EXT_INSTANCE_ID;

// After
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;

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

ตัวอย่างที่ใช้ได้จริง: ลบข้อมูลผู้ใช้

(ส่วนขยาย Stream Cloud Firestore to BigQuery ไม่อ่านรหัสอินสแตนซ์ จึงไม่มีข้อมูลที่จะย้ายข้อมูล) ส่วนขยายลบข้อมูลผู้ใช้จะใช้รหัสนี้เพื่อตั้งชื่อหัวข้อ Pub/Sub)

ก่อน อ่านเป็นตัวแปรสภาพแวดล้อมดิบใน config.ts โดยมีคำนำหน้า ext- ที่ส่วนขยายใช้สำหรับทรัพยากร

// functions/src/config.ts
discoveryTopic: `ext-${process.env.EXT_INSTANCE_ID}-discovery`,
deletionTopic: `ext-${process.env.EXT_INSTANCE_ID}-deletion`,

หลัง การอ่าน process.env แบบธรรมดาของ FIREBASE_KIT_INSTANCE_ID ที่ใช้สำหรับพารามิเตอร์ทั่วไป 2 รายการเพื่อให้ผู้ใช้ลบล้างชื่อหัวข้อได้

// src/config.ts
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;

// Non-empty defaults so Pub/Sub trigger bindings resolve during deploy
// discovery without freezing an empty topic name into the manifest.
discoveryTopicName: defineString("DISCOVERY_TOPIC_NAME", {
  default: `kit-${instanceId}-discovery`,
}),
deletionTopicName: defineString("DELETION_TOPIC_NAME", {
  default: `kit-${instanceId}-deletion`,
}),

ค่าเริ่มต้นต้องไม่ว่างเปล่าเนื่องจากการเชื่อมโยงทริกเกอร์จะได้รับการแก้ไขเมื่อค้นพบ ระบบจะเขียนค่าเริ่มต้นที่ว่างเปล่าลงในไฟล์ Manifest ของการติดตั้งใช้งานเป็นชื่อหัวข้อ นอกจากนี้ ชุดเครื่องมือยังป้องกันการทำงานนอกบริบทของชุดเครื่องมือด้วย หากไม่มีตัวแปร ค่าเริ่มต้นระดับโมดูลจะประเมินเป็น kit-undefined-discovery ดังนั้นตัวโหลดการกำหนดค่าจะดำเนินการไม่สำเร็จพร้อมข้อผิดพลาดที่อธิบายแทน ดังนี้

// ...
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
if (!instanceId) {
  throw new Error(
    "FIREBASE_KIT_INSTANCE_ID is not set. It is provided automatically to " +
      "kit instances by firebase-tools >= 15.32.0; deploy or emulate this " +
      "kit with a supported CLI version."
  );
}
// ...

การตรวจสอบนี้จะทำงานเมื่อเครื่องจัดการแก้ไขการกำหนดค่าเป็นครั้งแรก ดังนั้นตัวแปรที่ขาดหายไปจะทำให้เกิดข้อผิดพลาดขณะรันไทม์ที่ชัดเจน แทนที่จะเป็นฟังก์ชันที่เชื่อมโยงกับหัวข้อ kit-undefined-* อย่างเงียบๆ หากต้องการปฏิเสธการติดตั้งใช้งาน ให้ตรวจสอบที่ขอบเขตโมดูลเพื่อให้ทำงานระหว่างการค้นพบ เนื่องจาก CLI ได้รับรหัสอินสแตนซ์จาก firebase.json จึงไม่มี INSTANCE_ID ที่กำหนดค่าได้ และไม่มีอะไรที่ต้องซิงค์ในหลายอินสแตนซ์

ข้อมูลลับ

ใน extension.yaml คุณประกาศข้อมูลลับด้วย type: secret รันไทม์ของส่วนขยายจะจัดเก็บและเชื่อมโยงข้อมูลเหล่านั้น เพื่อให้โค้ดส่วนขยายอ่าน process.env.PARAM_NAME ได้โดยตรง ในฐานของโค้ด Cloud Functions ทั่วไป คุณจะประกาศและเชื่อมโยงข้อมูลลับแต่ละรายการอย่างชัดเจน ดังนี้

import { defineSecret } from "firebase-functions/params";
import { onRequest } from "firebase-functions/https";

const apiKey = defineSecret("API_KEY");
export const fn = onRequest({ secrets: [apiKey] }, handler);

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

ตัวอย่างที่ใช้ได้จริง: ทริกเกอร์อีเมลจาก Cloud Firestore

ก่อน MAIL_COLLECTION และ SMTP_PASSWORD จะอ่านเป็นตัวแปรสภาพแวดล้อมดิบใน config.ts ดังนี้

# extension.yaml
- param: MAIL_COLLECTION
  label: Email documents collection
  type: string
  default: mail
  required: true

- param: SMTP_PASSWORD
  label: SMTP password
  type: secret
// functions/src/config.ts
mailCollection: process.env.MAIL_COLLECTION,
smtpPassword: process.env.SMTP_PASSWORD,

หลัง defineString 1 รายการและ defineSecret 1 รายการ โดย CLI จะค้นพบทั้ง 2 รายการและอ่านจาก .env:

import { defineString, defineSecret } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";

const mailCollection = defineString("MAIL_COLLECTION", {
  label: "Email documents collection",
  default: "mail"
});

const smtpPassword = defineSecret("SMTP_PASSWORD", { label: "SMTP password" });

export const processQueue = onDocumentWritten(
  { document: `${mailCollection}/{documentId}`, secrets: [smtpPassword] },
  async (event) => {
    const collection = mailCollection.value();
    const password = smtpPassword.value();
    // ...
  }
);

5. ย้ายข้อมูลการโทรเข้าคิวงานภายใน

ส่วนขยายบางรายการจะจัดคิวงานลงในคิวงานของตัวเองจากภายในโค้ดฟังก์ชันโดยใช้ Firebase Admin SDK ซึ่งแตกต่างจากการรับงานที่ส่ง (ครอบคลุมในส่วนฟังก์ชันการอัปเกรดและฮุกวงจร Conversion) ในกรณีนี้ โค้ดของคุณคือผู้ผลิตที่เรียกใช้ queue.enqueue(...)

Admin SDK เวอร์ชันก่อนหน้ากำหนดให้ส่วนขยายต้องส่งรหัสอินสแตนซ์ส่วนขยายของตนเองเป็นพารามิเตอร์ที่ 2 เพื่อกำหนดเป้าหมายฟังก์ชันคิวงานในส่วนขยายเดียวกัน ตั้งแต่เวอร์ชัน firebase-admin 14.2.0 เป็นต้นไป ไม่จำเป็นต้องใช้และไม่แนะนำให้ใช้ ตอนนี้ Task Queue API จะกำหนดเป้าหมายคิวงานในบริบทเดียวกัน (เช่น ส่วนขยายหรือชุดเครื่องมือ) โดยค่าเริ่มต้น คุณสามารถนำพารามิเตอร์นี้ออกจากโค้ดได้อย่างปลอดภัยและเราขอแนะนำให้ทำเช่นนั้น ทั้งในรูปแบบส่วนขยายและฟังก์ชันแบบสแตนด์อโลน การนำพารามิเตอร์นี้ออกจะช่วยให้มั่นใจได้ถึงความสามารถในการพกพาและความเข้ากันได้ในอนาคต

ส่วนอื่นๆ ทั้งหมดเกี่ยวกับการเรียกใช้ enqueue เช่น เส้นทางทรัพยากร locations/<region>/functions/<name>, เพย์โหลดของงาน และตรรกะการลองใหม่จะยังคงเหมือนเดิม

ดูรายละเอียดเพิ่มเติมเกี่ยวกับการจัดคิวฟังก์ชันด้วย Cloud Tasks ได้ที่จัดคิวฟังก์ชันด้วย Cloud Tasks

ก่อน ส่วนขยายรุ่นที่ 1:

import { getFunctions } from "firebase-admin/functions";

const queue = getFunctions().taskQueue(
  `locations/${config.location}/functions/syncBigQuery`,
  process.env.EXT_INSTANCE_ID, // extension instance ID, injected by the runtime
);
await queue.enqueue(taskData);

หลัง ส่วนขยายรุ่นที่ 2:

import { getFunctions } from "firebase-admin/functions";

const queue = getFunctions().taskQueue(
  `locations/${process.env.FUNCTION_REGION}/functions/syncBigQuery`
);
await queue.enqueue(taskData);

หากการเรียก enqueue กำหนดเป้าหมายเป็นฐานของโค้ดที่มีคำนำหน้า ชื่อฟังก์ชันที่ค้นพบก็จะมีคำนำหน้าด้วย (เช่น orders-syncBigQuery) โปรดดูตรวจสอบและติดตั้งอินสแตนซ์ชุดฟังก์ชันทดแทนและทดสอบเป็นชุดฟังก์ชัน

6. ประกาศ API และบทบาท IAM ที่จำเป็น

ย้ายข้อกำหนด IAM และ API ของส่วนขยายออกจาก extension.yaml ไปยังโค้ด:

import { requiresAPI, requiresRole } from "firebase-functions";

requiresAPI("bigquery.googleapis.com", "Needed to write changelog rows");
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/bigquery.user");

เมื่อใช้การรักษาความปลอดภัยแบบประกาศ CLI ของ Firebase จะสร้างหรืออัปเดตบัญชีบริการรันไทม์ที่มีการจัดการสำหรับฐานของโค้ด และให้สิทธิ์รวมของบทบาทที่ประกาศทั้งหมด จัดทำเอกสารสำหรับผู้ใช้ว่าฟังก์ชันทั้งหมดในฐานของโค้ดจะทำงานด้วยบทบาทเหล่านั้น เว้นแต่ API สุดท้ายจะรองรับโมเดลที่แคบกว่า

ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery

ก่อน ประกาศใน extension.yaml รันไทม์ของส่วนขยายเปิดใช้ API และให้บทบาทแก่บัญชีที่จัดการ

apis:
  - apiName: bigquery.googleapis.com
roles:
  - role: bigquery.dataEditor
  - role: datastore.user
  - role: bigquery.user

หลัง ประกาศในโค้ดด้วย requiresAPI และ requiresRole:

import { requiresAPI, requiresRole } from "firebase-functions";

requiresAPI(
  "bigquery.googleapis.com",
  "Needed to write changelog rows and views"
);
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/datastore.user");
requiresRole("roles/bigquery.user");

หากส่วนขยายเผยแพร่เหตุการณ์ Eventarc คุณยังต้องตั้งค่าบทบาทและ API ที่จำเป็นที่เหมาะสมสำหรับการเผยแพร่เหตุการณ์ด้วย ก่อนหน้านี้ส่วนขยายจะจัดการเรื่องนี้โดยที่คุณไม่ต้องทำการเปลี่ยนแปลงใดๆ ใน extensions.yaml คุณสามารถทำเช่นนี้แบบมีเงื่อนไขในโค้ดเพื่อให้ชุดเครื่องมือขอสิทธิ์เหล่านี้เฉพาะเมื่อใช้ช่องทาง Eventarc ที่กำหนดเองเท่านั้น

if (!!process.env.EVENTARC_CHANNEL) {
  requiresRole("roles/eventarc.publisher");
  requiresAPI(
    "eventarcpublishing.googleapis.com",
    "Publishes the extension's custom events to its Eventarc channel."
  );
}

7. แปลงฮุกวงจร

หากส่วนขยายเรียกใช้ getExtensions().runtime() (เช่น setProcessingState หรือ setFatalError) ให้ลบการเรียกใช้เหล่านั้น เนื่องจากจะแสดงข้อผิดพลาดหากเรียกใช้จากฟังก์ชันรุ่นที่ 2 ที่ติดตั้งใช้งานตามปกติ ตอนนี้สถานะวงจรจะขับเคลื่อนโดย afterFirstDeploy และ afterRedeploy ซึ่งจะไม่มีการใช้การติดตามสถานะนี้

Firebase Extensions สามารถเรียกใช้การตั้งค่าเมื่อผู้ใช้ติดตั้ง อัปเดต หรือกำหนดค่าส่วนขยายใหม่ ในแพ็กเกจ npm ให้ประกาศการดำเนินการในวงจรที่เทียบเท่าในโค้ด

สำหรับการตั้งค่าแบบครั้งเดียว

import { afterFirstDeploy } from "firebase-functions/lifecycle";
import { onTaskDispatched } from "firebase-functions/tasks";

export const runInitialSetup = onTaskDispatched(async (request) => {
  await initializeResources(request.data);
});

afterFirstDeploy({
  task: {
    function: "runInitialSetup",
    body: {}
  }
});

สำหรับการอัปเดตการกำหนดค่าหรือโค้ด

import { afterRedeploy } from "firebase-functions/lifecycle";

afterRedeploy({
  task: {
    function: "runInitialSetup",
    body: { reconcile: true }
  }
});

ทำให้การดำเนินการตามวงจรเป็นแบบ Idempotent ผู้ใช้อาจต้องเรียกใช้สคริปต์อีกครั้งด้วยตนเองหากการส่งหรือการดำเนินการล้มเหลว โดยทำดังนี้

firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME
firebase functions:lifecycle:run afterRedeploy CODEBASE_NAME

ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery

ก่อน lifecycleEvents ใน extension.yaml ซึ่งขับเคลื่อนโดยรันไทม์ของส่วนขยาย

lifecycleEvents:
  onInstall:
    function: initBigQuerySync
    processingMessage: Configuring BigQuery Sync.
  onUpdate:
    function: setupBigQuerySync
    processingMessage: Configuring BigQuery Sync
  onConfigure:
    function: setupBigQuerySync
    processingMessage: Configuring BigQuery Sync

หลัง ประกาศในโค้ด งานจะจัดสรร BigQuery ในการติดตั้งใช้งานครั้งแรก:

import { afterFirstDeploy, afterRedeploy } from "firebase-functions/lifecycle";

afterFirstDeploy({ task: { function: "initBigQuerySync" } });
afterRedeploy({ task: { function: "setupBigQuerySync" } });

การจัดสรรเป็นแบบ Idempotent ดังนั้นการเรียกใช้ซ้ำจะปรับยอดชุดข้อมูล ตาราง และมุมมอง ผู้ใช้สามารถเรียกใช้ซ้ำด้วยตนเองได้โดยใช้ firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME

8. การตั้งค่าเอกสารสำหรับผู้ใช้

เขียนREADMEแพ็กเกจที่อธิบายข้อมูลต่อไปนี้เป็นอย่างน้อย

  • ค่า .env ที่แพ็กเกจต้องการ
  • ข้อมูลลับที่แพ็กเกจต้องการ และวิธีย้ายข้อมูลค่าข้อมูลลับที่มีอยู่
  • บทบาท IAM ที่แพ็กเกจประกาศด้วย requiresRole(...)
  • Google API ที่แพ็กเกจเปิดใช้หรือต้องใช้
  • ฮุกวงจรที่แพ็กเกจประกาศ และวิธีเรียกใช้ซ้ำด้วยตนเอง
  • หมายเหตุการเรียกเก็บเงิน
  • สิ่งที่เปลี่ยนแปลงไปเมื่อเทียบกับส่วนขยายเดิม
  • วิธีที่แพ็กเกจได้รับรหัสอินสแตนซ์ (FIREBASE_KIT_INSTANCE_ID ที่ตั้งค่าโดย CLI) และฟังก์ชันทั้งหมดของอินสแตนซ์ได้รับการติดตั้งใช้งานด้วยคำนำหน้า kit-<instanceId>-

ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery

แพ็กเกจ README จะจัดส่งตาราง "สิ่งที่เปลี่ยนแปลง" ที่เป็นรูปธรรม ดังนี้

ข้อกังวล ในฐานะส่วนขยาย ในฐานะ @firebase-function-kits/firestore-bigquery-export
การกำหนดค่า พารามิเตอร์ส่วนขยาย Cloud Functions พารามิเตอร์ผ่าน .env
IAM ได้รับสิทธิ์จากส่วนขยาย requiresRole(...), ใช้เมื่อติดตั้งใช้งาน
การจัดสรร งานในวงจรโดยส่วนขยาย afterFirstDeploy / afterRedeploy งาน
ชื่อฟังก์ชัน ext-<instanceId>-fsexportbigquery fsexportbigquery (มีคำนำหน้าหรือไม่ก็ได้)
รหัสอินสแตนซ์ EXT_INSTANCE_ID ที่แทรกโดยส่วนขยาย FIREBASE_KIT_INSTANCE_ID ตั้งค่าโดย CLI จาก firebase.json

9. ทดสอบฟังก์ชันรุ่นที่ 2

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

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

import { setGlobalOptions } from "firebase-functions";

if (!process.env.FIREBASE_KIT_INSTANCE_ID) {
  setGlobalOptions({
    region: "us-east1",
    maxInstances: 10,
  });
}

ตรวจสอบว่าคุณใช้ firebase-tools >= 15.32.0 และทำให้ฟังก์ชันรุ่นที่ 2 ที่แปลงแล้วใช้งานได้ในโปรเจ็กต์ทดสอบ พร้อมทรัพยากรที่เหมาะสมเพื่อทดสอบลักษณะการทำงาน หากคุณตั้งค่าโปรเจ็กต์ทดสอบจากการทดสอบส่วนขยายไว้แล้ว ให้เรียกใช้คำสั่งต่อไปนี้

firebase deploy --only functions

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

ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery

เราจะตรวจสอบการซิงค์ Cloud Firestore กับ BigQuery ตั้งแต่ต้นจนจบ ดังนี้

  1. ในหน้า Cloud Firestore ของคอนโซล Firebase ให้สร้างคอลเล็กชันที่คุณตั้งค่าเป็น COLLECTION_PATH (users) หากยังไม่มี
  2. สร้างเอกสารชื่อ bigquery-mirror-test ที่มีช่องใดก็ได้พร้อมค่าใดก็ได้
  3. ในหน้า BigQuery ของคอนโซล Google Cloud ให้ค้นหาตารางบันทึกการเปลี่ยนแปลงดิบ โดยควรมีแถวเดียวที่บันทึกการสร้างเอกสาร ดังนี้

    SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`
    
  4. ค้นหามุมมองล่าสุด ซึ่งควรแสดงเหตุการณ์การเปลี่ยนแปลงล่าสุดสำหรับเอกสารเดียวที่มีอยู่ (bigquery-mirror-test) ดังนี้

    SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`
    
  5. ลบเอกสาร bigquery-mirror-test ใน Cloud Firestore โดยจะหายไปจากมุมมองล่าสุด และระบบจะเพิ่มเหตุการณ์ DELETE ต่อท้ายตารางบันทึกการเปลี่ยนแปลงดิบ

    คุณสามารถตรวจสอบประวัติทั้งหมดของเอกสารรายการเดียวได้โดยทำดังนี้

    SELECT *
       FROM `PROJECT_ID.analytics.users_raw_changelog`
       WHERE document_name = "bigquery-mirror-test"
       ORDER BY timestamp ASC
    

ความแตกต่างจากการทดสอบส่วนขยาย

  • ทริกเกอร์จะติดตั้งใช้งานเป็น fsexportbigquery (ไม่มีคำนำหน้าเมื่อติดตั้งใช้งานเป็นฟังก์ชันรุ่นที่ 2 ทั่วไปและไม่ใช่ชุดอุปกรณ์) ไม่ใช่ ext-<instanceId>-fsexportbigquery มองหาชื่อนั้นในแดชบอร์ดและบันทึกของ Cloud Functions
  • ตอนนี้โค้ดของคุณจะทำงานใน Firebase Local Emulator Suite เป็นฟังก์ชันปกติ คุณตั้งค่าพารามิเตอร์เพื่อใช้ในโปรแกรมจำลองได้ด้วย .env.local นอกจากนี้ คุณยังทำ Unit Test โค้ดโดยใช้ SDK ของ firebase-functions-test ได้ตามที่อธิบายไว้ในการทำ Unit Test ของ Cloud Functions
  • รันไทม์ของส่วนขยายจะไม่ขับเคลื่อนการจัดสรรอีกต่อไป หากตารางบันทึกการเปลี่ยนแปลงหายไปหลังจากติดตั้งใช้งาน ให้เรียกใช้งานการตั้งค่าอีกครั้งด้วยตนเอง: firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME งานนี้เป็นแบบ Idempotent ดังนั้นการเรียกใช้อีกครั้งจะปรับยอดชุดข้อมูล ตาราง และมุมมอง
  • ค่าพารามิเตอร์มาจาก .env แทนที่จะเป็นแบบฟอร์มการติดตั้ง ดังนั้นการเรียกใช้ firebase deploy ซ้ำจึงไม่จำเป็นต้องมีการโต้ตอบเมื่อ .env เสร็จสมบูรณ์แล้ว

10. เผยแพร่ชุดฟังก์ชันใน npm

เมื่อตรวจสอบ Conversion จากส่วนขยายเป็นฟังก์ชันรุ่นที่ 2 แล้ว คุณจะเผยแพร่รุ่นที่อาจได้รับการเผยแพร่ไปยัง npm เพื่อทำการทดสอบตั้งแต่ต้นจนจบได้โดยใช้คำแนะนำอย่างใดอย่างหนึ่งต่อไปนี้

เมื่อเผยแพร่ชุดเครื่องมือไปยัง npm แล้ว คุณจะติดตั้งชุดเครื่องมือดังกล่าวได้ด้วย firebase functions:kits:install และแสดงเป็นรายการทดแทนอย่างเป็นทางการสำหรับส่วนขยาย

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

ก่อนเผยแพร่ ให้ทำดังนี้

  1. เลือกชื่อแพ็กเกจ ทั้งชื่อที่กำหนดขอบเขตและไม่ได้กำหนดขอบเขตจะใช้ได้ (ดูคำแนะนำด้านบน) โปรดทราบว่าแพ็กเกจที่กำหนดขอบเขตจะเป็นแบบส่วนตัวโดยค่าเริ่มต้น ดังนั้นให้ส่ง --access public
  2. สร้างและตรวจสอบสิ่งที่จัดส่ง main และ types ชี้ไปที่เอาต์พุตที่คอมไพล์แล้ว (lib/ ในตัวอย่างที่ใช้งานได้) ดังนั้นไดเรกทอรีดังกล่าวต้องรวมอยู่ในไฟล์ tar ที่เผยแพร่ ใช้ .npmignore หรือรายการที่อนุญาต files และตรวจสอบผลลัพธ์ด้วย npm pack --dry-run สคริปต์ prepublishOnly ที่เรียกใช้บิลด์จะป้องกันไม่ให้เผยแพร่เอาต์พุตที่ล้าสมัย

package.json สิ่งที่เพิ่มเข้ามาเพื่อทำให้การเผยแพร่ปลอดภัยโดยค่าเริ่มต้นมีดังนี้

{
  "files": ["lib", "README.md", "CHANGELOG.md"],
  "publishConfig": { "access": "public", "tag": "next" },
  "scripts": {
    "build": "tsc -b",
    "prepublishOnly": "npm run build && npm test"
  }
}

ช่อง "publishConfig": { "tag": "next" } ช่วยให้มั่นใจว่า npm publish แบบธรรมดาจะไม่เขียนทับ latest

การตัดรุ่นที่อาจได้รับการเผยแพร่:

เช่น หากต้องการเพิ่มเวอร์ชันในเครื่องจาก 0.0.2-rc.3 เป็น 0.0.2-rc.4 (การดำเนินการนี้จะคอมมิตและติดแท็กใน Git หาก package.json อยู่ที่รูทของที่เก็บ) ให้ทำดังนี้

npm version prerelease --preid rc

วิธีเผยแพร่รุ่นที่อาจได้รับการเผยแพร่และลงทะเบียน @your-org/your-kit@0.0.2-rc.4 ใน npm ภายใต้แท็ก next

npm publish

เว็บไซต์ npm อาจใช้เวลา 2-3 นาทีในการแสดงเวอร์ชันใหม่ npm view จะอ่านรีจิสทรีโดยตรง ดังนี้

npm view @your-org/your-kit versions dist-tags

เมื่อขั้นตอนที่ 11 และขั้นตอนที่ 12 ของคู่มือนี้เสร็จสมบูรณ์แล้ว คุณจะโปรโมตแพ็กเกจเป็นเวอร์ชันเสถียรได้โดยทำดังนี้

npm version 0.0.2
npm publish --tag latest
npm dist-tag add @your-org/your-kit@0.0.2 next

หมายเหตุเกี่ยวกับ npm-shrinkwrap.json: เราขอแนะนำอย่างยิ่งให้รวมไฟล์ npm-shrinkwrap.json ไว้ในแพ็กเกจ โดย CLI จะเตือนผู้ใช้เมื่อติดตั้งหากคุณไม่รวมไฟล์นี้ไว้ ซึ่งจะช่วยให้มั่นใจได้ว่าผู้ใช้จะใช้ทรัพยากร Dependency เดียวกันกับที่คุณทดสอบ และช่วยป้องกันการโจมตีซัพพลายเชน อย่างไรก็ตาม ระบบจะใช้ Shrinkwrap แบบคำต่อคำในโปรเจ็กต์ของผู้ใช้ รวมถึงในระหว่างการบิลด์ Cloud Functions (npm ci) ซึ่งรายการสำหรับนักพัฒนาแอปเท่านั้นอาจไม่สำเร็จด้วย EBADPLATFORM คุณอาจต้องนำรายการ "dev": true และ devDependencies ออกจากสำเนา Shrinkwrap ที่เผยแพร่

ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery

package.json ของชุดเครื่องมือ ณ เวลาที่เปิดตัวรุ่นที่อาจได้รับการเผยแพร่รุ่นที่ 4 มีดังนี้

{
  "name": "@firebase-function-kits/firestore-bigquery-export",
  "version": "0.0.2-rc.4",
  "repository": {
    "type": "git",
    "url": "https://github.com/firebase/extensions.git",
    "directory": "kits/firestore-bigquery-export"
  },
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "engines": { "node": "22" },
  "scripts": { "build": "tsc -b" }
}

โปรดทราบว่าในกรณีนี้ ชุดเครื่องมือจะอยู่ใน Monorepo ดังนั้นจึงต้องใส่ repository.directory เพื่อให้ลิงก์รีจิสทรี npm ชี้ไปยังโฟลเดอร์ที่ถูกต้อง CHANGELOG.md มีบันทึกสำหรับรุ่นที่รอดำเนินการ

11. ทดสอบเป็นชุดฟังก์ชัน

เมื่อเผยแพร่ชุดแล้ว เราขอแนะนำให้ทดสอบชุดโดยใช้ npm

ตรวจสอบว่าคุณใช้ firebase-tools เวอร์ชัน >= 15.32.0 และติดตั้งชุดเครื่องมือต่อไปนี้

firebase functions:kits:install --package <your-package-name>@<your-prerelease-version>

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

firebase deploy --only functions:<your-kit-instance-id>

หลังจากติดตั้งแล้ว Firebase CLI จะพิมพ์คำสั่งทำให้ใช้งานได้ที่คล้ายกันพร้อมรหัสอินสแตนซ์ที่แน่นอนที่คุณเลือกไว้ระหว่างการติดตั้ง

ตรวจสอบชุดอุปกรณ์อีกครั้งโดยทำตามวิธีการในขั้นตอนที่ 9 ทดสอบฟังก์ชันรุ่นที่ 2 ตอนนี้คุณกำลังติดตั้งใช้งานโดยใช้ชุดเครื่องมือ ฟังก์ชันของคุณจึงมีคำนำหน้าและชื่อเป็น kit-<instance-id>-<method-name> ซึ่งช่วยให้ชุดอุปกรณ์มีอินสแตนซ์หลายรายการ โดยติดตั้งใช้งานฟังก์ชันเดียวกันหลายครั้งในโปรเจ็กต์ โดยแต่ละรายการมีชื่อที่ไม่ซ้ำกัน

12. ทดสอบการแทนที่การย้ายข้อมูล

คุณสามารถตั้งค่าอินสแตนซ์ส่วนขยายที่ใช้งานได้ แล้วใช้คำแนะนำในการย้ายข้อมูลผู้ใช้ (โดยใช้ firebase ext:migrate --package หรือคำสั่ง CLI ของชุดฟังก์ชัน) เพื่อทดสอบชุดฟังก์ชันให้เสร็จสิ้นเพื่อใช้แทนการย้ายข้อมูล

13. แจ้งให้ผู้ใช้และ Google ทราบถึงส่วนขยายอย่างเป็นทางการที่ใช้แทน

เมื่อชุดฟังก์ชันทดแทนพร้อมใช้งานเป็นแพ็กเกจ npm ที่ผู้ใช้ควรย้ายข้อมูลไปใช้แล้ว ให้แจ้งทั้งผู้ใช้และ Google เกี่ยวกับการแทนที่อย่างเป็นทางการนี้ อัปเดตไฟล์ README.md ในที่เก็บ GitHub ที่โฮสต์ส่วนขยายด้วยข้อมูลต่อไปนี้

<!-- FIREBASE_EXTENSION_REPLACEMENT: extension="<your-extesion-id>" package="<your-npm-package-name>" -->
> [!WARNING]
> **Deprecation Notice:** The Firebase Extension `<your-extension>` is deprecated. Migrate to the [<your-npm-package-name>](<link-to-your-npm-package>) package.

Google จะสแกน README ของส่วนขยายที่รู้จักเพื่อหาความคิดเห็น เช่น <!-- FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export" package="@firebase-function-kits/firestore-bigquery-export" --> และใช้ข้อมูลนี้เพื่อสร้างรีจิสทรีอย่างเป็นทางการของรายการทดแทนที่จัดเก็บไว้ในที่เก็บ firebase-tools เป็น replacements.json นอกจากนี้ คุณยังตรวจสอบ replacements.json เพื่อดูว่าระบบจะสแกน README.md ใดสำหรับส่วนขยายของคุณได้ด้วย รายการอย่างเป็นทางการของผลิตภัณฑ์ทดแทนจะได้รับการอัปเดตทุกสัปดาห์

ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery

ส่วนขยาย firestore-bigquery-export README.md มีข้อมูลต่อไปนี้

<!-- FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export" package="@firebase-function-kits/firestore-bigquery-export" -->
> [!WARNING]
> **Deprecation Notice:** The Firebase Extension `firebase/firestore-bigquery-export` is deprecated. Please migrate to the [`@firebase-function-kits/firestore-bigquery-export`](https://www.npmjs.com/package/@firebase-function-kits/firestore-bigquery-export) package.