├── .config ├── dotnet-tools.json └── tsaoptions.json ├── .devcontainer └── devcontainer.json ├── .dockerignore ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── releases │ │ └── new-windows-release.md ├── dependabot.yml ├── labeler.yml └── workflows │ └── labeler.yml ├── .gitignore ├── .vault-config └── secrets.yaml ├── CODE-OF-CONDUCT.md ├── CODEOWNERS ├── LICENSE ├── Microsoft.DotNet.DockerTools.sln ├── NuGet.config ├── README.md ├── documentation ├── assets │ └── Base Image Dependency Flow.drawio ├── base-image-dependency-flow.md ├── images │ └── base-image-dependency-flow-diagram.png └── manifest-file.md ├── eng ├── check-base-image-subscriptions-buildtools.json ├── check-base-image-subscriptions.json ├── common │ ├── Dockerfile.WithRepo │ ├── Get-BaseImageStatus.ps1 │ ├── Get-ImageBuilder.ps1 │ ├── Get-ImageNameVars.ps1 │ ├── Install-DotNetSdk.ps1 │ ├── Invoke-CleanupDocker.ps1 │ ├── Invoke-ImageBuilder.ps1 │ ├── Invoke-WithRetry.ps1 │ ├── Retain-Build.ps1 │ ├── build.ps1 │ ├── pull-image.sh │ ├── readme.md │ └── templates │ │ ├── 1es-official.yml │ │ ├── 1es-unofficial.yml │ │ ├── jobs │ │ ├── build-images.yml │ │ ├── cg-build-projects.yml │ │ ├── copy-base-images-staging.yml │ │ ├── copy-base-images.yml │ │ ├── generate-matrix.yml │ │ ├── post-build.yml │ │ ├── publish.yml │ │ ├── test-images-linux-client.yml │ │ └── test-images-windows-client.yml │ │ ├── stages │ │ ├── build-and-test.yml │ │ ├── dotnet │ │ │ ├── build-and-test.yml │ │ │ ├── build-test-publish-repo.yml │ │ │ └── publish.yml │ │ ├── publish.yml │ │ └── setup-service-connections.yml │ │ ├── steps │ │ ├── annotate-eol-digests.yml │ │ ├── clean-acr-images.yml │ │ ├── cleanup-docker-linux.yml │ │ ├── cleanup-docker-windows.yml │ │ ├── common-init-for-matrix-and-build.yml │ │ ├── copy-base-images.yml │ │ ├── download-build-artifact.yml │ │ ├── init-common.yml │ │ ├── init-docker-linux.yml │ │ ├── init-docker-windows.yml │ │ ├── parse-test-arg-arrays.yml │ │ ├── publish-artifact.yml │ │ ├── publish-readmes.yml │ │ ├── retain-build.yml │ │ ├── run-imagebuilder.yml │ │ ├── run-pwsh-with-auth.yml │ │ ├── set-dry-run.yml │ │ ├── set-image-info-path-var.yml │ │ ├── test-images-linux-client.yml │ │ ├── test-images-windows-client.yml │ │ ├── validate-branch.yml │ │ ├── wait-for-mcr-doc-ingestion.yml │ │ └── wait-for-mcr-image-ingestion.yml │ │ ├── task-prefix-decorator.yml │ │ └── variables │ │ ├── common-paths.yml │ │ ├── common.yml │ │ ├── docker-images.yml │ │ └── dotnet │ │ ├── build-test-publish.yml │ │ └── common.yml ├── eng-common-file-pusher-config.json ├── image-builder-tag-update-config.json ├── pipelines │ ├── annotate-eol-digests.yml │ ├── cg-detection.yml │ ├── check-base-image-updates.yml │ ├── cleanup-acr-images-custom.yml │ ├── cleanup-acr-images.yml │ ├── dotnet-buildtools-image-builder-official.yml │ ├── dotnet-buildtools-image-builder-pr.yml │ ├── dotnet-docker-tools-eng-validation-pr.yml │ ├── dotnet-docker-tools-eng-validation.yml │ ├── import-image.yml │ ├── mirror-base-images.yml │ ├── push-common-updates.yml │ ├── secret-management-weekly.yml │ ├── templates │ │ ├── jobs │ │ │ ├── check-base-image-updates.yml │ │ │ └── copy-base-images-public-mirror.yml │ │ └── variables │ │ │ ├── build-test-publish.yml │ │ │ ├── common.yml │ │ │ ├── eng-validation.yml │ │ │ └── image-builder.yml │ ├── update-image-builder-tag.yml │ └── upload-file.yml ├── src │ ├── file-pusher │ │ ├── AzDoSafeTraceListenerWrapper.cs │ │ ├── Dockerfile │ │ ├── FilePusher.cs │ │ ├── Models │ │ │ ├── Config.cs │ │ │ └── GitRepo.cs │ │ ├── Options.cs │ │ └── file-pusher.csproj │ └── yaml-updater │ │ ├── Dockerfile │ │ ├── Options.cs │ │ ├── YamlUpdater.cs │ │ └── yaml-updater.csproj └── tests │ └── pipeline-validation │ ├── 1.0 │ ├── nanoserver-1809 │ │ └── amd64 │ │ │ └── Dockerfile │ └── noble │ │ ├── amd64 │ │ └── Dockerfile │ │ └── arm64v8 │ │ └── Dockerfile │ ├── README.placeholder.md │ ├── run-tests.ps1 │ ├── templates │ ├── README.md │ └── test-tags.yml │ └── test-manifest.json └── src └── Microsoft.DotNet.ImageBuilder ├── .dockerignore ├── Dockerfile.linux ├── Dockerfile.windows ├── NuGet.config ├── README.md ├── build.ps1 ├── manifest.json ├── run-tests.ps1 ├── src ├── AsyncLockedValue.cs ├── AuthHelper.cs ├── AzureScopes.cs ├── AzureTokenCredentialProvider.cs ├── AzureTokenCredentialProviderExtensions.cs ├── BlobsClientExtensions.cs ├── Commands │ ├── AnnotateEolDigestsCommand.cs │ ├── AnnotateEolDigestsOptions.cs │ ├── AzdoOptions.cs │ ├── AzdoTags.cs │ ├── BaseImageOverrideOptions.cs │ ├── BuildCommand.cs │ ├── BuildLegInfo.cs │ ├── BuildMatrixInfo.cs │ ├── BuildOptions.cs │ ├── CleanAcrImagesCommand.cs │ ├── CleanAcrImagesOptions.cs │ ├── CliHelper.cs │ ├── Command.TOptions.cs │ ├── CommandExtensions.cs │ ├── CopyAcrImagesCommand.cs │ ├── CopyAcrImagesOptions.cs │ ├── CopyBaseImagesCommand.cs │ ├── CopyBaseImagesOptions.cs │ ├── CopyImagesCommand.cs │ ├── CopyImagesOptions.cs │ ├── DockerfileFilterOptions.cs │ ├── GenerateArtifactsCommand.cs │ ├── GenerateArtifactsOptions.cs │ ├── GenerateBuildMatrixCommand.cs │ ├── GenerateBuildMatrixOptions.cs │ ├── GenerateDockerfilesCommand.cs │ ├── GenerateDockerfilesOptions.cs │ ├── GenerateEolAnnotationDataCommand.cs │ ├── GenerateEolAnnotationDataOptions.cs │ ├── GenerateReadmesCommand.cs │ ├── GenerateReadmesOptions.cs │ ├── GetBaseImageStatusCommand.cs │ ├── GetBaseImageStatusOptions.cs │ ├── GetStaleImagesCommand.cs │ ├── GetStaleImagesOptions.cs │ ├── GitOptions.cs │ ├── ICommand.cs │ ├── IFilterableOptions.cs │ ├── IGitOptionsHost.cs │ ├── IManifestCommand.cs │ ├── IOptions.cs │ ├── ImageInfoOptions.cs │ ├── IngestKustoImageInfoCommand.cs │ ├── IngestKustoImageInfoOptions.cs │ ├── ManifestCommand.cs │ ├── ManifestFilterOptions.cs │ ├── ManifestOptions.cs │ ├── MarIngestionOptions.cs │ ├── MatrixType.cs │ ├── MergeImageInfoCommand.cs │ ├── MergeImageInfoOptions.cs │ ├── NotificationLabels.cs │ ├── Options.cs │ ├── PlatformFilterOptions.cs │ ├── PostPublishNotificationCommand.cs │ ├── PostPublishNotificationOptions.cs │ ├── PublishImageInfoCommand.cs │ ├── PublishImageInfoOptions.cs │ ├── PublishManifestCommand.cs │ ├── PublishManifestOptions.cs │ ├── PublishMcrDocsCommand.cs │ ├── PublishMcrDocsOptions.cs │ ├── PullImagesCommand.cs │ ├── PullImagesOptions.cs │ ├── QueueBuildCommand.cs │ ├── QueueBuildOptions.cs │ ├── RegistryCredentialsOptions.cs │ ├── RegistryOptions.cs │ ├── ServiceConnectionOptions.cs │ ├── ShowImageStatsCommand.cs │ ├── ShowImageStatsOptions.cs │ ├── ShowManifestSchemaCommand.cs │ ├── Signing │ │ ├── GenerateSigningPayloadsCommand.cs │ │ └── GenerateSigningPayloadsOptions.cs │ ├── SubscriptionImagePaths.cs │ ├── SubscriptionOptions.cs │ ├── TrimUnchangedPlatformsCommand.cs │ ├── TrimUnchangedPlatformsOptions.cs │ ├── WaitForMarAnnotationIngestionCommand.cs │ ├── WaitForMarAnnotationIngestionOptions.cs │ ├── WaitForMcrDocIngestionCommand.cs │ ├── WaitForMcrDocIngestionOptions.cs │ ├── WaitForMcrImageIngestionCommand.cs │ └── WaitForMcrImageIngestionOptions.cs ├── ContainerRegistryClientFactory.cs ├── ContainerRegistryClientWrapper.cs ├── ContainerRegistryContentClientFactory.cs ├── ContainerRegistryContentClientWrapper.cs ├── CopyImageService.cs ├── CopyImageServiceFactory.cs ├── DateTimeService.cs ├── DockerHelper.cs ├── DockerService.cs ├── DockerServiceCache.cs ├── EnumHelper.cs ├── EnumerableExtensions.cs ├── EnvironmentService.cs ├── ExecuteHelper.cs ├── FileHelper.cs ├── GitHelper.cs ├── GitHubClientFactory.cs ├── GitService.cs ├── GitServiceExtensions.cs ├── GraphExtensions.cs ├── Helpers │ └── JwtHelper.cs ├── HttpClientProvider.cs ├── HttpHelper.cs ├── HttpPolicyBuilder.cs ├── IAzureTokenCredentialProvider.cs ├── IContainerRegistryClient.cs ├── IContainerRegistryClientFactory.cs ├── IContainerRegistryContentClient.cs ├── IContainerRegistryContentClientFactory.cs ├── IDateTimeService.cs ├── IDockerService.cs ├── IEnvironmentService.cs ├── IGitHubBranchRef.cs ├── IGitHubClientFactory.cs ├── IGitHubFileRef.cs ├── IGitHubRepoRef.cs ├── IGitService.cs ├── IHttpClientProvider.cs ├── ILifecycleMetadataService.cs ├── ILoggerService.cs ├── IManifestService.cs ├── IManifestServiceFactory.cs ├── IMcrStatusClient.cs ├── INotificationService.cs ├── IOctokitClientFactory.cs ├── IProcessService.cs ├── IRegistryContentClient.cs ├── IRegistryContentClientFactory.cs ├── IRegistryCredentialsHost.cs ├── IRegistryCredentialsProvider.cs ├── IServiceConnection.cs ├── ImageBuilder.cs ├── ImageCacheService.cs ├── ImageDigestCache.cs ├── ImageInfoHelper.cs ├── ImageInfoMergeOptions.cs ├── ImageName.cs ├── ImageNameResolver.cs ├── JsonHelper.cs ├── LifecycleMetadataService.cs ├── LockHelper.cs ├── Logger.cs ├── LoggerService.cs ├── ManifestQueryResult.cs ├── ManifestService.cs ├── ManifestServiceFactory.cs ├── MarImageIngestionReporter.cs ├── McrStatusClient.cs ├── McrStatusClientFactory.cs ├── McrTagsMetadataGenerator.cs ├── Microsoft.DotNet.ImageBuilder.csproj ├── Models │ ├── Annotations │ │ ├── EolAnnotationsData.cs │ │ └── EolDigestData.cs │ ├── Image │ │ ├── ImageArtifactDetails.cs │ │ ├── ImageData.cs │ │ ├── ManifestData.cs │ │ ├── PlatformData.cs │ │ └── RepoData.cs │ ├── Manifest │ │ ├── Architecture.cs │ │ ├── CustomBuildLegDependencyType.cs │ │ ├── CustomBuildLegGroup.cs │ │ ├── Image.cs │ │ ├── Manifest.cs │ │ ├── OS.cs │ │ ├── PackageQueryInfo.cs │ │ ├── Platform.cs │ │ ├── Readme.cs │ │ ├── Repo.cs │ │ ├── Tag.cs │ │ ├── TagDocumentationType.cs │ │ └── TagSyndication.cs │ ├── MarBulkDeletion │ │ └── BulkDeletionDescription.cs │ ├── McrStatus │ │ ├── CommitResult.cs │ │ ├── CommitResultDetailed.cs │ │ ├── CommitStatus.cs │ │ ├── ContentSubstatus.cs │ │ ├── ImageResult.cs │ │ ├── ImageResultDetailed.cs │ │ ├── ImageStatus.cs │ │ ├── ImageSubstatus.cs │ │ └── StageStatus.cs │ ├── McrTags │ │ ├── Repo.cs │ │ ├── TagGroup.cs │ │ └── TagsMetadata.cs │ ├── Notary │ │ └── Payload.cs │ ├── Oci │ │ ├── Descriptor.cs │ │ └── Manifest.cs │ ├── Oras │ │ └── OrasDiscoverData.cs │ ├── QueueNotification │ │ └── QueueInfo.cs │ └── Subscription │ │ ├── GitFile.cs │ │ ├── PipelineTrigger.cs │ │ ├── Subscription.cs │ │ └── SubscriptionManifest.cs ├── NotificationHelper.cs ├── NotificationService.cs ├── OAuthHelper.cs ├── OctokitClientFactory.cs ├── OrasClient.cs ├── PathHelper.cs ├── PipelineHelper.cs ├── ProcessService.cs ├── ReadmeHelper.cs ├── RegistryContentClientFactory.cs ├── RegistryCredentials.cs ├── RegistryCredentialsHostExtensions.cs ├── RegistryCredentialsProvider.cs ├── RegistryCredentialsProviderExtensions.cs ├── RegistryHttpClient.cs ├── RegistryServiceClient.cs ├── RetryHelper.cs ├── Services │ ├── AzdoGitHttpClientExtensions.cs │ ├── AzdoGitHttpClientFactory.cs │ ├── BuildExtensions.cs │ ├── IAzdoGitHttpClient.cs │ ├── IAzdoGitHttpClientFactory.cs │ ├── IBuildHttpClient.cs │ ├── IKustoClient.cs │ ├── IProjectHttpClient.cs │ ├── IVssConnection.cs │ ├── IVssConnectionFactory.cs │ ├── KustoClientWrapper.cs │ └── VssConnectionFactory.cs ├── StringExtensions.cs ├── SubscriptionHelper.cs ├── SystemCommandLineExtensions.cs ├── TaskHelper.cs ├── TreesClientExtensions.cs ├── ValidationException.cs └── ViewModel │ ├── IManifestOptionsInfo.cs │ ├── ImageDigestInfo.cs │ ├── ImageInfo.cs │ ├── ManifestFilter.cs │ ├── ManifestInfo.cs │ ├── ModelExtensions.cs │ ├── PlatformInfo.cs │ ├── RepoInfo.cs │ ├── TagInfo.cs │ └── VariableHelper.cs └── tests ├── AnnotateEolDigestsCommandTests.cs ├── BuildCommandTests.cs ├── CleanAcrImagesCommandTest.cs ├── CopyAcrImagesCommandTests.cs ├── CopyBaseImagesCommandTests.cs ├── DependencyInjectionTests.cs ├── GenerateBuildMatrixCommandTests.cs ├── GenerateDockerfilesCommandTests.cs ├── GenerateEolAnnotationDataCommandTests.cs ├── GenerateReadmesCommandTests.cs ├── GenerateSigningPayloadsCommandTests.cs ├── GetStaleImagesCommandTests.cs ├── Helpers ├── AsyncPageableMock.cs ├── ContainerRegistryHelper.cs ├── CopyImageHelper.cs ├── DigestInfoEqualityComparer.cs ├── DockerfileHelper.cs ├── ImageInfoHelper.cs ├── ManifestHelper.cs ├── ManifestServiceHelper.cs ├── MarStatusHelper.cs ├── ReturnsExtensions.cs ├── TestHelper.cs └── TestHttpMessageHandler.cs ├── ImageArtifactDetailsTests.cs ├── ImageInfoHelperTests.cs ├── IngestKustoImageInfoCommandTests.cs ├── ManifestInfoTests.cs ├── ManifestServiceDefaultImplementationTests.cs ├── MarImageIngestionReporterTests.cs ├── McrTagsMetadataGeneratorTests.cs ├── MergeImageInfoFilesCommandTests.cs ├── Microsoft.DotNet.ImageBuilder.Tests.csproj ├── ModelExtensionsTests.cs ├── PlatformDataTests.cs ├── PlatformInfoTests.cs ├── PublishImageInfoCommandTests.cs ├── PublishManifestCommandTests.cs ├── PublishMcrDocsCommandTests.cs ├── QueueBuildCommandTests.cs ├── RegistryContentClientFactoryTests.cs ├── ShowManifestSchemaCommandTests.cs ├── TrimUnchangedPlatformsCommandTests.cs ├── VariableHelperTests.cs ├── WaitForMarAnnotationIngestionCommandTests.cs ├── WaitForMcrDocIngestionCommandTests.cs └── WaitForMcrImageIngestionCommandTests.cs /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "microsoft.dnceng.secretmanager": { 6 | "version": "1.1.0-beta.25257.1", 7 | "commands": [ 8 | "secret-manager" 9 | ], 10 | "rollForward": false 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /.config/tsaoptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "instanceUrl": "https://devdiv.visualstudio.com/", 3 | "template": "TFSDEVDIV", 4 | "projectName": "DEVDIV", 5 | "areaPath": "DevDiv\\NET Fundamentals\\.NET Acquisition\\Docker", 6 | "iterationPath": "DevDiv", 7 | "notificationAliases": [ "dotnetADTeam@microsoft.com" ], 8 | "repositoryName":"dotnet-docker-tools", 9 | "codebaseName": "dotnet-docker-tools" 10 | } 11 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "mcr.microsoft.com/dotnet/sdk:9.0-noble", 3 | "features": { 4 | "ghcr.io/devcontainers/features/common-utils": { 5 | "username": "app", 6 | "userUid": 1654, 7 | "userGid": 1654 8 | }, 9 | "ghcr.io/devcontainers/features/docker-in-docker:2": {} 10 | }, 11 | "customizations": { 12 | "vscode": { 13 | "extensions": [ 14 | "ms-dotnettools.csdevkit", 15 | "GitHub.copilot" 16 | ], 17 | "settings": { 18 | "dotnet.defaultSolution": "Microsoft.DotNet.DockerTools.sln", 19 | "remote.autoForwardPorts": true, 20 | "remote.autoForwardPortsSource": "hybrid", 21 | "remote.otherPortsAttributes": { 22 | "onAutoForward": "ignore" 23 | } 24 | } 25 | } 26 | }, 27 | "remoteUser": "app", 28 | "onCreateCommand": "sudo chsh -s /bin/bash app" 29 | } 30 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/bin 2 | **/obj 3 | **/out 4 | **/.vscode 5 | **/.vs 6 | .Microsoft.DotNet.ImageBuilder 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Bug Report" 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | ## Steps to reproduce the issue 8 | 1. 9 | 2. 10 | 3. 11 | 12 | ## Expected behavior 13 | 14 | 15 | ## Actual behavior 16 | 17 | 18 | ## Additional information (e.g. issue happens only occasionally) 19 | 20 | 21 | ## Output of `docker version` 22 | 23 | ``` 24 | (paste your output here) 25 | ``` 26 | 27 | ## Output of `docker info` 28 | 29 | ``` 30 | (paste your output here) 31 | ``` 32 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "nuget" 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | day: "monday" 11 | time: "00:00" 12 | timezone: "America/Los_Angeles" 13 | commit-message: 14 | prefix: "[dependabot]" 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | interval: "weekly" 19 | day: "monday" 20 | time: "00:00" 21 | timezone: "America/Los_Angeles" 22 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | # Add 'untriaged' label to any issue that gets opened 2 | untriaged: 3 | - '/.*/' 4 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Issue Labeler" 2 | on: 3 | issues: 4 | types: [opened] 5 | 6 | permissions: 7 | issues: write 8 | contents: read 9 | 10 | jobs: 11 | triage: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: github/issue-labeler@v3.4 15 | with: 16 | configuration-path: .github/labeler.yml 17 | enable-versioned-regex: 0 18 | repo-token: ${{ github.token }} 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build output 2 | [Bb]in/ 3 | [Oo]bj/ 4 | [Oo]ut/ 5 | 6 | # Visual Studio Code cache/options directory 7 | .vscode/ 8 | .vs 9 | 10 | # Visual Studio user-specific files 11 | **/launchSettings.json 12 | **/*.user 13 | 14 | # ImageBuilder directory 15 | .Microsoft.DotNet.ImageBuilder 16 | 17 | # dotnet install directory 18 | .dotnet/ 19 | 20 | # Test files 21 | *.trx 22 | -------------------------------------------------------------------------------- /.vault-config/secrets.yaml: -------------------------------------------------------------------------------- 1 | # Partially copied from https://github.com/dotnet/arcade/blob/dfc6882da43decb37f12e0d9011ce82b25225578/.vault-config/product-builds-dnceng-pipeline-secrets.yaml 2 | 3 | storageLocation: 4 | type: azure-key-vault 5 | parameters: 6 | name: DotnetDockerKeyVault 7 | subscription: 941d4baa-5ef2-462e-b4b1-505791294610 8 | 9 | secrets: 10 | BotAccount-dotnet-docker-bot: 11 | type: github-account 12 | parameters: 13 | Name: dotnet-docker-bot 14 | 15 | BotAccount-dotnet-docker-bot-PAT: 16 | type: github-access-token 17 | parameters: 18 | gitHubBotAccountSecret: BotAccount-dotnet-docker-bot 19 | gitHubBotAccountName: dotnet-docker-bot 20 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the code of conduct defined by the Contributor Covenant 4 | to clarify expected behavior in our community. 5 | 6 | For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). 7 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Users referenced in this file will automatically be requested as reviewers for PRs that modify the given paths. 2 | # See https://help.github.com/articles/about-code-owners/ 3 | 4 | * @dotnet/dotnet-docker-reviewers 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 .NET Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker Tools 2 | 3 | This is a repo to house some common tools for use in the various .NET Docker repos. 4 | 5 | ## Image Builder 6 | 7 | A tool used to build and publish Docker images. 8 | 9 | The Image Builder tool can be acquired via a Docker image available at [mcr.microsoft.com/dotnet-buildtools/image-builder](https://mcr.microsoft.com/v2/dotnet-buildtools/image-builder/tags/list) or built from source via the [build script](./src/Microsoft.DotNet.ImageBuilder/build.ps1). 10 | 11 | The Image Builder tool relies on metadata which defines various information needed to build and tag Docker images. The metadata is stored in a manifest.json file ([sample](https://github.com/dotnet/dotnet-docker/blob/main/manifest.json)). The metadata schema is defined in [source](./src/Microsoft.DotNet.ImageBuilder/src/Model). 12 | 13 | The full list of supported commands can be seen by running the tool. 14 | 15 | - Linux container environment: `docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock mcr.microsoft.com/dotnet-buildtools/image-builder:debian-20190223173930 -h` 16 | 17 | The list of support command options can be seen by specifying the `-h` command option. The following illustrates how to list the build options. 18 | 19 | - Linux container environment: `docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock mcr.microsoft.com/dotnet-buildtools/image-builder:debian-20190223173930 build -h` 20 | -------------------------------------------------------------------------------- /documentation/images/base-image-dependency-flow-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/docker-tools/22707be9c359f3d8fe2d3a90b96f33cc78df2595/documentation/images/base-image-dependency-flow-diagram.png -------------------------------------------------------------------------------- /documentation/manifest-file.md: -------------------------------------------------------------------------------- 1 | # Manifest File 2 | 3 | The manifest file is the primary source of metadata that drives the production of all .NET Docker images. It describes various attributes of the Docker images that are to be produced by a given GitHub repo. .NET Docker's engineering system consumes this file in various ways as part of the automated build pipelines and other tools. It's intended to be product-agnostic meaning that it could be used to describe metadata for Docker image production of any product, not just .NET. 4 | 5 | For a description of the schema, see the source location at https://github.com/dotnet/docker-tools/tree/main/src/Microsoft.DotNet.ImageBuilder/src/Models/Manifest. Alternatively, you can generate a JSON schema of the manifest by using the ImageBuilder tool: 6 | ``` 7 | Microsoft.DotNet.ImageBuilder.exe showManifestSchema 8 | ``` 9 | -------------------------------------------------------------------------------- /eng/check-base-image-subscriptions-buildtools.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "manifest": { 4 | "owner": "dotnet", 5 | "repo": "dotnet-buildtools-prereqs-docker", 6 | "branch": "main", 7 | "path": "manifest.json", 8 | "variables": { 9 | "UniqueId": "" 10 | } 11 | }, 12 | "imageInfo": { 13 | "owner": "dotnet", 14 | "repo": "versions", 15 | "branch": "main", 16 | "path": "build-info/docker/image-info.dotnet-dotnet-buildtools-prereqs-docker-main.json" 17 | }, 18 | "pipelineTrigger": { 19 | "id": 1183, 20 | "pathVariable": "imageBuilder.pathArgs" 21 | } 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /eng/common/Dockerfile.WithRepo: -------------------------------------------------------------------------------- 1 | # Use this Dockerfile to create an ImageBuilder image 2 | ARG IMAGE 3 | FROM $IMAGE 4 | 5 | WORKDIR /repo 6 | COPY . . 7 | -------------------------------------------------------------------------------- /eng/common/Get-BaseImageStatus.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | Outputs the status of external base images referenced in the Dockerfiles. 6 | #> 7 | [cmdletbinding()] 8 | param( 9 | # Path to the manifest file to use 10 | [string] 11 | $Manifest = "manifest.json", 12 | 13 | # Architecture to filter Dockerfiles to 14 | [string] 15 | $Architecture = "*", 16 | 17 | # A value indicating whether to run the script continously 18 | [switch] 19 | $Continuous, 20 | 21 | # Number of seconds to wait between each iteration 22 | [int] 23 | $ContinuousDelay = 10 24 | ) 25 | 26 | Set-StrictMode -Version Latest 27 | 28 | $imageBuilderArgs = "getBaseImageStatus --manifest $Manifest --architecture $Architecture" 29 | if ($Continuous) { 30 | $imageBuilderArgs += " --continuous --continuous-delay $ContinuousDelay" 31 | } 32 | 33 | & "$PSScriptRoot/Invoke-ImageBuilder.ps1" -ImageBuilderArgs $imageBuilderArgs 34 | -------------------------------------------------------------------------------- /eng/common/Get-ImageBuilder.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | # Load common image names 4 | $imageNameVars = & $PSScriptRoot/Get-ImageNameVars.ps1 5 | foreach ($varName in $imageNameVars.Keys) { 6 | Set-Variable -Name $varName -Value $imageNameVars[$varName] -Scope Global 7 | } 8 | 9 | & docker inspect ${imageNames.imagebuilderName} | Out-Null 10 | if (-not $?) { 11 | Write-Output "Pulling" 12 | & $PSScriptRoot/Invoke-WithRetry.ps1 "docker pull ${imageNames.imagebuilderName}" 13 | } 14 | -------------------------------------------------------------------------------- /eng/common/Get-ImageNameVars.ps1: -------------------------------------------------------------------------------- 1 | # Returns a hashtable of variable name-to-value mapping representing the image name variables 2 | # used by the common build infrastructure. 3 | 4 | $vars = @{} 5 | Get-Content $PSScriptRoot/templates/variables/docker-images.yml | 6 | Where-Object { $_.Trim().Length -gt 0 -and $_.Trim() -notlike 'variables:' -and $_.Trim() -notlike '# *' } | 7 | ForEach-Object { 8 | $parts = $_.Split(':', 2) 9 | $vars[$parts[0].Trim()] = $parts[1].Trim() 10 | } 11 | 12 | return $vars 13 | -------------------------------------------------------------------------------- /eng/common/Invoke-CleanupDocker.ps1: -------------------------------------------------------------------------------- 1 | Set-StrictMode -Version Latest 2 | $ErrorActionPreference = 'Stop' 3 | 4 | docker ps -a -q | ForEach-Object { docker rm -f $_ } 5 | 6 | docker volume prune -f 7 | 8 | # Preserve the tagged Windows base images and the common eng infra images (e.g. ImageBuilder) 9 | # to avoid the expense of having to repull continuously. 10 | $imageNameVars = & $PSScriptRoot/Get-ImageNameVars.ps1 11 | 12 | docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" | 13 | Where-Object { 14 | $localImage = $_ 15 | $localImage.Contains(": ")` 16 | -Or -Not ($localImage.StartsWith("mcr.microsoft.com/windows")` 17 | -Or ($imageNameVars.Values.Where({ $localImage.StartsWith($_) }, 'First').Count -gt 0)) } | 18 | ForEach-Object { $_.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] } | 19 | Select-Object -Unique | 20 | ForEach-Object { docker rmi -f $_ } 21 | -------------------------------------------------------------------------------- /eng/common/Invoke-WithRetry.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | # Executes a command and retries if it fails. 4 | [cmdletbinding()] 5 | param ( 6 | [Parameter(Mandatory = $true)][string]$Cmd, 7 | [int]$Retries = 2, 8 | [int]$WaitFactor = 6 9 | ) 10 | 11 | Set-StrictMode -Version Latest 12 | $ErrorActionPreference = 'Stop' 13 | 14 | $count = 0 15 | $completed = $false 16 | 17 | Write-Output "Executing '$Cmd'" 18 | 19 | while (-not $completed) { 20 | try { 21 | Invoke-Expression $Cmd 22 | if (-not $(Test-Path variable:LASTEXITCODE) -or $LASTEXITCODE -eq 0) { 23 | $completed = $true 24 | continue 25 | } 26 | } 27 | catch { 28 | } 29 | 30 | $count++ 31 | 32 | if ($count -lt $Retries) { 33 | $wait = [Math]::Pow($WaitFactor, $count - 1) 34 | Write-Output "Retry $count/$Retries, retrying in $wait seconds..." 35 | Start-Sleep $wait 36 | } 37 | else { 38 | Write-Output "Retry $count/$Retries, no more retries left." 39 | throw "Failed to execute '$Cmd'" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /eng/common/Retain-Build.ps1: -------------------------------------------------------------------------------- 1 | # Adapted from https://github.com/dotnet/arcade/blob/main/eng/common/retain-build.ps1 2 | Param( 3 | [Parameter(Mandatory = $true)][int] $BuildId, 4 | [Parameter(Mandatory = $true)][string] $AzdoOrgUri, 5 | [Parameter(Mandatory = $true)][string] $AzdoProject, 6 | [Parameter(Mandatory = $true)][string] $Token 7 | ) 8 | 9 | $ErrorActionPreference = 'Stop' 10 | Set-StrictMode -Version 2.0 11 | 12 | function Get-AzDOHeaders( 13 | [string] $Token) { 14 | $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":${Token}")) 15 | $headers = @{"Authorization" = "Basic $base64AuthInfo" } 16 | return $headers 17 | } 18 | 19 | function Update-BuildRetention( 20 | [string] $AzdoOrgUri, 21 | [string] $AzdoProject, 22 | [int] $BuildId, 23 | [string] $Token) { 24 | $headers = Get-AzDOHeaders -Token $Token 25 | $requestBody = "{ 26 | `"keepForever`": `"true`" 27 | }" 28 | 29 | $requestUri = "${AzdoOrgUri}/${AzdoProject}/_apis/build/builds/${BuildId}?api-version=6.0" 30 | Write-Host "Attempting to retain build using the following URI: ${requestUri} ..." 31 | 32 | try { 33 | Invoke-RestMethod -Uri $requestUri -Method Patch -Body $requestBody -Header $headers -contentType "application/json" 34 | Write-Host "Updated retention settings for build ${BuildId}." 35 | } 36 | catch { 37 | Write-Host "##[error] Failed to update retention settings for build: $($_.Exception.Response.StatusDescription)" 38 | exit 1 39 | } 40 | } 41 | 42 | Update-BuildRetention -AzdoOrgUri $AzdoOrgUri -AzdoProject $AzdoProject -BuildId $BuildId -Token $Token 43 | exit 0 44 | -------------------------------------------------------------------------------- /eng/common/build.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | Builds the Dockerfiles 6 | #> 7 | 8 | [cmdletbinding()] 9 | param( 10 | # Product versions to filter by 11 | [string[]]$Version = "*", 12 | 13 | # Names of OS to filter by 14 | [string[]]$OS, 15 | 16 | # Type of architecture to filter by 17 | [string]$Architecture, 18 | 19 | # Additional custom path filters 20 | [string[]]$Paths, 21 | 22 | # Path to manifest file 23 | [string]$Manifest = "manifest.json", 24 | 25 | # Additional args to pass to ImageBuilder 26 | [string]$OptionalImageBuilderArgs 27 | ) 28 | 29 | Set-StrictMode -Version Latest 30 | $ErrorActionPreference = 'Stop' 31 | 32 | function Log { 33 | param ([string] $Message) 34 | 35 | Write-Output $Message 36 | } 37 | 38 | function Exec { 39 | param ([string] $Cmd) 40 | 41 | Log "Executing: '$Cmd'" 42 | Invoke-Expression $Cmd 43 | if ($LASTEXITCODE -ne 0) { 44 | throw "Failed: '$Cmd'" 45 | } 46 | } 47 | 48 | pushd $PSScriptRoot/../.. 49 | try { 50 | $args = $OptionalImageBuilderArgs 51 | 52 | if ($Version) { 53 | $args += ($Version | foreach { ' --version "{0}"' -f $_ }) 54 | } 55 | 56 | if ($OS) { 57 | $args += ($OS | foreach { ' --os-version "{0}"' -f $_ }) 58 | } 59 | 60 | if ($Architecture) { 61 | $args += ' --architecture "{0}"' -f $Architecture 62 | } 63 | 64 | if ($Paths) { 65 | $args += ($Paths | foreach { ' --path "{0}"' -f $_ }) 66 | } 67 | 68 | if ($Manifest) { 69 | $args += ' --manifest "{0}"' -f $Manifest 70 | } 71 | 72 | ./eng/common/Invoke-ImageBuilder.ps1 "build $args" 73 | } 74 | finally { 75 | popd 76 | } 77 | -------------------------------------------------------------------------------- /eng/common/pull-image.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Stop script on NZEC 4 | set -e 5 | # Stop script if unbound variable found (use ${var:-} if intentional) 6 | set -u 7 | 8 | say_err() { 9 | printf "%b\n" "Error: $1" >&2 10 | } 11 | 12 | # Executes a command and retries if it fails. 13 | execute() { 14 | local count=0 15 | until "$@"; do 16 | local exit=$? 17 | count=$(( $count + 1 )) 18 | if [ $count -lt $retries ]; then 19 | local wait=$(( waitFactor ** (( count - 1 )) )) 20 | echo "Retry $count/$retries exited $exit, retrying in $wait seconds..." 21 | sleep $wait 22 | else 23 | say_err "Retry $count/$retries exited $exit, no more retries left." 24 | return $exit 25 | fi 26 | done 27 | 28 | return 0 29 | } 30 | 31 | scriptName=$0 32 | retries=5 33 | waitFactor=6 34 | image=$1 35 | 36 | echo "Pulling Docker image $image" 37 | execute docker pull $image 38 | -------------------------------------------------------------------------------- /eng/common/readme.md: -------------------------------------------------------------------------------- 1 | # Don't touch this folder 2 | 3 | uuuuuuuuuuuuuuuuuuuu 4 | u" uuuuuuuuuuuuuuuuuu "u 5 | u" u$$$$$$$$$$$$$$$$$$$$u "u 6 | u" u$$$$$$$$$$$$$$$$$$$$$$$$u "u 7 | u" u$$$$$$$$$$$$$$$$$$$$$$$$$$$$u "u 8 | u" u$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$u "u 9 | u" u$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$u "u 10 | $ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $ 11 | $ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $ 12 | $ $$$" ... "$... ...$" ... "$$$ ... "$$$ $ 13 | $ $$$u `"$$$$$$$ $$$ $$$$$ $$ $$$ $$$ $ 14 | $ $$$$$$uu "$$$$ $$$ $$$$$ $$ """ u$$$ $ 15 | $ $$$""$$$ $$$$ $$$u "$$$" u$$ $$$$$$$$ $ 16 | $ $$$$....,$$$$$..$$$$$....,$$$$..$$$$$$$$ $ 17 | $ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $ 18 | "u "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" u" 19 | "u "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" u" 20 | "u "$$$$$$$$$$$$$$$$$$$$$$$$$$$$" u" 21 | "u "$$$$$$$$$$$$$$$$$$$$$$$$" u" 22 | "u "$$$$$$$$$$$$$$$$$$$$" u" 23 | "u """""""""""""""""" u" 24 | """""""""""""""""""" 25 | 26 | !!! Changes made in this directory are subject to being overwritten by automation !!! 27 | 28 | The files in this directory are shared by all .NET Docker repos. If you need to make changes to these files, open an issue or submit a pull request in https://github.com/dotnet/docker-tools. -------------------------------------------------------------------------------- /eng/common/templates/jobs/copy-base-images-staging.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: name 3 | type: string 4 | default: null 5 | - name: pool 6 | type: object 7 | default: {} 8 | - name: customInitSteps 9 | type: stepList 10 | default: [] 11 | - name: additionalOptions 12 | type: string 13 | default: '' 14 | - name: continueOnError 15 | type: string 16 | default: false 17 | 18 | jobs: 19 | - template: /eng/common/templates/jobs/copy-base-images.yml@self 20 | parameters: 21 | name: ${{ parameters.name }} 22 | pool: ${{ parameters.pool }} 23 | customInitSteps: ${{ parameters.customInitSteps }} 24 | additionalOptions: ${{ parameters.additionalOptions }} 25 | acr: 26 | server: $(acr-staging.server) 27 | serviceConnection: 28 | tenantId: $(internal-mirror.serviceConnection.tenantId) 29 | clientId: $(internal-mirror.serviceConnection.clientId) 30 | id: $(internal-mirror.serviceConnection.id) 31 | subscription: $(acr-staging.subscription) 32 | resourceGroup: $(acr-staging.resourceGroup) 33 | repoPrefix: $(mirrorRepoPrefix) 34 | -------------------------------------------------------------------------------- /eng/common/templates/jobs/copy-base-images.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: name 3 | type: string 4 | default: null 5 | - name: pool 6 | type: object 7 | default: {} 8 | - name: acr 9 | type: object 10 | default: null 11 | - name: repoPrefix 12 | type: string 13 | default: null 14 | - name: customInitSteps 15 | type: stepList 16 | default: [] 17 | - name: additionalOptions 18 | type: string 19 | default: '' 20 | - name: continueOnError 21 | type: string 22 | default: false 23 | - name: forceDryRun 24 | type: boolean 25 | default: false 26 | 27 | jobs: 28 | - job: ${{ parameters.name }} 29 | pool: ${{ parameters.pool }} 30 | steps: 31 | - template: /eng/common/templates/steps/init-docker-linux.yml@self 32 | - ${{ parameters.customInitSteps }} 33 | - template: /eng/common/templates/steps/copy-base-images.yml@self 34 | parameters: 35 | acr: ${{ parameters.acr }} 36 | repoPrefix: ${{ parameters.repoPrefix }} 37 | additionalOptions: ${{ parameters.additionalOptions }} 38 | continueOnError: ${{ parameters.continueOnError }} 39 | forceDryRun: ${{ parameters.forceDryRun }} 40 | -------------------------------------------------------------------------------- /eng/common/templates/jobs/test-images-linux-client.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | name: null 3 | pool: {} 4 | matrix: {} 5 | testJobTimeout: 60 6 | preBuildValidation: false 7 | internalProjectName: null 8 | customInitSteps: [] 9 | sourceBuildPipelineRunId: "" 10 | 11 | jobs: 12 | - job: ${{ parameters.name }} 13 | ${{ if eq(parameters.preBuildValidation, 'false') }}: 14 | condition: and(succeeded(), ${{ parameters.matrix }}) 15 | dependsOn: GenerateTestMatrix 16 | strategy: 17 | matrix: $[ ${{ parameters.matrix }} ] 18 | ${{ if eq(parameters.preBuildValidation, 'true') }}: 19 | condition: and(succeeded(), ne(variables.testScriptPath, '')) 20 | pool: ${{ parameters.pool }} 21 | timeoutInMinutes: ${{ parameters.testJobTimeout }} 22 | steps: 23 | - template: /eng/common/templates/steps/test-images-linux-client.yml@self 24 | parameters: 25 | preBuildValidation: ${{ parameters.preBuildValidation }} 26 | internalProjectName: ${{ parameters.internalProjectName }} 27 | customInitSteps: ${{ parameters.customInitSteps }} 28 | sourceBuildPipelineRunId: ${{ parameters.sourceBuildPipelineRunId }} 29 | -------------------------------------------------------------------------------- /eng/common/templates/jobs/test-images-windows-client.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | name: null 3 | pool: {} 4 | matrix: {} 5 | testJobTimeout: 60 6 | internalProjectName: null 7 | customInitSteps: [] 8 | sourceBuildPipelineRunId: "" 9 | 10 | jobs: 11 | - job: ${{ parameters.name }} 12 | condition: and(succeeded(), ${{ parameters.matrix }}) 13 | dependsOn: GenerateTestMatrix 14 | pool: ${{ parameters.pool }} 15 | strategy: 16 | matrix: $[ ${{ parameters.matrix }} ] 17 | timeoutInMinutes: ${{ parameters.testJobTimeout }} 18 | steps: 19 | - template: /eng/common/templates/steps/test-images-windows-client.yml@self 20 | parameters: 21 | internalProjectName: ${{ parameters.internalProjectName }} 22 | customInitSteps: ${{ parameters.customInitSteps }} 23 | sourceBuildPipelineRunId: ${{ parameters.sourceBuildPipelineRunId }} 24 | -------------------------------------------------------------------------------- /eng/common/templates/stages/setup-service-connections.yml: -------------------------------------------------------------------------------- 1 | # This stage exists to tell Azure DevOps about all of the service connections 2 | # that will be used in the pipeline. A service connection will not work unless 3 | # it is declared in this stage's parameters, even if your pipeline has already 4 | # been granted access to the service connection. This stage also does not need 5 | # to complete before the service connection is used. 6 | parameters: 7 | - name: pool 8 | type: object 9 | # serviceConnections object shape: 10 | # - name: string 11 | - name: serviceConnections 12 | type: object 13 | default: [] 14 | 15 | stages: 16 | 17 | - stage: SetupServiceConnectionsStage 18 | displayName: Setup service connections 19 | jobs: 20 | 21 | - job: SetupServiceConnectionsJob 22 | displayName: Setup service connections 23 | pool: ${{ parameters.pool }} 24 | steps: 25 | 26 | - ${{ each serviceConnection in parameters.serviceConnections }}: 27 | - task: AzureCLI@2 28 | displayName: Setup ${{ serviceConnection.name }} 29 | inputs: 30 | azureSubscription: ${{ serviceConnection.name }} 31 | scriptType: pscore 32 | scriptLocation: inlineScript 33 | inlineScript: | 34 | az account show 35 | -------------------------------------------------------------------------------- /eng/common/templates/steps/clean-acr-images.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | repo: null 3 | subscription: null 4 | resourceGroup: null 5 | acr: null 6 | action: null 7 | age: null 8 | customArgs: "" 9 | internalProjectName: null 10 | steps: 11 | - template: /eng/common/templates/steps/run-imagebuilder.yml@self 12 | parameters: 13 | displayName: Clean ACR Images - ${{ parameters.repo }} 14 | serviceConnections: 15 | - name: acr 16 | id: $(clean.serviceConnection.id) 17 | tenantId: $(clean.serviceConnection.tenantId) 18 | clientId: $(clean.serviceConnection.clientId) 19 | internalProjectName: ${{ parameters.internalProjectName }} 20 | args: >- 21 | cleanAcrImages 22 | ${{ parameters.repo }} 23 | ${{ parameters.subscription }} 24 | ${{ parameters.resourceGroup }} 25 | ${{ parameters.acr }} 26 | --action ${{ parameters.action }} 27 | --age ${{ parameters.age }} 28 | ${{ parameters.customArgs }} 29 | -------------------------------------------------------------------------------- /eng/common/templates/steps/cleanup-docker-linux.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | condition: true 3 | 4 | steps: 5 | ################################################################################ 6 | # Cleanup local Docker server 7 | ################################################################################ 8 | - script: docker stop $(docker ps -q) || true 9 | displayName: Stop Running Containers 10 | condition: and(always(), ${{ parameters.condition }}) 11 | continueOnError: true 12 | - script: docker system prune -a -f --volumes 13 | displayName: Cleanup Docker 14 | condition: and(always(), ${{ parameters.condition }}) 15 | continueOnError: true 16 | -------------------------------------------------------------------------------- /eng/common/templates/steps/cleanup-docker-windows.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | condition: true 3 | 4 | steps: 5 | ################################################################################ 6 | # Cleanup Docker Resources 7 | ################################################################################ 8 | - powershell: $(engCommonPath)/Invoke-CleanupDocker.ps1 9 | displayName: Cleanup Docker Images 10 | condition: and(always(), ${{ parameters.condition }}) 11 | continueOnError: true 12 | - powershell: | 13 | if (Test-Path $(Build.BinariesDirectory)\.Microsoft.DotNet.ImageBuilder) { 14 | Remove-Item $(Build.BinariesDirectory)\.Microsoft.DotNet.ImageBuilder -Force -Recurse; 15 | } 16 | displayName: Cleanup Image Builder 17 | condition: and(always(), ${{ parameters.condition }}) 18 | continueOnError: true 19 | -------------------------------------------------------------------------------- /eng/common/templates/steps/init-common.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | condition: true 3 | 4 | steps: 5 | - powershell: | 6 | $sourceBranch=$Env:BUILD_SOURCEBRANCH -replace "refs/heads/","" -replace "refs/tags/","" -replace "refs/pull/","" 7 | echo "##vso[task.setvariable variable=sourceBranch]$sourceBranch" 8 | displayName: Define Source Branch Variable 9 | condition: and(succeeded(), ${{ parameters.condition }}) 10 | -------------------------------------------------------------------------------- /eng/common/templates/steps/parse-test-arg-arrays.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - powershell: | 3 | # Formats the OS versions in a compact human-readable form (e.g. "os1/os2") 4 | $osVersionsDisplayName = '$(osVersions)' -Replace '--os-version ', '' -Replace ' ', '/' 5 | 6 | # Defines a PowerShell snippet in string-form that can be used to initialize an array of the OS versions 7 | $osVersionsArrayInitStr = "@('" + $($osVersionsDisplayName -Replace "/", "', '") + "')" 8 | 9 | echo "##vso[task.setvariable variable=osVersionsDisplayName]$osVersionsDisplayName" 10 | echo "##vso[task.setvariable variable=osVersionsArrayInitStr]$osVersionsArrayInitStr" 11 | 12 | # Defines a PowerShell snippet in string-form that can be used to initialize an array of the image builder paths 13 | $pathInitStr = "@('" + $('$(imageBuilderPaths)' -Replace '--path', '' -Replace " ", "', '") + "')" 14 | echo "##vso[task.setvariable variable=imageBuilderPathsArrayInitStr]$pathInitStr" 15 | displayName: Parse Test Arg Arrays 16 | -------------------------------------------------------------------------------- /eng/common/templates/steps/publish-artifact.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: path 3 | type: string 4 | - name: artifactName 5 | type: string 6 | - name: displayName 7 | type: string 8 | - name: internalProjectName 9 | type: string 10 | - name: publicProjectName 11 | type: string 12 | - name: condition 13 | type: string 14 | default: 'true' 15 | 16 | steps: 17 | - ${{ if eq(variables['System.TeamProject'], parameters.internalProjectName) }}: 18 | - task: 1ES.PublishPipelineArtifact@1 19 | inputs: 20 | path: ${{ parameters.path }} 21 | artifact: ${{ parameters.artifactName }} 22 | displayName: ${{ parameters.displayName }} 23 | condition: and(succeeded(), ${{ parameters.condition }}) 24 | - ${{ if eq(variables['System.TeamProject'], parameters.publicProjectName) }}: 25 | - publish: ${{ parameters.path }} 26 | artifact: ${{ parameters.artifactName }} 27 | displayName: ${{ parameters.displayName }} 28 | condition: and(succeeded(), ${{ parameters.condition }}) 29 | -------------------------------------------------------------------------------- /eng/common/templates/steps/publish-readmes.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | dryRunArg: "" 3 | condition: true 4 | 5 | steps: 6 | - script: > 7 | $(runImageBuilderCmd) publishMcrDocs 8 | --manifest '$(manifest)' 9 | --registry-override '$(acr.server)' 10 | '$(mcrDocsRepoInfo.userName)' 11 | '$(mcrDocsRepoInfo.email)' 12 | $(mcrDocsRepoInfo.authArgs) 13 | '$(publicGitRepoUri)' 14 | ${{ parameters.dryRunArg }} 15 | $(manifestVariables) 16 | $(imageBuilder.queueArgs) 17 | --git-owner 'Microsoft' 18 | --git-repo 'mcrdocs' 19 | --git-branch 'main' 20 | --git-path 'teams' 21 | $(additionalPublishMcrDocsArgs) 22 | name: PublishReadmes 23 | displayName: Publish Readmes 24 | condition: ${{ parameters.condition }} 25 | - template: /eng/common/templates/steps/wait-for-mcr-doc-ingestion.yml@self 26 | parameters: 27 | commitDigest: $(PublishReadmes.readmeCommitDigest) 28 | condition: and(${{ parameters.condition }}, ne(variables['PublishReadmes.readmeCommitDigest'], '')) 29 | dryRunArg: ${{ parameters.dryRunArg }} 30 | -------------------------------------------------------------------------------- /eng/common/templates/steps/retain-build.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - powershell: > 3 | $(engCommonPath)/Retain-Build.ps1 4 | -BuildId $(Build.BuildId) 5 | -AzdoOrgUri '$(System.CollectionUri)' 6 | -AzdoProject '$(System.TeamProject)' 7 | -Token '$(System.AccessToken)' 8 | displayName: Enable permanent build retention 9 | condition: and(succeeded(), eq(variables.retainBuild, 'true')) 10 | -------------------------------------------------------------------------------- /eng/common/templates/steps/run-pwsh-with-auth.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: name 3 | type: string 4 | default: "" 5 | - name: displayName 6 | type: string 7 | default: "Run PowerShell" 8 | - name: serviceConnection 9 | type: string 10 | default: "" 11 | - name: command 12 | type: string 13 | default: null 14 | - name: continueOnError 15 | type: boolean 16 | default: false 17 | - name: dockerClientOS 18 | type: string 19 | default: "linux" 20 | - name: condition 21 | type: string 22 | default: true 23 | 24 | steps: 25 | - task: AzureCLI@2 26 | ${{ if ne(parameters.name, '') }}: 27 | name: ${{ parameters.name }} 28 | displayName: ${{ parameters.displayName }} (Authenticated) 29 | continueOnError: ${{ parameters.continueOnError }} 30 | condition: and(succeeded(), ${{ parameters.condition }}) 31 | inputs: 32 | azureSubscription: ${{ parameters.serviceConnection }} 33 | addSpnToEnvironment: true 34 | ${{ if eq(parameters.dockerClientOS, 'windows') }}: 35 | scriptType: 'ps' 36 | ${{ else }}: 37 | scriptType: 'pscore' 38 | scriptLocation: 'inlineScript' 39 | inlineScript: ${{ parameters.command }}; 40 | -------------------------------------------------------------------------------- /eng/common/templates/steps/set-dry-run.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - powershell: | 3 | # Use dry-run option for certain publish operations if this is not a production build 4 | $dryRunArg="" 5 | if (-not "$(officialRepoPrefixes)".Split(',').Contains("$(publishRepoPrefix)") ` 6 | -or "$(System.TeamProject)" -eq "$(publicProjectName)") 7 | { 8 | $dryRunArg="--dry-run" 9 | } 10 | echo "##vso[task.setvariable variable=dryRunArg]$dryRunArg" 11 | displayName: Set dry-run arg for non-prod 12 | -------------------------------------------------------------------------------- /eng/common/templates/steps/set-image-info-path-var.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | publicSourceBranch: null 3 | 4 | steps: 5 | - powershell: | 6 | $basePath = "$(gitHubVersionsRepoInfo.path)" 7 | 8 | $publicSourceBranch = "${{ parameters.publicSourceBranch }}" 9 | 10 | if ($publicSourceBranch -eq "") { 11 | throw "publicSourceBranch variable is not set" 12 | } 13 | 14 | $buildRepoName = "$(Build.Repository.Name)".Replace("/", "-") 15 | $imageInfoName = "image-info.$buildRepoName-$publicSourceBranch$(imageInfoVariant).json" 16 | 17 | echo "##vso[task.setvariable variable=imageInfoVersionsPath]$basePath/$imageInfoName" 18 | echo "##vso[task.setvariable variable=gitHubImageInfoVersionsPath]$(gitHubVersionsRepoInfo.path)/$imageInfoName" 19 | displayName: Set Image Info Path Vars 20 | -------------------------------------------------------------------------------- /eng/common/templates/steps/validate-branch.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | internalProjectName: null 3 | 4 | steps: 5 | - ${{ if and(eq(variables['System.TeamProject'], parameters.internalProjectName), ne(variables['Build.Reason'], 'PullRequest')) }}: 6 | - powershell: | 7 | if ("$(officialBranches)".Split(',').Contains("$(sourceBranch)") ` 8 | -and "$(officialRepoPrefixes)".Split(',').Contains("$(publishRepoPrefix)")) 9 | { 10 | echo "Conditions met for official build, continuing..." 11 | exit 0 12 | } 13 | 14 | if (-not "$(officialRepoPrefixes)".Split(',').Contains("$(publishRepoPrefix)")) 15 | { 16 | echo "This build is a test build, continuing..." 17 | exit 0 18 | } 19 | 20 | if ("$(overrideOfficialBranchValidation)" -eq "true") 21 | { 22 | echo "Variable overrideOfficialBranchValidation is set to true, continuing..." 23 | exit 0 24 | } 25 | 26 | echo "##vso[task.logissue type=error]Official builds must be done from an official branch ($(officialBranches)) and repo prefix ($(officialRepoPrefixes))." 27 | exit 1 28 | displayName: Validate Branch 29 | -------------------------------------------------------------------------------- /eng/common/templates/steps/wait-for-mcr-doc-ingestion.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | commitDigest: null 3 | condition: true 4 | dryRunArg: "" 5 | 6 | steps: 7 | - template: /eng/common/templates/steps/run-imagebuilder.yml@self 8 | parameters: 9 | displayName: Wait for MCR Doc Ingestion 10 | condition: and(${{ parameters.condition }}, eq(variables['waitForIngestionEnabled'], 'true')) 11 | serviceConnections: 12 | - name: mar 13 | id: $(marStatus.serviceConnection.id) 14 | tenantId: $(marStatus.serviceConnection.tenantId) 15 | clientId: $(marStatus.serviceConnection.clientId) 16 | internalProjectName: 'internal' 17 | args: >- 18 | waitForMcrDocIngestion 19 | '${{ parameters.commitDigest }}' 20 | --timeout '$(mcrDocIngestionTimeout)' 21 | ${{ parameters.dryRunArg }} 22 | -------------------------------------------------------------------------------- /eng/common/templates/steps/wait-for-mcr-image-ingestion.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | imageInfoPath: null 3 | minQueueTime: null 4 | condition: true 5 | dryRunArg: "" 6 | 7 | steps: 8 | - template: /eng/common/templates/steps/run-imagebuilder.yml@self 9 | parameters: 10 | displayName: Wait for Image Ingestion 11 | condition: and(${{ parameters.condition }}, eq(variables['waitForIngestionEnabled'], 'true')) 12 | serviceConnections: 13 | - name: mar 14 | id: $(marStatus.serviceConnection.id) 15 | tenantId: $(marStatus.serviceConnection.tenantId) 16 | clientId: $(marStatus.serviceConnection.clientId) 17 | internalProjectName: 'internal' 18 | args: >- 19 | waitForMcrImageIngestion 20 | '${{ parameters.imageInfoPath }}' 21 | --manifest '$(manifest)' 22 | --repo-prefix '$(publishRepoPrefix)' 23 | --min-queue-time '${{ parameters.minQueueTime }}' 24 | --timeout '$(mcrImageIngestionTimeout)' 25 | $(manifestVariables) 26 | ${{ parameters.dryRunArg }} 27 | -------------------------------------------------------------------------------- /eng/common/templates/variables/common-paths.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | engCommonRelativePath: eng/common 3 | engCommonPath: $(Build.Repository.LocalPath)/$(engCommonRelativePath) 4 | engPath: $(Build.Repository.LocalPath)/eng 5 | testScriptPath: "" 6 | -------------------------------------------------------------------------------- /eng/common/templates/variables/docker-images.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | imageNames.imageBuilderName: mcr.microsoft.com/dotnet-buildtools/image-builder:2718660 3 | imageNames.imageBuilder: $(imageNames.imageBuilderName) 4 | imageNames.imageBuilder.withrepo: imagebuilder-withrepo:$(Build.BuildId)-$(System.JobId) 5 | imageNames.testRunner: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux3.0-docker-testrunner 6 | imageNames.testRunner.withrepo: testrunner-withrepo:$(Build.BuildId)-$(System.JobId) 7 | -------------------------------------------------------------------------------- /eng/eng-common-file-pusher-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "commitMessage": "Update common Docker engineering infrastructure with latest", 3 | "pullRequestTitle": "Update common Docker engineering infrastructure with latest", 4 | "pullRequestDescription": "Updates the common Docker engineering infrastructure with latest changes from the dotnet/docker-tools repo.", 5 | "sourcePath": "eng/common", 6 | "workingBranchSuffix": "-update-eng-common", 7 | "repos": [ 8 | { 9 | "owner": "dotnet", 10 | "name": "dotnet-docker", 11 | "branch": "nightly" 12 | }, 13 | { 14 | "owner": "dotnet", 15 | "name": "dotnet-docker", 16 | "branch": "main" 17 | }, 18 | { 19 | "owner": "dotnet", 20 | "name": "dotnet-buildtools-prereqs-docker", 21 | "branch": "main" 22 | }, 23 | { 24 | "owner": "microsoft", 25 | "name": "dotnet-framework-docker", 26 | "branch": "main" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /eng/image-builder-tag-update-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "commitMessage": "Update Image Builder tag reference", 3 | "pullRequestTitle": "Update Image Builder tag reference", 4 | "pullRequestDescription": "Updates the common pipeline variables to reference an updated tag of Image Builder.", 5 | "sourcePath": "eng/common/templates/variables/docker-images.yml", 6 | "workingBranchSuffix": "-image-builder-tag", 7 | "repos": [ 8 | { 9 | "owner": "dotnet", 10 | "name": "docker-tools", 11 | "branch": "main" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /eng/pipelines/annotate-eol-digests.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | pr: none 3 | 4 | parameters: 5 | - name: dataFile 6 | displayName: Relative path to EOL annotations data file (e.g. eol-3.1.json for file in root of the branch) 7 | type: string 8 | 9 | variables: 10 | - template: templates/variables/image-builder.yml 11 | - name: publishEolAnnotations 12 | value: true 13 | - name: dryRunArg 14 | value: "" 15 | 16 | extends: 17 | template: /eng/common/templates/1es-official.yml@self 18 | parameters: 19 | serviceConnections: 20 | - name: $(publish.serviceConnectionName) 21 | - name: $(marStatus.serviceConnectionName) 22 | stages: 23 | - stage: eolAnnotate 24 | displayName: Annotate EOL images 25 | dependsOn: [] 26 | jobs: 27 | - job: AnnotateImages 28 | displayName: Annotate EOL Images 29 | steps: 30 | - template: /eng/common/templates/steps/init-docker-linux.yml@self 31 | - template: /eng/common/templates/steps/annotate-eol-digests.yml@self 32 | parameters: 33 | dataFile: /repo/${{ parameters.dataFile }} 34 | -------------------------------------------------------------------------------- /eng/pipelines/cg-detection.yml: -------------------------------------------------------------------------------- 1 | # This pipelines builds all projects in the repository outside of Dockerfiles so that the artifacts 2 | # can be scanned by SDL steps. SDL steps do not scan artifacts that are built within Dockerfiles. 3 | trigger: 4 | branches: 5 | include: 6 | - main 7 | pr: none 8 | 9 | parameters: 10 | # Setting cgDryRun will run CG but not submit the results 11 | - name: cgDryRun 12 | type: boolean 13 | default: false 14 | displayName: CG Dry Run 15 | 16 | variables: 17 | - template: /eng/pipelines/templates/variables/common.yml@self 18 | # Skip CG detection (for debugging project builds, etc.) 19 | - name: skipComponentGovernanceDetection 20 | value: false 21 | 22 | extends: 23 | template: /eng/common/templates/1es-official.yml@self 24 | parameters: 25 | cgDryRun: ${{ parameters.cgDryRun }} 26 | stages: 27 | - stage: CgDetection 28 | displayName: CG Detection 29 | jobs: 30 | - template: /eng/common/templates/jobs/cg-build-projects.yml@self 31 | parameters: 32 | cgDryRun: ${{ parameters.cgDryRun }} 33 | -------------------------------------------------------------------------------- /eng/pipelines/check-base-image-updates.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | pr: none 3 | 4 | schedules: 5 | - cron: "0 0,4,8,12,16,20 * * *" 6 | displayName: Daily build 7 | branches: 8 | include: 9 | - main 10 | always: true 11 | 12 | variables: 13 | - template: /eng/pipelines/templates/variables/common.yml@self 14 | 15 | extends: 16 | template: /eng/common/templates/1es-official.yml@self 17 | parameters: 18 | serviceConnections: 19 | - name: $(acr-staging.serviceConnectionName) 20 | stages: 21 | - stage: CheckBaseImages 22 | displayName: Check Base Images 23 | dependsOn: [] 24 | jobs: 25 | - template: /eng/pipelines/templates/jobs/check-base-image-updates.yml@self 26 | parameters: 27 | jobName: CheckBaseImages 28 | subscriptionsPath: eng/check-base-image-subscriptions.json 29 | publicProjectName: ${{ variables.publicProjectName }} 30 | internalProjectName: ${{ variables.internalProjectName }} 31 | - template: /eng/pipelines/templates/jobs/check-base-image-updates.yml@self 32 | parameters: 33 | jobName: CheckBaseImages_BuildTools 34 | subscriptionsPath: eng/check-base-image-subscriptions-buildtools.json 35 | customGetStaleImagesArgs: --base-override-regex '^((centos|debian|ubuntu):.+)' --base-override-sub '$(overrideRegistry)/$1' 36 | publicProjectName: ${{ variables.publicProjectName }} 37 | internalProjectName: ${{ variables.internalProjectName }} 38 | -------------------------------------------------------------------------------- /eng/pipelines/cleanup-acr-images-custom.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | pr: none 3 | 4 | variables: 5 | - template: templates/variables/common.yml 6 | 7 | jobs: 8 | - job: Build 9 | pool: 10 | vmImage: $(defaultLinuxAmd64PoolImage) 11 | steps: 12 | - template: ../common/templates/steps/init-docker-linux.yml 13 | - template: ../common/templates/steps/clean-acr-images.yml 14 | parameters: 15 | internalProjectName: ${{ variables.internalProjectName }} 16 | repo: $(repo) 17 | subscription: $(acr.subscription) 18 | resourceGroup: $(acr.resourceGroup) 19 | acr: $(acr.server) 20 | action: $(action) 21 | age: $(age) 22 | customArgs: $(customArgs) 23 | -------------------------------------------------------------------------------- /eng/pipelines/dotnet-buildtools-image-builder-official.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | batch: true 3 | branches: 4 | include: 5 | - main 6 | paths: 7 | include: 8 | - src/* 9 | pr: none 10 | 11 | parameters: 12 | - name: sourceBuildPipelineRunId 13 | displayName: > 14 | Source build pipeline run ID. This refers to runs of *this pipeline*. 15 | Override this parametre in combination with disabling the `Build` stage to 16 | test or publish images that were build in a different pipeline run. 17 | The default value should be left alone if you want to build new images. 18 | type: string 19 | default: $(Build.BuildId) 20 | 21 | variables: 22 | - template: /eng/pipelines/templates/variables/image-builder.yml@self 23 | parameters: 24 | sourceBuildPipelineRunId: ${{ parameters.sourceBuildPipelineRunId }} 25 | - name: publishEolAnnotations 26 | value: true 27 | 28 | extends: 29 | template: /eng/common/templates/1es-official.yml@self 30 | parameters: 31 | serviceConnections: 32 | - name: $(internal-mirror.serviceConnectionName) 33 | - name: $(build.serviceConnectionName) 34 | - name: $(publish.serviceConnectionName) 35 | - name: $(kusto.serviceConnectionName) 36 | - name: $(marStatus.serviceConnectionName) 37 | stages: 38 | - template: /eng/common/templates/stages/dotnet/build-test-publish-repo.yml@self 39 | parameters: 40 | noCache: true 41 | internalProjectName: ${{ variables.internalProjectName }} 42 | publicProjectName: ${{ variables.publicProjectName }} 43 | sourceBuildPipelineRunId: ${{ parameters.sourceBuildPipelineRunId }} 44 | -------------------------------------------------------------------------------- /eng/pipelines/dotnet-buildtools-image-builder-pr.yml: -------------------------------------------------------------------------------- 1 | pr: 2 | branches: 3 | include: 4 | - main 5 | - feature/* 6 | paths: 7 | include: 8 | - src/* 9 | 10 | trigger: none 11 | 12 | variables: 13 | - template: templates/variables/image-builder.yml 14 | 15 | stages: 16 | - template: ../common/templates/stages/dotnet/build-test-publish-repo.yml 17 | parameters: 18 | buildMatrixType: platformVersionedOs 19 | noCache: true 20 | internalProjectName: ${{ variables.internalProjectName }} 21 | publicProjectName: ${{ variables.publicProjectName }} 22 | -------------------------------------------------------------------------------- /eng/pipelines/dotnet-docker-tools-eng-validation-pr.yml: -------------------------------------------------------------------------------- 1 | pr: 2 | branches: 3 | include: 4 | - main 5 | paths: 6 | include: 7 | - eng/* 8 | - test/* 9 | 10 | trigger: none 11 | 12 | variables: 13 | - template: templates/variables/eng-validation.yml 14 | 15 | stages: 16 | - template: ../common/templates/stages/dotnet/build-test-publish-repo.yml 17 | parameters: 18 | buildMatrixType: platformVersionedOs 19 | noCache: true 20 | internalProjectName: ${{ variables.internalProjectName }} 21 | publicProjectName: ${{ variables.publicProjectName }} 22 | -------------------------------------------------------------------------------- /eng/pipelines/dotnet-docker-tools-eng-validation.yml: -------------------------------------------------------------------------------- 1 | pr: none 2 | 3 | trigger: 4 | branches: 5 | include: 6 | - main 7 | paths: 8 | include: 9 | - eng/* 10 | - test/* 11 | 12 | variables: 13 | - template: /eng/pipelines/templates/variables/eng-validation.yml@self 14 | 15 | extends: 16 | template: /eng/common/templates/1es-official.yml@self 17 | parameters: 18 | serviceConnections: 19 | - name: $(internal-mirror.serviceConnectionName) 20 | - name: $(build.serviceConnectionName) 21 | - name: $(publish.serviceConnectionName) 22 | - name: $(kusto.serviceConnectionName) 23 | - name: $(marStatus.serviceConnectionName) 24 | stages: 25 | - template: /eng/common/templates/stages/dotnet/build-test-publish-repo.yml@self 26 | parameters: 27 | noCache: true 28 | internalProjectName: ${{ variables.internalProjectName }} 29 | publicProjectName: ${{ variables.publicProjectName }} 30 | -------------------------------------------------------------------------------- /eng/pipelines/mirror-base-images.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | pr: none 3 | 4 | schedules: 5 | - cron: "0 0,6,12,18 * * *" 6 | displayName: Daily build 7 | branches: 8 | include: 9 | - main 10 | always: true 11 | 12 | parameters: 13 | - name: dryRun 14 | displayName: Dry Run 15 | type: boolean 16 | default: false 17 | 18 | variables: 19 | - template: /eng/common/templates/variables/dotnet/common.yml@self 20 | - name: mirrorRepoPrefix 21 | value: "" 22 | 23 | extends: 24 | template: /eng/common/templates/1es-official.yml@self 25 | parameters: 26 | serviceConnections: 27 | - name: $(public-mirror.serviceConnectionName) 28 | stages: 29 | - stage: MirrorBaseImages 30 | displayName: Mirror Base Images 31 | dependsOn: [] 32 | jobs: 33 | - template: /eng/pipelines/templates/jobs/copy-base-images-public-mirror.yml@self 34 | parameters: 35 | name: "Public" 36 | subscriptionsPath: eng/check-base-image-subscriptions.json 37 | dryRun: ${{ parameters.dryRun }} 38 | - template: /eng/pipelines/templates/jobs/copy-base-images-public-mirror.yml@self 39 | parameters: 40 | name: "Public_Buildtools" 41 | subscriptionsPath: eng/check-base-image-subscriptions-buildtools.json 42 | dryRun: ${{ parameters.dryRun }} 43 | -------------------------------------------------------------------------------- /eng/pipelines/push-common-updates.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | batch: true 3 | branches: 4 | include: 5 | - main 6 | paths: 7 | include: 8 | - eng/common/* 9 | pr: none 10 | 11 | variables: 12 | - template: templates/variables/common.yml 13 | 14 | jobs: 15 | - job: Build 16 | pool: 17 | vmImage: $(defaultLinuxAmd64PoolImage) 18 | steps: 19 | - script: > 20 | docker build . -f ./eng/src/file-pusher/Dockerfile -t file-pusher 21 | displayName: Build File Pusher 22 | - script: > 23 | docker run --rm file-pusher 24 | $(filters) 25 | ./eng/eng-common-file-pusher-config.json 26 | $(dotnetDockerBot.userName) 27 | $(dotnetDockerBot.email) 28 | $(BotAccount-dotnet-docker-bot-PAT) 29 | displayName: Run File Pusher 30 | -------------------------------------------------------------------------------- /eng/pipelines/secret-management-weekly.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | 3 | schedules: 4 | - cron: 0 12 * * 0 5 | displayName: Weekly Sunday build 6 | branches: 7 | include: 8 | - main 9 | always: true 10 | 11 | variables: 12 | - template: templates/variables/common.yml 13 | 14 | extends: 15 | template: /eng/common/templates/1es-unofficial.yml@self 16 | parameters: 17 | stages: 18 | - stage: SynchronizeSecrets 19 | jobs: 20 | - job: Synchronize 21 | displayName: Synchronize secrets 22 | steps: 23 | - task: UseDotNet@2 24 | displayName: Install .NET 8.0 SDK 25 | inputs: 26 | packageType: sdk 27 | version: 8.0.x 28 | installationPath: '$(Build.Repository.LocalPath)/.dotnet' 29 | 30 | - task: UseDotNet@2 31 | displayName: Install .NET 6.0 runtime 32 | inputs: 33 | packageType: runtime 34 | version: 6.0.x 35 | installationPath: '$(Build.Repository.LocalPath)/.dotnet' 36 | 37 | - powershell: .dotnet/dotnet tool restore --tool-manifest .config/dotnet-tools.json 38 | workingDirectory: $(Build.Repository.LocalPath) 39 | displayName: Restore secret-manager 40 | 41 | - task: AzureCLI@2 42 | inputs: 43 | azureSubscription: DotNet Eng Services Secret Manager 44 | scriptType: pscore 45 | scriptLocation: inlineScript 46 | inlineScript: | 47 | Get-ChildItem .vault-config/*.yaml |% { .dotnet/dotnet secret-manager synchronize $_} 48 | workingDirectory: $(Build.Repository.LocalPath) 49 | displayName: Run secret-manager synchronize 50 | -------------------------------------------------------------------------------- /eng/pipelines/templates/jobs/copy-base-images-public-mirror.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: name 3 | type: string 4 | default: null 5 | - name: subscriptionsPath 6 | type: string 7 | default: null 8 | - name: customInitSteps 9 | type: stepList 10 | default: [] 11 | - name: dryRun 12 | type: boolean 13 | default: false 14 | 15 | jobs: 16 | - template: /eng/common/templates/jobs/copy-base-images.yml@self 17 | parameters: 18 | name: MirrorBaseImages_${{ parameters.name }} 19 | pool: 20 | name: $(default1ESInternalPoolName) 21 | image: $(default1ESInternalPoolImage) 22 | os: linux 23 | acr: 24 | server: $(public-mirror.server) 25 | serviceConnection: 26 | id: $(public-mirror.serviceConnection.id) 27 | tenantId: $(public-mirror.serviceConnection.tenantId) 28 | clientId: $(public-mirror.serviceConnection.clientId) 29 | subscription: $(public-mirror.subscription) 30 | resourceGroup: $(public-mirror.resourceGroup) 31 | repoPrefix: $(mirrorRepoPrefix) 32 | customInitSteps: ${{ parameters.customInitSteps }} 33 | additionalOptions: '--subscriptions-path ${{ parameters.subscriptionsPath }}' 34 | forceDryRun: ${{ parameters.dryRun }} 35 | -------------------------------------------------------------------------------- /eng/pipelines/templates/variables/build-test-publish.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: sourceBuildPipelineRunId 3 | type: string 4 | default: '' 5 | 6 | variables: 7 | - template: /eng/pipelines/templates/variables/common.yml@self 8 | parameters: 9 | sourceBuildPipelineRunId: ${{ parameters.sourceBuildPipelineRunId }} 10 | - template: /eng/common/templates/variables/dotnet/build-test-publish.yml@self 11 | - name: publicSourceBranch 12 | value: main 13 | -------------------------------------------------------------------------------- /eng/pipelines/templates/variables/common.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: sourceBuildPipelineRunId 3 | type: string 4 | default: '' 5 | 6 | variables: 7 | - template: /eng/common/templates/variables/dotnet/common.yml@self 8 | parameters: 9 | sourceBuildPipelineRunId: ${{ parameters.sourceBuildPipelineRunId }} 10 | -------------------------------------------------------------------------------- /eng/pipelines/templates/variables/eng-validation.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | - template: build-test-publish.yml 3 | - name: manifest 4 | value: eng/tests/pipeline-validation/test-manifest.json 5 | - name: testScriptPath 6 | value: ./eng/tests/pipeline-validation/run-tests.ps1 7 | - name: testResultsDirectory 8 | value: eng/tests/pipeline-validation/TestResults/ 9 | - name: publicGitRepoUri 10 | value: https://github.com/dotnet/dotnet-docker-test 11 | - name: publishRepoPrefix 12 | value: test/ 13 | - name: imageInfoVariant 14 | value: "-test" 15 | -------------------------------------------------------------------------------- /eng/pipelines/templates/variables/image-builder.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: sourceBuildPipelineRunId 3 | type: string 4 | default: '$(Build.BuildId)' 5 | 6 | variables: 7 | - template: /eng/pipelines/templates/variables/build-test-publish.yml@self 8 | parameters: 9 | sourceBuildPipelineRunId: ${{ parameters.sourceBuildPipelineRunId }} 10 | - name: manifest 11 | value: src/Microsoft.DotNet.ImageBuilder/manifest.json 12 | - name: publishReadme 13 | value: false 14 | - name: manifestVariables 15 | value: --var UniqueId=${{ parameters.sourceBuildPipelineRunId }} 16 | - name: imageInfoVariant 17 | value: "-imagebuilder" 18 | - name: testScriptPath 19 | value: ./src/Microsoft.DotNet.ImageBuilder/run-tests.ps1 20 | - name: testResultsDirectory 21 | value: src/Microsoft.DotNet.ImageBuilder/tests/TestResults/ 22 | - name: publicGitRepoUri 23 | value: https://github.com/dotnet/docker-tools 24 | -------------------------------------------------------------------------------- /eng/pipelines/update-image-builder-tag.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | pr: none 3 | 4 | resources: 5 | pipelines: 6 | - pipeline: image-builder 7 | source: docker-tools-imagebuilder 8 | trigger: 9 | branches: 10 | include: 11 | - main 12 | stages: 13 | - Publish 14 | 15 | variables: 16 | - template: templates/variables/common.yml 17 | 18 | jobs: 19 | - job: Build 20 | pool: 21 | vmImage: $(defaultLinuxAmd64PoolImage) 22 | steps: 23 | - download: image-builder 24 | artifact: source-build-id 25 | displayName: Download Source Build ID artifact 26 | - script: > 27 | echo "##vso[task.setvariable variable=imageBuilderTag]$(cat $(Pipeline.Workspace)/image-builder/source-build-id/source-build-id.txt)" 28 | displayName: Get Image Builder Tag 29 | - script: > 30 | docker build . -f ./eng/src/yaml-updater/Dockerfile -t yaml-updater 31 | displayName: Build YAML Updater 32 | - script: > 33 | docker run --rm yaml-updater 34 | ./eng/image-builder-tag-update-config.json 35 | variables/imageNames.imageBuilderName 36 | mcr.microsoft.com/dotnet-buildtools/image-builder:$(imageBuilderTag) 37 | $(dotnetDockerBot.userName) 38 | $(dotnetDockerBot.email) 39 | $(BotAccount-dotnet-docker-bot-PAT) 40 | dotnet 41 | docker-tools 42 | main 43 | displayName: Run YAML Updater 44 | -------------------------------------------------------------------------------- /eng/pipelines/upload-file.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | pr: none 3 | 4 | parameters: 5 | - name: containerName 6 | displayName: Blob Container Name 7 | type: string 8 | - name: sourceFilePath 9 | displayName: Source File Path 10 | type: string 11 | - name: destName 12 | displayName: Destination Name 13 | type: string 14 | 15 | variables: 16 | - template: /eng/pipelines/templates/variables/common.yml@self 17 | 18 | extends: 19 | template: /eng/common/templates/1es-official.yml@self 20 | parameters: 21 | stages: 22 | - stage: UploadFile 23 | displayName: Upload File 24 | jobs: 25 | - job: Execute 26 | steps: 27 | - script: > 28 | az storage blob upload 29 | --account-name $(dotnetBinaries.accountName) 30 | --account-key $(dotnetbinaries-accountkey) 31 | --container-name ${{ parameters.containerName }} 32 | --file $(Build.SourcesDirectory)/${{ parameters.sourceFilePath }} 33 | --name ${{ parameters.destName }} 34 | displayName: Upload File 35 | -------------------------------------------------------------------------------- /eng/src/file-pusher/AzDoSafeTraceListenerWrapper.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Diagnostics; 6 | 7 | namespace FilePusher; 8 | 9 | public class AzDoSafeTraceListenerWrapper(TraceListener innerTraceListener) : TraceListener 10 | { 11 | private readonly TraceListener _innerTraceListener = innerTraceListener; 12 | 13 | public override void Write(string? message) 14 | { 15 | _innerTraceListener.Write(EscapeVsoDirectives(message)); 16 | } 17 | 18 | public override void WriteLine(string? message) 19 | { 20 | _innerTraceListener.WriteLine(EscapeVsoDirectives(message)); 21 | } 22 | 23 | /// 24 | /// This method "escapes" Azure DevOps Pipeline tasks/variable assignments in strings so that they are safe to 25 | /// output to the console in pipeline. 26 | /// This prevents issues like https://github.com/dotnet/docker-tools/issues/1388, where pushing files containing 27 | /// these AzDO variable assignments results in pipeline failure. 28 | /// Azure DevOps documentation: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/set-variables-scripts 29 | /// 30 | private static string? EscapeVsoDirectives(string? message) 31 | { 32 | return message?.Replace("##vso", "#VSO_DIRECTIVE"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /eng/src/file-pusher/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile is intended to be built at the root of the repo. 2 | 3 | # build image 4 | FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build-env 5 | 6 | WORKDIR /file-pusher 7 | 8 | # copy csproj and restore as distinct layers 9 | COPY eng/src/file-pusher/*.csproj ./ 10 | COPY NuGet.config ./ 11 | RUN dotnet restore 12 | 13 | # copy everything else and build 14 | COPY eng/src/file-pusher/. ./ 15 | RUN dotnet publish -c Release -o out --no-restore 16 | 17 | 18 | # runtime image 19 | FROM mcr.microsoft.com/dotnet/runtime:9.0-alpine 20 | 21 | # copy file-pusher 22 | WORKDIR /file-pusher 23 | COPY --from=build-env /file-pusher/out ./ 24 | 25 | # copy repo 26 | WORKDIR /repo 27 | COPY . ./ 28 | 29 | ENTRYPOINT ["/file-pusher/file-pusher"] 30 | -------------------------------------------------------------------------------- /eng/src/file-pusher/Models/Config.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using Newtonsoft.Json; 6 | 7 | namespace FilePusher.Models 8 | { 9 | public class Config 10 | { 11 | [JsonProperty(Required = Required.Always)] 12 | public string CommitMessage { get; set; } = string.Empty; 13 | 14 | public string? PullRequestDescription { get; set; } 15 | 16 | [JsonProperty(Required = Required.Always)] 17 | public string PullRequestTitle { get; set; } = string.Empty; 18 | 19 | [JsonProperty(Required = Required.Always)] 20 | public string SourcePath { get; set; } = string.Empty; 21 | 22 | [JsonProperty(Required = Required.Always)] 23 | public string WorkingBranchSuffix { get; set; } = string.Empty; 24 | 25 | public GitRepo[] Repos { get; set; } = System.Array.Empty(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /eng/src/file-pusher/Models/GitRepo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using Newtonsoft.Json; 6 | 7 | namespace FilePusher.Models 8 | { 9 | public class GitRepo 10 | { 11 | [JsonProperty(Required = Required.Always)] 12 | public string Owner { get; set; } = string.Empty; 13 | 14 | [JsonProperty(Required = Required.Always)] 15 | public string Name { get; set; } = string.Empty; 16 | 17 | [JsonProperty(Required = Required.Always)] 18 | public string Branch { get; set; } = string.Empty; 19 | 20 | public override string ToString() => $"{Owner}/{Name}/{Branch}"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /eng/src/file-pusher/Options.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.CommandLine; 7 | using System.Linq; 8 | 9 | namespace FilePusher 10 | { 11 | public class Options 12 | { 13 | public IEnumerable Filters { get; set; } = Enumerable.Empty(); 14 | public string GitEmail { get; set; } = string.Empty; 15 | public string GitAuthToken { get; set; } = string.Empty; 16 | public string GitUser { get; set; } = string.Empty; 17 | public string ConfigPath { get; set; } = string.Empty; 18 | 19 | public static IEnumerable GetCliOptions() => 20 | new Symbol[] 21 | { 22 | new Option("--filter", () => Array.Empty(), 23 | "Filter to apply to repositories of the config json - wildcard chars * and ? supported") 24 | { 25 | Name = nameof(Filters), 26 | AllowMultipleArgumentsPerToken = false 27 | }, 28 | new Argument(nameof(ConfigPath), "Path to the config json file"), 29 | new Argument(nameof(GitUser), "GitHub user used to make PR"), 30 | new Argument(nameof(GitEmail), "GitHub email used to make PR"), 31 | new Argument(nameof(GitAuthToken), "GitHub authorization token used to make PR") 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /eng/src/file-pusher/file-pusher.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | FilePusher 7 | latest 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /eng/src/yaml-updater/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile is intended to be built at the root of the repo. 2 | 3 | # build image 4 | FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build-env 5 | 6 | WORKDIR /src 7 | 8 | # copy csproj and restore as distinct layers 9 | COPY eng/src/file-pusher/*.csproj ./file-pusher/ 10 | COPY eng/src/yaml-updater/*.csproj ./yaml-updater/ 11 | COPY NuGet.config ./ 12 | 13 | RUN dotnet restore ./yaml-updater/*.csproj 14 | 15 | # copy everything else and build 16 | COPY eng/src/file-pusher/. ./file-pusher/ 17 | COPY eng/src/yaml-updater/. ./yaml-updater/ 18 | RUN dotnet publish ./yaml-updater/*.csproj -c Release -o out --no-restore 19 | 20 | 21 | # runtime image 22 | FROM mcr.microsoft.com/dotnet/runtime:9.0-alpine 23 | 24 | # copy yaml-updater 25 | WORKDIR /yaml-updater 26 | COPY --from=build-env /src/out ./ 27 | 28 | # copy repo 29 | WORKDIR /repo 30 | COPY . ./ 31 | 32 | ENTRYPOINT ["/yaml-updater/yaml-updater"] 33 | -------------------------------------------------------------------------------- /eng/src/yaml-updater/yaml-updater.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | YamlUpdater 7 | latest 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /eng/tests/pipeline-validation/1.0/nanoserver-1809/amd64/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/windows/nanoserver:1809 2 | -------------------------------------------------------------------------------- /eng/tests/pipeline-validation/1.0/noble/amd64/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu.azurecr.io/ubuntu:noble 2 | -------------------------------------------------------------------------------- /eng/tests/pipeline-validation/1.0/noble/arm64v8/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu.azurecr.io/ubuntu:noble 2 | -------------------------------------------------------------------------------- /eng/tests/pipeline-validation/README.placeholder.md: -------------------------------------------------------------------------------- 1 | # Test Readme 2 | 3 | A placeholder file for testing purposes. The image tags and URLs referenced here are not meant to be accessible. This is just test data. 4 | 5 | # Full Tag Listing 6 | 7 | ## Linux amd64 Tags 8 | Tags | Dockerfile | OS Version 9 | -----------| -------------| ------------- 10 | noble | [Dockerfile](https://github.com/dotnet/dotnet-docker-test/blob/main/test/pipeline-validation/1.0/noble/amd64/Dockerfile) | Ubuntu 24.04 11 | 12 | ## Linux arm64 Tags 13 | Tags | Dockerfile | OS Version 14 | -----------| -------------| ------------- 15 | noble-arm64 | [Dockerfile](https://github.com/dotnet/dotnet-docker-test/blob/main/test/pipeline-validation/1.0/noble/arm64v8/Dockerfile) | Ubuntu 24.04 16 | 17 | ## Windows Server 2019 amd64 Tags 18 | Tag | Dockerfile 19 | ---------| --------------- 20 | nanoserver-1809 | [Dockerfile](https://github.com/dotnet/dotnet-docker-test/blob/main/test/pipeline-validation/1.0/nanoserver-1809/amd64/Dockerfile) 21 | 22 | # Next Header 23 | -------------------------------------------------------------------------------- /eng/tests/pipeline-validation/run-tests.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | # 3 | # Copyright (c) .NET Foundation and contributors. All rights reserved. 4 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 5 | # 6 | 7 | [cmdletbinding()] 8 | param( 9 | [string]$Version, 10 | [string]$Architecture, 11 | [string[]]$Paths, 12 | [string[]]$OSVersions, 13 | [string]$Registry, 14 | [string]$RepoPrefix, 15 | [switch]$DisableHttpVerification, 16 | [switch]$PullImages, 17 | [string]$ImageInfoPath, 18 | [ValidateSet("functional", "pre-build")] 19 | [string[]]$TestCategories = @("functional") 20 | ) 21 | 22 | # This script intentionally doesn't run any tests. It is to be used for pipeline validation. 23 | 24 | # Ensure that a TestResults folder exists to allow test pipeline to target folder for copying. 25 | $scriptDir = Split-Path -parent $PSCommandPath 26 | New-Item -ItemType Directory $scriptDir/TestResults 27 | -------------------------------------------------------------------------------- /eng/tests/pipeline-validation/templates/README.md: -------------------------------------------------------------------------------- 1 | # Test Readme 2 | 3 | A placeholder file for testing purposes. The image tags and URLs referenced here are not meant to be accessible. This is just test data. 4 | 5 | # Full Tag Listing 6 | 7 | ## Linux amd64 Tags 8 | Tags | Dockerfile | OS Version 9 | -----------| -------------| ------------- 10 | noble | [Dockerfile](https://github.com/dotnet/dotnet-docker-test/blob/main/test/pipeline-validation/1.0/noble/amd64/Dockerfile) | Ubuntu 24.04 11 | 12 | ## Linux arm64 Tags 13 | Tags | Dockerfile | OS Version 14 | -----------| -------------| ------------- 15 | noble-arm64 | [Dockerfile](https://github.com/dotnet/dotnet-docker-test/blob/main/test/pipeline-validation/1.0/noble/arm64v8/Dockerfile) | Ubuntu 24.04 16 | 17 | ## Windows Server 2019 amd64 Tags 18 | Tag | Dockerfile 19 | ---------| --------------- 20 | nanoserver-1809 | [Dockerfile](https://github.com/dotnet/dotnet-docker-test/blob/main/test/pipeline-validation/1.0/nanoserver-1809/amd64/Dockerfile) 21 | 22 | # Next Header 23 | -------------------------------------------------------------------------------- /eng/tests/pipeline-validation/templates/test-tags.yml: -------------------------------------------------------------------------------- 1 | $(McrTagsYmlRepo:test) 2 | $(McrTagsYmlTagGroup:noble) 3 | $(McrTagsYmlTagGroup:noble-arm64) 4 | $(McrTagsYmlTagGroup:nanoserver-1809) 5 | -------------------------------------------------------------------------------- /eng/tests/pipeline-validation/test-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "registry": "mcr.microsoft.com", 3 | "repos": [ 4 | { 5 | "id": "test", 6 | "name": "dotnet/test", 7 | "readme": "README.placeholder.md", 8 | "readmeTemplate": "templates/README.md", 9 | "mcrTagsMetadataTemplate": "templates/test-tags.yml", 10 | "images": [ 11 | { 12 | "platforms": [ 13 | { 14 | "dockerfile": "1.0/noble/amd64", 15 | "os": "linux", 16 | "osVersion": "noble", 17 | "tags": { 18 | "noble": {} 19 | } 20 | }, 21 | { 22 | "architecture": "arm64", 23 | "dockerfile": "1.0/noble/arm64v8", 24 | "os": "linux", 25 | "osVersion": "noble", 26 | "tags": { 27 | "noble-arm64": {} 28 | }, 29 | "variant": "v8" 30 | }, 31 | { 32 | "dockerfile": "1.0/nanoserver-1809/amd64", 33 | "os": "windows", 34 | "osVersion": "nanoserver-1809", 35 | "tags": { 36 | "nanoserver-1809": {} 37 | } 38 | } 39 | ] 40 | } 41 | ] 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/.dockerignore: -------------------------------------------------------------------------------- 1 | **/bin 2 | **/obj 3 | **/out 4 | **/.vscode 5 | **/.vs 6 | **/Dockerfile.windows 7 | **/Dockerfile.linux 8 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/Dockerfile.linux: -------------------------------------------------------------------------------- 1 | # Use this Dockerfile to create a runner image 2 | # docker build -t image-builder . 3 | # docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v :/repo -w /repo image-builder 4 | 5 | # build Microsoft.DotNet.ImageBuilder 6 | FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0-azurelinux3.0 AS build-env 7 | ARG TARGETARCH 8 | 9 | # download oras package tarball 10 | WORKDIR / 11 | RUN oras_version=1.2.2 \ 12 | && curl -fSL --output oras_linux.tar.gz https://github.com/oras-project/oras/releases/download/v${oras_version}/oras_${oras_version}_linux_${TARGETARCH}.tar.gz \ 13 | && mkdir -p oras-install/ \ 14 | && tar -zxf oras_linux.tar.gz -C oras-install/ \ 15 | && rm -rf oras_linux.tar.gz 16 | 17 | WORKDIR /image-builder 18 | 19 | # restore packages before copying entire source - provides optimizations when rebuilding 20 | COPY NuGet.config ./ 21 | COPY src/Microsoft.DotNet.ImageBuilder.csproj ./src/ 22 | RUN dotnet restore -r linux-$TARGETARCH ./src/Microsoft.DotNet.ImageBuilder.csproj 23 | 24 | # copy everything else and publish 25 | COPY . ./ 26 | RUN dotnet publish -r linux-$TARGETARCH ./src/Microsoft.DotNet.ImageBuilder.csproj --self-contained=true --no-restore -o out 27 | 28 | 29 | # build runtime image 30 | FROM mcr.microsoft.com/dotnet/runtime-deps:9.0-azurelinux3.0 31 | 32 | # install tooling 33 | RUN tdnf install -y \ 34 | moby-engine \ 35 | docker-cli \ 36 | docker-buildx \ 37 | git \ 38 | && tdnf clean all 39 | 40 | # install oras tool 41 | COPY --from=build-env ["/oras-install/oras", "/usr/local/bin"] 42 | 43 | # install image-builder 44 | WORKDIR /image-builder 45 | COPY --from=build-env /image-builder/out ./ 46 | 47 | ENTRYPOINT ["/image-builder/Microsoft.DotNet.ImageBuilder"] 48 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/README.md: -------------------------------------------------------------------------------- 1 | # ImageBuilder 2 | 3 | ImageBuilder is a tool used to build and publish Docker images. 4 | 5 | ## Building the ImageBuilder container image 6 | 7 | All commands are relative to the root of the repo. 8 | 9 | ### Build a single-platform image 10 | 11 | Using Linux or Windows, simply run the build script: 12 | 13 | ```pwsh 14 | # From src/Microsoft.DotNet.ImageBuilder 15 | pwsh -f build.ps1 16 | 17 | # From the root of the repo 18 | pwsh -wd ./src/Microsoft.DotNet.ImageBuilder/ -f src/Microsoft.DotNet.ImageBuilder/build.ps1 19 | ``` 20 | 21 | ### Build a multi-arch Linux image 22 | 23 | If you don't need to test on Windows, this is the easiest way to create a multi-arch manifest list. 24 | 25 | ```pwsh 26 | # Build the image. Choose one or both platforms, and optionally push to a registry or load the image locally. 27 | docker buildx build [--push,--load] --platform [linux/amd64,linux/arm64] -t "${REPO}:${TAG}" -f .\src\Microsoft.DotNet.ImageBuilder\Dockerfile.linux .\src\Microsoft.DotNet.ImageBuilder\ 28 | ``` 29 | 30 | ### Create a multi-platform manifest list 31 | 32 | First, build and push Linux and Windows images separately. 33 | Gather the specific digests for the images you want to put into one manifest list. 34 | Then, create the manifest list and push it: 35 | 36 | ```pwsh 37 | docker manifest create "${REPO}:${TAG}" "${REPO}@sha256:abcde12345" "${REPO}@sha256:fghij67890" 38 | docker manifest push "${REPO}:${TAG}" 39 | ``` 40 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/build.ps1: -------------------------------------------------------------------------------- 1 | Set-StrictMode -Version Latest 2 | $ErrorActionPreference = 'Stop' 3 | 4 | & ./../../eng/common/build.ps1 ` 5 | -Manifest src/Microsoft.DotNet.ImageBuilder/manifest.json ` 6 | -OptionalImageBuilderArgs "--var UniqueId=$(Get-Date -Format yyyyMMddHHmmss)" ` 7 | -Paths "*" 8 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/run-tests.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | # 3 | # Copyright (c) .NET Foundation and contributors. All rights reserved. 4 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 5 | # 6 | 7 | [cmdletbinding()] 8 | param( 9 | [string]$Version, 10 | [string]$Architecture, 11 | [string[]]$Paths, 12 | [string[]]$OSVersions, 13 | [string]$Registry, 14 | [string]$RepoPrefix, 15 | [switch]$DisableHttpVerification, 16 | [switch]$PullImages, 17 | [string]$ImageInfoPath, 18 | [ValidateSet("functional", "pre-build")] 19 | [string[]]$TestCategories = @("functional") 20 | ) 21 | 22 | Set-StrictMode -Version Latest 23 | $ErrorActionPreference = 'Stop' 24 | 25 | $dotnetInstallDir = "$PSScriptRoot/../../.dotnet" 26 | 27 | Push-Location $PSScriptRoot 28 | 29 | if ($TestCategories.Contains("pre-build")) { 30 | Write-Output "There are no pre-build tests" 31 | } 32 | 33 | if ($TestCategories.Contains("functional")) { 34 | try { 35 | & ../../eng/common/Install-DotNetSdk.ps1 $dotnetInstallDir 36 | 37 | $cmd = "$DotnetInstallDir/dotnet test $PSScriptRoot/tests/Microsoft.DotNet.ImageBuilder.Tests.csproj --logger:trx" 38 | 39 | Write-Output "Executing '$cmd'" 40 | Invoke-Expression $cmd 41 | if ($LASTEXITCODE -ne 0) { 42 | throw "Failed: '$cmd'" 43 | } 44 | } 45 | finally { 46 | Pop-Location 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/src/AuthHelper.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using Azure.Core; 9 | using Azure.ResourceManager; 10 | 11 | namespace Microsoft.DotNet.ImageBuilder 12 | { 13 | public static class AuthHelper 14 | { 15 | public static Guid GetTenantId(ILoggerService loggerService, TokenCredential credential) 16 | { 17 | ArmClient armClient = new(credential); 18 | IEnumerable tenants = armClient.GetTenants().ToList() 19 | .Select(tenantResource => tenantResource.Data.TenantId) 20 | .Where(guid => guid != null) 21 | .Select(guid => (Guid)guid); 22 | 23 | if (!tenants.Any()) 24 | { 25 | throw new Exception("Found no tenants for given credential."); 26 | } 27 | 28 | if (tenants.Count() > 1) 29 | { 30 | string allTenantIds = string.Join(' ', tenants.Select(guid => guid.ToString())); 31 | loggerService.WriteMessage("Found more than one tenant. Selecting the first one."); 32 | loggerService.WriteMessage($"Tenants: {allTenantIds}"); 33 | } 34 | 35 | return tenants.First(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/src/AzureScopes.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Microsoft.DotNet.ImageBuilder; 5 | 6 | #nullable enable 7 | internal static class AzureScopes 8 | { 9 | public const string ScopeSuffix = "/.default"; 10 | public const string DefaultAzureManagementScope = "https://management.azure.com" + ScopeSuffix; 11 | public const string ContainerRegistryScope = "https://containerregistry.azure.net" + ScopeSuffix; 12 | public const string McrStatusScope = "api://c00053c3-a979-4ee6-b94e-941881e62d8e" + ScopeSuffix; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/src/AzureTokenCredentialProviderExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Azure.Core; 7 | 8 | namespace Microsoft.DotNet.ImageBuilder; 9 | 10 | #nullable enable 11 | internal static class AzureTokenCredentialProviderExtensions 12 | { 13 | public static ValueTask GetTokenAsync( 14 | this IAzureTokenCredentialProvider provider, 15 | IServiceConnection serviceConnection, 16 | string scope = AzureScopes.DefaultAzureManagementScope) 17 | { 18 | return provider 19 | .GetCredential(serviceConnection, scope) 20 | .GetTokenAsync(new TokenRequestContext([scope]), CancellationToken.None); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/src/BlobsClientExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Octokit; 8 | 9 | #nullable enable 10 | namespace Microsoft.DotNet.ImageBuilder 11 | { 12 | public static class BlobsClientExtensions 13 | { 14 | public static async Task GetFileContentAsync( 15 | this IBlobsClient blobsClient, string repoOwner, string repoName, string fileSha) 16 | { 17 | Blob fileBlob = await blobsClient.Get(repoOwner, repoName, fileSha); 18 | 19 | switch (fileBlob.Encoding.Value) 20 | { 21 | case EncodingType.Utf8: 22 | return fileBlob.Content; 23 | case EncodingType.Base64: 24 | byte[] bytes = Convert.FromBase64String(fileBlob.Content); 25 | return Encoding.UTF8.GetString(bytes); 26 | default: 27 | throw new NotSupportedException( 28 | $"The blob for file SHA '{fileSha}' in repo '{repoOwner}/{repoName}' uses an unsupported encoding: {fileBlob.Encoding}"); 29 | } 30 | } 31 | } 32 | } 33 | #nullable disable 34 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/src/Commands/AzdoTags.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Microsoft.DotNet.ImageBuilder.Commands 5 | { 6 | public static class AzdoTags 7 | { 8 | public const string AutoBuilder = "autobuilder"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/src/Commands/BuildLegInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace Microsoft.DotNet.ImageBuilder.Commands 8 | { 9 | public class BuildLegInfo 10 | { 11 | public string Name { get; set; } 12 | public List<(string Name, string Value)> Variables { get; } = new List<(string, string)>(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/src/Commands/BuildMatrixInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Microsoft.DotNet.ImageBuilder.Commands 9 | { 10 | public class BuildMatrixInfo 11 | { 12 | public string Name { get; set; } 13 | public List Legs { get; } = new List(); 14 | 15 | public IEnumerable OrderedLegs { get => Legs.OrderBy(leg => leg.Name); } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/src/Commands/CommandExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Microsoft.DotNet.ImageBuilder.Commands 5 | { 6 | public static class CommandExtensions 7 | { 8 | public static string GetCommandName(this ICommand command) 9 | { 10 | string commandName = command.GetType().Name.TrimEndString("Command"); 11 | return char.ToLowerInvariant(commandName[0]) + commandName.Substring(1); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Microsoft.DotNet.ImageBuilder/src/Commands/DockerfileFilterOptions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Collections.Generic; 6 | using System.CommandLine; 7 | using static Microsoft.DotNet.ImageBuilder.Commands.CliHelper; 8 | 9 | #nullable enable 10 | namespace Microsoft.DotNet.ImageBuilder.Commands; 11 | 12 | public class DockerfileFilterOptions 13 | { 14 | public IEnumerable Paths { get; set; } = []; 15 | public IEnumerable ProductVersions { get; set; } = []; 16 | } 17 | 18 | public class DockerfileFilterOptionsBuilder 19 | { 20 | public const string PathOptionName = "path"; 21 | 22 | public IEnumerable