Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 17 additions & 33 deletions .github/workflows/update-semconv-integration-branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ on:

permissions:
contents: read
# Needed so the "Pick integration branch" step can open a tracking issue
# via gh when it detects problems (e.g. stale integration branches).
issues: write

env:
REPO: semantic-conventions
# Abbreviation of REPO used in branch and submodule names
ABBR: semconv

jobs:
update-semconv-integration-branch:
Expand All @@ -28,34 +36,10 @@ jobs:
# this is needed to trigger workflows when pushing new commits to an existing PR
token: ${{ steps.otelbot-token.outputs.token }}

- name: Set environment variables
- name: Pick integration branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
branch_prefix="otelbot/semconv-integration"

version=$(git branch -r \
| grep -E "^ *origin/$branch_prefix-v[0-9]+\.[0-9]+\..*-dev" \
| sed "s|^ *origin/$branch_prefix-||" \
| sed "s|-dev$||")

if [[ -z "$version" ]]; then
latest_version=$(gh release view \
--repo open-telemetry/semantic-conventions \
--json tagName \
--jq .tagName)
if [[ $latest_version =~ ^v([0-9]+)\.([0-9]+)\. ]]; then
major="${BASH_REMATCH[1]}"
minor="${BASH_REMATCH[2]}"
version="v$major.$((minor + 1)).0"
else
echo "unexpected version: $latest_version"
exit 1
fi
fi

echo "VERSION=$version" >> $GITHUB_ENV
echo "BRANCH=$branch_prefix-$version-dev" >> $GITHUB_ENV
run: node scripts/gh/specs/pick-branch/cli.mjs --spec=semconv

- name: Checkout or create branch
run: |
Expand All @@ -82,8 +66,8 @@ jobs:

- name: Update submodule and Hugo mounts
run: |
git submodule update --init content-modules/semantic-conventions
cd content-modules/semantic-conventions
git submodule update --init content-modules/${{ env.REPO }}
cd content-modules/${{ env.REPO }}

if git ls-remote --exit-code --tags origin $VERSION; then
git reset --hard $VERSION
Expand All @@ -95,18 +79,18 @@ jobs:
commit_desc=$(git describe --tags)
cd ../..

sed -i "s/^\tsemconv-pin = .*/\tsemconv-pin = $commit_desc/" .gitmodules
sed -i "s/^\t${{ env.ABBR }}-pin = .*/\t${{ env.ABBR }}-pin = $commit_desc/" .gitmodules

if [ "$tag_exists" == "true" ]; then
sed -i "s/^\(\s *\)semconv: .*/\1semconv: ${commit_desc#v}/" scripts/content-modules/adjust-pages.pl
sed -i "s/^\(\s *\)${{ env.ABBR }}: .*/\1${{ env.ABBR }}: ${commit_desc#v}/" scripts/content-modules/adjust-pages.pl
fi

./scripts/update-semconv-mounts.pl

git add -A

if ! git diff-index --quiet --cached HEAD; then
git commit -am "Update semconv submodule to $commit_desc"
git commit -am "Update ${{ env.ABBR }} submodule to $commit_desc"
git push
fi

Expand Down Expand Up @@ -136,7 +120,7 @@ jobs:
run: |
prs=$(gh pr list --state open --head $BRANCH)
if [ -z "$prs" ]; then
gh pr create --title "DRAFT Update semantic conventions to unreleased $VERSION-dev" \
--body "This is a draft PR used for identifying issues integrating the latest (unreleased) semantic conventions." \
gh pr create --title "DRAFT Update ${{ env.REPO }} to unreleased $VERSION-dev" \
--body "This is a draft PR used for identifying issues integrating the latest (unreleased) [${{ env.REPO }}](https://github.com/open-telemetry/${{ env.REPO }})." \
--draft
fi
31 changes: 5 additions & 26 deletions .github/workflows/update-spec-integration-branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:

permissions:
contents: read
# Needed so the "Pick integration branch" step can open a tracking issue
# via gh when it detects problems (e.g. stale integration branches).
issues: write

env:
REPO: opentelemetry-specification
Expand All @@ -32,34 +35,10 @@ jobs:
fetch-depth: 0
token: ${{ steps.otelbot-token.outputs.token }}

- name: Set environment variables
- name: Pick integration branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
branch_prefix="otelbot/${{ env.ABBR }}-integration"

version=$(git branch -r \
| grep -E "^ *origin/$branch_prefix-v[0-9]+\.[0-9]+\..*-dev" \
| sed "s|^ *origin/$branch_prefix-||" \
| sed "s|-dev$||")

if [[ -z "$version" ]]; then
latest_version=$(gh release view \
--repo open-telemetry/${{ env.REPO }} \
--json tagName \
--jq .tagName)
if [[ $latest_version =~ ^v([0-9]+)\.([0-9]+)\. ]]; then
major="${BASH_REMATCH[1]}"
minor="${BASH_REMATCH[2]}"
version="v$major.$((minor + 1)).0"
else
echo "unexpected version: $latest_version"
exit 1
fi
fi

