본문 바로가기

번들

Capgo에서 Bundle은 핵심 업데이트 패키지입니다. 각 bundle에는 앱 콘텐츠를 구성하는 웹 에셋(HTML, CSS, JS)이 포함되어 있습니다. Bundles API를 사용하면 bundle 목록 확인 및 삭제를 포함하여 이러한 업데이트 패키지를 관리할 수 있습니다.

bundle은 앱 웹 콘텐츠의 특정 bundle(버전)을 나타내며 다음을 포함합니다:

  • Bundle (버전): Semantic version 번호 bundle용
  • Checksum: 번들의 무결성을 검증하기 위한 고유 해시
  • 스토리지 정보: 번들이 저장된 위치 및 방식에 대한 상세 정보
  • Native 요구 사항: 최소 네이티브 앱 버전 요구 사항
  • Metadata: 생성 시간, 소유권 및 기타 추적 정보

수동 번들 생성 (CLI 미사용)

“수동 번들 생성 (CLI 미사용)” 섹션

Capgo CLI를 사용하지 않고 번들을 수동으로 생성하고 upload하는 방법은 다음과 같습니다:

먼저, 앱의 웹 에셋을 빌드하세요:

터미널 창
npm run build

2단계: Capgo CLI와 동일한 패키지를 사용하여 Bundle Zip 생성

“2단계: Capgo CLI와 동일한 패키지를 사용하여 Bundle Zip 생성” 섹션

중요: 호환성을 보장하려면 Capgo CLI가 내부적으로 사용하는 것과 정확히 동일한 JavaScript 패키지를 사용하세요.

터미널 창
npm install adm-zip @tomasklaen/checksum

JavaScript로 Zip Bundle 생성 (Capgo CLI와 동일)

“JavaScript로 Zip Bundle 생성 (Capgo CLI와 동일)” 섹션

참고: 아래 예제에서 version 은(는) API에서 사용하는 bundle (version) 이름을 의미합니다.

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

3단계: CLI와 동일한 패키지를 사용하여 SHA256 Checksum 계산하기

“3단계: CLI와 동일한 패키지를 사용하여 SHA256 Checksum 계산하기” 섹션
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();

4단계: Storage에 Bundle 업로드하기

“4단계: Storage에 Bundle 업로드하기” 섹션

zip 파일을 웹에서 접근 가능한 저장소에 업로드하세요:

터미널 창
# 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"

중요: 번들은 반드시 공개적으로 접근 가능한 HTTPS URL 형태여야 합니다 (인증 불필요). Capgo 서버가 이 URL에서 번들을 다운로드해야 합니다.

유효한 공개 URL 예시:

  • 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

5단계: Capgo API로 번들 등록

“5단계: Capgo API로 번들 등록” 섹션

직접적인 API 호출을 통해 외부 번들을 Capgo에 등록하세요:

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;
}
파라미터설명필수
app_id앱 식별자예
versionBundle (버전) 시맨틱 버전 (예: “1.2.3”)예
external_url공개 접근 가능 Bundle 다운로드 가능한 HTTPS URL (인증 불필요)예
checksumzip 파일의 SHA256 체크섬예

Bundle 구조 요구 사항

“Bundle 구조 요구 사항” 섹션

Bundle zip 파일은 다음 요구 사항을 준수해야 합니다:

  1. 루트 인덱스 파일: 필수 사항 index.html 루트 레벨에 위치
  2. Capacitor 통합: 필수 호출 notifyAppReady() 앱의 code에서 호출해야 함
  3. Asset 경로: 모든 asset에 상대 경로 사용
bundle.zip
├── index.html
├── assets/
│ ├── app.js
│ └── styles.css
└── images/

수동 워크플로우 전체 예시

“수동 워크플로우 전체 예시” 섹션

zip 압축, 체크섬 생성, Capgo 업로드를 위한 간단한 Node.js 스크립트:

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

의존성 설치:

터미널 창
npm install adm-zip @tomasklaen/checksum node-fetch

JavaScript Checksum 계산 (Capgo CLI와 동일)

“JavaScript Checksum 계산 (Capgo CLI와 동일)” 섹션

Capgo 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;
}
// 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();
  • Bundle 무결성: 전송 중 bundle이 손상되지 않았음을 보장합니다
  • API 검증: Capgo는 bundle을 수락하기 전에 checksum을 검증합니다
  • Plugin 검증: 모바일 plugin은 업데이트를 적용하기 전에 checksum을 검증합니다
  1. Bundle (버전) 관리: semantic versioning이어야 일관되게
  2. 스토리지 최적화: 사용하지 않는 bundle을 주기적으로 삭제
  3. Bundle (버전) 호환성: 적절한 최소 native 버전 요구사항 설정
  4. 백업 전략: 주요 bundle (버전) 백업 유지

