MyPortfolio
One desktop catalog for every app you ship. MyPortfolio reads your GitHub releases and gives you a single Windows app to install desktop binaries, load Chromium extensions, and pull Android APKs across to your PC.
MyPortfolio replaces three separate stores — LocalDesktopStore, LocalChromeStore, and LocalAndroidStore — with one shell. The Desktop and Chrome tabs install and uninstall like before. The Android tab is download-only on the Windows host: it pulls every .apk you publish straight into your local download folder so you can sideload it onto a device yourself.
What's in the app
| Tab | Source | What it does |
|---|---|---|
| Desktop apps | .msi, .exe (Inno / NSIS / generic), .zip portable |
Install, run, and uninstall — silent installers via msiexec /qb, /SILENT /NORESTART, /S, or extract-and-shortcut for portable ZIPs. SHA-256 sidecar verification. |
| Chrome extensions | .zip or .crx |
Download, extract (zip-slip guarded, CRX2/CRX3 header strip), then launch Chrome / Brave / Edge / Vivaldi / Opera with --load-extension="path1,path2,...". |
| Android APKs | .apk |
Download to %USERPROFILE%\Downloads\MyPortfolio\Android\<owner>\<repo>\<version>\ with hash verification. Read package name, version name, and version code from the APK manifest when available. Reveal in Explorer when you're ready to sideload. |
One settings drawer drives all three tabs — same GitHub user, same PAT, optional extra collaborator owners, shared Mocha / Latte appearance, accent color, separate topic filter / verification toggles per tab.
Why one app instead of three
Three separate stores, three separate setups, three separate logs. Same GitHub user every time. The only thing that varied per store was which release asset to pick and what to do with it. So:
- Shared shell — one window, Catppuccin Mocha / Latte themes, one activity log feeding from every tab, one settings drawer.
- Tabbed surface — switch between Desktop / Chrome / Android with a click; per-tab refresh button + per-tab "Refresh all" trigger from the header.
- One on-disk identity —
%APPDATA%\MyPortfolio\settings.json. Per-tab manifests for installed/downloaded state.
Install
From release (recommended)
- Grab
MyPortfolio-vX.Y.Z-win-x64.zipfrom the Releases page. - Verify SHA-256 against the
.sha256.txtsidecar. - Extract anywhere. Run
MyPortfolio.exe.
Requires the .NET 9 Desktop Runtime — Windows x64 Desktop Runtime installer.
From source
git clone https://github.com/SysAdminDoc/MyPortfolio.git
cd MyPortfolio
dotnet build src/MyPortfolio/MyPortfolio.csproj -c Release
./src/MyPortfolio/bin/Release/net9.0-windows/MyPortfolio.exe
Usage
- Click Settings (top-right).
- Set your GitHub user / org (defaults to
SysAdminDoc). - (Optional) Paste a personal access token to raise the rate limit and surface private repos.
- (Optional) Add extra owners — use the chip editor for collaborator users / orgs, or paste a comma / semicolon / newline-separated list.
- Toggle topic filters per tab if you want to scope discovery (
windows-app,chrome-extension,android-appare the suggested defaults). - Pick a Catppuccin Theme and Accent if you want a lighter surface or a different focus color.
- Enable Refresh all tabs when MyPortfolio starts if you want discovery to run automatically on launch.
- Click Save and refresh all — each tab refreshes in a staged pass that can be canceled from the header.
- Switch between tabs and click Install / Download APK / Launch with extensions as you like.
Each tab shows its last successful refresh time beside its catalog summary, so stale discovery state is visible before you install or download anything. While a tab refreshes, the header shows the current discovery stage plus owner/repo counts. After refresh, each catalog also shows a compact discovery summary with partial-failure warnings, skipped archived/hidden/topic-filtered repo counts, repo probe issues, cache-hit counts, and current GitHub API quota. Expand owner diagnostics when you need the per-owner breakdown of matched, skipped, cached, failed, and probe-issue counts without adding noise to the activity log. Copy diagnostics creates a shareable support bundle with the compact summary, owner breakdown, rate-limit state, and recent activity lines; Save diagnostics writes the same redacted bundle to %LOCALAPPDATA%\MyPortfolio\diagnostics\ with a timestamped .txt filename and reveals it in Explorer. The saved GitHub token and common GitHub token formats are redacted before the text reaches the clipboard or disk. Release and topic probes are cached in memory for five minutes per GitHub user/token so repeated refreshes avoid burning through the same per-repo API calls. If GitHub stops discovery for a primary or secondary rate limit, the warning text tells you when or how long to wait before retrying. Downloaded Android cards also show the APK package name plus manifest version name and code when the manifest can be decoded. The Details action on each card expands local artifact metadata, including path, release asset, SHA-256, release date, and copy/open shortcuts.
Every action streams into the activity log at the bottom of the window. Nothing fails silently; everything is logged in-app and to %LOCALAPPDATA%\MyPortfolio\logs\.
On-disk layout
%APPDATA%\MyPortfolio\
├── settings.json # one shared AppSettings instance
├── desktop-installed.json # InstalledAppsManifest + local artifact details
├── chrome-installed.json # InstalledExtensionsManifest + local artifact details
└── android-downloads.json # DownloadedApksManifest + APK manifest details
%LOCALAPPDATA%\MyPortfolio\
├── desktop\apps\<owner>\<repo>\<version>\ # portable ZIP extractions
├── desktop\downloads\ # MSI / EXE staging
├── chrome\extensions\<owner>\<repo>\<version>\
├── cache\icons\ # icon cache (per-tab prefix)
├── diagnostics\ # timestamped redacted support bundles
└── logs\ # crash logs
%USERPROFILE%\Downloads\MyPortfolio\Android\<owner>\<repo>\<version>\<asset>.apk
The Android download folder lives under your Downloads on purpose — it's where you actually look when you go to sideload.
Architecture
C# WPF, .NET 9, single-project MVVM. No third-party MVVM toolkit. Three NuGet deps:
AndroidXml 1.1.24— binary Android manifest decoding for APK metadataOctokit 13.0.1— GitHub APIMicrosoft.Win32.Registry 5.0.0— Windows uninstall key reads (Desktop tab only)
src/MyPortfolio/
├── App.xaml # merged-dictionary theme bootstrap
├── MainWindow.xaml # header / settings drawer / TabControl / log / status bar
├── MainViewModel.cs # owns SettingsService, HttpDownloader, LogSink, three tab VMs
│
├── Common/ # ViewModelBase, RelayCommand, AppSettings, SettingsService,
│ # GitHubClientFactory, DiscoveryDiagnostics,
│ # DiscoveryProgress, DiagnosticsSupportBundle,
│ # DiscoveryProbeCache, HttpDownloader,
│ # HashVerifier, LogSink, Format
├── Converters/ # BoolToVis, NullToVis, EmptyStringToVis
├── Themes/ # Catppuccin token dictionary + runtime Mocha / Latte theme service
│
├── Desktop/ # Models / Services / ViewModels / Views — desktop-app install
├── Chrome/ # Models / Services / ViewModels / Views — extension install + launcher
└── Android/ # Models / Services / ViewModels / Views — APK download-only
Each tab owns its discovery service, its cards collection, and its install/download manifest. The shell exposes them through a single MainViewModel.
Source repos that fed into this
| Repo | Status |
|---|---|
| LocalDesktopStore | Code ported; functionality lives in the Desktop tab. |
| LocalChromeStore | Code ported; functionality lives in the Chrome tab. |
| LocalAndroidStore | The Android tab here is download-only. The Android-host install pipeline (signature pinning, PackageInstaller.Session, ACTION_DELETE) stays inside the LocalAndroidStore Android app — it can't run on Windows. |
License
MIT — see the LICENSE file for full text.