echo "VERSION=$version" >> $GITHUB_ENV
echo "BRANCH=$branch_prefix-$version-dev" >> $GITHUB_ENV
run: node scripts/gh/specs/pick-branch/cli.mjs --spec=otel

- name: Checkout or create branch
run: |
Expand Down
1 change: 1 addition & 0 deletions content/en/docs/contributing/pr-checks.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Pull request checks and tests
linkTitle: PR checks & tests
description: Learn how to make your pull request successfully pass all checks
weight: 40
---
Expand Down
49 changes: 48 additions & 1 deletion content/en/docs/contributing/sig-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ PRs with changes to translations should aim for two approvals: one by a docs
approver and one by a translation approver. Similar practices apply as suggested
for the co-owned PRs.

### Merging PRs
## Merging PRs

The following workflow can be applied by maintainers to merge PRs:

Expand All @@ -233,3 +233,50 @@ The following workflow can be applied by maintainers to merge PRs:
```shell
export PR=<ID OF THE PR>; gh pr checks ${PR} --watch && gh pr merge ${PR} --squash
```

## Specification PRs and integration branches {#spec-integration-branches}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@carlosalberto and @open-telemetry/specs-approvers, this is a writeup of what to do before publishing a release. Is this clear & helpful? See the section for "Spec / semconv SIG maintainers". Feedback welcome. For a preview, see https://deploy-preview-9682--opentelemetry.netlify.app/docs/contributing/sig-practices/#spec-integration-branches

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merging now, but feedback is still welcome.


The website continuously integrates unreleased changes from the
[opentelemetry-specification][] and [semantic-conventions][] repositories. Two
scheduled workflows ([details][ci-section]) run daily and keep a draft
"integration" PR current with the next dev version:

- Branch pattern: `otelbot/spec-integration-vX.Y.Z-dev` and
`otelbot/semconv-integration-vX.Y.Z-dev`.
- List of live branches: [spec][spec-branches] · [semconv][semconv-branches].

[opentelemetry-specification]:
https://github.com/open-telemetry/opentelemetry-specification
[semantic-conventions]: https://github.com/open-telemetry/semantic-conventions
[ci-section]: /site/build/ci-workflows/#spec-integration-branches
[spec-branches]:
https://github.com/open-telemetry/opentelemetry.io/branches/all?query=spec-integration
[semconv-branches]:
https://github.com/open-telemetry/opentelemetry.io/branches/all?query=semconv-integration

### Spec / semconv SIG maintainers

Just before cutting a release:

1. Find the latest integration branch for your spec at the links given in the
previous section (e.g. `otelbot/spec-integration-v1.56.0-dev`).
2. Open the associated PR (linked from the branch page).
3. Trigger a fresh run of the corresponding workflow to pick up your latest
changes:
- [update-spec-integration-branch.yml][]
- [update-semconv-integration-branch.yml][]
4. If the PR checks are green, the spec is safe to release. If not, ping
`@open-telemetry/docs-maintainers` so we can address breakage before the
release goes out.

### Comms SIG maintainers

Throughout the month, regularly check the integration PRs and commit incremental
fixes to keep their CI checks green. Catching breakage early — while the
upstream change set is still small — is much easier than firefighting on release
day.

[update-spec-integration-branch.yml]:
https://github.com/open-telemetry/opentelemetry.io/actions/workflows/update-spec-integration-branch.yml
[update-semconv-integration-branch.yml]:
https://github.com/open-telemetry/opentelemetry.io/actions/workflows/update-semconv-integration-branch.yml
53 changes: 53 additions & 0 deletions content/en/site/build/ci-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,59 @@ nothing needed to be committed.
[pr-actions]:
https://github.com/open-telemetry/opentelemetry.io/blob/main/.github/workflows/pr-actions.yml

## Spec integration branches {#spec-integration-branches}

Two scheduled workflows track unreleased changes from upstream spec repositories
and keep a draft PR ("integration branch") current with the next development
version:

| Workflow file | Upstream repository | Branch slug |
| ----------------------------------------- | ----------------------------- | ----------- |
| [update-spec-integration-branch.yml][] | `opentelemetry-specification` | `spec` |
| [update-semconv-integration-branch.yml][] | `semantic-conventions` | `semconv` |

[update-spec-integration-branch.yml]:
https://github.com/open-telemetry/opentelemetry.io/blob/main/.github/workflows/update-spec-integration-branch.yml
[update-semconv-integration-branch.yml]:
https://github.com/open-telemetry/opentelemetry.io/blob/main/.github/workflows/update-semconv-integration-branch.yml

Both workflows delegate the "pick the next version + branch" step to a shared
Node helper, [scripts/gh/specs/pick-branch/cli.mjs][]. The helper:

