ย้ายข้อมูล 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

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

คุณสามารถทำให้ขั้นตอนที่ 1 ถึง 8 (การจัดทำรายการทรัพยากร การทริกเกอร์ การอัปเกรด การแปลงพารามิเตอร์และข้อมูลลับ IAM แบบประกาศ ฮุกวงจรชีวิต และ การสร้าง README ของแพ็กเกจ) เป็นแบบอัตโนมัติได้โดยใช้ทักษะเอเจนต์ AI ของ 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 Extension นี้ไปยังแพ็กเกจ Function Kit รุ่นที่ 2 ที่เผยแพร่ได้ โดยทำตามวิธีการในextension-to-functions-codebase ทักษะ"

1. จัดทำรายการส่วนขยาย

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

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

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

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

  • 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)
);

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

ความแตกต่างที่สำคัญระหว่างฟังก์ชันรุ่นที่ 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ที่เกี่ยวข้องข้างต้นเพื่อแก้ไขปัญหาตำแหน่งสำหรับฟังก์ชันที่ทริกเกอร์เหตุการณ์รุ่นที่ 2

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

พารามิเตอร์

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

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

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 รายการเดียว 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 แทรกจะไม่ปรากฏในระบบ พารามิเตอร์ อ่านได้โดยตรงจากสภาพแวดล้อม

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

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

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

ตัวอย่างการทำงาน: ลบข้อมูลผู้ใช้

(ส่วนขยายสตรีม Cloud Firestore ถึง 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 ซึ่งแตกต่างจากการรับงานที่มอบหมาย (ครอบคลุมในส่วนฟังก์ชันการอัปเกรดและHook วงจรการเปลี่ยนลูกค้า) ในที่นี้ โค้ดของคุณคือผู้ผลิตที่เรียกใช้ 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 และไปที่ code:

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

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

เมื่อใช้การรักษาความปลอดภัยแบบประกาศ Firebase CLI จะสร้างหรืออัปเดตบัญชีบริการรันไทม์ที่มีการจัดการสำหรับฐานของโค้ด และให้สิทธิ์การรวมบทบาทที่ประกาศทั้งหมด จัดทำเอกสารสำหรับผู้ใช้ว่าฟังก์ชันทั้งหมดในฐานของโค้ดทำงานโดยมีบทบาทเหล่านั้น เว้นแต่ 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 ที่แพ็กเกจเปิดใช้หรือต้องใช้
  • Lifecycle Hook ที่แพ็กเกจประกาศและวิธีกรอกอีกครั้ง ด้วยตนเอง
  • หมายเหตุเกี่ยวกับการเรียกเก็บเงิน
  • มีการเปลี่ยนแปลงอะไรบ้างเมื่อเทียบกับส่วนขยายเดิม
  • วิธีที่แพ็กเกจได้รับรหัสอินสแตนซ์ (FIREBASE_KIT_INSTANCE_ID ที่ตั้งค่าโดย CLI) และฟังก์ชันทั้งหมดของอินสแตนซ์ได้รับการติดตั้งใช้งานด้วยคำนำหน้า kit-<instanceId>-

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

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

ความกังวล เป็นส่วนขยาย As @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 ในโค้ด Wrapper เพื่อกำหนดค่าพารามิเตอร์เหล่านี้ และจะได้รับคำเตือนหากเกิดเหตุการณ์นี้ 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 นอกจากนี้ คุณยังทดสอบโค้ดแบบหน่วยได้โดยใช้ SDK ของ firebase-functions-test ตามที่อธิบายไว้ในการทดสอบแบบหน่วยของ Cloud Functions
  • การจัดสรรไม่ได้ขับเคลื่อนโดยรันไทม์ของส่วนขยายอีกต่อไป หากตารางบันทึกการเปลี่ยนแปลง หายไปหลังจากที่ติดตั้งใช้งาน ให้เรียกใช้การตั้งค่าอีกครั้งด้วยตนเอง: firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME งานนี้เป็น ไอดีมโปเตนต์ ดังนั้นการเรียกใช้ซ้ำจะทำให้ชุดข้อมูล ตาราง และมุมมองสอดคล้องกัน
  • ค่าพารามิเตอร์มาจาก .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 อาจใช้เวลาสักครู่ในการแสดงเวอร์ชันใหม่ 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 จะเตือนผู้ใช้เมื่อติดตั้งหากคุณไม่ได้รวมไฟล์ดังกล่าว ซึ่งจะช่วยให้มั่นใจได้ว่าผู้ใช้จะใช้การอ้างอิงที่แน่นอนที่คุณทดสอบ และช่วยป้องกันการโจมตีซัพพลายเชน อย่างไรก็ตาม จะมีการใช้ shrinkwrap ตามตัวอักษรในโปรเจ็กต์ของผู้ใช้ รวมถึงในระหว่างCloud Functionsบิลด์ (npm ci) ซึ่งรายการที่ใช้ได้เฉพาะในสภาพแวดล้อมการพัฒนาอาจล้มเหลวด้วย EBADPLATFORM คุณอาจต้อง ลบรายการ "dev": true และ devDependencies ออกจากสำเนา ที่เผยแพร่แล้ว

ตัวอย่างการทำงาน: สตรีม 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.