Add SDK bootstrap and sdkmanager wrapper#275
Merged
jonathanpeppers merged 46 commits intomainfrom Mar 2, 2026
Merged
Conversation
10b4527 to
1a94a8d
Compare
tests/Xamarin.Android.Tools.AndroidSdk-Tests/SdkManagerTests.cs
Outdated
Show resolved
Hide resolved
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as resolved.
This comment was marked as resolved.
930f96d to
8bc3209
Compare
5565656 to
bdeeb7f
Compare
Contributor
…cords Use one-liner positional record syntax and update all call sites to constructor syntax. Remove EnvironmentVariableNames usage from this PR (handled by #290). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove StartShellExecuteProcessAsync, RequiresElevation, RunSdkManagerElevatedAsync, and SanitizeCmdArgument. Elevation support will be added in a separate PR. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The AbiToArchMap and MapAbiToArchitecture were not used by any code. Keep only GetEnvironment which is used by SdkManager. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Avoids typos and string comparisons. The XML attribute value is parsed into the enum during manifest parsing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove unused AndroidEnvironmentHelper class - Remove unused SdkManagerTimeout property - Remove unused sdkManagerPath in AcceptLicensesAsync - Make SdkManifestComponent and GetManifestComponentsAsync internal - Remove unused TryDeleteFiles and CopyDirectoryRecursive from FileUtil Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reuse the existing DownloadUtils.ReadAsStringAsync helper which handles the netstandard2.0 vs NET5+ API difference. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use a list reference instead of string section tracking, eliminating duplicate add logic and string comparisons. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
a6990ed to
b804814
Compare
src/Xamarin.Android.Tools.AndroidSdk/Models/Sdk/SdkBootstrapPhase.cs
Outdated
Show resolved
Hide resolved
src/Xamarin.Android.Tools.AndroidSdk/Models/Sdk/SdkBootstrapProgress.cs
Outdated
Show resolved
Hide resolved
tests/Xamarin.Android.Tools.AndroidSdk-Tests/SdkManagerTests.cs
Outdated
Show resolved
Hide resolved
tests/Xamarin.Android.Tools.AndroidSdk-Tests/SdkManagerTests.cs
Outdated
Show resolved
Hide resolved
…t named arg - Convert all new .cs files to file-scoped namespaces per project convention - Use NullProgress no-op pattern in Bootstrap instead of null-conditional calls - Make private methods take non-nullable IProgress<T> - Simplify DownloadFileAsync progress adapter (no null check needed) - Revert onStarted: named arg, pass null explicitly for environmentVariables Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add StdinPollDelayMs const (500ms) for stdin polling loops - Fix Licenses.cs: write-then-delay ordering (was delay-then-write) - Use const in both Process.cs and Licenses.cs Task.Delay calls - Remove consecutive blank lines in SdkManagerTests.cs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
jonathanpeppers
left a comment
There was a problem hiding this comment.
This is really close now, but I'm also going to ask copilot to review again.
Replace raw "ANDROID_HOME" and "JAVA_HOME" strings with EnvironmentVariableNames.AndroidHome and .JavaHome constants. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Extract ComputeHashString helper in DownloadUtils, shared by VerifyChecksum and ComputeLicenseHash (removes duplication) - Use Directory.EnumerateFiles().Any() in HasAcceptedLicenses for short-circuit instead of GetFiles().Length - Use Path.GetTempFileName() in BootstrapAsync instead of manual Guid-based temp path - Remove unused System.Security.Cryptography using from Licenses Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jonathanpeppers
approved these changes
Mar 2, 2026
jonpryor
reviewed
Mar 2, 2026
Per Microsoft guidelines, HttpClient instances should be static and shared across the application lifetime. Changed SdkManager.httpClient from instance to static readonly field and removed disposal from Dispose(). Added convention to copilot-instructions.md. See: https://learn.microsoft.com/dotnet/fundamentals/networking/http/httpclient-guidelines Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
SdkManagerclass andsdkmanagerCLI wrapper toXamarin.Android.Tools.AndroidSdk, enabling programmatic Android SDK bootstrap and package management.Closes #271
Architecture
Two-phase approach:
cmdline-tools/<version>/sdkmanagerCLI for all package operations (list, install, uninstall, update, licenses)SdkManageris split into focused partial classes for maintainability:SdkManager.csIDisposableSdkManager.Manifest.csXmlReader-based parsingSdkManager.Bootstrap.csSdkManager.Packages.csSdkManager.Licenses.csGetPendingLicensesAsyncSdkManager.Process.csProcessUtils, environment configNew Files
Models (
Models/Sdk/):ChecksumType.cs— Enum for hash algorithms (SHA-256, SHA-1)SdkBootstrapProgress.cs— Progress reporting during bootstrapSdkLicense.cs— License data withIdandTextSdkManifestComponent.cs— Parsed manifest entry (URL, checksum, platform)SdkPackage.cs— Installed/available SDK package infoUtilities (extended existing files):
DownloadUtils.cs— Added SHA-1 support,ComputeHashStringshared helper,ChecksumTypeenumFileUtil.cs— AddedExtractAndInstall(safe extraction with rollback) andIsUnderDirectoryKey Features
XmlReader-based parsing with platform/architecture filteringIDisposablewithThrowIfDisposedguard on all public methodsANDROID_HOMEonly: Sets env var per Android docs; deprecatedANDROID_SDK_ROOTnot usedANDROID_USER_HOME: Sets~/.androidso sdkmanager writes to user-writable locationcmdline-tools/<version>/(notlatest/)Directory.Movefalls back to recursive copy onIOExceptionchmodwith process fallback for Unix executable permissions (throws on failure)ArrayPoolbuffers (NET5_0_OR_GREATER) for download operationsProcessUtilsfor all process execution — no directSystem.Diagnostics.ProcessusageProcessStartInfo.ArgumentListviaProcessUtils.CreateProcessStartInfo(falls back toArgumentson netstandard2.0)GetPendingLicensesAsync()returnsSdkLicenseobjects for IDE/CLI display;AcceptLicensesAsync(IEnumerable<string>)accepts specific licenses by IDnetstandard2.0+net10.0compatible, fullCancellationTokensupportEnvironmentVariableNamesconstants used throughout (no raw strings)Coding Conventions Updated
.github/copilot-instructions.mdupdated with conventions established during review:internalby default,InternalsVisibleTofor tests)ProcessUtilsfor process executionFileUtilfor file operationsnetstandard2.0awarenessTests
SdkManagerTests.cscovers:ArgumentException)InvalidOperationException(Install, Uninstall, List, Update, AcceptLicenses)ArgumentNullExceptionHasAcceptedLicenses) withAssert.Ignoreon failureReview Feedback Addressed
All review feedback from @jonathanpeppers and Copilot reviewer addressed:
HttpClientinjection (internal only)IDisposablewith dispose guardXmlReaderinstead ofXElementfor manifest parsinglatest/)ArrayPool<byte>for download buffersChecksumTypevalidated (throwsNotSupportedExceptionfor unknown)chmod(throwsInvalidOperationExceptionon failure)EnvironmentVariableNamesconstants created and usedANDROID_SDK_ROOTremoved (deprecated)GetPendingLicensesAsync+AcceptLicensesAsyncwith IDs)#regiondirectivesComputeHashStringhelper (removes duplication)EnumerateFiles().Any()for short-circuitPath.GetTempFileName()instead of manual Guid pathsStdinPollDelayMsconst for stdin pollingCo-authored-by: Copilot 223556219+Copilot@users.noreply.github.com