엔드포인트

“Endpoints” 섹션

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

번들 정보를 가져옵니다. 필터가 없으면 페이지당 50개의 번들을 반환하며, version 또는 id필터 사용 시, 0개 또는 1개의 번들이 포함된 동일한 JSON 배열 형식을 반환합니다.

  • app_id: 필수. 앱의 ID
  • page: 선택 사항. 페이지 번호 (다음의 경우 무시됨) version 또는 id 설정되었는지 여부)
  • version: 선택 사항. 정확한 bundle 버전 이름 (app_versions.name, 예를 들어 1.2.3). 항목이 0개 또는 1개인 JSON 배열을 반환합니다.
  • id: 선택 사항. 숫자 bundle id (app_versions.id). 항목이 0개 또는 1개인 JSON 배열을 반환합니다.

페이지네이션된 목록과 동일한 authorization header와 API 키 app scoping을 사용하세요. query-schema 검증에서 거부된 값은 invalid_query error를 반환합니다.

버전 존재 여부 확인 (CI)

“버전 존재 여부 확인 (CI)” 섹션

CLI 대신 HTTP 체크가 필요한 경우, upload 또는 channel 할당 전에 이 기능을 사용하세요:

터미널 창
curl -H "authorization: YOUR_API_KEY" \
"https://api.capgo.app/bundle/?app_id=com.example.app&version=1.2.3"

JSON 배열이 비어 있지 않으면 해당 앱에 이미 버전이 존재함을 의미합니다. 빈 배열이면 존재하지 않는 것입니다.

upload 워크플로우의 경우, Capgo CLI 플래그를 bundle upload --version-exists-ok 대상 버전이 이미 존재하면 성공적으로 종료되어, 이 엔드포인트를 호출하지 않고도 많은 CI 작업을 처리할 수 있습니다.

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
}
터미널 창
# 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/

앱의 번들 하나 또는 전체를 삭제합니다. 이 작업은 되돌릴 수 없으므로 주의해서 사용하세요.

특정 번들을 삭제하려면:

interface BundleDelete {
app_id: string
version: string
}

모든 번들을 삭제하려면:

interface BundleDeleteAll {
app_id: string
}
터미널 창
# 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/

외부 URL로 새 bundle 생성

interface CreateBundleBody {
app_id: string
version: string
external_url: string // Must be publicly accessible HTTPS URL
checksum: string
}
터미널 창
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"
}

POST (메타데이터)

“POST (Metadata)” 섹션

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

link 및 comment 정보와 같은 bundle metadata를 업데이트합니다.

interface UpdateMetadataBody {
app_id: string
version_id: number // bundle (version) id
link?: string
comment?: string
}
터미널 창
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/

번들을 특정 채널에 설정합니다. 이 작업을 통해 번들(버전)을 배포용 채널에 연결할 수 있습니다.

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

target 채널에 progressive rollout이 설정된 경우 번들이 연결되는 위치를 제어합니다:

  • auto — progressive rollout이 설정되면 rollout target, 아니면 stable(기본값).
  • rollout — 항상 rollout target을 설정하고, stable fallback은 그대로 유지합니다.
  • stable — stable fallback을 대체합니다(escape hatch).
터미널 창
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"
}

모든 에러는 다음에 설명된 표준 에러 형식을 따릅니다: API 개요.

주요 에러 시나리오와 응답:

// 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. 오래된 번들(버전) 정리
// Delete outdated beta bundles (versions)
{
"app_id": "app_123",
"version": "1.0.0-beta.1"
}
  1. 앱 리셋
// Remove all bundles to start fresh
{
"app_id": "app_123"
}

스토리지 고려 사항

“스토리지 고려 사항” 섹션
  1. 보관 정책: 이전 bundle 보관 기간 설정
  2. 용량 관리: bundle 크기 및 스토리지 사용량 모니터링
  3. 백업 전략: 중요한 bundle(버전) 백업 고려
  4. 비용 최적화: 스토리지 비용 최적화를 위해 불필요한 bundle 삭제

사용 중인 경우 번들 스토리지와 파일 처리를 계획하려면, 다음과 연결하세요 @capgo/capacitor-data-storage-sqlite @capgo/capacitor-data-storage-sqlite의 구현 상세 내용은, @capgo/capacitor-data-storage-sqlite 사용하기 @capgo/capacitor-data-storage-sqlite 사용 시 네이티브 기능은, @capgo/capacitor-file @capgo/capacitor-file의 구현 상세 내용은, @capgo/capacitor-file 사용하기 @capgo/capacitor-file 사용 시의 native capability를 위해, @capgo/capacitor-uploader @capgo/capacitor-uploader.의 구현 상세 내용은