Compare commits
11 Commits
main
...
create-lib
| Author | SHA1 | Date | |
|---|---|---|---|
| bed4cc0121 | |||
| e2d31dc053 | |||
| 518826b908 | |||
| 014402d3cc | |||
| 815f865060 | |||
| d1f98c92dd | |||
| 51c18cd986 | |||
| 44ae8b436a | |||
| 092299ff33 | |||
| 631046df34 | |||
| 7e2c013cdc |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,10 +1,12 @@
|
|||||||
# Local configuration files (contain secrets - do NOT commit)
|
# Local configuration files (contain secrets - do NOT commit)
|
||||||
# Developers should copy config.example to config and add their test credentials
|
# Developers should copy config.example to config and add their test credentials
|
||||||
config
|
config.json
|
||||||
*.config
|
|
||||||
!config.example
|
!config.example
|
||||||
!*.config.example
|
!*.config.example
|
||||||
|
|
||||||
|
# Gitea API documentation (downloaded for reference, not part of source)
|
||||||
|
gitea-swagger.v1.json
|
||||||
|
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
build/
|
build/
|
||||||
*.o
|
*.o
|
||||||
@@ -68,3 +70,5 @@ third-party/build/
|
|||||||
*.tmp
|
*.tmp
|
||||||
*.bak
|
*.bak
|
||||||
*.cache
|
*.cache
|
||||||
|
|
||||||
|
test-builds
|
||||||
1192
.plans/CreateGiteaIssues.md
Normal file
1192
.plans/CreateGiteaIssues.md
Normal file
File diff suppressed because it is too large
Load Diff
749
.plans/CreateLibNextcloud.md
Normal file
749
.plans/CreateLibNextcloud.md
Normal file
@@ -0,0 +1,749 @@
|
|||||||
|
# libnextcloud Shared Library Plan
|
||||||
|
|
||||||
|
**Created**: 2026-01-27
|
||||||
|
**Status**: 📋 Planning
|
||||||
|
**Target**: Cross-platform C++17 shared library for Nextcloud connectivity
|
||||||
|
**Branch**: `create-libnextcloud`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Create `libnextcloud` - a cross-platform C++17 shared library that provides Nextcloud connectivity for embedded devices and homebrew platforms. The library will handle all Nextcloud communication, authentication, file operations, and settings management through a clean, platform-agnostic API.
|
||||||
|
|
||||||
|
**Design Philosophy**:
|
||||||
|
- **Platform-agnostic**: Works on 3DS, Switch, Wii U, Vita, PC, etc.
|
||||||
|
- **Minimal dependencies**: Only libcurl, mbedTLS, and tinyxml2
|
||||||
|
- **Header-only where possible**: nlohmann/json for configuration
|
||||||
|
- **Modern C++17**: Using standard library features
|
||||||
|
- **Testable**: Mock-friendly design with dependency injection
|
||||||
|
- **Documented**: Doxygen API documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
shared/
|
||||||
|
├── include/
|
||||||
|
│ └── nextcloud/
|
||||||
|
│ ├── client.hpp # Main client interface
|
||||||
|
│ ├── webdav_client.hpp # WebDAV protocol implementation
|
||||||
|
│ ├── auth.hpp # Authentication manager
|
||||||
|
│ ├── upload_manager.hpp # File upload with chunking
|
||||||
|
│ ├── folder_manager.hpp # Remote folder operations
|
||||||
|
│ ├── config.hpp # Configuration management
|
||||||
|
│ ├── types.hpp # Common types and enums
|
||||||
|
│ └── version.hpp # Library version info
|
||||||
|
├── src/
|
||||||
|
│ ├── webdav_client.cpp
|
||||||
|
│ ├── auth.cpp
|
||||||
|
│ ├── upload_manager.cpp
|
||||||
|
│ ├── folder_manager.cpp
|
||||||
|
│ └── config.cpp
|
||||||
|
├── tests/
|
||||||
|
│ ├── webdav_client_test.cpp
|
||||||
|
│ ├── auth_test.cpp
|
||||||
|
│ ├── upload_manager_test.cpp
|
||||||
|
│ ├── folder_manager_test.cpp
|
||||||
|
│ ├── config_test.cpp
|
||||||
|
│ └── mocks/
|
||||||
|
│ ├── mock_http_client.hpp
|
||||||
|
│ └── mock_xml_parser.hpp
|
||||||
|
├── examples/
|
||||||
|
│ ├── simple_upload.cpp
|
||||||
|
│ ├── folder_listing.cpp
|
||||||
|
│ └── authentication.cpp
|
||||||
|
├── docs/
|
||||||
|
│ ├── api.md # API documentation
|
||||||
|
│ ├── architecture.md # Design overview
|
||||||
|
│ └── Doxyfile # Doxygen configuration
|
||||||
|
├── CMakeLists.txt
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
### Required
|
||||||
|
- **libcurl** (7.68+): HTTP/HTTPS client
|
||||||
|
- **mbedTLS** (2.16+): SSL/TLS support (works on embedded)
|
||||||
|
- **tinyxml2** (9.0+): Lightweight XML parsing
|
||||||
|
|
||||||
|
### Header-Only
|
||||||
|
- **nlohmann/json** (3.10+): JSON parsing/serialization
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- **Google Test** (1.11+): Unit testing framework
|
||||||
|
- **Google Mock** (1.11+): Mocking framework
|
||||||
|
|
||||||
|
### Build Tools
|
||||||
|
- **CMake** (3.15+): Build system
|
||||||
|
- **C++17 compiler**: GCC 7+, Clang 5+, MSVC 2017+
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
## Phase 1: Project Scaffolding (Issue #??)
|
||||||
|
|
||||||
|
**Goal**: Set up the library project structure and build system.
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
- [ ] Create `shared/` directory structure
|
||||||
|
- [ ] Set up CMakeLists.txt with:
|
||||||
|
- Library target (static/shared)
|
||||||
|
- C++17 standard
|
||||||
|
- Include directories
|
||||||
|
- Dependency linking (curl, mbedtls, tinyxml2)
|
||||||
|
- Install rules
|
||||||
|
- Export config for downstream projects
|
||||||
|
- [ ] Create version.hpp with semantic versioning
|
||||||
|
- [ ] Create types.hpp with common enums and structures:
|
||||||
|
- `NextcloudError` enum
|
||||||
|
- `UploadProgress` struct
|
||||||
|
- `FileInfo` struct
|
||||||
|
- `FolderInfo` struct
|
||||||
|
- [ ] Set up Google Test framework
|
||||||
|
- [ ] Create basic README for the library
|
||||||
|
- [ ] Add pkg-config file generation
|
||||||
|
- [ ] Verify library compiles as standalone project
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
- [ ] CMake builds library successfully
|
||||||
|
- [ ] Can link against library from test project
|
||||||
|
- [ ] Version info accessible at runtime
|
||||||
|
- [ ] Ready for component implementation
|
||||||
|
|
||||||
|
**Estimated Time**: 4-6 hours
|
||||||
|
**Priority**: Critical (blocks all other work)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: WebDAV Client (Issue #8)
|
||||||
|
|
||||||
|
**Goal**: Implement HTTP/WebDAV communication layer.
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
- [ ] Create `webdav_client.hpp` interface
|
||||||
|
- [ ] Implement HTTP wrapper around libcurl:
|
||||||
|
- GET, PUT, POST, DELETE, custom methods
|
||||||
|
- Request/response headers
|
||||||
|
- Request body (string, stream)
|
||||||
|
- Response body handling
|
||||||
|
- Timeout configuration
|
||||||
|
- User-Agent setting
|
||||||
|
- [ ] Add SSL/TLS support with mbedTLS
|
||||||
|
- [ ] Implement WebDAV methods:
|
||||||
|
- PROPFIND (list files/folders)
|
||||||
|
- MKCOL (create folder)
|
||||||
|
- PUT (upload file)
|
||||||
|
- DELETE (remove file/folder)
|
||||||
|
- MOVE (rename/move)
|
||||||
|
- COPY (duplicate)
|
||||||
|
- [ ] Parse WebDAV XML responses (using tinyxml2):
|
||||||
|
- Multi-status responses
|
||||||
|
- File properties (size, mtime, type)
|
||||||
|
- Error responses
|
||||||
|
- [ ] Implement error handling:
|
||||||
|
- HTTP status codes
|
||||||
|
- Network errors
|
||||||
|
- XML parsing errors
|
||||||
|
- Error code mapping
|
||||||
|
- [ ] Add timeout and retry logic
|
||||||
|
- [ ] Write unit tests with mocked HTTP
|
||||||
|
- [ ] Document API in Doxygen format
|
||||||
|
|
||||||
|
### API Design
|
||||||
|
```cpp
|
||||||
|
class WebDAVClient {
|
||||||
|
public:
|
||||||
|
WebDAVClient(const std::string& baseUrl);
|
||||||
|
|
||||||
|
// Core HTTP
|
||||||
|
Response get(const std::string& path);
|
||||||
|
Response put(const std::string& path, const std::vector<uint8_t>& data);
|
||||||
|
Response delete_(const std::string& path);
|
||||||
|
|
||||||
|
// WebDAV operations
|
||||||
|
std::vector<FileInfo> propfind(const std::string& path, int depth = 1);
|
||||||
|
bool mkcol(const std::string& path);
|
||||||
|
bool move(const std::string& from, const std::string& to);
|
||||||
|
bool copy(const std::string& from, const std::string& to);
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
void setTimeout(int seconds);
|
||||||
|
void setAuthHeader(const std::string& header);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
- [ ] All WebDAV methods implemented
|
||||||
|
- [ ] SSL/TLS connections working
|
||||||
|
- [ ] XML responses correctly parsed
|
||||||
|
- [ ] Error handling comprehensive
|
||||||
|
- [ ] Unit tests with >80% coverage
|
||||||
|
- [ ] API documented
|
||||||
|
- [ ] Works with test Nextcloud servers
|
||||||
|
|
||||||
|
**Estimated Time**: 12-16 hours
|
||||||
|
**Priority**: Critical (foundation for all features)
|
||||||
|
**Dependencies**: Phase 1 complete
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: Authentication System (Issue #9)
|
||||||
|
|
||||||
|
**Goal**: Handle authentication with Nextcloud servers.
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
- [ ] Create `auth.hpp` interface
|
||||||
|
- [ ] Implement HTTP Basic Authentication:
|
||||||
|
- Base64 encoding
|
||||||
|
- Authorization header generation
|
||||||
|
- Credential storage
|
||||||
|
- [ ] Add authentication verification:
|
||||||
|
- Test connection to server
|
||||||
|
- Validate credentials
|
||||||
|
- Return meaningful error codes
|
||||||
|
- [ ] Secure credential handling:
|
||||||
|
- In-memory only (no disk storage by default)
|
||||||
|
- Platform-specific secure storage hooks
|
||||||
|
- [ ] Session management:
|
||||||
|
- Keep-alive connections
|
||||||
|
- Connection pooling
|
||||||
|
- Timeout handling
|
||||||
|
- [ ] Future: OAuth2 support (stubbed for now)
|
||||||
|
- [ ] Write unit tests for auth flows
|
||||||
|
- [ ] Document security considerations
|
||||||
|
|
||||||
|
### API Design
|
||||||
|
```cpp
|
||||||
|
class AuthManager {
|
||||||
|
public:
|
||||||
|
// Basic Auth
|
||||||
|
void setBasicAuth(const std::string& username, const std::string& password);
|
||||||
|
std::string getAuthHeader() const;
|
||||||
|
|
||||||
|
// Verification
|
||||||
|
bool verifyConnection(WebDAVClient& client);
|
||||||
|
|
||||||
|
// Session management
|
||||||
|
void clearCredentials();
|
||||||
|
bool hasCredentials() const;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
- [ ] Basic auth working with real Nextcloud
|
||||||
|
- [ ] Credentials never logged or leaked
|
||||||
|
- [ ] Connection verification works
|
||||||
|
- [ ] Error messages helpful
|
||||||
|
- [ ] Unit tests cover edge cases
|
||||||
|
- [ ] API documented
|
||||||
|
|
||||||
|
**Estimated Time**: 6-8 hours
|
||||||
|
**Priority**: High
|
||||||
|
**Dependencies**: Phase 2 complete
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Upload Manager (Issue #10)
|
||||||
|
|
||||||
|
**Goal**: Implement file upload with chunking and progress tracking.
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
- [ ] Create `upload_manager.hpp` interface
|
||||||
|
- [ ] Implement streaming upload:
|
||||||
|
- Read file in chunks
|
||||||
|
- Send via PUT requests
|
||||||
|
- Handle large files (>1GB)
|
||||||
|
- [ ] Add chunking support:
|
||||||
|
- Configurable chunk size (default: 10MB)
|
||||||
|
- Resume capability (future)
|
||||||
|
- Parallel chunks (future)
|
||||||
|
- [ ] Progress tracking:
|
||||||
|
- Callback interface
|
||||||
|
- Bytes uploaded / total
|
||||||
|
- Transfer speed
|
||||||
|
- Time remaining estimate
|
||||||
|
- [ ] Error handling and retry:
|
||||||
|
- Network failures
|
||||||
|
- Server errors (503, 507)
|
||||||
|
- Automatic retry with backoff
|
||||||
|
- Partial upload cleanup
|
||||||
|
- [ ] Path handling:
|
||||||
|
- URL encoding
|
||||||
|
- Special characters
|
||||||
|
- Nested folders
|
||||||
|
- [ ] Write unit tests with mock uploads
|
||||||
|
- [ ] Document upload best practices
|
||||||
|
|
||||||
|
### API Design
|
||||||
|
```cpp
|
||||||
|
class UploadManager {
|
||||||
|
public:
|
||||||
|
using ProgressCallback = std::function<void(const UploadProgress&)>;
|
||||||
|
|
||||||
|
UploadManager(WebDAVClient& client, AuthManager& auth);
|
||||||
|
|
||||||
|
// Upload operations
|
||||||
|
bool uploadFile(
|
||||||
|
const std::string& localPath,
|
||||||
|
const std::string& remotePath,
|
||||||
|
ProgressCallback callback = nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
void setChunkSize(size_t bytes);
|
||||||
|
void setRetryCount(int count);
|
||||||
|
void setTimeout(int seconds);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UploadProgress {
|
||||||
|
std::string filename;
|
||||||
|
size_t bytesUploaded;
|
||||||
|
size_t totalBytes;
|
||||||
|
float percentComplete;
|
||||||
|
float bytesPerSecond;
|
||||||
|
int secondsRemaining;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
- [ ] Uploads files successfully
|
||||||
|
- [ ] Chunking works for large files
|
||||||
|
- [ ] Progress callback accurate
|
||||||
|
- [ ] Handles network failures gracefully
|
||||||
|
- [ ] Retries work correctly
|
||||||
|
- [ ] Unit tests with various file sizes
|
||||||
|
- [ ] API documented
|
||||||
|
|
||||||
|
**Estimated Time**: 10-12 hours
|
||||||
|
**Priority**: High
|
||||||
|
**Dependencies**: Phases 2, 3 complete
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: Folder Management (Issue #11)
|
||||||
|
|
||||||
|
**Goal**: Remote folder operations and navigation.
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
- [ ] Create `folder_manager.hpp` interface
|
||||||
|
- [ ] Implement folder listing:
|
||||||
|
- List contents of remote folder
|
||||||
|
- Parse file metadata (size, mtime, type)
|
||||||
|
- Recursive listing support
|
||||||
|
- Filtering (files only, folders only)
|
||||||
|
- [ ] Folder creation:
|
||||||
|
- Create single folder
|
||||||
|
- Create nested folders (mkdir -p)
|
||||||
|
- Validate folder names
|
||||||
|
- [ ] Folder navigation:
|
||||||
|
- Path normalization
|
||||||
|
- Parent folder detection
|
||||||
|
- Breadcrumb generation
|
||||||
|
- [ ] Caching (optional):
|
||||||
|
- Cache folder contents
|
||||||
|
- TTL-based invalidation
|
||||||
|
- Memory limits
|
||||||
|
- [ ] Write unit tests
|
||||||
|
- [ ] Document folder operations
|
||||||
|
|
||||||
|
### API Design
|
||||||
|
```cpp
|
||||||
|
class FolderManager {
|
||||||
|
public:
|
||||||
|
FolderManager(WebDAVClient& client, AuthManager& auth);
|
||||||
|
|
||||||
|
// Folder operations
|
||||||
|
std::vector<FileInfo> listFolder(const std::string& path);
|
||||||
|
bool createFolder(const std::string& path);
|
||||||
|
bool createFolders(const std::string& path); // mkdir -p
|
||||||
|
bool deleteFolder(const std::string& path);
|
||||||
|
bool folderExists(const std::string& path);
|
||||||
|
|
||||||
|
// Navigation helpers
|
||||||
|
std::string getParentFolder(const std::string& path);
|
||||||
|
std::vector<std::string> getBreadcrumbs(const std::string& path);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileInfo {
|
||||||
|
std::string name;
|
||||||
|
std::string path;
|
||||||
|
bool isDirectory;
|
||||||
|
size_t size;
|
||||||
|
time_t modifiedTime;
|
||||||
|
std::string contentType;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
- [ ] Lists folders correctly
|
||||||
|
- [ ] Creates folders successfully
|
||||||
|
- [ ] Handles nested paths
|
||||||
|
- [ ] Metadata parsing accurate
|
||||||
|
- [ ] Unit tests comprehensive
|
||||||
|
- [ ] API documented
|
||||||
|
|
||||||
|
**Estimated Time**: 8-10 hours
|
||||||
|
**Priority**: Medium
|
||||||
|
**Dependencies**: Phases 2, 3 complete
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: Favorites and Recent Folders (Issue #12)
|
||||||
|
|
||||||
|
**Goal**: Persistence layer for user preferences.
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
- [ ] Create `config.hpp` interface
|
||||||
|
- [ ] Implement favorites management:
|
||||||
|
- Add favorite folder
|
||||||
|
- Remove favorite folder
|
||||||
|
- List favorites
|
||||||
|
- Reorder favorites
|
||||||
|
- [ ] Implement recent folders tracking:
|
||||||
|
- Auto-track used folders
|
||||||
|
- Configurable limit (default: 5)
|
||||||
|
- LRU eviction
|
||||||
|
- Duplicate detection
|
||||||
|
- [ ] JSON persistence:
|
||||||
|
- Save to file
|
||||||
|
- Load from file
|
||||||
|
- Handle missing/corrupt files
|
||||||
|
- Migration support
|
||||||
|
- [ ] Platform-specific paths:
|
||||||
|
- Linux: ~/.config/nextcloud-share/
|
||||||
|
- 3DS: /nextcloud-share/
|
||||||
|
- Switch: /switch/nextcloud-share/
|
||||||
|
- [ ] Write unit tests
|
||||||
|
- [ ] Document config format
|
||||||
|
|
||||||
|
### API Design
|
||||||
|
```cpp
|
||||||
|
class Config {
|
||||||
|
public:
|
||||||
|
Config(const std::string& configPath = "");
|
||||||
|
|
||||||
|
// Favorites
|
||||||
|
void addFavorite(const std::string& path, const std::string& name = "");
|
||||||
|
void removeFavorite(const std::string& path);
|
||||||
|
std::vector<FolderInfo> getFavorites() const;
|
||||||
|
|
||||||
|
// Recent folders
|
||||||
|
void addRecentFolder(const std::string& path);
|
||||||
|
std::vector<std::string> getRecentFolders() const;
|
||||||
|
void setMaxRecentFolders(int count);
|
||||||
|
|
||||||
|
// Persistence
|
||||||
|
bool load();
|
||||||
|
bool save();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FolderInfo {
|
||||||
|
std::string path;
|
||||||
|
std::string displayName;
|
||||||
|
time_t lastUsed;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
- [ ] Favorites persist across restarts
|
||||||
|
- [ ] Recent folders tracked correctly
|
||||||
|
- [ ] JSON format clean and readable
|
||||||
|
- [ ] Handles missing config gracefully
|
||||||
|
- [ ] Unit tests cover edge cases
|
||||||
|
- [ ] API documented
|
||||||
|
|
||||||
|
**Estimated Time**: 6-8 hours
|
||||||
|
**Priority**: Low
|
||||||
|
**Dependencies**: None (standalone)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 7: Testing and Documentation (Issue #13)
|
||||||
|
|
||||||
|
**Goal**: Comprehensive test coverage and documentation.
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
- [ ] Achieve >80% code coverage
|
||||||
|
- [ ] Integration tests with real Nextcloud:
|
||||||
|
- Test against cloud.tomusan.com
|
||||||
|
- Test against disobedient.cloud
|
||||||
|
- Verify all operations work
|
||||||
|
- [ ] Mock server for offline testing:
|
||||||
|
- Mock HTTP responses
|
||||||
|
- Simulate errors
|
||||||
|
- Test edge cases
|
||||||
|
- [ ] Performance testing:
|
||||||
|
- Large file uploads
|
||||||
|
- Many small files
|
||||||
|
- Concurrent operations
|
||||||
|
- [ ] Generate Doxygen documentation
|
||||||
|
- [ ] Write architecture document
|
||||||
|
- [ ] Create usage examples
|
||||||
|
- [ ] Document build process
|
||||||
|
- [ ] Add API reference
|
||||||
|
|
||||||
|
### Documentation Structure
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── api.md # Full API reference
|
||||||
|
├── architecture.md # Library design and internals
|
||||||
|
├── building.md # Build instructions
|
||||||
|
├── examples.md # Usage examples
|
||||||
|
├── testing.md # How to run tests
|
||||||
|
├── contributing.md # Contribution guidelines
|
||||||
|
└── changelog.md # Version history
|
||||||
|
```
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
- [ ] All classes have Doxygen comments
|
||||||
|
- [ ] Code coverage >80%
|
||||||
|
- [ ] Integration tests pass
|
||||||
|
- [ ] Documentation complete
|
||||||
|
- [ ] Examples compile and run
|
||||||
|
- [ ] Ready for platform integration
|
||||||
|
|
||||||
|
**Estimated Time**: 10-12 hours
|
||||||
|
**Priority**: High
|
||||||
|
**Dependencies**: All phases complete
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Issues to Create
|
||||||
|
|
||||||
|
Based on this plan, create the following Gitea issues:
|
||||||
|
|
||||||
|
### Issue: Initialize libnextcloud Project Structure
|
||||||
|
**Labels**: library, setup, priority:critical
|
||||||
|
**Description**:
|
||||||
|
```
|
||||||
|
Set up the shared library project structure and build system for libnextcloud.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
- [ ] Create `shared/` directory structure
|
||||||
|
- [ ] Set up CMakeLists.txt for library
|
||||||
|
- [ ] Create version.hpp and types.hpp
|
||||||
|
- [ ] Set up Google Test framework
|
||||||
|
- [ ] Create basic README
|
||||||
|
- [ ] Add pkg-config file generation
|
||||||
|
- [ ] Verify library compiles standalone
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- libcurl 7.68+
|
||||||
|
- mbedTLS 2.16+
|
||||||
|
- tinyxml2 9.0+
|
||||||
|
- Google Test 1.11+
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- [ ] CMake builds library successfully
|
||||||
|
- [ ] Can link against library from test project
|
||||||
|
- [ ] Version info accessible at runtime
|
||||||
|
- [ ] Ready for component implementation
|
||||||
|
|
||||||
|
Estimated: 4-6 hours
|
||||||
|
Blocks: #8, #9, #10, #11, #12, #13
|
||||||
|
```
|
||||||
|
|
||||||
|
### Keep Existing Issues (Update Dependencies)
|
||||||
|
|
||||||
|
**Issue #8**: Design and implement WebDAV client
|
||||||
|
- Add dependency: "Requires libnextcloud scaffolding (new issue)"
|
||||||
|
- Priority: High
|
||||||
|
- Estimated: 12-16 hours
|
||||||
|
|
||||||
|
**Issue #9**: Implement authentication system
|
||||||
|
- Add dependency: "Requires #8"
|
||||||
|
- Priority: High
|
||||||
|
- Estimated: 6-8 hours
|
||||||
|
|
||||||
|
**Issue #10**: Implement file upload functionality
|
||||||
|
- Add dependency: "Requires #8, #9"
|
||||||
|
- Priority: High
|
||||||
|
- Estimated: 10-12 hours
|
||||||
|
|
||||||
|
**Issue #11**: Implement folder management
|
||||||
|
- Add dependency: "Requires #8, #9"
|
||||||
|
- Priority: Medium
|
||||||
|
- Estimated: 8-10 hours
|
||||||
|
|
||||||
|
**Issue #12**: Implement favorites and recent folders
|
||||||
|
- Priority: Low
|
||||||
|
- Estimated: 6-8 hours
|
||||||
|
|
||||||
|
**Issue #13**: Write unit tests for shared library
|
||||||
|
- Add dependency: "Requires #8-#12"
|
||||||
|
- Priority: High
|
||||||
|
- Estimated: 10-12 hours
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build System Design
|
||||||
|
|
||||||
|
### CMakeLists.txt Structure
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
project(libnextcloud VERSION 0.1.0 LANGUAGES CXX)
|
||||||
|
|
||||||
|
# C++17 required
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Library sources
|
||||||
|
add_library(nextcloud
|
||||||
|
src/webdav_client.cpp
|
||||||
|
src/auth.cpp
|
||||||
|
src/upload_manager.cpp
|
||||||
|
src/folder_manager.cpp
|
||||||
|
src/config.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Include directories
|
||||||
|
target_include_directories(nextcloud
|
||||||
|
PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
|
)
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
find_package(CURL REQUIRED)
|
||||||
|
find_package(MbedTLS REQUIRED)
|
||||||
|
find_package(tinyxml2 REQUIRED)
|
||||||
|
|
||||||
|
target_link_libraries(nextcloud
|
||||||
|
PUBLIC
|
||||||
|
CURL::libcurl
|
||||||
|
MbedTLS::mbedtls
|
||||||
|
tinyxml2::tinyxml2
|
||||||
|
)
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Install rules
|
||||||
|
install(TARGETS nextcloud EXPORT nextcloudTargets)
|
||||||
|
install(DIRECTORY include/ DESTINATION include)
|
||||||
|
install(EXPORT nextcloudTargets
|
||||||
|
FILE nextcloudTargets.cmake
|
||||||
|
NAMESPACE nextcloud::
|
||||||
|
DESTINATION lib/cmake/nextcloud
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Platform-Specific Considerations
|
||||||
|
|
||||||
|
**Nintendo 3DS**:
|
||||||
|
- Static library only (no shared objects)
|
||||||
|
- mbedTLS available via dkp-pacman
|
||||||
|
- libcurl with 3DS patches
|
||||||
|
- Filesystem: `/nextcloud-share/config.json`
|
||||||
|
|
||||||
|
**Nintendo Switch**:
|
||||||
|
- Static library
|
||||||
|
- Standard libcurl and mbedTLS
|
||||||
|
- Filesystem: `/switch/nextcloud-share/config.json`
|
||||||
|
|
||||||
|
**Linux/PC**:
|
||||||
|
- Shared library (.so) or static
|
||||||
|
- System libcurl and mbedTLS
|
||||||
|
- Filesystem: `~/.config/nextcloud-share/config.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
- Mock all external dependencies (HTTP, filesystem)
|
||||||
|
- Test each class in isolation
|
||||||
|
- Cover edge cases and error conditions
|
||||||
|
- Fast execution (<1 second total)
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
- Real HTTP requests to test Nextcloud servers
|
||||||
|
- Require valid credentials from config.json
|
||||||
|
- Test full workflows end-to-end
|
||||||
|
- Can be skipped if credentials not available
|
||||||
|
|
||||||
|
### Performance Tests
|
||||||
|
- Upload 1GB file and measure time
|
||||||
|
- Upload 1000 small files
|
||||||
|
- Concurrent uploads
|
||||||
|
- Memory usage profiling
|
||||||
|
|
||||||
|
### Test Configuration
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"test": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"name": "tomusan",
|
||||||
|
"url": "https://cloud.tomusan.com",
|
||||||
|
"username": "test",
|
||||||
|
"password": "xxx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "disobedient",
|
||||||
|
"url": "https://disobedient.cloud/nextcloud",
|
||||||
|
"username": "test",
|
||||||
|
"password": "xxx"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Timeline Estimate
|
||||||
|
|
||||||
|
**Total Estimated Time**: 56-74 hours
|
||||||
|
|
||||||
|
| Phase | Tasks | Estimated Time |
|
||||||
|
|-------|-------|----------------|
|
||||||
|
| 1. Scaffolding | Project setup | 4-6 hours |
|
||||||
|
| 2. WebDAV Client | HTTP/WebDAV implementation | 12-16 hours |
|
||||||
|
| 3. Authentication | Auth system | 6-8 hours |
|
||||||
|
| 4. Upload Manager | File uploads | 10-12 hours |
|
||||||
|
| 5. Folder Management | Folder operations | 8-10 hours |
|
||||||
|
| 6. Favorites/Recent | Config persistence | 6-8 hours |
|
||||||
|
| 7. Testing/Docs | Tests and docs | 10-12 hours |
|
||||||
|
|
||||||
|
**Realistic Timeline**: 2-3 weeks working part-time (15-20 hrs/week)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
The library is complete when:
|
||||||
|
|
||||||
|
- ✅ All phases implemented and tested
|
||||||
|
- ✅ Code coverage >80%
|
||||||
|
- ✅ Integration tests pass with real Nextcloud
|
||||||
|
- ✅ API documentation complete
|
||||||
|
- ✅ Examples compile and run
|
||||||
|
- ✅ Can be integrated into 3DS app
|
||||||
|
- ✅ Ready for additional platforms (Switch, Wii U, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Review this plan** - Adjust scope, estimates, priorities
|
||||||
|
2. **Create Gitea issues** - One for scaffolding, update existing issues
|
||||||
|
3. **Start Phase 1** - Set up project structure
|
||||||
|
4. **Implement phases sequentially** - Each phase depends on previous
|
||||||
|
5. **Test continuously** - Don't defer testing to the end
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Ready to proceed?** Let's create the issues and start building!
|
||||||
1153
.plans/DockerForDevkitARM.md
Normal file
1153
.plans/DockerForDevkitARM.md
Normal file
File diff suppressed because it is too large
Load Diff
362
.plans/DockerForVitaSDK.md
Normal file
362
.plans/DockerForVitaSDK.md
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
# Docker Build Environment Plan for VitaSDK (PS Vita)
|
||||||
|
|
||||||
|
**Status**: Planning / Research
|
||||||
|
**Last Updated**: 2026-01-27
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
PS Vita homebrew development uses **VitaSDK**, a completely separate toolchain from devkitPro. This requires a different container approach but follows similar principles: extend official base images and install all available packages for a complete development environment.
|
||||||
|
|
||||||
|
**Key Differences from devkitPro**:
|
||||||
|
- Separate ecosystem with its own toolchain (vita-toolchain)
|
||||||
|
- Different package manager: `vdpm` (shell-based) instead of `dkp-pacman`
|
||||||
|
- Different build system: CMake with Vita-specific macros
|
||||||
|
- Official Docker images: `vitasdk/vitasdk` (not on devkitPro organization)
|
||||||
|
- Output formats: SELF (executable), VPK (installable package archive)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PS Vita Platform Capabilities
|
||||||
|
|
||||||
|
### Hardware Features
|
||||||
|
- **Networking**: ✅ Built-in Wi-Fi (802.11 b/g/n)
|
||||||
|
- Optional 3G modem (discontinued in 2013, limited availability)
|
||||||
|
- Bluetooth 2.1+EDR
|
||||||
|
- **Screenshots**: ✅ Native system-level screenshot capability
|
||||||
|
- **Cameras**: ✅ Dual 0.3MP cameras (front and back)
|
||||||
|
- VGA resolution (640×480 @ 60fps)
|
||||||
|
- Can capture photos and videos
|
||||||
|
- **Display**: 5-inch OLED touchscreen (original) or LCD (slim)
|
||||||
|
- **CPU**: Quad-core ARM Cortex-A9 MPCore
|
||||||
|
- **GPU**: Quad-core PowerVR SGX543MP4+
|
||||||
|
- **RAM**: 512 MB system RAM, 128 MB VRAM
|
||||||
|
|
||||||
|
### Project Fit Assessment
|
||||||
|
✅ **Networking**: Yes - Wi-Fi built-in, suitable for Nextcloud uploads
|
||||||
|
✅ **Screenshots**: Yes - native system capability for game captures
|
||||||
|
✅ **Cameras**: Yes - can take photos with built-in cameras
|
||||||
|
✅ **Homebrew Scene**: Active community with VitaSDK
|
||||||
|
✅ **Container Support**: Official Docker images available
|
||||||
|
|
||||||
|
**Priority**: **HIGH** - Meets all requirements (networking + screenshots + cameras)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VitaSDK Ecosystem
|
||||||
|
|
||||||
|
### Official Resources
|
||||||
|
- **Website**: https://vitasdk.org/
|
||||||
|
- **GitHub Organization**: https://github.com/vitasdk (15 repositories)
|
||||||
|
- **Docker Repository**: https://github.com/vitasdk/docker
|
||||||
|
- **Package Repository**: https://github.com/vitasdk/packages (updated frequently)
|
||||||
|
- **Samples Repository**: https://github.com/vitasdk/samples (335 stars)
|
||||||
|
- **Documentation**: https://docs.vitasdk.org/
|
||||||
|
|
||||||
|
### Toolchain Components
|
||||||
|
- **vita-toolchain**: ARM-based cross-compiler (MIT license)
|
||||||
|
- **newlib**: PS Vita port of C library (GPL-2.0)
|
||||||
|
- **vita-headers**: System headers (233 stars, 94 forks)
|
||||||
|
- **buildscripts**: CMake-based build system (65 stars)
|
||||||
|
- **taihen**: Plugin framework
|
||||||
|
- **libvita2d**: 2D graphics library
|
||||||
|
|
||||||
|
### Package Management
|
||||||
|
- **Tool**: vdpm (Vita SDK Package Manager)
|
||||||
|
- **Type**: Shell-based installer scripts
|
||||||
|
- **Repository**: https://github.com/vitasdk/packages
|
||||||
|
- **Installation**: Bootstrap with `./install-all.sh` from vdpm repo
|
||||||
|
- **Build Format**: vita-makepkg (similar to Arch Linux makepkg)
|
||||||
|
|
||||||
|
### Build System
|
||||||
|
- **Primary**: CMake with Vita-specific macros
|
||||||
|
- `vita_create_self()` - Creates SELF executable
|
||||||
|
- `vita_create_vpk()` - Creates VPK package
|
||||||
|
- **Output Formats**:
|
||||||
|
- SELF: Signed ELF executable format
|
||||||
|
- VPK: Installable package archive (SELF + data + metadata)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Official Docker Images
|
||||||
|
|
||||||
|
### Available on Docker Hub
|
||||||
|
```bash
|
||||||
|
# Search results show multiple official images
|
||||||
|
docker.io/vitasdk/vitasdk # Main official image
|
||||||
|
docker.io/vitasdk/vitasdk-softfp # Soft float variant
|
||||||
|
docker.io/vitasdk/buildscripts # Build tools base image
|
||||||
|
```
|
||||||
|
|
||||||
|
### Base Image Structure
|
||||||
|
From `github.com/vitasdk/docker`:
|
||||||
|
|
||||||
|
**Main Dockerfile**:
|
||||||
|
- Base: `vitasdk/buildscripts:latest`
|
||||||
|
- Installs: git, curl, bash, make, pkgconf, cmake, sudo
|
||||||
|
- Uses multi-stage build to clone and install vdpm
|
||||||
|
- Installs all packages via `vdpm/install-all.sh`
|
||||||
|
- Environment: `VITASDK=/usr/local/vitasdk`
|
||||||
|
|
||||||
|
**Non-root Dockerfile**:
|
||||||
|
- Same base, but creates unprivileged user
|
||||||
|
- Adds sudo access for package installation
|
||||||
|
- Better for local development (matches host UID)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Container Strategy
|
||||||
|
|
||||||
|
### Approach
|
||||||
|
Unlike devkitPro platforms, VitaSDK already provides comprehensive official images with all packages pre-installed. Our strategy should be:
|
||||||
|
|
||||||
|
**Option 1: Use Official Images Directly** (Recommended)
|
||||||
|
- Use `vitasdk/vitasdk:latest` as-is
|
||||||
|
- No custom Dockerfile needed
|
||||||
|
- Pull official image when needed
|
||||||
|
- Simplest approach, maintained by VitaSDK team
|
||||||
|
|
||||||
|
**Option 2: Extend Official Images** (If customization needed)
|
||||||
|
- Base: `FROM vitasdk/vitasdk:latest`
|
||||||
|
- Add project-specific tools or scripts
|
||||||
|
- Pin to specific tag for reproducibility
|
||||||
|
- Only if we need additional packages
|
||||||
|
|
||||||
|
### Tagging Strategy
|
||||||
|
If we publish custom images:
|
||||||
|
- Format: `tomusan/vitasdk-vita:<date>` or `tomusan/vitasdk-vita:<date>-v<semver>`
|
||||||
|
- Example: `tomusan/vitasdk-vita:20251231` or `tomusan/vitasdk-vita:20251231-v1.0.0`
|
||||||
|
- Follow same conventions as devkitPro containers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Package Ecosystem Research
|
||||||
|
|
||||||
|
### Known Libraries (from vdpm)
|
||||||
|
- **zlib**: Compression library
|
||||||
|
- **freetype**: Font rendering
|
||||||
|
- **libvita2d**: 2D graphics library (Vita-specific)
|
||||||
|
- **taihen**: Plugin framework (Vita-specific)
|
||||||
|
|
||||||
|
### Additional Research Needed
|
||||||
|
- [ ] Query vdpm package list for available networking libraries
|
||||||
|
- [ ] Check for curl, libcurl support
|
||||||
|
- [ ] Verify SSL/TLS libraries (mbedtls, openssl, etc.)
|
||||||
|
- [ ] Check for image format libraries (PNG, JPEG)
|
||||||
|
- [ ] Verify XML/JSON parsing libraries
|
||||||
|
- [ ] Document complete package list similar to devkitPro research
|
||||||
|
|
||||||
|
### How to Query Packages
|
||||||
|
```bash
|
||||||
|
# Run official container and list packages
|
||||||
|
podman run --rm vitasdk/vitasdk:latest bash -c "cd /usr/local/vitasdk && find . -name '*.a' -o -name '*.so'"
|
||||||
|
|
||||||
|
# Or check vdpm repository
|
||||||
|
# Clone https://github.com/vitasdk/packages and inspect package definitions
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hardware Requirements
|
||||||
|
|
||||||
|
### Hacked Console Required
|
||||||
|
PS Vita homebrew requires modified firmware:
|
||||||
|
- **HENkaku**: Firmware 3.60
|
||||||
|
- **h-encore**: Firmware 3.65-3.68
|
||||||
|
- **Trinity**: Firmware 3.69-3.70
|
||||||
|
|
||||||
|
This is similar to 3DS requiring custom firmware (CFW). Not a blocker for development, but users need hacked consoles to run homebrew.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
### Phase 1: Research & Validation
|
||||||
|
- [ ] Test official `vitasdk/vitasdk` image locally
|
||||||
|
- [ ] Create simple "Hello World" program
|
||||||
|
- [ ] Build VPK package and test on hardware/emulator
|
||||||
|
- [ ] Document build process
|
||||||
|
- [ ] Query complete package list from vdpm
|
||||||
|
- [ ] Verify networking library availability (curl, SSL/TLS)
|
||||||
|
|
||||||
|
### Phase 2: Integration Planning
|
||||||
|
- [ ] Determine if custom Dockerfile is needed (likely not)
|
||||||
|
- [ ] Create build scripts for Vita platform
|
||||||
|
- `scripts/build-vita.sh` - Build project using container
|
||||||
|
- `scripts/container-shell-vita.sh` - Interactive development shell
|
||||||
|
- [ ] Update main `scripts/build-container.sh` to handle VitaSDK
|
||||||
|
- [ ] Document Vita-specific build flags and configuration
|
||||||
|
|
||||||
|
### Phase 3: Project Integration
|
||||||
|
- [ ] Create Vita source directory structure
|
||||||
|
- [ ] Implement Nextcloud client for Vita
|
||||||
|
- [ ] Test screenshot upload functionality
|
||||||
|
- [ ] Test camera photo upload functionality
|
||||||
|
- [ ] Integrate with main project build system
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Platform Priority Assessment
|
||||||
|
|
||||||
|
**Priority: HIGH**
|
||||||
|
|
||||||
|
Justification:
|
||||||
|
- ✅ Full networking support (Wi-Fi built-in)
|
||||||
|
- ✅ Native screenshot capability for game captures
|
||||||
|
- ✅ Dual cameras for photo capture
|
||||||
|
- ✅ Active homebrew community and toolchain
|
||||||
|
- ✅ Official Docker images available
|
||||||
|
- ✅ Similar form factor to 3DS (handheld gaming device)
|
||||||
|
- ✅ Fits project goals perfectly
|
||||||
|
|
||||||
|
**Recommendation**: Add PS Vita alongside 3DS, Switch, and Wii U as HIGH priority platform.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Differences from devkitPro Platforms
|
||||||
|
|
||||||
|
| Aspect | devkitPro | VitaSDK |
|
||||||
|
|--------|-----------|---------|
|
||||||
|
| **Organization** | devkitPro | vita-dev (separate) |
|
||||||
|
| **Toolchain** | devkitARM/A64/PPC | vita-toolchain (ARM) |
|
||||||
|
| **Package Manager** | dkp-pacman (Arch-style) | vdpm (shell scripts) |
|
||||||
|
| **Build System** | Native makefiles + CMake | CMake with Vita macros |
|
||||||
|
| **Docker Hub** | devkitpro/* | vitasdk/* |
|
||||||
|
| **Package Format** | .pkg.tar.xz | vita-makepkg scripts |
|
||||||
|
| **Output Format** | .elf, .nro, .rpx | .self, .vpk |
|
||||||
|
| **Install Path** | /opt/devkitpro | /usr/local/vitasdk |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Container Usage Examples
|
||||||
|
|
||||||
|
### Using Official Image Directly
|
||||||
|
```bash
|
||||||
|
# Pull official image
|
||||||
|
podman pull vitasdk/vitasdk:latest
|
||||||
|
|
||||||
|
# Build project
|
||||||
|
podman run --rm -v .:/project:z -w /project/vita \
|
||||||
|
vitasdk/vitasdk:latest \
|
||||||
|
cmake -B build && cmake --build build
|
||||||
|
|
||||||
|
# Interactive shell for development
|
||||||
|
podman run --rm -it -v .:/project:z -w /project \
|
||||||
|
vitasdk/vitasdk:latest \
|
||||||
|
bash
|
||||||
|
```
|
||||||
|
|
||||||
|
### If Custom Image Needed
|
||||||
|
```dockerfile
|
||||||
|
# vita.Dockerfile
|
||||||
|
FROM vitasdk/vitasdk:latest
|
||||||
|
|
||||||
|
# Add any project-specific tools
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
vim \
|
||||||
|
tree \
|
||||||
|
htop
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /project
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Community & Support
|
||||||
|
|
||||||
|
### Official Channels
|
||||||
|
- **Discord**: Active VitaSDK community server
|
||||||
|
- **Matrix**: Bridge to Discord
|
||||||
|
- **IRC**: #vitasdk on libera.chat
|
||||||
|
- **Forums**: Various homebrew forums
|
||||||
|
|
||||||
|
### Related Projects
|
||||||
|
- **VitaShell**: File manager (reference for file operations)
|
||||||
|
- **Adrenaline**: PSP emulator (advanced homebrew example)
|
||||||
|
- **RetroArch**: Multi-emulator (networking reference)
|
||||||
|
- Various homebrew games and apps using VitaSDK
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hardware Specifications
|
||||||
|
|
||||||
|
| Component | Specification |
|
||||||
|
|-----------|---------------|
|
||||||
|
| **CPU** | Quad-core ARM Cortex-A9 MPCore |
|
||||||
|
| **GPU** | Quad-core PowerVR SGX543MP4+ |
|
||||||
|
| **RAM** | 512 MB + 128 MB VRAM |
|
||||||
|
| **Display** | 5" OLED/LCD touchscreen, 960×544 (qHD) |
|
||||||
|
| **Storage** | Proprietary memory cards (4-64 GB) |
|
||||||
|
| **Cameras** | Front/back 0.3MP (VGA @ 60fps) |
|
||||||
|
| **Connectivity** | Wi-Fi b/g/n, Bluetooth 2.1+EDR, (3G optional) |
|
||||||
|
| **Battery** | 2210 mAh (3-5 hours gameplay) |
|
||||||
|
| **Input** | Touchscreen, rear touchpad, dual analog sticks, buttons, Sixaxis motion |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
### Official Documentation
|
||||||
|
- [VitaSDK Website](https://vitasdk.org/) - Main landing page
|
||||||
|
- [VitaSDK GitHub](https://github.com/vitasdk) - Organization with all repos
|
||||||
|
- [VitaSDK Docker](https://github.com/vitasdk/docker) - Official Dockerfile source
|
||||||
|
- [VitaSDK Packages](https://github.com/vitasdk/packages) - Available libraries
|
||||||
|
- [VitaSDK Samples](https://github.com/vitasdk/samples) - Example code
|
||||||
|
- [VitaSDK Documentation](https://docs.vitasdk.org/) - API reference
|
||||||
|
|
||||||
|
### Docker Images
|
||||||
|
- [Docker Hub - vitasdk/vitasdk](https://hub.docker.com/r/vitasdk/vitasdk)
|
||||||
|
- [Docker Hub - vitasdk/buildscripts](https://hub.docker.com/r/vitasdk/buildscripts)
|
||||||
|
|
||||||
|
### Hardware Information
|
||||||
|
- [PlayStation Vita - Wikipedia](https://en.wikipedia.org/wiki/PlayStation_Vita) - Comprehensive hardware specs
|
||||||
|
- [PS Vita Dev Wiki](https://wiki.henkaku.xyz/) - Homebrew development wiki
|
||||||
|
|
||||||
|
### Community Resources
|
||||||
|
- VitaSDK Discord Server - Active developer community
|
||||||
|
- [/r/vitahacks](https://reddit.com/r/vitahacks) - Reddit community
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
When ready to implement PS Vita support:
|
||||||
|
|
||||||
|
1. **Test Official Image**:
|
||||||
|
```bash
|
||||||
|
podman pull vitasdk/vitasdk:latest
|
||||||
|
podman run --rm -it vitasdk/vitasdk:latest bash
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Create Hello World**:
|
||||||
|
- Simple CMakeLists.txt
|
||||||
|
- Basic main.c with vita2d graphics
|
||||||
|
- Build VPK package
|
||||||
|
|
||||||
|
3. **Research Networking Libraries**:
|
||||||
|
- Query vdpm for available packages
|
||||||
|
- Test curl/libcurl availability
|
||||||
|
- Verify SSL/TLS support
|
||||||
|
|
||||||
|
4. **Determine Custom Dockerfile Need**:
|
||||||
|
- If official image has everything → use directly
|
||||||
|
- If missing tools → create custom Dockerfile extending official image
|
||||||
|
|
||||||
|
5. **Update Main Plan**:
|
||||||
|
- Add PS Vita to DockerForDevkitARM.md platform summary
|
||||||
|
- Note it uses separate VitaSDK ecosystem
|
||||||
|
- Include in Phase 3 implementation alongside Wii U
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- VitaSDK is completely independent from devkitPro - different organization, different toolchain
|
||||||
|
- Official images already include most/all packages via vdpm install-all
|
||||||
|
- May not need custom Dockerfile at all - official image might be sufficient
|
||||||
|
- PS Vita was discontinued in 2019, but homebrew scene remains active
|
||||||
|
- Estimated 15-16 million units sold worldwide (smaller than 3DS but larger than Wii U)
|
||||||
|
- Active indie game development continues as of 2026
|
||||||
|
|
||||||
410
CONTRIBUTING.md
Normal file
410
CONTRIBUTING.md
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
# Contributing to Nextcloud Share
|
||||||
|
|
||||||
|
Thank you for your interest in contributing to Nextcloud Share! This document provides guidelines and workflows for contributing to the project.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Code of Conduct](#code-of-conduct)
|
||||||
|
- [Getting Started](#getting-started)
|
||||||
|
- [Git Workflow](#git-workflow)
|
||||||
|
- [Development Setup](#development-setup)
|
||||||
|
- [Making Changes](#making-changes)
|
||||||
|
- [Submitting a Pull Request](#submitting-a-pull-request)
|
||||||
|
- [Issue Guidelines](#issue-guidelines)
|
||||||
|
- [Commit Message Guidelines](#commit-message-guidelines)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
We expect all contributors to be respectful, inclusive, and professional. Please:
|
||||||
|
|
||||||
|
- Be welcoming to newcomers
|
||||||
|
- Provide constructive feedback
|
||||||
|
- Focus on what is best for the community
|
||||||
|
- Show empathy towards other community members
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
1. **Fork the repository** on Gitea (if external contributor)
|
||||||
|
2. **Clone your fork** locally:
|
||||||
|
```bash
|
||||||
|
git clone https://git.tomusan.com/maj/nextcloud-share.git
|
||||||
|
cd nextcloud-share
|
||||||
|
```
|
||||||
|
3. **Add upstream remote** (if forked):
|
||||||
|
```bash
|
||||||
|
git remote add upstream https://git.tomusan.com/maj/nextcloud-share.git
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Git Workflow
|
||||||
|
|
||||||
|
This project uses the **git-flow** branching model. Understanding this workflow is essential for contributing.
|
||||||
|
|
||||||
|
### Branch Structure
|
||||||
|
|
||||||
|
#### `main` - Production Branch
|
||||||
|
- **Purpose**: Contains production-ready, stable releases only
|
||||||
|
- **Rules**:
|
||||||
|
- Never commit directly to `main`
|
||||||
|
- Only receives merges from `release/*` or `hotfix/*` branches
|
||||||
|
- Every commit on `main` is tagged with a version number
|
||||||
|
- Represents the current production state
|
||||||
|
|
||||||
|
#### `develop` - Integration Branch
|
||||||
|
- **Purpose**: Integration branch where features come together
|
||||||
|
- **Rules**:
|
||||||
|
- Default branch for development
|
||||||
|
- Never commit directly to `develop`
|
||||||
|
- Receives merges from `feature/*` branches
|
||||||
|
- Always contains the latest delivered development changes
|
||||||
|
- Should always be in a working state
|
||||||
|
|
||||||
|
#### `feature/*` - Feature Branches
|
||||||
|
- **Purpose**: Develop new features or enhancements
|
||||||
|
- **Naming**: `feature/<issue-number>-<short-description>`
|
||||||
|
- Example: `feature/8-webdav-client`
|
||||||
|
- **Branch from**: `develop`
|
||||||
|
- **Merge into**: `develop`
|
||||||
|
- **Lifetime**: Deleted after merge
|
||||||
|
|
||||||
|
**Creating a feature branch**:
|
||||||
|
```bash
|
||||||
|
git checkout develop
|
||||||
|
git pull origin develop
|
||||||
|
git checkout -b feature/8-webdav-client
|
||||||
|
```
|
||||||
|
|
||||||
|
**Merging a feature**:
|
||||||
|
```bash
|
||||||
|
git checkout develop
|
||||||
|
git merge --no-ff feature/8-webdav-client
|
||||||
|
git push origin develop
|
||||||
|
git branch -d feature/8-webdav-client
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `release/*` - Release Preparation Branches
|
||||||
|
- **Purpose**: Prepare for a new production release
|
||||||
|
- **Naming**: `release/v<version>`
|
||||||
|
- Example: `release/v0.1.0`
|
||||||
|
- **Branch from**: `develop`
|
||||||
|
- **Merge into**: `main` AND `develop`
|
||||||
|
- **Lifetime**: Deleted after merge
|
||||||
|
|
||||||
|
**Creating a release branch**:
|
||||||
|
```bash
|
||||||
|
git checkout develop
|
||||||
|
git checkout -b release/v0.1.0
|
||||||
|
# Bump version numbers, update CHANGELOG, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Finishing a release**:
|
||||||
|
```bash
|
||||||
|
# Merge to main
|
||||||
|
git checkout main
|
||||||
|
git merge --no-ff release/v0.1.0
|
||||||
|
git tag -a v0.1.0 -m "Release version 0.1.0"
|
||||||
|
git push origin main --tags
|
||||||
|
|
||||||
|
# Merge back to develop
|
||||||
|
git checkout develop
|
||||||
|
git merge --no-ff release/v0.1.0
|
||||||
|
git push origin develop
|
||||||
|
|
||||||
|
# Delete release branch
|
||||||
|
git branch -d release/v0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `hotfix/*` - Emergency Fix Branches
|
||||||
|
- **Purpose**: Quick fixes for critical production bugs
|
||||||
|
- **Naming**: `hotfix/v<version>-<description>`
|
||||||
|
- Example: `hotfix/v0.1.1-fix-crash`
|
||||||
|
- **Branch from**: `main`
|
||||||
|
- **Merge into**: `main` AND `develop`
|
||||||
|
- **Lifetime**: Deleted after merge
|
||||||
|
|
||||||
|
**Creating a hotfix**:
|
||||||
|
```bash
|
||||||
|
git checkout main
|
||||||
|
git checkout -b hotfix/v0.1.1-fix-crash
|
||||||
|
# Make the fix
|
||||||
|
```
|
||||||
|
|
||||||
|
**Finishing a hotfix**:
|
||||||
|
```bash
|
||||||
|
# Merge to main
|
||||||
|
git checkout main
|
||||||
|
git merge --no-ff hotfix/v0.1.1-fix-crash
|
||||||
|
git tag -a v0.1.1 -m "Hotfix version 0.1.1"
|
||||||
|
git push origin main --tags
|
||||||
|
|
||||||
|
# Merge to develop
|
||||||
|
git checkout develop
|
||||||
|
git merge --no-ff hotfix/v0.1.1-fix-crash
|
||||||
|
git push origin develop
|
||||||
|
|
||||||
|
# Delete hotfix branch
|
||||||
|
git branch -d hotfix/v0.1.1-fix-crash
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- **Podman** (preferred) or **Docker**
|
||||||
|
- **Git**
|
||||||
|
- **jq** (for config parsing)
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
1. **Copy the example config**:
|
||||||
|
```bash
|
||||||
|
cp config.example.json config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Edit `config.json`** with your Nextcloud test credentials:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"nextcloud": {
|
||||||
|
"url": "https://cloud.tomusan.com",
|
||||||
|
"username": "your-username",
|
||||||
|
"password": "your-app-password"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Never commit `config.json`** - it's in `.gitignore`
|
||||||
|
|
||||||
|
### Building with Containers
|
||||||
|
|
||||||
|
All builds should be done inside containers to ensure consistency:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build 3DS app (when available)
|
||||||
|
podman build -f docker/devkitarm.Dockerfile -t nextcloud-share-3ds .
|
||||||
|
podman run --rm -v ./:/project:z nextcloud-share-3ds make -C 3ds
|
||||||
|
|
||||||
|
# Build shared library (when available)
|
||||||
|
podman build -f docker/builder.Dockerfile -t builder .
|
||||||
|
podman run --rm -v ./:/project:z builder cmake -B build -S shared
|
||||||
|
podman run --rm -v ./:/project:z builder cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Making Changes
|
||||||
|
|
||||||
|
### 1. Find or Create an Issue
|
||||||
|
|
||||||
|
- Check [existing issues](https://git.tomusan.com/maj/nextcloud-share/issues)
|
||||||
|
- Create a new issue if needed
|
||||||
|
- Get issue number (e.g., `#42`)
|
||||||
|
|
||||||
|
### 2. Create a Feature Branch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout develop
|
||||||
|
git pull origin develop
|
||||||
|
git checkout -b feature/42-short-description
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Make Your Changes
|
||||||
|
|
||||||
|
- Write clean, maintainable code
|
||||||
|
- Follow existing code style
|
||||||
|
- Add tests for new functionality
|
||||||
|
- Update documentation as needed
|
||||||
|
- Test your changes thoroughly
|
||||||
|
|
||||||
|
### 4. Commit Your Changes
|
||||||
|
|
||||||
|
Follow the [commit message guidelines](#commit-message-guidelines):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add <files>
|
||||||
|
git commit -m "feat: add WebDAV client implementation (#42)"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Keep Your Branch Updated
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout develop
|
||||||
|
git pull origin develop
|
||||||
|
git checkout feature/42-short-description
|
||||||
|
git rebase develop
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Push Your Branch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push origin feature/42-short-description
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Submitting a Pull Request
|
||||||
|
|
||||||
|
1. **Push your feature branch** to the repository
|
||||||
|
2. **Open a Pull Request** on Gitea:
|
||||||
|
- Base: `develop`
|
||||||
|
- Compare: `feature/42-short-description`
|
||||||
|
3. **Fill out the PR template**:
|
||||||
|
- Reference the issue number (e.g., "Closes #42")
|
||||||
|
- Describe what changed and why
|
||||||
|
- Add any testing notes
|
||||||
|
4. **Wait for review**:
|
||||||
|
- Address any feedback
|
||||||
|
- Push additional commits if needed
|
||||||
|
- CI must pass before merge
|
||||||
|
|
||||||
|
### PR Checklist
|
||||||
|
|
||||||
|
- [ ] Branch is up-to-date with `develop`
|
||||||
|
- [ ] All tests pass locally
|
||||||
|
- [ ] New tests added for new features
|
||||||
|
- [ ] Documentation updated
|
||||||
|
- [ ] Commit messages follow guidelines
|
||||||
|
- [ ] CI pipeline passes
|
||||||
|
- [ ] Code has been self-reviewed
|
||||||
|
- [ ] No sensitive data (passwords, tokens) in commits
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Issue Guidelines
|
||||||
|
|
||||||
|
### Creating Issues
|
||||||
|
|
||||||
|
- **Use a clear, descriptive title**
|
||||||
|
- **Provide context**: What are you trying to do?
|
||||||
|
- **Include reproduction steps** for bugs
|
||||||
|
- **Add relevant labels**: `bug`, `feature`, `platform:3ds`, etc.
|
||||||
|
- **Reference related issues** if applicable
|
||||||
|
|
||||||
|
### Working on Issues
|
||||||
|
|
||||||
|
- **Comment on the issue** before starting work to avoid duplication
|
||||||
|
- **Update the issue** with progress notes
|
||||||
|
- **Link commits** to issues using `#<issue-number>` in commit messages
|
||||||
|
- **Close issues** with "Closes #<number>" or "Fixes #<number>" in PR description
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commit Message Guidelines
|
||||||
|
|
||||||
|
We follow the [Conventional Commits](https://www.conventionalcommits.org/) specification.
|
||||||
|
|
||||||
|
### Format
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject> (#<issue>)
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Types
|
||||||
|
|
||||||
|
- **feat**: New feature
|
||||||
|
- **fix**: Bug fix
|
||||||
|
- **docs**: Documentation changes
|
||||||
|
- **style**: Code style changes (formatting, no logic change)
|
||||||
|
- **refactor**: Code refactoring
|
||||||
|
- **test**: Adding or updating tests
|
||||||
|
- **chore**: Maintenance tasks
|
||||||
|
- **ci**: CI/CD changes
|
||||||
|
- **perf**: Performance improvements
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Simple feature
|
||||||
|
git commit -m "feat: add WebDAV client implementation (#8)"
|
||||||
|
|
||||||
|
# Bug fix
|
||||||
|
git commit -m "fix: resolve memory leak in upload manager (#25)"
|
||||||
|
|
||||||
|
# With scope
|
||||||
|
git commit -m "feat(3ds): implement SD card file browser (#16)"
|
||||||
|
|
||||||
|
# With body
|
||||||
|
git commit -m "refactor: simplify authentication logic (#9)
|
||||||
|
|
||||||
|
The previous implementation had unnecessary complexity.
|
||||||
|
This refactoring makes the code more maintainable and easier to test."
|
||||||
|
|
||||||
|
# Breaking change
|
||||||
|
git commit -m "feat!: change API authentication method (#9)
|
||||||
|
|
||||||
|
BREAKING CHANGE: Authentication now requires app passwords instead of
|
||||||
|
regular passwords. Update your config.json accordingly."
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue References
|
||||||
|
|
||||||
|
- **Always include issue number**: `(#42)` in commit message
|
||||||
|
- **Close issues** with keywords in PR description:
|
||||||
|
- `Closes #42`
|
||||||
|
- `Fixes #42`
|
||||||
|
- `Resolves #42`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
### C++
|
||||||
|
- **Standard**: C++17
|
||||||
|
- **Formatting**: Follow existing style (consider using clang-format)
|
||||||
|
- **Naming**:
|
||||||
|
- Classes: `PascalCase`
|
||||||
|
- Functions/methods: `camelCase`
|
||||||
|
- Variables: `snake_case`
|
||||||
|
- Constants: `UPPER_SNAKE_CASE`
|
||||||
|
- Namespaces: `lowercase`
|
||||||
|
|
||||||
|
### Python
|
||||||
|
- **Standard**: PEP 8
|
||||||
|
- **Formatting**: Use Black or autopep8
|
||||||
|
|
||||||
|
### Shell Scripts
|
||||||
|
- **Use bash** explicitly: `#!/bin/bash`
|
||||||
|
- **Quote variables**: `"${VAR}"`
|
||||||
|
- **Check return codes**: `if [ $? -eq 0 ]; then`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- **Write tests** for all new features
|
||||||
|
- **Run tests locally** before pushing:
|
||||||
|
```bash
|
||||||
|
# When available
|
||||||
|
podman run --rm -v ./:/project:z builder ctest --test-dir build
|
||||||
|
```
|
||||||
|
- **Ensure CI passes** before requesting review
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Questions?
|
||||||
|
|
||||||
|
- **Open an issue** for questions
|
||||||
|
- **Comment on relevant issues** for feature discussions
|
||||||
|
- **Check existing documentation** in the `docs/` directory
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
By contributing to Nextcloud Share, you agree that your contributions will be licensed under the MIT License.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Thank you for contributing! 🎉
|
||||||
186
README.md
186
README.md
@@ -62,87 +62,177 @@ git clone https://git.tomusan.com/your-username/nextcloud-share.git
|
|||||||
cd nextcloud-share
|
cd nextcloud-share
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Configure Test Environment
|
### 2. Build the 3DS Development Container
|
||||||
|
|
||||||
Copy the example configuration file:
|
Build the container image (first time only, or when Dockerfile changes):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp config.example config
|
./scripts/build-container.sh 3ds
|
||||||
```
|
```
|
||||||
|
|
||||||
Edit `config` and add your test Nextcloud credentials:
|
This creates a complete 3DS development environment with:
|
||||||
|
- devkitARM GCC 15.2.0
|
||||||
|
- All 47 available 3DS portlibs
|
||||||
|
- CIA creation tools (bannertool, makerom, ctrtool)
|
||||||
|
- Build time: ~10-15 minutes on first build
|
||||||
|
|
||||||
```ini
|
**Note**: The container is built once and reused. You only rebuild when the Dockerfile changes.
|
||||||
# Local test configuration - DO NOT COMMIT THIS FILE
|
|
||||||
NEXTCLOUD_URL=https://your-nextcloud-instance.com
|
|
||||||
NEXTCLOUD_USER=your-username
|
|
||||||
NEXTCLOUD_PASSWORD=your-password
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note**: The `config` file is in `.gitignore` and will never be committed. Each developer should create their own local config.
|
### 3. Compile Your 3DS Project
|
||||||
|
|
||||||
### 3. Build Using Podman
|
|
||||||
|
|
||||||
#### Build the 3DS version:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build the container image (first time only)
|
# From within your project directory
|
||||||
podman build -f docker/devkitarm.Dockerfile -t nextcloud-share-3ds .
|
cd your-3ds-project
|
||||||
|
podman run --rm -v .:/project:z tomusan/devkitarm-3ds:latest make
|
||||||
|
|
||||||
# Compile the project
|
# Or specify a path
|
||||||
podman run --rm -v ./:/project:z nextcloud-share-3ds make -C 3ds
|
podman run --rm -v ~/my-game:/project:z tomusan/devkitarm-3ds:latest make
|
||||||
|
|
||||||
# Output files will be in 3ds/build/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Run tests:
|
**Output files**: `.3dsx` (homebrew), `.elf` (debug), `.smdh` (metadata), `.cia` (installable)
|
||||||
|
|
||||||
|
### 4. Interactive Development Shell
|
||||||
|
|
||||||
|
For debugging or manual builds:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
podman run --rm -v ./:/project:z nextcloud-share-3ds make test
|
# Current directory
|
||||||
|
./scripts/container-shell.sh
|
||||||
|
|
||||||
|
# Specific project
|
||||||
|
./scripts/container-shell.sh ~/my-3ds-game
|
||||||
|
|
||||||
|
# With extra volumes or environment
|
||||||
|
./scripts/container-shell.sh ~/my-game -v /data:/data:z -e DEBUG=1
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Interactive development shell:
|
Inside the container, all devkitARM tools are available in PATH. Run `make` to build, `exit` to leave.
|
||||||
|
|
||||||
|
### 5. Configure Test Environment (Optional)
|
||||||
|
|
||||||
|
For testing with real Nextcloud servers, copy the example configuration:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
podman run --rm -it -v ./:/project:z nextcloud-share-3ds bash
|
cp config.example.json config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Edit `config.json` and add your test Nextcloud credentials:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"nextcloud": {
|
||||||
|
"url": "https://your-nextcloud-instance.com",
|
||||||
|
"username": "your-username",
|
||||||
|
"password": "your-app-password"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"maxRecentFolders": 5,
|
||||||
|
"uploadChunkSize": 10485760
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The `config.json` file is in `.gitignore` and will never be committed.
|
||||||
|
|
||||||
## 🔧 Building
|
## 🔧 Building
|
||||||
|
|
||||||
Detailed build instructions for each platform will be added as they are implemented. See platform-specific README files:
|
### Using Development Containers
|
||||||
|
|
||||||
- [3ds/README.md](3ds/README.md) - Nintendo 3DS build instructions
|
All builds run in isolated containers with complete toolchains. See [docker/README.md](docker/README.md) for detailed container documentation.
|
||||||
- More coming soon...
|
|
||||||
|
**Available containers:**
|
||||||
|
- **3DS**: `tomusan/devkitarm-3ds:latest` - Nintendo 3DS with full CIA support
|
||||||
|
|
||||||
|
**Quick build commands:**
|
||||||
|
```bash
|
||||||
|
# Build 3DS container (first time)
|
||||||
|
./scripts/build-container.sh 3ds
|
||||||
|
|
||||||
|
# Compile a 3DS project
|
||||||
|
podman run --rm -v ~/my-game:/project:z tomusan/devkitarm-3ds:latest make
|
||||||
|
|
||||||
|
# Interactive shell
|
||||||
|
./scripts/container-shell.sh ~/my-game
|
||||||
|
```
|
||||||
|
|
||||||
|
**Build performance:**
|
||||||
|
- First build: ~10-15 minutes (downloads and compiles tools)
|
||||||
|
- Subsequent builds: Instant (uses build cache)
|
||||||
|
- Container rebuilds: Only needed when Dockerfile changes
|
||||||
|
|
||||||
|
Platform-specific build instructions:
|
||||||
|
- [docker/README.md](docker/README.md) - Container usage and CIA creation
|
||||||
|
- More platforms coming soon...
|
||||||
|
|
||||||
## ⚙️ Configuration
|
## ⚙️ Configuration
|
||||||
|
|
||||||
### Local Development Configuration
|
### Local Development Configuration
|
||||||
|
|
||||||
The `config` file (copied from `config.example`) is used for local testing:
|
The project uses a JSON configuration file for managing test credentials and build options.
|
||||||
|
|
||||||
```ini
|
**Setup:**
|
||||||
NEXTCLOUD_URL=https://cloud.example.com
|
```bash
|
||||||
NEXTCLOUD_USER=testuser
|
# Copy the example configuration
|
||||||
NEXTCLOUD_PASSWORD=testpass
|
cp config.example.json config.json
|
||||||
MAX_RECENT_FOLDERS=5 # Build-time option
|
|
||||||
|
# Edit config.json with your credentials
|
||||||
|
nano config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
### CI/CD Environment Variables
|
**Configuration structure:**
|
||||||
|
```json
|
||||||
For CI/CD pipelines, use environment variables to override the local config:
|
{
|
||||||
|
"nextcloud": {
|
||||||
- `NEXTCLOUD_TEST_URL` - Test server URL
|
"url": "https://cloud.example.com",
|
||||||
- `NEXTCLOUD_TEST_USER` - Test username
|
"username": "testuser",
|
||||||
- `NEXTCLOUD_TEST_PASSWORD` - Test password
|
"password": "your-app-password"
|
||||||
- `MAX_RECENT_FOLDERS` - Number of recent folders to track
|
},
|
||||||
|
"settings": {
|
||||||
Example:
|
"maxRecentFolders": 5,
|
||||||
|
"uploadChunkSize": 10485760
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Loading configuration:**
|
||||||
```bash
|
```bash
|
||||||
NEXTCLOUD_TEST_URL=https://cloud.tomusan.com \
|
# Load configuration and export as environment variables
|
||||||
NEXTCLOUD_TEST_USER=ci-user \
|
source scripts/load-config.sh
|
||||||
NEXTCLOUD_TEST_PASSWORD=ci-pass \
|
|
||||||
podman run --rm -v ./:/project:z nextcloud-share-3ds make
|
# Validate configuration
|
||||||
|
./scripts/validate-config.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variable Overrides
|
||||||
|
|
||||||
|
Environment variables take precedence over JSON configuration values. This is useful for CI/CD pipelines where you don't want to store credentials in files.
|
||||||
|
|
||||||
|
**Supported environment variables:**
|
||||||
|
- `NEXTCLOUD_URL` - Nextcloud server URL (required)
|
||||||
|
- `NEXTCLOUD_USER` - Username for authentication (required)
|
||||||
|
- `NEXTCLOUD_PASSWORD` - Password or app password (required)
|
||||||
|
- `MAX_RECENT_FOLDERS` - Number of recent folders to track (default: 5)
|
||||||
|
- `UPLOAD_CHUNK_SIZE` - Upload chunk size in bytes (default: 10485760)
|
||||||
|
- `CONFIG_FILE` - Path to config file (default: config.json)
|
||||||
|
|
||||||
|
**CI/CD Example:**
|
||||||
|
```bash
|
||||||
|
# Override configuration with environment variables
|
||||||
|
export NEXTCLOUD_URL=https://cloud.tomusan.com
|
||||||
|
export NEXTCLOUD_USER=ci-user
|
||||||
|
export NEXTCLOUD_PASSWORD=ci-pass
|
||||||
|
|
||||||
|
# Load any remaining config from file
|
||||||
|
source scripts/load-config.sh
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
podman run --rm \
|
||||||
|
-e NEXTCLOUD_URL \
|
||||||
|
-e NEXTCLOUD_USER \
|
||||||
|
-e NEXTCLOUD_PASSWORD \
|
||||||
|
-v ./:/project:z \
|
||||||
|
tomusan/devkitarm-3ds:latest \
|
||||||
|
make test
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🧪 Testing
|
## 🧪 Testing
|
||||||
|
|||||||
11
config.example.json
Normal file
11
config.example.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"nextcloud": {
|
||||||
|
"url": "https://cloud.example.com",
|
||||||
|
"username": "your-username",
|
||||||
|
"password": "your-app-password"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"maxRecentFolders": 5,
|
||||||
|
"uploadChunkSize": 10485760
|
||||||
|
}
|
||||||
|
}
|
||||||
62
docker/.dockerignore
Normal file
62
docker/.dockerignore
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# Docker build context exclusions
|
||||||
|
# Keep build context minimal for faster builds
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.gitattributes
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
*.o
|
||||||
|
*.elf
|
||||||
|
*.3dsx
|
||||||
|
*.cia
|
||||||
|
*.smdh
|
||||||
|
*.nro
|
||||||
|
*.nso
|
||||||
|
*.rpx
|
||||||
|
*.rpl
|
||||||
|
*.dol
|
||||||
|
*.wad
|
||||||
|
*.vpk
|
||||||
|
*.self
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# IDE and editor files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Documentation and plans (not needed in container)
|
||||||
|
docs/
|
||||||
|
.plans/
|
||||||
|
*.md
|
||||||
|
!docker/*.md
|
||||||
|
|
||||||
|
# Config files (copied separately if needed)
|
||||||
|
*.json
|
||||||
|
*.toml
|
||||||
|
*.yaml
|
||||||
|
*.yml
|
||||||
|
!package.json
|
||||||
|
|
||||||
|
# Node modules and dependencies
|
||||||
|
node_modules/
|
||||||
|
venv/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cache directories
|
||||||
|
.cache/
|
||||||
|
.ccache/
|
||||||
|
*.cache
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
122
docker/3ds.Dockerfile
Normal file
122
docker/3ds.Dockerfile
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# Nintendo 3DS Development Container
|
||||||
|
# Based on official devkitPro devkitARM image
|
||||||
|
# Contains ALL available 3DS portlibs for complete development environment
|
||||||
|
|
||||||
|
FROM devkitpro/devkitarm:20251231
|
||||||
|
|
||||||
|
LABEL maintainer="nextcloud-share contributors"
|
||||||
|
LABEL description="Complete Nintendo 3DS development environment with all available portlibs"
|
||||||
|
LABEL org.opencontainers.image.base.name="devkitpro/devkitarm:20251231"
|
||||||
|
LABEL org.opencontainers.image.title="Nintendo 3DS Development Container"
|
||||||
|
LABEL platform="3ds"
|
||||||
|
LABEL toolchain="devkitARM"
|
||||||
|
|
||||||
|
# Update package database
|
||||||
|
RUN dkp-pacman -Sy --noconfirm
|
||||||
|
|
||||||
|
# Install 3DS development group (includes devkitARM, libctru, citro3d, citro2d, tools)
|
||||||
|
RUN dkp-pacman -S --needed --noconfirm 3ds-dev
|
||||||
|
|
||||||
|
# Install ALL available 3DS portlibs (alphabetical for clarity)
|
||||||
|
# List verified from devkitpro/devkitarm:20251231 on 2026-01-27
|
||||||
|
RUN dkp-pacman -S --needed --noconfirm \
|
||||||
|
3ds-box2d \
|
||||||
|
3ds-bulletphysics \
|
||||||
|
3ds-bzip2 \
|
||||||
|
3ds-curl \
|
||||||
|
3ds-flac \
|
||||||
|
3ds-flite \
|
||||||
|
3ds-freetype \
|
||||||
|
3ds-giflib \
|
||||||
|
3ds-jansson \
|
||||||
|
3ds-libarchive \
|
||||||
|
3ds-libconfig \
|
||||||
|
3ds-libfribidi \
|
||||||
|
3ds-libiconv \
|
||||||
|
3ds-libid3tag \
|
||||||
|
3ds-libjpeg-turbo \
|
||||||
|
3ds-libjson-c \
|
||||||
|
3ds-liblua51 \
|
||||||
|
3ds-liblzma \
|
||||||
|
3ds-libmad \
|
||||||
|
3ds-libmodplug \
|
||||||
|
3ds-libogg \
|
||||||
|
3ds-libopus \
|
||||||
|
3ds-libpng \
|
||||||
|
3ds-libsidplay \
|
||||||
|
3ds-libtheora \
|
||||||
|
3ds-libvorbisidec \
|
||||||
|
3ds-libxmp \
|
||||||
|
3ds-libzstd \
|
||||||
|
3ds-lz4 \
|
||||||
|
3ds-mbedtls \
|
||||||
|
3ds-mikmod \
|
||||||
|
3ds-mpg123 \
|
||||||
|
3ds-opusfile \
|
||||||
|
3ds-physfs \
|
||||||
|
3ds-sdl \
|
||||||
|
3ds-sdl_gfx \
|
||||||
|
3ds-sdl_image \
|
||||||
|
3ds-sdl_mixer \
|
||||||
|
3ds-sdl_ttf \
|
||||||
|
3ds-tinyxml2 \
|
||||||
|
3ds-wildmidi \
|
||||||
|
3ds-wslay \
|
||||||
|
3ds-yaml_cpp \
|
||||||
|
3ds-zlib && \
|
||||||
|
dkp-pacman -Scc --noconfirm
|
||||||
|
|
||||||
|
# Install system build tools for development
|
||||||
|
# Note: devkitarm-cmake is already installed as part of 3ds-dev group
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
ccache \
|
||||||
|
ninja-build \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
jq \
|
||||||
|
git \
|
||||||
|
zip \
|
||||||
|
unzip && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Configure ccache for faster rebuilds
|
||||||
|
ENV CCACHE_DIR=/project/.ccache
|
||||||
|
ENV CCACHE_MAXSIZE=2G
|
||||||
|
|
||||||
|
# Add devkitARM compiler to PATH
|
||||||
|
ENV PATH="${DEVKITARM}/bin:/usr/lib/ccache:${PATH}"
|
||||||
|
|
||||||
|
# Build and install bannertool for CIA file creation
|
||||||
|
RUN git clone --recursive https://github.com/diasurgical/bannertool.git /tmp/bannertool && \
|
||||||
|
cd /tmp/bannertool && \
|
||||||
|
make && \
|
||||||
|
cp output/linux-x86_64/bannertool /opt/devkitpro/tools/bin/ && \
|
||||||
|
chmod +x /opt/devkitpro/tools/bin/bannertool && \
|
||||||
|
cd / && \
|
||||||
|
rm -rf /tmp/bannertool
|
||||||
|
|
||||||
|
# Build and install makerom and ctrtool for CIA file creation
|
||||||
|
RUN git clone https://github.com/3DSGuy/Project_CTR.git /tmp/Project_CTR && \
|
||||||
|
cd /tmp/Project_CTR/makerom && \
|
||||||
|
make deps && make && \
|
||||||
|
cp bin/makerom /opt/devkitpro/tools/bin/ && \
|
||||||
|
chmod +x /opt/devkitpro/tools/bin/makerom && \
|
||||||
|
cd /tmp/Project_CTR/ctrtool && \
|
||||||
|
make deps && make && \
|
||||||
|
cp bin/ctrtool /opt/devkitpro/tools/bin/ && \
|
||||||
|
chmod +x /opt/devkitpro/tools/bin/ctrtool && \
|
||||||
|
cd / && \
|
||||||
|
rm -rf /tmp/Project_CTR
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /project
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
RUN arm-none-eabi-gcc --version && \
|
||||||
|
make --version && \
|
||||||
|
cmake --version
|
||||||
|
|
||||||
|
# Show installed 3DS packages on container startup
|
||||||
|
CMD ["bash", "-c", "echo 'Nintendo 3DS Development Container Ready' && echo 'devkitARM:' && arm-none-eabi-gcc --version | head -n1 && echo '' && echo '3DS packages installed:' && dkp-pacman -Qq | grep '^3ds-' | wc -l && echo 'packages' && bash"]
|
||||||
316
docker/README.md
Normal file
316
docker/README.md
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
# Nintendo 3DS Development Container
|
||||||
|
|
||||||
|
Complete Docker/Podman container for Nintendo 3DS homebrew development with full CIA file creation support.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Complete devkitARM toolchain** - GCC 15.2.0 for ARM development
|
||||||
|
- **All 47 3DS portlibs** - Every available library including graphics, audio, networking, physics, and compression
|
||||||
|
- **CIA Creation Tools**:
|
||||||
|
- `bannertool` - Creates banner and icon files for CIA packages
|
||||||
|
- `makerom` - Assembles final CIA installation files
|
||||||
|
- `ctrtool` - Inspects and validates CIA structure
|
||||||
|
- `3dstools` - Includes 3dsxtool, smdhtool, mkromfs3ds
|
||||||
|
- **Build optimization** - ccache for faster rebuilds
|
||||||
|
- **Modern build tools** - CMake, Ninja, Python 3
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Using Helper Scripts (Recommended)
|
||||||
|
|
||||||
|
The easiest way to use these containers is with the provided helper scripts:
|
||||||
|
|
||||||
|
**1. Build the container:**
|
||||||
|
```bash
|
||||||
|
./scripts/build-container.sh 3ds
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Open interactive shell:**
|
||||||
|
```bash
|
||||||
|
# Current directory
|
||||||
|
./scripts/container-shell.sh
|
||||||
|
|
||||||
|
# Specific project
|
||||||
|
./scripts/container-shell.sh ~/my-3ds-game
|
||||||
|
|
||||||
|
# With additional volumes or environment
|
||||||
|
./scripts/container-shell.sh ~/my-game -v /data:/data:z -e DEBUG=1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Container Usage
|
||||||
|
|
||||||
|
If you prefer to use podman/docker commands directly:
|
||||||
|
|
||||||
|
**Building the Container:**
|
||||||
|
```bash
|
||||||
|
# Using Podman (recommended for rootless operation)
|
||||||
|
podman build -t tomusan/devkitarm-3ds:latest -f docker/3ds.Dockerfile .
|
||||||
|
|
||||||
|
# Using Docker
|
||||||
|
docker build -t tomusan/devkitarm-3ds:latest -f docker/3ds.Dockerfile .
|
||||||
|
```
|
||||||
|
|
||||||
|
Build time: ~10-15 minutes on first build (downloads and compiles bannertool, makerom, ctrtool)
|
||||||
|
|
||||||
|
**Compiling a 3DS Project:**
|
||||||
|
|
||||||
|
Basic compilation (produces .3dsx homebrew file):
|
||||||
|
```bash
|
||||||
|
podman run --rm -v ./your-project:/project:z tomusan/devkitarm-3ds:latest make
|
||||||
|
```
|
||||||
|
|
||||||
|
With CIA file creation:
|
||||||
|
```bash
|
||||||
|
# Most 3DS projects with proper Makefile configuration
|
||||||
|
podman run --rm -v ./your-project:/project:z tomusan/devkitarm-3ds:latest make
|
||||||
|
```
|
||||||
|
|
||||||
|
Clean and rebuild:
|
||||||
|
```bash
|
||||||
|
podman run --rm -v ./your-project:/project:z tomusan/devkitarm-3ds:latest bash -c "make clean && make"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Interactive Shell:**
|
||||||
|
|
||||||
|
For debugging or manual builds:
|
||||||
|
```bash
|
||||||
|
podman run -it --rm -v ./your-project:/project:z tomusan/devkitarm-3ds:latest bash
|
||||||
|
```
|
||||||
|
|
||||||
|
## Helper Scripts
|
||||||
|
|
||||||
|
### build-container.sh
|
||||||
|
|
||||||
|
Builds and tags development containers with automatic runtime detection (podman/docker).
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
./scripts/build-container.sh <platform> [version]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```bash
|
||||||
|
# Build with default version (0.1.0)
|
||||||
|
./scripts/build-container.sh 3ds
|
||||||
|
|
||||||
|
# Build with custom version
|
||||||
|
./scripts/build-container.sh 3ds 0.2.0
|
||||||
|
|
||||||
|
# Show help
|
||||||
|
./scripts/build-container.sh --help
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Auto-detects podman/docker runtime
|
||||||
|
- Validates semantic versioning (X.Y.Z format)
|
||||||
|
- Tags both version and latest
|
||||||
|
- Colored output with build status
|
||||||
|
- Shows usage examples after successful build
|
||||||
|
|
||||||
|
**Environment Variables:**
|
||||||
|
- `CONTAINER_RUNTIME` - Override runtime (podman or docker)
|
||||||
|
|
||||||
|
### container-shell.sh
|
||||||
|
|
||||||
|
Opens an interactive bash shell in the development container with your project mounted.
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
./scripts/container-shell.sh [project-path] [podman-args...]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```bash
|
||||||
|
# Shell in current directory
|
||||||
|
./scripts/container-shell.sh
|
||||||
|
|
||||||
|
# Shell in specific project
|
||||||
|
./scripts/container-shell.sh ~/my-3ds-game
|
||||||
|
|
||||||
|
# With additional volume mount
|
||||||
|
./scripts/container-shell.sh ~/my-game -v /data:/data:z
|
||||||
|
|
||||||
|
# With environment variable
|
||||||
|
./scripts/container-shell.sh ~/my-game -e DEBUG=1
|
||||||
|
|
||||||
|
# Multiple extra arguments
|
||||||
|
./scripts/container-shell.sh ~/my-game -v /data:/data:z -e DEBUG=1 --network=host
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- First argument = project path (defaults to current directory)
|
||||||
|
- All additional arguments passed to podman/docker run
|
||||||
|
- Auto-detects podman/docker runtime
|
||||||
|
- Colored output with container information
|
||||||
|
- Project mounted at `/project` with working directory set
|
||||||
|
|
||||||
|
**Environment Variables:**
|
||||||
|
- `CONTAINER_RUNTIME` - Override runtime (podman or docker)
|
||||||
|
|
||||||
|
## Output Files
|
||||||
|
|
||||||
|
A complete 3DS build produces:
|
||||||
|
|
||||||
|
- **`.3dsx`** - Homebrew Launcher executable format
|
||||||
|
- **`.elf`** - Executable with debug symbols
|
||||||
|
- **`.smdh`** - Icon and metadata for Homebrew Launcher
|
||||||
|
- **`banner.bnr`** - Banner displayed when CIA is selected on home menu
|
||||||
|
- **`icon.icn`** - Icon file for CIA package
|
||||||
|
- **`.cia`** - Installable file for 3DS home menu
|
||||||
|
|
||||||
|
## CIA File Creation
|
||||||
|
|
||||||
|
The container includes all tools needed for complete CIA file creation:
|
||||||
|
|
||||||
|
### Manual CIA Creation Workflow
|
||||||
|
|
||||||
|
If your Makefile doesn't include CIA generation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
podman run --rm -v ./your-project:/project:z tomusan/devkitarm-3ds:latest bash -c '
|
||||||
|
# Build the application
|
||||||
|
make
|
||||||
|
|
||||||
|
# Create banner (requires banner.png and audio.wav in assets/)
|
||||||
|
bannertool makebanner -i assets/banner.png -a assets/audio.wav -o build/banner.bnr
|
||||||
|
|
||||||
|
# Create icon (requires icon.png in assets/)
|
||||||
|
bannertool makesmdh -s "App Title" -l "Description" -p "Author" \
|
||||||
|
-i assets/icon.png -o build/icon.icn
|
||||||
|
|
||||||
|
# Create CIA file (requires app.rsf in assets/)
|
||||||
|
makerom -f cia -o output.cia -target t -exefslogo \
|
||||||
|
-elf output.elf -rsf assets/app.rsf \
|
||||||
|
-banner build/banner.bnr -icon build/icon.icn \
|
||||||
|
-DAPP_TITLE="App Title" \
|
||||||
|
-DAPP_PRODUCT_CODE="CTR-P-XXXX" \
|
||||||
|
-DAPP_UNIQUE_ID="0x12345"
|
||||||
|
'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validating CIA Files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Inspect CIA structure
|
||||||
|
podman run --rm -v ./your-project:/project:z tomusan/devkitarm-3ds:latest \
|
||||||
|
ctrtool -i output.cia
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installed Libraries
|
||||||
|
|
||||||
|
The container includes all available 3DS portlibs:
|
||||||
|
|
||||||
|
**Graphics & UI:**
|
||||||
|
- citro3d, citro2d - 3DS GPU libraries
|
||||||
|
- SDL, SDL_gfx, SDL_image, SDL_mixer, SDL_ttf
|
||||||
|
- freetype, libpng, libjpeg-turbo, giflib
|
||||||
|
|
||||||
|
**Audio:**
|
||||||
|
- libopus, opusfile, libvorbisidec, libogg
|
||||||
|
- flac, libmad, libmodplug, libxmp, mikmod, mpg123
|
||||||
|
- wildmidi, libid3tag
|
||||||
|
|
||||||
|
**Networking:**
|
||||||
|
- curl, mbedtls, wslay
|
||||||
|
|
||||||
|
**Data Formats:**
|
||||||
|
- json-c, jansson, yaml_cpp, tinyxml2
|
||||||
|
- libarchive, bzip2, liblzma, lz4, libzstd, zlib
|
||||||
|
|
||||||
|
**Physics & Math:**
|
||||||
|
- box2d, bulletphysics
|
||||||
|
|
||||||
|
**Other:**
|
||||||
|
- libconfig, libfribidi, libiconv, liblua51
|
||||||
|
- physfs, libsidplay, libtheora
|
||||||
|
|
||||||
|
## Container Environment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DEVKITPRO=/opt/devkitpro
|
||||||
|
DEVKITARM=/opt/devkitpro/devkitARM
|
||||||
|
PATH includes:
|
||||||
|
- ${DEVKITARM}/bin (ARM toolchain)
|
||||||
|
- /opt/devkitpro/tools/bin (bannertool, makerom, ctrtool)
|
||||||
|
- /usr/lib/ccache (build cache)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Verified working with:
|
||||||
|
- **60/61 official 3ds-examples** - 98.4% success rate
|
||||||
|
- **Checkpoint** - Real-world save manager application with full CIA creation
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Permission Issues with Podman
|
||||||
|
|
||||||
|
The `:z` flag in volume mounts sets proper SELinux context:
|
||||||
|
```bash
|
||||||
|
-v ./project:/project:z
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container Size
|
||||||
|
|
||||||
|
Expected size: ~1.6 GB (includes all libraries and build tools)
|
||||||
|
|
||||||
|
### Build Cache
|
||||||
|
|
||||||
|
To speed up rebuilds, the container uses ccache. The cache is stored in your project at `.ccache/`:
|
||||||
|
```bash
|
||||||
|
# Clean cache if needed
|
||||||
|
rm -rf ./your-project/.ccache
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Errors
|
||||||
|
|
||||||
|
**"makerom: command not found"**
|
||||||
|
- Rebuild container with latest Dockerfile (includes makerom build steps)
|
||||||
|
|
||||||
|
**"bannertool: command not found"**
|
||||||
|
- Rebuild container with latest Dockerfile (includes bannertool build steps)
|
||||||
|
|
||||||
|
**CIA creation fails silently**
|
||||||
|
- Check that all required files exist:
|
||||||
|
- `assets/app.rsf` - CIA metadata
|
||||||
|
- `assets/banner.png` and `assets/audio.wav` - Banner resources
|
||||||
|
- `assets/icon.png` - Icon resource
|
||||||
|
- `output.elf` - Compiled application
|
||||||
|
|
||||||
|
## Version Information
|
||||||
|
|
||||||
|
- **Base Image**: devkitpro/devkitarm:20251231
|
||||||
|
- **devkitARM**: GCC 15.2.0
|
||||||
|
- **CMake**: 3.31.6
|
||||||
|
- **GNU Make**: 4.3
|
||||||
|
- **3DS Packages**: 47 portlibs installed
|
||||||
|
|
||||||
|
## Building from Source
|
||||||
|
|
||||||
|
The Dockerfile includes build steps for:
|
||||||
|
|
||||||
|
1. **bannertool** (diasurgical/bannertool)
|
||||||
|
- Cloned with `--recursive` for submodules
|
||||||
|
- Built with system make
|
||||||
|
- Installed to `/opt/devkitpro/tools/bin/`
|
||||||
|
|
||||||
|
2. **makerom & ctrtool** (3DSGuy/Project_CTR)
|
||||||
|
- Cloned from active fork
|
||||||
|
- Built dependencies first with `make deps`
|
||||||
|
- Built tools with `make`
|
||||||
|
- Installed to `/opt/devkitpro/tools/bin/`
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
- [devkitPro Documentation](https://devkitpro.org/)
|
||||||
|
- [3DS Homebrew Development](https://www.3dbrew.org/wiki/Homebrew_Development)
|
||||||
|
- [Checkpoint Repository](https://github.com/BernardoGiordano/Checkpoint) - Example of complete CIA project
|
||||||
|
- [3ds-examples](https://github.com/devkitPro/3ds-examples) - Official examples
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This container configuration is provided as-is. Individual tools and libraries maintain their respective licenses:
|
||||||
|
- devkitARM: GPL/proprietary tools
|
||||||
|
- bannertool: Custom license
|
||||||
|
- makerom/ctrtool: Custom license
|
||||||
|
- Portlibs: Various open source licenses
|
||||||
203
docs/configuration.md
Normal file
203
docs/configuration.md
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
# Configuration Setup Guide
|
||||||
|
|
||||||
|
This guide explains how to set up your Nextcloud configuration for development and testing.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. **Copy the example configuration**:
|
||||||
|
```bash
|
||||||
|
cp config.example.json config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Generate a Nextcloud App Password** (recommended for security):
|
||||||
|
- Go to your Nextcloud instance
|
||||||
|
- Navigate to: **Settings → Security → Devices & sessions**
|
||||||
|
- Scroll to "App passwords" section
|
||||||
|
- Enter a name (e.g., "nextcloud-share-dev")
|
||||||
|
- Click "Create new app password"
|
||||||
|
- Copy the generated token
|
||||||
|
|
||||||
|
3. **Edit `config.json`** with your credentials:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"nextcloud": {
|
||||||
|
"url": "https://cloud.tomusan.com",
|
||||||
|
"username": "your-username",
|
||||||
|
"password": "xxxxx-xxxxx-xxxxx-xxxxx-xxxxx"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"maxRecentFolders": 5,
|
||||||
|
"uploadChunkSize": 10485760
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Validate your configuration**:
|
||||||
|
```bash
|
||||||
|
./scripts/validate-config.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Generating App Passwords
|
||||||
|
|
||||||
|
### For cloud.tomusan.com
|
||||||
|
|
||||||
|
1. Log in to https://cloud.tomusan.com
|
||||||
|
2. Click your profile icon (top right) → Settings
|
||||||
|
3. Go to **Security** in the left sidebar
|
||||||
|
4. Scroll down to **Devices & sessions**
|
||||||
|
5. Under "App passwords" or "App-specific passwords":
|
||||||
|
- Enter a name: `nextcloud-share-dev`
|
||||||
|
- Click **Create new app password**
|
||||||
|
6. Copy the generated password (format: `xxxxx-xxxxx-xxxxx-xxxxx-xxxxx`)
|
||||||
|
7. Paste it into `config.json` as the `password` field
|
||||||
|
|
||||||
|
### For disobedient.cloud/nextcloud
|
||||||
|
|
||||||
|
1. Log in to https://disobedient.cloud/nextcloud
|
||||||
|
2. Follow the same steps as above
|
||||||
|
3. Generate a separate app password for this instance
|
||||||
|
|
||||||
|
## Why App Passwords?
|
||||||
|
|
||||||
|
App passwords are more secure than using your main account password because:
|
||||||
|
|
||||||
|
- ✅ **Limited scope**: Only grants access to files, not account settings
|
||||||
|
- ✅ **Revocable**: Can be revoked without changing your main password
|
||||||
|
- ✅ **Traceable**: Shows up as a separate session in your account
|
||||||
|
- ✅ **Safer storage**: If leaked, doesn't compromise your main account
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
### Required Fields
|
||||||
|
|
||||||
|
- **`nextcloud.url`**: Your Nextcloud server URL (must use HTTPS in production)
|
||||||
|
- **`nextcloud.username`**: Your Nextcloud username
|
||||||
|
- **`nextcloud.password`**: App password or regular password
|
||||||
|
|
||||||
|
### Optional Fields
|
||||||
|
|
||||||
|
- **`settings.maxRecentFolders`**: Number of recent folders to remember (default: 5)
|
||||||
|
- **`settings.uploadChunkSize`**: Upload chunk size in bytes (default: 10485760 = 10MB)
|
||||||
|
|
||||||
|
## Environment Variable Overrides
|
||||||
|
|
||||||
|
You can override any configuration value with environment variables (useful for CI/CD):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Override config values
|
||||||
|
export NEXTCLOUD_URL="https://cloud.example.com"
|
||||||
|
export NEXTCLOUD_USER="testuser"
|
||||||
|
export NEXTCLOUD_PASSWORD="app-password-here"
|
||||||
|
|
||||||
|
# Run your tests
|
||||||
|
./scripts/validate-config.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Environment variables always take precedence over `config.json`.
|
||||||
|
|
||||||
|
## Loading Configuration
|
||||||
|
|
||||||
|
To load configuration in your scripts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
source ./scripts/load-config.sh
|
||||||
|
|
||||||
|
# Now you have access to:
|
||||||
|
# - $NEXTCLOUD_URL
|
||||||
|
# - $NEXTCLOUD_USER
|
||||||
|
# - $NEXTCLOUD_PASSWORD
|
||||||
|
# - $MAX_RECENT_FOLDERS
|
||||||
|
# - $UPLOAD_CHUNK_SIZE
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validating Configuration
|
||||||
|
|
||||||
|
The validation script checks:
|
||||||
|
|
||||||
|
- ✅ All required fields are present
|
||||||
|
- ✅ URL format is valid
|
||||||
|
- ✅ HTTPS is used (warns if HTTP)
|
||||||
|
- ✅ Numeric settings are valid numbers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/validate-config.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- ⚠️ **Never commit `config.json`** - it's in `.gitignore`
|
||||||
|
- ⚠️ **Use HTTPS**, not HTTP
|
||||||
|
- ⚠️ **Use app passwords** when possible
|
||||||
|
- ⚠️ **Revoke app passwords** when done with a project
|
||||||
|
- ⚠️ Keep your `config.json` file permissions restricted:
|
||||||
|
```bash
|
||||||
|
chmod 600 config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multiple Test Accounts
|
||||||
|
|
||||||
|
If you have multiple Nextcloud instances for testing:
|
||||||
|
|
||||||
|
**Option 1: Multiple config files**
|
||||||
|
```bash
|
||||||
|
cp config.example.json config-tomusan.json
|
||||||
|
cp config.example.json config-disobedient.json
|
||||||
|
|
||||||
|
# Use with:
|
||||||
|
CONFIG_FILE=config-tomusan.json ./scripts/validate-config.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 2: Environment variables**
|
||||||
|
```bash
|
||||||
|
export NEXTCLOUD_URL="https://disobedient.cloud/nextcloud"
|
||||||
|
export NEXTCLOUD_USER="your-username"
|
||||||
|
export NEXTCLOUD_PASSWORD="xxxxx-xxxxx-xxxxx-xxxxx-xxxxx"
|
||||||
|
|
||||||
|
./scripts/validate-config.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "jq is not installed"
|
||||||
|
|
||||||
|
Install jq on your system:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ubuntu/Debian
|
||||||
|
sudo apt-get install jq
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
brew install jq
|
||||||
|
|
||||||
|
# Arch Linux
|
||||||
|
sudo pacman -S jq
|
||||||
|
```
|
||||||
|
|
||||||
|
### "NEXTCLOUD_URL must start with http:// or https://"
|
||||||
|
|
||||||
|
Make sure your URL includes the protocol:
|
||||||
|
- ✅ `https://cloud.tomusan.com`
|
||||||
|
- ❌ `cloud.tomusan.com`
|
||||||
|
|
||||||
|
### "Configuration is not valid"
|
||||||
|
|
||||||
|
Run with the validation script to see specific errors:
|
||||||
|
```bash
|
||||||
|
./scripts/validate-config.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD Configuration
|
||||||
|
|
||||||
|
For Gitea Actions or other CI systems, set secrets in your repository:
|
||||||
|
|
||||||
|
1. Go to repository **Settings → Secrets**
|
||||||
|
2. Add these secrets:
|
||||||
|
- `NEXTCLOUD_TEST_URL`
|
||||||
|
- `NEXTCLOUD_TEST_USER`
|
||||||
|
- `NEXTCLOUD_TEST_PASSWORD`
|
||||||
|
3. The CI will use environment variables instead of `config.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For more information, see [CONTRIBUTING.md](../CONTRIBUTING.md).
|
||||||
177
scripts/build-container.sh
Executable file
177
scripts/build-container.sh
Executable file
@@ -0,0 +1,177 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Build development containers for various platforms
|
||||||
|
# Usage: ./scripts/build-container.sh <platform> [version]
|
||||||
|
#
|
||||||
|
# Platforms: 3ds, switch, wiiu, wii, gamecube, nds, gba
|
||||||
|
# Version: Semantic version (default: 0.1.0)
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Print colored message
|
||||||
|
print_msg() {
|
||||||
|
local color=$1
|
||||||
|
shift
|
||||||
|
echo -e "${color}$*${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print usage
|
||||||
|
usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: $0 <platform> [version]
|
||||||
|
|
||||||
|
Build development container for specified platform.
|
||||||
|
|
||||||
|
Platforms:
|
||||||
|
3ds Nintendo 3DS (devkitARM)
|
||||||
|
switch Nintendo Switch (devkitA64) - Not yet implemented
|
||||||
|
wiiu Nintendo Wii U (devkitPPC) - Not yet implemented
|
||||||
|
wii Nintendo Wii (devkitPPC) - Not yet implemented
|
||||||
|
gamecube Nintendo GameCube (devkitPPC) - Not yet implemented
|
||||||
|
nds Nintendo DS (devkitARM) - Not yet implemented
|
||||||
|
gba Game Boy Advance (devkitARM) - Not yet implemented
|
||||||
|
|
||||||
|
Version:
|
||||||
|
Semantic version tag (default: 0.1.0)
|
||||||
|
Example: 0.1.0, 1.0.0, 0.2.0-beta
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$0 3ds # Build 3DS container with version 0.1.0
|
||||||
|
$0 3ds 0.2.0 # Build 3DS container with version 0.2.0
|
||||||
|
$0 switch # Build Switch container (when implemented)
|
||||||
|
|
||||||
|
Environment Variables:
|
||||||
|
CONTAINER_RUNTIME Container runtime to use (podman or docker, default: auto-detect)
|
||||||
|
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detect container runtime
|
||||||
|
detect_runtime() {
|
||||||
|
if [ -n "$CONTAINER_RUNTIME" ]; then
|
||||||
|
echo "$CONTAINER_RUNTIME"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v podman &> /dev/null; then
|
||||||
|
echo "podman"
|
||||||
|
elif command -v docker &> /dev/null; then
|
||||||
|
echo "docker"
|
||||||
|
else
|
||||||
|
print_msg "$RED" "Error: Neither podman nor docker found in PATH"
|
||||||
|
print_msg "$YELLOW" "Please install podman (recommended) or docker"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build container
|
||||||
|
build_container() {
|
||||||
|
local platform=$1
|
||||||
|
local version=$2
|
||||||
|
local dockerfile="docker/${platform}.Dockerfile"
|
||||||
|
local image_name="tomusan/devkitarm-${platform}"
|
||||||
|
|
||||||
|
if [ ! -f "$PROJECT_ROOT/$dockerfile" ]; then
|
||||||
|
print_msg "$RED" "Error: Dockerfile not found: $dockerfile"
|
||||||
|
print_msg "$YELLOW" "This platform may not be implemented yet."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local runtime
|
||||||
|
runtime=$(detect_runtime)
|
||||||
|
|
||||||
|
print_msg "$BLUE" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
print_msg "$GREEN" "Building ${platform} container"
|
||||||
|
print_msg "$BLUE" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo
|
||||||
|
print_msg "$YELLOW" "Platform: $platform"
|
||||||
|
print_msg "$YELLOW" "Version: $version"
|
||||||
|
print_msg "$YELLOW" "Runtime: $runtime"
|
||||||
|
print_msg "$YELLOW" "Dockerfile: $dockerfile"
|
||||||
|
print_msg "$YELLOW" "Image: ${image_name}:${version}"
|
||||||
|
print_msg "$YELLOW" " ${image_name}:latest"
|
||||||
|
echo
|
||||||
|
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
|
||||||
|
# Build with both version and latest tags
|
||||||
|
print_msg "$GREEN" "Starting build..."
|
||||||
|
echo
|
||||||
|
|
||||||
|
"$runtime" build \
|
||||||
|
-t "${image_name}:${version}" \
|
||||||
|
-t "${image_name}:latest" \
|
||||||
|
-f "$dockerfile" \
|
||||||
|
.
|
||||||
|
|
||||||
|
local exit_code=$?
|
||||||
|
|
||||||
|
if [ $exit_code -eq 0 ]; then
|
||||||
|
echo
|
||||||
|
print_msg "$BLUE" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
print_msg "$GREEN" "✓ Build successful!"
|
||||||
|
print_msg "$BLUE" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo
|
||||||
|
print_msg "$GREEN" "Tagged images:"
|
||||||
|
print_msg "$YELLOW" " ${image_name}:${version}"
|
||||||
|
print_msg "$YELLOW" " ${image_name}:latest"
|
||||||
|
echo
|
||||||
|
print_msg "$GREEN" "To use this container:"
|
||||||
|
print_msg "$YELLOW" " $runtime run --rm -v ./your-project:/project:z ${image_name}:latest make"
|
||||||
|
echo
|
||||||
|
print_msg "$GREEN" "For interactive shell:"
|
||||||
|
print_msg "$YELLOW" " $runtime run -it --rm -v ./your-project:/project:z ${image_name}:latest bash"
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
print_msg "$RED" "✗ Build failed with exit code $exit_code"
|
||||||
|
exit $exit_code
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main
|
||||||
|
main() {
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
local platform=$1
|
||||||
|
local version=${2:-0.1.0}
|
||||||
|
|
||||||
|
# Validate platform
|
||||||
|
case "$platform" in
|
||||||
|
3ds)
|
||||||
|
;;
|
||||||
|
switch|wiiu|wii|gamecube|nds|gba)
|
||||||
|
print_msg "$RED" "Error: Platform '$platform' is not yet implemented"
|
||||||
|
print_msg "$YELLOW" "Currently only '3ds' is available"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_msg "$RED" "Error: Unknown platform '$platform'"
|
||||||
|
echo
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Validate version format (basic semantic versioning)
|
||||||
|
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$ ]]; then
|
||||||
|
print_msg "$RED" "Error: Invalid version format '$version'"
|
||||||
|
print_msg "$YELLOW" "Expected format: X.Y.Z or X.Y.Z-label (e.g., 0.1.0, 1.0.0, 0.2.0-beta)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
build_container "$platform" "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
136
scripts/container-shell.sh
Executable file
136
scripts/container-shell.sh
Executable file
@@ -0,0 +1,136 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Open interactive shell in 3DS development container
|
||||||
|
# Usage: ./scripts/container-shell.sh [project-path] [podman-args...]
|
||||||
|
#
|
||||||
|
# All arguments after project-path are passed to podman/docker run before the container name
|
||||||
|
# This allows mounting additional volumes, setting environment variables, etc.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Print colored message
|
||||||
|
print_msg() {
|
||||||
|
local color=$1
|
||||||
|
shift
|
||||||
|
echo -e "${color}$*${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print usage
|
||||||
|
usage() {
|
||||||
|
cat << 'EOF'
|
||||||
|
Usage: ./scripts/container-shell.sh [project-path] [podman-args...]
|
||||||
|
|
||||||
|
Open interactive bash shell in 3DS development container with project mounted.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
project-path Path to project directory (default: current directory)
|
||||||
|
podman-args Additional arguments passed to podman/docker run
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
# Shell in current directory
|
||||||
|
./scripts/container-shell.sh
|
||||||
|
|
||||||
|
# Shell in specific project
|
||||||
|
./scripts/container-shell.sh ~/my-3ds-game
|
||||||
|
|
||||||
|
# With additional volume mount
|
||||||
|
./scripts/container-shell.sh ~/my-game -v /data:/data:z
|
||||||
|
|
||||||
|
# With environment variable
|
||||||
|
./scripts/container-shell.sh ~/my-game -e DEBUG=1
|
||||||
|
|
||||||
|
# Multiple extra arguments
|
||||||
|
./scripts/container-shell.sh ~/my-game -v /data:/data:z -e DEBUG=1 --network=host
|
||||||
|
|
||||||
|
Inside the container:
|
||||||
|
- Project is mounted at /project
|
||||||
|
- Working directory is /project
|
||||||
|
- All devkitARM tools are in PATH
|
||||||
|
- Run 'make' to build your project
|
||||||
|
- Run 'exit' or Ctrl+D to leave
|
||||||
|
|
||||||
|
Environment Variables:
|
||||||
|
CONTAINER_RUNTIME Container runtime to use (podman or docker, default: auto-detect)
|
||||||
|
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detect container runtime
|
||||||
|
detect_runtime() {
|
||||||
|
if [ -n "$CONTAINER_RUNTIME" ]; then
|
||||||
|
echo "$CONTAINER_RUNTIME"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v podman &> /dev/null; then
|
||||||
|
echo "podman"
|
||||||
|
elif command -v docker &> /dev/null; then
|
||||||
|
echo "docker"
|
||||||
|
else
|
||||||
|
print_msg "$RED" "Error: Neither podman nor docker found in PATH"
|
||||||
|
print_msg "$YELLOW" "Please install podman (recommended) or docker"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main
|
||||||
|
main() {
|
||||||
|
# Show help
|
||||||
|
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine project path
|
||||||
|
local project_path="${1:-.}"
|
||||||
|
shift || true # Remove first arg if present, don't fail if no args
|
||||||
|
|
||||||
|
# Resolve absolute path
|
||||||
|
project_path=$(cd "$project_path" 2>/dev/null && pwd) || {
|
||||||
|
print_msg "$RED" "Error: Project path does not exist: ${1:-.}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Collect remaining arguments for podman/docker run
|
||||||
|
local extra_args=("$@")
|
||||||
|
|
||||||
|
local runtime
|
||||||
|
runtime=$(detect_runtime)
|
||||||
|
|
||||||
|
local image_name="tomusan/devkitarm-3ds:latest"
|
||||||
|
|
||||||
|
print_msg "$BLUE" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
print_msg "$GREEN" "3DS Development Container Shell"
|
||||||
|
print_msg "$BLUE" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo
|
||||||
|
print_msg "$YELLOW" "Project: $project_path"
|
||||||
|
print_msg "$YELLOW" "Runtime: $runtime"
|
||||||
|
print_msg "$YELLOW" "Image: $image_name"
|
||||||
|
if [ ${#extra_args[@]} -gt 0 ]; then
|
||||||
|
print_msg "$YELLOW" "Extra: ${extra_args[*]}"
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
print_msg "$GREEN" "Type 'exit' or press Ctrl+D to leave the container"
|
||||||
|
echo
|
||||||
|
print_msg "$BLUE" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Run container with interactive shell
|
||||||
|
# Project mounted to /project, any extra args passed to podman/docker run
|
||||||
|
"$runtime" run -it --rm \
|
||||||
|
-v "$project_path:/project:z" \
|
||||||
|
"${extra_args[@]}" \
|
||||||
|
"$image_name" \
|
||||||
|
bash
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
69
scripts/load-config.sh
Executable file
69
scripts/load-config.sh
Executable file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Load configuration from JSON file and export as environment variables
|
||||||
|
# Environment variables take precedence over JSON values
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Default config file location
|
||||||
|
CONFIG_FILE="${CONFIG_FILE:-config.json}"
|
||||||
|
|
||||||
|
# Color output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to safely extract JSON values
|
||||||
|
get_json_value() {
|
||||||
|
local file="$1"
|
||||||
|
local key="$2"
|
||||||
|
local default="${3:-}"
|
||||||
|
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
value=$(jq -r "$key" "$file" 2>/dev/null || echo "null")
|
||||||
|
if [ "$value" = "null" ] || [ -z "$value" ]; then
|
||||||
|
echo "$default"
|
||||||
|
else
|
||||||
|
echo "$value"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$default"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if config file exists
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
echo -e "${YELLOW}Warning: $CONFIG_FILE not found${NC}" >&2
|
||||||
|
echo -e "${YELLOW}Using environment variables only${NC}" >&2
|
||||||
|
echo -e "${YELLOW}Copy config.example.json to config.json and fill in your credentials${NC}" >&2
|
||||||
|
echo "" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if jq is installed
|
||||||
|
if ! command -v jq &> /dev/null; then
|
||||||
|
echo -e "${RED}Error: jq is not installed${NC}" >&2
|
||||||
|
echo -e "${RED}Install it with: sudo apt-get install jq${NC}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load configuration values, preferring environment variables
|
||||||
|
export NEXTCLOUD_URL="${NEXTCLOUD_URL:-$(get_json_value "$CONFIG_FILE" '.nextcloud.url' '')}"
|
||||||
|
export NEXTCLOUD_USER="${NEXTCLOUD_USER:-$(get_json_value "$CONFIG_FILE" '.nextcloud.username' '')}"
|
||||||
|
export NEXTCLOUD_PASSWORD="${NEXTCLOUD_PASSWORD:-$(get_json_value "$CONFIG_FILE" '.nextcloud.password' '')}"
|
||||||
|
|
||||||
|
# Load settings
|
||||||
|
export MAX_RECENT_FOLDERS="${MAX_RECENT_FOLDERS:-$(get_json_value "$CONFIG_FILE" '.settings.maxRecentFolders' '5')}"
|
||||||
|
export UPLOAD_CHUNK_SIZE="${UPLOAD_CHUNK_SIZE:-$(get_json_value "$CONFIG_FILE" '.settings.uploadChunkSize' '10485760')}"
|
||||||
|
|
||||||
|
# Show what was loaded (without showing password)
|
||||||
|
if [ -n "$NEXTCLOUD_URL" ]; then
|
||||||
|
echo -e "${GREEN}✓${NC} NEXTCLOUD_URL: $NEXTCLOUD_URL"
|
||||||
|
fi
|
||||||
|
if [ -n "$NEXTCLOUD_USER" ]; then
|
||||||
|
echo -e "${GREEN}✓${NC} NEXTCLOUD_USER: $NEXTCLOUD_USER"
|
||||||
|
fi
|
||||||
|
if [ -n "$NEXTCLOUD_PASSWORD" ]; then
|
||||||
|
echo -e "${GREEN}✓${NC} NEXTCLOUD_PASSWORD: [set]"
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}✓${NC} MAX_RECENT_FOLDERS: $MAX_RECENT_FOLDERS"
|
||||||
|
echo -e "${GREEN}✓${NC} UPLOAD_CHUNK_SIZE: $UPLOAD_CHUNK_SIZE"
|
||||||
73
scripts/validate-config.sh
Executable file
73
scripts/validate-config.sh
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Validate that all required configuration is present
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Color output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Load configuration first
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/load-config.sh" > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Validation flags
|
||||||
|
VALID=true
|
||||||
|
|
||||||
|
# Validate required fields
|
||||||
|
if [ -z "$NEXTCLOUD_URL" ]; then
|
||||||
|
echo -e "${RED}✗ Error: NEXTCLOUD_URL is not set${NC}" >&2
|
||||||
|
VALID=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$NEXTCLOUD_USER" ]; then
|
||||||
|
echo -e "${RED}✗ Error: NEXTCLOUD_USER is not set${NC}" >&2
|
||||||
|
VALID=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$NEXTCLOUD_PASSWORD" ]; then
|
||||||
|
echo -e "${RED}✗ Error: NEXTCLOUD_PASSWORD is not set${NC}" >&2
|
||||||
|
VALID=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate URL format
|
||||||
|
if [ -n "$NEXTCLOUD_URL" ]; then
|
||||||
|
if [[ ! "$NEXTCLOUD_URL" =~ ^https?:// ]]; then
|
||||||
|
echo -e "${RED}✗ Error: NEXTCLOUD_URL must start with http:// or https://${NC}" >&2
|
||||||
|
VALID=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Warn if using HTTP instead of HTTPS
|
||||||
|
if [[ "$NEXTCLOUD_URL" =~ ^http:// ]]; then
|
||||||
|
echo -e "${RED}⚠ Warning: Using HTTP instead of HTTPS is insecure!${NC}" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate numeric settings
|
||||||
|
if ! [[ "$MAX_RECENT_FOLDERS" =~ ^[0-9]+$ ]]; then
|
||||||
|
echo -e "${RED}✗ Error: MAX_RECENT_FOLDERS must be a number${NC}" >&2
|
||||||
|
VALID=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "$UPLOAD_CHUNK_SIZE" =~ ^[0-9]+$ ]]; then
|
||||||
|
echo -e "${RED}✗ Error: UPLOAD_CHUNK_SIZE must be a number${NC}" >&2
|
||||||
|
VALID=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Exit with appropriate code
|
||||||
|
if [ "$VALID" = true ]; then
|
||||||
|
echo -e "${GREEN}✓ Configuration is valid${NC}"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "Please set the missing configuration values in:"
|
||||||
|
echo " 1. config.json (copy from config.example.json), or"
|
||||||
|
echo " 2. Environment variables"
|
||||||
|
echo ""
|
||||||
|
echo "Required environment variables:"
|
||||||
|
echo " - NEXTCLOUD_URL"
|
||||||
|
echo " - NEXTCLOUD_USER"
|
||||||
|
echo " - NEXTCLOUD_PASSWORD"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
131
shared/CMakeLists.txt
Normal file
131
shared/CMakeLists.txt
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
project(libnextcloud VERSION 0.1.0 LANGUAGES CXX)
|
||||||
|
|
||||||
|
# C++17 required
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
# Options
|
||||||
|
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||||
|
option(BUILD_TESTING "Build tests" ON)
|
||||||
|
option(BUILD_EXAMPLES "Build examples" OFF)
|
||||||
|
|
||||||
|
# Library sources (currently empty, will be populated as we implement components)
|
||||||
|
set(NEXTCLOUD_SOURCES
|
||||||
|
# src/webdav_client.cpp # Will be added in future
|
||||||
|
# src/auth.cpp # Will be added in future
|
||||||
|
# src/upload_manager.cpp # Will be added in future
|
||||||
|
# src/folder_manager.cpp # Will be added in future
|
||||||
|
# src/config.cpp # Will be added in future
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create library target
|
||||||
|
# For now, it's header-only (version.hpp and types.hpp)
|
||||||
|
# We'll add sources when we implement components
|
||||||
|
add_library(nextcloud INTERFACE)
|
||||||
|
|
||||||
|
# Include directories
|
||||||
|
target_include_directories(nextcloud
|
||||||
|
INTERFACE
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
|
||||||
|
# Require C++17 for consumers
|
||||||
|
target_compile_features(nextcloud INTERFACE cxx_std_17)
|
||||||
|
|
||||||
|
# Dependencies (will be uncommented as we add implementation)
|
||||||
|
# find_package(CURL REQUIRED)
|
||||||
|
# find_package(MbedTLS REQUIRED)
|
||||||
|
# find_package(tinyxml2 REQUIRED)
|
||||||
|
|
||||||
|
# Link libraries (commented out until we have actual implementation)
|
||||||
|
# target_link_libraries(nextcloud
|
||||||
|
# PUBLIC
|
||||||
|
# CURL::libcurl
|
||||||
|
# MbedTLS::mbedtls
|
||||||
|
# tinyxml2::tinyxml2
|
||||||
|
# )
|
||||||
|
|
||||||
|
# Compiler warnings
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(nextcloud INTERFACE /W4)
|
||||||
|
else()
|
||||||
|
target_compile_options(nextcloud INTERFACE -Wall -Wextra -Wpedantic)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
if(BUILD_EXAMPLES)
|
||||||
|
add_subdirectory(examples)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Install rules
|
||||||
|
install(TARGETS nextcloud
|
||||||
|
EXPORT nextcloudTargets
|
||||||
|
LIBRARY DESTINATION lib
|
||||||
|
ARCHIVE DESTINATION lib
|
||||||
|
RUNTIME DESTINATION bin
|
||||||
|
INCLUDES DESTINATION include
|
||||||
|
)
|
||||||
|
|
||||||
|
install(DIRECTORY include/
|
||||||
|
DESTINATION include
|
||||||
|
FILES_MATCHING PATTERN "*.hpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generate and install CMake config files
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
|
write_basic_package_version_file(
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/nextcloudConfigVersion.cmake"
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
COMPATIBILITY AnyNewerVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_package_config_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/nextcloudConfig.cmake.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/nextcloudConfig.cmake"
|
||||||
|
INSTALL_DESTINATION lib/cmake/nextcloud
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/nextcloudConfig.cmake"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/nextcloudConfigVersion.cmake"
|
||||||
|
DESTINATION lib/cmake/nextcloud
|
||||||
|
)
|
||||||
|
|
||||||
|
install(EXPORT nextcloudTargets
|
||||||
|
FILE nextcloudTargets.cmake
|
||||||
|
NAMESPACE nextcloud::
|
||||||
|
DESTINATION lib/cmake/nextcloud
|
||||||
|
)
|
||||||
|
|
||||||
|
# pkg-config file
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/libnextcloud.pc.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/libnextcloud.pc"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnextcloud.pc"
|
||||||
|
DESTINATION lib/pkgconfig
|
||||||
|
)
|
||||||
|
|
||||||
|
# Print configuration summary
|
||||||
|
message(STATUS "")
|
||||||
|
message(STATUS "libnextcloud Configuration Summary:")
|
||||||
|
message(STATUS " Version: ${PROJECT_VERSION}")
|
||||||
|
message(STATUS " Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
message(STATUS " C++ standard: C++${CMAKE_CXX_STANDARD}")
|
||||||
|
message(STATUS " Shared libraries: ${BUILD_SHARED_LIBS}")
|
||||||
|
message(STATUS " Build tests: ${BUILD_TESTING}")
|
||||||
|
message(STATUS " Build examples: ${BUILD_EXAMPLES}")
|
||||||
|
message(STATUS " Install prefix: ${CMAKE_INSTALL_PREFIX}")
|
||||||
|
message(STATUS "")
|
||||||
288
shared/README.md
Normal file
288
shared/README.md
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
# libnextcloud
|
||||||
|
|
||||||
|
Cross-platform C++17 library for Nextcloud connectivity on embedded devices and homebrew platforms.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`libnextcloud` provides a clean, modern C++ API for interacting with Nextcloud servers. It's designed to work on resource-constrained devices like game console homebrew (Nintendo 3DS, Switch, Wii U) while also supporting desktop platforms.
|
||||||
|
|
||||||
|
**Status**: 🚧 In Development - Scaffolding complete, components being implemented
|
||||||
|
|
||||||
|
## Features (Planned)
|
||||||
|
|
||||||
|
- ✅ **Version Management**: Semantic versioning with compile-time and runtime queries
|
||||||
|
- ✅ **Type System**: Comprehensive error codes and data structures
|
||||||
|
- 🚧 **WebDAV Client**: HTTP/HTTPS communication with Nextcloud WebDAV API
|
||||||
|
- 🚧 **Authentication**: Basic Auth (OAuth2 planned)
|
||||||
|
- 🚧 **File Upload**: Streaming uploads with chunking and progress tracking
|
||||||
|
- 🚧 **Folder Management**: List, create, navigate remote folders
|
||||||
|
- 🚧 **Favorites & Recent**: Persistent user preferences
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Build Tools
|
||||||
|
- **CMake** 3.15 or later
|
||||||
|
- **C++17 compiler**: GCC 7+, Clang 5+, MSVC 2017+
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
**Required** (when implementation complete):
|
||||||
|
- libcurl 7.68+
|
||||||
|
- mbedTLS 2.16+
|
||||||
|
- tinyxml2 9.0+
|
||||||
|
|
||||||
|
**Testing**:
|
||||||
|
- Google Test 1.11+
|
||||||
|
|
||||||
|
**Header-Only**:
|
||||||
|
- nlohmann/json 3.10+ (for configuration, future)
|
||||||
|
|
||||||
|
### Platform Support
|
||||||
|
|
||||||
|
| Platform | Status | Notes |
|
||||||
|
|----------|--------|-------|
|
||||||
|
| Linux | ✅ Development | Primary development platform |
|
||||||
|
| Nintendo 3DS | ✅ Planned | Via devkitARM in container |
|
||||||
|
| Nintendo Switch | 🔜 Planned | |
|
||||||
|
| Wii U | 🔜 Planned | |
|
||||||
|
| PlayStation Vita | 🔜 Planned | |
|
||||||
|
| Windows | 🔜 Planned | |
|
||||||
|
| macOS | 🔜 Planned | |
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Configure (modern CMake style)
|
||||||
|
cmake -S . -B build
|
||||||
|
|
||||||
|
# Build
|
||||||
|
cmake --build build
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
cmake --build build --target test
|
||||||
|
# or with CTest for detailed output:
|
||||||
|
# ctest --test-dir build --output-on-failure
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using in Your Project
|
||||||
|
|
||||||
|
#### CMake
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
find_package(nextcloud REQUIRED)
|
||||||
|
|
||||||
|
add_executable(my_app main.cpp)
|
||||||
|
target_link_libraries(my_app nextcloud::nextcloud)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### pkg-config
|
||||||
|
|
||||||
|
```bash
|
||||||
|
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs libnextcloud)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <nextcloud/version.hpp>
|
||||||
|
#include <nextcloud/types.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Get library version
|
||||||
|
std::cout << "libnextcloud v" << nextcloud::getVersionString() << std::endl;
|
||||||
|
|
||||||
|
// Check version compatibility
|
||||||
|
if (nextcloud::isVersionAtLeast(0, 1, 0)) {
|
||||||
|
std::cout << "Version is compatible!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use error types
|
||||||
|
nextcloud::NextcloudError error = nextcloud::NextcloudError::Success;
|
||||||
|
std::cout << "Status: " << nextcloud::errorToString(error) << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
shared/
|
||||||
|
├── CMakeLists.txt # Build configuration
|
||||||
|
├── README.md # This file
|
||||||
|
├── libnextcloud.pc.in # pkg-config template
|
||||||
|
├── nextcloudConfig.cmake.in # CMake config template
|
||||||
|
├── include/
|
||||||
|
│ └── nextcloud/
|
||||||
|
│ ├── version.hpp # Version information
|
||||||
|
│ ├── types.hpp # Common types and errors
|
||||||
|
│ ├── webdav_client.hpp # (Coming soon)
|
||||||
|
│ ├── auth.hpp # (Coming soon)
|
||||||
|
│ ├── upload_manager.hpp # (Coming soon)
|
||||||
|
│ ├── folder_manager.hpp # (Coming soon)
|
||||||
|
│ └── config.hpp # (Coming soon)
|
||||||
|
├── src/ # Implementation files (future)
|
||||||
|
├── tests/
|
||||||
|
│ ├── CMakeLists.txt
|
||||||
|
│ └── smoke_test.cpp # Basic tests
|
||||||
|
├── examples/ # Usage examples (future)
|
||||||
|
└── docs/ # Documentation (future)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building for Platforms
|
||||||
|
|
||||||
|
### Linux (Development)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nintendo 3DS (Container)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From project root
|
||||||
|
podman run --rm -v .:/project:z tomusan/devkitarm-3ds:latest \
|
||||||
|
bash -c "cd /project/shared && mkdir -p build && cd build && cmake .. && make"
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
|
||||||
|
### Version Information
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <nextcloud/version.hpp>
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
nextcloud::VERSION_MAJOR // 0
|
||||||
|
nextcloud::VERSION_MINOR // 1
|
||||||
|
nextcloud::VERSION_PATCH // 0
|
||||||
|
nextcloud::VERSION_STRING // "0.1.0"
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
std::string getVersionString();
|
||||||
|
void getVersion(int& major, int& minor, int& patch);
|
||||||
|
bool isVersionAtLeast(int major, int minor, int patch);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <nextcloud/types.hpp>
|
||||||
|
|
||||||
|
// Error codes
|
||||||
|
enum class NextcloudError {
|
||||||
|
Success,
|
||||||
|
NetworkError,
|
||||||
|
AuthenticationFailed,
|
||||||
|
ServerError,
|
||||||
|
FileNotFound,
|
||||||
|
// ... more error codes
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert to string
|
||||||
|
const char* errorToString(NextcloudError error);
|
||||||
|
|
||||||
|
// Result type
|
||||||
|
nextcloud::Result<int> result;
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
// Use result.value
|
||||||
|
} else {
|
||||||
|
std::cerr << result.errorMessage() << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Structures
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Upload progress
|
||||||
|
struct UploadProgress {
|
||||||
|
std::string filename;
|
||||||
|
uint64_t bytesUploaded;
|
||||||
|
uint64_t totalBytes;
|
||||||
|
float percentComplete;
|
||||||
|
float bytesPerSecond;
|
||||||
|
int secondsRemaining;
|
||||||
|
};
|
||||||
|
|
||||||
|
// File information
|
||||||
|
struct FileInfo {
|
||||||
|
std::string name;
|
||||||
|
std::string path;
|
||||||
|
bool isDirectory;
|
||||||
|
uint64_t size;
|
||||||
|
std::time_t modifiedTime;
|
||||||
|
std::string contentType;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Folder information
|
||||||
|
struct FolderInfo {
|
||||||
|
std::string path;
|
||||||
|
std::string displayName;
|
||||||
|
std::time_t lastUsed;
|
||||||
|
bool isFavorite;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
cd build
|
||||||
|
ctest --output-on-failure
|
||||||
|
|
||||||
|
# Or use the custom target
|
||||||
|
make run_tests
|
||||||
|
|
||||||
|
# Run specific test
|
||||||
|
./tests/smoke_test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Status
|
||||||
|
|
||||||
|
- ✅ **Phase 1: Scaffolding** - Complete
|
||||||
|
- Directory structure
|
||||||
|
- CMake build system
|
||||||
|
- Version and types headers
|
||||||
|
- Google Test integration
|
||||||
|
- Basic smoke tests
|
||||||
|
|
||||||
|
- 🚧 **Phase 2: WebDAV Client** - Not started
|
||||||
|
- Issue #8
|
||||||
|
|
||||||
|
- 🚧 **Phase 3: Authentication** - Not started
|
||||||
|
- Issue #9
|
||||||
|
|
||||||
|
- 🚧 **Phase 4: Upload Manager** - Not started
|
||||||
|
- Issue #10
|
||||||
|
|
||||||
|
- 🚧 **Phase 5: Folder Management** - Not started
|
||||||
|
- Issue #11
|
||||||
|
|
||||||
|
- 🚧 **Phase 6: Favorites & Recent** - Not started
|
||||||
|
- Issue #12
|
||||||
|
|
||||||
|
- 🚧 **Phase 7: Testing & Docs** - Not started
|
||||||
|
- Issue #13
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
See [CONTRIBUTING.md](../CONTRIBUTING.md) in the project root.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[License TBD - to be determined]
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- [Project Plan](../.plans/CreateLibNextcloud.md)
|
||||||
|
- [Issue #25](https://git.tomusan.com/maj/nextcloud-share/issues/25) - Initialize libnextcloud
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Note**: This library is in active development. The API is subject to change until version 1.0.0.
|
||||||
176
shared/include/nextcloud/types.hpp
Normal file
176
shared/include/nextcloud/types.hpp
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#ifndef NEXTCLOUD_TYPES_HPP
|
||||||
|
#define NEXTCLOUD_TYPES_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
namespace nextcloud {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Error codes for Nextcloud operations
|
||||||
|
*/
|
||||||
|
enum class NextcloudError {
|
||||||
|
Success = 0,
|
||||||
|
|
||||||
|
// Network errors
|
||||||
|
NetworkError,
|
||||||
|
ConnectionFailed,
|
||||||
|
Timeout,
|
||||||
|
SSLError,
|
||||||
|
|
||||||
|
// Authentication errors
|
||||||
|
AuthenticationFailed,
|
||||||
|
InvalidCredentials,
|
||||||
|
Unauthorized,
|
||||||
|
|
||||||
|
// Server errors
|
||||||
|
ServerError,
|
||||||
|
ServiceUnavailable,
|
||||||
|
InsufficientStorage,
|
||||||
|
|
||||||
|
// File/path errors
|
||||||
|
FileNotFound,
|
||||||
|
PathNotFound,
|
||||||
|
InvalidPath,
|
||||||
|
FileAlreadyExists,
|
||||||
|
PathAlreadyExists,
|
||||||
|
|
||||||
|
// Operation errors
|
||||||
|
OperationFailed,
|
||||||
|
OperationCancelled,
|
||||||
|
InvalidOperation,
|
||||||
|
|
||||||
|
// Parsing/format errors
|
||||||
|
ParseError,
|
||||||
|
InvalidResponse,
|
||||||
|
UnsupportedFormat,
|
||||||
|
|
||||||
|
// Configuration errors
|
||||||
|
InvalidConfiguration,
|
||||||
|
MissingConfiguration,
|
||||||
|
|
||||||
|
// Unknown
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert error code to human-readable string
|
||||||
|
* @param error The error code
|
||||||
|
* @return String description of the error
|
||||||
|
*/
|
||||||
|
inline const char* errorToString(NextcloudError error) {
|
||||||
|
switch (error) {
|
||||||
|
case NextcloudError::Success: return "Success";
|
||||||
|
case NextcloudError::NetworkError: return "Network error";
|
||||||
|
case NextcloudError::ConnectionFailed: return "Connection failed";
|
||||||
|
case NextcloudError::Timeout: return "Operation timed out";
|
||||||
|
case NextcloudError::SSLError: return "SSL/TLS error";
|
||||||
|
case NextcloudError::AuthenticationFailed: return "Authentication failed";
|
||||||
|
case NextcloudError::InvalidCredentials: return "Invalid credentials";
|
||||||
|
case NextcloudError::Unauthorized: return "Unauthorized";
|
||||||
|
case NextcloudError::ServerError: return "Server error";
|
||||||
|
case NextcloudError::ServiceUnavailable: return "Service unavailable";
|
||||||
|
case NextcloudError::InsufficientStorage: return "Insufficient storage";
|
||||||
|
case NextcloudError::FileNotFound: return "File not found";
|
||||||
|
case NextcloudError::PathNotFound: return "Path not found";
|
||||||
|
case NextcloudError::InvalidPath: return "Invalid path";
|
||||||
|
case NextcloudError::FileAlreadyExists: return "File already exists";
|
||||||
|
case NextcloudError::PathAlreadyExists: return "Path already exists";
|
||||||
|
case NextcloudError::OperationFailed: return "Operation failed";
|
||||||
|
case NextcloudError::OperationCancelled: return "Operation cancelled";
|
||||||
|
case NextcloudError::InvalidOperation: return "Invalid operation";
|
||||||
|
case NextcloudError::ParseError: return "Parse error";
|
||||||
|
case NextcloudError::InvalidResponse: return "Invalid response";
|
||||||
|
case NextcloudError::UnsupportedFormat: return "Unsupported format";
|
||||||
|
case NextcloudError::InvalidConfiguration: return "Invalid configuration";
|
||||||
|
case NextcloudError::MissingConfiguration: return "Missing configuration";
|
||||||
|
case NextcloudError::Unknown: return "Unknown error";
|
||||||
|
default: return "Unrecognized error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Result type for operations that can fail
|
||||||
|
* @tparam T The success value type
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct Result {
|
||||||
|
NextcloudError error;
|
||||||
|
T value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the operation was successful
|
||||||
|
* @return True if no error occurred
|
||||||
|
*/
|
||||||
|
bool isSuccess() const { return error == NextcloudError::Success; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if an error occurred
|
||||||
|
* @return True if an error occurred
|
||||||
|
*/
|
||||||
|
bool isError() const { return error != NextcloudError::Success; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get error message
|
||||||
|
* @return Human-readable error description
|
||||||
|
*/
|
||||||
|
const char* errorMessage() const { return errorToString(error); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Upload progress information
|
||||||
|
*/
|
||||||
|
struct UploadProgress {
|
||||||
|
std::string filename; ///< Name of file being uploaded
|
||||||
|
uint64_t bytesUploaded; ///< Number of bytes uploaded so far
|
||||||
|
uint64_t totalBytes; ///< Total file size in bytes
|
||||||
|
float percentComplete; ///< Completion percentage (0.0 - 100.0)
|
||||||
|
float bytesPerSecond; ///< Current transfer speed
|
||||||
|
int secondsRemaining; ///< Estimated seconds remaining (-1 if unknown)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Information about a file or directory
|
||||||
|
*/
|
||||||
|
struct FileInfo {
|
||||||
|
std::string name; ///< File or directory name
|
||||||
|
std::string path; ///< Full path on server
|
||||||
|
bool isDirectory; ///< True if this is a directory
|
||||||
|
uint64_t size; ///< File size in bytes (0 for directories)
|
||||||
|
std::time_t modifiedTime; ///< Last modification time (Unix timestamp)
|
||||||
|
std::string contentType; ///< MIME type (e.g., "image/png")
|
||||||
|
std::string etag; ///< ETag for change detection
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Information about a favorite or recent folder
|
||||||
|
*/
|
||||||
|
struct FolderInfo {
|
||||||
|
std::string path; ///< Full path on server
|
||||||
|
std::string displayName; ///< User-friendly name
|
||||||
|
std::time_t lastUsed; ///< Last access time (Unix timestamp)
|
||||||
|
bool isFavorite; ///< True if marked as favorite
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HTTP response information
|
||||||
|
*/
|
||||||
|
struct HttpResponse {
|
||||||
|
int statusCode; ///< HTTP status code (e.g., 200, 404)
|
||||||
|
std::string body; ///< Response body
|
||||||
|
std::string contentType; ///< Content-Type header value
|
||||||
|
NextcloudError error; ///< Error code if request failed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if response indicates success (2xx status code)
|
||||||
|
* @return True if status code is 200-299
|
||||||
|
*/
|
||||||
|
bool isSuccess() const {
|
||||||
|
return statusCode >= 200 && statusCode < 300 && error == NextcloudError::Success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace nextcloud
|
||||||
|
|
||||||
|
#endif // NEXTCLOUD_TYPES_HPP
|
||||||
57
shared/include/nextcloud/version.hpp
Normal file
57
shared/include/nextcloud/version.hpp
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#ifndef NEXTCLOUD_VERSION_HPP
|
||||||
|
#define NEXTCLOUD_VERSION_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace nextcloud {
|
||||||
|
|
||||||
|
// Semantic versioning
|
||||||
|
constexpr int VERSION_MAJOR = 0;
|
||||||
|
constexpr int VERSION_MINOR = 1;
|
||||||
|
constexpr int VERSION_PATCH = 0;
|
||||||
|
|
||||||
|
// Build metadata
|
||||||
|
constexpr const char* VERSION_STRING = "0.1.0";
|
||||||
|
constexpr const char* BUILD_DATE = __DATE__;
|
||||||
|
constexpr const char* BUILD_TIME = __TIME__;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the library version as a string
|
||||||
|
* @return Version string in format "MAJOR.MINOR.PATCH"
|
||||||
|
*/
|
||||||
|
inline std::string getVersionString() {
|
||||||
|
return VERSION_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the library version components
|
||||||
|
* @param major Output parameter for major version
|
||||||
|
* @param minor Output parameter for minor version
|
||||||
|
* @param patch Output parameter for patch version
|
||||||
|
*/
|
||||||
|
inline void getVersion(int& major, int& minor, int& patch) {
|
||||||
|
major = VERSION_MAJOR;
|
||||||
|
minor = VERSION_MINOR;
|
||||||
|
patch = VERSION_PATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the library version is at least the specified version
|
||||||
|
* @param major Required major version
|
||||||
|
* @param minor Required minor version
|
||||||
|
* @param patch Required patch version
|
||||||
|
* @return True if current version >= required version
|
||||||
|
*/
|
||||||
|
inline bool isVersionAtLeast(int major, int minor, int patch) {
|
||||||
|
if (VERSION_MAJOR > major) return true;
|
||||||
|
if (VERSION_MAJOR < major) return false;
|
||||||
|
|
||||||
|
if (VERSION_MINOR > minor) return true;
|
||||||
|
if (VERSION_MINOR < minor) return false;
|
||||||
|
|
||||||
|
return VERSION_PATCH >= patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nextcloud
|
||||||
|
|
||||||
|
#endif // NEXTCLOUD_VERSION_HPP
|
||||||
11
shared/libnextcloud.pc.in
Normal file
11
shared/libnextcloud.pc.in
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
|
exec_prefix=${prefix}
|
||||||
|
libdir=${exec_prefix}/lib
|
||||||
|
includedir=${prefix}/include
|
||||||
|
|
||||||
|
Name: libnextcloud
|
||||||
|
Description: Cross-platform C++ library for Nextcloud connectivity
|
||||||
|
Version: @PROJECT_VERSION@
|
||||||
|
Requires:
|
||||||
|
Cflags: -I${includedir}
|
||||||
|
Libs: -L${libdir} -lnextcloud
|
||||||
5
shared/nextcloudConfig.cmake.in
Normal file
5
shared/nextcloudConfig.cmake.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/nextcloudTargets.cmake")
|
||||||
|
|
||||||
|
check_required_components(nextcloud)
|
||||||
30
shared/tests/CMakeLists.txt
Normal file
30
shared/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
|
# Find Google Test
|
||||||
|
find_package(GTest REQUIRED)
|
||||||
|
|
||||||
|
# Include Google Test helpers
|
||||||
|
include(GoogleTest)
|
||||||
|
|
||||||
|
# Test executable
|
||||||
|
add_executable(smoke_test
|
||||||
|
smoke_test.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Link against the library and GTest
|
||||||
|
target_link_libraries(smoke_test
|
||||||
|
PRIVATE
|
||||||
|
nextcloud
|
||||||
|
GTest::GTest
|
||||||
|
GTest::Main
|
||||||
|
)
|
||||||
|
|
||||||
|
# Discover and register tests
|
||||||
|
gtest_discover_tests(smoke_test)
|
||||||
|
|
||||||
|
# Add custom target to run tests
|
||||||
|
add_custom_target(run_tests
|
||||||
|
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
|
||||||
|
DEPENDS smoke_test
|
||||||
|
COMMENT "Running tests..."
|
||||||
|
)
|
||||||
140
shared/tests/smoke_test.cpp
Normal file
140
shared/tests/smoke_test.cpp
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <nextcloud/version.hpp>
|
||||||
|
#include <nextcloud/types.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file smoke_test.cpp
|
||||||
|
* @brief Basic smoke tests to verify library setup
|
||||||
|
*
|
||||||
|
* These tests ensure that the library headers compile correctly
|
||||||
|
* and basic functionality works as expected.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Test version information
|
||||||
|
TEST(VersionTest, VersionConstants) {
|
||||||
|
EXPECT_EQ(nextcloud::VERSION_MAJOR, 0);
|
||||||
|
EXPECT_EQ(nextcloud::VERSION_MINOR, 1);
|
||||||
|
EXPECT_EQ(nextcloud::VERSION_PATCH, 0);
|
||||||
|
EXPECT_STREQ(nextcloud::VERSION_STRING, "0.1.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VersionTest, GetVersionString) {
|
||||||
|
std::string version = nextcloud::getVersionString();
|
||||||
|
EXPECT_EQ(version, "0.1.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VersionTest, GetVersionComponents) {
|
||||||
|
int major, minor, patch;
|
||||||
|
nextcloud::getVersion(major, minor, patch);
|
||||||
|
EXPECT_EQ(major, 0);
|
||||||
|
EXPECT_EQ(minor, 1);
|
||||||
|
EXPECT_EQ(patch, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VersionTest, IsVersionAtLeast) {
|
||||||
|
// Current version is 0.1.0
|
||||||
|
EXPECT_TRUE(nextcloud::isVersionAtLeast(0, 0, 0));
|
||||||
|
EXPECT_TRUE(nextcloud::isVersionAtLeast(0, 1, 0));
|
||||||
|
EXPECT_FALSE(nextcloud::isVersionAtLeast(0, 2, 0));
|
||||||
|
EXPECT_FALSE(nextcloud::isVersionAtLeast(1, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test error types
|
||||||
|
TEST(TypesTest, ErrorToString) {
|
||||||
|
EXPECT_STREQ(nextcloud::errorToString(nextcloud::NextcloudError::Success), "Success");
|
||||||
|
EXPECT_STREQ(nextcloud::errorToString(nextcloud::NextcloudError::NetworkError), "Network error");
|
||||||
|
EXPECT_STREQ(nextcloud::errorToString(nextcloud::NextcloudError::AuthenticationFailed), "Authentication failed");
|
||||||
|
EXPECT_STREQ(nextcloud::errorToString(nextcloud::NextcloudError::FileNotFound), "File not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TypesTest, ResultSuccess) {
|
||||||
|
nextcloud::Result<int> result;
|
||||||
|
result.error = nextcloud::NextcloudError::Success;
|
||||||
|
result.value = 42;
|
||||||
|
|
||||||
|
EXPECT_TRUE(result.isSuccess());
|
||||||
|
EXPECT_FALSE(result.isError());
|
||||||
|
EXPECT_EQ(result.value, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TypesTest, ResultError) {
|
||||||
|
nextcloud::Result<std::string> result;
|
||||||
|
result.error = nextcloud::NextcloudError::NetworkError;
|
||||||
|
result.value = "";
|
||||||
|
|
||||||
|
EXPECT_FALSE(result.isSuccess());
|
||||||
|
EXPECT_TRUE(result.isError());
|
||||||
|
EXPECT_STREQ(result.errorMessage(), "Network error");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TypesTest, UploadProgressStruct) {
|
||||||
|
nextcloud::UploadProgress progress;
|
||||||
|
progress.filename = "test.txt";
|
||||||
|
progress.bytesUploaded = 50;
|
||||||
|
progress.totalBytes = 100;
|
||||||
|
progress.percentComplete = 50.0f;
|
||||||
|
progress.bytesPerSecond = 1024.0f;
|
||||||
|
progress.secondsRemaining = 1;
|
||||||
|
|
||||||
|
EXPECT_EQ(progress.filename, "test.txt");
|
||||||
|
EXPECT_EQ(progress.bytesUploaded, 50);
|
||||||
|
EXPECT_EQ(progress.totalBytes, 100);
|
||||||
|
EXPECT_FLOAT_EQ(progress.percentComplete, 50.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TypesTest, FileInfoStruct) {
|
||||||
|
nextcloud::FileInfo file;
|
||||||
|
file.name = "document.pdf";
|
||||||
|
file.path = "/Documents/document.pdf";
|
||||||
|
file.isDirectory = false;
|
||||||
|
file.size = 1024;
|
||||||
|
file.modifiedTime = 1234567890;
|
||||||
|
file.contentType = "application/pdf";
|
||||||
|
|
||||||
|
EXPECT_EQ(file.name, "document.pdf");
|
||||||
|
EXPECT_EQ(file.path, "/Documents/document.pdf");
|
||||||
|
EXPECT_FALSE(file.isDirectory);
|
||||||
|
EXPECT_EQ(file.size, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TypesTest, FolderInfoStruct) {
|
||||||
|
nextcloud::FolderInfo folder;
|
||||||
|
folder.path = "/Photos";
|
||||||
|
folder.displayName = "My Photos";
|
||||||
|
folder.lastUsed = 1234567890;
|
||||||
|
folder.isFavorite = true;
|
||||||
|
|
||||||
|
EXPECT_EQ(folder.path, "/Photos");
|
||||||
|
EXPECT_EQ(folder.displayName, "My Photos");
|
||||||
|
EXPECT_TRUE(folder.isFavorite);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TypesTest, HttpResponseSuccess) {
|
||||||
|
nextcloud::HttpResponse response;
|
||||||
|
response.statusCode = 200;
|
||||||
|
response.body = "OK";
|
||||||
|
response.contentType = "text/plain";
|
||||||
|
response.error = nextcloud::NextcloudError::Success;
|
||||||
|
|
||||||
|
EXPECT_TRUE(response.isSuccess());
|
||||||
|
EXPECT_EQ(response.statusCode, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TypesTest, HttpResponseError) {
|
||||||
|
nextcloud::HttpResponse response;
|
||||||
|
response.statusCode = 404;
|
||||||
|
response.body = "Not Found";
|
||||||
|
response.error = nextcloud::NextcloudError::FileNotFound;
|
||||||
|
|
||||||
|
EXPECT_FALSE(response.isSuccess());
|
||||||
|
EXPECT_EQ(response.statusCode, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user