Questa guida mostra come eseguire la migrazione delle estensioni dall'ambiente Firebase Extensions ritirato a una funzione che gli utenti installano ed eseguono il deployment nel proprio Cloud Functions per la base di codice Firebase (2ª gen.).
Questo è il percorso di migrazione consigliato. Firebasegestirà un elenco di estensioni con equivalenti npm ufficiali; questa guida ti illustra come creare la tua.
In questa guida, l'estensione Stream Cloud Firestore to
BigQuery (firestore-bigquery-export) viene utilizzata come esempio pratico. Ogni
sezione termina con un Esempio pratico che mostra l'aspetto dell'estensione
prima della migrazione e dopo, come pacchetto
@firebase-function-kits/firestore-bigquery-export.
Registrati per ricevere maggiori informazioni e assistenza sulla migrazione delle estensioni
Se hai domande su come eseguire la migrazione da Firebase Extensions, puoi contattarci all'indirizzo firebase-extensions-migrator-support-external@google.com. Invieremo un'email a questo gruppo anche quando aggiorneremo la guida con ulteriori informazioni su pacchettizzazione, test e distribuzione delle funzioni di 2ª gen.
Per unirti a questo gruppo, invia un messaggio all'indirizzo firebase-extensions-migrator-support-external+subscribe@google.com, che risponderà con un'email di richiesta di iscrizione. Devi rispondere a questa email, non fare clic sul pulsante "Unisciti a questo gruppo".
Prima di iniziare
Per completare questa migrazione come descritto, utilizzerai le seguenti funzionalità di Cloud Functions:
Configurazione parametrizzata. Ogni parametro dichiarato in
extension.yamldiventa un parametro definito nel codice del pacchetto.Ruoli IAM dichiarativi e API richieste. Ogni ruolo che dichiari in
extension.yamldiventa una chiamatarequiresRole(...)e ogni API diventa una chiamatarequiresAPI(...)nel codice del pacchetto. Al momento del deployment, la CLI Firebase concede i ruoli dichiarati a un service account di runtime gestito e attiva le API dichiarate per tuo conto.Eventi del ciclo di vita per i codebase Cloud Functions. Le codebase Cloud Functions ora supportano gli eventi del ciclo di vita analoghi a Firebase Extensions. Dichiara la configurazione in fase di installazione e aggiornamento con gli hook del ciclo di vita
afterFirstDeploy(...)eafterRedeploy(...). Questi sostituisconolifecycleEventsche dichiari inextension.yaml.
Eseguire la migrazione dell'origine Firebase Extensions a una funzione di 2ª gen.
(Facoltativo) Migrazione automatica con la skill Firebase dell'agente
Puoi automatizzare i passaggi da 1 a 8 (inventario delle risorse, trigger
degli upgrade, conversioni di parametri e secret, IAM dichiarativa, hook del ciclo di vita e
generazione del file README del pacchetto) utilizzando la competenza dell'agente AI
extension-to-functions-codebase ufficiale.
Installare la skill
Se tu o il tuo assistente di programmazione AI (Gemini in Firebase, Cursor, Claude Code, GitHub Copilot) non avete ancora installato la skill, esegui il seguente comando utilizzando l'interfaccia a riga di comando delle skill:
npx skills add firebase/agent-skills --skill extension-to-functions-codebase
Una volta installata la competenza nel progetto, l'assistente alla programmazione AI segue automaticamente le regole di migrazione e i passaggi di trasformazione. Puoi utilizzare il seguente prompt:
"Esegui la migrazione di questa estensione Firebase in un pacchetto di kit di funzioni di seconda generazione pubblicabile
seguendo le istruzioni nella skill extension-to-functions-codebase."
1. Inventariare l'estensione
Inizia con un inventario della tua estensione: un elenco completo di tutto ciò che l'estensione dichiara, spedisce e documenta, in modo che ogni comportamento abbia una destinazione definita nella funzione di 2ª gen. e nulla vada perso durante la migrazione.
Esamina ciascuno dei seguenti elementi e annota ciò che trovi:
extension.yaml, che dichiara parametri, funzioni, eventi, ruoli IAM, API richieste, secret e hook del ciclo di vita.functions/, che contiene il codice della funzione, le dipendenze, la configurazione della build, i trigger e le funzioni della coda di attività.README.md,PREINSTALL.mdePOSTINSTALL.md, che contengono passaggi di configurazione, avvisi e note di fatturazione.scripts/, che contiene eventuali utilità di importazione, backfill, IAM, riparazione o migrazione e qualsiasi altro strumento fornito insieme all'estensione.
Poi, per ogni elemento in
extension.yaml, decidi dove
deve essere inserito nel pacchetto npm:
Converti la configurazione utente in parametri Cloud Functions (passaggio 4).
Converti i secret in Cloud Functions secret (passaggio 4).
Converti i ruoli IAM in dichiarazioni
requiresRole(...)(passaggio 6).Converti le API di Google richieste in dichiarazioni
requiresAPI(...), se opportuno (passaggio 6).Converti gli hook di installazione e aggiornamento in dichiarazioni
afterFirstDeploy(...)eafterRedeploy(...)(passaggio 7).Converti gli ID istanza da
EXT_INSTANCE_IDaFIREBASE_KIT_INSTANCE_ID(passaggio 4).
Esempio elaborato: riproduci in streaming Cloud Firestore su BigQuery
La lettura di firestore-bigquery-export/extension.yaml e functions/ produce
questo inventario:
In extension.yaml |
Conteggio / valore | Dove vengono inviati |
|---|---|---|
params |
25 (COLLECTION_PATH, DATASET_ID, TABLE_ID, DATASET_LOCATION, VIEW_TYPE, …) |
Cloud Functions params (passaggio 4) |
apis |
bigquery.googleapis.com |
requiresAPI(...) (passaggio 6) |
roles |
bigquery.dataEditor, datastore.user, bigquery.user |
requiresRole(...) (passaggio 6) |
resources |
1 trigger di evento (fsexportbigquery) + funzioni di coda di attività (initBigQuerySync, setupBigQuerySync) |
Funzioni del pacchetto esportato (passaggio 3) |
lifecycleEvents |
onInstall → initBigQuerySync; onUpdate / onConfigure → setupBigQuerySync |
afterFirstDeploy / afterRedeploy (passaggio 7) |
| ID istanza | Non utilizzato (nessuna lettura EXT_INSTANCE_ID) |
Nessun elemento da migrare |
scripts/ |
import/ (backfill), gen-schema-view/ |
Mantenuti come script (non rientrano nell'ambito di questo articolo) |
Analisi. L'estensione non dichiara parametri type: secret, quindi
non c'è nulla da migrare per i secret nel passaggio 4. Il trigger
di eventi è già di 2ª gen.; solo le funzioni di code di attività sono ancora di 1ª
gen. (pertinente nel passaggio 3).
2. Aggiorna package.json
Aggiorna il file package.json dell'estensione. Se esegui la migrazione di un'estensione,
puoi utilizzare la package.json principale. Se esegui la migrazione di molte estensioni in un unico
repository, assegna a ogni estensione il proprio pacchetto.
Versioni minime dell'SDK: dichiara
firebase-functions >=
7.4.0 e firebase-admin >=
14.2.0 come dipendenze. Dichiara la tua versione di firebase-functions anche come dipendenza peer, in modo che il progetto Cloud Functions dei tuoi utenti abbia la stessa versione dell'SDK con cui è stata scritta la tua libreria.
{
"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"
}
}
Esempio elaborato: riproduci in streaming Cloud Firestore su BigQuery
Prima. Il file functions/package.json dell'estensione è privato, assegna un nome
all'ID estensione e dichiara firebase-functions come dipendenza diretta:
{
"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"
}
}
Dopo. Un pacchetto pubblicabile: nome con ambito, una mappa exports e
firebase-functions spostato in 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. Esegui l'upgrade delle funzioni dalla 1ª alla 2ª gen.
Se la tua estensione esporta ancora funzioni di 1ª gen., converti ogni trigger nel suo
equivalente di 2ª gen. Importa dai moduli firebase-functions/... e passa
le impostazioni di runtime nelle opzioni di attivazione.
Consulta la guida all'upgrade di Cloud Functions di 2ª gen.. In particolare, puoi ridurre al minimo gli sforzi di riscrittura utilizzando la destrutturazione degli eventi patch di 2ª gen. ed evitare di riscrivere la logica della funzione perché l'SDK di 2ª gen. espone i parametri v1 come campi nell'oggetto evento, consentendoti di utilizzare parametri destrutturati/denominati e mantenere invariata la logica di business.
Prima. 1ª gen.:
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);
});
Dopo. 2ª gen.:
import { onDocumentWritten } from "firebase-functions/firestore";
export const syncV2 = onDocumentWritten(
{ document: "{collectionId}/{documentId}" },
async ({ change, context }) =>
await handleWrite(change.before, change.after, context.params)
);
Consulta il confronto tra le versioni di Cloud Functions per un elenco completo delle differenze tra le funzioni di 1ª e 2ª gen.
Una differenza significativa tra le funzioni di 1ª e 2ª gen. è che le funzioni di 2ª gen. devono trovarsi nella stessa posizione delle risorse trigger. Quando gli utenti eseguono la migrazione da un'estensione che utilizza funzioni di 1ª gen. a un kit che utilizza funzioni di 2ª gen., potrebbero dover modificare le posizioni delle funzioni per soddisfare questo requisito.
Le posizioni di Cloud Functions selezionate dagli utenti potrebbero non funzionare durante la migrazione perché la posizione esistente viene mantenuta. Se le funzioni attivate dagli eventi della tua estensione si basavano sulla posizione delle funzioni fornite dall'utente, ti consigliamo di aggiungere un nuovo parametro all'estensione per raccogliere la posizione di attivazione dell'evento e mapparla alla nuova posizione della funzione.
Esempio elaborato: riproduci in streaming Cloud Firestore su 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())
);
Al primo deployment del kit, agli utenti verrà chiesto il loro
DATABASE_REGION e le funzioni verranno implementate nel corrispondente
functionRegion sopra, risolvendo eventuali problemi di posizione per le funzioni attivate
dagli eventi gen2.
4. Convertire i parametri e i secret dell'estensione
Parametri
Ogni parametro che dichiari in extension.yaml diventa un parametro Cloud Functions.
Converti le letture dirette dell'ambiente:
const collectionPath = process.env.COLLECTION_PATH;
nei parametri 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);
}
);
Utilizza collectionPath.value() per leggere la stringa all'interno di un gestore; utilizza
collectionPath direttamente dove è previsto un segnaposto, ad esempio un percorso di attivazione
della funzione.
La CLI Firebase rileva i parametri e legge i relativi valori da .env, .env.<projectId> o chiede agli utenti durante il deployment. Mantieni gli stessi nomi
dei parametri, in modo che i valori di un'installazione esistente vengano trasferiti.
È importante non modificare in alcun modo i nomi dei parametri dichiarati nel codice. La migrazione delle estensioni conserva automaticamente i valori dei parametri degli utenti finali esistenti, ma solo quando i nomi rimangono invariati.
Esempio elaborato: riproduci in streaming Cloud Firestore su BigQuery
Prima. Un parametro dichiarato in extension.yaml, letto come variabile di ambiente
non elaborata in config.ts:
# extension.yaml
- param: COLLECTION_PATH
label: Collection path
type: string
required: true
// functions/src/config.ts
collectionPath: process.env.COLLECTION_PATH,
Dopo. Un defineString; la CLI lo rileva e legge da .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 } }
}),
Il nome del parametro è invariato, quindi un .env esistente continua a funzionare.
ID istanza
Le estensioni leggono il proprio ID istanza da EXT_INSTANCE_ID, inserito dal runtime delle estensioni. I kit di funzioni leggono il proprio ID istanza da
FIREBASE_KIT_INSTANCE_ID, che la CLI Firebase imposta per ogni istanza del kit
sulla chiave dell'istanza nella mappa instances in firebase.json. La
CLI lo fornisce durante l'individuazione in fase di deployment, nell'emulatore e alle
funzioni di cui è stato eseguito il deployment.
L'ID istanza non è un parametro, quindi non dichiararlo con defineString. Infatti,
FIREBASE_... è un prefisso riservato nei file .env, quindi gli utenti non potranno
impostarlo o sostituirlo. I valori inseriti dalla CLI non sono visibili al sistema di parametri. Leggilo direttamente dall'ambiente:
// Before
const instanceId = process.env.EXT_INSTANCE_ID;
// After
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
La variabile verrà impostata solo quando il pacchetto viene implementato come kit. Se il tuo codice viene implementato anche come codebase autonoma (vedi Passaggio 9), consideralo facoltativo o genera un errore rapido con un messaggio chiaro quando non è presente. Se la tua estensione ha esposto l'ID istanza come parametro rivolto agli utenti, devi rimuoverlo, in quanto ora il valore è di proprietà della CLI Firebase.
Esempio pratico: Elimina dati utente
L'estensione Stream Cloud Firestore to BigQuery non legge il suo ID istanza, quindi non c'è nulla da migrare. L'estensione Elimina dati utente lo utilizza per denominare i propri argomenti Pub/Sub.)
Prima. Leggi come variabile di ambiente non elaborata in config.ts con il prefisso ext-
utilizzato dalle estensioni per le relative risorse:
// functions/src/config.ts
discoveryTopic: `ext-${process.env.EXT_INSTANCE_ID}-discovery`,
deletionTopic: `ext-${process.env.EXT_INSTANCE_ID}-deletion`,
Dopo. Una semplice lettura process.env di FIREBASE_KIT_INSTANCE_ID utilizzata per
due parametri ordinari in modo che gli utenti possano sostituire i nomi degli argomenti:
// 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`,
}),
I valori predefiniti non devono essere vuoti perché i binding dei trigger vengono risolti al momento
del rilevamento. Un valore predefinito vuoto verrà scritto nel manifest di deployment come
nome dell'argomento. Il kit è anche difensivo contro l'esecuzione al di fuori del contesto di un kit. Se la variabile non è presente, il valore predefinito a livello di modulo verrà valutato come
kit-undefined-discovery, quindi il caricatore della configurazione non riesce a caricare la configurazione e restituisce un errore esplicativo
invece:
// ...
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."
);
}
// ...
Questo controllo viene eseguito quando un gestore risolve per la prima volta la sua configurazione, quindi una variabile mancante produce un errore di runtime chiaro anziché funzioni associate silenziosamente agli argomenti kit-undefined-*. Per rifiutare la distribuzione stessa, esegui il controllo
nell'ambito del modulo in modo che venga eseguito durante il rilevamento. Poiché la CLI deriva l'ID istanza da firebase.json, non esiste un INSTANCE_ID configurabile e non è necessario mantenere la sincronizzazione tra più istanze.
Secret
In extension.yaml, dichiari i secret con type: secret. L'ambiente di runtime delle estensioni le archivia e le associa, in modo che il codice dell'estensione possa leggere process.env.PARAM_NAME direttamente. In una codebase Cloud Functions tipica,
dichiari e associ ogni secret in modo esplicito:
import { defineSecret } from "firebase-functions/params";
import { onRequest } from "firebase-functions/https";
const apiKey = defineSecret("API_KEY");
export const fn = onRequest({ secrets: [apiKey] }, handler);
Una volta eseguita la migrazione dell'estensione a un pacchetto/kit npm, i riferimenti ai secret vengono
gestiti nel file .env dell'utente finale. È importante non
modificare in alcun modo i nomi dei secret dichiarati nel codice. Durante la migrazione,
i secret dell'utente finale vengono migrati di conseguenza.
Esempio elaborato: Trigger Email From Cloud Firestore
Prima. MAIL_COLLECTION e SMTP_PASSWORD vengono lette come variabili di ambiente
non elaborate in 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,
Dopo. Un defineString e un defineSecret; la CLI rileva entrambi
e legge da .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. Eseguire la migrazione delle chiamate alla coda di attività interna
Alcune estensioni mettono in coda il lavoro nelle proprie code di attività dall'interno del codice della funzione, utilizzando Firebase Admin SDK. Questa operazione è diversa dalla ricezione di un'attività inviata (descritta nelle sezioni Funzioni di upgrade e Convertire gli hook del ciclo di vita). Qui il tuo codice è il producer che chiama
queue.enqueue(...).
Le versioni precedenti di Admin SDK richiedevano che le estensioni trasmettessero il proprio ID istanza di estensione come secondo parametro per indirizzare una funzione di coda di attività nella stessa estensione. A partire dalla versione 14.2.0 di firebase-admin, questa operazione non è né obbligatoria né consigliata. L'API Task Queue ora ha come target le code di attività nello stesso contesto (ad esempio, un'estensione o un kit) per impostazione predefinita. È sicuro e consigliato rimuovere
questo parametro nel codice sia come estensione che come funzioni autonome.
La rimozione di questo parametro garantisce la portabilità e la compatibilità futura.
Tutto il resto della chiamata di accodamento, ovvero il
percorso della risorsa locations/<region>/functions/<name>, il payload dell'attività e la
logica di ripetizione, rimane invariato.
Per saperne di più sull'accodamento delle funzioni con Cloud Tasks, consulta Accodare le funzioni con Cloud Tasks.
Prima. Estensione di 1ª gen.:
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);
Dopo. Estensione di 2ª gen.:
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${process.env.FUNCTION_REGION}/functions/syncBigQuery`
);
await queue.enqueue(taskData);
Se la chiamata di accodamento ha come target una codebase con prefisso, anche il nome della funzione rilevato
ha un prefisso (ad esempio, orders-syncBigQuery); vedi
Esamina e installa l'istanza del kit di funzioni di sostituzione
e Test come kit di funzioni.
6. Dichiarare le API e i ruoli IAM richiesti
Sposta i requisiti IAM e API della tua estensione da extension.yaml al codice:
import { requiresAPI, requiresRole } from "firebase-functions";
requiresAPI("bigquery.googleapis.com", "Needed to write changelog rows");
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/bigquery.user");
Con la sicurezza dichiarativa, la CLI Firebase crea o aggiorna un service account di runtime gestito per il codebase e gli concede l'unione di tutti i ruoli dichiarati. Documenta per i tuoi utenti che tutte le funzioni nel codebase vengono eseguite con questi ruoli, a meno che l'API finale non supporti un modello più ristretto.
Esempio elaborato: riproduci in streaming Cloud Firestore su BigQuery
Prima. Dichiarato in extension.yaml; il runtime delle estensioni ha attivato l'API e concesso i ruoli a un account gestito:
apis:
- apiName: bigquery.googleapis.com
roles:
- role: bigquery.dataEditor
- role: datastore.user
- role: bigquery.user
Dopo. Dichiarato nel codice con requiresAPI e 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");
Se la tua estensione pubblica eventi Eventarc, devi anche impostare i ruoli e le API richiesti appropriati per la pubblicazione degli eventi. In precedenza, questa operazione veniva gestita dalle estensioni senza richiedere modifiche al tuo extensions.yaml. Puoi farlo in modo condizionale nel codice in modo che il kit
richieda queste autorizzazioni solo quando viene utilizzato un canale Eventarc personalizzato.
if (!!process.env.EVENTARC_CHANNEL) {
requiresRole("roles/eventarc.publisher");
requiresAPI(
"eventarcpublishing.googleapis.com",
"Publishes the extension's custom events to its Eventarc channel."
);
}
7. Convertire gli hook del ciclo di vita
Se la tua estensione chiama getExtensions().runtime() (ad esempio, setProcessingState o setFatalError), elimina queste chiamate, in quanto generano un errore se chiamate da una funzione di 2ª gen. normalmente implementata. Lo stato del ciclo di vita è
ora determinato da afterFirstDeploy e afterRedeploy, dove questo monitoraggio dello stato
non viene utilizzato.
Firebase Extensions può eseguire la configurazione quando un utente installa, aggiorna o riconfigura un'estensione. Nel pacchetto npm, dichiara azioni del ciclo di vita equivalenti nel codice.
Per la configurazione una tantum:
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: {}
}
});
Per gli aggiornamenti di configurazione o del codice:
import { afterRedeploy } from "firebase-functions/lifecycle";
afterRedeploy({
task: {
function: "runInitialSetup",
body: { reconcile: true }
}
});
Rendi idempotenti le azioni del ciclo di vita. Se l'invio o l'esecuzione non va a buon fine, gli utenti potrebbero doverli eseguire di nuovo manualmente:
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME
firebase functions:lifecycle:run afterRedeploy CODEBASE_NAME
Esempio elaborato: riproduci in streaming Cloud Firestore su BigQuery
Prima. lifecycleEvents in extension.yaml, generato dal runtime
delle estensioni:
lifecycleEvents:
onInstall:
function: initBigQuerySync
processingMessage: Configuring BigQuery Sync.
onUpdate:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
onConfigure:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
Dopo. Dichiarato nel codice; le disposizioni del task BigQuery al primo deployment:
import { afterFirstDeploy, afterRedeploy } from "firebase-functions/lifecycle";
afterFirstDeploy({ task: { function: "initBigQuerySync" } });
afterRedeploy({ task: { function: "setupBigQuerySync" } });
Il provisioning è idempotente, quindi una nuova esecuzione riconcilia il set di dati, la tabella e le viste.
Gli utenti possono eseguire nuovamente il test manualmente con firebase functions:lifecycle:run afterFirstDeploy
CODEBASE_NAME.
8. Configurazione dei documenti per gli utenti
Scrivi un README del pacchetto che spieghi, come minimo:
- I valori
.envrichiesti dal pacchetto. - I secret richiesti dal pacchetto e come eseguire la migrazione dei valori dei secret esistenti.
- I ruoli IAM dichiarati dal pacchetto con
requiresRole(...). - Le API di Google che il pacchetto abilita o richiede.
- Gli hook del ciclo di vita dichiarati dal pacchetto e come eseguirli di nuovo manualmente.
- Note di fatturazione.
- Che cosa è cambiato rispetto all'estensione originale.
- Come il pacchetto ottiene l'ID istanza (
FIREBASE_KIT_INSTANCE_IDimpostato dalla CLI) e che tutte le funzioni dell'istanza vengono implementate con un prefissokit-<instanceId>-.
Esempio elaborato: riproduci in streaming Cloud Firestore su BigQuery
Il pacchetto README include una tabella concreta "Che cosa è cambiato":
| Problema | Come estensione | Come @firebase-function-kits/firestore-bigquery-export |
|---|---|---|
| Configurazione | Parametri di estensione | Cloud Functions params via .env |
| IAM | Concessa dalle estensioni | requiresRole(...), applicato al momento del deployment |
| In fase di provisioning | Attività del ciclo di vita per estensioni | afterFirstDeploy / afterRedeploy attività |
| Nomi delle funzioni | ext-<instanceId>-fsexportbigquery |
fsexportbigquery (con prefisso facoltativo) |
| ID istanza | EXT_INSTANCE_ID inserito dalle estensioni |
FIREBASE_KIT_INSTANCE_ID, impostato dalla CLI da firebase.json |
9. Testare la funzione di 2ª gen.
Ora dovresti avere una funzione di 2ª gen. che, una volta eseguito il deployment, si comporta in modo identico a una nuova installazione della tua estensione. Il passaggio successivo consiste nel verificare e correggere eventuali problemi introdotti accidentalmente durante il processo.
Per chiamare setGlobalOptions
per impostare opzioni globali come una regione o una CPU predefinita, devi farlo solo quando
esegui il deployment del kit come funzione di 2ª gen. autonoma. Quando il kit viene installato
come pacchetto npm, gli utenti chiamano setGlobalOptions nel codice di wrapping per
configurare questi parametri e riceveranno avvisi se ciò si verifica due volte.
Puoi proteggere questa chiamata controllando la variabile di ambiente FIREBASE_KIT_INSTANCE_ID:
import { setGlobalOptions } from "firebase-functions";
if (!process.env.FIREBASE_KIT_INSTANCE_ID) {
setGlobalOptions({
region: "us-east1",
maxInstances: 10,
});
}
Assicurati di utilizzare
firebase-tools
>= 15.32.0 e di eseguire il deployment della funzione di 2ª gen. convertita in un progetto di test
con le risorse appropriate per testarne il comportamento. Se hai già configurato un progetto di test
per testare l'estensione, esegui questo comando:
firebase deploy --only functions
Compila la procedura guidata risultante che ti chiede i valori dei parametri nello stesso modo in cui avresti compilato il modulo di installazione nella console Firebase per l'estensione.
Esempio elaborato: riproduci in streaming Cloud Firestore su BigQuery
Verifichiamo la sincronizzazione da Cloud Firestore a BigQuery end-to-end:
- Nella pagina Cloud Firestore della console Firebase, crea la raccolta
che hai impostato come
COLLECTION_PATH(users) se non esiste già. - Crea un documento denominato
bigquery-mirror-testcontenente campi con valori. Nella pagina BigQuery della console Google Cloud, esegui una query sulla tabella raw_changelog. Deve contenere una singola riga che registra la creazione del documento:
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`Esegui una query sulla visualizzazione più recente, che dovrebbe restituire l'ultimo evento di modifica per l'unico documento presente (
bigquery-mirror-test):SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`Elimina il documento
bigquery-mirror-testin Cloud Firestore. Scompare dalla visualizzazione più recente e alla tabella del changelog non elaborato viene aggiunto un eventoDELETE.Puoi esaminare la cronologia completa di un singolo documento con:
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog` WHERE document_name = "bigquery-mirror-test" ORDER BY timestamp ASC
Differenze rispetto al test dell'estensione:
- Il trigger viene eseguito il deployment come
fsexportbigquery(nessun prefisso quando viene eseguito il deployment come una tipica funzione di 2ª gen. e non come un kit), non comeext-<instanceId>-fsexportbigquery. Cerca questo nome nel dashboard e nei log Cloud Functions. - Il codice ora viene eseguito in Firebase Local Emulator Suite come funzioni normali.
Puoi impostare il valore dei parametri da utilizzare nell'emulatore con
.env.local. Puoi anche testare le unità del tuo codice utilizzando l'SDKfirebase-functions-testcome descritto in Test delle unità di Cloud Functions. - Il provisioning non è più gestito dal runtime delle estensioni. Se la tabella
del log delle modifiche non è presente dopo il deployment, esegui di nuovo manualmente l'attività di configurazione:
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME. L'attività è idempotente, quindi la sua esecuzione riconcilia il set di dati, la tabella e le viste. - I valori dei parametri provengono da
.envanziché dal modulo di installazione, pertanto le nuove esecuzioni difirebase deploynon sono interattive una volta completata l'esecuzione di.env.
10. Pubblica il tuo kit di funzioni su npm
Una volta convalidata la conversione dall'estensione alla funzione di 2ª gen., puoi pubblicare un candidato per la release su npm per i test end-to-end utilizzando una delle seguenti guide:
- Creare e pubblicare pacchetti pubblici senza ambito
- Creare e pubblicare pacchetti pubblici con ambito
Quando il kit viene pubblicato su npm, può essere installato con
firebase functions:kits:install ed elencato come
sostituzione ufficiale della tua estensione.
Ti consigliamo vivamente di pubblicare prima un candidato per la release. I kit vengono installati in base al nome e alla versione del pacchetto, quindi una pre-release ti consente di testare il flusso di installazione reale rispetto al registro senza esporre un pacchetto non finito agli utenti che eseguono l'installazione con il tag latest predefinito.
Prima della pubblicazione:
- Scegli un nome di pacchetto. Funzionano sia i nomi con ambito sia quelli senza ambito (vedi le guide
sopra). Tieni presente che i pacchetti con ambito sono privati per impostazione predefinita, quindi passa
--access public. - Costruisci e controlla le navi.
mainetypespuntano all'output compilato (lib/nel nostro esempio pratico), quindi questa directory deve essere inclusa nel file tar pubblicato. Utilizza.npmignoreo una lista consentitafilese ispeziona il risultato connpm pack --dry-run. Uno scriptprepublishOnlyche esegue la build impedisce la pubblicazione di output obsoleti.
Aggiunte a package.json che rendono la pubblicazione sicura per impostazione predefinita:
{
"files": ["lib", "README.md", "CHANGELOG.md"],
"publishConfig": { "access": "public", "tag": "next" },
"scripts": {
"build": "tsc -b",
"prepublishOnly": "npm run build && npm test"
}
}
Il campo "publishConfig": { "tag": "next" } assicura che un npm
publish semplice non sovrascriva mai latest.
Taglio di un candidato per la release:
Ad esempio, per incrementare la versione localmente da 0.0.2-rc.3 a 0.0.2-rc.4
(questo esegue il commit e il tagging in Git se package.json si trova nella radice del repository):
npm version prerelease --preid rc
Per pubblicare il candidato per la release e registrare @your-org/your-kit@0.0.2-rc.4 su
npm con il tag next:
npm publish
Potrebbero essere necessari alcuni minuti prima che sul sito web npm venga visualizzata la nuova versione; npm view legge
il registro direttamente:
npm view @your-org/your-kit versions dist-tags
Una volta completati il passaggio 11 e il passaggio 12 di questa guida, puoi promuovere il pacchetto a una versione stabile:
npm version 0.0.2
npm publish --tag latest
npm dist-tag add @your-org/your-kit@0.0.2 next
Nota su npm-shrinkwrap.json: ti consigliamo vivamente di includere un file npm-shrinkwrap.json nel pacchetto. Se non lo fai, la CLI avvisa gli utenti durante l'installazione. Garantisce che gli utenti utilizzino le dipendenze esatte che hai testato e
contribuisce a proteggere dagli attacchi alla catena di fornitura. Tuttavia, il wrapping viene applicato
alla lettera nei progetti degli utenti, anche durante la build Cloud Functions
(npm ci), dove le voci solo per sviluppatori possono non riuscire con EBADPLATFORM. Potresti dover
rimuovere le voci "dev": true e devDependencies dalla copia
shrinkwrap pubblicata.
Esempio elaborato: riproduci in streaming Cloud Firestore su BigQuery
package.json del kit al momento del quarto candidato per la release:
{
"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" }
}
Tieni presente che in questo caso il kit si trova in un monorepo, quindi è importante
includere repository.directory per il link al registro npm in modo che punti alla cartella
corretta. Il suo CHANGELOG.md contiene le note per la release in attesa.
11. Testare come kit di funzioni
Una volta pubblicato il kit, ti consigliamo di testarlo utilizzando npm.
Assicurati di utilizzare
firebase-tools
versione >= 15.32.0 e installa il kit:
firebase functions:kits:install --package <your-package-name>@<your-prerelease-version>
In questo modo, il pacchetto viene scaricato da npm, configurato in una nuova directory di origine per il kit e ti viene chiesto di configurare la prima istanza in modo simile al flusso di installazione delle estensioni. Una volta installato e configurato il pacchetto in locale, esegui un deployment per creare le risorse nel progetto Google Cloud:
firebase deploy --only functions:<your-kit-instance-id>
Dopo l'installazione, la CLI Firebase stampa un comando di deployment simile con l'ID istanza esatto che hai scelto durante l'installazione.
Convalida di nuovo il kit seguendo le istruzioni riportate nel
passaggio 9. Testa la funzione di 2ª gen.. Ora che esegui il deployment utilizzando i kit, le tue funzioni hanno il prefisso e il nome kit-<instance-id>-<method-name>. Ciò consente ai kit di avere più istanze,
di eseguire il deployment della stessa funzione più volte in un progetto, ognuna con un nome univoco.
12. Test migration replacement
Puoi configurare un'istanza di estensione funzionante e poi utilizzare la guida alla migrazione degli utenti (utilizzando firebase ext:migrate --package o i comandi CLI dei kit di funzioni) per completare il test del kit di funzioni come sostituzione della migrazione.
13. Notificare agli utenti e a Google la sostituzione ufficiale dell'estensione
Una volta che la sostituzione del kit di funzioni è pronta e disponibile come pacchetto npm a cui gli utenti devono eseguire la migrazione, informa sia gli utenti che Google di questa sostituzione ufficiale. Aggiorna il file README.md nel repository GitHub che ospita la tua estensione con le seguenti informazioni:
<!-- 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 esamina i file README delle estensioni noti per commenti come <!--
FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export"
package="@firebase-function-kits/firestore-bigquery-export" --> e li utilizza
per compilare il nostro registro ufficiale delle sostituzioni archiviato nel repository firebase-tools
come
replacements.json.
Puoi anche controllare replacements.json per vedere quali README.md verranno scansionati
per la tua estensione. L'elenco ufficiale delle sostituzioni viene aggiornato settimanalmente.
Esempio elaborato: riproduci in streaming Cloud Firestore su BigQuery
L'estensione firestore-bigquery-export
README.md
contiene:
<!-- 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.