- Reuses an existing `otelbot/<slug>-integration-vX.Y.Z-dev` branch when one
exists and the version has not yet been released; otherwise bumps the latest
release tag's minor version.
- Writes `VERSION` and `BRANCH` to `$GITHUB_ENV` for downstream steps.
- Opens a tracking issue (label `<slug>-integration-warning`, deduplicated) when
it detects problems such as multiple stale integration branches.

[scripts/gh/specs/pick-branch/cli.mjs]:
https://github.com/open-telemetry/opentelemetry.io/tree/main/scripts/gh/specs/pick-branch

### Run modes

The helper auto-selects between dry-run and write mode and prints a `[mode]`
banner explaining its choice:

| Context | Default behavior | Override |
| --------------------- | ---------------- | ------------------- |
| GitHub Actions | write | pass `--dry-run` |
| Local (anywhere else) | dry-run | pass `--no-dry-run` |

Locally, dry-run still runs all read-only `git`/`gh` commands (so the issue
deduplication check executes), but skips writes. With `--no-dry-run` the helper
uses your local `gh` credentials; if `GITHUB_ENV` is unset, `VERSION`/`BRANCH`
are printed to stdout only. Try it:

```sh
node scripts/gh/specs/pick-branch/cli.mjs --spec=otel
node scripts/gh/specs/pick-branch/cli.mjs --spec=semconv --no-dry-run
node scripts/gh/specs/pick-branch/cli.mjs --help
```

Pure logic and CLI argument parsing live in `index.mjs` and are covered by
`*.test.mjs` files in the same folder (`npm run test:local-tools` to run them).

## Other workflows

The repository includes several other workflows:
Expand Down
82 changes: 82 additions & 0 deletions scripts/gh/specs/pick-branch/cli-args.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { describe, test } from 'node:test';
import assert from 'node:assert/strict';

import { parseCliArgs, cliUsage } from './index.mjs';

describe('pick-branch: CLI args', () => {
test('defaults: spec=otel, dry-run on when not in Actions', () => {
const result = parseCliArgs([], {});
assert.equal(result.spec, 'otel');
assert.equal(result.dryRun, true);
assert.equal(result.help, false);
assert.match(result.dryRunReason, /GITHUB_ACTIONS/);
});

test('default dry-run is OFF when GITHUB_ACTIONS=true', () => {
const result = parseCliArgs([], { GITHUB_ACTIONS: 'true' });
assert.equal(result.dryRun, false);
assert.equal(result.dryRunReason, 'GITHUB_ACTIONS=true');
});

test('default dry-run is ON when GITHUB_ACTIONS is set to anything else', () => {
assert.equal(parseCliArgs([], { GITHUB_ACTIONS: 'false' }).dryRun, true);
assert.equal(parseCliArgs([], { GITHUB_ACTIONS: '' }).dryRun, true);
});

test('--dry-run forces dry-run even under Actions', () => {
const result = parseCliArgs(['--dry-run'], { GITHUB_ACTIONS: 'true' });
assert.equal(result.dryRun, true);
assert.equal(result.dryRunReason, '--dry-run flag');
});

test('--no-dry-run forces writes even outside Actions', () => {
const result = parseCliArgs(['--no-dry-run'], {});
assert.equal(result.dryRun, false);
assert.equal(result.dryRunReason, '--no-dry-run flag');
});

test('later --dry-run / --no-dry-run wins', () => {
assert.equal(parseCliArgs(['--no-dry-run', '--dry-run'], {}).dryRun, true);
assert.equal(parseCliArgs(['--dry-run', '--no-dry-run'], {}).dryRun, false);
});

test('--spec=<id> selects a known spec', () => {
assert.equal(parseCliArgs(['--spec=semconv'], {}).spec, 'semconv');
});

test('--spec <id> (separate token) selects a known spec', () => {
assert.equal(parseCliArgs(['--spec', 'semconv'], {}).spec, 'semconv');
});

test('-s <id> short form selects a known spec', () => {
assert.equal(parseCliArgs(['-s', 'semconv'], {}).spec, 'semconv');
});

test('--help sets help flag without throwing', () => {
const result = parseCliArgs(['--help'], {});
assert.equal(result.help, true);
});

test('-h short form sets help flag', () => {
assert.equal(parseCliArgs(['-h'], {}).help, true);
});

test('unknown --spec value throws', () => {
assert.throws(() => parseCliArgs(['--spec=bogus'], {}), /Unknown --spec/);
});

test('unknown flag throws', () => {
assert.throws(() => parseCliArgs(['--bogus'], {}), /Unknown argument/);
});

test('--spec without value throws', () => {
assert.throws(() => parseCliArgs(['--spec'], {}), /Missing value/);
});

test('cliUsage mentions both specs and the dry-run flags', () => {
const text = cliUsage();
assert.match(text, /otel\|semconv/);
assert.match(text, /--dry-run/);
assert.match(text, /--no-dry-run/);
});
});
Loading
Loading