Vai al contenuto

Bundle

I bundle sono i pacchetti di aggiornamento principali in Capgo. Ogni bundle contiene gli asset web (HTML, CSS, JS) che compongono il contenuto della tua app. La API Bundles ti permette di gestire questi pacchetti di aggiornamento, inclusi elenco ed eliminazione.

Un bundle rappresenta una specifica versione del contenuto web della tua app e include:

  • Bundle (versione): Numero di versione semantica per il bundle
  • Checksum: Hash univoco per verificare l'integrità del bundle
  • Info Storage: Dettagli su dove e come viene salvato il bundle
  • Requisiti Native: Requisiti minimi della versione dell'app native
  • Metadati: Timestamp di creazione, proprietario e altre info di tracciamento

Ecco come creare e caricare i bundle manualmente senza usare Capgo CLI:

Per prima cosa, builda gli asset web della tua app:

Finestra del terminale
npm run build

Passaggio 2: Crea lo zip del bundle usando gli stessi pacchetti di Capgo CLI

Sezione intitolata “Passaggio 2: Crea il bundle zip usando gli stessi pacchetti di Capgo CLI”

Importante: Usa gli stessi identici pacchetti JavaScript che Capgo CLI usa internamente per garantire la compatibilità.

Finestra del terminale
npm install adm-zip @tomasklaen/checksum

Crea un bundle Zip con JavaScript (come Capgo CLI)

Sezione intitolata “Crea un bundle Zip con JavaScript (come Capgo CLI)”

Nota: negli esempi seguenti, version si riferisce al nome del bundle (versione) usato da API.

const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const AdmZip = require('adm-zip');
const { checksum: getChecksum } = require('@tomasklaen/checksum');
// Exact same implementation as Capgo CLI
function zipFileUnix(filePath) {
const zip = new AdmZip();
zip.addLocalFolder(filePath);
return zip.toBuffer();
}
async function zipFileWindows(filePath) {
console.log('Zipping file windows mode');
const zip = new AdmZip();
const addToZip = (folderPath, zipPath) => {
const items = fs.readdirSync(folderPath);
for (const item of items) {
const itemPath = path.join(folderPath, item);
const stats = fs.statSync(itemPath);
if (stats.isFile()) {
const fileContent = fs.readFileSync(itemPath);
zip.addFile(path.join(zipPath, item).split(path.sep).join('/'), fileContent);
} else if (stats.isDirectory()) {
addToZip(itemPath, path.join(zipPath, item));
}
}
};
addToZip(filePath, '');
return zip.toBuffer();
}
// Main zipFile function (exact same logic as CLI)
async function zipFile(filePath) {
if (os.platform() === 'win32') {
return zipFileWindows(filePath);
} else {
return zipFileUnix(filePath);
}
}
async function createBundle(inputPath, outputPath, version) {
// Create zip using exact same method as Capgo CLI
const zipped = await zipFile(inputPath);
// Write to file
fs.writeFileSync(outputPath, zipped);
// Calculate checksum using exact same package as CLI
const checksum = await getChecksum(zipped, 'sha256');
return {
filename: path.basename(outputPath),
version: version,
size: zipped.length,
checksum: checksum
};
}
// Usage
async function main() {
try {
const result = await createBundle('./dist', './my-app-1.2.3.zip', '1.2.3');
console.log('Bundle info:', JSON.stringify(result, null, 2));
} catch (error) {
console.error('Error creating bundle:', error);
}
}
main();

Passaggio 3: Calcola il checksum SHA256 usando lo stesso pacchetto di CLI

Sezione intitolata “Passaggio 3: Calcola il checksum SHA256 usando lo stesso pacchetto di CLI”
const fs = require('node:fs');
const { checksum: getChecksum } = require('@tomasklaen/checksum');
async function calculateChecksum(filePath) {
const fileBuffer = fs.readFileSync(filePath);
// Use exact same package and method as Capgo CLI
const checksum = await getChecksum(fileBuffer, 'sha256');
return checksum;
}
// Usage
async function main() {
const checksum = await calculateChecksum('./my-app-1.2.3.zip');
console.log('Checksum:', checksum);
}
main();

Carica il tuo file zip su uno storage accessibile via web:

