├── .github
├── renovate.json
└── workflows
│ ├── ci.yml
│ ├── docker-publish.yml
│ ├── init.yml
│ └── release.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── README.md
├── authentication.md
├── commands
│ ├── images.md
│ ├── manifests.md
│ ├── referrers.md
│ ├── repositories.md
│ ├── settings.md
│ └── tags.md
├── platform-resolution.md
└── settings.md
├── dredge-logo.png
├── global.json
├── src
├── .dockerignore
├── Dockerfile
├── Valleysoft.Dredge.Analyzers
│ ├── SettingsPropertyGenerator.cs
│ └── Valleysoft.Dredge.Analyzers.csproj
├── Valleysoft.Dredge.Tests
│ ├── CompareLayersCommandTests.cs
│ ├── DockerfileCommandTests.cs
│ ├── TestData
│ │ └── DockerfileCommand
│ │ │ ├── fedora
│ │ │ ├── expected-output-format.txt
│ │ │ ├── expected-output-no-format.txt
│ │ │ └── image.json
│ │ │ ├── golang
│ │ │ ├── expected-output-format.txt
│ │ │ ├── expected-output-no-format.txt
│ │ │ └── image.json
│ │ │ ├── mariner
│ │ │ ├── expected-output-format.txt
│ │ │ ├── expected-output-no-format.txt
│ │ │ └── image.json
│ │ │ ├── openjdk
│ │ │ ├── expected-output-format.txt
│ │ │ ├── expected-output-no-format.txt
│ │ │ └── image.json
│ │ │ ├── python
│ │ │ ├── expected-output-format.txt
│ │ │ ├── expected-output-no-format.txt
│ │ │ └── image.json
│ │ │ └── windows
│ │ │ ├── expected-output-format.txt
│ │ │ ├── expected-output-no-format.txt
│ │ │ └── image.json
│ ├── TestHelper.cs
│ ├── Usings.cs
│ └── Valleysoft.Dredge.Tests.csproj
├── Valleysoft.Dredge.sln
└── Valleysoft.Dredge
│ ├── AppSettings.cs
│ ├── CommandHelper.cs
│ ├── Commands
│ ├── CommandWithOptions.cs
│ ├── Image
│ │ ├── CompareCommand.cs
│ │ ├── CompareFilesCommand.cs
│ │ ├── CompareFilesOptions.cs
│ │ ├── CompareLayersCommand.cs
│ │ ├── CompareLayersOptions.cs
│ │ ├── CompareOptionsBase.cs
│ │ ├── DockerfileCommand.cs
│ │ ├── DockerfileOptions.cs
│ │ ├── ImageCommand.cs
│ │ ├── InspectCommand.cs
│ │ ├── InspectOptions.cs
│ │ ├── OsCommand.cs
│ │ ├── OsOptions.cs
│ │ ├── SaveLayersCommand.cs
│ │ └── SaveLayersOptions.cs
│ ├── Manifest
│ │ ├── DigestCommand.cs
│ │ ├── DigestOptions.cs
│ │ ├── GetCommand.cs
│ │ ├── GetOptions.cs
│ │ ├── ManifestCommand.cs
│ │ ├── ResolveCommand.cs
│ │ └── ResolveOptions.cs
│ ├── OptionsBase.cs
│ ├── PlatformOptionsBase.cs
│ ├── Referrer
│ │ ├── ListCommand.cs
│ │ ├── ListOptions.cs
│ │ └── ReferrerCommand.cs
│ ├── RegistryCommandBase.cs
│ ├── Repo
│ │ ├── ListCommand.cs
│ │ ├── ListOptions.cs
│ │ └── RepoCommand.cs
│ ├── Settings
│ │ ├── ClearCacheCommand.cs
│ │ ├── GetCommand.cs
│ │ ├── GetOptions.cs
│ │ ├── OpenCommand.cs
│ │ ├── SetCommand.cs
│ │ ├── SetOptions.cs
│ │ └── SettingsCommand.cs
│ └── Tag
│ │ ├── ListCommand.cs
│ │ ├── ListOptions.cs
│ │ └── TagCommand.cs
│ ├── CompareFilesOutput.cs
│ ├── CompareLayersOutput.cs
│ ├── CompareLayersResult.cs
│ ├── DockerHubHelper.cs
│ ├── DockerRegistryClientFactory.cs
│ ├── DockerRegistryClientWrapper.cs
│ ├── DredgeState.cs
│ ├── FileHelper.cs
│ ├── IDockerRegistryClient.cs
│ ├── IDockerRegistryClientFactory.cs
│ ├── ImageHelper.cs
│ ├── ImageName.cs
│ ├── JsonHelper.cs
│ ├── LinuxOsInfo.cs
│ ├── ManifestHelper.cs
│ ├── Program.cs
│ ├── RegistryHelper.cs
│ ├── Valleysoft.Dredge.csproj
│ └── WindowsOsInfo.cs
└── version.txt
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:best-practices"
5 | ],
6 | "mode": "full"
7 | }
8 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ main, dev ]
6 | pull_request:
7 | branches: [ main, dev ]
8 |
9 | defaults:
10 | run:
11 | working-directory: src
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
19 |
20 | - name: Setup .NET SDK
21 | uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
22 |
23 | - name: Install dependencies
24 | run: dotnet restore
25 |
26 | - name: Build
27 | run: dotnet build -c Release --no-restore
28 |
29 | - name: Test
30 | run: dotnet test --no-restore -v normal -c Release --results-directory test-results -l trx
31 |
32 | - name: Upload Test Results
33 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
34 | if: always()
35 | with:
36 | name: test-results
37 | path: src/test-results/*
38 |
--------------------------------------------------------------------------------
/.github/workflows/docker-publish.yml:
--------------------------------------------------------------------------------
1 | on:
2 | workflow_call:
3 | workflow_dispatch:
4 | pull_request:
5 | branches: [ main, dev ]
6 |
7 | env:
8 | REGISTRY: ghcr.io
9 | IMAGE_NAME: ${{ github.repository }}
10 |
11 | jobs:
12 |
13 | init:
14 | uses: ./.github/workflows/init.yml
15 |
16 | docker:
17 | name: Build Docker Image
18 | runs-on: ubuntu-latest
19 | needs: [ init ]
20 |
21 | steps:
22 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
23 |
24 | - name: Log in to the Container registry
25 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
26 | with:
27 | registry: ${{ env.REGISTRY }}
28 | username: ${{ github.actor }}
29 | password: ${{ secrets.GITHUB_TOKEN }}
30 |
31 | - name: Set up QEMU
32 | uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3
33 |
34 | - name: Set up Docker Buildx
35 | uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
36 |
37 | - name: Build and push
38 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
39 | with:
40 | context: ./src
41 | platforms: linux/amd64,linux/arm64
42 | push: ${{ github.event_name != 'pull_request' }}
43 | tags: >
44 | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.init.outputs.product-version }},
45 | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.init.outputs.product-version-major }},
46 | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
47 | build-args: |
48 | PACKAGE_VERSION=${{ needs.init.outputs.product-version }}
49 |
--------------------------------------------------------------------------------
/.github/workflows/init.yml:
--------------------------------------------------------------------------------
1 | on:
2 | workflow_call:
3 | outputs:
4 | product-version:
5 | value: ${{ jobs.init.outputs.product-version }}
6 | product-version-major:
7 | value: ${{ jobs.init.outputs.product-version-major }}
8 |
9 | jobs:
10 |
11 | init:
12 | name: Initialize
13 | runs-on: ubuntu-latest
14 |
15 | outputs:
16 | product-version: ${{ steps.version.outputs.product-version }}
17 | product-version-major: ${{ steps.version.outputs.product-version-major }}
18 |
19 | steps:
20 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
21 |
22 | - name: Get Version
23 | id: version
24 | working-directory: ./
25 | run: |
26 | productVersion=$(cat version.txt)
27 | majorVersion=$(echo "$productVersion" | cut -d'.' -f1)
28 | echo "product-version=$productVersion" >> $GITHUB_OUTPUT
29 | echo "product-version-major=$majorVersion" >> $GITHUB_OUTPUT
30 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | confirmVersionIsSet:
7 | description: 'Confirm version is set'
8 | type: boolean
9 |
10 | defaults:
11 | run:
12 | working-directory: src
13 |
14 | jobs:
15 |
16 | init:
17 | uses: ./.github/workflows/init.yml
18 |
19 | exe:
20 | name: Build executables
21 | runs-on: ubuntu-latest
22 | needs: init
23 |
24 | strategy:
25 | matrix:
26 | rid:
27 | - win-x64
28 | - win-arm64
29 | - osx-x64
30 | - osx-arm64
31 | - linux-x64
32 | - linux-arm64
33 | - linux-musl-x64
34 | - linux-musl-arm64
35 |
36 | steps:
37 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
38 |
39 | - name: Setup .NET SDK
40 | uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
41 |
42 | - name: Install dependencies
43 | working-directory: src/Valleysoft.Dredge
44 | run: dotnet restore --runtime ${{ matrix.rid }}
45 |
46 | - name: Publish
47 | working-directory: src/Valleysoft.Dredge
48 | env:
49 | PACKAGE_VERSION: ${{ needs.init.outputs.product-version }}
50 | run: dotnet publish -f net9.0 -c Release -p:Version=$PACKAGE_VERSION --no-restore -o ${{ github.workspace }}/publish --runtime ${{ matrix.rid }} --no-self-contained
51 |
52 | - name: Rename output
53 | run: |
54 | if [[ "${{ matrix.rid }}" == *"win"* ]]; then
55 | dredgeExt=".exe"
56 | else
57 | dredgeExt=""
58 | fi
59 |
60 | exeName="dredge-${{ needs.init.outputs.product-version }}-${{ matrix.rid }}${dredgeExt}"
61 | echo "EXE_NAME=${exeName}" >> $GITHUB_ENV
62 | mv ${{ github.workspace }}/publish/dredge${dredgeExt} ${{ github.workspace }}/publish/${exeName}
63 |
64 | - name: Generate checksum
65 | run: sha256sum ${EXE_NAME} >${EXE_NAME}.sha256sum
66 | working-directory: ${{ github.workspace }}/publish
67 |
68 | - name: Save build binaries
69 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
70 | with:
71 | name: dredge-binaries-${{ matrix.rid }}
72 | path: ${{ github.workspace }}/publish
73 |
74 | save-exes:
75 | name: Save executables
76 | needs: exe
77 | runs-on: ubuntu-latest
78 |
79 | steps:
80 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
81 |
82 | - name: Download build binaries
83 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
84 | with:
85 | path: ${{ github.workspace }}/publish
86 |
87 | - name: Move all files
88 | run: |
89 | mv ${{ github.workspace }}/publish/dredge-binaries-*/* ${{ github.workspace }}/publish
90 | rm -r ${{ github.workspace }}/publish/dredge-binaries-*
91 |
92 | - name: Save build binaries
93 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
94 | with:
95 | name: dredge-binaries
96 | path: ${{ github.workspace }}/publish
97 |
98 | nuget:
99 | name: Publish NuGet Package
100 | runs-on: ubuntu-latest
101 | needs: [ init, save-exes ]
102 |
103 | steps:
104 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
105 |
106 | - name: Setup .NET SDK
107 | uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
108 |
109 | - name: Install dependencies
110 | run: dotnet restore
111 |
112 | - name: Build
113 | env:
114 | PACKAGE_VERSION: ${{ needs.init.outputs.product-version }}
115 | run: dotnet build -c Release -p:Version=$PACKAGE_VERSION --no-restore Valleysoft.Dredge
116 |
117 | - name: Pack
118 | env:
119 | PACKAGE_VERSION: ${{ needs.init.outputs.product-version }}
120 | run: dotnet pack -c Release -p:Version=$PACKAGE_VERSION Valleysoft.Dredge -p:IsPack=true
121 |
122 | - name: Publish Package
123 | run: dotnet nuget push "Valleysoft.Dredge/bin/Release/*.nupkg" -k ${{secrets.NUGET_ORG_API_KEY}} -s https://nuget.org
124 |
125 | docker:
126 | uses: ./.github/workflows/docker-publish.yml
127 | needs: [ nuget ]
128 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 | launchSettings.json
13 |
14 | # User-specific files (MonoDevelop/Xamarin Studio)
15 | *.userprefs
16 |
17 | # Mono auto generated files
18 | mono_crash.*
19 |
20 | # Build results
21 | [Dd]ebug/
22 | [Dd]ebugPublic/
23 | [Rr]elease/
24 | [Rr]eleases/
25 | x64/
26 | x86/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # StyleCop
66 | StyleCopReport.xml
67 |
68 | # Files built by Visual Studio
69 | *_i.c
70 | *_p.c
71 | *_h.h
72 | *.ilk
73 | *.meta
74 | *.obj
75 | *.iobj
76 | *.pch
77 | *.pdb
78 | *.ipdb
79 | *.pgc
80 | *.pgd
81 | *.rsp
82 | *.sbr
83 | *.tlb
84 | *.tli
85 | *.tlh
86 | *.tmp
87 | *.tmp_proj
88 | *_wpftmp.csproj
89 | *.log
90 | *.vspscc
91 | *.vssscc
92 | .builds
93 | *.pidb
94 | *.svclog
95 | *.scc
96 |
97 | # Chutzpah Test files
98 | _Chutzpah*
99 |
100 | # Visual C++ cache files
101 | ipch/
102 | *.aps
103 | *.ncb
104 | *.opendb
105 | *.opensdf
106 | *.sdf
107 | *.cachefile
108 | *.VC.db
109 | *.VC.VC.opendb
110 |
111 | # Visual Studio profiler
112 | *.psess
113 | *.vsp
114 | *.vspx
115 | *.sap
116 |
117 | # Visual Studio Trace Files
118 | *.e2e
119 |
120 | # TFS 2012 Local Workspace
121 | $tf/
122 |
123 | # Guidance Automation Toolkit
124 | *.gpState
125 |
126 | # ReSharper is a .NET coding add-in
127 | _ReSharper*/
128 | *.[Rr]e[Ss]harper
129 | *.DotSettings.user
130 |
131 | # TeamCity is a build add-in
132 | _TeamCity*
133 |
134 | # DotCover is a Code Coverage Tool
135 | *.dotCover
136 |
137 | # AxoCover is a Code Coverage Tool
138 | .axoCover/*
139 | !.axoCover/settings.json
140 |
141 | # Visual Studio code coverage results
142 | *.coverage
143 | *.coveragexml
144 |
145 | # NCrunch
146 | _NCrunch_*
147 | .*crunch*.local.xml
148 | nCrunchTemp_*
149 |
150 | # MightyMoose
151 | *.mm.*
152 | AutoTest.Net/
153 |
154 | # Web workbench (sass)
155 | .sass-cache/
156 |
157 | # Installshield output folder
158 | [Ee]xpress/
159 |
160 | # DocProject is a documentation generator add-in
161 | DocProject/buildhelp/
162 | DocProject/Help/*.HxT
163 | DocProject/Help/*.HxC
164 | DocProject/Help/*.hhc
165 | DocProject/Help/*.hhk
166 | DocProject/Help/*.hhp
167 | DocProject/Help/Html2
168 | DocProject/Help/html
169 |
170 | # Click-Once directory
171 | publish/
172 |
173 | # Publish Web Output
174 | *.[Pp]ublish.xml
175 | *.azurePubxml
176 | # Note: Comment the next line if you want to checkin your web deploy settings,
177 | # but database connection strings (with potential passwords) will be unencrypted
178 | *.pubxml
179 | *.publishproj
180 |
181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
182 | # checkin your Azure Web App publish settings, but sensitive information contained
183 | # in these scripts will be unencrypted
184 | PublishScripts/
185 |
186 | # NuGet Packages
187 | *.nupkg
188 | # NuGet Symbol Packages
189 | *.snupkg
190 | # The packages folder can be ignored because of Package Restore
191 | **/[Pp]ackages/*
192 | # except build/, which is used as an MSBuild target.
193 | !**/[Pp]ackages/build/
194 | # Uncomment if necessary however generally it will be regenerated when needed
195 | #!**/[Pp]ackages/repositories.config
196 | # NuGet v3's project.json files produces more ignorable files
197 | *.nuget.props
198 | *.nuget.targets
199 |
200 | # Microsoft Azure Build Output
201 | csx/
202 | *.build.csdef
203 |
204 | # Microsoft Azure Emulator
205 | ecf/
206 | rcf/
207 |
208 | # Windows Store app package directories and files
209 | AppPackages/
210 | BundleArtifacts/
211 | Package.StoreAssociation.xml
212 | _pkginfo.txt
213 | *.appx
214 | *.appxbundle
215 | *.appxupload
216 |
217 | # Visual Studio cache files
218 | # files ending in .cache can be ignored
219 | *.[Cc]ache
220 | # but keep track of directories ending in .cache
221 | !?*.[Cc]ache/
222 |
223 | # Others
224 | ClientBin/
225 | ~$*
226 | *~
227 | *.dbmdl
228 | *.dbproj.schemaview
229 | *.jfm
230 | *.pfx
231 | *.publishsettings
232 | orleans.codegen.cs
233 |
234 | # Including strong name files can present a security risk
235 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
236 | #*.snk
237 |
238 | # Since there are multiple workflows, uncomment next line to ignore bower_components
239 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
240 | #bower_components/
241 |
242 | # RIA/Silverlight projects
243 | Generated_Code/
244 |
245 | # Backup & report files from converting an old project file
246 | # to a newer Visual Studio version. Backup files are not needed,
247 | # because we have git ;-)
248 | _UpgradeReport_Files/
249 | Backup*/
250 | UpgradeLog*.XML
251 | UpgradeLog*.htm
252 | ServiceFabricBackup/
253 | *.rptproj.bak
254 |
255 | # SQL Server files
256 | *.mdf
257 | *.ldf
258 | *.ndf
259 |
260 | # Business Intelligence projects
261 | *.rdl.data
262 | *.bim.layout
263 | *.bim_*.settings
264 | *.rptproj.rsuser
265 | *- [Bb]ackup.rdl
266 | *- [Bb]ackup ([0-9]).rdl
267 | *- [Bb]ackup ([0-9][0-9]).rdl
268 |
269 | # Microsoft Fakes
270 | FakesAssemblies/
271 |
272 | # GhostDoc plugin setting file
273 | *.GhostDoc.xml
274 |
275 | # Node.js Tools for Visual Studio
276 | .ntvs_analysis.dat
277 | node_modules/
278 |
279 | # Visual Studio 6 build log
280 | *.plg
281 |
282 | # Visual Studio 6 workspace options file
283 | *.opt
284 |
285 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
286 | *.vbw
287 |
288 | # Visual Studio LightSwitch build output
289 | **/*.HTMLClient/GeneratedArtifacts
290 | **/*.DesktopClient/GeneratedArtifacts
291 | **/*.DesktopClient/ModelManifest.xml
292 | **/*.Server/GeneratedArtifacts
293 | **/*.Server/ModelManifest.xml
294 | _Pvt_Extensions
295 |
296 | # Paket dependency manager
297 | .paket/paket.exe
298 | paket-files/
299 |
300 | # FAKE - F# Make
301 | .fake/
302 |
303 | # CodeRush personal settings
304 | .cr/personal
305 |
306 | # Python Tools for Visual Studio (PTVS)
307 | __pycache__/
308 | *.pyc
309 |
310 | # Cake - Uncomment if you are using it
311 | # tools/**
312 | # !tools/packages.config
313 |
314 | # Tabs Studio
315 | *.tss
316 |
317 | # Telerik's JustMock configuration file
318 | *.jmconfig
319 |
320 | # BizTalk build output
321 | *.btp.cs
322 | *.btm.cs
323 | *.odx.cs
324 | *.xsd.cs
325 |
326 | # OpenCover UI analysis results
327 | OpenCover/
328 |
329 | # Azure Stream Analytics local run output
330 | ASALocalRun/
331 |
332 | # MSBuild Binary and Structured Log
333 | *.binlog
334 |
335 | # NVidia Nsight GPU debugger configuration file
336 | *.nvuser
337 |
338 | # MFractors (Xamarin productivity tool) working folder
339 | .mfractor/
340 |
341 | # Local History for Visual Studio
342 | .localhistory/
343 |
344 | # BeatPulse healthcheck temp database
345 | healthchecksdb
346 |
347 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
348 | MigrationBackup/
349 |
350 | # Ionide (cross platform F# VS Code tools) working folder
351 | .ionide/
352 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are welcome. Submit a pull request or open an issue as necessary.
4 |
5 | ## Developer Prerequisites
6 |
7 | * [Docker](https://docs.docker.com/get-docker/)
8 | * [.NET 6.0 SDK](https://docs.microsoft.com/dotnet/core/install/)
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Matthew Thalman
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Dredge: A Container Registry Client CLI
4 |
5 | Dredge is a CLI built on .NET that provides a simple way to execute commands on a container registry's HTTP API. Currently, only read operations are supported.
6 |
7 | ## Features
8 |
9 | * Access to raw JSON data from the registry's HTTP API.
10 | * Extended, derived data such as [image configuration](docs/commands/images.md#inspect-image-configuration), [OS information](docs/commands/images.md#image-os-information), and comparison of [layers](docs/commands/images.md#compare-image-layers) and [files](docs/commands/images.md#compare-image-files).
11 |
12 | ### Documentation
13 |
14 | The main documentation is in the [docs](docs) directory.
15 |
16 | ## Install
17 |
18 | ### Installing as a standalone executable
19 |
20 | Download the desired executable from the [release page](https://github.com/mthalman/dredge/releases).
21 |
22 | Prerequisites:
23 | * [.NET 9 runtime](https://dotnet.microsoft.com/download/dotnet/9.0)
24 |
25 | ### Running as a container
26 |
27 | ```shell
28 | docker run --rm ghcr.io/mthalman/dredge --help
29 | ```
30 |
31 | ### Installing as a .NET global tool
32 |
33 | ```console
34 | > dotnet tool install -g Valleysoft.Dredge
35 | ```
36 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Dredge Docs
2 |
3 | * Commands
4 | * [`image`](commands/images.md)
5 | * [`manifest`](commands/manifests.md)
6 | * [`repo`](commands/repositories.md)
7 | * [`tag`](commands/tags.md)
8 | * [`settings`](commands/settings.md)
9 | * [Authentication](authentication.md)
10 | * [Settings file](settings.md)
11 | * [Platform resolution](platform-resolution.md)
12 |
--------------------------------------------------------------------------------
/docs/authentication.md:
--------------------------------------------------------------------------------
1 | # Registry Authentication
2 |
3 | For container registries requiring authentication, Dredge can make use of credentials stored in your environment via the `docker login` command.
4 | Alternatively, you can set the `DREDGE_TOKEN` environment variable to an OAuth bearer token or set the `DREDGE_USERNAME` and `DREDGE_PASSWORD` environment variables if you have credentials.
5 | Dredge will look for the environment variables first and fall back to any `docker login` credentials if they exist.
6 |
--------------------------------------------------------------------------------
/docs/commands/images.md:
--------------------------------------------------------------------------------
1 | # Images
2 |
3 | Sub-commands:
4 |
5 | * [`inspect`](#inspect-image-configuration) - Inspects an image configuration
6 | * [`os`](#image-os-information) - Image OS information
7 | * [`compare layers`](#compare-image-layers) - Compares the layers of two images
8 | * [`compare files`](#compare-image-files) - Compares the files of two images
9 | * [`save layers`](#save-layers) - Saves the layers of an image to disk
10 | * [`dockerfile`](#generate-dockerfile) - Generates a Dockerfile that represents the image
11 |
12 | ## Inspect Image Configuration
13 |
14 | The `image inspect` command returns the image configuration of the specified image name.
15 |
16 | ```console
17 | > dredge image inspect amd64/ubuntu:22.04
18 | {
19 | "architecture": "amd64",
20 | "config": {
21 | "Hostname": "",
22 | "Domainname": "",
23 | "User": "",
24 | "AttachStdin": false,
25 | "AttachStdout": false,
26 | --- ---
27 | "os": "linux",
28 | "rootfs": {
29 | "type": "layers",
30 | "diff_ids": [
31 | "sha256:f4a670ac65b68f8757aea863ac0de19e627c0ea57165abad8094eae512ca7dad"
32 | ]
33 | }
34 | }
35 | ```
36 |
37 | ## Image OS Information
38 |
39 | The `image os` command returns information about the OS of the specified image name. Supports both Linux and Windows images.
40 |
41 | ### Linux
42 |
43 | ```console
44 | > dredge image os amd64/ubuntu:22.04
45 | {
46 | "PRETTY_NAME": "Ubuntu 22.04.1 LTS",
47 | "NAME": "Ubuntu",
48 | "ID": "ubuntu",
49 | "ID_LIKE": [
50 | "debian"
51 | ],
52 | "VERSION": "22.04.1 LTS (Jammy Jellyfish)",
53 | "VERSION_ID": "22.04",
54 | "VERSION_CODENAME": "jammy",
55 | "HOME_URL": "https://www.ubuntu.com/",
56 | "SUPPORT_URL": "https://help.ubuntu.com/",
57 | "BUG_REPORT_URL": "https://bugs.launchpad.net/ubuntu/",
58 | "PRIVACY_POLICY_URL": "https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
59 | }
60 | ```
61 |
62 | ### Windows
63 |
64 | ```console
65 | > dredge image os mcr.microsoft.com/windows/nanoserver:ltsc2022-amd64
66 | {
67 | "Type": "Nano Server",
68 | "Version": "10.0.20348.1249"
69 | }
70 | ```
71 |
72 | ### Compare Image Layers
73 |
74 | The `image compare layers` command compares the layers of two images.
75 |
76 | There are a variety of output options available:
77 |
78 | * SideBySide (default): Displays the comparison side-by-side in a table layout
79 | * Inline: Displays the comparison in an inline fashion
80 | * JSON: Returns a JSON representation of the comparison, including summary analysis
81 |
82 | There's also a `--history` option to include the layer history information associated with the given layer.
83 |
84 | By default, the comparison makes use of green and red colors to indicate differences. For accessibility purposes, you can choose to use the `--no-color` option which will disable the use of these colors and use textual means to indicate diffs instead.
85 |
86 | ```diff
87 | $ dredge image compare layers --output inline amd64/node:19.1-alpine amd64/node:19.2-alpine
88 | sha256:ca7dd9ec2225f2385955c43b2379305acd51543c28cf1d4e94522b3d94cce3ce
89 | - sha256:4487691952c066cb3964b94606825bc96c698377909c7d74c889fd12e24e36a7
90 | + sha256:bfebca31f7556839677aca8626941ec4be0d5e2a1a59f1bd991807828de37167
91 | - sha256:206c50ffab466a0ed68db742d6d2015abcedd0a0b2500babb1938ce2a272b425
92 | + sha256:cc0056ab0c4160f34cd7046016f9aa6d1d14c206f61768b34efa69c45c38a0cb
93 | - sha256:f6d4361cf153f2e83958f504356eef6e3d041eb3c4d23da466480ee2dfe656ae
94 | + sha256:6e25476b6324255c964f6b86e587d867e79046e94933123d0f1312dbddfe87b7
95 | ```
96 |
97 | ```
98 | > dredge image compare layers --history --no-color mcr.microsoft.com/dotnet/runtime:6.0.5-jammy-amd64 mcr.microsoft.com/dotnet/runtime:6.0.6-jammy-amd64
99 | ┌──────────────────────────────────────────────────────────────────────────┬───────────┬─────────────────────────────────────────────────────────────────────────┐
100 | │ mcr.microsoft.com/dotnet/runtime:6.0.5-jammy-amd64 │ Compare │ mcr.microsoft.com/dotnet/runtime:6.0.6-jammy-amd64 │
101 | ├──────────────────────────────────────────────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────┤
102 | │ sha256:405f018f9d1d0f351c196b841a7c7f226fb8ea448acd6339a9ed8741600275a2 │ Equal │ sha256:405f018f9d1d0f351c196b841a7c7f226fb8ea448acd6339a9ed8741600275a2 │
103 | │ /bin/sh -c #(nop) ADD │ │ /bin/sh -c #(nop) ADD │
104 | │ file:11157b07dde10107f3f6f2b892c869ea83868475d5825167b5f466a7e410eb05 in │ │ file:11157b07dde10107f3f6f2b892c869ea83868475d5825167b5f466a7e410eb05 │
105 | │ / │ │ in / │
106 | │ │ │ │
107 | │ │ Equal │ │
108 | │ /bin/sh -c #(nop) CMD ["bash"] │ │ /bin/sh -c #(nop) CMD ["bash"] │
109 | │ │ │ │
110 | │ sha256:7f5199084fb2409a567d45cbe1eebb7ad2bb92d2f2eeac1f9d7d1521b6529da5 │ Not Equal │ sha256:1d6b7ed86f8a0efb7b44af3ac71d881ea686c7e26f2bf9b509ffcee50d503a44 │
111 | │ /bin/sh -c apt-get update && apt-get install -y │ │ /bin/sh -c apt-get update && apt-get install -y │
112 | │ --no-install-recommends ca-certificates libc6 │ │ --no-install-recommends ca-certificates libc6 │
113 | │ libgcc1 libgssapi-krb5-2 libicu70 libssl3 │ │ libgcc1 libgssapi-krb5-2 libicu70 libssl3 │
114 | │ libstdc++6 zlib1g && rm -rf /var/lib/apt/lists/* │ │ libstdc++6 zlib1g && rm -rf /var/lib/apt/lists/* │
115 | │ │ │ │
116 | │ │ Equal │ │
117 | │ /bin/sh -c #(nop) ENV ASPNETCORE_URLS=http://+:80 │ │ /bin/sh -c #(nop) ENV ASPNETCORE_URLS=http://+:80 │
118 | │ DOTNET_RUNNING_IN_CONTAINER=true │ │ DOTNET_RUNNING_IN_CONTAINER=true │
119 | │ │ │ │
120 | │ │ Not Equal │ │
121 | │ /bin/sh -c #(nop) ENV DOTNET_VERSION=6.0.5 │ │ /bin/sh -c #(nop) ENV DOTNET_VERSION=6.0.6 │
122 | │ │ │ │
123 | │ sha256:ae2c6691208b45534916003bf6e5607998bab42aa923dc5f1e21fc244f0a9832 │ Not Equal │ sha256:18a715d5177a41204dd062b5760565bd282526e22063ab158b4180833f5a5156 │
124 | │ /bin/sh -c #(nop) COPY │ │ /bin/sh -c #(nop) COPY │
125 | │ dir:49b45e3ccadd0521a7513b91e6cb00a52ff23f9e8004ce74c832042e93fe7e33 in │ │ dir:fb7195f4bc42fce62a7104cc5ef211701a1267b4666b445b59f649b0f86ecaa6 in │
126 | │ /usr/share/dotnet │ │ /usr/share/dotnet │
127 | │ │ │ │
128 | │ sha256:114810c4073fb2a42557832ebfa76ec9a0f3ddcd13edf20b9f6d690f0d0be720 │ Not Equal │ sha256:6adc839fa9c17fc4a0f1965aa58b446f6531b4b926995080404321a223ce82b2 │
129 | │ /bin/sh -c ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet │ │ /bin/sh -c ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet │
130 | └──────────────────────────────────────────────────────────────────────────┴───────────┴─────────────────────────────────────────────────────────────────────────┘
131 | ```
132 |
133 | ### Compare Image Files
134 |
135 | The `image compare files` command provides a way to compare the file contents of two images.
136 |
137 | Example usage:
138 |
139 | ```shell
140 | > dredge image compare files amd64/node:19.1-alpine amd64/node:19.2-alpine
141 | ```
142 |
143 | The layers of the images are downloaded and applied (squashed) to produce a local representation of the file system for each image.
144 |
145 | The actual comparison of the files requires an external tool provided by the user. This external comparison tool is configured with Dredge's settings.json file.
146 |
147 | Example:
148 |
149 | ```json
150 | {
151 | "fileCompareTool": {
152 | "exePath": "C:\\Program Files\\Beyond Compare 4\\BCompare.exe",
153 | "args": "{0} {1}"
154 | }
155 | }
156 | ```
157 |
158 | In addition to comparing the entire image, you can also include a subset of the image by specifying a layer index in the command options. This will only apply the layers of the image up to the specified index. For example, the following command compares only the first two layers of the images:
159 |
160 | ```shell
161 | > dredge image compare files amd64/node:19.1-alpine amd64/node:19.2-alpine --base-layer-index 1 --target-layer-index 1
162 | ```
163 |
164 | This option also enables you to compare the layers of a single image. This command compares the difference between the 2nd and 3rd layer of the `amd64/node:19.1-alpine` image:
165 |
166 | ```shell
167 | > dredge image compare files amd64/node:19.1-alpine amd64/node:19.1-alpine --base-layer-index 1 --target-layer-index 2
168 | ```
169 |
170 | ### Save Layers
171 |
172 | The `image save-layers` command provides a way to save the extracted layers of an image to disk.
173 |
174 | Example usage:
175 |
176 | ```shell
177 | > dredge image save-layers amd64/node:19.2-alpine out/layers/node
178 | ```
179 |
180 | By default, the layers of the image are squashed and saved to a single directory. The `--no-squash` option can be used to disable this behavior and save the layers as individual directories.
181 |
182 | If you want to target a specific layer, you can use the `--layer-index` option. This will only save the specified layer (and any layers that it depends on if squashing is being applied).
183 |
184 | ## Generate Dockerfile
185 |
186 | The `image dockerfile` command generates a Dockerfile that represents an image.
187 |
188 | By default, it uses a set of heuristics to generate line breaks for a Dockerfile instruction to make it more readable. This can be disabled with the `--no-format` option. The output also uses syntax coloring by default for readability. This can be disabled with the `--no-color` option.
189 |
--------------------------------------------------------------------------------
/docs/commands/manifests.md:
--------------------------------------------------------------------------------
1 | # Manifests
2 |
3 | Sub-commands:
4 |
5 | * [`get`](#query-manifest) - Gets a manifest
6 | * [`digest`](#query-digest) - Gets the digest of a manifest
7 | * [`resolve`](#resolve-manifest) - Resolves a manifest
8 |
9 | ## Query Manifest
10 |
11 | Returns the manifest of the specified name.
12 |
13 | ```console
14 | > dredge manifest get ubuntu:22.04
15 | {
16 | "manifests": [
17 | {
18 | "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
19 | "size": 529,
20 | "digest": "sha256:817cfe4672284dcbfee885b1a66094fd907630d610cab329114d036716be49ba",
21 | "platform": {
22 | "architecture": "amd64",
23 | "os": "linux",
24 | "os.version": null,
25 | "os.features": [],
26 | "variant": null,
27 | "features": []
28 | }
29 | },
30 | --- ---
31 | {
32 | "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
33 | "size": 529,
34 | "digest": "sha256:75f39282185d9d952d5d19491a0c98ed9f798b0251c6d9a026e5b71cc2bf4de3",
35 | "platform": {
36 | "architecture": "s390x",
37 | "os": "linux",
38 | "os.version": null,
39 | "os.features": [],
40 | "variant": null,
41 | "features": []
42 | }
43 | }
44 | ],
45 | "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
46 | "schemaVersion": 2
47 | }
48 | ```
49 |
50 | ## Query Digest
51 |
52 | Returns the digest of the specified name.
53 |
54 | ```console
55 | > dredge manifest digest ubuntu:22.04
56 | sha256:4b1d0c4a2d2aaf63b37111f34eb9fa89fa1bf53dd6e4ca954d47caebca4005c2
57 | ```
58 |
59 | ## Resolve Manifest
60 |
61 | Resolves a manifest to a target platform's fully-qualified image digest. This is useful when you want to get the image digest of a specific platform from a multi-arch tag.
62 |
63 | ```console
64 | > dredge manifest resolve ubuntu:22.04 --os linux --arch amd64
65 | library/ubuntu@sha256:817cfe4672284dcbfee885b1a66094fd907630d610cab329114d036716be49ba
66 | ```
67 |
--------------------------------------------------------------------------------
/docs/commands/referrers.md:
--------------------------------------------------------------------------------
1 | # Referrers
2 |
3 | Sub-commands:
4 |
5 | * [`list`](#query-referrers) - Lists the referrers to a manifest
6 |
7 | ## Query Referrers
8 |
9 | Returns the referrers to the specified manifest.
10 |
11 | ```console
12 | > dredge referrer list mcr.microsoft.com/dotnet/core/sdk:latest
13 | {
14 | "manifests": [
15 | {
16 | "mediaType": "application/vnd.oci.image.manifest.v1+json",
17 | "digest": "sha256:551e9aa2046071e51b1611a7e85f85af3d2cc6841935cc176a931de4194ecdc1",
18 | "size": 788,
19 | "urls": [],
20 | "annotations": {
21 | "org.opencontainers.image.created": "2024-08-13T14:20:19Z",
22 | "vnd.microsoft.artifact.lifecycle.end-of-life.date": "2022-12-13"
23 | },
24 | "artifactType": "application/vnd.microsoft.artifact.lifecycle"
25 | }
26 | ],
27 | "annotations": {},
28 | "schemaVersion": 2,
29 | "mediaType": "application/vnd.oci.image.index.v1+json"
30 | }
31 | ```
32 |
--------------------------------------------------------------------------------
/docs/commands/repositories.md:
--------------------------------------------------------------------------------
1 | # Repositories
2 |
3 | Sub-commands:
4 |
5 | * [`list`](#query-repositories) - Lists the repositories in a registry
6 |
7 | ## Query Repositories
8 |
9 | Returns the list of repositories from the specified registry.
10 |
11 | > Not supported for Docker Hub.
12 |
13 | ```console
14 | > dredge repo list mcr.microsoft.com
15 | [
16 | "acc/samples/acc-perl",
17 | "acc/samples/attestation-inproc",
18 | "acc/samples/attestation-outproc",
19 | "acc/samples/attested-tls-inproc",
20 | "acc/samples/attested-tls-outproc",
21 | --- ---
22 | "windows/servercore/iis",
23 | "windows/servercore/iis/insider",
24 | "windows/servercore/insider",
25 | "windowsprotocoltestsuites",
26 | "wwllab/skills/skills-extractor-cognitive-search"
27 | ]
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/commands/settings.md:
--------------------------------------------------------------------------------
1 | # Settings
2 |
3 | Sub-commands:
4 |
5 | * [`open`](#open-settings) - Opens the Dredge settings file
6 | * [`get`](#get-setting) - Gets the value of a setting
7 | * [`set`](#set-setting) - Sets the value of a setting
8 | * [`clear-cache`](#clear-cache) - Deletes the cached files used by Dredge
9 |
10 | ## Open Settings
11 |
12 | The `settings open` command opens the Dredge settings file in the default associated program if it can.
13 | Otherwise, it outputs the path to the settings file.
14 |
15 | ## Get Setting
16 |
17 | The `settings get` command gets the value of a setting from the Dredge settings file.
18 | It takes a single argument as the name of the setting to get.
19 | Because the settings in the settings file are hierarchical and represented as JSON, setting names use a dot notation to separate the names to access the desired setting.
20 | For example: `dredge settings get fileCompareTool.exePath` gets the executable path of the file compare tool from the settings file.
21 |
22 | ## Set Setting
23 |
24 | The `settings set` command sets the value of a setting from the Dredge settings file.
25 | It takes the name of the setting to set followed by the value to set it to.
26 | Because the settings in the settings file are hierarchical and represented as JSON, setting names use a dot notation to separate the names to access the desired setting.
27 | For example: `dredge settings set platform.os linux` sets the default platform OS to "linux".
28 |
29 | ## Clear Cache
30 |
31 | The `settings clear-cache` command deletes the local cache of layer data stored in the temporary directory.
32 |
--------------------------------------------------------------------------------
/docs/commands/tags.md:
--------------------------------------------------------------------------------
1 | # Tags
2 |
3 | Sub-commands:
4 |
5 | * [`list`](#query-tags) - Lists the tags in a repository
6 |
7 | ## Query Tags
8 |
9 | Returns the tags associated with the specified repository.
10 |
11 | ```console
12 | > dredge tag list ubuntu
13 | [
14 | "10.04",
15 | "12.04",
16 | "12.04.5",
17 | "12.10",
18 | "13.04",
19 | --- ---
20 | "zesty-20170703",
21 | "zesty-20170913",
22 | "zesty-20170915",
23 | "zesty-20171114",
24 | "zesty-20171122"
25 | ]
26 | ```
27 |
--------------------------------------------------------------------------------
/docs/platform-resolution.md:
--------------------------------------------------------------------------------
1 | # Platform Resolution
2 |
3 | For [certain operations](#operations-that-use-platform-resolution), Dredge needs to resolve a platform to a specific image manifest if a multi-arch/multi-platform tag is provided as input.
4 | For example, the `alpine:latest` tag points to different images for different architectures.
5 | For these commands, Dredge needs to know which image to use for the operation.
6 | If it's unable to resolve the platform, it will fail with an error.
7 | You can influence the platform resolution by setting the command's platform options or by setting the global platform settings.
8 |
9 | ## Operations that use platform resolution
10 |
11 | The following operations make use of platform resolution:
12 |
13 | * [`manifest resolve`](commands/manifests.md#resolve-manifest)
14 | * All [`image`](commands/images.md) sub-commands
15 |
16 | ## Platform options
17 |
18 | The following [platform options](https://github.com/mthalman/dredge/pull/52) can be used to influence platform resolution:
19 |
20 | * `--os`: The operating system of the platform ("linux" or "windows").
21 | * `--os-version`: The operating system version of the platform. This is usually only relevant for Windows images but may be relevant for Linux images in some rare cases.
22 | * `--arch`: The architecture of the platform (e.g. "amd64", "arm", "arm64").
23 |
24 | ## Global platform settings
25 |
26 | [Global platform settings](https://github.com/mthalman/dredge/pull/54) allow you to statically define platform settings in the Dredge settings file that will be used for all operations that use platform resolution.
27 | The same platform options can be used as described in the previous section.
28 |
29 | Here's the configuration of these global platform settings:
30 |
31 | ```json
32 | {
33 | "platform": {
34 | "os": "",
35 | "osVersion": "",
36 | "arch": ""
37 | }
38 | }
39 | ```
40 |
41 | You can set these values by using the [`settings set`](commands/settings.md#set-settings) command:
42 |
43 | Example:
44 |
45 | ```console
46 | dredge settings set platform.os linux
47 | ```
48 |
49 | ## Platform resolution order
50 |
51 | Since the platform options and global platform settings can be used to influence platform resolution, it's important to understand the order in which these settings are applied.
52 |
53 | Platform options provided in the call to the command take precedence over global platform settings.
54 | If a platform option is not provided in the call to the command, the global platform setting is used.
55 |
56 | It's not always necessary to set all of the platform values in order to successfully resolve the platform.
57 | For example, many images are only available for Linux.
58 | In that case, it's not necessary to set the `os` value because it doesn't reduce the amount of available platforms.
59 | For Linux, it's often enough to just set the `arch` value.
60 | As long as the provided platform values reduce the number of available platforms to a single platform, the platform resolution will succeed.
61 |
--------------------------------------------------------------------------------
/docs/settings.md:
--------------------------------------------------------------------------------
1 | # Settings File
2 |
3 | Dredge uses a settings file to store configuration information. The settings file is a JSON file named `settings.json` that is located in the following location:
4 |
5 | * Windows: `%LOCALAPPDATA%\Valleysoft.Dredge\settings.json`
6 | * Linux: `$HOME/.local/share/Valleysoft.Dredge/settings.json`
7 | * Mac: `$HOME/Library/Application Support/Valleysoft.Dredge/settings.json`
8 |
9 | Dredge will create the settings file automatically when it's needed.
10 |
11 | The [`settings`](commands/settings.md) command can be used to manipulate the settings file.
12 |
13 | ## Schema
14 |
15 | ```json
16 | {
17 | "fileCompareTool": {
18 | "exePath": "",
19 | "args": ""
20 | },
21 | "platform": {
22 | "os": "",
23 | "osVersion": "",
24 | "arch": ""
25 | }
26 | }
27 | ```
28 |
29 | See [`image compare files`](commands/images.md#compare-image-files) for more information about the `fileCompareTool` setting.
30 |
31 | See [platform resolution](platform-resolution.md) for more information about the `platform` setting.
32 |
--------------------------------------------------------------------------------
/dredge-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mthalman/dredge/f5f20754c8916204d74ddf87d9a6c02b7c38228c/dredge-logo.png
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "9.0.300",
4 | "rollForward": "latestPatch",
5 | "allowPrerelease": false
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/.dockerignore:
--------------------------------------------------------------------------------
1 | # directories
2 | **/bin/
3 | **/obj/
4 | **/out/
5 |
6 | # files
7 | Dockerfile*
8 | **/*.trx
9 | **/*.md
10 | **/*.ps1
11 | **/*.cmd
12 | **/*.sh
13 |
--------------------------------------------------------------------------------
/src/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0-noble@sha256:58fa5442c6da3bd654cab866fd6668de2713769511e412a3aa23c14368b84b16 AS build
2 |
3 | ARG TARGETARCH
4 | ARG PACKAGE_VERSION
5 |
6 | WORKDIR /source
7 |
8 | COPY Valleysoft.Dredge/*.csproj Valleysoft.Dredge/
9 | COPY Valleysoft.Dredge.Analyzers/*.csproj Valleysoft.Dredge.Analyzers/
10 | RUN dotnet restore -a $TARGETARCH Valleysoft.Dredge/*.csproj
11 |
12 | COPY Valleysoft.Dredge/ Valleysoft.Dredge/
13 | COPY Valleysoft.Dredge.Analyzers/ Valleysoft.Dredge.Analyzers/
14 | RUN dotnet publish Valleysoft.Dredge/*.csproj -f net9.0 -o /app -a $TARGETARCH --no-self-contained /p:Version=$PACKAGE_VERSION --no-restore
15 |
16 |
17 | FROM mcr.microsoft.com/dotnet/runtime:9.0-noble-chiseled@sha256:bd4288d187eac2d9753e4623e0466b9ceec2b340254a640858d3ebb1b25afbac
18 | WORKDIR /app
19 | COPY --from=build /app .
20 | ENTRYPOINT ["./dredge"]
21 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Analyzers/SettingsPropertyGenerator.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp;
3 | using Microsoft.CodeAnalysis.CSharp.Syntax;
4 | using Microsoft.CodeAnalysis.Text;
5 | using System.Collections.Immutable;
6 | using System.Text;
7 |
8 | namespace Valleysoft.Dredge.Analyzers;
9 |
10 | [Generator]
11 | public class SettingsSourceGenerator : IIncrementalGenerator
12 | {
13 | public void Initialize(IncrementalGeneratorInitializationContext context)
14 | {
15 | // Register a syntax receiver that will be created for each generation pass
16 | IncrementalValuesProvider classDeclarations = context.SyntaxProvider
17 | .CreateSyntaxProvider(
18 | predicate: static (s, _) => IsSettingsClass(s),
19 | transform: static (ctx, _) => GetClassDeclaration(ctx))
20 | .Where(static m => m is not null)!;
21 |
22 | IncrementalValueProvider<(Compilation, ImmutableArray)> compilationAndClasses =
23 | context.CompilationProvider.Combine(classDeclarations.Collect());
24 |
25 | context.RegisterSourceOutput(compilationAndClasses, static (spc, source) => Execute(source.Item1, source.Item2, spc));
26 | }
27 |
28 | private static bool IsSettingsClass(SyntaxNode node)
29 | {
30 | return node is ClassDeclarationSyntax classDeclaration && classDeclaration.Identifier.Text.EndsWith("Settings");
31 | }
32 |
33 | private static ClassDeclarationSyntax GetClassDeclaration(GeneratorSyntaxContext context)
34 | {
35 | return (ClassDeclarationSyntax)context.Node;
36 | }
37 |
38 | private static void Execute(Compilation compilation, ImmutableArray classes, SourceProductionContext context)
39 | {
40 | foreach (ClassDeclarationSyntax classDecl in classes)
41 | {
42 | SemanticModel semanticModel = compilation.GetSemanticModel(classDecl.SyntaxTree);
43 | INamedTypeSymbol classSymbol = semanticModel.GetDeclaredSymbol(classDecl)!;
44 |
45 | StringBuilder sourceBuilder = new();
46 |
47 | sourceBuilder.AppendLine("#nullable enable");
48 | sourceBuilder.AppendLine("using System;");
49 | sourceBuilder.AppendLine("using System.Collections.Generic;");
50 | sourceBuilder.AppendLine("using Newtonsoft.Json;");
51 |
52 | sourceBuilder.AppendLine($"namespace {classSymbol.ContainingNamespace}");
53 | sourceBuilder.AppendLine("{");
54 | sourceBuilder.AppendLine($" internal partial class {classSymbol.Name}");
55 | sourceBuilder.AppendLine(" {");
56 |
57 | sourceBuilder.AppendLine(GetMethod(semanticModel, classDecl,
58 | "public void SetProperty(Queue propertyPath, string value)",
59 | propertyName => $"{propertyName}.SetProperty(propertyPath, value);",
60 | propertyName => $"{propertyName} = value;",
61 | includeBreak: true));
62 |
63 | sourceBuilder.AppendLine(GetMethod(semanticModel, classDecl,
64 | "public object? GetProperty(Queue propertyPath)",
65 | propertyName => $"return {propertyName}.GetProperty(propertyPath);",
66 | propertyName => $"return {propertyName};",
67 | includeBreak: false));
68 |
69 | sourceBuilder.AppendLine(" }"); // end class
70 | sourceBuilder.AppendLine("}"); // end namespace
71 | sourceBuilder.AppendLine("#nullable disable");
72 |
73 | context.AddSource($"{classDecl.Identifier.Text}.Generated.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
74 | }
75 | }
76 |
77 | private static string GetMethod(
78 | SemanticModel semanticModel, ClassDeclarationSyntax classDecl, string methodSignature,
79 | Func settingsPropertyAction, Func propertyAction, bool includeBreak)
80 | {
81 | StringBuilder sourceBuilder = new();
82 |
83 | sourceBuilder.AppendLine($@"
84 | {methodSignature}
85 | {{
86 | if (propertyPath.Count == 0)
87 | {{
88 | throw new ArgumentException(""Property path cannot be empty"", nameof(propertyPath));
89 | }}
90 |
91 | var currentProperty = propertyPath.Dequeue();
92 | switch (currentProperty)
93 | {{");
94 |
95 | foreach (PropertyDeclarationSyntax property in classDecl.DescendantNodes().OfType())
96 | {
97 | AttributeSyntax jsonPropertyAttribute = property.AttributeLists
98 | .SelectMany(a => a.Attributes)
99 | .FirstOrDefault(a => a.Name.ToString() == "JsonProperty");
100 |
101 | if (jsonPropertyAttribute != null)
102 | {
103 | AttributeArgumentSyntax attribArg = jsonPropertyAttribute.ArgumentList?.Arguments[0]!;
104 | string jsonPropertyName = semanticModel.GetConstantValue(attribArg.Expression).Value?.ToString().Trim('"')!;
105 |
106 | if (property.Type.ToString().EndsWith("Settings"))
107 | {
108 | sourceBuilder.AppendLine($@"
109 | case ""{jsonPropertyName}"":
110 | if (propertyPath.Count > 0)
111 | {{
112 | {settingsPropertyAction(property.Identifier.Text)}
113 | }}
114 | else
115 | {{
116 | throw new ArgumentException(""Property path must point to a valid property"", nameof(propertyPath));
117 | }}
118 | {(includeBreak ? "break;" : string.Empty)}");
119 | }
120 | else
121 | {
122 | sourceBuilder.AppendLine($@"
123 | case ""{jsonPropertyName}"":
124 | if (propertyPath.Count > 0)
125 | {{
126 | throw new ArgumentException(""Property path must point to a valid property"", nameof(propertyPath));
127 | }}
128 | {propertyAction(property.Identifier.Text)}
129 | {(includeBreak ? "break;" : string.Empty)}");
130 | }
131 | }
132 | }
133 |
134 | sourceBuilder.AppendLine($@"
135 | default:
136 | throw new ArgumentException($""Unknown property: {{currentProperty}}"", nameof(propertyPath));
137 | }}
138 | }}");
139 |
140 | return sourceBuilder.ToString();
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Analyzers/Valleysoft.Dredge.Analyzers.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | enable
6 | enable
7 | true
8 | 12.0
9 | IDE0290
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/DockerfileCommandTests.cs:
--------------------------------------------------------------------------------
1 | namespace Valleysoft.Dredge.Tests;
2 |
3 | using Newtonsoft.Json;
4 | using Spectre.Console;
5 | using System.Text;
6 | using Valleysoft.DockerRegistryClient.Models.Images;
7 | using Valleysoft.DockerRegistryClient.Models.Manifests;
8 | using Valleysoft.DockerRegistryClient.Models.Manifests.Docker;
9 | using Valleysoft.Dredge.Commands.Image;
10 |
11 | public class DockerfileCommandTests
12 | {
13 | private const string Registry = "test-registry.io";
14 |
15 | public static IEnumerable> GetTestData()
16 | {
17 | DirectoryInfo workingDir = new(Path.Combine(Environment.CurrentDirectory, "TestData", "DockerfileCommand"));
18 | return workingDir.GetDirectories()
19 | .SelectMany(dir => new TestScenario[]
20 | {
21 | new(
22 | dir.Name,
23 | Path.Combine(dir.FullName, "image.json"),
24 | noFormat: false,
25 | Path.Combine(dir.FullName, "expected-output-format.txt")),
26 | new(
27 | dir.Name,
28 | Path.Combine(dir.FullName, "image.json"),
29 | noFormat: true,
30 | Path.Combine(dir.FullName, "expected-output-no-format.txt"))
31 | })
32 | .Select(scenario => new TheoryDataRow(scenario));
33 |
34 | }
35 |
36 | public class TestScenario
37 | {
38 | public TestScenario(string name, string imagePath, bool noFormat, string expectedOutputPath)
39 | {
40 | Name = name;
41 | ImagePath = imagePath;
42 | NoFormat = noFormat;
43 | ExpectedOutputPath = expectedOutputPath;
44 | }
45 |
46 | public string Name { get; }
47 | public string ImagePath { get; }
48 | public bool NoFormat { get; }
49 | public string ExpectedOutputPath { get; }
50 | }
51 |
52 |
53 | [Theory]
54 | [MemberData(nameof(GetTestData))]
55 | public async Task Test(TestScenario scenario)
56 | {
57 | const string RepoName = "repo";
58 | const string TagName = "tag";
59 | const string ImageName = $"{Registry}/{RepoName}:{TagName}";
60 | const string Digest = "digest";
61 |
62 | Mock clientFactoryMock = new();
63 | Mock mcrClientMock = new();
64 |
65 | clientFactoryMock
66 | .Setup(o => o.GetClientAsync(RegistryHelper.McrRegistry))
67 | .ReturnsAsync(mcrClientMock.Object);
68 |
69 | ManifestLayer[] layers = [];
70 | Image image = JsonConvert.DeserializeObject(File.ReadAllText(scenario.ImagePath))!;
71 | if (image.Os == "windows")
72 | {
73 | layers =
74 | [
75 | new ManifestLayer
76 | {
77 | Digest = "layer0digest"
78 | },
79 | new ManifestLayer
80 | {
81 | Digest = "layer1digest"
82 | }
83 | ];
84 |
85 | mcrClientMock
86 | .Setup(o => o.Blobs.ExistsAsync(It.IsAny(), It.IsAny(), It.IsAny()))
87 | .ReturnsAsync(false);
88 |
89 | mcrClientMock
90 | .Setup(o => o.Blobs.ExistsAsync(
91 | "windows/servercore", It.Is(digest => digest == "layer0digest" || digest == "layer1digest"), It.IsAny()))
92 | .ReturnsAsync(true);
93 | }
94 |
95 | Mock registryClientMock = new();
96 | registryClientMock
97 | .Setup(o => o.Manifests.GetAsync(RepoName, TagName, It.IsAny()))
98 | .ReturnsAsync(new ManifestInfo("media-type", "digest",
99 | new DockerManifest
100 | {
101 | Config = new ManifestConfig
102 | {
103 | Digest = Digest
104 | },
105 | Layers = layers
106 | }));
107 |
108 | registryClientMock
109 | .Setup(o => o.Blobs.GetAsync(RepoName, Digest, It.IsAny()))
110 | .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes(File.ReadAllText(scenario.ImagePath))));
111 |
112 | clientFactoryMock
113 | .Setup(o => o.GetClientAsync(Registry))
114 | .ReturnsAsync(registryClientMock.Object);
115 |
116 | DockerfileCommand command = new(clientFactoryMock.Object)
117 | {
118 | Options = new DockerfileOptions
119 | {
120 | Image = ImageName,
121 | NoFormat = scenario.NoFormat
122 | }
123 | };
124 |
125 | string markupStr = await command.GetMarkupStringAsync();
126 |
127 | string actual = TestHelper.Normalize(markupStr);
128 | string expected = TestHelper.Normalize(File.ReadAllText(scenario.ExpectedOutputPath));
129 | Assert.Equal(expected, actual);
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/fedora/expected-output-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]scratch[/]
2 | [#6D9A58]# No instruction info
3 | [/]
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/fedora/expected-output-no-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]scratch[/]
2 | [#6D9A58]# No instruction info
3 | [/]
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/fedora/image.json:
--------------------------------------------------------------------------------
1 | {
2 | "__comment": "registry.fedoraproject.org/fedora@sha256:9b8e2b468d0d0523529bde44e8a0f17aad93dcedcf876c111183ffd4ea40d724",
3 | "os": "linux",
4 | "history": [
5 | {
6 | "comment": "Created by Image Factory",
7 | "created": "2022-12-09T05:50:20Z"
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/golang/expected-output-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]scratch[/]
2 | [#C285BF]ADD[/] [#96DCFE]file:c13b430c8699df107ffd9ea5230b92238bc037a8e1cbbe35d6ab664941d575da[/] [#96DCFE]/[/]
3 | [#C285BF]CMD[/] [#FAC81F][[[/][#CA9178]"bash"[/][#FAC81F]]][/]
4 | [#C285BF]RUN[/] [#96DCFE]set -eux; [#FAC81F]\[/]
5 | apt-get update; [#FAC81F]\[/]
6 | apt-get install -y --no-install-recommends ca-certificates curl netbase wget ; [#FAC81F]\[/]
7 | rm -rf /var/lib/apt/lists/*
8 | [/][#C285BF]RUN[/] [#96DCFE]set -ex; [#FAC81F]\[/]
9 | if ! command -v gpg > /dev/null; then apt-get update; [#FAC81F]\[/]
10 | apt-get install -y --no-install-recommends gnupg dirmngr ; [#FAC81F]\[/]
11 | rm -rf /var/lib/apt/lists/*; [#FAC81F]\[/]
12 | fi
13 | [/][#C285BF]RUN[/] [#96DCFE]apt-get update && apt-get install -y --no-install-recommends git mercurial openssh-client subversion procps [#FAC81F]\[/]
14 | && rm -rf /var/lib/apt/lists/*
15 | [/][#C285BF]RUN[/] [#96DCFE]set -eux; [#FAC81F]\[/]
16 | apt-get update; [#FAC81F]\[/]
17 | apt-get install -y --no-install-recommends g++ gcc libc6-dev make pkg-config ; [#FAC81F]\[/]
18 | rm -rf /var/lib/apt/lists/*
19 | [/][#C285BF]ENV[/] [green]PATH[/][#FAC81F]=[/][#96DCFE]/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin[/]
20 | [#C285BF]ENV[/] [green]GOLANG_VERSION[/][#FAC81F]=[/][#96DCFE]1.19.4[/]
21 | [#C285BF]RUN[/] [#96DCFE]set -eux; [#FAC81F]\[/]
22 | arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; [#FAC81F]\[/]
23 | url=; [#FAC81F]\[/]
24 | case "$arch" in 'amd64') url='https://dl.google.com/go/go1.19.4.linux-amd64.tar.gz'; [#FAC81F]\[/]
25 | sha256='c9c08f783325c4cf840a94333159cc937f05f75d36a8b307951d5bd959cf2ab8'; [#FAC81F]\[/]
26 | ;; [#FAC81F]\[/]
27 | 'armel') export GOARCH='arm' GOARM='5' GOOS='linux'; [#FAC81F]\[/]
28 | ;; [#FAC81F]\[/]
29 | 'armhf') url='https://dl.google.com/go/go1.19.4.linux-armv6l.tar.gz'; [#FAC81F]\[/]
30 | sha256='7a51dae4f3a52d2dfeaf59367cc0b8a296deddc87e95aa619bf87d24661d2370'; [#FAC81F]\[/]
31 | ;; [#FAC81F]\[/]
32 | 'arm64') url='https://dl.google.com/go/go1.19.4.linux-arm64.tar.gz'; [#FAC81F]\[/]
33 | sha256='9df122d6baf6f2275270306b92af3b09d7973fb1259257e284dba33c0db14f1b'; [#FAC81F]\[/]
34 | ;; [#FAC81F]\[/]
35 | 'i386') url='https://dl.google.com/go/go1.19.4.linux-386.tar.gz'; [#FAC81F]\[/]
36 | sha256='e5f0b0551e120bf3d1246cb960ec58032d7ca69e1adcf0fdb91c07da620e0c61'; [#FAC81F]\[/]
37 | ;; [#FAC81F]\[/]
38 | 'mips64el') export GOARCH='mips64le' GOOS='linux'; [#FAC81F]\[/]
39 | ;; [#FAC81F]\[/]
40 | 'ppc64el') url='https://dl.google.com/go/go1.19.4.linux-ppc64le.tar.gz'; [#FAC81F]\[/]
41 | sha256='fbc6c7d1d169bbdc82223d861d2fadc6add01c126533d3efbba3fdca9b362035'; [#FAC81F]\[/]
42 | ;; [#FAC81F]\[/]
43 | 's390x') url='https://dl.google.com/go/go1.19.4.linux-s390x.tar.gz'; [#FAC81F]\[/]
44 | sha256='4b8d25acbdca8010c31ea8c5fd4aba93471ff6ada7a8b4fb04b935baee873dc8'; [#FAC81F]\[/]
45 | ;; [#FAC81F]\[/]
46 | *) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; [#FAC81F]\[/]
47 | esac; [#FAC81F]\[/]
48 | build=; [#FAC81F]\[/]
49 | if [[ -z "$url" ]]; then build=1; [#FAC81F]\[/]
50 | url='https://dl.google.com/go/go1.19.4.src.tar.gz'; [#FAC81F]\[/]
51 | sha256='eda74db4ac494800a3e66ee784e495bfbb9b8e535df924a8b01b1a8028b7f368'; [#FAC81F]\[/]
52 | echo >&2; [#FAC81F]\[/]
53 | echo >&2 "warning: current architecture ($arch) does not have a compatible Go binary release; will be building from source"; [#FAC81F]\[/]
54 | echo >&2; [#FAC81F]\[/]
55 | fi; [#FAC81F]\[/]
56 | wget -O go.tgz.asc "$url.asc"; [#FAC81F]\[/]
57 | wget -O go.tgz "$url" --progress=dot:giga; [#FAC81F]\[/]
58 | echo "$sha256 *go.tgz" | sha256sum -c -; [#FAC81F]\[/]
59 | GNUPGHOME="$(mktemp -d)"; export GNUPGHOME; [#FAC81F]\[/]
60 | gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 'EB4C 1BFD 4F04 2F6D DDCC EC91 7721 F63B D38B 4796'; [#FAC81F]\[/]
61 | gpg --batch --keyserver keyserver.ubuntu.com --recv-keys '2F52 8D36 D67B 69ED F998 D857 78BD 6547 3CB3 BD13'; [#FAC81F]\[/]
62 | gpg --batch --verify go.tgz.asc go.tgz; [#FAC81F]\[/]
63 | gpgconf --kill all; [#FAC81F]\[/]
64 | rm -rf "$GNUPGHOME" go.tgz.asc; [#FAC81F]\[/]
65 | tar -C /usr/local -xzf go.tgz; [#FAC81F]\[/]
66 | rm go.tgz; [#FAC81F]\[/]
67 | if [[ -n "$build" ]]; then savedAptMark="$(apt-mark showmanual)"; [#FAC81F]\[/]
68 | apt-get update; [#FAC81F]\[/]
69 | apt-get install -y --no-install-recommends golang-go; [#FAC81F]\[/]
70 | export GOCACHE='/tmp/gocache'; [#FAC81F]\[/]
71 | ( cd /usr/local/go/src; [#FAC81F]\[/]
72 | export GOROOT_BOOTSTRAP="$(go env GOROOT)" GOHOSTOS="$GOOS" GOHOSTARCH="$GOARCH"; [#FAC81F]\[/]
73 | ./make.bash; [#FAC81F]\[/]
74 | ); [#FAC81F]\[/]
75 | apt-mark auto '.*' > /dev/null; [#FAC81F]\[/]
76 | apt-mark manual $savedAptMark > /dev/null; [#FAC81F]\[/]
77 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; [#FAC81F]\[/]
78 | rm -rf /var/lib/apt/lists/*; [#FAC81F]\[/]
79 | rm -rf /usr/local/go/pkg/*/cmd /usr/local/go/pkg/bootstrap /usr/local/go/pkg/obj /usr/local/go/pkg/tool/*/api /usr/local/go/pkg/tool/*/go_bootstrap /usr/local/go/src/cmd/dist/dist "$GOCACHE" ; [#FAC81F]\[/]
80 | fi; [#FAC81F]\[/]
81 | go version
82 | [/][#C285BF]ENV[/] [green]GOPATH[/][#FAC81F]=[/][#96DCFE]/go[/]
83 | [#C285BF]ENV[/] [green]PATH[/][#FAC81F]=[/][#96DCFE]/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin[/]
84 | [#C285BF]RUN[/] [#96DCFE]mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
85 | [/][#C285BF]WORKDIR[/] [#96DCFE]/go
86 | [/]
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/golang/expected-output-no-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]scratch[/]
2 | [#C285BF]ADD[/] [#96DCFE]file:c13b430c8699df107ffd9ea5230b92238bc037a8e1cbbe35d6ab664941d575da[/] [#96DCFE]/[/]
3 | [#C285BF]CMD[/] [#FAC81F][[[/][#CA9178]"bash"[/][#FAC81F]]][/]
4 | [#C285BF]RUN[/] [#96DCFE]set -eux; apt-get update; apt-get install -y --no-install-recommends ca-certificates curl netbase wget ; rm -rf /var/lib/apt/lists/*
5 | [/][#C285BF]RUN[/] [#96DCFE]set -ex; if ! command -v gpg > /dev/null; then apt-get update; apt-get install -y --no-install-recommends gnupg dirmngr ; rm -rf /var/lib/apt/lists/*; fi
6 | [/][#C285BF]RUN[/] [#96DCFE]apt-get update && apt-get install -y --no-install-recommends git mercurial openssh-client subversion procps && rm -rf /var/lib/apt/lists/*
7 | [/][#C285BF]RUN[/] [#96DCFE]set -eux; apt-get update; apt-get install -y --no-install-recommends g++ gcc libc6-dev make pkg-config ; rm -rf /var/lib/apt/lists/*
8 | [/][#C285BF]ENV[/] [green]PATH[/][#FAC81F]=[/][#96DCFE]/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin[/]
9 | [#C285BF]ENV[/] [green]GOLANG_VERSION[/][#FAC81F]=[/][#96DCFE]1.19.4[/]
10 | [#C285BF]RUN[/] [#96DCFE]set -eux; arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; url=; case "$arch" in 'amd64') url='https://dl.google.com/go/go1.19.4.linux-amd64.tar.gz'; sha256='c9c08f783325c4cf840a94333159cc937f05f75d36a8b307951d5bd959cf2ab8'; ;; 'armel') export GOARCH='arm' GOARM='5' GOOS='linux'; ;; 'armhf') url='https://dl.google.com/go/go1.19.4.linux-armv6l.tar.gz'; sha256='7a51dae4f3a52d2dfeaf59367cc0b8a296deddc87e95aa619bf87d24661d2370'; ;; 'arm64') url='https://dl.google.com/go/go1.19.4.linux-arm64.tar.gz'; sha256='9df122d6baf6f2275270306b92af3b09d7973fb1259257e284dba33c0db14f1b'; ;; 'i386') url='https://dl.google.com/go/go1.19.4.linux-386.tar.gz'; sha256='e5f0b0551e120bf3d1246cb960ec58032d7ca69e1adcf0fdb91c07da620e0c61'; ;; 'mips64el') export GOARCH='mips64le' GOOS='linux'; ;; 'ppc64el') url='https://dl.google.com/go/go1.19.4.linux-ppc64le.tar.gz'; sha256='fbc6c7d1d169bbdc82223d861d2fadc6add01c126533d3efbba3fdca9b362035'; ;; 's390x') url='https://dl.google.com/go/go1.19.4.linux-s390x.tar.gz'; sha256='4b8d25acbdca8010c31ea8c5fd4aba93471ff6ada7a8b4fb04b935baee873dc8'; ;; *) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; esac; build=; if [[ -z "$url" ]]; then build=1; url='https://dl.google.com/go/go1.19.4.src.tar.gz'; sha256='eda74db4ac494800a3e66ee784e495bfbb9b8e535df924a8b01b1a8028b7f368'; echo >&2; echo >&2 "warning: current architecture ($arch) does not have a compatible Go binary release; will be building from source"; echo >&2; fi; wget -O go.tgz.asc "$url.asc"; wget -O go.tgz "$url" --progress=dot:giga; echo "$sha256 *go.tgz" | sha256sum -c -; GNUPGHOME="$(mktemp -d)"; export GNUPGHOME; gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 'EB4C 1BFD 4F04 2F6D DDCC EC91 7721 F63B D38B 4796'; gpg --batch --keyserver keyserver.ubuntu.com --recv-keys '2F52 8D36 D67B 69ED F998 D857 78BD 6547 3CB3 BD13'; gpg --batch --verify go.tgz.asc go.tgz; gpgconf --kill all; rm -rf "$GNUPGHOME" go.tgz.asc; tar -C /usr/local -xzf go.tgz; rm go.tgz; if [[ -n "$build" ]]; then savedAptMark="$(apt-mark showmanual)"; apt-get update; apt-get install -y --no-install-recommends golang-go; export GOCACHE='/tmp/gocache'; ( cd /usr/local/go/src; export GOROOT_BOOTSTRAP="$(go env GOROOT)" GOHOSTOS="$GOOS" GOHOSTARCH="$GOARCH"; ./make.bash; ); apt-mark auto '.*' > /dev/null; apt-mark manual $savedAptMark > /dev/null; apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; rm -rf /var/lib/apt/lists/*; rm -rf /usr/local/go/pkg/*/cmd /usr/local/go/pkg/bootstrap /usr/local/go/pkg/obj /usr/local/go/pkg/tool/*/api /usr/local/go/pkg/tool/*/go_bootstrap /usr/local/go/src/cmd/dist/dist "$GOCACHE" ; fi; go version
11 | [/][#C285BF]ENV[/] [green]GOPATH[/][#FAC81F]=[/][#96DCFE]/go[/]
12 | [#C285BF]ENV[/] [green]PATH[/][#FAC81F]=[/][#96DCFE]/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin[/]
13 | [#C285BF]RUN[/] [#96DCFE]mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
14 | [/][#C285BF]WORKDIR[/] [#96DCFE]/go
15 | [/]
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/golang/image.json:
--------------------------------------------------------------------------------
1 | {
2 | "__comment": "library/golang@sha256:766625f2182dacec4c8774355a65a81a3b73acb0b4287b6a32a8efc185aede2c",
3 | "os": "linux",
4 | "history": [
5 | {
6 | "created": "2022-12-21T01:20:21.922936512Z",
7 | "created_by": "/bin/sh -c #(nop) ADD file:c13b430c8699df107ffd9ea5230b92238bc037a8e1cbbe35d6ab664941d575da in / "
8 | },
9 | {
10 | "created": "2022-12-21T01:20:22.590344295Z",
11 | "created_by": "/bin/sh -c #(nop) CMD [\"bash\"]",
12 | "empty_layer": true
13 | },
14 | {
15 | "created": "2022-12-21T11:13:51.512187477Z",
16 | "created_by": "/bin/sh -c set -eux; \tapt-get update; \tapt-get install -y --no-install-recommends \t\tca-certificates \t\tcurl \t\tnetbase \t\twget \t; \trm -rf /var/lib/apt/lists/*"
17 | },
18 | {
19 | "created": "2022-12-21T11:13:58.806979115Z",
20 | "created_by": "/bin/sh -c set -ex; \tif ! command -v gpg > /dev/null; then \t\tapt-get update; \t\tapt-get install -y --no-install-recommends \t\t\tgnupg \t\t\tdirmngr \t\t; \t\trm -rf /var/lib/apt/lists/*; \tfi"
21 | },
22 | {
23 | "created": "2022-12-21T11:14:17.676013622Z",
24 | "created_by": "/bin/sh -c apt-get update && apt-get install -y --no-install-recommends \t\tgit \t\tmercurial \t\topenssh-client \t\tsubversion \t\t\t\tprocps \t&& rm -rf /var/lib/apt/lists/*"
25 | },
26 | {
27 | "created": "2022-12-21T17:03:24.401848277Z",
28 | "created_by": "/bin/sh -c set -eux; \tapt-get update; \tapt-get install -y --no-install-recommends \t\tg++ \t\tgcc \t\tlibc6-dev \t\tmake \t\tpkg-config \t; \trm -rf /var/lib/apt/lists/*"
29 | },
30 | {
31 | "created": "2022-12-21T17:03:25.042977729Z",
32 | "created_by": "/bin/sh -c #(nop) ENV PATH=/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
33 | "empty_layer": true
34 | },
35 | {
36 | "created": "2022-12-21T17:04:20.804087865Z",
37 | "created_by": "/bin/sh -c #(nop) ENV GOLANG_VERSION=1.19.4",
38 | "empty_layer": true
39 | },
40 | {
41 | "created": "2022-12-21T17:04:34.285435675Z",
42 | "created_by": "/bin/sh -c set -eux; \tarch=\"$(dpkg --print-architecture)\"; arch=\"${arch##*-}\"; \turl=; \tcase \"$arch\" in \t\t'amd64') \t\t\turl='https://dl.google.com/go/go1.19.4.linux-amd64.tar.gz'; \t\t\tsha256='c9c08f783325c4cf840a94333159cc937f05f75d36a8b307951d5bd959cf2ab8'; \t\t\t;; \t\t'armel') \t\t\texport GOARCH='arm' GOARM='5' GOOS='linux'; \t\t\t;; \t\t'armhf') \t\t\turl='https://dl.google.com/go/go1.19.4.linux-armv6l.tar.gz'; \t\t\tsha256='7a51dae4f3a52d2dfeaf59367cc0b8a296deddc87e95aa619bf87d24661d2370'; \t\t\t;; \t\t'arm64') \t\t\turl='https://dl.google.com/go/go1.19.4.linux-arm64.tar.gz'; \t\t\tsha256='9df122d6baf6f2275270306b92af3b09d7973fb1259257e284dba33c0db14f1b'; \t\t\t;; \t\t'i386') \t\t\turl='https://dl.google.com/go/go1.19.4.linux-386.tar.gz'; \t\t\tsha256='e5f0b0551e120bf3d1246cb960ec58032d7ca69e1adcf0fdb91c07da620e0c61'; \t\t\t;; \t\t'mips64el') \t\t\texport GOARCH='mips64le' GOOS='linux'; \t\t\t;; \t\t'ppc64el') \t\t\turl='https://dl.google.com/go/go1.19.4.linux-ppc64le.tar.gz'; \t\t\tsha256='fbc6c7d1d169bbdc82223d861d2fadc6add01c126533d3efbba3fdca9b362035'; \t\t\t;; \t\t's390x') \t\t\turl='https://dl.google.com/go/go1.19.4.linux-s390x.tar.gz'; \t\t\tsha256='4b8d25acbdca8010c31ea8c5fd4aba93471ff6ada7a8b4fb04b935baee873dc8'; \t\t\t;; \t\t*) echo >&2 \"error: unsupported architecture '$arch' (likely packaging update needed)\"; exit 1 ;; \tesac; \tbuild=; \tif [ -z \"$url\" ]; then \t\tbuild=1; \t\turl='https://dl.google.com/go/go1.19.4.src.tar.gz'; \t\tsha256='eda74db4ac494800a3e66ee784e495bfbb9b8e535df924a8b01b1a8028b7f368'; \t\techo >&2; \t\techo >&2 \"warning: current architecture ($arch) does not have a compatible Go binary release; will be building from source\"; \t\techo >&2; \tfi; \t\twget -O go.tgz.asc \"$url.asc\"; \twget -O go.tgz \"$url\" --progress=dot:giga; \techo \"$sha256 *go.tgz\" | sha256sum -c -; \t\tGNUPGHOME=\"$(mktemp -d)\"; export GNUPGHOME; \tgpg --batch --keyserver keyserver.ubuntu.com --recv-keys 'EB4C 1BFD 4F04 2F6D DDCC EC91 7721 F63B D38B 4796'; \tgpg --batch --keyserver keyserver.ubuntu.com --recv-keys '2F52 8D36 D67B 69ED F998 D857 78BD 6547 3CB3 BD13'; \tgpg --batch --verify go.tgz.asc go.tgz; \tgpgconf --kill all; \trm -rf \"$GNUPGHOME\" go.tgz.asc; \t\ttar -C /usr/local -xzf go.tgz; \trm go.tgz; \t\tif [ -n \"$build\" ]; then \t\tsavedAptMark=\"$(apt-mark showmanual)\"; \t\tapt-get update; \t\tapt-get install -y --no-install-recommends golang-go; \t\t\t\texport GOCACHE='/tmp/gocache'; \t\t\t\t( \t\t\tcd /usr/local/go/src; \t\t\texport GOROOT_BOOTSTRAP=\"$(go env GOROOT)\" GOHOSTOS=\"$GOOS\" GOHOSTARCH=\"$GOARCH\"; \t\t\t./make.bash; \t\t); \t\t\t\tapt-mark auto '.*' > /dev/null; \t\tapt-mark manual $savedAptMark > /dev/null; \t\tapt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \t\trm -rf /var/lib/apt/lists/*; \t\t\t\trm -rf \t\t\t/usr/local/go/pkg/*/cmd \t\t\t/usr/local/go/pkg/bootstrap \t\t\t/usr/local/go/pkg/obj \t\t\t/usr/local/go/pkg/tool/*/api \t\t\t/usr/local/go/pkg/tool/*/go_bootstrap \t\t\t/usr/local/go/src/cmd/dist/dist \t\t\t\"$GOCACHE\" \t\t; \tfi; \t\tgo version"
43 | },
44 | {
45 | "created": "2022-12-21T17:04:35.657671507Z",
46 | "created_by": "/bin/sh -c #(nop) ENV GOPATH=/go",
47 | "empty_layer": true
48 | },
49 | {
50 | "created": "2022-12-21T17:04:35.763897771Z",
51 | "created_by": "/bin/sh -c #(nop) ENV PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
52 | "empty_layer": true
53 | },
54 | {
55 | "created": "2022-12-21T17:04:36.338119752Z",
56 | "created_by": "/bin/sh -c mkdir -p \"$GOPATH/src\" \"$GOPATH/bin\" && chmod -R 777 \"$GOPATH\""
57 | },
58 | {
59 | "created": "2022-12-21T17:04:36.454187246Z",
60 | "created_by": "/bin/sh -c #(nop) WORKDIR /go",
61 | "empty_layer": true
62 | }
63 | ]
64 | }
65 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/mariner/expected-output-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]scratch[/]
2 | [#6D9A58]# No instruction info
3 | [/][#C285BF]ARG[/] [green]EULA[/][#FAC81F]=[/][#96DCFE]@EULA_FILE@[/]
4 | [#C285BF]COPY[/] [#96DCFE]EULA-Container.txt[/] [#96DCFE].[/] [#6D9A58]# buildkit
5 | [/][#C285BF]CMD[/] [#FAC81F][[[/][#CA9178]"bash"[/][#FAC81F]]][/]
6 | [#C285BF]ENV[/] [green]ASPNETCORE_URLS[/][#FAC81F]=[/][#96DCFE]http://+:80[/] [#FAC81F]\[/]
7 | [green]DOTNET_RUNNING_IN_CONTAINER[/][#FAC81F]=[/][#96DCFE]true[/]
8 | [#C285BF]RUN[/] [#96DCFE]/bin/sh -c tdnf install -y ca-certificates glibc icu krb5 libgcc libstdc++ openssl-libs zlib && tdnf clean all # buildkit
9 | [/][#C285BF]RUN[/] [#96DCFE]/bin/sh -c dotnet_version=6.0.36 && curl -fSL --output dotnet-runtime-deps.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$dotnet_version/dotnet-runtime-deps-$dotnet_version-cm.2-x64.rpm && dotnet_sha512='c480ab7722f34eef1e0e55bf60b393cf6f4dc51ea78878b5a3b0e9c8c45d054d3c81b29a8783732c01ca62d3c5b6298a166a2de7c0e07f70067f22af375b6f1c' && echo "$dotnet_sha512 dotnet-runtime-deps.rpm" | sha512sum -c - && tdnf install -y --disablerepo=* dotnet-runtime-deps.rpm && rm dotnet-runtime-deps.rpm # buildkit
10 | [/][#C285BF]ENV[/] [green]DOTNET_VERSION[/][#FAC81F]=[/][#96DCFE]6.0.36[/]
11 | [#C285BF]RUN[/] [#96DCFE]/bin/sh -c curl -fSL --output dotnet-host.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-host-$DOTNET_VERSION-x64.rpm && dotnet_sha512='00d9f978054aef3ec4b2bd81eedee8184e4fd44eee364d82b81eab7079958a9d0fe6cfdf5f29c05f33a20e2cfc9761306a8bcc378dbc804cf1f38eb9ea29871e' && echo "$dotnet_sha512 dotnet-host.rpm" | sha512sum -c - && curl -fSL --output dotnet-hostfxr.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-hostfxr-$DOTNET_VERSION-x64.rpm && dotnet_sha512='79faa94cef34307a1d947300755e002056f42094003b3fc3447efea6731ddece3d6d18ab62c0292498418a627ba395c6c2053a8b92fbdac356cd0afb141e7e7d' && echo "$dotnet_sha512 dotnet-hostfxr.rpm" | sha512sum -c - && curl -fSL --output dotnet-runtime.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-runtime-$DOTNET_VERSION-x64.rpm && dotnet_sha512='a3a544b6d315daa8e4fceb75d7414502d5b8fa5c6f7dc14c5ea05a8c32d50adf8422471eac69893eb8ea10ff908879aea277fc2b6aa5a723b3f60cf3c2e84c7e' && echo "$dotnet_sha512 dotnet-runtime.rpm" | sha512sum -c - && tdnf install -y --disablerepo=* dotnet-host.rpm dotnet-hostfxr.rpm dotnet-runtime.rpm && rm dotnet-host.rpm dotnet-hostfxr.rpm dotnet-runtime.rpm # buildkit
12 | [/]
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/mariner/expected-output-no-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]scratch[/]
2 | [#6D9A58]# No instruction info
3 | [/][#C285BF]ARG[/] [green]EULA[/][#FAC81F]=[/][#96DCFE]@EULA_FILE@[/]
4 | [#C285BF]COPY[/] [#96DCFE]EULA-Container.txt[/] [#96DCFE].[/] [#6D9A58]# buildkit
5 | [/][#C285BF]CMD[/] [#FAC81F][[[/][#CA9178]"bash"[/][#FAC81F]]][/]
6 | [#C285BF]ENV[/] [green]ASPNETCORE_URLS[/][#FAC81F]=[/][#96DCFE]http://+:80[/] [green]DOTNET_RUNNING_IN_CONTAINER[/][#FAC81F]=[/][#96DCFE]true[/]
7 | [#C285BF]RUN[/] [#96DCFE]/bin/sh -c tdnf install -y ca-certificates glibc icu krb5 libgcc libstdc++ openssl-libs zlib && tdnf clean all # buildkit
8 | [/][#C285BF]RUN[/] [#96DCFE]/bin/sh -c dotnet_version=6.0.36 && curl -fSL --output dotnet-runtime-deps.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$dotnet_version/dotnet-runtime-deps-$dotnet_version-cm.2-x64.rpm && dotnet_sha512='c480ab7722f34eef1e0e55bf60b393cf6f4dc51ea78878b5a3b0e9c8c45d054d3c81b29a8783732c01ca62d3c5b6298a166a2de7c0e07f70067f22af375b6f1c' && echo "$dotnet_sha512 dotnet-runtime-deps.rpm" | sha512sum -c - && tdnf install -y --disablerepo=* dotnet-runtime-deps.rpm && rm dotnet-runtime-deps.rpm # buildkit
9 | [/][#C285BF]ENV[/] [green]DOTNET_VERSION[/][#FAC81F]=[/][#96DCFE]6.0.36[/]
10 | [#C285BF]RUN[/] [#96DCFE]/bin/sh -c curl -fSL --output dotnet-host.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-host-$DOTNET_VERSION-x64.rpm && dotnet_sha512='00d9f978054aef3ec4b2bd81eedee8184e4fd44eee364d82b81eab7079958a9d0fe6cfdf5f29c05f33a20e2cfc9761306a8bcc378dbc804cf1f38eb9ea29871e' && echo "$dotnet_sha512 dotnet-host.rpm" | sha512sum -c - && curl -fSL --output dotnet-hostfxr.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-hostfxr-$DOTNET_VERSION-x64.rpm && dotnet_sha512='79faa94cef34307a1d947300755e002056f42094003b3fc3447efea6731ddece3d6d18ab62c0292498418a627ba395c6c2053a8b92fbdac356cd0afb141e7e7d' && echo "$dotnet_sha512 dotnet-hostfxr.rpm" | sha512sum -c - && curl -fSL --output dotnet-runtime.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-runtime-$DOTNET_VERSION-x64.rpm && dotnet_sha512='a3a544b6d315daa8e4fceb75d7414502d5b8fa5c6f7dc14c5ea05a8c32d50adf8422471eac69893eb8ea10ff908879aea277fc2b6aa5a723b3f60cf3c2e84c7e' && echo "$dotnet_sha512 dotnet-runtime.rpm" | sha512sum -c - && tdnf install -y --disablerepo=* dotnet-host.rpm dotnet-hostfxr.rpm dotnet-runtime.rpm && rm dotnet-host.rpm dotnet-hostfxr.rpm dotnet-runtime.rpm # buildkit
11 | [/]
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/mariner/image.json:
--------------------------------------------------------------------------------
1 | {
2 | "__comment": "mcr.microsoft.com/dotnet/runtime@sha256:fc41073691b0e33a21de5ed4fd64f06bbc827031e975e7b317e0a4ced85a96ed",
3 | "os": "linux",
4 | "history": [
5 | {
6 | "created": "2024-12-08T04:27:33.807635718Z",
7 | "comment": "Imported from -"
8 | },
9 | {
10 | "created": "2024-12-08T04:27:42.708534541Z",
11 | "created_by": "ARG EULA=@EULA_FILE@",
12 | "comment": "buildkit.dockerfile.v0",
13 | "empty_layer": true
14 | },
15 | {
16 | "created": "2024-12-08T04:27:42.708534541Z",
17 | "created_by": "COPY EULA-Container.txt . # buildkit",
18 | "comment": "buildkit.dockerfile.v0"
19 | },
20 | {
21 | "created": "2024-12-08T04:27:42.708534541Z",
22 | "created_by": "CMD [\"bash\"]",
23 | "comment": "buildkit.dockerfile.v0",
24 | "empty_layer": true
25 | },
26 | {
27 | "created": "2024-12-11T20:31:20.806995463Z",
28 | "created_by": "ENV ASPNETCORE_URLS=http://+:80 DOTNET_RUNNING_IN_CONTAINER=true",
29 | "comment": "buildkit.dockerfile.v0",
30 | "empty_layer": true
31 | },
32 | {
33 | "created": "2024-12-11T20:31:20.806995463Z",
34 | "created_by": "RUN /bin/sh -c tdnf install -y ca-certificates glibc icu krb5 libgcc libstdc++ openssl-libs zlib \u0026\u0026 tdnf clean all # buildkit",
35 | "comment": "buildkit.dockerfile.v0"
36 | },
37 | {
38 | "created": "2024-12-11T20:31:22.638464065Z",
39 | "created_by": "RUN /bin/sh -c dotnet_version=6.0.36 \u0026\u0026 curl -fSL --output dotnet-runtime-deps.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$dotnet_version/dotnet-runtime-deps-$dotnet_version-cm.2-x64.rpm \u0026\u0026 dotnet_sha512='c480ab7722f34eef1e0e55bf60b393cf6f4dc51ea78878b5a3b0e9c8c45d054d3c81b29a8783732c01ca62d3c5b6298a166a2de7c0e07f70067f22af375b6f1c' \u0026\u0026 echo \"$dotnet_sha512 dotnet-runtime-deps.rpm\" | sha512sum -c - \u0026\u0026 tdnf install -y --disablerepo=* dotnet-runtime-deps.rpm \u0026\u0026 rm dotnet-runtime-deps.rpm # buildkit",
40 | "comment": "buildkit.dockerfile.v0"
41 | },
42 | {
43 | "created": "2024-12-11T20:31:29.473616568Z",
44 | "created_by": "ENV DOTNET_VERSION=6.0.36",
45 | "comment": "buildkit.dockerfile.v0",
46 | "empty_layer": true
47 | },
48 | {
49 | "created": "2024-12-11T20:31:29.473616568Z",
50 | "created_by": "RUN /bin/sh -c curl -fSL --output dotnet-host.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-host-$DOTNET_VERSION-x64.rpm \u0026\u0026 dotnet_sha512='00d9f978054aef3ec4b2bd81eedee8184e4fd44eee364d82b81eab7079958a9d0fe6cfdf5f29c05f33a20e2cfc9761306a8bcc378dbc804cf1f38eb9ea29871e' \u0026\u0026 echo \"$dotnet_sha512 dotnet-host.rpm\" | sha512sum -c - \u0026\u0026 curl -fSL --output dotnet-hostfxr.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-hostfxr-$DOTNET_VERSION-x64.rpm \u0026\u0026 dotnet_sha512='79faa94cef34307a1d947300755e002056f42094003b3fc3447efea6731ddece3d6d18ab62c0292498418a627ba395c6c2053a8b92fbdac356cd0afb141e7e7d' \u0026\u0026 echo \"$dotnet_sha512 dotnet-hostfxr.rpm\" | sha512sum -c - \u0026\u0026 curl -fSL --output dotnet-runtime.rpm https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-runtime-$DOTNET_VERSION-x64.rpm \u0026\u0026 dotnet_sha512='a3a544b6d315daa8e4fceb75d7414502d5b8fa5c6f7dc14c5ea05a8c32d50adf8422471eac69893eb8ea10ff908879aea277fc2b6aa5a723b3f60cf3c2e84c7e' \u0026\u0026 echo \"$dotnet_sha512 dotnet-runtime.rpm\" | sha512sum -c - \u0026\u0026 tdnf install -y --disablerepo=* dotnet-host.rpm dotnet-hostfxr.rpm dotnet-runtime.rpm \u0026\u0026 rm dotnet-host.rpm dotnet-hostfxr.rpm dotnet-runtime.rpm # buildkit",
51 | "comment": "buildkit.dockerfile.v0"
52 | }
53 | ]
54 | }
55 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/openjdk/expected-output-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]scratch[/]
2 | [#6D9A58]# No instruction info
3 | [/][#C285BF]ARG[/] [green]EULA[/][#FAC81F]=[/][#96DCFE]@EULA_FILE@[/]
4 | [#C285BF]COPY[/] [#96DCFE]file:2c9b9395238ee55ff215d908ee6cf02975b45c4e6c97b276333bcd60ee705729[/] [#96DCFE].[/]
5 | [#C285BF]LABEL[/] [#96DCFE]Author[/][#FAC81F]=[/][#96DCFE]Microsoft[/]
6 | [#C285BF]LABEL[/] [#96DCFE]Support[/][#FAC81F]=[/][#CA9178]"Microsoft OpenJDK Support "[/]
7 | [#C285BF]COPY[/] [#96DCFE]/staging/[/] [#96DCFE]/[/] [#6D9A58]# buildkit
8 | [/][#C285BF]COPY[/] [#96DCFE]/usr/jdk/[/] [#96DCFE]/usr/jdk/[/] [#6D9A58]# buildkit
9 | [/][#C285BF]COPY[/] [#96DCFE]/staging/home/app[/] [#96DCFE]/home/app[/] [#6D9A58]# buildkit
10 | [/][#C285BF]ENV[/] [green]JAVA_HOME[/][#FAC81F]=[/][#96DCFE]/usr/jdk[/]
11 | [#C285BF]ENV[/] [green]PATH[/][#FAC81F]=[/][#96DCFE]/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/jdk/bin[/]
12 | [#C285BF]ENTRYPOINT[/] [#FAC81F][[[/][#CA9178]"/usr/jdk/bin/java"[/][#FAC81F]]][/]
13 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/openjdk/expected-output-no-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]scratch[/]
2 | [#6D9A58]# No instruction info
3 | [/][#C285BF]ARG[/] [green]EULA[/][#FAC81F]=[/][#96DCFE]@EULA_FILE@[/]
4 | [#C285BF]COPY[/] [#96DCFE]file:2c9b9395238ee55ff215d908ee6cf02975b45c4e6c97b276333bcd60ee705729[/] [#96DCFE].[/]
5 | [#C285BF]LABEL[/] [#96DCFE]Author[/][#FAC81F]=[/][#96DCFE]Microsoft[/]
6 | [#C285BF]LABEL[/] [#96DCFE]Support[/][#FAC81F]=[/][#CA9178]"Microsoft OpenJDK Support "[/]
7 | [#C285BF]COPY[/] [#96DCFE]/staging/[/] [#96DCFE]/[/] [#6D9A58]# buildkit
8 | [/][#C285BF]COPY[/] [#96DCFE]/usr/jdk/[/] [#96DCFE]/usr/jdk/[/] [#6D9A58]# buildkit
9 | [/][#C285BF]COPY[/] [#96DCFE]/staging/home/app[/] [#96DCFE]/home/app[/] [#6D9A58]# buildkit
10 | [/][#C285BF]ENV[/] [green]JAVA_HOME[/][#FAC81F]=[/][#96DCFE]/usr/jdk[/]
11 | [#C285BF]ENV[/] [green]PATH[/][#FAC81F]=[/][#96DCFE]/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/jdk/bin[/]
12 | [#C285BF]ENTRYPOINT[/] [#FAC81F][[[/][#CA9178]"/usr/jdk/bin/java"[/][#FAC81F]]][/]
13 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/openjdk/image.json:
--------------------------------------------------------------------------------
1 | {
2 | "__comment": "mcr.microsoft.com/openjdk/jdk@sha256:4b13d577acde77b9fc30ec9831b1e01751d3918fe2bbc5d13d047fdc81fa2957",
3 | "os": "linux",
4 | "history": [
5 | {
6 | "created": "2023-04-16T23:55:54.508257077Z",
7 | "comment": "Imported from -"
8 | },
9 | {
10 | "created": "2023-04-16T23:55:55.744677521Z",
11 | "created_by": "/bin/sh -c #(nop) ARG EULA=@EULA_FILE@",
12 | "empty_layer": true
13 | },
14 | {
15 | "created": "2023-04-16T23:55:57.00065357Z",
16 | "created_by": "/bin/sh -c #(nop) COPY file:2c9b9395238ee55ff215d908ee6cf02975b45c4e6c97b276333bcd60ee705729 in . "
17 | },
18 | {
19 | "created": "2023-04-24T09:11:59.635096955Z",
20 | "created_by": "LABEL Author=Microsoft",
21 | "comment": "buildkit.dockerfile.v0",
22 | "empty_layer": true
23 | },
24 | {
25 | "created": "2023-04-24T09:11:59.635096955Z",
26 | "created_by": "LABEL Support=Microsoft OpenJDK Support ",
27 | "comment": "buildkit.dockerfile.v0",
28 | "empty_layer": true
29 | },
30 | {
31 | "created": "2023-04-24T09:11:59.635096955Z",
32 | "created_by": "COPY /staging/ / # buildkit",
33 | "comment": "buildkit.dockerfile.v0"
34 | },
35 | {
36 | "created": "2023-04-24T09:12:02.030218924Z",
37 | "created_by": "COPY /usr/jdk/ /usr/jdk/ # buildkit",
38 | "comment": "buildkit.dockerfile.v0"
39 | },
40 | {
41 | "created": "2023-04-24T09:12:02.044834481Z",
42 | "created_by": "COPY /staging/home/app /home/app # buildkit",
43 | "comment": "buildkit.dockerfile.v0"
44 | },
45 | {
46 | "created": "2023-04-24T09:12:02.044834481Z",
47 | "created_by": "ENV JAVA_HOME=/usr/jdk",
48 | "comment": "buildkit.dockerfile.v0",
49 | "empty_layer": true
50 | },
51 | {
52 | "created": "2023-04-24T09:12:02.044834481Z",
53 | "created_by": "ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/jdk/bin",
54 | "comment": "buildkit.dockerfile.v0",
55 | "empty_layer": true
56 | },
57 | {
58 | "created": "2023-04-24T09:12:02.044834481Z",
59 | "created_by": "ENTRYPOINT [\"/usr/jdk/bin/java\"]",
60 | "comment": "buildkit.dockerfile.v0",
61 | "empty_layer": true
62 | }
63 | ]
64 | }
65 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/python/expected-output-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]scratch[/]
2 | [#C285BF]ADD[/] [#96DCFE]file:c13b430c8699df107ffd9ea5230b92238bc037a8e1cbbe35d6ab664941d575da[/] [#96DCFE]/[/]
3 | [#C285BF]CMD[/] [#FAC81F][[[/][#CA9178]"bash"[/][#FAC81F]]][/]
4 | [#C285BF]RUN[/] [#96DCFE]set -eux; [#FAC81F]\[/]
5 | apt-get update; [#FAC81F]\[/]
6 | apt-get install -y --no-install-recommends ca-certificates curl netbase wget ; [#FAC81F]\[/]
7 | rm -rf /var/lib/apt/lists/*
8 | [/][#C285BF]RUN[/] [#96DCFE]set -ex; [#FAC81F]\[/]
9 | if ! command -v gpg > /dev/null; then apt-get update; [#FAC81F]\[/]
10 | apt-get install -y --no-install-recommends gnupg dirmngr ; [#FAC81F]\[/]
11 | rm -rf /var/lib/apt/lists/*; [#FAC81F]\[/]
12 | fi
13 | [/][#C285BF]RUN[/] [#96DCFE]apt-get update && apt-get install -y --no-install-recommends git mercurial openssh-client subversion procps [#FAC81F]\[/]
14 | && rm -rf /var/lib/apt/lists/*
15 | [/][#C285BF]RUN[/] [#96DCFE]set -ex; [#FAC81F]\[/]
16 | apt-get update; [#FAC81F]\[/]
17 | apt-get install -y --no-install-recommends autoconf automake bzip2 dpkg-dev file g++ gcc imagemagick libbz2-dev libc6-dev libcurl4-openssl-dev libdb-dev libevent-dev libffi-dev libgdbm-dev libglib2.0-dev libgmp-dev libjpeg-dev libkrb5-dev liblzma-dev libmagickcore-dev libmagickwand-dev libmaxminddb-dev libncurses5-dev libncursesw5-dev libpng-dev libpq-dev libreadline-dev libsqlite3-dev libssl-dev libtool libwebp-dev libxml2-dev libxslt-dev libyaml-dev make patch unzip xz-utils zlib1g-dev $( if apt-cache show 'default-libmysqlclient-dev' 2>/dev/null | grep -q '^Version:'; then echo 'default-libmysqlclient-dev'; [#FAC81F]\[/]
18 | else echo 'libmysqlclient-dev'; [#FAC81F]\[/]
19 | fi ) ; [#FAC81F]\[/]
20 | rm -rf /var/lib/apt/lists/*
21 | [/][#C285BF]ENV[/] [green]PATH[/][#FAC81F]=[/][#96DCFE]/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin[/]
22 | [#C285BF]ENV[/] [green]LANG[/][#FAC81F]=[/][#96DCFE]C.UTF-8[/]
23 | [#C285BF]RUN[/] [#96DCFE]set -eux; [#FAC81F]\[/]
24 | apt-get update; [#FAC81F]\[/]
25 | apt-get install -y --no-install-recommends libbluetooth-dev tk-dev uuid-dev ; [#FAC81F]\[/]
26 | rm -rf /var/lib/apt/lists/*
27 | [/][#C285BF]ENV[/] [green]GPG_KEY[/][#FAC81F]=[/][#96DCFE]A035C8C19219BA821ECEA86B64E628F8D684696D[/]
28 | [#C285BF]ENV[/] [green]PYTHON_VERSION[/][#FAC81F]=[/][#96DCFE]3.11.1[/]
29 | [#C285BF]RUN[/] [#96DCFE]set -eux; [#FAC81F]\[/]
30 | wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[[a-z]]*}/Python-$PYTHON_VERSION.tar.xz"; [#FAC81F]\[/]
31 | wget -O python.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[[a-z]]*}/Python-$PYTHON_VERSION.tar.xz.asc"; [#FAC81F]\[/]
32 | GNUPGHOME="$(mktemp -d)"; export GNUPGHOME; [#FAC81F]\[/]
33 | gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$GPG_KEY"; [#FAC81F]\[/]
34 | gpg --batch --verify python.tar.xz.asc python.tar.xz; [#FAC81F]\[/]
35 | command -v gpgconf > /dev/null && gpgconf --kill all || :; [#FAC81F]\[/]
36 | rm -rf "$GNUPGHOME" python.tar.xz.asc; [#FAC81F]\[/]
37 | mkdir -p /usr/src/python; [#FAC81F]\[/]
38 | tar --extract --directory /usr/src/python --strip-components=1 --file python.tar.xz; [#FAC81F]\[/]
39 | rm python.tar.xz; [#FAC81F]\[/]
40 | cd /usr/src/python; [#FAC81F]\[/]
41 | gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; [#FAC81F]\[/]
42 | ./configure --build="$gnuArch" --enable-loadable-sqlite-extensions --enable-optimizations --enable-option-checking=fatal --enable-shared --with-lto --with-system-expat --without-ensurepip ; [#FAC81F]\[/]
43 | nproc="$(nproc)"; [#FAC81F]\[/]
44 | make -j "$nproc" ; [#FAC81F]\[/]
45 | make install; [#FAC81F]\[/]
46 | bin="$(readlink -ve /usr/local/bin/python3)"; [#FAC81F]\[/]
47 | dir="$(dirname "$bin")"; [#FAC81F]\[/]
48 | mkdir -p "/usr/share/gdb/auto-load/$dir"; [#FAC81F]\[/]
49 | cp -vL Tools/gdb/libpython.py "/usr/share/gdb/auto-load/$bin-gdb.py"; [#FAC81F]\[/]
50 | cd /; [#FAC81F]\[/]
51 | rm -rf /usr/src/python; [#FAC81F]\[/]
52 | find /usr/local -depth \( \( -type d -a \( -name test -o -name tests -o -name idle_test \) \) -o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name 'libpython*.a' \) \) \) -exec rm -rf '{}' + ; [#FAC81F]\[/]
53 | ldconfig; [#FAC81F]\[/]
54 | python3 --version
55 | [/][#C285BF]RUN[/] [#96DCFE]set -eux; [#FAC81F]\[/]
56 | for src in idle3 pydoc3 python3 python3-config; do dst="$(echo "$src" | tr -d 3)"; [#FAC81F]\[/]
57 | [[ -s "/usr/local/bin/$src" ]]; [#FAC81F]\[/]
58 | [[ ! -e "/usr/local/bin/$dst" ]]; [#FAC81F]\[/]
59 | ln -svT "$src" "/usr/local/bin/$dst"; [#FAC81F]\[/]
60 | done
61 | [/][#C285BF]ENV[/] [green]PYTHON_PIP_VERSION[/][#FAC81F]=[/][#96DCFE]22.3.1[/]
62 | [#C285BF]ENV[/] [green]PYTHON_SETUPTOOLS_VERSION[/][#FAC81F]=[/][#96DCFE]65.5.1[/]
63 | [#C285BF]ENV[/] [green]PYTHON_GET_PIP_URL[/][#FAC81F]=[/][#96DCFE]https://github.com/pypa/get-pip/raw/66030fa03382b4914d4c4d0896961a0bdeeeb274/public/get-pip.py[/]
64 | [#C285BF]ENV[/] [green]PYTHON_GET_PIP_SHA256[/][#FAC81F]=[/][#96DCFE]1e501cf004eac1b7eb1f97266d28f995ae835d30250bec7f8850562703067dc6[/]
65 | [#C285BF]RUN[/] [#96DCFE]set -eux; [#FAC81F]\[/]
66 | wget -O get-pip.py "$PYTHON_GET_PIP_URL"; [#FAC81F]\[/]
67 | echo "$PYTHON_GET_PIP_SHA256 *get-pip.py" | sha256sum -c -; [#FAC81F]\[/]
68 | export PYTHONDONTWRITEBYTECODE=1; [#FAC81F]\[/]
69 | python get-pip.py --disable-pip-version-check --no-cache-dir --no-compile "pip==$PYTHON_PIP_VERSION" "setuptools==$PYTHON_SETUPTOOLS_VERSION" ; [#FAC81F]\[/]
70 | rm -f get-pip.py; [#FAC81F]\[/]
71 | pip --version
72 | [/][#C285BF]CMD[/] [#FAC81F][[[/][#CA9178]"python3"[/][#FAC81F]]][/]
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/python/expected-output-no-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]scratch[/]
2 | [#C285BF]ADD[/] [#96DCFE]file:c13b430c8699df107ffd9ea5230b92238bc037a8e1cbbe35d6ab664941d575da[/] [#96DCFE]/[/]
3 | [#C285BF]CMD[/] [#FAC81F][[[/][#CA9178]"bash"[/][#FAC81F]]][/]
4 | [#C285BF]RUN[/] [#96DCFE]set -eux; apt-get update; apt-get install -y --no-install-recommends ca-certificates curl netbase wget ; rm -rf /var/lib/apt/lists/*
5 | [/][#C285BF]RUN[/] [#96DCFE]set -ex; if ! command -v gpg > /dev/null; then apt-get update; apt-get install -y --no-install-recommends gnupg dirmngr ; rm -rf /var/lib/apt/lists/*; fi
6 | [/][#C285BF]RUN[/] [#96DCFE]apt-get update && apt-get install -y --no-install-recommends git mercurial openssh-client subversion procps && rm -rf /var/lib/apt/lists/*
7 | [/][#C285BF]RUN[/] [#96DCFE]set -ex; apt-get update; apt-get install -y --no-install-recommends autoconf automake bzip2 dpkg-dev file g++ gcc imagemagick libbz2-dev libc6-dev libcurl4-openssl-dev libdb-dev libevent-dev libffi-dev libgdbm-dev libglib2.0-dev libgmp-dev libjpeg-dev libkrb5-dev liblzma-dev libmagickcore-dev libmagickwand-dev libmaxminddb-dev libncurses5-dev libncursesw5-dev libpng-dev libpq-dev libreadline-dev libsqlite3-dev libssl-dev libtool libwebp-dev libxml2-dev libxslt-dev libyaml-dev make patch unzip xz-utils zlib1g-dev $( if apt-cache show 'default-libmysqlclient-dev' 2>/dev/null | grep -q '^Version:'; then echo 'default-libmysqlclient-dev'; else echo 'libmysqlclient-dev'; fi ) ; rm -rf /var/lib/apt/lists/*
8 | [/][#C285BF]ENV[/] [green]PATH[/][#FAC81F]=[/][#96DCFE]/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin[/]
9 | [#C285BF]ENV[/] [green]LANG[/][#FAC81F]=[/][#96DCFE]C.UTF-8[/]
10 | [#C285BF]RUN[/] [#96DCFE]set -eux; apt-get update; apt-get install -y --no-install-recommends libbluetooth-dev tk-dev uuid-dev ; rm -rf /var/lib/apt/lists/*
11 | [/][#C285BF]ENV[/] [green]GPG_KEY[/][#FAC81F]=[/][#96DCFE]A035C8C19219BA821ECEA86B64E628F8D684696D[/]
12 | [#C285BF]ENV[/] [green]PYTHON_VERSION[/][#FAC81F]=[/][#96DCFE]3.11.1[/]
13 | [#C285BF]RUN[/] [#96DCFE]set -eux; wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[[a-z]]*}/Python-$PYTHON_VERSION.tar.xz"; wget -O python.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[[a-z]]*}/Python-$PYTHON_VERSION.tar.xz.asc"; GNUPGHOME="$(mktemp -d)"; export GNUPGHOME; gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$GPG_KEY"; gpg --batch --verify python.tar.xz.asc python.tar.xz; command -v gpgconf > /dev/null && gpgconf --kill all || :; rm -rf "$GNUPGHOME" python.tar.xz.asc; mkdir -p /usr/src/python; tar --extract --directory /usr/src/python --strip-components=1 --file python.tar.xz; rm python.tar.xz; cd /usr/src/python; gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; ./configure --build="$gnuArch" --enable-loadable-sqlite-extensions --enable-optimizations --enable-option-checking=fatal --enable-shared --with-lto --with-system-expat --without-ensurepip ; nproc="$(nproc)"; make -j "$nproc" ; make install; bin="$(readlink -ve /usr/local/bin/python3)"; dir="$(dirname "$bin")"; mkdir -p "/usr/share/gdb/auto-load/$dir"; cp -vL Tools/gdb/libpython.py "/usr/share/gdb/auto-load/$bin-gdb.py"; cd /; rm -rf /usr/src/python; find /usr/local -depth \( \( -type d -a \( -name test -o -name tests -o -name idle_test \) \) -o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name 'libpython*.a' \) \) \) -exec rm -rf '{}' + ; ldconfig; python3 --version
14 | [/][#C285BF]RUN[/] [#96DCFE]set -eux; for src in idle3 pydoc3 python3 python3-config; do dst="$(echo "$src" | tr -d 3)"; [[ -s "/usr/local/bin/$src" ]]; [[ ! -e "/usr/local/bin/$dst" ]]; ln -svT "$src" "/usr/local/bin/$dst"; done
15 | [/][#C285BF]ENV[/] [green]PYTHON_PIP_VERSION[/][#FAC81F]=[/][#96DCFE]22.3.1[/]
16 | [#C285BF]ENV[/] [green]PYTHON_SETUPTOOLS_VERSION[/][#FAC81F]=[/][#96DCFE]65.5.1[/]
17 | [#C285BF]ENV[/] [green]PYTHON_GET_PIP_URL[/][#FAC81F]=[/][#96DCFE]https://github.com/pypa/get-pip/raw/66030fa03382b4914d4c4d0896961a0bdeeeb274/public/get-pip.py[/]
18 | [#C285BF]ENV[/] [green]PYTHON_GET_PIP_SHA256[/][#FAC81F]=[/][#96DCFE]1e501cf004eac1b7eb1f97266d28f995ae835d30250bec7f8850562703067dc6[/]
19 | [#C285BF]RUN[/] [#96DCFE]set -eux; wget -O get-pip.py "$PYTHON_GET_PIP_URL"; echo "$PYTHON_GET_PIP_SHA256 *get-pip.py" | sha256sum -c -; export PYTHONDONTWRITEBYTECODE=1; python get-pip.py --disable-pip-version-check --no-cache-dir --no-compile "pip==$PYTHON_PIP_VERSION" "setuptools==$PYTHON_SETUPTOOLS_VERSION" ; rm -f get-pip.py; pip --version
20 | [/][#C285BF]CMD[/] [#FAC81F][[[/][#CA9178]"python3"[/][#FAC81F]]][/]
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/python/image.json:
--------------------------------------------------------------------------------
1 | {
2 | "__comment": "library/python@sha256:779bcaad95871999fb6734a3923ff6a09cf6459231913d706b302eef5b6e383e",
3 | "os": "linux",
4 | "history": [
5 | {
6 | "created": "2022-12-21T01:20:21.922936512Z",
7 | "created_by": "/bin/sh -c #(nop) ADD file:c13b430c8699df107ffd9ea5230b92238bc037a8e1cbbe35d6ab664941d575da in / "
8 | },
9 | {
10 | "created": "2022-12-21T01:20:22.590344295Z",
11 | "created_by": "/bin/sh -c #(nop) CMD [\"bash\"]",
12 | "empty_layer": true
13 | },
14 | {
15 | "created": "2022-12-21T11:13:51.512187477Z",
16 | "created_by": "/bin/sh -c set -eux; \tapt-get update; \tapt-get install -y --no-install-recommends \t\tca-certificates \t\tcurl \t\tnetbase \t\twget \t; \trm -rf /var/lib/apt/lists/*"
17 | },
18 | {
19 | "created": "2022-12-21T11:13:58.806979115Z",
20 | "created_by": "/bin/sh -c set -ex; \tif ! command -v gpg > /dev/null; then \t\tapt-get update; \t\tapt-get install -y --no-install-recommends \t\t\tgnupg \t\t\tdirmngr \t\t; \t\trm -rf /var/lib/apt/lists/*; \tfi"
21 | },
22 | {
23 | "created": "2022-12-21T11:14:17.676013622Z",
24 | "created_by": "/bin/sh -c apt-get update && apt-get install -y --no-install-recommends \t\tgit \t\tmercurial \t\topenssh-client \t\tsubversion \t\t\t\tprocps \t&& rm -rf /var/lib/apt/lists/*"
25 | },
26 | {
27 | "created": "2022-12-21T11:15:31.49682344Z",
28 | "created_by": "/bin/sh -c set -ex; \tapt-get update; \tapt-get install -y --no-install-recommends \t\tautoconf \t\tautomake \t\tbzip2 \t\tdpkg-dev \t\tfile \t\tg++ \t\tgcc \t\timagemagick \t\tlibbz2-dev \t\tlibc6-dev \t\tlibcurl4-openssl-dev \t\tlibdb-dev \t\tlibevent-dev \t\tlibffi-dev \t\tlibgdbm-dev \t\tlibglib2.0-dev \t\tlibgmp-dev \t\tlibjpeg-dev \t\tlibkrb5-dev \t\tliblzma-dev \t\tlibmagickcore-dev \t\tlibmagickwand-dev \t\tlibmaxminddb-dev \t\tlibncurses5-dev \t\tlibncursesw5-dev \t\tlibpng-dev \t\tlibpq-dev \t\tlibreadline-dev \t\tlibsqlite3-dev \t\tlibssl-dev \t\tlibtool \t\tlibwebp-dev \t\tlibxml2-dev \t\tlibxslt-dev \t\tlibyaml-dev \t\tmake \t\tpatch \t\tunzip \t\txz-utils \t\tzlib1g-dev \t\t\t\t$( \t\t\tif apt-cache show 'default-libmysqlclient-dev' 2>/dev/null | grep -q '^Version:'; then \t\t\t\techo 'default-libmysqlclient-dev'; \t\t\telse \t\t\t\techo 'libmysqlclient-dev'; \t\t\tfi \t\t) \t; \trm -rf /var/lib/apt/lists/*"
29 | },
30 | {
31 | "created": "2022-12-21T17:54:25.078912736Z",
32 | "created_by": "/bin/sh -c #(nop) ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
33 | "empty_layer": true
34 | },
35 | {
36 | "created": "2022-12-21T17:54:25.176780719Z",
37 | "created_by": "/bin/sh -c #(nop) ENV LANG=C.UTF-8",
38 | "empty_layer": true
39 | },
40 | {
41 | "created": "2022-12-21T17:54:30.87196833Z",
42 | "created_by": "/bin/sh -c set -eux; \tapt-get update; \tapt-get install -y --no-install-recommends \t\tlibbluetooth-dev \t\ttk-dev \t\tuuid-dev \t; \trm -rf /var/lib/apt/lists/*"
43 | },
44 | {
45 | "created": "2022-12-21T18:22:49.128929869Z",
46 | "created_by": "/bin/sh -c #(nop) ENV GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D",
47 | "empty_layer": true
48 | },
49 | {
50 | "created": "2022-12-21T18:22:49.227106757Z",
51 | "created_by": "/bin/sh -c #(nop) ENV PYTHON_VERSION=3.11.1",
52 | "empty_layer": true
53 | },
54 | {
55 | "created": "2022-12-21T18:36:43.481204294Z",
56 | "created_by": "/bin/sh -c set -eux; \t\twget -O python.tar.xz \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz\"; \twget -O python.tar.xz.asc \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc\"; \tGNUPGHOME=\"$(mktemp -d)\"; export GNUPGHOME; \tgpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$GPG_KEY\"; \tgpg --batch --verify python.tar.xz.asc python.tar.xz; \tcommand -v gpgconf > /dev/null && gpgconf --kill all || :; \trm -rf \"$GNUPGHOME\" python.tar.xz.asc; \tmkdir -p /usr/src/python; \ttar --extract --directory /usr/src/python --strip-components=1 --file python.tar.xz; \trm python.tar.xz; \t\tcd /usr/src/python; \tgnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\"; \t./configure \t\t--build=\"$gnuArch\" \t\t--enable-loadable-sqlite-extensions \t\t--enable-optimizations \t\t--enable-option-checking=fatal \t\t--enable-shared \t\t--with-lto \t\t--with-system-expat \t\t--without-ensurepip \t; \tnproc=\"$(nproc)\"; \tmake -j \"$nproc\" \t; \tmake install; \t\tbin=\"$(readlink -ve /usr/local/bin/python3)\"; \tdir=\"$(dirname \"$bin\")\"; \tmkdir -p \"/usr/share/gdb/auto-load/$dir\"; \tcp -vL Tools/gdb/libpython.py \"/usr/share/gdb/auto-load/$bin-gdb.py\"; \t\tcd /; \trm -rf /usr/src/python; \t\tfind /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests -o -name idle_test \\) \\) \t\t\t-o \\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' -o -name 'libpython*.a' \\) \\) \t\t\\) -exec rm -rf '{}' + \t; \t\tldconfig; \t\tpython3 --version"
57 | },
58 | {
59 | "created": "2022-12-21T18:36:44.251539564Z",
60 | "created_by": "/bin/sh -c set -eux; \tfor src in idle3 pydoc3 python3 python3-config; do \t\tdst=\"$(echo \"$src\" | tr -d 3)\"; \t\t[ -s \"/usr/local/bin/$src\" ]; \t\t[ ! -e \"/usr/local/bin/$dst\" ]; \t\tln -svT \"$src\" \"/usr/local/bin/$dst\"; \tdone"
61 | },
62 | {
63 | "created": "2022-12-21T18:36:44.351889165Z",
64 | "created_by": "/bin/sh -c #(nop) ENV PYTHON_PIP_VERSION=22.3.1",
65 | "empty_layer": true
66 | },
67 | {
68 | "created": "2023-01-06T18:41:34.9197611Z",
69 | "created_by": "/bin/sh -c #(nop) ENV PYTHON_SETUPTOOLS_VERSION=65.5.1",
70 | "empty_layer": true
71 | },
72 | {
73 | "created": "2023-01-06T18:41:35.026340408Z",
74 | "created_by": "/bin/sh -c #(nop) ENV PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/66030fa03382b4914d4c4d0896961a0bdeeeb274/public/get-pip.py",
75 | "empty_layer": true
76 | },
77 | {
78 | "created": "2023-01-06T18:41:35.138016472Z",
79 | "created_by": "/bin/sh -c #(nop) ENV PYTHON_GET_PIP_SHA256=1e501cf004eac1b7eb1f97266d28f995ae835d30250bec7f8850562703067dc6",
80 | "empty_layer": true
81 | },
82 | {
83 | "created": "2023-01-06T18:41:40.450192701Z",
84 | "created_by": "/bin/sh -c set -eux; \t\twget -O get-pip.py \"$PYTHON_GET_PIP_URL\"; \techo \"$PYTHON_GET_PIP_SHA256 *get-pip.py\" | sha256sum -c -; \t\texport PYTHONDONTWRITEBYTECODE=1; \t\tpython get-pip.py \t\t--disable-pip-version-check \t\t--no-cache-dir \t\t--no-compile \t\t\"pip==$PYTHON_PIP_VERSION\" \t\t\"setuptools==$PYTHON_SETUPTOOLS_VERSION\" \t; \trm -f get-pip.py; \t\tpip --version"
85 | },
86 | {
87 | "created": "2023-01-06T18:41:40.59753588Z",
88 | "created_by": "/bin/sh -c #(nop) CMD [\"python3\"]",
89 | "empty_layer": true
90 | }
91 | ]
92 | }
93 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/windows/expected-output-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]mcr.microsoft.com/windows/servercore:10.0.20348.1366-amd64[/]
2 | [#C285BF]ENV[/] [green]ASPNETCORE_URLS[/][#FAC81F]=[/][#96DCFE]http://+:80[/] [#FAC81F]\[/]
3 | [green]DOTNET_RUNNING_IN_CONTAINER[/][#FAC81F]=[/][#96DCFE]true[/] [#FAC81F]\[/]
4 | [green]DOTNET_VERSION[/][#FAC81F]=[/][#96DCFE]7.0.1[/]
5 | [#C285BF]RUN[/] [#96DCFE]powershell -Command $ErrorActionPreference = 'Stop'; [#FAC81F]\[/]
6 | $ProgressPreference = 'SilentlyContinue'; [#FAC81F]\[/]
7 | Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.azureedge.net/dotnet/Runtime/$Env:DOTNET_VERSION/dotnet-runtime-$Env:DOTNET_VERSION-win-x64.zip; [#FAC81F]\[/]
8 | $dotnet_sha512 = '36a2245abc70c794282a7e6f270585baccd074875faff09b6eccff1c7ac8fada782951bc0f34bdc4bb33794508660346daa39c03aa0a45313e109c05eb98bd13'; [#FAC81F]\[/]
9 | if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512){ [#FAC81F]\[/]
10 | Write-Host 'CHECKSUM VERIFICATION FAILED!'; [#FAC81F]\[/]
11 | exit 1; [#FAC81F]\[/]
12 | }; [#FAC81F]\[/]
13 | mkdir $Env:ProgramFiles\dotnet; [#FAC81F]\[/]
14 | tar -oxzf dotnet.zip -C $Env:ProgramFiles\dotnet; [#FAC81F]\[/]
15 | Remove-Item -Force dotnet.zip
16 | [/][#C285BF]RUN[/] [#96DCFE]setx /M PATH "%PATH%;C:\Program Files\dotnet"
17 | [/][#C285BF]ENV[/] [green]ASPNET_VERSION[/][#FAC81F]=[/][#96DCFE]7.0.1[/]
18 | [#C285BF]RUN[/] [#96DCFE]powershell -Command $ErrorActionPreference = 'Stop'; [#FAC81F]\[/]
19 | $ProgressPreference = 'SilentlyContinue'; [#FAC81F]\[/]
20 | Invoke-WebRequest -OutFile aspnetcore.zip https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$Env:ASPNET_VERSION/aspnetcore-runtime-$Env:ASPNET_VERSION-win-x64.zip; [#FAC81F]\[/]
21 | $aspnetcore_sha512 = '7afa7bb9febabe32b9a38ac9a6376e342cb08c6d1f967647575b49995dd74feafdd6e6723688e1378a2edbd7e893ce15a75c74bec534303f0e60356ccf29d330'; [#FAC81F]\[/]
22 | if ((Get-FileHash aspnetcore.zip -Algorithm sha512).Hash -ne $aspnetcore_sha512){ [#FAC81F]\[/]
23 | Write-Host 'CHECKSUM VERIFICATION FAILED!'; [#FAC81F]\[/]
24 | exit 1; [#FAC81F]\[/]
25 | }; [#FAC81F]\[/]
26 | tar -oxzf aspnetcore.zip -C $Env:ProgramFiles\dotnet ./shared/Microsoft.AspNetCore.App; [#FAC81F]\[/]
27 | Remove-Item -Force aspnetcore.zip
28 | [/][#C285BF]ENV[/] [green]ASPNETCORE_URLS[/][#FAC81F]=[/][#96DCFE][/] [#FAC81F]\[/]
29 | [green]DOTNET_GENERATE_ASPNET_CERTIFICATE[/][#FAC81F]=[/][#96DCFE]false[/] [#FAC81F]\[/]
30 | [green]DOTNET_NOLOGO[/][#FAC81F]=[/][#96DCFE]true[/] [#FAC81F]\[/]
31 | [green]DOTNET_SDK_VERSION[/][#FAC81F]=[/][#96DCFE]7.0.101[/] [#FAC81F]\[/]
32 | [green]DOTNET_USE_POLLING_FILE_WATCHER[/][#FAC81F]=[/][#96DCFE]true[/] [#FAC81F]\[/]
33 | [green]NUGET_XMLDOC_MODE[/][#FAC81F]=[/][#96DCFE]skip[/] [#FAC81F]\[/]
34 | [green]POWERSHELL_DISTRIBUTION_CHANNEL[/][#FAC81F]=[/][#96DCFE]PSDocker-DotnetSDK-WindowsServerCore-ltsc2022[/]
35 | [#C285BF]RUN[/] [#96DCFE]powershell -Command " $ErrorActionPreference = 'Stop'; [#FAC81F]\[/]
36 | $ProgressPreference = 'SilentlyContinue'; [#FAC81F]\[/]
37 | Invoke-WebRequest -OutFile mingit.zip https://github.com/git-for-windows/git/releases/download/v2.37.3.windows.1/MinGit-2.37.3-64-bit.zip; [#FAC81F]\[/]
38 | $mingit_sha256 = 'cec8d038fadbdd82e269a5c458fd2a62711c1bb9a76c85f07c46de3bff6cdf32'; [#FAC81F]\[/]
39 | if ((Get-FileHash mingit.zip -Algorithm sha256).Hash -ne $mingit_sha256){ [#FAC81F]\[/]
40 | Write-Host 'CHECKSUM VERIFICATION FAILED!'; [#FAC81F]\[/]
41 | exit 1; [#FAC81F]\[/]
42 | }; [#FAC81F]\[/]
43 | mkdir $Env:ProgramFiles\MinGit; [#FAC81F]\[/]
44 | tar -oxzf mingit.zip -C $Env:ProgramFiles\MinGit; [#FAC81F]\[/]
45 | Remove-Item -Force mingit.zip"
46 | [/][#C285BF]RUN[/] [#96DCFE]powershell -Command " $ErrorActionPreference = 'Stop'; [#FAC81F]\[/]
47 | $ProgressPreference = 'SilentlyContinue'; [#FAC81F]\[/]
48 | Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.azureedge.net/dotnet/Sdk/$Env:DOTNET_SDK_VERSION/dotnet-sdk-$Env:DOTNET_SDK_VERSION-win-x64.zip; [#FAC81F]\[/]
49 | $dotnet_sha512 = 'f7083e2fef2f5c93c7d899cdf047f5c88626603ad0fdddf1f176820b74a32e3fcfb2402aef49406765ac8f160b5b48a714f09db2cce0ed04575f71dc6a49eaed'; [#FAC81F]\[/]
50 | if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512){ [#FAC81F]\[/]
51 | Write-Host 'CHECKSUM VERIFICATION FAILED!'; [#FAC81F]\[/]
52 | exit 1; [#FAC81F]\[/]
53 | }; [#FAC81F]\[/]
54 | tar -oxzf dotnet.zip -C $Env:ProgramFiles\dotnet ./LICENSE.txt ./ThirdPartyNotices.txt ./packs ./sdk ./sdk-manifests ./templates ./shared/Microsoft.WindowsDesktop.App; [#FAC81F]\[/]
55 | Remove-Item -Force dotnet.zip; [#FAC81F]\[/]
56 | $powershell_version = '7.3.0'; [#FAC81F]\[/]
57 | Invoke-WebRequest -OutFile PowerShell.Windows.x64.$powershell_version.nupkg https://pwshtool.blob.core.windows.net/tool/$powershell_version/PowerShell.Windows.x64.$powershell_version.nupkg; [#FAC81F]\[/]
58 | $powershell_sha512 = '5c5459e739c9abb2eb72249158af9dd868823ed6200a33d07385f0b37c4405b490c0b40f4ababd850d72721f884b41b86b3c8d8039e5bf1efb3aa84c72162cdf'; [#FAC81F]\[/]
59 | if ((Get-FileHash PowerShell.Windows.x64.$powershell_version.nupkg -Algorithm sha512).Hash -ne $powershell_sha512){ [#FAC81F]\[/]
60 | Write-Host 'CHECKSUM VERIFICATION FAILED!'; [#FAC81F]\[/]
61 | exit 1; [#FAC81F]\[/]
62 | }; [#FAC81F]\[/]
63 | & $Env:ProgramFiles\dotnet\dotnet tool install --add-source . --tool-path $Env:ProgramFiles\powershell --version $powershell_version PowerShell.Windows.x64; [#FAC81F]\[/]
64 | & $Env:ProgramFiles\dotnet\dotnet nuget locals all --clear; [#FAC81F]\[/]
65 | Remove-Item -Force PowerShell.Windows.x64.$powershell_version.nupkg; [#FAC81F]\[/]
66 | Remove-Item -Path $Env:ProgramFiles\powershell\.store\powershell.windows.x64\$powershell_version\powershell.windows.x64\$powershell_version\powershell.windows.x64.$powershell_version.nupkg -Force;"
67 | [/][#C285BF]RUN[/] [#96DCFE]setx /M PATH "%PATH%;C:\Program Files\powershell;C:\Program Files\MinGit\cmd"
68 | [/][#C285BF]RUN[/] [#96DCFE]dotnet help
69 | [/]
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/windows/expected-output-no-format.txt:
--------------------------------------------------------------------------------
1 | [#C285BF]FROM[/] [#96DCFE]mcr.microsoft.com/windows/servercore:10.0.20348.1366-amd64[/]
2 | [#C285BF]ENV[/] [green]ASPNETCORE_URLS[/][#FAC81F]=[/][#96DCFE]http://+:80[/] [green]DOTNET_RUNNING_IN_CONTAINER[/][#FAC81F]=[/][#96DCFE]true[/] [green]DOTNET_VERSION[/][#FAC81F]=[/][#96DCFE]7.0.1[/]
3 | [#C285BF]RUN[/] [#96DCFE]powershell -Command $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.azureedge.net/dotnet/Runtime/$Env:DOTNET_VERSION/dotnet-runtime-$Env:DOTNET_VERSION-win-x64.zip; $dotnet_sha512 = '36a2245abc70c794282a7e6f270585baccd074875faff09b6eccff1c7ac8fada782951bc0f34bdc4bb33794508660346daa39c03aa0a45313e109c05eb98bd13'; if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512) { Write-Host 'CHECKSUM VERIFICATION FAILED!'; exit 1; }; mkdir $Env:ProgramFiles\dotnet; tar -oxzf dotnet.zip -C $Env:ProgramFiles\dotnet; Remove-Item -Force dotnet.zip
4 | [/][#C285BF]RUN[/] [#96DCFE]setx /M PATH "%PATH%;C:\Program Files\dotnet"
5 | [/][#C285BF]ENV[/] [green]ASPNET_VERSION[/][#FAC81F]=[/][#96DCFE]7.0.1[/]
6 | [#C285BF]RUN[/] [#96DCFE]powershell -Command $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -OutFile aspnetcore.zip https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$Env:ASPNET_VERSION/aspnetcore-runtime-$Env:ASPNET_VERSION-win-x64.zip; $aspnetcore_sha512 = '7afa7bb9febabe32b9a38ac9a6376e342cb08c6d1f967647575b49995dd74feafdd6e6723688e1378a2edbd7e893ce15a75c74bec534303f0e60356ccf29d330'; if ((Get-FileHash aspnetcore.zip -Algorithm sha512).Hash -ne $aspnetcore_sha512) { Write-Host 'CHECKSUM VERIFICATION FAILED!'; exit 1; }; tar -oxzf aspnetcore.zip -C $Env:ProgramFiles\dotnet ./shared/Microsoft.AspNetCore.App; Remove-Item -Force aspnetcore.zip
7 | [/][#C285BF]ENV[/] [green]ASPNETCORE_URLS[/][#FAC81F]=[/][#96DCFE][/] [green]DOTNET_GENERATE_ASPNET_CERTIFICATE[/][#FAC81F]=[/][#96DCFE]false[/] [green]DOTNET_NOLOGO[/][#FAC81F]=[/][#96DCFE]true[/] [green]DOTNET_SDK_VERSION[/][#FAC81F]=[/][#96DCFE]7.0.101[/] [green]DOTNET_USE_POLLING_FILE_WATCHER[/][#FAC81F]=[/][#96DCFE]true[/] [green]NUGET_XMLDOC_MODE[/][#FAC81F]=[/][#96DCFE]skip[/] [green]POWERSHELL_DISTRIBUTION_CHANNEL[/][#FAC81F]=[/][#96DCFE]PSDocker-DotnetSDK-WindowsServerCore-ltsc2022[/]
8 | [#C285BF]RUN[/] [#96DCFE]powershell -Command " $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -OutFile mingit.zip https://github.com/git-for-windows/git/releases/download/v2.37.3.windows.1/MinGit-2.37.3-64-bit.zip; $mingit_sha256 = 'cec8d038fadbdd82e269a5c458fd2a62711c1bb9a76c85f07c46de3bff6cdf32'; if ((Get-FileHash mingit.zip -Algorithm sha256).Hash -ne $mingit_sha256) { Write-Host 'CHECKSUM VERIFICATION FAILED!'; exit 1; }; mkdir $Env:ProgramFiles\MinGit; tar -oxzf mingit.zip -C $Env:ProgramFiles\MinGit; Remove-Item -Force mingit.zip"
9 | [/][#C285BF]RUN[/] [#96DCFE]powershell -Command " $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.azureedge.net/dotnet/Sdk/$Env:DOTNET_SDK_VERSION/dotnet-sdk-$Env:DOTNET_SDK_VERSION-win-x64.zip; $dotnet_sha512 = 'f7083e2fef2f5c93c7d899cdf047f5c88626603ad0fdddf1f176820b74a32e3fcfb2402aef49406765ac8f160b5b48a714f09db2cce0ed04575f71dc6a49eaed'; if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512) { Write-Host 'CHECKSUM VERIFICATION FAILED!'; exit 1; }; tar -oxzf dotnet.zip -C $Env:ProgramFiles\dotnet ./LICENSE.txt ./ThirdPartyNotices.txt ./packs ./sdk ./sdk-manifests ./templates ./shared/Microsoft.WindowsDesktop.App; Remove-Item -Force dotnet.zip; $powershell_version = '7.3.0'; Invoke-WebRequest -OutFile PowerShell.Windows.x64.$powershell_version.nupkg https://pwshtool.blob.core.windows.net/tool/$powershell_version/PowerShell.Windows.x64.$powershell_version.nupkg; $powershell_sha512 = '5c5459e739c9abb2eb72249158af9dd868823ed6200a33d07385f0b37c4405b490c0b40f4ababd850d72721f884b41b86b3c8d8039e5bf1efb3aa84c72162cdf'; if ((Get-FileHash PowerShell.Windows.x64.$powershell_version.nupkg -Algorithm sha512).Hash -ne $powershell_sha512) { Write-Host 'CHECKSUM VERIFICATION FAILED!'; exit 1; }; & $Env:ProgramFiles\dotnet\dotnet tool install --add-source . --tool-path $Env:ProgramFiles\powershell --version $powershell_version PowerShell.Windows.x64; & $Env:ProgramFiles\dotnet\dotnet nuget locals all --clear; Remove-Item -Force PowerShell.Windows.x64.$powershell_version.nupkg; Remove-Item -Path $Env:ProgramFiles\powershell\.store\powershell.windows.x64\$powershell_version\powershell.windows.x64\$powershell_version\powershell.windows.x64.$powershell_version.nupkg -Force;"
10 | [/][#C285BF]RUN[/] [#96DCFE]setx /M PATH "%PATH%;C:\Program Files\powershell;C:\Program Files\MinGit\cmd"
11 | [/][#C285BF]RUN[/] [#96DCFE]dotnet help
12 | [/]
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestData/DockerfileCommand/windows/image.json:
--------------------------------------------------------------------------------
1 | {
2 | "__comment": "mcr.microsoft.com/dotnet/sdk@sha256:321018d653470f9d9b4da5d2aea9915916752ce6a4ef9806bfb68046aed62053",
3 | "os": "windows",
4 | "os.version": "10.0.20348.1366",
5 | "architecture": "amd64",
6 | "history": [
7 | {
8 | "created": "2022-04-22T01:12:09.4542389Z",
9 | "created_by": "Apply image 10.0.20348.643"
10 | },
11 | {
12 | "created": "2022-12-09T09:36:47.5016031Z",
13 | "created_by": "Install update 10.0.20348.1366"
14 | },
15 | {
16 | "created": "2022-12-13T18:38:52.9281645Z",
17 | "created_by": "cmd /S /C #(nop) ENV ASPNETCORE_URLS=http://+:80 DOTNET_RUNNING_IN_CONTAINER=true DOTNET_VERSION=7.0.1"
18 | },
19 | {
20 | "created": "2022-12-13T18:39:21.1523699Z",
21 | "created_by": "cmd /S /C powershell -Command $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.azureedge.net/dotnet/Runtime/$Env:DOTNET_VERSION/dotnet-runtime-$Env:DOTNET_VERSION-win-x64.zip; $dotnet_sha512 = '36a2245abc70c794282a7e6f270585baccd074875faff09b6eccff1c7ac8fada782951bc0f34bdc4bb33794508660346daa39c03aa0a45313e109c05eb98bd13'; if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512) { Write-Host 'CHECKSUM VERIFICATION FAILED!'; exit 1; }; mkdir $Env:ProgramFiles\\dotnet; tar -oxzf dotnet.zip -C $Env:ProgramFiles\\dotnet; Remove-Item -Force dotnet.zip"
22 | },
23 | {
24 | "created": "2022-12-13T18:39:35.052291Z",
25 | "created_by": "cmd /S /C setx /M PATH \"%PATH%;C:\\Program Files\\dotnet\""
26 | },
27 | {
28 | "created": "2022-12-13T18:39:38.3216918Z",
29 | "created_by": "cmd /S /C #(nop) ENV ASPNET_VERSION=7.0.1"
30 | },
31 | {
32 | "created": "2022-12-13T18:40:02.618451Z",
33 | "created_by": "cmd /S /C powershell -Command $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -OutFile aspnetcore.zip https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$Env:ASPNET_VERSION/aspnetcore-runtime-$Env:ASPNET_VERSION-win-x64.zip; $aspnetcore_sha512 = '7afa7bb9febabe32b9a38ac9a6376e342cb08c6d1f967647575b49995dd74feafdd6e6723688e1378a2edbd7e893ce15a75c74bec534303f0e60356ccf29d330'; if ((Get-FileHash aspnetcore.zip -Algorithm sha512).Hash -ne $aspnetcore_sha512) { Write-Host 'CHECKSUM VERIFICATION FAILED!'; exit 1; }; tar -oxzf aspnetcore.zip -C $Env:ProgramFiles\\dotnet ./shared/Microsoft.AspNetCore.App; Remove-Item -Force aspnetcore.zip"
34 | },
35 | {
36 | "created": "2022-12-13T18:40:05.0965059Z",
37 | "created_by": "cmd /S /C #(nop) ENV ASPNETCORE_URLS= DOTNET_GENERATE_ASPNET_CERTIFICATE=false DOTNET_NOLOGO=true DOTNET_SDK_VERSION=7.0.101 DOTNET_USE_POLLING_FILE_WATCHER=true NUGET_XMLDOC_MODE=skip POWERSHELL_DISTRIBUTION_CHANNEL=PSDocker-DotnetSDK-WindowsServerCore-ltsc2022"
38 | },
39 | {
40 | "created": "2022-12-13T18:40:37.2184725Z",
41 | "created_by": "cmd /S /C powershell -Command \" $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -OutFile mingit.zip https://github.com/git-for-windows/git/releases/download/v2.37.3.windows.1/MinGit-2.37.3-64-bit.zip; $mingit_sha256 = 'cec8d038fadbdd82e269a5c458fd2a62711c1bb9a76c85f07c46de3bff6cdf32'; if ((Get-FileHash mingit.zip -Algorithm sha256).Hash -ne $mingit_sha256) { Write-Host 'CHECKSUM VERIFICATION FAILED!'; exit 1; }; mkdir $Env:ProgramFiles\\MinGit; tar -oxzf mingit.zip -C $Env:ProgramFiles\\MinGit; Remove-Item -Force mingit.zip\""
42 | },
43 | {
44 | "created": "2022-12-13T18:41:59.2475589Z",
45 | "created_by": "cmd /S /C powershell -Command \" $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.azureedge.net/dotnet/Sdk/$Env:DOTNET_SDK_VERSION/dotnet-sdk-$Env:DOTNET_SDK_VERSION-win-x64.zip; $dotnet_sha512 = 'f7083e2fef2f5c93c7d899cdf047f5c88626603ad0fdddf1f176820b74a32e3fcfb2402aef49406765ac8f160b5b48a714f09db2cce0ed04575f71dc6a49eaed'; if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512) { Write-Host 'CHECKSUM VERIFICATION FAILED!'; exit 1; }; tar -oxzf dotnet.zip -C $Env:ProgramFiles\\dotnet ./LICENSE.txt ./ThirdPartyNotices.txt ./packs ./sdk ./sdk-manifests ./templates ./shared/Microsoft.WindowsDesktop.App; Remove-Item -Force dotnet.zip; $powershell_version = '7.3.0'; Invoke-WebRequest -OutFile PowerShell.Windows.x64.$powershell_version.nupkg https://pwshtool.blob.core.windows.net/tool/$powershell_version/PowerShell.Windows.x64.$powershell_version.nupkg; $powershell_sha512 = '5c5459e739c9abb2eb72249158af9dd868823ed6200a33d07385f0b37c4405b490c0b40f4ababd850d72721f884b41b86b3c8d8039e5bf1efb3aa84c72162cdf'; if ((Get-FileHash PowerShell.Windows.x64.$powershell_version.nupkg -Algorithm sha512).Hash -ne $powershell_sha512) { Write-Host 'CHECKSUM VERIFICATION FAILED!'; exit 1; }; & $Env:ProgramFiles\\dotnet\\dotnet tool install --add-source . --tool-path $Env:ProgramFiles\\powershell --version $powershell_version PowerShell.Windows.x64; & $Env:ProgramFiles\\dotnet\\dotnet nuget locals all --clear; Remove-Item -Force PowerShell.Windows.x64.$powershell_version.nupkg; Remove-Item -Path $Env:ProgramFiles\\powershell\\.store\\powershell.windows.x64\\$powershell_version\\powershell.windows.x64\\$powershell_version\\powershell.windows.x64.$powershell_version.nupkg -Force;\""
46 | },
47 | {
48 | "created": "2022-12-13T18:42:11.406177Z",
49 | "created_by": "cmd /S /C setx /M PATH \"%PATH%;C:\\Program Files\\powershell;C:\\Program Files\\MinGit\\cmd\""
50 | },
51 | {
52 | "created": "2022-12-13T18:42:24.3686611Z",
53 | "created_by": "cmd /S /C dotnet help"
54 | }
55 | ]
56 | }
57 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/TestHelper.cs:
--------------------------------------------------------------------------------
1 | using Spectre.Console.Rendering;
2 | using System.Text;
3 |
4 | namespace Valleysoft.Dredge.Tests;
5 |
6 | public static class TestHelper
7 | {
8 | public static string GetString(IEnumerable segments)
9 | {
10 | StringBuilder builder = new();
11 | foreach (Segment segment in segments)
12 | {
13 | builder.Append(segment.Text);
14 | }
15 | return builder.ToString();
16 | }
17 |
18 | public static string Normalize(string val) =>
19 | val.Replace("\r", string.Empty).TrimEnd();
20 | }
21 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using Moq;
2 | global using Xunit;
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.Tests/Valleysoft.Dredge.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | enable
6 | enable
7 |
8 | false
9 | IDE0290;xUnit1047
10 |
11 |
12 |
13 |
14 |
15 |
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 | all
18 |
19 |
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 | all
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | PreserveNewest
33 |
34 |
35 | PreserveNewest
36 |
37 |
38 | PreserveNewest
39 |
40 |
41 | PreserveNewest
42 |
43 |
44 | PreserveNewest
45 |
46 |
47 | PreserveNewest
48 |
49 |
50 | PreserveNewest
51 |
52 |
53 | PreserveNewest
54 |
55 |
56 | PreserveNewest
57 |
58 |
59 | PreserveNewest
60 |
61 |
62 | PreserveNewest
63 |
64 |
65 | PreserveNewest
66 |
67 |
68 | PreserveNewest
69 |
70 |
71 | PreserveNewest
72 |
73 |
74 | PreserveNewest
75 |
76 |
77 | PreserveNewest
78 |
79 |
80 | PreserveNewest
81 |
82 |
83 | PreserveNewest
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.5.33109.374
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Valleysoft.Dredge", "Valleysoft.Dredge\Valleysoft.Dredge.csproj", "{781B651C-A6F3-445B-AD9D-ABB5E5632445}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Valleysoft.Dredge.Tests", "Valleysoft.Dredge.Tests\Valleysoft.Dredge.Tests.csproj", "{39FD6D54-14E0-4894-95AC-951E3209A59F}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Valleysoft.Dredge.Analyzers", "Valleysoft.Dredge.Analyzers\Valleysoft.Dredge.Analyzers.csproj", "{23CD573A-1001-4856-A9E5-188043E314CE}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {781B651C-A6F3-445B-AD9D-ABB5E5632445}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {781B651C-A6F3-445B-AD9D-ABB5E5632445}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {781B651C-A6F3-445B-AD9D-ABB5E5632445}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {781B651C-A6F3-445B-AD9D-ABB5E5632445}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {39FD6D54-14E0-4894-95AC-951E3209A59F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {39FD6D54-14E0-4894-95AC-951E3209A59F}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {39FD6D54-14E0-4894-95AC-951E3209A59F}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {39FD6D54-14E0-4894-95AC-951E3209A59F}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {23CD573A-1001-4856-A9E5-188043E314CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {23CD573A-1001-4856-A9E5-188043E314CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {23CD573A-1001-4856-A9E5-188043E314CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {23CD573A-1001-4856-A9E5-188043E314CE}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {5097BADF-0AD9-4D7F-BFB7-2991DAEC0C5F}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/AppSettings.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 |
3 | namespace Valleysoft.Dredge;
4 |
5 | internal partial class AppSettings
6 | {
7 | public static readonly string SettingsPath =
8 | Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Valleysoft.Dredge", "settings.json");
9 |
10 | public const string FileCompareToolName = "fileCompareTool";
11 |
12 | [JsonProperty(FileCompareToolName)]
13 | public FileCompareToolSettings FileCompareTool { get; set; } = new();
14 |
15 | [JsonProperty("platform")]
16 | public PlatformSettings Platform { get; set; } = new();
17 |
18 | private AppSettings() {}
19 |
20 | public static AppSettings Load()
21 | {
22 | if (!File.Exists(SettingsPath))
23 | {
24 | AppSettings settings = new();
25 | string settingsStr = JsonConvert.SerializeObject(settings, JsonHelper.Settings);
26 |
27 | string dirName = Path.GetDirectoryName(SettingsPath)!;
28 | if (!Directory.Exists(dirName))
29 | {
30 | Directory.CreateDirectory(dirName);
31 | }
32 |
33 | File.WriteAllText(SettingsPath, settingsStr);
34 | return settings;
35 | }
36 | else
37 | {
38 | string settings = File.ReadAllText(SettingsPath);
39 | return JsonConvert.DeserializeObject(settings)!;
40 | }
41 | }
42 |
43 | public void Save()
44 | {
45 | string settingsStr = JsonConvert.SerializeObject(this, JsonHelper.Settings);
46 | File.WriteAllText(SettingsPath, settingsStr);
47 | }
48 | }
49 |
50 | internal partial class FileCompareToolSettings
51 | {
52 | [JsonProperty("exePath")]
53 | public string ExePath { get; set; } = string.Empty;
54 |
55 | [JsonProperty("args")]
56 | public string Args { get; set; } = string.Empty;
57 | }
58 |
59 | internal partial class PlatformSettings
60 | {
61 | [JsonProperty("os")]
62 | public string Os { get; set; } = string.Empty;
63 |
64 | [JsonProperty("osVersion")]
65 | public string OsVersion { get; set; } = string.Empty;
66 |
67 | [JsonProperty("arch")]
68 | public string Architecture { get; set; } = string.Empty;
69 | }
70 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/CommandHelper.cs:
--------------------------------------------------------------------------------
1 | using Valleysoft.DockerRegistryClient;
2 | using Valleysoft.DockerRegistryClient.Models;
3 |
4 | namespace Valleysoft.Dredge;
5 |
6 | internal static class CommandHelper
7 | {
8 | public static async Task ExecuteCommandAsync(string? registry, Func execute)
9 | {
10 | try
11 | {
12 | await execute();
13 | }
14 | catch (Exception e)
15 | {
16 | ConsoleColor savedColor = Console.ForegroundColor;
17 | Console.ForegroundColor = ConsoleColor.Red;
18 |
19 | string message = e.Message;
20 | if (e is RegistryException dockerRegistryException)
21 | {
22 | Error? error = dockerRegistryException.Errors.FirstOrDefault();
23 | if (error?.Code == "UNAUTHORIZED")
24 | {
25 | string loginCommand = "docker login";
26 | if (registry is not null)
27 | {
28 | loginCommand += $" {registry}";
29 | }
30 |
31 | message = $"The repository does not exist or may require authentication. If authentication is required, ensure that your credentials are stored for the registry by running '{loginCommand}'.";
32 | }
33 | else
34 | {
35 | message = error?.Message ?? message;
36 | }
37 | }
38 |
39 | Console.Error.WriteLine(message);
40 | Console.ForegroundColor = savedColor;
41 | Environment.Exit(1);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/CommandWithOptions.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 | using System.CommandLine.Invocation;
3 |
4 | namespace Valleysoft.Dredge.Commands;
5 |
6 | public abstract class CommandWithOptions : Command
7 | where TOptions : OptionsBase, new()
8 | {
9 | public new TOptions Options { get; set; } = new();
10 |
11 | protected CommandWithOptions(string name, string description)
12 | : base(name, description)
13 | {
14 | Options.SetCommandOptions(this);
15 | this.SetHandler(ExecuteAsyncCore);
16 | }
17 |
18 | private async Task ExecuteAsyncCore(InvocationContext context)
19 | {
20 | Options.SetParseResult(context.BindingContext.ParseResult);
21 | await ExecuteAsync();
22 | }
23 |
24 | protected abstract Task ExecuteAsync();
25 | }
26 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/CompareCommand.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class CompareCommand : Command
6 | {
7 | public CompareCommand(IDockerRegistryClientFactory dockerRegistryClientFactory)
8 | : base("compare", "Compares two images")
9 | {
10 | AddCommand(new CompareLayersCommand(dockerRegistryClientFactory));
11 | AddCommand(new CompareFilesCommand(dockerRegistryClientFactory));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/CompareFilesCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class CompareFilesCommand : RegistryCommandBase
6 | {
7 | private const string BaseOutputDirName = "base";
8 | private const string TargetOutputDirName = "target";
9 |
10 | private static readonly string CompareTempPath = Path.Combine(DredgeState.DredgeTempPath, "compare");
11 |
12 | public CompareFilesCommand(IDockerRegistryClientFactory dockerRegistryClientFactory)
13 | : base("files", "Compares two images by their files", dockerRegistryClientFactory)
14 | {
15 | }
16 |
17 | protected override Task ExecuteAsync()
18 | {
19 | return CommandHelper.ExecuteCommandAsync(registry: null, async () =>
20 | {
21 | AppSettings settings = AppSettings.Load();
22 | if (settings.FileCompareTool is null ||
23 | settings.FileCompareTool.ExePath == string.Empty ||
24 | settings.FileCompareTool.Args == string.Empty)
25 | {
26 | throw new Exception(
27 | $"This command requires additional configuration.{Environment.NewLine}In order to compare files, you must first set the '{AppSettings.FileCompareToolName}' setting in {AppSettings.SettingsPath}. This is an external tool of your choosing that will be executed to compare two directories containing files of the specified images. Use '{{0}}' and '{{1}}' placeholders in the args to indicate the base and target path locations that will be the inputs to the compare tool.");
28 | }
29 |
30 | await SaveImageLayersToDiskAsync(Options.BaseImage, BaseOutputDirName, Options.BaseLayerIndex, CompareOptionsBase.BaseArg);
31 | Console.Error.WriteLine();
32 | await SaveImageLayersToDiskAsync(Options.TargetImage, TargetOutputDirName, Options.TargetLayerIndex, CompareOptionsBase.TargetArg);
33 |
34 | string args = settings.FileCompareTool.Args
35 | .Replace("{0}", Path.Combine(CompareTempPath, BaseOutputDirName))
36 | .Replace("{1}", Path.Combine(CompareTempPath, TargetOutputDirName));
37 | Process.Start(settings.FileCompareTool.ExePath, args);
38 | });
39 | }
40 |
41 | private Task SaveImageLayersToDiskAsync(string image, string outputDirName, int? layerIndex, string layerIndexArg)
42 | {
43 | string workingDir = Path.Combine(CompareTempPath, outputDirName);
44 | if (Directory.Exists(workingDir))
45 | {
46 | Directory.Delete(workingDir, recursive: true);
47 | }
48 |
49 | return ImageHelper.SaveImageLayersToDiskAsync(
50 | DockerRegistryClientFactory,
51 | image,
52 | workingDir,
53 | layerIndex,
54 | layerIndexArg + CompareFilesOptions.LayerIndexSuffix,
55 | noSquash: false,
56 | Options);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/CompareFilesOptions.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class CompareFilesOptions : CompareOptionsBase
6 | {
7 | public const string LayerIndexSuffix = "-layer-index";
8 |
9 | private readonly Option baseLayerIndex;
10 | private readonly Option targetLayerIndex;
11 | private readonly Option outputOption;
12 |
13 | public int? BaseLayerIndex { get; set; }
14 | public int? TargetLayerIndex { get; set; }
15 | public CompareFilesOutput OutputType { get; set; }
16 |
17 | public CompareFilesOptions()
18 | {
19 | baseLayerIndex = Add(new Option($"--{BaseArg}{LayerIndexSuffix}", "Non-empty layer index of the base container image to compare with"));
20 | targetLayerIndex = Add(new Option($"--{TargetArg}{LayerIndexSuffix}", "Non-empty layer index of the target container image to compare against"));
21 | outputOption = Add(new Option("--output", () => CompareFilesOutput.ExternalTool, "Output type"));
22 | }
23 |
24 | protected override void GetValues()
25 | {
26 | base.GetValues();
27 | BaseLayerIndex = GetValue(baseLayerIndex);
28 | TargetLayerIndex = GetValue(targetLayerIndex);
29 | OutputType = GetValue(outputOption);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/CompareLayersOptions.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class CompareLayersOptions : CompareOptionsBase
6 | {
7 | private readonly Option outputOption;
8 | private readonly Option noColorOption;
9 | private readonly Option historyOption;
10 | private readonly Option compressedSizeOption;
11 |
12 | public CompareLayersOutput OutputFormat { get; set; }
13 | public bool IsColorDisabled { get; set; }
14 | public bool IncludeHistory { get; set; }
15 | public bool IncludeCompressedSize { get; set; }
16 |
17 | public CompareLayersOptions()
18 | {
19 | outputOption = Add(new Option("--output", () => CompareLayersOutput.SideBySide, "Output format"));
20 | noColorOption = Add(new Option("--no-color", "Disables dependency on color in comparison results"));
21 | historyOption = Add(new Option("--history", "Include layer history as part of the comparison"));
22 | compressedSizeOption = Add(new Option("--compressed-size", "Show the compressed size of the layer"));
23 | }
24 |
25 | protected override void GetValues()
26 | {
27 | base.GetValues();
28 | OutputFormat = GetValue(outputOption);
29 | IsColorDisabled = GetValue(noColorOption);
30 | IncludeHistory = GetValue(historyOption);
31 | IncludeCompressedSize = GetValue(compressedSizeOption);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/CompareOptionsBase.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class CompareOptionsBase : PlatformOptionsBase
6 | {
7 | public const string BaseArg = "base";
8 | public const string TargetArg = "target";
9 |
10 | private readonly Argument baseImageArg;
11 | private readonly Argument targetImageArg;
12 |
13 | public string BaseImage { get; set; } = string.Empty;
14 | public string TargetImage { get; set; } = string.Empty;
15 |
16 | public CompareOptionsBase()
17 | {
18 | baseImageArg = Add(new Argument(BaseArg, "Name of the base container image (, :, or @)"));
19 | targetImageArg = Add(new Argument(TargetArg, "Name of the target container image (, :, or @)"));
20 | }
21 |
22 | protected override void GetValues()
23 | {
24 | base.GetValues();
25 | BaseImage = GetValue(baseImageArg);
26 | TargetImage = GetValue(targetImageArg);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/DockerfileOptions.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class DockerfileOptions : PlatformOptionsBase
6 | {
7 | private readonly Argument imageArg;
8 | private readonly Option noColorOption;
9 | private readonly Option noFormatOption;
10 |
11 | public string Image { get; set; } = string.Empty;
12 | public bool NoColor { get; set; }
13 | public bool NoFormat { get; set; }
14 |
15 | public DockerfileOptions()
16 | {
17 | imageArg = Add(new Argument("image", "Name of the container image (, :, or @)"));
18 | noColorOption = Add(new Option("--no-color", "Disables use of syntax color in the output"));
19 | noFormatOption = Add(new Option("--no-format", "Disables use of heuristics to format the layer history for better readability"));
20 | }
21 |
22 | protected override void GetValues()
23 | {
24 | base.GetValues();
25 | Image = GetValue(imageArg);
26 | NoColor = GetValue(noColorOption);
27 | NoFormat = GetValue(noFormatOption);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/ImageCommand.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class ImageCommand : Command
6 | {
7 | public ImageCommand(IDockerRegistryClientFactory dockerRegistryClientFactory)
8 | : base("image", "Commands related to container images")
9 | {
10 | AddCommand(new CompareCommand(dockerRegistryClientFactory));
11 | AddCommand(new InspectCommand(dockerRegistryClientFactory));
12 | AddCommand(new OsCommand(dockerRegistryClientFactory));
13 | AddCommand(new SaveLayersCommand(dockerRegistryClientFactory));
14 | AddCommand(new DockerfileCommand(dockerRegistryClientFactory));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/InspectCommand.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Valleysoft.DockerRegistryClient.Models.Manifests;
3 |
4 | namespace Valleysoft.Dredge.Commands.Image;
5 |
6 | public class InspectCommand : RegistryCommandBase
7 | {
8 | public InspectCommand(IDockerRegistryClientFactory dockerRegistryClientFactory)
9 | : base("inspect", "Return low-level information on a container image", dockerRegistryClientFactory)
10 | {
11 | }
12 |
13 | protected override Task ExecuteAsync()
14 | {
15 | ImageName imageName = ImageName.Parse(Options.Image);
16 | return CommandHelper.ExecuteCommandAsync(imageName.Registry, async () =>
17 | {
18 | using IDockerRegistryClient client = await DockerRegistryClientFactory.GetClientAsync(imageName.Registry);
19 | IImageManifest manifest = (await ManifestHelper.GetResolvedManifestAsync(client, imageName, Options)).Manifest;
20 | string? digest = (manifest.Config?.Digest) ??
21 | throw new NotSupportedException($"Could not resolve the image config digest of '{Options.Image}'.");
22 | Stream blob = await client.Blobs.GetAsync(imageName.Repo, digest);
23 | using StreamReader reader = new(blob);
24 | string content = await reader.ReadToEndAsync();
25 | object? json = JsonConvert.DeserializeObject(content) ??
26 | throw new Exception($"Unable to deserialize content into JSON:\n{content}");
27 | string output = JsonConvert.SerializeObject(json, JsonHelper.Settings);
28 | Console.Out.WriteLine(output);
29 | });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/InspectOptions.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class InspectOptions : PlatformOptionsBase
6 | {
7 | private readonly Argument imageArg;
8 |
9 | public string Image { get; set; } = string.Empty;
10 |
11 | public InspectOptions()
12 | {
13 | imageArg = Add(new Argument("image", "Name of the container image (, :, or @)"));
14 | }
15 |
16 | protected override void GetValues()
17 | {
18 | base.GetValues();
19 | Image = GetValue(imageArg);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/OsCommand.cs:
--------------------------------------------------------------------------------
1 | using ICSharpCode.SharpZipLib.Tar;
2 | using Newtonsoft.Json;
3 | using System.IO.Compression;
4 | using System.Text;
5 | using System.Text.RegularExpressions;
6 | using Valleysoft.DockerRegistryClient;
7 | using Valleysoft.DockerRegistryClient.Models.Manifests;
8 | using ImageConfig = Valleysoft.DockerRegistryClient.Models.Images.Image;
9 |
10 | namespace Valleysoft.Dredge.Commands.Image;
11 |
12 | public partial class OsCommand : RegistryCommandBase
13 | {
14 | private static readonly Regex osReleaseRegex = OsReleaseRegex();
15 |
16 | public OsCommand(IDockerRegistryClientFactory dockerRegistryClientFactory)
17 | : base("os", "Gets OS info about the container image", dockerRegistryClientFactory)
18 | {
19 | }
20 |
21 | protected override Task ExecuteAsync()
22 | {
23 | ImageName imageName = ImageName.Parse(Options.Image);
24 | return CommandHelper.ExecuteCommandAsync(imageName.Registry, async () =>
25 | {
26 | using IDockerRegistryClient client = await DockerRegistryClientFactory.GetClientAsync(imageName.Registry);
27 | IImageManifest manifest = (await ManifestHelper.GetResolvedManifestAsync(client, imageName, Options)).Manifest;
28 |
29 | string? configDigest = (manifest.Config?.Digest) ?? throw new NotSupportedException($"Could not resolve the image config digest of '{Options.Image}'.");
30 | ImageConfig imageConfig = await client.Blobs.GetImageAsync(imageName.Repo, configDigest);
31 |
32 | IDescriptor baseLayer = manifest.Layers.First();
33 | if (baseLayer.Digest is null)
34 | {
35 | throw new Exception($"No digest was found for the base layer of '{Options.Image}'.");
36 | }
37 |
38 | object? osInfo;
39 | if (imageConfig.Os.Equals("windows", StringComparison.OrdinalIgnoreCase))
40 | {
41 | var windowsOsInfo = await GetWindowsOsInfoAsync(imageConfig, baseLayer.Digest, DockerRegistryClientFactory);
42 | osInfo = windowsOsInfo?.Info;
43 | }
44 | else
45 | {
46 | osInfo = await GetLinuxOsInfoAsync(client, imageName, baseLayer.Digest);
47 | }
48 |
49 | if (osInfo is null)
50 | {
51 | throw new Exception("Unable to derive OS information from the image.");
52 | }
53 |
54 | string output = JsonConvert.SerializeObject(osInfo, JsonHelper.SettingsNoCamelCase);
55 | Console.Out.WriteLine(output);
56 | });
57 | }
58 |
59 | private static async Task GetLinuxOsInfoAsync(IDockerRegistryClient client, ImageName imageName, string baseLayerDigest)
60 | {
61 | using Stream blobStream = await client.Blobs.GetAsync(imageName.Repo, baseLayerDigest);
62 | using GZipStream gZipStream = new(blobStream, CompressionMode.Decompress);
63 |
64 | // Can't use System.Formats.Tar.TarReader because it fails to read certain types of tarballs:
65 | // https://github.com/dotnet/runtime/issues/74316#issuecomment-1312227247
66 |
67 | using TarInputStream tarStream = new(gZipStream, Encoding.UTF8);
68 | TarEntry? entry = null;
69 | do
70 | {
71 | entry = tarStream.GetNextEntry();
72 |
73 | // Look for the os-release file (skip symlinks)
74 | if (entry is not null &&
75 | entry.Size > 0 &&
76 | (osReleaseRegex.IsMatch(entry.Name)))
77 | {
78 | using MemoryStream memStream = new();
79 | tarStream.CopyEntryContents(memStream);
80 | memStream.Position = 0;
81 | using StreamReader reader = new(memStream);
82 | string content = await reader.ReadToEndAsync();
83 | return LinuxOsInfo.Parse(content);
84 | }
85 | } while (entry is not null);
86 |
87 | return null;
88 | }
89 |
90 | internal static async Task<(WindowsOsInfo Info, string Repo)?> GetWindowsOsInfoAsync(ImageConfig imageConfig, string baseLayerDigest,
91 | IDockerRegistryClientFactory dockerRegistryClientFactory)
92 | {
93 | const string NanoServerRepo = "windows/nanoserver";
94 | const string ServerCoreRepo = "windows/servercore";
95 | const string ServerRepo = "windows/server";
96 | const string WindowsRepo = "windows";
97 |
98 | using IDockerRegistryClient mcrClient =
99 | await dockerRegistryClientFactory.GetClientAsync(RegistryHelper.McrRegistry);
100 |
101 | if (await mcrClient.Blobs.ExistsAsync(NanoServerRepo, baseLayerDigest))
102 | {
103 | return (new(WindowsType.NanoServer, imageConfig.OsVersion), NanoServerRepo);
104 | }
105 | else if (await mcrClient.Blobs.ExistsAsync(ServerCoreRepo, baseLayerDigest))
106 | {
107 | return (new(WindowsType.ServerCore, imageConfig.OsVersion), ServerCoreRepo);
108 | }
109 | else if (await mcrClient.Blobs.ExistsAsync(ServerRepo, baseLayerDigest))
110 | {
111 | return (new(WindowsType.Server, imageConfig.OsVersion), ServerRepo);
112 | }
113 | else if (await mcrClient.Blobs.ExistsAsync(WindowsRepo, baseLayerDigest))
114 | {
115 | return (new(WindowsType.Windows, imageConfig.OsVersion), WindowsRepo);
116 | }
117 | else
118 | {
119 | return null;
120 | }
121 | }
122 |
123 | [GeneratedRegex(@"(\./)?(etc|usr/lib)/os-release")]
124 | private static partial Regex OsReleaseRegex();
125 | }
126 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/OsOptions.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class OsOptions : PlatformOptionsBase
6 | {
7 | private readonly Argument imageArg;
8 |
9 | public string Image { get; set; } = string.Empty;
10 |
11 | public OsOptions()
12 | {
13 | imageArg = Add(new Argument("image", "Name of the container image (, :, or @)"));
14 | }
15 |
16 | protected override void GetValues()
17 | {
18 | base.GetValues();
19 | Image = GetValue(imageArg);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/SaveLayersCommand.cs:
--------------------------------------------------------------------------------
1 | using Valleysoft.DockerRegistryClient.Models.Manifests;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class SaveLayersCommand : RegistryCommandBase
6 | {
7 | public SaveLayersCommand(IDockerRegistryClientFactory dockerRegistryClientFactory)
8 | : base("save-layers", "Saves an image's extracted layers to disk", dockerRegistryClientFactory)
9 | {
10 | }
11 |
12 | protected override Task ExecuteAsync()
13 | {
14 | ImageName imageName = ImageName.Parse(Options.Image);
15 | return CommandHelper.ExecuteCommandAsync(imageName.Registry, async () =>
16 | {
17 | using IDockerRegistryClient client = await DockerRegistryClientFactory.GetClientAsync(imageName.Registry);
18 | IImageManifest manifest = (await ManifestHelper.GetResolvedManifestAsync(client, imageName, Options)).Manifest;
19 | string? digest = (manifest.Config?.Digest) ?? throw new NotSupportedException($"Could not resolve the image config digest of '{Options.Image}'.");
20 | await ImageHelper.SaveImageLayersToDiskAsync(
21 | DockerRegistryClientFactory,
22 | Options.Image,
23 | Options.OutputPath,
24 | Options.LayerIndex,
25 | SaveLayersOptions.LayerIndexOptionName,
26 | Options.NoSquash,
27 | Options);
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Image/SaveLayersOptions.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Image;
4 |
5 | public class SaveLayersOptions : PlatformOptionsBase
6 | {
7 | public const string LayerIndexOptionName = "--layer-index";
8 |
9 | private readonly Argument imageArg;
10 | private readonly Argument outputPathArg;
11 | private readonly Option noSquashOption;
12 | private readonly Option layerIndexOption;
13 |
14 | public string Image { get; set; } = string.Empty;
15 | public string OutputPath { get; set; } = string.Empty;
16 | public bool NoSquash { get; set; }
17 | public int? LayerIndex { get; set; }
18 |
19 | public SaveLayersOptions()
20 | {
21 | imageArg = Add(new Argument("image", "Name of the container image (, :, or @)"));
22 | outputPathArg = Add(new Argument("output-path", "Path to the output location"));
23 | noSquashOption = Add(new Option("--no-squash", "Do not squash the image layers"));
24 | layerIndexOption = Add(new Option(LayerIndexOptionName, "Index of the image layer to target"));
25 | }
26 |
27 | protected override void GetValues()
28 | {
29 | base.GetValues();
30 | Image = GetValue(imageArg);
31 | OutputPath = GetValue(outputPathArg);
32 | NoSquash = GetValue(noSquashOption);
33 | LayerIndex = GetValue(layerIndexOption);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Manifest/DigestCommand.cs:
--------------------------------------------------------------------------------
1 | using Valleysoft.DockerRegistryClient;
2 |
3 | namespace Valleysoft.Dredge.Commands.Manifest;
4 |
5 | public class DigestCommand : RegistryCommandBase
6 | {
7 | public DigestCommand(IDockerRegistryClientFactory dockerRegistryClientFactory)
8 | : base("digest", "Queries the digest of a manifest", dockerRegistryClientFactory)
9 | {
10 | }
11 |
12 | protected override Task ExecuteAsync()
13 | {
14 | ImageName imageName = ImageName.Parse(Options.Image);
15 | return CommandHelper.ExecuteCommandAsync(imageName.Registry, async () =>
16 | {
17 | using IDockerRegistryClient client = await DockerRegistryClientFactory.GetClientAsync(imageName.Registry);
18 |
19 | string digest = await client.Manifests.GetDigestAsync(imageName.Repo, (imageName.Tag ?? imageName.Digest)!);
20 |
21 | Console.Out.WriteLine(digest);
22 | });
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Manifest/DigestOptions.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Manifest;
4 |
5 | public class DigestOptions : OptionsBase
6 | {
7 | private readonly Argument imageArg;
8 |
9 | public string Image { get; set; } = string.Empty;
10 |
11 | public DigestOptions()
12 | {
13 | imageArg = Add(new Argument("name", "Name of the manifest (, :, or @)"));
14 | }
15 |
16 | protected override void GetValues()
17 | {
18 | Image = GetValue(imageArg);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Manifest/GetCommand.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Valleysoft.DockerRegistryClient.Models.Manifests;
3 |
4 | namespace Valleysoft.Dredge.Commands.Manifest;
5 |
6 | public class GetCommand : RegistryCommandBase
7 | {
8 | public GetCommand(IDockerRegistryClientFactory dockerRegistryClientFactory)
9 | : base("get", "Queries a manifest", dockerRegistryClientFactory)
10 | {
11 | }
12 |
13 | protected override Task ExecuteAsync()
14 | {
15 | ImageName imageName = ImageName.Parse(Options.Image);
16 | return CommandHelper.ExecuteCommandAsync(imageName.Registry, async () =>
17 | {
18 | using IDockerRegistryClient client = await DockerRegistryClientFactory.GetClientAsync(imageName.Registry);
19 |
20 | ManifestInfo manifestInfo = await client.Manifests.GetAsync(imageName.Repo, (imageName.Tag ?? imageName.Digest)!);
21 |
22 | string output = JsonConvert.SerializeObject(manifestInfo.Manifest, JsonHelper.Settings);
23 |
24 | Console.Out.WriteLine(output);
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Manifest/GetOptions.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Manifest;
4 |
5 | public class GetOptions : OptionsBase
6 | {
7 | private readonly Argument imageArg;
8 |
9 | public string Image { get; set; } = string.Empty;
10 |
11 | public GetOptions()
12 | {
13 | imageArg = Add(new Argument("name", "Name of the manifest (, :, or @)"));
14 | }
15 |
16 | protected override void GetValues()
17 | {
18 | Image = GetValue(imageArg);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Manifest/ManifestCommand.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Manifest;
4 |
5 | public class ManifestCommand : Command
6 | {
7 | public ManifestCommand(IDockerRegistryClientFactory dockerRegistryClientFactory) : base("manifest", "Commands related to manifests")
8 | {
9 | AddCommand(new GetCommand(dockerRegistryClientFactory));
10 | AddCommand(new DigestCommand(dockerRegistryClientFactory));
11 | AddCommand(new ResolveCommand(dockerRegistryClientFactory));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Manifest/ResolveCommand.cs:
--------------------------------------------------------------------------------
1 | using Valleysoft.DockerRegistryClient.Models.Manifests;
2 |
3 | namespace Valleysoft.Dredge.Commands.Manifest;
4 |
5 | public class ResolveCommand : RegistryCommandBase
6 | {
7 | public ResolveCommand(IDockerRegistryClientFactory dockerRegistryClientFactory)
8 | : base("resolve", "Resolves a manifest to a target platform's fully-qualified image digest", dockerRegistryClientFactory)
9 | {
10 | }
11 |
12 | protected override Task ExecuteAsync()
13 | {
14 | ImageName imageName = ImageName.Parse(Options.Image);
15 | return CommandHelper.ExecuteCommandAsync(imageName.Registry, async () =>
16 | {
17 | using IDockerRegistryClient client = await DockerRegistryClientFactory.GetClientAsync(imageName.Registry);
18 | ManifestInfo manifestInfo = (await ManifestHelper.GetResolvedManifestAsync(client, imageName, Options)).ManifestInfo;
19 | ImageName fullyQualifiedDigest = new(imageName.Registry, imageName.Repo, tag: null, manifestInfo.DockerContentDigest);
20 |
21 | Console.Out.WriteLine(fullyQualifiedDigest.ToString());
22 | });
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/Manifest/ResolveOptions.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 |
3 | namespace Valleysoft.Dredge.Commands.Manifest;
4 |
5 | public class SetOptions : PlatformOptionsBase
6 | {
7 | private readonly Argument imageArg;
8 |
9 | public string Image { get; set; } = string.Empty;
10 |
11 | public SetOptions()
12 | {
13 | imageArg = Add(new Argument("image", "Name of the container image (, :, or @)"));
14 | }
15 |
16 | protected override void GetValues()
17 | {
18 | base.GetValues();
19 | Image = GetValue(imageArg);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Valleysoft.Dredge/Commands/OptionsBase.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 | using System.CommandLine.Parsing;
3 | using System.Diagnostics.CodeAnalysis;
4 |
5 | namespace Valleysoft.Dredge.Commands;
6 |
7 | public abstract class OptionsBase
8 | {
9 | private readonly List arguments = [];
10 | private readonly List