mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-11-14 15:00:40 +00:00
feat: enhance coding harness, model security and system features
This commit is contained in:
2
scripts/sandbox/.dockerignore
Normal file
2
scripts/sandbox/.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
**
|
||||
!Dockerfile
|
||||
38
scripts/sandbox/Dockerfile
Normal file
38
scripts/sandbox/Dockerfile
Normal file
@@ -0,0 +1,38 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
# Both upstream stages are official images pinned to the linux/amd64 manifest digest observed
|
||||
# on 2026-08-30. The final scratch stage intentionally drops inherited image metadata such as
|
||||
# VOLUME, CMD, ENTRYPOINT, healthcheck and environment before adding a minimal allowlist.
|
||||
FROM node:24-bookworm@sha256:9137a20e25879e0b557227b57e3ee4e9af4bde29eb3db66134cd1723e84f830b AS node
|
||||
FROM maven:3.9.16-eclipse-temurin-21-noble@sha256:edf045813426842617b1667456ddec0026885146465e890b897655d877ba3386 AS assembly
|
||||
|
||||
USER root
|
||||
COPY --from=node /usr/local/ /usr/local/
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates git python3 ripgrep \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm -f /usr/local/bin/pnpm /usr/local/bin/pnpx \
|
||||
&& corepack enable pnpm \
|
||||
&& corepack prepare pnpm@10.14.0 --activate \
|
||||
&& groupadd --gid 65532 harness \
|
||||
&& useradd --uid 65532 --gid 65532 --no-create-home --home-dir /tmp \
|
||||
--shell /usr/sbin/nologin harness \
|
||||
&& java -version \
|
||||
&& mvn -version \
|
||||
&& node --version \
|
||||
&& npm --version \
|
||||
&& pnpm --version \
|
||||
&& python3 --version \
|
||||
&& rg --version \
|
||||
&& git --version
|
||||
|
||||
FROM scratch
|
||||
COPY --from=assembly / /
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
|
||||
JAVA_HOME=/opt/java/openjdk \
|
||||
MAVEN_HOME=/usr/share/maven \
|
||||
HOME=/tmp \
|
||||
LANG=C.UTF-8
|
||||
USER 65532:65532
|
||||
WORKDIR /workspace
|
||||
ENTRYPOINT []
|
||||
CMD []
|
||||
28
scripts/sandbox/README.md
Normal file
28
scripts/sandbox/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Coding harness sandbox image
|
||||
|
||||
This directory builds the evaluator-owned Linux toolchain used by `execute_process`.
|
||||
Runtime code never builds or pulls an image; it accepts only the final local `sha256:<image-id>`.
|
||||
|
||||
The build uses official Node and Maven manifests pinned for `linux/amd64`, then copies the
|
||||
assembled filesystem into a `scratch` stage so inherited volumes, commands, entrypoints,
|
||||
healthchecks, and environment do not cross the trust boundary. The final image contains Java,
|
||||
Maven, Node, npm, pnpm 10.14.0, Python 3, ripgrep, and Git, and runs as `65532:65532`.
|
||||
|
||||
`build-trusted-sandbox.ps1` verifies the Docker CLI digest and owner/ACL before its first Docker
|
||||
call. Every build gets a fresh empty CLI config, a minimal process environment, a bounded process
|
||||
timeout, and a private context containing only digest-approved copies of `Dockerfile` and
|
||||
`.dockerignore`. Image inspection uses a separate runtime config directory that must stay
|
||||
completely empty and checks the exact platform, non-root user, working directory, absent implicit
|
||||
commands/volumes/healthcheck, and an exact five-variable environment allowlist. The script prints
|
||||
the final image ID, CLI digest, and build-definition digests; deployments must pass the exact image
|
||||
and CLI values through `CODING_HARNESS_SANDBOX_IMAGE` and
|
||||
`CODING_HARNESS_DOCKER_EXECUTABLE_SHA256`.
|
||||
|
||||
The digest defaults in this repository prevent accidental drift but are not an external trust
|
||||
anchor: an operator release must approve the script/build-definition digests from an ACL- or
|
||||
signature-protected evaluator boundary. APT package resolution and the Corepack download are not
|
||||
yet hermetic, so the approved final image ID—not a rebuilt tag—is the runtime authority.
|
||||
|
||||
This image alone does not make the sandbox complete. Runtime create/start/inspect/remove state,
|
||||
workspace identity, restart cleanup, and live adversarial canaries remain independent release
|
||||
gates. Dependency caches are deliberately not inherited from the host and are not yet baked in.
|
||||
297
scripts/sandbox/build-trusted-sandbox.ps1
Normal file
297
scripts/sandbox/build-trusted-sandbox.ps1
Normal file
@@ -0,0 +1,297 @@
|
||||
#requires -Version 7.0
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$DockerExecutable = "C:/Program Files/Docker/Docker/resources/bin/docker.exe",
|
||||
[string]$ExpectedDockerExecutableSha256 = "83f31577a39b1c9f48c571cc2c31c99d74f74df1ab44003b4022301ed30a9831",
|
||||
[string]$ExpectedDockerfileSha256 = "72c262c9396e59359e52edddb9963360da4c581298247231f6ab2e369ce43eef",
|
||||
[string]$ExpectedDockerignoreSha256 = "16150e43ce98c333f93ad82a4991269a89bdd5596c427b4f5066ad266d6f871d",
|
||||
[string]$BuildDockerConfigDirectory = "C:/Users/wl/.ruoyi-harness/evaluator/docker-build-config",
|
||||
[string]$RuntimeDockerConfigDirectory = "C:/Users/wl/.ruoyi-harness/evaluator/docker-runtime-empty-config",
|
||||
[string]$DockerHost = "npipe:////./pipe/docker_engine",
|
||||
[string]$SourceContextDirectory = $PSScriptRoot,
|
||||
[string]$ImageTag = "ruoyi-harness-toolchain:local"
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$sourceContext = [System.IO.Path]::GetFullPath($SourceContextDirectory)
|
||||
if (-not [System.IO.Directory]::Exists($sourceContext) -or
|
||||
([System.IO.DirectoryInfo]::new($sourceContext).Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0 -or
|
||||
-not [System.IO.File]::Exists((Join-Path $sourceContext "Dockerfile")) -or
|
||||
-not [System.IO.File]::Exists((Join-Path $sourceContext ".dockerignore"))) {
|
||||
throw "BUILD_SOURCE_CONTEXT_INVALID"
|
||||
}
|
||||
|
||||
function Assert-NoBroadWriteAcl {
|
||||
param([Parameter(Mandatory)][string]$LiteralPath)
|
||||
|
||||
$trustedWriterSids = [System.Collections.Generic.HashSet[string]]::new(
|
||||
[System.StringComparer]::OrdinalIgnoreCase)
|
||||
$null = $trustedWriterSids.Add("S-1-5-18") # SYSTEM
|
||||
$null = $trustedWriterSids.Add("S-1-5-32-544") # BUILTIN\Administrators
|
||||
$null = $trustedWriterSids.Add([System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value)
|
||||
try {
|
||||
$trustedInstallerSid = ([System.Security.Principal.NTAccount]::new(
|
||||
"NT SERVICE", "TrustedInstaller")).Translate(
|
||||
[System.Security.Principal.SecurityIdentifier]).Value
|
||||
$null = $trustedWriterSids.Add($trustedInstallerSid)
|
||||
} catch {
|
||||
throw "TRUSTED_INSTALLER_IDENTITY_UNRESOLVED"
|
||||
}
|
||||
$writeMask = [System.Security.AccessControl.FileSystemRights]::WriteData -bor
|
||||
[System.Security.AccessControl.FileSystemRights]::AppendData -bor
|
||||
[System.Security.AccessControl.FileSystemRights]::WriteExtendedAttributes -bor
|
||||
[System.Security.AccessControl.FileSystemRights]::WriteAttributes -bor
|
||||
[System.Security.AccessControl.FileSystemRights]::DeleteSubdirectoriesAndFiles -bor
|
||||
[System.Security.AccessControl.FileSystemRights]::Delete -bor
|
||||
[System.Security.AccessControl.FileSystemRights]::ChangePermissions -bor
|
||||
[System.Security.AccessControl.FileSystemRights]::TakeOwnership
|
||||
$target = [System.IO.Path]::GetFullPath($LiteralPath)
|
||||
$paths = @($target)
|
||||
$parent = [System.IO.Directory]::GetParent($target)
|
||||
if ($null -ne $parent) { $paths += $parent.FullName }
|
||||
foreach ($path in $paths) {
|
||||
$acl = Get-Acl -LiteralPath $path
|
||||
$ownerSid = ([System.Security.Principal.NTAccount]$acl.Owner).Translate(
|
||||
[System.Security.Principal.SecurityIdentifier]).Value
|
||||
if (-not $trustedWriterSids.Contains($ownerSid)) {
|
||||
throw "UNTRUSTED_EVALUATOR_ACL_OWNER"
|
||||
}
|
||||
foreach ($rule in $acl.Access) {
|
||||
if ($rule.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow -or
|
||||
($rule.FileSystemRights -band $writeMask) -eq 0) {
|
||||
continue
|
||||
}
|
||||
try {
|
||||
$sid = $rule.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value
|
||||
} catch {
|
||||
throw "UNRESOLVED_EVALUATOR_ACL_IDENTITY"
|
||||
}
|
||||
if (-not $trustedWriterSids.Contains($sid)) {
|
||||
throw "UNTRUSTED_WRITE_EVALUATOR_ACL_DENIED"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-IsolatedDocker {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Executable,
|
||||
[Parameter(Mandatory)][string[]]$ArgumentList,
|
||||
[Parameter(Mandatory)][string]$TempDirectory,
|
||||
[ValidateRange(1, 600000)][int]$TimeoutMs = 120000,
|
||||
[switch]$EchoOutput
|
||||
)
|
||||
|
||||
$startInfo = [System.Diagnostics.ProcessStartInfo]::new()
|
||||
$startInfo.FileName = $Executable
|
||||
$startInfo.UseShellExecute = $false
|
||||
$startInfo.CreateNoWindow = $true
|
||||
$startInfo.RedirectStandardOutput = $true
|
||||
$startInfo.RedirectStandardError = $true
|
||||
foreach ($argument in $ArgumentList) {
|
||||
$startInfo.ArgumentList.Add($argument)
|
||||
}
|
||||
$startInfo.Environment.Clear()
|
||||
$windowsDirectory = [Environment]::GetFolderPath([Environment+SpecialFolder]::Windows)
|
||||
$startInfo.Environment["SystemRoot"] = $windowsDirectory
|
||||
$startInfo.Environment["WINDIR"] = $windowsDirectory
|
||||
$startInfo.Environment["TEMP"] = $TempDirectory
|
||||
$startInfo.Environment["TMP"] = $TempDirectory
|
||||
|
||||
$process = [System.Diagnostics.Process]::new()
|
||||
$process.StartInfo = $startInfo
|
||||
if (-not $process.Start()) {
|
||||
throw "TRUSTED_DOCKER_PROCESS_START_FAILED"
|
||||
}
|
||||
$stdoutTask = $process.StandardOutput.ReadToEndAsync()
|
||||
$stderrTask = $process.StandardError.ReadToEndAsync()
|
||||
if (-not $process.WaitForExit($TimeoutMs)) {
|
||||
try {
|
||||
$process.Kill($true)
|
||||
$null = $process.WaitForExit(5000)
|
||||
} finally {
|
||||
$process.Dispose()
|
||||
}
|
||||
throw "TRUSTED_DOCKER_PROCESS_TIMEOUT"
|
||||
}
|
||||
$stdout = $stdoutTask.GetAwaiter().GetResult()
|
||||
$stderr = $stderrTask.GetAwaiter().GetResult()
|
||||
if ($EchoOutput) {
|
||||
if (-not [string]::IsNullOrEmpty($stdout)) { [Console]::Out.Write($stdout) }
|
||||
if (-not [string]::IsNullOrEmpty($stderr)) { [Console]::Error.Write($stderr) }
|
||||
}
|
||||
$result = [pscustomobject]@{
|
||||
ExitCode = $process.ExitCode
|
||||
Stdout = $stdout
|
||||
Stderr = $stderr
|
||||
}
|
||||
$process.Dispose()
|
||||
$result
|
||||
}
|
||||
|
||||
$docker = [System.IO.Path]::GetFullPath($DockerExecutable)
|
||||
if (-not [System.IO.File]::Exists($docker) -or
|
||||
-not [System.IO.Path]::GetFileName($docker).Equals("docker.exe", [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "TRUSTED_DOCKER_CLI_REQUIRED"
|
||||
}
|
||||
$dockerInfo = [System.IO.FileInfo]::new($docker)
|
||||
if (($dockerInfo.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) {
|
||||
throw "TRUSTED_DOCKER_CLI_REPARSE_DENIED"
|
||||
}
|
||||
if ($ExpectedDockerExecutableSha256 -notmatch '^[0-9a-fA-F]{64}$') {
|
||||
throw "TRUSTED_DOCKER_CLI_DIGEST_REQUIRED"
|
||||
}
|
||||
$dockerSha256 = (Get-FileHash -LiteralPath $docker -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
if (-not $dockerSha256.Equals($ExpectedDockerExecutableSha256.ToLowerInvariant(), [System.StringComparison]::Ordinal)) {
|
||||
throw "TRUSTED_DOCKER_CLI_DIGEST_MISMATCH"
|
||||
}
|
||||
Assert-NoBroadWriteAcl -LiteralPath $docker
|
||||
|
||||
$buildConfigRoot = [System.IO.Path]::GetFullPath($BuildDockerConfigDirectory)
|
||||
$runtimeConfig = [System.IO.Path]::GetFullPath($RuntimeDockerConfigDirectory)
|
||||
[System.IO.Directory]::CreateDirectory($buildConfigRoot) | Out-Null
|
||||
[System.IO.Directory]::CreateDirectory($runtimeConfig) | Out-Null
|
||||
$buildConfigRootInfo = [System.IO.DirectoryInfo]::new($buildConfigRoot)
|
||||
$runtimeConfigInfo = [System.IO.DirectoryInfo]::new($runtimeConfig)
|
||||
if (($buildConfigRootInfo.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) {
|
||||
throw "CREDENTIAL_FREE_BUILD_DOCKER_CONFIG_REQUIRED"
|
||||
}
|
||||
if (($runtimeConfigInfo.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0 -or
|
||||
@($runtimeConfigInfo.EnumerateFileSystemInfos()).Count -ne 0) {
|
||||
throw "EMPTY_LINK_FREE_RUNTIME_DOCKER_CONFIG_REQUIRED"
|
||||
}
|
||||
Assert-NoBroadWriteAcl -LiteralPath $buildConfigRoot
|
||||
Assert-NoBroadWriteAcl -LiteralPath $runtimeConfig
|
||||
$buildConfig = [System.IO.Path]::GetFullPath((Join-Path $buildConfigRoot ("session-" + [guid]::NewGuid().ToString("N"))))
|
||||
if (-not ([System.IO.DirectoryInfo]::new($buildConfig).Parent.FullName).Equals($buildConfigRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "BUILD_DOCKER_CONFIG_BOUNDARY_INVALID"
|
||||
}
|
||||
[System.IO.Directory]::CreateDirectory($buildConfig) | Out-Null
|
||||
$buildConfigInfo = [System.IO.DirectoryInfo]::new($buildConfig)
|
||||
if (($buildConfigInfo.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0 -or
|
||||
@($buildConfigInfo.EnumerateFileSystemInfos()).Count -ne 0) {
|
||||
throw "EMPTY_LINK_FREE_BUILD_DOCKER_CONFIG_REQUIRED"
|
||||
}
|
||||
Assert-NoBroadWriteAcl -LiteralPath $buildConfig
|
||||
if ($DockerHost -ne "npipe:////./pipe/docker_engine") {
|
||||
throw "LOCAL_DOCKER_ENGINE_REQUIRED"
|
||||
}
|
||||
|
||||
$processRoot = [System.IO.Path]::GetFullPath((Join-Path $buildConfigRoot ("process-" + [guid]::NewGuid().ToString("N"))))
|
||||
try {
|
||||
if (-not ([System.IO.DirectoryInfo]::new($processRoot).Parent.FullName).Equals($buildConfigRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "BUILD_PROCESS_BOUNDARY_INVALID"
|
||||
}
|
||||
$context = Join-Path $processRoot "context"
|
||||
$processTemp = Join-Path $processRoot "temp"
|
||||
[System.IO.Directory]::CreateDirectory($context) | Out-Null
|
||||
[System.IO.Directory]::CreateDirectory($processTemp) | Out-Null
|
||||
Assert-NoBroadWriteAcl -LiteralPath $processRoot
|
||||
Assert-NoBroadWriteAcl -LiteralPath $context
|
||||
Assert-NoBroadWriteAcl -LiteralPath $processTemp
|
||||
|
||||
if ($ExpectedDockerfileSha256 -notmatch '^[0-9a-fA-F]{64}$' -or
|
||||
$ExpectedDockerignoreSha256 -notmatch '^[0-9a-fA-F]{64}$') {
|
||||
throw "APPROVED_BUILD_CONTEXT_DIGESTS_REQUIRED"
|
||||
}
|
||||
$dockerfile = Join-Path $context "Dockerfile"
|
||||
$dockerignore = Join-Path $context ".dockerignore"
|
||||
[System.IO.File]::Copy((Join-Path $sourceContext "Dockerfile"), $dockerfile, $false)
|
||||
[System.IO.File]::Copy((Join-Path $sourceContext ".dockerignore"), $dockerignore, $false)
|
||||
$dockerfileSha256 = (Get-FileHash -LiteralPath $dockerfile -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$dockerignoreSha256 = (Get-FileHash -LiteralPath $dockerignore -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
if (-not $dockerfileSha256.Equals($ExpectedDockerfileSha256.ToLowerInvariant(), [System.StringComparison]::Ordinal) -or
|
||||
-not $dockerignoreSha256.Equals($ExpectedDockerignoreSha256.ToLowerInvariant(), [System.StringComparison]::Ordinal)) {
|
||||
throw "BUILD_CONTEXT_DIGEST_MISMATCH"
|
||||
}
|
||||
$buildResult = Invoke-IsolatedDocker -Executable $docker -TempDirectory $processTemp -EchoOutput `
|
||||
-ArgumentList @("--config", $buildConfig, "--host", $DockerHost, "build",
|
||||
"--platform", "linux/amd64", "--pull", "--tag", $ImageTag,
|
||||
"--file", $dockerfile, $context)
|
||||
if ($buildResult.ExitCode -ne 0) {
|
||||
throw "SANDBOX_IMAGE_BUILD_FAILED"
|
||||
}
|
||||
|
||||
$inspectResult = Invoke-IsolatedDocker -Executable $docker -TempDirectory $processTemp -TimeoutMs 10000 `
|
||||
-ArgumentList @("--config", $runtimeConfig, "--host", $DockerHost,
|
||||
"image", "inspect", $ImageTag)
|
||||
if ($inspectResult.ExitCode -ne 0) {
|
||||
throw "SANDBOX_IMAGE_INSPECT_FAILED"
|
||||
}
|
||||
$inspectJson = $inspectResult.Stdout
|
||||
$records = @($inspectJson | ConvertFrom-Json -Depth 32)
|
||||
if ($records.Count -ne 1) {
|
||||
throw "SANDBOX_IMAGE_IDENTITY_AMBIGUOUS"
|
||||
}
|
||||
$image = $records[0]
|
||||
$volumesProperty = $image.Config.PSObject.Properties['Volumes']
|
||||
$cmdProperty = $image.Config.PSObject.Properties['Cmd']
|
||||
$healthcheckProperty = $image.Config.PSObject.Properties['Healthcheck']
|
||||
$entrypointProperty = $image.Config.PSObject.Properties['Entrypoint']
|
||||
$volumes = if ($null -eq $volumesProperty) { $null } else { $volumesProperty.Value }
|
||||
$commandValue = if ($null -eq $cmdProperty) { $null } else { $cmdProperty.Value }
|
||||
$healthcheck = if ($null -eq $healthcheckProperty) { $null } else { $healthcheckProperty.Value }
|
||||
$entrypoint = if ($null -eq $entrypointProperty) { $null } else { $entrypointProperty.Value }
|
||||
if ([string]$image.Id -notmatch '^sha256:[0-9a-f]{64}$' -or
|
||||
[string]$image.Os -ne "linux" -or [string]$image.Architecture -ne "amd64" -or
|
||||
[string]$image.Config.User -ne "65532:65532" -or
|
||||
[string]$image.Config.WorkingDir -ne "/workspace" -or
|
||||
$null -ne $volumes -or $null -ne $commandValue -or
|
||||
$null -ne $healthcheck -or $null -ne $entrypoint) {
|
||||
throw "SANDBOX_IMAGE_METADATA_UNTRUSTED"
|
||||
}
|
||||
|
||||
$approvedEnvironment = @{
|
||||
PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
JAVA_HOME = "/opt/java/openjdk"
|
||||
MAVEN_HOME = "/usr/share/maven"
|
||||
HOME = "/tmp"
|
||||
LANG = "C.UTF-8"
|
||||
}
|
||||
$seenEnvironment = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal)
|
||||
foreach ($entry in @($image.Config.Env)) {
|
||||
$separator = ([string]$entry).IndexOf('=')
|
||||
if ($separator -le 0) {
|
||||
throw "SANDBOX_IMAGE_ENVIRONMENT_UNTRUSTED"
|
||||
}
|
||||
$name = ([string]$entry).Substring(0, $separator)
|
||||
$value = ([string]$entry).Substring($separator + 1)
|
||||
if (-not $approvedEnvironment.ContainsKey($name) -or
|
||||
-not ([string]$approvedEnvironment[$name]).Equals($value, [System.StringComparison]::Ordinal) -or
|
||||
-not $seenEnvironment.Add($name)) {
|
||||
throw "SANDBOX_IMAGE_ENVIRONMENT_UNTRUSTED"
|
||||
}
|
||||
}
|
||||
if ($seenEnvironment.Count -ne $approvedEnvironment.Count) {
|
||||
throw "SANDBOX_IMAGE_ENVIRONMENT_UNTRUSTED"
|
||||
}
|
||||
|
||||
[ordered]@{
|
||||
schemaVersion = 1
|
||||
status = "BUILT_AND_INSPECTED"
|
||||
imageId = [string]$image.Id
|
||||
dockerExecutableSha256 = $dockerSha256
|
||||
dockerfileSha256 = $dockerfileSha256
|
||||
dockerignoreSha256 = $dockerignoreSha256
|
||||
dockerConfigDirectory = $runtimeConfig
|
||||
dockerHost = $DockerHost
|
||||
baseManifests = @(
|
||||
"maven@sha256:edf045813426842617b1667456ddec0026885146465e890b897655d877ba3386"
|
||||
"node@sha256:9137a20e25879e0b557227b57e3ee4e9af4bde29eb3db66134cd1723e84f830b"
|
||||
)
|
||||
} | ConvertTo-Json -Depth 6 -Compress
|
||||
} finally {
|
||||
$sessionInfo = [System.IO.DirectoryInfo]::new($buildConfig)
|
||||
if ([System.IO.Directory]::Exists($buildConfig) -and
|
||||
$sessionInfo.Parent.FullName.Equals($buildConfigRoot, [System.StringComparison]::OrdinalIgnoreCase) -and
|
||||
$sessionInfo.Name.StartsWith("session-", [System.StringComparison]::Ordinal)) {
|
||||
Remove-Item -LiteralPath $buildConfig -Recurse -Force
|
||||
}
|
||||
$processInfo = [System.IO.DirectoryInfo]::new($processRoot)
|
||||
if ([System.IO.Directory]::Exists($processRoot) -and
|
||||
$processInfo.Parent.FullName.Equals($buildConfigRoot, [System.StringComparison]::OrdinalIgnoreCase) -and
|
||||
$processInfo.Name.StartsWith("process-", [System.StringComparison]::Ordinal)) {
|
||||
Remove-Item -LiteralPath $processRoot -Recurse -Force
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user