Finestra del terminale
# Example: Upload to your server via scp
scp my-app-1.2.3.zip user@your-server.com:/var/www/bundles/
# Example: Upload to S3 using AWS CLI
aws s3 cp my-app-1.2.3.zip s3://your-bucket/bundles/
# Example: Upload via curl to a custom endpoint
curl -X POST https://your-storage-api.com/upload \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@my-app-1.2.3.zip"

Importante: Il tuo bundle deve essere accessibile pubblicamente tramite un URL HTTPS (senza autenticazione). I server di Capgo devono scaricare il bundle da questo URL.

Esempi di URL pubblici validi:

  • https://your-storage.com/bundles/my-app-1.2.3.zip
  • https://github.com/username/repo/releases/download/v1.2.3/bundle.zip
  • https://cdn.jsdelivr.net/gh/username/repo@v1.2.3/dist.zip

Registra il bundle esterno con Capgo tramite chiamate API dirette:

async function registerWithCapgo(appId, version, bundleUrl, checksum, apiKey) {
const fetch = require('node-fetch');
// Create bundle (version)
const response = await fetch('https://api.capgo.app/bundle/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'authorization': apiKey
},
body: JSON.stringify({
app_id: appId,
version: version,
external_url: bundleUrl,
checksum: checksum
})
});
if (!response.ok) {
throw new Error(`Failed to create bundle: ${response.statusText}`);
}
const data = await response.json();
console.log('Bundle created:', data);
return data;
}
ParametroDescrizioneObbligatorio
app_idL'identifier della tua appSì
versionBundle (versione) versione semantica (es. “1.2.3”)Sì
external_urlAccessibile pubblicamente URL HTTPS per il download del bundle (senza auth)Sì
checksumChecksum SHA256 del file zipSì

Il tuo bundle zip deve rispettare questi requisiti:

  1. File index nella root: Deve avere index.html al livello root
  2. Integrazione Capacitor: Deve chiamare notifyAppReady() nella tua app code
  3. Asset Paths: Usa percorsi relativi per tutti gli asset
bundle.zip
├── index.html
├── assets/
│ ├── app.js
│ └── styles.css
└── images/

Script Node.js semplice per zip, checksum e upload su Capgo:

const fs = require('node:fs');
const os = require('node:os');
const AdmZip = require('adm-zip');
const { checksum: getChecksum } = require('@tomasklaen/checksum');
const fetch = require('node-fetch');
async function deployToCapgo() {
const APP_ID = 'com.example.app';
const VERSION = '1.2.3';
const BUNDLE_URL = 'https://your-storage.com/bundles/app-1.2.3.zip';
const API_KEY = process.env.CAPGO_API_KEY;
// 1. Create zip (same as Capgo CLI)
const zip = new AdmZip();
zip.addLocalFolder('./dist');
const zipped = zip.toBuffer();
// 2. Calculate checksum (same as Capgo CLI)
const checksum = await getChecksum(zipped, 'sha256');
console.log('Checksum:', checksum);
// 3. Upload to your storage (replace with your upload logic)
// fs.writeFileSync('./bundle.zip', zipped);
// ... upload bundle.zip to your storage ...
// 4. Register with Capgo API
const response = await fetch('https://api.capgo.app/bundle/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'authorization': API_KEY
},
body: JSON.stringify({
app_id: APP_ID,
version: VERSION,
external_url: BUNDLE_URL,
checksum: checksum
})
});
if (!response.ok) {
throw new Error(`Failed: ${response.statusText}`);
}
console.log('Bundle registered with Capgo!');
}
deployToCapgo().catch(console.error);

Installa le dipendenze:

Finestra del terminale
npm install adm-zip @tomasklaen/checksum node-fetch

Usa lo stesso identico pacchetto e metodo che Capgo CLI usa internamente:

