Ce guide vous explique comment migrer vos extensions de l'environnement Firebase Extensions obsolète vers une fonction que vos utilisateurs installent et déploient dans leur propre Cloud Functions pour la codebase Firebase (2e génération).
Il s'agit de la méthode de migration recommandée. Firebase tiendra à jour une liste des extensions avec leurs équivalents npm officiels. Ce guide vous explique comment créer les vôtres.
Tout au long de ce guide, l'extension Stream Cloud Firestore to
BigQuery (firestore-bigquery-export) est utilisée comme exemple. Chaque section se termine par un exemple concret qui montre à quoi ressemblait cette extension avant la migration et à quoi elle ressemble après, en tant que package @firebase-function-kits/firestore-bigquery-export.
Inscrivez-vous pour obtenir plus d'informations et d'aide sur la migration des extensions
Si vous avez des questions sur la migration depuis Firebase Extensions, vous pouvez nous contacter à l'adresse firebase-extensions-migrator-support-external@google.com. Nous enverrons également un e-mail à ce groupe lorsque nous mettrons à jour le guide avec plus d'informations sur l'empaquetage, le test et la distribution de vos fonctions de 2e génération.
Pour rejoindre ce groupe, envoyez un message à l'adresse firebase-extensions-migrator-support-external+subscribe@google.com. Vous recevrez alors un e-mail de demande d'adhésion. Vous devez répondre à cet e-mail, et non cliquer sur le bouton "Rejoindre ce groupe".
Avant de commencer
Pour effectuer cette migration comme indiqué, vous allez utiliser les fonctionnalités suivantes de Cloud Functions :
Configuration paramétrée. Chaque paramètre que vous déclarez dans
extension.yamldevient un paramètre défini dans le code de votre package.Rôles IAM déclaratifs et API requises Chaque rôle que vous déclarez dans
extension.yamldevient un appelrequiresRole(...), et chaque API devient un appelrequiresAPI(...)dans le code de votre package. Au moment du déploiement, la CLI Firebase attribue les rôles déclarés à un compte de service d'exécution géré et active les API déclarées en votre nom.Événements de cycle de vie pour les bases de code Cloud Functions. Les bases de code Cloud Functions sont désormais compatibles avec les événements de cycle de vie analogues à Firebase Extensions. Déclarez la configuration au moment de l'installation et de la mise à jour avec les hooks de cycle de vie
afterFirstDeploy(...)etafterRedeploy(...). Ils remplacent leslifecycleEventsque vous déclarez dansextension.yaml.
Migrer une source Firebase Extensions vers une fonction de 2e génération
(Facultatif) Migration automatique avec la compétence d'agent Firebase
Vous pouvez automatiser les étapes 1 à 8 (inventaire des ressources, déclenchement des mises à niveau, conversions de paramètres et de secrets, IAM déclaratif, hooks de cycle de vie et génération du fichier README du package) à l'aide de la compétence d'agent d'IA extension-to-functions-codebase officielle.
Installer la compétence
Si vous ou votre assistant de codage IA (Gemini dans Firebase, Cursor, Claude Code, GitHub Copilot) n'avez pas encore installé la compétence, exécutez la commande suivante à l'aide de Skills CLI :
npx skills add firebase/agent-skills --skill extension-to-functions-codebase
Une fois la compétence installée dans votre projet, votre assistant de programmation IA suit automatiquement ses règles de migration et ses étapes de transformation. Vous pouvez utiliser la requête suivante :
"Veuillez migrer cette extension Firebase vers un package de kit de fonctions de 2e génération publiable en suivant les instructions de la compétence extension-to-functions-codebase."
1. Inventaire de l'extension
Commencez par faire l'inventaire de votre extension : une liste complète de tout ce que l'extension déclare, envoie et documente. Ainsi, chaque comportement a une destination définie dans la fonction de 2e génération et rien n'est perdu lors de la migration.
Examinez chacun des éléments suivants et notez ce que vous trouvez :
extension.yaml, qui déclare vos paramètres, fonctions, événements, rôles IAM, API requises, secrets et hooks de cycle de vie.functions/, qui contient le code de votre fonction, les dépendances, la configuration de compilation, les déclencheurs et les fonctions de file d'attente des tâches.README.md,PREINSTALL.mdetPOSTINSTALL.md, qui contiennent des étapes de configuration, des avertissements et des notes sur la facturation.scripts/, qui contient tous les utilitaires d'importation, de remplissage, IAM, de réparation ou de migration, ainsi que tout autre outil que vous fournissez avec l'extension.
Ensuite, pour chaque élément de extension.yaml, déterminez où il doit être placé dans le package npm :
Convertissez la configuration utilisateur en paramètres Cloud Functions (étape 4).
Convertissez les secrets en secrets Cloud Functions (étape 4).
Convertissez les rôles IAM en déclarations
requiresRole(...)(étape 6).Convertissez les API Google requises en déclarations
requiresAPI(...)le cas échéant (étape 6).Convertissez les hooks d'installation et de mise à jour en déclarations
afterFirstDeploy(...)etafterRedeploy(...)(étape 7).Convertissez les ID d'instance de
EXT_INSTANCE_IDenFIREBASE_KIT_INSTANCE_ID(étape 4).
Exemple : Flux de Cloud Firestore à BigQuery
La lecture de firestore-bigquery-export/extension.yaml et de functions/ produit l'inventaire suivant :
Dans extension.yaml |
Nombre / Valeur | Où vont-elles ? |
|---|---|---|
params |
25 (COLLECTION_PATH, DATASET_ID, TABLE_ID, DATASET_LOCATION, VIEW_TYPE, …) |
Paramètres Cloud Functions (étape 4) |
apis |
bigquery.googleapis.com |
requiresAPI(...) (Étape 6) |
roles |
bigquery.dataEditor, datastore.user, bigquery.user |
requiresRole(...) (Étape 6) |
resources |
1 déclencheur d'événement (fsexportbigquery) + fonctions de file d'attente des tâches (initBigQuerySync, setupBigQuerySync) |
Fonctions du package exporté (étape 3) |
lifecycleEvents |
onInstall → initBigQuerySync ; onUpdate / onConfigure → setupBigQuerySync |
afterFirstDeploy / afterRedeploy (étape 7) |
| ID d'instance | Non utilisé (aucune lecture EXT_INSTANCE_ID) |
Rien à migrer |
scripts/ |
import/ (remplissage), gen-schema-view/ |
Conservés en tant que scripts (hors champ d'application ici) |
Analyse L'extension ne déclare aucun paramètre type: secret. Il n'y a donc rien à migrer pour les secrets à l'étape 4. Le déclencheur d'événement est déjà de 2e génération. Seules les fonctions de file d'attente des tâches sont encore de 1re génération (pertinent à l'étape 3).
2. Mettre à jour package.json
Mettez à jour le fichier package.json de votre extension. Si vous migrez une seule extension, il peut s'agir de la racine package.json. Si vous migrez de nombreuses extensions dans un seul dépôt, attribuez à chacune d'elles son propre package.
Versions minimales du SDK : déclarez firebase-functions >=
7.4.0 et firebase-admin >=
14.2.0 comme dépendances. Déclarez également votre version de firebase-functions comme dépendance homologue, afin que le projet Cloud Functions de vos utilisateurs dispose de la même version du SDK que celle avec laquelle votre bibliothèque a été écrite.
{
"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"
}
}
Exemple : Flux de Cloud Firestore à BigQuery
Avant Le functions/package.json de l'extension est privé, nomme l'ID de l'extension et déclare firebase-functions comme dépendance directe :
{
"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"
}
}
Après Package publiable : nom avec champ d'application, carte exports et firebase-functions déplacé vers 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. Mettre à niveau les fonctions de la 1re génération vers la 2e génération
Si votre extension exporte toujours des fonctions de 1re génération, convertissez chaque déclencheur en son équivalent de 2e génération. Importez les modules firebase-functions/... et transmettez les paramètres d'exécution dans les options de déclencheur.
Consultez le guide de mise à niveau de la 2e génération de Cloud Functions. Vous pouvez notamment minimiser les efforts de réécriture en utilisant la déstructuration d'événements corrigée de 2e génération et éviter de réécrire la logique de votre fonction, car le SDK de 2e génération expose les paramètres v1 en tant que champs dans l'objet d'événement. Vous pouvez ainsi utiliser des paramètres déstructurés/nommés et conserver votre logique métier inchangée.
Avant 1re génération :
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);
});
Après 2e génération :
import { onDocumentWritten } from "firebase-functions/firestore";
export const syncV2 = onDocumentWritten(
{ document: "{collectionId}/{documentId}" },
async ({ change, context }) =>
await handleWrite(change.before, change.after, context.params)
);
Consultez la comparaison des versions Cloud Functions pour obtenir une liste complète des différences entre les fonctions de 1re et 2e génération.
4. Convertir les paramètres et les secrets de l'extension
Paramètres
Chaque paramètre que vous déclarez dans extension.yaml devient un paramètre Cloud Functions.
Convertir les lectures directes de l'environnement :
const collectionPath = process.env.COLLECTION_PATH;
dans les paramètres 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);
}
);
Utilisez collectionPath.value() pour lire la chaîne à l'intérieur d'un gestionnaire. Utilisez collectionPath directement lorsqu'un espace réservé est attendu, comme un chemin de déclencheur de fonction.
La CLI Firebase détecte vos paramètres et lit leurs valeurs à partir de .env ou .env.<projectId>, ou invite vos utilisateurs lors du déploiement. Conservez les mêmes noms de paramètres pour que les valeurs d'une installation existante soient conservées.
Il est important de ne pas modifier les noms des paramètres déclarés dans votre code. Absolument pas. La migration des extensions préserve automatiquement les valeurs de paramètres des utilisateurs finaux existantes, mais uniquement lorsque les noms restent inchangés.
Exemple : Flux de Cloud Firestore à BigQuery
Avant Paramètre déclaré dans extension.yaml, lu en tant que variable d'environnement brute dans config.ts :
# extension.yaml
- param: COLLECTION_PATH
label: Collection path
type: string
required: true
// functions/src/config.ts
collectionPath: process.env.COLLECTION_PATH,
Après Un fichier defineString. La CLI le détecte et le lit à partir de .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 } }
}),
Le nom du paramètre n'a pas changé. Par conséquent, un .env existant continue de fonctionner.
ID d'instance
Les extensions lisent leur ID d'instance à partir de EXT_INSTANCE_ID, injecté par le runtime Extensions. Les kits de fonctions lisent leur ID d'instance à partir de FIREBASE_KIT_INSTANCE_ID, que la CLI Firebase définit pour chaque instance de kit sur la clé de l'instance dans la carte instances de firebase.json. La CLI le fournit lors de la découverte au moment du déploiement, dans l'émulateur et aux fonctions déployées.
L'ID d'instance n'est pas un paramètre. Vous ne devez donc pas le déclarer avec defineString. En fait, FIREBASE_... est un préfixe réservé dans les fichiers .env. Les utilisateurs ne pourront donc pas le définir ni le remplacer. Les valeurs injectées par l'interface de ligne de commande ne sont pas visibles par le système de paramètres. Lisez-le directement depuis l'environnement :
// Before
const instanceId = process.env.EXT_INSTANCE_ID;
// After
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
La variable ne sera définie que lorsque votre package sera déployé en tant que kit. Si votre code est également déployé en tant que codebase autonome (voir l'étape 9), traitez-le comme facultatif ou échouez rapidement avec un message clair lorsqu'il est manquant. Si votre extension exposait l'ID d'instance en tant que paramètre visible par l'utilisateur, vous devez supprimer ce paramètre, car la CLI Firebase possède désormais la valeur.
Exemple concret : supprimer des données utilisateur
(L'extension Stream Cloud Firestore à BigQuery ne lit pas son ID d'instance. Il n'y a donc rien à migrer.) L'extension Delete User Data l'utilise pour nommer ses sujets Pub/Sub.)
Avant Lisez-le comme une variable d'environnement brute dans config.ts avec le préfixe ext- que les extensions utilisent pour leurs ressources :
// functions/src/config.ts
discoveryTopic: `ext-${process.env.EXT_INSTANCE_ID}-discovery`,
deletionTopic: `ext-${process.env.EXT_INSTANCE_ID}-deletion`,
Après Une simple lecture process.env de FIREBASE_KIT_INSTANCE_ID utilisée pour deux paramètres ordinaires afin que les utilisateurs puissent remplacer les noms de thèmes :
// 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`,
}),
Les valeurs par défaut ne doivent pas être vides, car les liaisons de déclencheur sont résolues au moment de la découverte. Un nom par défaut vide serait écrit dans le fichier manifeste de déploiement en tant que nom de sujet. Le kit est également défensif contre l'exécution en dehors du contexte d'un kit. Si la variable est manquante, la valeur par défaut au niveau du module est kit-undefined-discovery. Le chargeur de configuration échoue donc avec une erreur explicative :
// ...
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."
);
}
// ...
Cette vérification s'exécute lorsqu'un gestionnaire résout sa configuration pour la première fois. Par conséquent, une variable manquante produit une erreur d'exécution claire plutôt que des fonctions liées silencieusement aux thèmes kit-undefined-*. Pour refuser le déploiement lui-même, effectuez la vérification au niveau du module afin qu'elle s'exécute lors de la découverte. Étant donné que l'interface CLI dérive l'ID d'instance de firebase.json, il n'y a pas de INSTANCE_ID configurable ni rien à synchroniser sur plusieurs instances.
Secrets
Dans extension.yaml, vous déclarez des secrets avec type: secret. Le runtime Extensions les stocke et les lie, de sorte que le code de votre extension peut lire process.env.PARAM_NAME directement. Dans une codebase Cloud Functions typique, vous déclarez et liez chaque secret de manière explicite :
import { defineSecret } from "firebase-functions/params";
import { onRequest } from "firebase-functions/https";
const apiKey = defineSecret("API_KEY");
export const fn = onRequest({ secrets: [apiKey] }, handler);
Une fois votre extension migrée vers un package/kit npm, les références secrètes sont gérées dans le fichier .env de l'utilisateur final. Il est important de ne pas modifier les noms secrets déclarés dans votre code du tout. Pendant la migration, les secrets des utilisateurs finaux sont migrés en conséquence.
Exemple : déclencher un e-mail à partir de Cloud Firestore
Avant MAIL_COLLECTION et SMTP_PASSWORD sont lues en tant que variables d'environnement brutes dans 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,
Après Un fichier defineString et un fichier defineSecret. La CLI détecte les deux et lit à partir de .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. Migrer les appels de file d'attente de tâches internes
Certaines extensions mettent en file d'attente des tâches sur leurs propres files d'attente de tâches à partir du code de leur fonction, à l'aide de Firebase Admin SDK. Cela diffère de la réception d'une tâche distribuée (abordée dans les sections Fonctions de mise à niveau et Convertir les hooks de cycle de vie). Ici, votre code est le producteur qui appelle queue.enqueue(...).
Les versions précédentes de Admin SDK exigeaient que les extensions transmettent leur propre ID d'instance d'extension en tant que deuxième paramètre pour cibler une fonction de file d'attente de tâches dans la même extension. À partir de la version 14.2.0 de firebase-admin, cette étape n'est ni requise ni recommandée. L'API Task Queue cible désormais par défaut les files d'attente de tâches dans le même contexte (par exemple, une extension ou un kit). Il est sûr et recommandé de supprimer ce paramètre dans votre code, à la fois en tant qu'extension et en tant que fonctions autonomes.
La suppression de ce paramètre garantit la portabilité et la compatibilité ascendante.
Tout le reste de l'appel d'envoi en file d'attente (chemin de ressource locations/<region>/functions/<name>, charge utile de la tâche et logique de réessai) reste identique.
Pour en savoir plus sur la mise en file d'attente des fonctions avec Cloud Tasks, consultez Mettre en file d'attente des fonctions avec Cloud Tasks.
Avant Extension de la 1re génération :
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);
Après Extension de 2e génération :
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${process.env.FUNCTION_REGION}/functions/syncBigQuery`
);
await queue.enqueue(taskData);
Si votre appel d'envoi cible une codebase avec préfixe, le nom de la fonction détectée est également préfixé (par exemple, orders-syncBigQuery). Consultez Examiner et installer l'instance du kit de fonctions de remplacement et Tester en tant que kit de fonctions.
6. Déclarer les API et les rôles IAM requis
Déplacez les exigences IAM et d'API de votre extension hors de extension.yaml et dans le code :
import { requiresAPI, requiresRole } from "firebase-functions";
requiresAPI("bigquery.googleapis.com", "Needed to write changelog rows");
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/bigquery.user");
Avec la sécurité déclarative, la CLI Firebase crée ou met à jour un compte de service d'exécution géré pour la codebase, et lui accorde l'ensemble de tous les rôles déclarés. Indiquez à vos utilisateurs que toutes les fonctions du code s'exécutent avec ces rôles, sauf si l'API finale est compatible avec un modèle plus étroit.
Exemple : Flux de Cloud Firestore à BigQuery
Avant Déclaré dans extension.yaml. L'environnement d'exécution des extensions a activé l'API et attribué les rôles à un compte géré :
apis:
- apiName: bigquery.googleapis.com
roles:
- role: bigquery.dataEditor
- role: datastore.user
- role: bigquery.user
Après Déclaré dans le code avec requiresAPI et 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");
7. Convertir les hooks de cycle de vie
Si votre extension appelle getExtensions().runtime() (par exemple, setProcessingState ou setFatalError), supprimez ces appels, car ils génèrent une erreur s'ils sont appelés à partir d'une fonction de 2e génération déployée normalement. L'état du cycle de vie est désormais déterminé par afterFirstDeploy et afterRedeploy, où ce suivi de l'état n'est pas utilisé.
Firebase Extensions peut exécuter la configuration lorsqu'un utilisateur installe, met à jour ou reconfigure une extension. Dans votre package npm, déclarez les actions de cycle de vie équivalentes dans le code.
Pour une configuration unique :
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: {}
}
});
Pour les mises à jour de configuration ou de code :
import { afterRedeploy } from "firebase-functions/lifecycle";
afterRedeploy({
task: {
function: "runInitialSetup",
body: { reconcile: true }
}
});
Rendez vos actions de cycle de vie idempotentes. Vos utilisateurs devront peut-être les réexécuter manuellement en cas d'échec de l'envoi ou de l'exécution :
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME
firebase functions:lifecycle:run afterRedeploy CODEBASE_NAME
Exemple : Flux de Cloud Firestore à BigQuery
Avant lifecycleEvents dans extension.yaml, en raison du runtime des extensions :
lifecycleEvents:
onInstall:
function: initBigQuerySync
processingMessage: Configuring BigQuery Sync.
onUpdate:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
onConfigure:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
Après Déclarée dans le code, la tâche provisionne BigQuery lors du premier déploiement :
import { afterFirstDeploy, afterRedeploy } from "firebase-functions/lifecycle";
afterFirstDeploy({ task: { function: "initBigQuerySync" } });
afterRedeploy({ task: { function: "setupBigQuerySync" } });
Le provisionnement est idempotent. Par conséquent, une nouvelle exécution réconcilie l'ensemble de données, la table et les vues.
Les utilisateurs peuvent les réexécuter manuellement avec firebase functions:lifecycle:run afterFirstDeploy
CODEBASE_NAME.
8. Configurer des documents pour vos utilisateurs
Rédigez un package README qui explique au minimum :
- Valeurs
.envrequises par le package. - Les secrets requis par le package et la façon de migrer les valeurs de secrets existantes.
- Rôles IAM que le package déclare avec
requiresRole(...). - Les API Google que le package active ou requiert.
- Les hooks de cycle de vie déclarés par le package et comment les réexécuter manuellement.
- Remarques sur la facturation.
- Ce qui a changé par rapport à l'extension d'origine.
- Comment le package obtient son ID d'instance (
FIREBASE_KIT_INSTANCE_IDdéfini par l'interface CLI) et que toutes les fonctions de l'instance sont déployées avec un préfixekit-<instanceId>-.
Exemple : Flux de Cloud Firestore à BigQuery
Le package README fournit un tableau concret des modifications :
| Problème | En tant qu'extension | En tant que @firebase-function-kits/firestore-bigquery-export |
|---|---|---|
| Configuration | Paramètres d'extension | Paramètres Cloud Functions via .env |
| IAM | Accordé par les extensions | requiresRole(...), appliqué au déploiement |
| Provisionnement | Tâche de cycle de vie par extensions | afterFirstDeploy tâche sur afterRedeploy |
| Noms des fonctions | ext-<instanceId>-fsexportbigquery |
fsexportbigquery (avec ou sans préfixe) |
| ID d'instance | EXT_INSTANCE_ID injecté par les extensions |
FIREBASE_KIT_INSTANCE_ID, défini par la CLI à partir de firebase.json |
9. Tester votre fonction de 2e génération
Vous devriez maintenant disposer d'une fonction de 2e génération qui, une fois déployée, se comporte de la même manière qu'une nouvelle installation de votre extension. L'étape suivante consiste à vérifier et à corriger les problèmes qui ont pu être introduits par inadvertance.
Pour appeler setGlobalOptions afin de définir des options globales telles qu'une région ou un processeur par défaut, vous ne devez le faire que lorsque vous déployez votre kit en tant que fonction de 2e génération autonome. Lorsque votre kit est installé en tant que package npm, vos utilisateurs appellent setGlobalOptions dans leur code d'encapsulation pour configurer ces paramètres. Ils recevront des avertissements si cela se produit deux fois.
Vous pouvez protéger cet appel en vérifiant la variable d'environnement FIREBASE_KIT_INSTANCE_ID :
import { setGlobalOptions } from "firebase-functions";
if (!process.env.FIREBASE_KIT_INSTANCE_ID) {
setGlobalOptions({
region: "us-east1",
maxInstances: 10,
});
}
Assurez-vous d'utiliser firebase-tools
>= 15.32.0 et de déployer votre fonction de 2e génération convertie dans un projet de test avec les ressources appropriées pour tester son comportement. Si vous avez déjà configuré un projet de test lors du test de votre extension, exécutez la commande suivante :
firebase deploy --only functions
Remplissez l'assistant qui s'affiche et qui vous demande les valeurs des paramètres de la même manière que vous auriez rempli le formulaire d'installation dans la console Firebase pour l'extension.
Exemple : Flux de Cloud Firestore à BigQuery
Nous vérifions la synchronisation de Cloud Firestore à BigQuery de bout en bout :
- Sur la page Cloud Firestore de la console Firebase, créez la collection que vous avez définie comme
COLLECTION_PATH(users) si elle n'existe pas déjà. - Créez un document nommé
bigquery-mirror-testcontenant des champs avec des valeurs. Sur la page BigQuery de la console Google Cloud, interrogez la table brute du journal des modifications. Elle doit contenir une seule ligne enregistrant la création du document :
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`Interrogez la dernière vue, qui devrait renvoyer le dernier événement de modification pour le seul document présent (
bigquery-mirror-test) :SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`Supprimez le document
bigquery-mirror-testdans Cloud Firestore. Il disparaît de la vue "Dernières modifications", et un événementDELETEest ajouté au tableau du journal des modifications brutes.Vous pouvez inspecter l'historique complet d'un document avec :
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog` WHERE document_name = "bigquery-mirror-test" ORDER BY timestamp ASC
Différences par rapport aux tests d'extension :
- Le déclencheur est déployé en tant que
fsexportbigquery(sans préfixe lorsqu'il est déployé en tant que fonction de 2e génération typique et non en tant que kit), et non en tant queext-<instanceId>-fsexportbigquery. Recherchez ce nom dans le tableau de bord et les journaux Cloud Functions. - Votre code s'exécute désormais dans Firebase Local Emulator Suite comme des fonctions normales.
Vous pouvez définir la valeur des paramètres à utiliser dans l'émulateur avec
.env.local. Vous pouvez également tester votre code à l'aide du SDKfirebase-functions-test, comme décrit dans Tests unitaires de Cloud Functions. - Le provisionnement n'est plus géré par le runtime Extensions. Si le tableau du journal des modifications est manquant après le déploiement, réexécutez manuellement la tâche de configuration :
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME. La tâche est idempotente. Par conséquent, si vous l'exécutez à nouveau, l'ensemble de données, la table et les vues sont réconciliés. - Les valeurs des paramètres proviennent de
.envplutôt que du formulaire d'installation. Les réexécutions defirebase deployne sont donc pas interactives une fois.envterminé.
10. Publier votre kit de fonctions sur npm
Une fois que vous avez validé votre conversion d'extension en fonction de 2e génération, vous pouvez publier une version candidate sur npm pour effectuer des tests de bout en bout à l'aide de l'un des guides suivants :
- Créer et publier des packages publics sans portée
- Créer et publier des packages publics à portée limitée
Une fois votre kit publié sur npm, il peut être installé avec firebase functions:kits:install et listé comme remplacement officiel de votre extension.
Nous vous recommandons vivement de publier d'abord une version candidate. Les kits sont installés par nom de package et par version. Une version préliminaire vous permet donc de tester le flux d'installation réel par rapport au registre sans exposer un package non terminé aux utilisateurs qui installent avec le tag latest par défaut.
Avant de publier :
- Choisissez un nom de package. Les noms avec et sans portée fonctionnent (voir les guides ci-dessus). Notez que les packages de portée sont privés par défaut. Vous devez donc transmettre
--access public. - Créez et vérifiez les navires.
mainettypespointent vers votre sortie compilée (lib/dans notre exemple), ce répertoire doit donc être inclus dans le fichier tar publié. Utilisez.npmignoreou une liste d'autorisationfiles, et inspectez le résultat avecnpm pack --dry-run. Un scriptprepublishOnlyqui exécute votre build empêche la publication d'une sortie obsolète.
Ajouts package.json qui rendent la publication sécurisée par défaut :
{
"files": ["lib", "README.md", "CHANGELOG.md"],
"publishConfig": { "access": "public", "tag": "next" },
"scripts": {
"build": "tsc -b",
"prepublishOnly": "npm run build && npm test"
}
}
Le champ "publishConfig": { "tag": "next" } garantit qu'un npm
publish simple n'écrase jamais latest.
Créer une version candidate :
Par exemple, pour incrémenter la version localement de 0.0.2-rc.3 à 0.0.2-rc.4 (cela valide et tague dans Git si package.json se trouve à la racine du dépôt) :
npm version prerelease --preid rc
Pour publier la version candidate et enregistrer @your-org/your-kit@0.0.2-rc.4 sur npm sous le tag next :
npm publish
L'affichage de la nouvelle version sur le site Web npm peut prendre quelques minutes. npm view lit directement le registre :
npm view @your-org/your-kit versions dist-tags
Une fois les étapes 11 et 12 de ce guide terminées, vous pouvez promouvoir le package vers une version stable :
npm version 0.0.2
npm publish --tag latest
npm dist-tag add @your-org/your-kit@0.0.2 next
Remarque sur npm-shrinkwrap.json : Nous vous recommandons vivement d'inclure un fichier npm-shrinkwrap.json avec votre package. La CLI avertit les utilisateurs lors de l'installation si vous ne le faites pas. Il garantit que les utilisateurs utilisent les dépendances exactes que vous avez testées et permet de se protéger contre les attaques liées à la chaîne d'approvisionnement. Toutefois, un shrinkwrap est appliqué mot pour mot dans les projets de vos utilisateurs, y compris lors de la compilation Cloud Functions (npm ci), où les entrées réservées aux développeurs peuvent échouer avec EBADPLATFORM. Vous devrez peut-être supprimer les entrées "dev": true et devDependencies de la copie shrinkwrap publiée.
Exemple : Flux de Cloud Firestore à BigQuery
package.json du kit au moment de sa quatrième version candidate :
{
"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" }
}
Notez que dans ce cas, le kit se trouve dans un monorepo. Il est donc important d'inclure repository.directory pour que le lien vers le registre npm pointe vers le bon dossier. Son CHANGELOG.md contient les notes de la version en attente.
11. Tester en tant que kit de fonctions
Une fois votre kit publié, nous vous recommandons de le tester à l'aide de npm.
Assurez-vous d'utiliser la version >= 15.32.0 de firebase-tools et installez le kit :
firebase functions:kits:install --package <your-package-name>@<your-prerelease-version>
Cette commande télécharge votre package depuis npm, le configure dans un nouveau répertoire source pour votre kit et vous guide dans la configuration de la première instance, comme pour le flux d'installation des extensions. Une fois que vous avez installé et configuré votre package en local, exécutez un déploiement pour créer les ressources dans votre projet Google Cloud :
firebase deploy --only functions:<your-kit-instance-id>
Après l'installation, la CLI Firebase affiche une commande de déploiement semblable avec l'ID d'instance exact que vous avez choisi lors de l'installation.
Validez à nouveau votre kit en suivant les instructions de l'étape 9. Testez votre fonction de 2e génération. Maintenant que vous déployez à l'aide de kits, vos fonctions sont préfixées et nommées kit-<instance-id>-<method-name>. Cela permet aux kits d'avoir plusieurs instances, en déployant la même fonction plusieurs fois dans un projet, chacune avec un nom unique.
12. Tester le remplacement de la migration
Vous pouvez configurer une instance d'extension fonctionnelle, puis utiliser le guide de migration pour les utilisateurs (avec firebase ext:migrate --package ou les commandes CLI des kits de fonctions) pour terminer de tester votre kit de fonctions en tant que remplacement de la migration.
13. Informer les utilisateurs et Google de votre extension de remplacement officielle
Une fois que votre kit de fonctions de remplacement est prêt et disponible en tant que package npm vers lequel les utilisateurs doivent migrer, informez-en vos utilisateurs et Google. Mettez à jour le fichier README.md dans le dépôt GitHub qui héberge votre extension en y ajoutant les informations suivantes :
<!-- 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 analyse les fichiers README des extensions connues pour y trouver des commentaires tels que <!--
FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export"
package="@firebase-function-kits/firestore-bigquery-export" --> et les utilise pour remplir son registre officiel des remplacements stockés dans le dépôt firebase-tools sous replacements.json.
Vous pouvez également consulter replacements.json pour voir quels README.md seront analysés pour votre extension. La liste officielle des remplacements est mise à jour toutes les semaines.
Exemple : Flux de Cloud Firestore à BigQuery
L'extension firestore-bigquery-exportREADME.md contient les éléments suivants :
<!-- 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.