#!/usr/bin/perl -w -i $^W = 1; use strict; use warnings; use diagnostics; my $file = ''; my $frontMatterFromFile = ''; my $title = ''; my $linkTitle = ''; my $gD = 0; my $otelSpecRepoUrl = 'https://github.com/open-telemetry/opentelemetry-specification'; my $otlpSpecRepoUrl = 'https://github.com/open-telemetry/opentelemetry-proto'; my $opAmpSpecRepoUrl = 'https://github.com/open-telemetry/opamp-spec'; my $semconvSpecRepoUrl = 'https://github.com/open-telemetry/semantic-conventions'; my $semConvRef = "$otelSpecRepoUrl/blob/main/semantic_conventions/README.md"; my $specBasePath = '/docs/specs'; my %patchMsgCount; my $lineNum; my %versionsRaw = # Keyname must end with colons because the auto-version update script expects one qw( spec: 1.41.0 otlp: 1.5.0 semconv: 1.30.0 ); # Versions map without the colon in the keys my %versions = map { s/://r => $versionsRaw{$_} } keys %versionsRaw; # Shorthands my $otelSpecVers = $versions{'spec'}; my $otlpSpecVers = $versions{'otlp'}; my $semconvVers = $versions{'semconv'}; my %versFromSubmod = %versions; # Actual version of submodules. Updated by getVersFromSubmodule(). sub printFrontMatter() { print "---\n"; if ($title eq 'OpenTelemetry Specification') { $title .= " $otelSpecVers"; $frontMatterFromFile =~ s/(linkTitle:) .*/$1 OTel $otelSpecVers/; # TODO: add to spec landing page $frontMatterFromFile .= "weight: 10\n" if $frontMatterFromFile !~ /^\s*weight/; } elsif ($title eq 'OpenTelemetry Protocol Specification') { $frontMatterFromFile =~ s/(title|linkTitle): .*/$& $otlpSpecVers/g; # TODO: add to spec landing page $frontMatterFromFile .= "weight: 20\n" if $frontMatterFromFile !~ /^\s*weight/; } elsif ($ARGV =~ /^tmp\/semconv\/docs\/\w+.md$/) { $title .= " $semconvVers"; $frontMatterFromFile =~ s/linkTitle: .*/$& $semconvVers/; # $frontMatterFromFile =~ s/body_class: .*/$& td-page--draft/; # $frontMatterFromFile =~ s/cascade:\n/$& draft: true\n/; } # Sample front-matter patch: # # } elsif ($ARGV =~ /otel\/specification\/logs\/api.md$/) { # $frontMatterFromFile .= "linkTitle: API\naliases: [bridge-api]\n" if # applyPatchOrPrintMsgIf('2024-12-01-bridge-api', 'spec', '1.39.0'); # } if ($ARGV =~ m{^tmp/semconv/docs.*/(README|_index)\.md$} && applyPatchOrPrintMsgIf('2025-01-29-path-base', 'semconv', '1.30.0-19-g') && $frontMatterFromFile =~ /^path_base_for_github_subdir:/m ) { $frontMatterFromFile =~ s/\npath_base_for_github_subdir:.*?\n/\n/; $frontMatterFromFile =~ s|\n from: tmp/semconv/docs/.*?\n|\n|; $frontMatterFromFile =~ s/\n to: .*README.md($|\n)/$1/; } my $titleMaybeQuoted = ($title =~ ':') ? "\"$title\"" : $title; print "title: $titleMaybeQuoted\n" if $frontMatterFromFile !~ /title: /; if ($title =~ /^OpenTelemetry (Protocol )?(.*)/) { $linkTitle = $2; } # TODO: add to front matter of OTel spec file and drop next line: $linkTitle = 'Design Goals' if $title eq 'Design Goals for OpenTelemetry Wire Protocol'; # printf STDOUT "> $title -> $linkTitle\n"; print "linkTitle: $linkTitle\n" if $linkTitle and $frontMatterFromFile !~ /linkTitle: /; print "$frontMatterFromFile" if $frontMatterFromFile; print "---\n"; } sub applyPatchOrPrintMsgIf($$$) { # Returns truthy if patch should be applied, otherwise prints message (once) as to why not. my ($patchID, $specName, $targetVers) = @_; my $vers; my $key = $specName . $patchID; return 0 if $patchMsgCount{$key}; if (($vers = $versions{$specName}) gt $targetVers) { print STDOUT "INFO: remove obsolete patch '$patchID' now that spec '$specName' is at v$vers > v$targetVers - $0\n"; } elsif (($vers = $versFromSubmod{$specName}) gt $targetVers) { print STDOUT "INFO [$patchID]: skipping patch '$patchID' since spec '$specName' submodule is at v$vers > v$targetVers - $0\n"; } else { return 'Apply the patch'; } $patchMsgCount{$key}++; return 0; } sub patchEventAliases() { return unless $ARGV =~ /^tmp\/otel\/specification\/logs\// && applyPatchOrPrintMsgIf('2025-01-23-event-aliases', 'spec', '1.41.0'); my $aliases = '^( - )(event-(api|sdk))$'; s|$aliases|$1./$2|; } sub patchSemConv1_30_0() { return unless $ARGV =~ /^tmp\/semconv\/docs\// && applyPatchOrPrintMsgIf('2025-01-24-emit-an-event-etc', 'semconv', '1.30.0-18-g'); s|Emit Event API|Log API|; s|(docs/specs/otel/logs/api.md#emit-a)n-event|$1-logrecord|; s|\[semantic-convention-groups\]|[group-stability]|; s|\Q../../docs/|../|g; # https://github.com/open-telemetry/semantic-conventions/pull/1843 } sub getVersFromSubmodule() { my %repoNames = qw( otlp opentelemetry-proto semconv semantic-conventions spec opentelemetry-specification ); foreach my $spec (keys %repoNames) { my $directory = $repoNames{$spec}; my $vers = qx( cd content-modules/$directory; git describe --tags 2>&1; ); chomp($vers); if ($?) { warn "WARNING: submodule '$spec': call to 'git describe' failed: '$vers'"; } else { $vers =~ s/v//; $versFromSubmod{$spec} = $vers; } } } # main getVersFromSubmodule(); while(<>) { $lineNum++; # printf STDOUT "$ARGV Got:$lineNum: $_" if $gD; if ($file ne $ARGV) { $file = $ARGV; $frontMatterFromFile = ''; $title = ''; $lineNum = 1; if (/^() { $lineNum++; last if /^--->?/; patchEventAliases(); patchSemConv1_30_0(); $frontMatterFromFile .= $_; } next; } } if(! $title) { ($title) = /^#\s+(.*)/; $linkTitle = ''; printFrontMatter() if $title; next; } if (/
/) { while(<>) { $lineNum++; last if /<\/details>/; } next; } if(//) { my $tocstop = ''; while(<>) { $lineNum++; last if/$tocstop/; next if /^\s*([-\+\*]\s|$)/; warn "WARN $ARGV:$lineNum: missing '$tocstop' directive? Aborting toc scan at line:\n $lineNum: $_"; print; last; } next; } ## Semconv if ($ARGV =~ /^tmp\/semconv/) { s|(\]\()/docs/|$1$specBasePath/semconv/|g; s|(\]:\s*)/docs/|$1$specBasePath/semconv/|; s|\((/model/.*?)\)|($semconvSpecRepoUrl/tree/v$semconvVers/$1)|g; } # SPECIFICATION custom processing s|\(https://github.com/open-telemetry/opentelemetry-specification\)|($specBasePath/otel/)|; s|(\]\()/specification/|$1$specBasePath/otel/)|; s|\.\./specification/(.*?\))|../otel/$1|g if $ARGV =~ /otel\/specification/; # Match markdown inline links or link definitions to OTel spec pages: "[...](URL)" or "[...]: URL" s|(\]:\s+\|\()https://github.com/open-telemetry/opentelemetry-specification/\w+/(main\|v$otelSpecVers)/specification(.*?\)?)|$1$specBasePath/otel$3|; # Match links to OTLP s|(\]:\s+\|\()?https://github.com/open-telemetry/opentelemetry-proto/(\w+/.*?/)?docs/specification.md(\)?)|$1$specBasePath/otlp/$3|g; s|github.com/open-telemetry/opentelemetry-proto/docs/specification.md|OTLP|g; # Localize links to semconv s|(\]:\s+\|\()https://github.com/open-telemetry/semantic-conventions/\w+/(main\|v$semconvVers)/docs(.*?\)?)|$1$specBasePath/semconv$3|g; # Images s|(\.\./)?internal(/img/[-\w]+\.png)|$2|g; s|(\]\()(img/.*?\))|$1../$2|g if $ARGV !~ /(logs|schemas)._index/ && $ARGV !~ /otlp\/docs/; s|(\]\()([^)]+\.png\))|$1../$2|g if $ARGV =~ /\btmp\/semconv\/docs\/general\/attributes/; s|(\]\()([^)]+\.png\))|$1../$2|g if $ARGV =~ /\btmp\/semconv\/docs\/http\/http-spans/; # Rewrite paths that are outside of the spec folders as external links: s|\.\.\/README.md|$otelSpecRepoUrl/|g if $ARGV =~ /specification._index/; s|\.\.\/README.md|/docs/specs/otel/| if $ARGV =~ /specification\/library-guidelines.md/; s|(\.\.\/)+(experimental\/[^)]+)|$otelSpecRepoUrl/tree/v$otelSpecVers/$2|g; s|(\.\.\/)+(supplementary-guidelines\/compatibility\/[^)]+)|$otelSpecRepoUrl/tree/v$otelSpecVers/$2|g; s|\.\./((?:examples/)?README\.md)|$otlpSpecRepoUrl/tree/v$otlpSpecVers/$1|g if $ARGV =~ /^tmp\/otlp/; # Make website-local page references local: s|https://opentelemetry.io/|/|g; ## OTLP proto files: link into the repo: s|\.\./(opentelemetry/proto/?.*)|$otlpSpecRepoUrl/tree/v$otlpSpecVers/$1|g if $ARGV =~ /\btmp\/otlp/; ## OpAMP s|\]\((proto/opamp.proto)\)|]($opAmpSpecRepoUrl/blob/main/$1)|; patchSemConv1_30_0(); print; }