const fs = require('node:fs');
const { checksum: getChecksum } = require('@tomasklaen/checksum');
async function calculateChecksum(filePath) {
const fileBuffer = fs.readFileSync(filePath);
// Use exact same package and method as Capgo CLI
const checksum = await getChecksum(fileBuffer, 'sha256');
return checksum;
}
// Verify checksum matches
async function verifyChecksum(filePath, expectedChecksum) {
const actualChecksum = await calculateChecksum(filePath);
const isValid = actualChecksum === expectedChecksum;
console.log(`File: ${filePath}`);
console.log(`Expected: ${expectedChecksum}`);
console.log(`Actual: ${actualChecksum}`);
console.log(`Valid: ${isValid}`);
return isValid;
}
// Usage
async function main() {
const bundleChecksum = await calculateChecksum('./my-app-1.2.3.zip');
console.log('SHA256 Checksum:', bundleChecksum);
}
main();
  • Integrità del Bundle: Garantisce che il bundle non sia stato corrotto durante il trasferimento
  • Verifica API: Capgo verifica i checksum prima di accettare i bundle
  • Verifica del Plugin: Il plugin mobile verifica i checksum prima di applicare gli update
  1. Gestione Bundle (versione): Usa versionamento semantico in modo costante
  2. Ottimizzazione dello Storage: Rimuovi periodicamente i bundle non utilizzati
  3. Compatibilità Bundle (versione): Imposta i requisiti minimi della versione native
  4. Strategia di Backup: Mantieni backup dei bundle critici (versioni)

https://api.capgo.app/bundle/

Recupera le informazioni sul bundle. Senza filtri, restituisce 50 bundle per pagina. Con version o id, restituisce lo stesso array JSON con zero o un bundle.

  • app_id: Obbligatorio. L'ID della tua app
  • page: Opzionale. Numero di pagina per la paginazione (ignorato quando version o id è impostato)
  • version: Opzionale. Nome esatto della versione del bundle ("app_versions.name, ad esempio 1.2.3). Restituisce un array JSON con zero o un elemento.
  • id: Opzionale. ID numerico del bundle ("app_versions.id). Restituisce un array JSON con zero o un elemento.

Usa lo stesso authorization header e API per lo scoping dell'app come lista paginata. I valori rifiutati dalla validazione dello schema di query restituiscono un invalid_query errore.

Usa questo endpoint prima dell'upload o dell'assegnazione del canale se ti serve un controllo HTTP invece di CLI:

Finestra del terminale
curl -H "authorization: YOUR_API_KEY" \
"https://api.capgo.app/bundle/?app_id=com.example.app&version=1.2.3"

Un array JSON non vuoto indica che la versione esiste già per l'app. Un array vuoto indica che non esiste.

Per i workflow di upload, il flag Capgo CLI bundle upload --version-exists-ok risulta già di successo se la versione target esiste, coprendo molti job CI senza chiamare questo endpoint.

interface Bundle {
app_id: string
bucket_id: string | null
checksum: string | null
created_at: string | null
deleted: boolean
external_url: string | null
id: number
minUpdateVersion: string | null
name: string
native_packages: Json[] | null
owner_org: string
r2_path: string | null
session_key: string | null
storage_provider: string
updated_at: string | null
user_id: string | null
}
Finestra del terminale
# Get all bundles
curl -H "authorization: your-api-key" \
"https://api.capgo.app/bundle/?app_id=app_123"
# Get next page
curl -H "authorization: your-api-key" \
"https://api.capgo.app/bundle/?app_id=app_123&page=1"
# Look up one bundle by version name
curl -H "authorization: your-api-key" \
"https://api.capgo.app/bundle/?app_id=app_123&version=1.0.0"
# Look up one bundle by id
curl -H "authorization: your-api-key" \
"https://api.capgo.app/bundle/?app_id=app_123&id=456"
[
{
"id": 1,
"app_id": "app_123",
"name": "1.0.0",
"checksum": "abc123...",
"minUpdateVersion": "1.0.0",
"storage_provider": "r2",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"deleted": false,
"owner_org": "org_123",
"user_id": "user_123"
}
]

https://api.capgo.app/bundle/

Elimina uno o tutti i bundle di un'app. Usa questa funzione con cautela: l'azione non è reversibile.

Per eliminare un bundle specifico:

interface BundleDelete {
app_id: string
version: string
}

Per eliminare tutti i bundle:

interface BundleDeleteAll {
app_id: string
}
Finestra del terminale
# Delete specific bundle
curl -X DELETE \
-H "authorization: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"app_id": "app_123",
"version": "1.0.0"
}' \
https://api.capgo.app/bundle/
# Delete all bundles
curl -X DELETE \
-H "authorization: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"app_id": "app_123"
}' \
https://api.capgo.app/bundle/
{
"status": "ok"
}

https://api.capgo.app/bundle/

Crea un nuovo bundle con URL esterno.

