Skip to content

Commit 4eaede9

Browse files
authored
Merge branch 'main' into native_stack_walk
2 parents 7fa7845 + 656d521 commit 4eaede9

172 files changed

Lines changed: 3440 additions & 1922 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ If applicable, add screenshots to help explain your problem.
2222
- OpenTelemetry Automatic Instrumentation version: [e.g. 1.0.0]
2323
- OS: [e.g. Windows Server 2012 R2 ]
2424
- .NET version: [e.g. .NET Framework 4.6.2, .NET Core 2.1]
25+
- Deployment mechanism, one of the following:
26+
- [ ] [OpenTelemetry.AutoInstrumentation NuGet package](../../docs/README.md#install-using-nuget-packages)
27+
- [ ] [Shell scripts](../../docs/README.md#shell-scripts)
28+
- [ ] [PowerShell module](../../docs/README.md#powershell-module-windows)
29+
- [ ] [Manual installation](../../docs/README.md#install-manually)
2530

2631
**Additional context**
2732
Add any other context about the problem here.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Cleanup Build Environment
2+
description: Cleanup the build environment on the host or inside a Docker container.
3+
4+
inputs:
5+
environment-type:
6+
description: Either `host` or `container`.
7+
required: true
8+
container-name:
9+
description: Running Docker container name when environment-type is container.
10+
required: false
11+
default: ""
12+
13+
runs:
14+
using: composite
15+
steps:
16+
- name: Validate cleanup inputs
17+
shell: pwsh
18+
run: |
19+
$environmentType = '${{ inputs.environment-type }}'
20+
$containerName = '${{ inputs.container-name }}'
21+
22+
if ($environmentType -notin @('host', 'container')) {
23+
throw "environment-type must be either 'host' or 'container'."
24+
}
25+
26+
if ($environmentType -eq 'container' -and [string]::IsNullOrEmpty($containerName)) {
27+
throw 'container-name input is required when environment-type is container.'
28+
}
29+
30+
- name: Stop build container
31+
if: ${{ inputs.environment-type == 'container' && inputs.container-name != '' }}
32+
shell: bash
33+
run: |
34+
set -u
35+
docker rm -f "${{ inputs.container-name }}"
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: Prepare Build Environment
2+
description: Prepare the build environment on the host or inside a Docker container and publish common execution details.
3+
4+
inputs:
5+
artifact-name:
6+
description: Artifact suffix for uploaded outputs.
7+
required: true
8+
environment-type:
9+
description: Either `host` or `container`.
10+
required: true
11+
dockerfile:
12+
description: Path to the Dockerfile used for container execution.
13+
required: false
14+
default: ""
15+
16+
outputs:
17+
container-name:
18+
description: Running Docker container name when environment-type is container.
19+
value: ${{ steps.start-build-container.outputs.container_name }}
20+
21+
runs:
22+
using: composite
23+
steps:
24+
- name: Validate environment inputs
25+
shell: pwsh
26+
run: |
27+
$environmentType = '${{ inputs.environment-type }}'
28+
29+
if ($environmentType -notin @('host', 'container')) {
30+
throw "environment-type must be either 'host' or 'container'."
31+
}
32+
33+
if ($environmentType -eq 'container') {
34+
if ([string]::IsNullOrEmpty('${{ inputs.dockerfile }}')) {
35+
throw 'dockerfile input is required when environment-type is container.'
36+
}
37+
}
38+
39+
- name: Setup .NET
40+
if: ${{ inputs.environment-type == 'host' }}
41+
uses: ./.github/actions/setup-dotnet
42+
43+
- name: Set up Docker Buildx
44+
if: ${{ inputs.environment-type == 'container' }}
45+
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # tag: v4.0.0
46+
47+
- name: Resolve image name
48+
if: ${{ inputs.environment-type == 'container' }}
49+
id: resolve-image-name
50+
shell: pwsh
51+
run: |
52+
$artifactName = '${{ inputs.artifact-name }}'.Trim().ToLowerInvariant()
53+
$sanitizedArtifactName = [regex]::Replace($artifactName, '[^a-z0-9._-]', '-')
54+
$imageName = "otel-dotnet-auto-build-$sanitizedArtifactName"
55+
"image_name=$imageName" >> $env:GITHUB_OUTPUT
56+
57+
- name: Build Docker image
58+
if: ${{ inputs.environment-type == 'container' }}
59+
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # tag: v7.1.0
60+
with:
61+
context: .
62+
file: ${{ inputs.dockerfile }}
63+
load: true
64+
tags: ${{ steps.resolve-image-name.outputs.image_name }}
65+
66+
- name: Start build container
67+
if: ${{ inputs.environment-type == 'container' }}
68+
id: start-build-container
69+
shell: bash
70+
run: |
71+
set -u
72+
sanitized_name="$(echo "${{ steps.resolve-image-name.outputs.image_name }}" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-')"
73+
container_name="${sanitized_name}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
74+
75+
docker_args=(
76+
--detach
77+
--name "${container_name}"
78+
--user "$(id -u):$(id -g)"
79+
--mount "type=bind,source=${GITHUB_WORKSPACE},target=/project"
80+
--workdir /project
81+
-e "HOME=/tmp"
82+
-e "NUGET_PACKAGES=/project/packages"
83+
)
84+
85+
docker run "${docker_args[@]}" "${{ steps.resolve-image-name.outputs.image_name }}" /bin/sh -c 'git config --global --add safe.directory /project && trap "exit 0" TERM INT; while true; do sleep 3600; done'
86+
echo "container_name=${container_name}" >> "$GITHUB_OUTPUT"
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Prepare Test Environment
2+
description: Prepare test prerequisites on the host or inside an existing Docker container.
3+
4+
inputs:
5+
environment-type:
6+
description: Build environment type. Use `host` for direct runner builds or `container` for Docker-based builds.
7+
required: true
8+
container-name:
9+
description: Running Docker container name when environment-type is container.
10+
required: false
11+
default: ""
12+
log-directory:
13+
description: Log directory expected to contain profiler output after the smoke test.
14+
required: true
15+
16+
runs:
17+
using: composite
18+
steps:
19+
- name: Validate test environment inputs
20+
shell: pwsh
21+
run: |
22+
$environmentType = '${{ inputs.environment-type }}'
23+
$containerName = '${{ inputs.container-name }}'
24+
25+
if ($environmentType -notin @('host', 'container')) {
26+
throw "environment-type must be either 'host' or 'container'."
27+
}
28+
29+
if ($environmentType -eq 'container' -and [string]::IsNullOrEmpty($containerName)) {
30+
throw 'container-name input is required when environment-type is container.'
31+
}
32+
33+
- name: Install macOS CoreUtils
34+
if: ${{ inputs.environment-type != 'container' && runner.os == 'macOS' }}
35+
shell: bash
36+
run: brew install coreutils
37+
38+
- name: Create test directory on host
39+
if: ${{ inputs.environment-type != 'container' && runner.os != 'Windows' }}
40+
shell: bash
41+
run: |
42+
sudo mkdir -p '${{ inputs.log-directory }}'
43+
sudo chmod a+rwx '${{ inputs.log-directory }}'
44+
45+
- name: Create test directory in container
46+
if: ${{ inputs.environment-type == 'container' }}
47+
uses: ./.github/actions/run-command-in-environment
48+
with:
49+
container-name: ${{ inputs.container-name }}
50+
container-user: "0"
51+
run: |
52+
mkdir -p '${{ inputs.log-directory }}'
53+
chmod a+rwx '${{ inputs.log-directory }}'
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Replace Build Artifacts
2+
description: Replace files or directories in a target tree using paths taken from downloaded build artifacts.
3+
4+
inputs:
5+
replace-artifacts:
6+
description: Multiline list of replacements in the form <artifact_name>!<path>.
7+
required: false
8+
default: ""
9+
target-root:
10+
description: Root directory whose matching paths should be replaced.
11+
required: false
12+
default: bin/tracer-home
13+
14+
runs:
15+
using: composite
16+
steps:
17+
- name: Download replacement artifacts
18+
if: ${{ inputs.replace-artifacts != '' }}
19+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # tag: v8.0.1
20+
with:
21+
path: bin/ci-artifacts/replacements
22+
23+
- name: Replace target paths from artifacts
24+
if: ${{ inputs.replace-artifacts != '' }}
25+
shell: pwsh
26+
run: '& "${{ github.action_path }}/replace-build-artifacts.ps1"'
27+
env:
28+
REPLACE_ARTIFACTS: ${{ inputs.replace-artifacts }}
29+
TARGET_ROOT: ${{ inputs.target-root }}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
$ErrorActionPreference = 'Stop'
2+
3+
$workspaceRoot = [System.IO.Path]::GetFullPath((Get-Location).Path)
4+
$sourceRoot = [System.IO.Path]::GetFullPath((Join-Path $workspaceRoot 'bin/ci-artifacts/replacements'))
5+
$targetRoot = [System.IO.Path]::GetFullPath((Join-Path $workspaceRoot $env:TARGET_ROOT))
6+
7+
function Test-IsUnderRoot {
8+
param(
9+
[string] $Path,
10+
[string] $Root
11+
)
12+
13+
$normalizedRoot = [System.IO.Path]::TrimEndingDirectorySeparator($Root)
14+
$normalizedPath = [System.IO.Path]::TrimEndingDirectorySeparator($Path)
15+
16+
return $normalizedPath -eq $normalizedRoot -or $normalizedPath.StartsWith($normalizedRoot + [System.IO.Path]::DirectorySeparatorChar, [System.StringComparison]::OrdinalIgnoreCase)
17+
}
18+
19+
function Get-SafeFullPath {
20+
param(
21+
[string] $Root,
22+
[string] $RelativePath
23+
)
24+
25+
if ([string]::IsNullOrWhiteSpace($RelativePath)) {
26+
throw 'Replacement path must not be empty.'
27+
}
28+
29+
if ($RelativePath -match '^[\\/]' -or $RelativePath -match '^[A-Za-z]:') {
30+
throw "Replacement path '$RelativePath' must be relative."
31+
}
32+
33+
$segments = $RelativePath -split '[\\/]+' | Where-Object { $_ -ne '' }
34+
if ($segments.Count -eq 0) {
35+
throw "Replacement path '$RelativePath' must not be empty."
36+
}
37+
38+
foreach ($segment in $segments) {
39+
if ($segment -eq '.' -or $segment -eq '..') {
40+
throw "Replacement path '$RelativePath' must not contain '.' or '..' segments."
41+
}
42+
}
43+
44+
$combinedPath = [System.IO.Path]::GetFullPath((Join-Path $Root $RelativePath))
45+
if (-not (Test-IsUnderRoot -Path $combinedPath -Root $Root)) {
46+
throw "Replacement path '$RelativePath' escapes root '$Root'."
47+
}
48+
49+
return $combinedPath
50+
}
51+
52+
function Get-ReplacementFileList {
53+
param(
54+
[string] $Path,
55+
[string] $DisplayRoot
56+
)
57+
58+
if (-not (Test-Path -LiteralPath $Path)) {
59+
return @()
60+
}
61+
62+
$item = Get-Item -LiteralPath $Path -Force
63+
if ($item.PSIsContainer) {
64+
$items = Get-ChildItem -LiteralPath $Path -File -Recurse | ForEach-Object {
65+
$relativePath = [System.IO.Path]::GetRelativePath($Path, $_.FullName)
66+
($DisplayRoot.TrimEnd('/', '\') + '/' + $relativePath.Replace('\', '/')).TrimStart('/')
67+
}
68+
69+
return @($items | Sort-Object -Unique)
70+
}
71+
72+
return @($DisplayRoot.Replace('\', '/'))
73+
}
74+
75+
$replacementEntries = ($env:REPLACE_ARTIFACTS ?? '') -split '\r?\n'
76+
77+
foreach ($rawEntry in $replacementEntries) {
78+
$replacement = $rawEntry.Trim()
79+
if ([string]::IsNullOrWhiteSpace($replacement)) {
80+
continue
81+
}
82+
83+
$separatorIndex = $replacement.IndexOf('!')
84+
if ($separatorIndex -lt 1 -or $separatorIndex -eq ($replacement.Length - 1)) {
85+
throw "Invalid replacement entry '$replacement'. Expected <artifact_name>!<path>."
86+
}
87+
88+
$artifactName = $replacement.Substring(0, $separatorIndex).Trim()
89+
$replacePath = $replacement.Substring($separatorIndex + 1).Trim()
90+
91+
if ($artifactName -match '[\\/:*?"<>|]') {
92+
throw "Artifact name '$artifactName' contains invalid path characters."
93+
}
94+
95+
$artifactRoot = [System.IO.Path]::GetFullPath((Join-Path $sourceRoot $artifactName))
96+
if (-not (Test-IsUnderRoot -Path $artifactRoot -Root $sourceRoot)) {
97+
throw "Artifact name '$artifactName' resolves outside '$sourceRoot'."
98+
}
99+
100+
$sourcePath = Get-SafeFullPath -Root $artifactRoot -RelativePath $replacePath
101+
$targetPath = Get-SafeFullPath -Root $targetRoot -RelativePath $replacePath
102+
103+
Write-Output "::group::Replace $replacePath from $artifactName"
104+
105+
if (-not (Test-Path -LiteralPath $sourcePath)) {
106+
throw "Replacement source '$sourcePath' does not exist."
107+
}
108+
109+
$deletedFiles = Get-ReplacementFileList -Path $targetPath -DisplayRoot $replacePath
110+
$addedFiles = Get-ReplacementFileList -Path $sourcePath -DisplayRoot $replacePath
111+
112+
$targetParent = Split-Path -Parent $targetPath
113+
if (-not [string]::IsNullOrWhiteSpace($targetParent)) {
114+
New-Item -ItemType Directory -Path $targetParent -Force | Out-Null
115+
}
116+
117+
if (Test-Path -LiteralPath $targetPath) {
118+
Remove-Item -LiteralPath $targetPath -Recurse -Force
119+
}
120+
121+
Copy-Item -LiteralPath $sourcePath -Destination $targetPath -Recurse -Force
122+
123+
if ($deletedFiles.Count -gt 0) {
124+
Write-Output 'Deleted files:'
125+
$deletedFiles | ForEach-Object { Write-Output " $_" }
126+
}
127+
else {
128+
Write-Output 'Deleted files: (none)'
129+
}
130+
131+
if ($addedFiles.Count -gt 0) {
132+
Write-Output 'Added files:'
133+
$addedFiles | ForEach-Object { Write-Output " $_" }
134+
}
135+
else {
136+
Write-Output 'Added files: (none)'
137+
}
138+
139+
$deletedOnly = @($deletedFiles | Where-Object { $_ -notin $addedFiles })
140+
$addedOnly = @($addedFiles | Where-Object { $_ -notin $deletedFiles })
141+
142+
if ($deletedOnly.Count -gt 0 -or $addedOnly.Count -gt 0) {
143+
$warningDetails = New-Object System.Collections.Generic.List[string]
144+
$warningDetails.Add("Replacement '$replacement' changed the file list.")
145+
146+
if ($deletedOnly.Count -gt 0) {
147+
$warningDetails.Add('Deleted but not added: ' + ($deletedOnly -join ', '))
148+
}
149+
150+
if ($addedOnly.Count -gt 0) {
151+
$warningDetails.Add('Added but not deleted: ' + ($addedOnly -join ', '))
152+
}
153+
154+
Write-Output "::warning::$($warningDetails -join ' ')"
155+
}
156+
157+
Write-Output "::endgroup::"
158+
}

0 commit comments

Comments
 (0)