[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CBS28MP2EKW2R6XF9CSG8J":3,"skill-same:01M6CBS28MP2EKW2R6XF9CSG8J":44},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":21,"frontMatter":22,"refName":23,"commitSha":24,"skillDirectory":25,"forksCount":13,"repositoryHtmlUrl":26,"repositoryHomepageUrl":6,"ownerHtmlUrl":27,"ownerBlogUrl":6,"ownerDescription":28,"updatedAt":29,"install":30,"resources":43},"01M6CBS28MP2EKW2R6XF9CSG8J","22-bundle-upload",null,"devwinsoft\u002Fdevian","devwinsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F22570160?v=4","01M6C6RR8W05NH8NPKQNCE3Q72","maoshy","01M6C6RRDJ5TJJCZXRTY801YDH",0,"MIT","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian-unity\u002F50-mobile-package\u002F70-build-operation\u002F22-bundle-upload\u002FSKILL.md",[],[18],"22 bundle upload","2026-11-10 10:12:30",false,"# 22-bundle-upload — Addressable Bundle Upload\n\nStatus: ACTIVE\nAppliesTo: v1\nType: Component Specification\n\n## 목적\n\nAddressables에서 Remote으로 설정된 group의 빌드 산출물만 release repo에 복사하고 git commit한다.\nGitHub Pages로 서빙하여 앱이 런타임에 번들을 다운로드할 수 있게 한다.\n\n## 파일 위치 (SSOT)\n\n- `com.devian.foundation\u002FSamples~\u002FMobilePackage\u002FEditor\u002FBuildAutomation\u002FBundleUploadRunner.cs`\n\n## Public API\n\n```csharp\nnamespace Devian\n{\n    public static class BundleUploadRunner\n    {\n        \u002F\u002F\u002F \u003Csummary>\n        \u002F\u002F\u002F Remote group의 빌드 산출물을 release repo에 복사하고 git commit한다.\n        \u002F\u002F\u002F \u003C\u002Fsummary>\n        \u002F\u002F\u002F \u003Cparam name=\"settings\">BuildAutomationSettings\u003C\u002Fparam>\n        \u002F\u002F\u002F \u003Cparam name=\"ct\">취소 토큰\u003C\u002Fparam>\n        \u002F\u002F\u002F \u003Creturns>성공 시 true\u003C\u002Freturns>\n        public static async Task\u003Cbool> Run(\n            BuildAutomationSettings settings,\n            CancellationToken ct = default);\n    }\n}\n```\n\n## 실행 흐름\n\n```\n1. AddressableAssetSettingsDefaultObject.Settings 로드\n2. Remote group 필터링\n   - groups 순회 → IsRemoteGroup()으로 Remote group 탐색\n3. 빌드 산출물 경로 결정\n   - Remote group의 BundledAssetGroupSchema.BuildPath.GetValue(aaSettings)로 실제 빌드 경로 resolve\n   - 해당 경로의 모든 파일 수집 (번들 + catalog + hash)\n4. release repo 대상 경로에 복사\n   - 대상: {releaseRepoRoot}\u002Fv{bundleVersion}\u002F{BuildTarget}\u002F\n   - 기존 파일 정리 후 복사 (clean copy)\n5. GitRunner.Commit()으로 git add → commit\n   - workingDirectory: releaseRepoRoot\n   - commitMessage: \"chore: update addressable bundles ({BuildTarget})\"\n   - filePaths: v{bundleVersion}\u002F{BuildTarget} 하위 전체\n   - nothing to commit (exit code 1 + \"nothing to commit\") → 경고 로그만 출력, 성공 반환\n6. push는 사용자가 직접 수행\n```\n\n## Remote Group 판별\n\n```csharp\nprivate static bool IsRemoteGroup(AddressableAssetGroup group,\n    AddressableAssetSettings aaSettings)\n{\n    var schema = group.GetSchema\u003CBundledAssetGroupSchema>();\n    if (schema == null) return false;\n\n    \u002F\u002F LoadPath가 http\u002Fhttps로 시작하면 Remote group\n    var loadPath = schema.LoadPath.GetValue(aaSettings);\n    return !string.IsNullOrEmpty(loadPath)\n        && loadPath.StartsWith(\"http\");\n}\n```\n\n## 복사 대상 파일\n\n| 파일 유형 | 설명 |\n|-----------|------|\n| `*.bundle` | 번들 파일 (Remote group에 속한 것만) |\n| `catalog_*.json` | 카탈로그 파일 |\n| `catalog_*.hash` | 카탈로그 해시 (버전 체크용) |\n\n## 대상 경로 구조\n\n```\n{releaseRepoRoot}\u002F\n├── v0.2.0\u002F\n│   └── Android\u002F\n│       ├── string-pb64_xxx.bundle\n│       └── string-ndjson_xxx.bundle\n├── v0.2.1\u002F\n│   └── Android\u002F\n│       └── ...\n├── version_aos.json\n└── version_ios.json\n```\n\n### 경로 매칭 규칙 (SSOT: remoteCdnUrl)\n\n앱 런타임 요청 URL과 release repo 파일 경로가 일치해야 한다:\n\n```\nRemote.LoadPath:  {remoteCdnUrl}\u002Fv{bundleVersion}\u002F[BuildTarget]\u002F{bundle}\nUpload dest:      {releaseRepoRoot}\u002Fv{bundleVersion}\u002F{BuildTarget}\u002F{bundle}\n```\n\n`remoteCdnUrl`은 `BuildAutomationSettings.remoteCdnUrl`에서 관리한다.\n\n## EditorWindow 연동\n\n### Release 탭 (수동 실행)\n\n```\n── Bundle Upload ──\nRelease Repo Root  ..\u002Frelease-repo\nRemote Groups: {N}개 감지\n[⬆  Upload Bundles to Git]\n```\n\n- Remote group 수는 `OnEditorUpdate`에서 갱신\n- 버튼 클릭 → `BundleUploadRunner.Run(settings, ct)`\n\n### Pipeline 탭 (자동 실행)\n\n- Pipeline > Build 실행 시 `_buildAddressables`가 true이면:\n  1. `BundleBuildRunner.Run()` — 번들 빌드\n  2. 성공 시 `BundleUploadRunner.Run()` — 자동으로 번들 복사 + git commit\n  3. Bundle Copy 실패 시 경고 로그만 출력, 앱 빌드는 계속 진행\n\n## Hard Rules\n\n- Remote group의 산출물만 복사한다. Local group은 제외.\n- 복사 전 대상 디렉토리를 clean한다 (이전 빌드 잔여물 방지).\n- push 금지 — commit만 수행. 사용자가 직접 push.\n- `AddressableAssetSettingsDefaultObject.Settings`가 null이면 에러 로그 후 중단.\n- `releaseRepoRoot`가 비어있으면 프로젝트 루트 사용 (`ResolveReleaseRoot()` 재활용).\n- 로그는 `BuildAutomationLogger` 경유.\n\n## Implementation Location (3-path mirror)\n\n| 경로 | 역할 |\n|------|------|\n| `framework-cs\u002Fupm\u002Fcom.devian.foundation\u002FSamples~\u002FMobilePackage\u002FEditor\u002FBuildAutomation\u002FBundleUploadRunner.cs` | UPM (정본) |\n| `framework-cs\u002Fapps\u002FUnityExample\u002FPackages\u002Fcom.devian.foundation\u002FSamples~\u002FMobilePackage\u002FEditor\u002FBuildAutomation\u002FBundleUploadRunner.cs` | Packages (sync) |\n| `Assets\u002FSamples\u002FDevian Foundation\u002F{version}\u002FMobilePackage\u002FEditor\u002FBuildAutomation\u002FBundleUploadRunner.cs` | Assets\u002FSamples (import) |\n\n## Reference\n\n- Parent: `skills\u002Fdevian-unity\u002F50-mobile-package\u002F70-build-operation\u002F00-overview\u002FSKILL.md`\n- Settings: `skills\u002Fdevian-unity\u002F50-mobile-package\u002F70-build-operation\u002F10-settings\u002FSKILL.md`\n- GitRunner: `skills\u002Fdevian-unity\u002F50-mobile-package\u002F70-build-operation\u002F41-git-runner\u002FSKILL.md`\n- EditorWindow: `skills\u002Fdevian-unity\u002F50-mobile-package\u002F70-build-operation\u002F50-editor-window\u002FSKILL.md`\n- Install (Remote Profile): `skills\u002Fdevian-unity\u002F50-mobile-package\u002F70-build-operation\u002F02-install\u002FSKILL.md`\n",{},"main","8cb5bd877a3a7004867d2f55697cb9e4ff2f1734","skills\u002Fdevian-unity\u002F50-mobile-package\u002F70-build-operation\u002F22-bundle-upload","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian","https:\u002F\u002Fgithub.com\u002Fdevwinsoft","Kim Hyong Joon","2026-04-12 18:44:10",{"source":7,"skillName":5,"commands":31,"prompt":41,"zipAvailable":42},[32,35,38],{"tool":33,"command":34},"npx","npx skills add devwinsoft\u002Fdevian --skill 22-bundle-upload",{"tool":36,"command":37},"bunx","bunx skills add devwinsoft\u002Fdevian --skill 22-bundle-upload",{"tool":39,"command":40},"pnpm","pnpm dlx skills add devwinsoft\u002Fdevian --skill 22-bundle-upload","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian-unity\u002F50-mobile-package\u002F70-build-operation\u002F22-bundle-upload\u002FSKILL.md",true,[],[45,51,59,67,76,83],{"skillNo":46,"name":47,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":48,"categories":49,"keywords":50,"publishedAt":19},"01M6CBX3K2JJV38ANHVGCAT8A0","devian","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002FSKILL.md",[],[47],{"skillNo":52,"name":53,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":54,"categories":55,"keywords":56,"publishedAt":19},"01M6CBX210BFMM8AMN770J9WY8","90-project-archive","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F90-project-archive\u002FSKILL.md",[],[57,58],"archive","90 project archive",{"skillNo":60,"name":61,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":62,"categories":63,"keywords":64,"publishedAt":19},"01M6CBWZSGRR9R3YJZB3177667","30-env-management","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F30-env-management\u002FSKILL.md",[],[65,66],"management","30 env management",{"skillNo":68,"name":69,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":72,"publishedAt":19},"01M6CBWYD3TGWBQV7V35551THF","19-page-push-send","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F19-page-push-send\u002FSKILL.md",[],[73,74,75],"push","page","19 page push send",{"skillNo":77,"name":78,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":81,"publishedAt":19},"01M6CBWWRQ05A7604GHJKTPJTF","18-page-savedata","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F18-page-savedata\u002FSKILL.md",[],[74,82],"18 page savedata",{"skillNo":84,"name":85,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":88,"publishedAt":19},"01M6CBWVH4S2VF1V7B2GBTESCT","17-page-deobfuscate","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F17-page-deobfuscate\u002FSKILL.md",[],[74,89],"17 page deobfuscate"]