interface CreateBundleBody {
app_id: string
version: string
external_url: string // Must be publicly accessible HTTPS URL
checksum: string
}
Finestra del terminale
curl -X POST \
-H "authorization: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"app_id": "com.example.app",
"version": "1.2.3",
"external_url": "https://your-storage.com/bundles/app-1.2.3.zip",
"checksum": "a1b2c3d4e5f6789abcdef123456789abcdef123456789abcdef123456789abcd"
}' \
https://api.capgo.app/bundle/
{
"status": "ok"
}

https://api.capgo.app/bundle/metadata

Aggiorna i metadati del bundle, come il link e le informazioni sul commento.

interface UpdateMetadataBody {
app_id: string
version_id: number // bundle (version) id
link?: string
comment?: string
}
Finestra del terminale
curl -X POST \
-H "authorization: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"app_id": "app_123",
"version_id": 456,
"link": "https://github.com/myorg/myapp/releases/tag/v1.0.0",
"comment": "Fixed critical bug in authentication"
}' \
https://api.capgo.app/bundle/metadata
{
"status": "success"
}

https://api.capgo.app/bundle/

Assegna un bundle a un canale specifico. Questo collega un bundle (versione) a un canale per la distribuzione.

interface SetChannelBody {
app_id: string
version_id: number // bundle (version) id
channel_id: number
target?: "auto" | "stable" | "rollout" // default: auto
}

target determina dove viene collegato il bundle quando il canale ha un progressive rollout configurato:

  • auto — il target del rollout se il progressive rollout è configurato; altrimenti stable (default).
  • rollout — imposta sempre il target del rollout; il fallback stable rimane invariato.
  • stable — sostituisce il fallback stable (escape hatch).
Finestra del terminale
curl -X PUT \
-H "authorization: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"app_id": "app_123",
"version_id": 456,
"channel_id": 789
}' \
https://api.capgo.app/bundle/
{
"status": "success",
"message": "Bundle 1.0.0 set to channel production"
}

Tutti gli errori seguono il formato standard descritto nella panoramica di API.

Scenari di errore comuni e relative risposte:

// app_id is not a reverse-domain identifier
{
"error": "invalid_app_id",
"message": "App ID must be a reverse domain string",
"moreInfo": { "app_id": "my app" }
}
// Deleting a bundle without access to the app
{
"error": "cannot_delete_bundle",
"message": "You can't access this app",
"moreInfo": { "app_id": "com.example.app" }
}
// Deleting a bundle that a channel still uses
{
"error": "cannot_delete_linked_version",
"message": "Cannot delete a bundle linked to a channel",
"moreInfo": { "app_id": "com.example.app", "version": "1.0.0" }
}
// Creating a bundle whose version is not valid semver
{
"error": "invalid_version_format",
"message": "Version must be valid semver format (e.g., 1.0.0, 1.0.0-alpha.1)",
"moreInfo": { "version": "v1" }
}
// Setting a channel to a bundle that does not exist
{
"error": "cannot_find_version",
"message": "Cannot find version",
"moreInfo": {}
}
  1. Pulizia vecchi Bundle (Versioni)
// Delete outdated beta bundles (versions)
{
"app_id": "app_123",
"version": "1.0.0-beta.1"
}
  1. Reset App
// Remove all bundles to start fresh
{
"app_id": "app_123"
}
  1. Policy di Retention: Definisci per quanto tempo conservare i vecchi bundle
  2. Gestione Dimensioni: Monitora le dimensioni dei bundle e l'uso dello storage
  3. Strategia di Backup: Valuta il backup dei bundle critici (versioni)
  4. Ottimizzazione Costi: Rimuovi i bundle non necessari per ottimizzare i costi di storage

Se stai usando Bundle per pianificare lo storage e la gestione dei file, collegalo a @capgo/capacitor-data-storage-sqlite per i dettagli di implementazione in @capgo/capacitor-data-storage-sqlite, Utilizzo di @capgo/capacitor-data-storage-sqlite per la funzionalità nativa in Utilizzo di @capgo/capacitor-data-storage-sqlite, @capgo/capacitor-file per i dettagli di implementazione in @capgo/capacitor-file, Utilizzo di @capgo/capacitor-file per la funzionalità nativa in Usare @capgo/capacitor-file, e @capgo/capacitor-uploader per i dettagli implementativi in @capgo/capacitor-uploader.