ci: add OPC Classic integration testing - #25

Merged
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration
Aug 6, 2026
Merged

ci: add OPC Classic integration testing#25
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration

Conversation

@Ronbb

@RonbbRonbb commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an ignored Windows integration test for a real OPC DA server
  • install, verify, register, and clean up OPC Labs Kit Server in GitHub Actions
  • validate server status, group and item handles, quality, timestamp, and synchronous read/write
  • pin Rust 1.97.1 and add formatting, workspace tests, Clippy, and generated-binding checks
  • upload OPC server and test diagnostics on every integration run

Verification

  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • real OPC Kit Server round-trip on windows-latest: Simulation.Register_I4 = I32(12345), quality 0x00c0, non-zero timestamp
  • server unregister, process cleanup, Core Components uninstall, and temporary-file cleanup

GitHub Actions run: https://github.com/Ronbb/rust_opc/actions/runs/31068173343

Summary by CodeRabbit

  • Tests

    • Added Windows integration coverage for OPC DA connections, item reads, writes, permissions, and server state.
    • Expanded automated validation across formatting, workspace tests, linting, generated bindings, and integration scenarios.
    • Added diagnostics, cleanup, and log collection to improve troubleshooting for integration test failures.
  • Documentation

    • Documented how to configure and run the OPC Classic COM integration test locally.
    • Clarified the automated setup and cleanup performed by the integration workflow.
  • Release

    • Added automated publishing for version-tagged releases.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Ronbb, you've reached your PR review limit, so we couldn't start this review.

Next review available in:54 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 162dd4f3-595b-45ee-9c63-4f25310d49f9

📥 Commits

Reviewing files that changed from the base of the PR and between 27ee42a and 99a0b58.

📒 Files selected for processing (2)
  • .github/workflows/rust.yml
  • opc_da_bindings/tests/opc_kit.rs

Walkthrough

The PR adds pinned Windows Rust validation, an ignored OPC DA integration test, OPC Kit Server setup and cleanup automation, and a tag-triggered workflow that publishes selected crates.

Changes

OPC integration CI

Layer / File(s)Summary
Pinned Windows validation
.github/workflows/rust.yml, .github/workflows/rust-clippy.yml
The workflows pin Rust 1.97.1 and the Windows MSVC target. They run formatting, workspace tests, Clippy, binding regeneration, and generated-file validation.
OPC server setup and registration
.github/workflows/rust.yml
The integration job downloads and verifies the OPC Kit Server, installs OPC Classic Core Components, and validates registration in both registry views.
OPC DA round-trip test
opc_da_bindings/tests/opc_kit.rs, README.md, .github/workflows/rust.yml
The ignored Windows test validates the server, group, and items. It performs an I32 write/read round trip. The README documents local execution.
Failure diagnostics and cleanup
.github/workflows/rust.yml, opc_da_bindings/tests/opc_kit.rs
The job captures diagnostics and logs, unregisters the server, removes installed components, verifies cleanup, uploads logs, and safely deletes temporary files.

Release automation

Layer / File(s)Summary
Tagged crate publishing
.github/workflows/release.yml
A version-tag workflow authenticates with crates.io through OIDC and publishes selected crates with locked Cargo metadata in dependency order.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
participant GitHub Actions
participant OPC Kit Server
participant Windows Registry
participant opc_kit integration test
GitHub Actions->>OPC Kit Server: download and verify executable
GitHub Actions->>Windows Registry: register and validate ProgID
GitHub Actions->>opc_kit integration test: run ignored OPC DA test
opc_kit integration test->>OPC Kit Server: connect, read, write, and read back
GitHub Actions->>OPC Kit Server: terminate and unregister server
GitHub Actions->>Windows Registry: verify registration removal
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding OPC Classic integration testing in CI.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/opc-classic-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rust.yml:
- Around line 116-123: Update the post-registration validation around the 32-bit
and 64-bit registry queries so setup fails unless both $reg32Exit and $reg64Exit
indicate successful registration. Keep the existing registry diagnostics log and
failure message, but change the condition from accepting either view to
requiring both views before tests run.
In `@opc_da_bindings/tests/opc_kit.rs`:
- Around line 127-163: Update the write round-trip test around its write
operation to preserve before.value and restore it through cleanup that runs
after every write attempt, including failed writes or assertions. Reuse the
existing group.write API and write_item.server_handle, ensuring the original
value is restored before returning while preserving the current validation
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67695c21-097a-4d3a-bec7-27e633fbff6c

📥 Commits

Reviewing files that changed from the base of the PR and between b077b29 and 0c3d044.

📒 Files selected for processing (4)
  • .github/workflows/rust-clippy.yml
  • .github/workflows/rust.yml
  • README.md
  • opc_da_bindings/tests/opc_kit.rs

Comment thread.github/workflows/rust.yml Outdated
Comment threadopc_da_bindings/tests/opc_kit.rs

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Line 29: Update every uses entry in the release workflow, including
actions/checkout and dtolnay/rust-toolchain, to reference a trusted immutable
40-character commit SHA instead of a tag, branch, or other mutable reference;
preserve each action’s current version and workflow behavior.
- Around line 56-61: Update the release workflow’s package-publishing loop to
invoke `cargo publish --locked --package` separately for each package in
dependency order, rather than accumulating all packages in `$arguments`. Before
each upload, query crates.io and skip only when that exact package version
already exists; otherwise publish it individually while preserving retry
behavior for partial failures.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa5f0342-0e94-467d-8cf9-16b7e9763093

📥 Commits

Reviewing files that changed from the base of the PR and between 0c3d044 and 27ee42a.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

runs-on: windows-latest
steps:
- name: Check out release
uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"## workflow excerpt"
sed -n '1,90p' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## uses references"
rg -n 'uses:\s*[^#]+' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## id-token permission"
rg -n 'id-token|permissions:' .github/workflows/release.yml 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 2068


Security Misconfiguration (CWE-829): Inclusion of Functionality from Untrusted Control Sphere

Reachability: External

Pin every action to a full commit SHA.

Lines 29, 32, 37, and 41 use mutable references, including dtolnay/rust-toolchain@master. If a referenced action is retargeted or compromised before the tag trigger runs, its code executes with id-token: write and can mint the workflow’s OIDC token for crates.io Trusted Publishing. Replace each uses: value with a 40-character commit SHA.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 28-29: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 29, Update every uses entry in the
release workflow, including actions/checkout and dtolnay/rust-toolchain, to
reference a trusted immutable 40-character commit SHA instead of a tag, branch,
or other mutable reference; preserve each action’s current version and workflow
behavior.

Comment on lines +56 to +61
$arguments = @("publish", "--locked")
foreach ($package in $packages) {
$arguments += @("--package", $package)
}

& cargo @arguments

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
nl -ba .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== packages/manifest names =="
python3 - <<'PY'import os, refor root, dirs, files in os.walk('.'): dirs[:] = [d for d in dirs if d not in {'.git','target','node_modules'}] if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() m=re.search(r'^\[package\].*?^name\s*=\s*"(.*?)"', data, re.M|re.S) if m: print(path, m.group(1))PYecho"== references to opc_classic/opc_comn in repo =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 215


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== package manifests and dependency-like fields =="
python3 - <<'PY'import os, refor root, dirs, files in sorted(os.walk('.')): dirs[:] = sorted([d for d in dirs if d not in {'.git','target','node_modules'}]) if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() name=None lines=[] for line in data.splitlines(): if re.match(r'^name\s*=\s*"([^"]+)"', line): name=re.match(r'^name\s*=\s*"([^"]+)"', line).group(1) stripped=line.strip() if any(key in stripped for key in ['dependencies','dev-dependencies','build-dependencies','[workspace]', 'members', 'workspace.package.version', 'build']): lines.append(line) if name: print("---", path, name, "---") for line in lines: print(line)PYecho"== references to opc_classic/opc_comn in relevant files =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 7119


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== package toml files =="forfin opc_classic_types/Cargo.toml opc_classic_utils/Cargo.toml opc_comn_bindings/Cargo.toml opc_classic_abi/Cargo.toml;doif [ -f"$f" ];thenecho"--- $f ---"
awk '{printf "%6d\t%s\n", NR, $0}'"$f"fidoneecho"== workspace package versions =="
awk '/^\[workspace\.package\]/{flag=1} flag; /^\[workspace\]/{if (NR>1) flag=0}' Cargo.toml | awk '{printf "%6d\t%s\n", NR, $0}'echo"== lockfile relevant packages =="
python3 - <<'PY'from pathlib import Pathimport retext = Path("Cargo.lock").read_text()for pkg in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): m = re.search(r'\n\s*\[\[package\]\]\n(.*?)\nname = "' + re.escape(pkg) + '"\n', text, re.S) print(f"--- {pkg} ---") print(m.group(0)[:500] if m else "not found")PYecho"== Cargo.lock dependency edges for publish packages =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): sections = re.split(r'(?=\[\[package\]\])', lock) pkg = next((s for s in sections if f'name = "{name}"' in s), None) print(f"--- deps for {name} ---") for line in (pkg or "").splitlines(): if "dependencies" in line or ("name =" in line and pkg): pass # concise dependency block m=re.search(r'name = "' + re.escape(name) + '".*?(?=^name = |\Z)', pkg, re.S|re.M) if m: block=m.group(0) for line in block.splitlines(): if line.startswith('dependencies =') or line.startswith('"') or '=' in line: print(line)PYecho"== deterministic publish command shape =="
python3 - <<'PY'import reworkflow = open(".github/workflows/release.yml").read()fn = re.search(r'foreach \(\$package in \$packages\) \{[^}]+\}', workflow, re.S)print(fn.group(0))items = re.findall(r'"([^"]+)"', fn.group(0))print("items:", items)print("final command:", " ".join(["cargo", "publish", "--locked"] + [arg for item in items for arg in ["--package", item]]))PY

Repository: Ronbb/rust_opc

Length of output: 5702


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package section exact =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): print(f"--- {name} lock section ---") p = re.search(r'(?ms)(^\[\[package\]\].*?(?=^\[\[package\]\]|\Z))', lock) for m in re.finditer(r'(?ms)^\[\[package\]\]\n((?:.*?\n)*?)^name = "'+ re.escape(name) +'"', lock): # no, wrong: find containing name line under package passPY# simpler exact:
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()sections = re.split(r'(?=^\[\[package\]\])', lock, flags=re.M)for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): section = next((s for s in sections if re.search(r'^name = "'+re.escape(name)+'"$', s, re.M)), None) print(f"--- {name} sections={section is not None} ---") print(section if section else "NO SECTION")PYecho"== workflow parse exact item order =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()# find package list block inside publish stepstart = text.find('[$packages = @(')end = text.find(')', start)# fallback broadm = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print("m:", bool(m))print("package block:")print(m.group(0))print("quoted package values:")print(sorted(re.findall(r'"([^"]+)"', m.group(0)), key=lambda s: None)) # bad?print(sorted(re.findall(r'"([^"]+)"', m.group(0))))# print whole publish section line numbersfor i,line in enumerate(text.splitlines(), 1): if 'Publish crates' in line or 'cargo `@arguments`' in line: print("line", i, line)PY

Repository: Ronbb/rust_opc

Length of output: 144


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package summary for publish packages =="fornamein opc_classic_types opc_classic_utils opc_comn_bindings;doecho"--- $name version and deps ---"
grep -nE '^(name|version|dependencies|source|dependencies|optional) =|name = "'"$name"'"$|dependencies = \[' Cargo.lock | head -80
done| sed -n '1,120p'echo"== package list in workflow =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()m = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print(m.group(0))print([s for s in m.group(0).split(',') if '"' in s])PYecho"== deterministic publish command shape =="
python3 - <<'PY'packages = ["opc_classic_types", "opc_classic_utils", "opc_comn_bindings"]command = ["cargo", "publish", "--locked"] + [x for p in packages for x in ["--package", p]]print(" ".join(command))PY

Repository: Ronbb/rust_opc

Length of output: 4304


Publish crates one at a time before the retryable upload.

This step sends cargo publish --locked --package opc_classic_types --package opc_classic_utils --package opc_comn_bindings as one command, so it can publish the earlier crates before a later crate fails. Crate releases cannot be overwritten, and index availability can lag after upload. Run cargo publish --locked --package for each package in dependency order. Before each upload, skip the exact package and version only if it already exists on crates.io. Test the partial-failure retry flow against crates.io staging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 56 - 61, Update the release
workflow’s package-publishing loop to invoke `cargo publish --locked --package`
separately for each package in dependency order, rather than accumulating all
packages in `$arguments`. Before each upload, query crates.io and skip only when
that exact package version already exists; otherwise publish it individually
while preserving retry behavior for partial failures.

@Ronbb
Ronbb merged commit da44a99 into masterAug 6, 2026
5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Ronbb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

ci: add OPC Classic integration testing - #25

Merged
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration
Aug 6, 2026
Merged

ci: add OPC Classic integration testing#25
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration

Conversation

@Ronbb

@RonbbRonbb commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an ignored Windows integration test for a real OPC DA server
  • install, verify, register, and clean up OPC Labs Kit Server in GitHub Actions
  • validate server status, group and item handles, quality, timestamp, and synchronous read/write
  • pin Rust 1.97.1 and add formatting, workspace tests, Clippy, and generated-binding checks
  • upload OPC server and test diagnostics on every integration run

Verification

  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • real OPC Kit Server round-trip on windows-latest: Simulation.Register_I4 = I32(12345), quality 0x00c0, non-zero timestamp
  • server unregister, process cleanup, Core Components uninstall, and temporary-file cleanup

GitHub Actions run: https://github.com/Ronbb/rust_opc/actions/runs/31068173343

Summary by CodeRabbit

  • Tests

    • Added Windows integration coverage for OPC DA connections, item reads, writes, permissions, and server state.
    • Expanded automated validation across formatting, workspace tests, linting, generated bindings, and integration scenarios.
    • Added diagnostics, cleanup, and log collection to improve troubleshooting for integration test failures.
  • Documentation

    • Documented how to configure and run the OPC Classic COM integration test locally.
    • Clarified the automated setup and cleanup performed by the integration workflow.
  • Release

    • Added automated publishing for version-tagged releases.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Ronbb, you've reached your PR review limit, so we couldn't start this review.

Next review available in:54 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 162dd4f3-595b-45ee-9c63-4f25310d49f9

📥 Commits

Reviewing files that changed from the base of the PR and between 27ee42a and 99a0b58.

📒 Files selected for processing (2)
  • .github/workflows/rust.yml
  • opc_da_bindings/tests/opc_kit.rs

Walkthrough

The PR adds pinned Windows Rust validation, an ignored OPC DA integration test, OPC Kit Server setup and cleanup automation, and a tag-triggered workflow that publishes selected crates.

Changes

OPC integration CI

Layer / File(s)Summary
Pinned Windows validation
.github/workflows/rust.yml, .github/workflows/rust-clippy.yml
The workflows pin Rust 1.97.1 and the Windows MSVC target. They run formatting, workspace tests, Clippy, binding regeneration, and generated-file validation.
OPC server setup and registration
.github/workflows/rust.yml
The integration job downloads and verifies the OPC Kit Server, installs OPC Classic Core Components, and validates registration in both registry views.
OPC DA round-trip test
opc_da_bindings/tests/opc_kit.rs, README.md, .github/workflows/rust.yml
The ignored Windows test validates the server, group, and items. It performs an I32 write/read round trip. The README documents local execution.
Failure diagnostics and cleanup
.github/workflows/rust.yml, opc_da_bindings/tests/opc_kit.rs
The job captures diagnostics and logs, unregisters the server, removes installed components, verifies cleanup, uploads logs, and safely deletes temporary files.

Release automation

Layer / File(s)Summary
Tagged crate publishing
.github/workflows/release.yml
A version-tag workflow authenticates with crates.io through OIDC and publishes selected crates with locked Cargo metadata in dependency order.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
participant GitHub Actions
participant OPC Kit Server
participant Windows Registry
participant opc_kit integration test
GitHub Actions->>OPC Kit Server: download and verify executable
GitHub Actions->>Windows Registry: register and validate ProgID
GitHub Actions->>opc_kit integration test: run ignored OPC DA test
opc_kit integration test->>OPC Kit Server: connect, read, write, and read back
GitHub Actions->>OPC Kit Server: terminate and unregister server
GitHub Actions->>Windows Registry: verify registration removal
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding OPC Classic integration testing in CI.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/opc-classic-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rust.yml:
- Around line 116-123: Update the post-registration validation around the 32-bit
and 64-bit registry queries so setup fails unless both $reg32Exit and $reg64Exit
indicate successful registration. Keep the existing registry diagnostics log and
failure message, but change the condition from accepting either view to
requiring both views before tests run.
In `@opc_da_bindings/tests/opc_kit.rs`:
- Around line 127-163: Update the write round-trip test around its write
operation to preserve before.value and restore it through cleanup that runs
after every write attempt, including failed writes or assertions. Reuse the
existing group.write API and write_item.server_handle, ensuring the original
value is restored before returning while preserving the current validation
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67695c21-097a-4d3a-bec7-27e633fbff6c

📥 Commits

Reviewing files that changed from the base of the PR and between b077b29 and 0c3d044.

📒 Files selected for processing (4)
  • .github/workflows/rust-clippy.yml
  • .github/workflows/rust.yml
  • README.md
  • opc_da_bindings/tests/opc_kit.rs

Comment thread.github/workflows/rust.yml Outdated
Comment threadopc_da_bindings/tests/opc_kit.rs

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Line 29: Update every uses entry in the release workflow, including
actions/checkout and dtolnay/rust-toolchain, to reference a trusted immutable
40-character commit SHA instead of a tag, branch, or other mutable reference;
preserve each action’s current version and workflow behavior.
- Around line 56-61: Update the release workflow’s package-publishing loop to
invoke `cargo publish --locked --package` separately for each package in
dependency order, rather than accumulating all packages in `$arguments`. Before
each upload, query crates.io and skip only when that exact package version
already exists; otherwise publish it individually while preserving retry
behavior for partial failures.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa5f0342-0e94-467d-8cf9-16b7e9763093

📥 Commits

Reviewing files that changed from the base of the PR and between 0c3d044 and 27ee42a.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

runs-on: windows-latest
steps:
- name: Check out release
uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"## workflow excerpt"
sed -n '1,90p' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## uses references"
rg -n 'uses:\s*[^#]+' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## id-token permission"
rg -n 'id-token|permissions:' .github/workflows/release.yml 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 2068


Security Misconfiguration (CWE-829): Inclusion of Functionality from Untrusted Control Sphere

Reachability: External

Pin every action to a full commit SHA.

Lines 29, 32, 37, and 41 use mutable references, including dtolnay/rust-toolchain@master. If a referenced action is retargeted or compromised before the tag trigger runs, its code executes with id-token: write and can mint the workflow’s OIDC token for crates.io Trusted Publishing. Replace each uses: value with a 40-character commit SHA.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 28-29: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 29, Update every uses entry in the
release workflow, including actions/checkout and dtolnay/rust-toolchain, to
reference a trusted immutable 40-character commit SHA instead of a tag, branch,
or other mutable reference; preserve each action’s current version and workflow
behavior.

Comment on lines +56 to +61
$arguments = @("publish", "--locked")
foreach ($package in $packages) {
$arguments += @("--package", $package)
}

& cargo @arguments

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
nl -ba .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== packages/manifest names =="
python3 - <<'PY'import os, refor root, dirs, files in os.walk('.'): dirs[:] = [d for d in dirs if d not in {'.git','target','node_modules'}] if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() m=re.search(r'^\[package\].*?^name\s*=\s*"(.*?)"', data, re.M|re.S) if m: print(path, m.group(1))PYecho"== references to opc_classic/opc_comn in repo =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 215


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== package manifests and dependency-like fields =="
python3 - <<'PY'import os, refor root, dirs, files in sorted(os.walk('.')): dirs[:] = sorted([d for d in dirs if d not in {'.git','target','node_modules'}]) if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() name=None lines=[] for line in data.splitlines(): if re.match(r'^name\s*=\s*"([^"]+)"', line): name=re.match(r'^name\s*=\s*"([^"]+)"', line).group(1) stripped=line.strip() if any(key in stripped for key in ['dependencies','dev-dependencies','build-dependencies','[workspace]', 'members', 'workspace.package.version', 'build']): lines.append(line) if name: print("---", path, name, "---") for line in lines: print(line)PYecho"== references to opc_classic/opc_comn in relevant files =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 7119


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== package toml files =="forfin opc_classic_types/Cargo.toml opc_classic_utils/Cargo.toml opc_comn_bindings/Cargo.toml opc_classic_abi/Cargo.toml;doif [ -f"$f" ];thenecho"--- $f ---"
awk '{printf "%6d\t%s\n", NR, $0}'"$f"fidoneecho"== workspace package versions =="
awk '/^\[workspace\.package\]/{flag=1} flag; /^\[workspace\]/{if (NR>1) flag=0}' Cargo.toml | awk '{printf "%6d\t%s\n", NR, $0}'echo"== lockfile relevant packages =="
python3 - <<'PY'from pathlib import Pathimport retext = Path("Cargo.lock").read_text()for pkg in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): m = re.search(r'\n\s*\[\[package\]\]\n(.*?)\nname = "' + re.escape(pkg) + '"\n', text, re.S) print(f"--- {pkg} ---") print(m.group(0)[:500] if m else "not found")PYecho"== Cargo.lock dependency edges for publish packages =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): sections = re.split(r'(?=\[\[package\]\])', lock) pkg = next((s for s in sections if f'name = "{name}"' in s), None) print(f"--- deps for {name} ---") for line in (pkg or "").splitlines(): if "dependencies" in line or ("name =" in line and pkg): pass # concise dependency block m=re.search(r'name = "' + re.escape(name) + '".*?(?=^name = |\Z)', pkg, re.S|re.M) if m: block=m.group(0) for line in block.splitlines(): if line.startswith('dependencies =') or line.startswith('"') or '=' in line: print(line)PYecho"== deterministic publish command shape =="
python3 - <<'PY'import reworkflow = open(".github/workflows/release.yml").read()fn = re.search(r'foreach \(\$package in \$packages\) \{[^}]+\}', workflow, re.S)print(fn.group(0))items = re.findall(r'"([^"]+)"', fn.group(0))print("items:", items)print("final command:", " ".join(["cargo", "publish", "--locked"] + [arg for item in items for arg in ["--package", item]]))PY

Repository: Ronbb/rust_opc

Length of output: 5702


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package section exact =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): print(f"--- {name} lock section ---") p = re.search(r'(?ms)(^\[\[package\]\].*?(?=^\[\[package\]\]|\Z))', lock) for m in re.finditer(r'(?ms)^\[\[package\]\]\n((?:.*?\n)*?)^name = "'+ re.escape(name) +'"', lock): # no, wrong: find containing name line under package passPY# simpler exact:
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()sections = re.split(r'(?=^\[\[package\]\])', lock, flags=re.M)for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): section = next((s for s in sections if re.search(r'^name = "'+re.escape(name)+'"$', s, re.M)), None) print(f"--- {name} sections={section is not None} ---") print(section if section else "NO SECTION")PYecho"== workflow parse exact item order =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()# find package list block inside publish stepstart = text.find('[$packages = @(')end = text.find(')', start)# fallback broadm = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print("m:", bool(m))print("package block:")print(m.group(0))print("quoted package values:")print(sorted(re.findall(r'"([^"]+)"', m.group(0)), key=lambda s: None)) # bad?print(sorted(re.findall(r'"([^"]+)"', m.group(0))))# print whole publish section line numbersfor i,line in enumerate(text.splitlines(), 1): if 'Publish crates' in line or 'cargo `@arguments`' in line: print("line", i, line)PY

Repository: Ronbb/rust_opc

Length of output: 144


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package summary for publish packages =="fornamein opc_classic_types opc_classic_utils opc_comn_bindings;doecho"--- $name version and deps ---"
grep -nE '^(name|version|dependencies|source|dependencies|optional) =|name = "'"$name"'"$|dependencies = \[' Cargo.lock | head -80
done| sed -n '1,120p'echo"== package list in workflow =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()m = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print(m.group(0))print([s for s in m.group(0).split(',') if '"' in s])PYecho"== deterministic publish command shape =="
python3 - <<'PY'packages = ["opc_classic_types", "opc_classic_utils", "opc_comn_bindings"]command = ["cargo", "publish", "--locked"] + [x for p in packages for x in ["--package", p]]print(" ".join(command))PY

Repository: Ronbb/rust_opc

Length of output: 4304


Publish crates one at a time before the retryable upload.

This step sends cargo publish --locked --package opc_classic_types --package opc_classic_utils --package opc_comn_bindings as one command, so it can publish the earlier crates before a later crate fails. Crate releases cannot be overwritten, and index availability can lag after upload. Run cargo publish --locked --package for each package in dependency order. Before each upload, skip the exact package and version only if it already exists on crates.io. Test the partial-failure retry flow against crates.io staging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 56 - 61, Update the release
workflow’s package-publishing loop to invoke `cargo publish --locked --package`
separately for each package in dependency order, rather than accumulating all
packages in `$arguments`. Before each upload, query crates.io and skip only when
that exact package version already exists; otherwise publish it individually
while preserving retry behavior for partial failures.

@Ronbb
Ronbb merged commit da44a99 into masterAug 6, 2026
5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Ronbb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: add OPC Classic integration testing - #25

Merged
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration
Aug 6, 2026
Merged

ci: add OPC Classic integration testing#25
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration

Conversation

@Ronbb

@RonbbRonbb commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an ignored Windows integration test for a real OPC DA server
  • install, verify, register, and clean up OPC Labs Kit Server in GitHub Actions
  • validate server status, group and item handles, quality, timestamp, and synchronous read/write
  • pin Rust 1.97.1 and add formatting, workspace tests, Clippy, and generated-binding checks
  • upload OPC server and test diagnostics on every integration run

Verification

  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • real OPC Kit Server round-trip on windows-latest: Simulation.Register_I4 = I32(12345), quality 0x00c0, non-zero timestamp
  • server unregister, process cleanup, Core Components uninstall, and temporary-file cleanup

GitHub Actions run: https://github.com/Ronbb/rust_opc/actions/runs/31068173343

Summary by CodeRabbit

  • Tests

    • Added Windows integration coverage for OPC DA connections, item reads, writes, permissions, and server state.
    • Expanded automated validation across formatting, workspace tests, linting, generated bindings, and integration scenarios.
    • Added diagnostics, cleanup, and log collection to improve troubleshooting for integration test failures.
  • Documentation

    • Documented how to configure and run the OPC Classic COM integration test locally.
    • Clarified the automated setup and cleanup performed by the integration workflow.
  • Release

    • Added automated publishing for version-tagged releases.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Ronbb, you've reached your PR review limit, so we couldn't start this review.

Next review available in:54 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 162dd4f3-595b-45ee-9c63-4f25310d49f9

📥 Commits

Reviewing files that changed from the base of the PR and between 27ee42a and 99a0b58.

📒 Files selected for processing (2)
  • .github/workflows/rust.yml
  • opc_da_bindings/tests/opc_kit.rs

Walkthrough

The PR adds pinned Windows Rust validation, an ignored OPC DA integration test, OPC Kit Server setup and cleanup automation, and a tag-triggered workflow that publishes selected crates.

Changes

OPC integration CI

Layer / File(s)Summary
Pinned Windows validation
.github/workflows/rust.yml, .github/workflows/rust-clippy.yml
The workflows pin Rust 1.97.1 and the Windows MSVC target. They run formatting, workspace tests, Clippy, binding regeneration, and generated-file validation.
OPC server setup and registration
.github/workflows/rust.yml
The integration job downloads and verifies the OPC Kit Server, installs OPC Classic Core Components, and validates registration in both registry views.
OPC DA round-trip test
opc_da_bindings/tests/opc_kit.rs, README.md, .github/workflows/rust.yml
The ignored Windows test validates the server, group, and items. It performs an I32 write/read round trip. The README documents local execution.
Failure diagnostics and cleanup
.github/workflows/rust.yml, opc_da_bindings/tests/opc_kit.rs
The job captures diagnostics and logs, unregisters the server, removes installed components, verifies cleanup, uploads logs, and safely deletes temporary files.

Release automation

Layer / File(s)Summary
Tagged crate publishing
.github/workflows/release.yml
A version-tag workflow authenticates with crates.io through OIDC and publishes selected crates with locked Cargo metadata in dependency order.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
participant GitHub Actions
participant OPC Kit Server
participant Windows Registry
participant opc_kit integration test
GitHub Actions->>OPC Kit Server: download and verify executable
GitHub Actions->>Windows Registry: register and validate ProgID
GitHub Actions->>opc_kit integration test: run ignored OPC DA test
opc_kit integration test->>OPC Kit Server: connect, read, write, and read back
GitHub Actions->>OPC Kit Server: terminate and unregister server
GitHub Actions->>Windows Registry: verify registration removal
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding OPC Classic integration testing in CI.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/opc-classic-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rust.yml:
- Around line 116-123: Update the post-registration validation around the 32-bit
and 64-bit registry queries so setup fails unless both $reg32Exit and $reg64Exit
indicate successful registration. Keep the existing registry diagnostics log and
failure message, but change the condition from accepting either view to
requiring both views before tests run.
In `@opc_da_bindings/tests/opc_kit.rs`:
- Around line 127-163: Update the write round-trip test around its write
operation to preserve before.value and restore it through cleanup that runs
after every write attempt, including failed writes or assertions. Reuse the
existing group.write API and write_item.server_handle, ensuring the original
value is restored before returning while preserving the current validation
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67695c21-097a-4d3a-bec7-27e633fbff6c

📥 Commits

Reviewing files that changed from the base of the PR and between b077b29 and 0c3d044.

📒 Files selected for processing (4)
  • .github/workflows/rust-clippy.yml
  • .github/workflows/rust.yml
  • README.md
  • opc_da_bindings/tests/opc_kit.rs

Comment thread.github/workflows/rust.yml Outdated
Comment threadopc_da_bindings/tests/opc_kit.rs

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Line 29: Update every uses entry in the release workflow, including
actions/checkout and dtolnay/rust-toolchain, to reference a trusted immutable
40-character commit SHA instead of a tag, branch, or other mutable reference;
preserve each action’s current version and workflow behavior.
- Around line 56-61: Update the release workflow’s package-publishing loop to
invoke `cargo publish --locked --package` separately for each package in
dependency order, rather than accumulating all packages in `$arguments`. Before
each upload, query crates.io and skip only when that exact package version
already exists; otherwise publish it individually while preserving retry
behavior for partial failures.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa5f0342-0e94-467d-8cf9-16b7e9763093

📥 Commits

Reviewing files that changed from the base of the PR and between 0c3d044 and 27ee42a.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

runs-on: windows-latest
steps:
- name: Check out release
uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"## workflow excerpt"
sed -n '1,90p' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## uses references"
rg -n 'uses:\s*[^#]+' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## id-token permission"
rg -n 'id-token|permissions:' .github/workflows/release.yml 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 2068


Security Misconfiguration (CWE-829): Inclusion of Functionality from Untrusted Control Sphere

Reachability: External

Pin every action to a full commit SHA.

Lines 29, 32, 37, and 41 use mutable references, including dtolnay/rust-toolchain@master. If a referenced action is retargeted or compromised before the tag trigger runs, its code executes with id-token: write and can mint the workflow’s OIDC token for crates.io Trusted Publishing. Replace each uses: value with a 40-character commit SHA.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 28-29: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 29, Update every uses entry in the
release workflow, including actions/checkout and dtolnay/rust-toolchain, to
reference a trusted immutable 40-character commit SHA instead of a tag, branch,
or other mutable reference; preserve each action’s current version and workflow
behavior.

Comment on lines +56 to +61
$arguments = @("publish", "--locked")
foreach ($package in $packages) {
$arguments += @("--package", $package)
}

& cargo @arguments

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
nl -ba .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== packages/manifest names =="
python3 - <<'PY'import os, refor root, dirs, files in os.walk('.'): dirs[:] = [d for d in dirs if d not in {'.git','target','node_modules'}] if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() m=re.search(r'^\[package\].*?^name\s*=\s*"(.*?)"', data, re.M|re.S) if m: print(path, m.group(1))PYecho"== references to opc_classic/opc_comn in repo =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 215


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== package manifests and dependency-like fields =="
python3 - <<'PY'import os, refor root, dirs, files in sorted(os.walk('.')): dirs[:] = sorted([d for d in dirs if d not in {'.git','target','node_modules'}]) if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() name=None lines=[] for line in data.splitlines(): if re.match(r'^name\s*=\s*"([^"]+)"', line): name=re.match(r'^name\s*=\s*"([^"]+)"', line).group(1) stripped=line.strip() if any(key in stripped for key in ['dependencies','dev-dependencies','build-dependencies','[workspace]', 'members', 'workspace.package.version', 'build']): lines.append(line) if name: print("---", path, name, "---") for line in lines: print(line)PYecho"== references to opc_classic/opc_comn in relevant files =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 7119


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== package toml files =="forfin opc_classic_types/Cargo.toml opc_classic_utils/Cargo.toml opc_comn_bindings/Cargo.toml opc_classic_abi/Cargo.toml;doif [ -f"$f" ];thenecho"--- $f ---"
awk '{printf "%6d\t%s\n", NR, $0}'"$f"fidoneecho"== workspace package versions =="
awk '/^\[workspace\.package\]/{flag=1} flag; /^\[workspace\]/{if (NR>1) flag=0}' Cargo.toml | awk '{printf "%6d\t%s\n", NR, $0}'echo"== lockfile relevant packages =="
python3 - <<'PY'from pathlib import Pathimport retext = Path("Cargo.lock").read_text()for pkg in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): m = re.search(r'\n\s*\[\[package\]\]\n(.*?)\nname = "' + re.escape(pkg) + '"\n', text, re.S) print(f"--- {pkg} ---") print(m.group(0)[:500] if m else "not found")PYecho"== Cargo.lock dependency edges for publish packages =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): sections = re.split(r'(?=\[\[package\]\])', lock) pkg = next((s for s in sections if f'name = "{name}"' in s), None) print(f"--- deps for {name} ---") for line in (pkg or "").splitlines(): if "dependencies" in line or ("name =" in line and pkg): pass # concise dependency block m=re.search(r'name = "' + re.escape(name) + '".*?(?=^name = |\Z)', pkg, re.S|re.M) if m: block=m.group(0) for line in block.splitlines(): if line.startswith('dependencies =') or line.startswith('"') or '=' in line: print(line)PYecho"== deterministic publish command shape =="
python3 - <<'PY'import reworkflow = open(".github/workflows/release.yml").read()fn = re.search(r'foreach \(\$package in \$packages\) \{[^}]+\}', workflow, re.S)print(fn.group(0))items = re.findall(r'"([^"]+)"', fn.group(0))print("items:", items)print("final command:", " ".join(["cargo", "publish", "--locked"] + [arg for item in items for arg in ["--package", item]]))PY

Repository: Ronbb/rust_opc

Length of output: 5702


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package section exact =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): print(f"--- {name} lock section ---") p = re.search(r'(?ms)(^\[\[package\]\].*?(?=^\[\[package\]\]|\Z))', lock) for m in re.finditer(r'(?ms)^\[\[package\]\]\n((?:.*?\n)*?)^name = "'+ re.escape(name) +'"', lock): # no, wrong: find containing name line under package passPY# simpler exact:
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()sections = re.split(r'(?=^\[\[package\]\])', lock, flags=re.M)for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): section = next((s for s in sections if re.search(r'^name = "'+re.escape(name)+'"$', s, re.M)), None) print(f"--- {name} sections={section is not None} ---") print(section if section else "NO SECTION")PYecho"== workflow parse exact item order =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()# find package list block inside publish stepstart = text.find('[$packages = @(')end = text.find(')', start)# fallback broadm = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print("m:", bool(m))print("package block:")print(m.group(0))print("quoted package values:")print(sorted(re.findall(r'"([^"]+)"', m.group(0)), key=lambda s: None)) # bad?print(sorted(re.findall(r'"([^"]+)"', m.group(0))))# print whole publish section line numbersfor i,line in enumerate(text.splitlines(), 1): if 'Publish crates' in line or 'cargo `@arguments`' in line: print("line", i, line)PY

Repository: Ronbb/rust_opc

Length of output: 144


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package summary for publish packages =="fornamein opc_classic_types opc_classic_utils opc_comn_bindings;doecho"--- $name version and deps ---"
grep -nE '^(name|version|dependencies|source|dependencies|optional) =|name = "'"$name"'"$|dependencies = \[' Cargo.lock | head -80
done| sed -n '1,120p'echo"== package list in workflow =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()m = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print(m.group(0))print([s for s in m.group(0).split(',') if '"' in s])PYecho"== deterministic publish command shape =="
python3 - <<'PY'packages = ["opc_classic_types", "opc_classic_utils", "opc_comn_bindings"]command = ["cargo", "publish", "--locked"] + [x for p in packages for x in ["--package", p]]print(" ".join(command))PY

Repository: Ronbb/rust_opc

Length of output: 4304


Publish crates one at a time before the retryable upload.

This step sends cargo publish --locked --package opc_classic_types --package opc_classic_utils --package opc_comn_bindings as one command, so it can publish the earlier crates before a later crate fails. Crate releases cannot be overwritten, and index availability can lag after upload. Run cargo publish --locked --package for each package in dependency order. Before each upload, skip the exact package and version only if it already exists on crates.io. Test the partial-failure retry flow against crates.io staging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 56 - 61, Update the release
workflow’s package-publishing loop to invoke `cargo publish --locked --package`
separately for each package in dependency order, rather than accumulating all
packages in `$arguments`. Before each upload, query crates.io and skip only when
that exact package version already exists; otherwise publish it individually
while preserving retry behavior for partial failures.

@Ronbb
Ronbb merged commit da44a99 into masterAug 6, 2026
5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Ronbb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: add OPC Classic integration testing - #25

Merged
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration
Aug 6, 2026
Merged

ci: add OPC Classic integration testing#25
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration

Conversation

@Ronbb

@RonbbRonbb commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an ignored Windows integration test for a real OPC DA server
  • install, verify, register, and clean up OPC Labs Kit Server in GitHub Actions
  • validate server status, group and item handles, quality, timestamp, and synchronous read/write
  • pin Rust 1.97.1 and add formatting, workspace tests, Clippy, and generated-binding checks
  • upload OPC server and test diagnostics on every integration run

Verification

  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • real OPC Kit Server round-trip on windows-latest: Simulation.Register_I4 = I32(12345), quality 0x00c0, non-zero timestamp
  • server unregister, process cleanup, Core Components uninstall, and temporary-file cleanup

GitHub Actions run: https://github.com/Ronbb/rust_opc/actions/runs/31068173343

Summary by CodeRabbit

  • Tests

    • Added Windows integration coverage for OPC DA connections, item reads, writes, permissions, and server state.
    • Expanded automated validation across formatting, workspace tests, linting, generated bindings, and integration scenarios.
    • Added diagnostics, cleanup, and log collection to improve troubleshooting for integration test failures.
  • Documentation

    • Documented how to configure and run the OPC Classic COM integration test locally.
    • Clarified the automated setup and cleanup performed by the integration workflow.
  • Release

    • Added automated publishing for version-tagged releases.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Ronbb, you've reached your PR review limit, so we couldn't start this review.

Next review available in:54 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 162dd4f3-595b-45ee-9c63-4f25310d49f9

📥 Commits

Reviewing files that changed from the base of the PR and between 27ee42a and 99a0b58.

📒 Files selected for processing (2)
  • .github/workflows/rust.yml
  • opc_da_bindings/tests/opc_kit.rs

Walkthrough

The PR adds pinned Windows Rust validation, an ignored OPC DA integration test, OPC Kit Server setup and cleanup automation, and a tag-triggered workflow that publishes selected crates.

Changes

OPC integration CI

Layer / File(s)Summary
Pinned Windows validation
.github/workflows/rust.yml, .github/workflows/rust-clippy.yml
The workflows pin Rust 1.97.1 and the Windows MSVC target. They run formatting, workspace tests, Clippy, binding regeneration, and generated-file validation.
OPC server setup and registration
.github/workflows/rust.yml
The integration job downloads and verifies the OPC Kit Server, installs OPC Classic Core Components, and validates registration in both registry views.
OPC DA round-trip test
opc_da_bindings/tests/opc_kit.rs, README.md, .github/workflows/rust.yml
The ignored Windows test validates the server, group, and items. It performs an I32 write/read round trip. The README documents local execution.
Failure diagnostics and cleanup
.github/workflows/rust.yml, opc_da_bindings/tests/opc_kit.rs
The job captures diagnostics and logs, unregisters the server, removes installed components, verifies cleanup, uploads logs, and safely deletes temporary files.

Release automation

Layer / File(s)Summary
Tagged crate publishing
.github/workflows/release.yml
A version-tag workflow authenticates with crates.io through OIDC and publishes selected crates with locked Cargo metadata in dependency order.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
participant GitHub Actions
participant OPC Kit Server
participant Windows Registry
participant opc_kit integration test
GitHub Actions->>OPC Kit Server: download and verify executable
GitHub Actions->>Windows Registry: register and validate ProgID
GitHub Actions->>opc_kit integration test: run ignored OPC DA test
opc_kit integration test->>OPC Kit Server: connect, read, write, and read back
GitHub Actions->>OPC Kit Server: terminate and unregister server
GitHub Actions->>Windows Registry: verify registration removal
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding OPC Classic integration testing in CI.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/opc-classic-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rust.yml:
- Around line 116-123: Update the post-registration validation around the 32-bit
and 64-bit registry queries so setup fails unless both $reg32Exit and $reg64Exit
indicate successful registration. Keep the existing registry diagnostics log and
failure message, but change the condition from accepting either view to
requiring both views before tests run.
In `@opc_da_bindings/tests/opc_kit.rs`:
- Around line 127-163: Update the write round-trip test around its write
operation to preserve before.value and restore it through cleanup that runs
after every write attempt, including failed writes or assertions. Reuse the
existing group.write API and write_item.server_handle, ensuring the original
value is restored before returning while preserving the current validation
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67695c21-097a-4d3a-bec7-27e633fbff6c

📥 Commits

Reviewing files that changed from the base of the PR and between b077b29 and 0c3d044.

📒 Files selected for processing (4)
  • .github/workflows/rust-clippy.yml
  • .github/workflows/rust.yml
  • README.md
  • opc_da_bindings/tests/opc_kit.rs

Comment thread.github/workflows/rust.yml Outdated
Comment threadopc_da_bindings/tests/opc_kit.rs

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Line 29: Update every uses entry in the release workflow, including
actions/checkout and dtolnay/rust-toolchain, to reference a trusted immutable
40-character commit SHA instead of a tag, branch, or other mutable reference;
preserve each action’s current version and workflow behavior.
- Around line 56-61: Update the release workflow’s package-publishing loop to
invoke `cargo publish --locked --package` separately for each package in
dependency order, rather than accumulating all packages in `$arguments`. Before
each upload, query crates.io and skip only when that exact package version
already exists; otherwise publish it individually while preserving retry
behavior for partial failures.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa5f0342-0e94-467d-8cf9-16b7e9763093

📥 Commits

Reviewing files that changed from the base of the PR and between 0c3d044 and 27ee42a.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

runs-on: windows-latest
steps:
- name: Check out release
uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"## workflow excerpt"
sed -n '1,90p' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## uses references"
rg -n 'uses:\s*[^#]+' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## id-token permission"
rg -n 'id-token|permissions:' .github/workflows/release.yml 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 2068


Security Misconfiguration (CWE-829): Inclusion of Functionality from Untrusted Control Sphere

Reachability: External

Pin every action to a full commit SHA.

Lines 29, 32, 37, and 41 use mutable references, including dtolnay/rust-toolchain@master. If a referenced action is retargeted or compromised before the tag trigger runs, its code executes with id-token: write and can mint the workflow’s OIDC token for crates.io Trusted Publishing. Replace each uses: value with a 40-character commit SHA.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 28-29: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 29, Update every uses entry in the
release workflow, including actions/checkout and dtolnay/rust-toolchain, to
reference a trusted immutable 40-character commit SHA instead of a tag, branch,
or other mutable reference; preserve each action’s current version and workflow
behavior.

Comment on lines +56 to +61
$arguments = @("publish", "--locked")
foreach ($package in $packages) {
$arguments += @("--package", $package)
}

& cargo @arguments

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
nl -ba .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== packages/manifest names =="
python3 - <<'PY'import os, refor root, dirs, files in os.walk('.'): dirs[:] = [d for d in dirs if d not in {'.git','target','node_modules'}] if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() m=re.search(r'^\[package\].*?^name\s*=\s*"(.*?)"', data, re.M|re.S) if m: print(path, m.group(1))PYecho"== references to opc_classic/opc_comn in repo =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 215


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== package manifests and dependency-like fields =="
python3 - <<'PY'import os, refor root, dirs, files in sorted(os.walk('.')): dirs[:] = sorted([d for d in dirs if d not in {'.git','target','node_modules'}]) if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() name=None lines=[] for line in data.splitlines(): if re.match(r'^name\s*=\s*"([^"]+)"', line): name=re.match(r'^name\s*=\s*"([^"]+)"', line).group(1) stripped=line.strip() if any(key in stripped for key in ['dependencies','dev-dependencies','build-dependencies','[workspace]', 'members', 'workspace.package.version', 'build']): lines.append(line) if name: print("---", path, name, "---") for line in lines: print(line)PYecho"== references to opc_classic/opc_comn in relevant files =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 7119


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== package toml files =="forfin opc_classic_types/Cargo.toml opc_classic_utils/Cargo.toml opc_comn_bindings/Cargo.toml opc_classic_abi/Cargo.toml;doif [ -f"$f" ];thenecho"--- $f ---"
awk '{printf "%6d\t%s\n", NR, $0}'"$f"fidoneecho"== workspace package versions =="
awk '/^\[workspace\.package\]/{flag=1} flag; /^\[workspace\]/{if (NR>1) flag=0}' Cargo.toml | awk '{printf "%6d\t%s\n", NR, $0}'echo"== lockfile relevant packages =="
python3 - <<'PY'from pathlib import Pathimport retext = Path("Cargo.lock").read_text()for pkg in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): m = re.search(r'\n\s*\[\[package\]\]\n(.*?)\nname = "' + re.escape(pkg) + '"\n', text, re.S) print(f"--- {pkg} ---") print(m.group(0)[:500] if m else "not found")PYecho"== Cargo.lock dependency edges for publish packages =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): sections = re.split(r'(?=\[\[package\]\])', lock) pkg = next((s for s in sections if f'name = "{name}"' in s), None) print(f"--- deps for {name} ---") for line in (pkg or "").splitlines(): if "dependencies" in line or ("name =" in line and pkg): pass # concise dependency block m=re.search(r'name = "' + re.escape(name) + '".*?(?=^name = |\Z)', pkg, re.S|re.M) if m: block=m.group(0) for line in block.splitlines(): if line.startswith('dependencies =') or line.startswith('"') or '=' in line: print(line)PYecho"== deterministic publish command shape =="
python3 - <<'PY'import reworkflow = open(".github/workflows/release.yml").read()fn = re.search(r'foreach \(\$package in \$packages\) \{[^}]+\}', workflow, re.S)print(fn.group(0))items = re.findall(r'"([^"]+)"', fn.group(0))print("items:", items)print("final command:", " ".join(["cargo", "publish", "--locked"] + [arg for item in items for arg in ["--package", item]]))PY

Repository: Ronbb/rust_opc

Length of output: 5702


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package section exact =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): print(f"--- {name} lock section ---") p = re.search(r'(?ms)(^\[\[package\]\].*?(?=^\[\[package\]\]|\Z))', lock) for m in re.finditer(r'(?ms)^\[\[package\]\]\n((?:.*?\n)*?)^name = "'+ re.escape(name) +'"', lock): # no, wrong: find containing name line under package passPY# simpler exact:
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()sections = re.split(r'(?=^\[\[package\]\])', lock, flags=re.M)for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): section = next((s for s in sections if re.search(r'^name = "'+re.escape(name)+'"$', s, re.M)), None) print(f"--- {name} sections={section is not None} ---") print(section if section else "NO SECTION")PYecho"== workflow parse exact item order =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()# find package list block inside publish stepstart = text.find('[$packages = @(')end = text.find(')', start)# fallback broadm = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print("m:", bool(m))print("package block:")print(m.group(0))print("quoted package values:")print(sorted(re.findall(r'"([^"]+)"', m.group(0)), key=lambda s: None)) # bad?print(sorted(re.findall(r'"([^"]+)"', m.group(0))))# print whole publish section line numbersfor i,line in enumerate(text.splitlines(), 1): if 'Publish crates' in line or 'cargo `@arguments`' in line: print("line", i, line)PY

Repository: Ronbb/rust_opc

Length of output: 144


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package summary for publish packages =="fornamein opc_classic_types opc_classic_utils opc_comn_bindings;doecho"--- $name version and deps ---"
grep -nE '^(name|version|dependencies|source|dependencies|optional) =|name = "'"$name"'"$|dependencies = \[' Cargo.lock | head -80
done| sed -n '1,120p'echo"== package list in workflow =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()m = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print(m.group(0))print([s for s in m.group(0).split(',') if '"' in s])PYecho"== deterministic publish command shape =="
python3 - <<'PY'packages = ["opc_classic_types", "opc_classic_utils", "opc_comn_bindings"]command = ["cargo", "publish", "--locked"] + [x for p in packages for x in ["--package", p]]print(" ".join(command))PY

Repository: Ronbb/rust_opc

Length of output: 4304


Publish crates one at a time before the retryable upload.

This step sends cargo publish --locked --package opc_classic_types --package opc_classic_utils --package opc_comn_bindings as one command, so it can publish the earlier crates before a later crate fails. Crate releases cannot be overwritten, and index availability can lag after upload. Run cargo publish --locked --package for each package in dependency order. Before each upload, skip the exact package and version only if it already exists on crates.io. Test the partial-failure retry flow against crates.io staging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 56 - 61, Update the release
workflow’s package-publishing loop to invoke `cargo publish --locked --package`
separately for each package in dependency order, rather than accumulating all
packages in `$arguments`. Before each upload, query crates.io and skip only when
that exact package version already exists; otherwise publish it individually
while preserving retry behavior for partial failures.

@Ronbb
Ronbb merged commit da44a99 into masterAug 6, 2026
5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Ronbb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

ci: add OPC Classic integration testing - #25

Merged
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration
Aug 6, 2026
Merged

ci: add OPC Classic integration testing#25
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration

Conversation

@Ronbb

@RonbbRonbb commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an ignored Windows integration test for a real OPC DA server
  • install, verify, register, and clean up OPC Labs Kit Server in GitHub Actions
  • validate server status, group and item handles, quality, timestamp, and synchronous read/write
  • pin Rust 1.97.1 and add formatting, workspace tests, Clippy, and generated-binding checks
  • upload OPC server and test diagnostics on every integration run

Verification

  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • real OPC Kit Server round-trip on windows-latest: Simulation.Register_I4 = I32(12345), quality 0x00c0, non-zero timestamp
  • server unregister, process cleanup, Core Components uninstall, and temporary-file cleanup

GitHub Actions run: https://github.com/Ronbb/rust_opc/actions/runs/31068173343

Summary by CodeRabbit

  • Tests

    • Added Windows integration coverage for OPC DA connections, item reads, writes, permissions, and server state.
    • Expanded automated validation across formatting, workspace tests, linting, generated bindings, and integration scenarios.
    • Added diagnostics, cleanup, and log collection to improve troubleshooting for integration test failures.
  • Documentation

    • Documented how to configure and run the OPC Classic COM integration test locally.
    • Clarified the automated setup and cleanup performed by the integration workflow.
  • Release

    • Added automated publishing for version-tagged releases.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Ronbb, you've reached your PR review limit, so we couldn't start this review.

Next review available in:54 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 162dd4f3-595b-45ee-9c63-4f25310d49f9

📥 Commits

Reviewing files that changed from the base of the PR and between 27ee42a and 99a0b58.

📒 Files selected for processing (2)
  • .github/workflows/rust.yml
  • opc_da_bindings/tests/opc_kit.rs

Walkthrough

The PR adds pinned Windows Rust validation, an ignored OPC DA integration test, OPC Kit Server setup and cleanup automation, and a tag-triggered workflow that publishes selected crates.

Changes

OPC integration CI

Layer / File(s)Summary
Pinned Windows validation
.github/workflows/rust.yml, .github/workflows/rust-clippy.yml
The workflows pin Rust 1.97.1 and the Windows MSVC target. They run formatting, workspace tests, Clippy, binding regeneration, and generated-file validation.
OPC server setup and registration
.github/workflows/rust.yml
The integration job downloads and verifies the OPC Kit Server, installs OPC Classic Core Components, and validates registration in both registry views.
OPC DA round-trip test
opc_da_bindings/tests/opc_kit.rs, README.md, .github/workflows/rust.yml
The ignored Windows test validates the server, group, and items. It performs an I32 write/read round trip. The README documents local execution.
Failure diagnostics and cleanup
.github/workflows/rust.yml, opc_da_bindings/tests/opc_kit.rs
The job captures diagnostics and logs, unregisters the server, removes installed components, verifies cleanup, uploads logs, and safely deletes temporary files.

Release automation

Layer / File(s)Summary
Tagged crate publishing
.github/workflows/release.yml
A version-tag workflow authenticates with crates.io through OIDC and publishes selected crates with locked Cargo metadata in dependency order.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
participant GitHub Actions
participant OPC Kit Server
participant Windows Registry
participant opc_kit integration test
GitHub Actions->>OPC Kit Server: download and verify executable
GitHub Actions->>Windows Registry: register and validate ProgID
GitHub Actions->>opc_kit integration test: run ignored OPC DA test
opc_kit integration test->>OPC Kit Server: connect, read, write, and read back
GitHub Actions->>OPC Kit Server: terminate and unregister server
GitHub Actions->>Windows Registry: verify registration removal
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding OPC Classic integration testing in CI.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/opc-classic-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rust.yml:
- Around line 116-123: Update the post-registration validation around the 32-bit
and 64-bit registry queries so setup fails unless both $reg32Exit and $reg64Exit
indicate successful registration. Keep the existing registry diagnostics log and
failure message, but change the condition from accepting either view to
requiring both views before tests run.
In `@opc_da_bindings/tests/opc_kit.rs`:
- Around line 127-163: Update the write round-trip test around its write
operation to preserve before.value and restore it through cleanup that runs
after every write attempt, including failed writes or assertions. Reuse the
existing group.write API and write_item.server_handle, ensuring the original
value is restored before returning while preserving the current validation
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67695c21-097a-4d3a-bec7-27e633fbff6c

📥 Commits

Reviewing files that changed from the base of the PR and between b077b29 and 0c3d044.

📒 Files selected for processing (4)
  • .github/workflows/rust-clippy.yml
  • .github/workflows/rust.yml
  • README.md
  • opc_da_bindings/tests/opc_kit.rs

Comment thread.github/workflows/rust.yml Outdated
Comment threadopc_da_bindings/tests/opc_kit.rs

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Line 29: Update every uses entry in the release workflow, including
actions/checkout and dtolnay/rust-toolchain, to reference a trusted immutable
40-character commit SHA instead of a tag, branch, or other mutable reference;
preserve each action’s current version and workflow behavior.
- Around line 56-61: Update the release workflow’s package-publishing loop to
invoke `cargo publish --locked --package` separately for each package in
dependency order, rather than accumulating all packages in `$arguments`. Before
each upload, query crates.io and skip only when that exact package version
already exists; otherwise publish it individually while preserving retry
behavior for partial failures.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa5f0342-0e94-467d-8cf9-16b7e9763093

📥 Commits

Reviewing files that changed from the base of the PR and between 0c3d044 and 27ee42a.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

runs-on: windows-latest
steps:
- name: Check out release
uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"## workflow excerpt"
sed -n '1,90p' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## uses references"
rg -n 'uses:\s*[^#]+' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## id-token permission"
rg -n 'id-token|permissions:' .github/workflows/release.yml 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 2068


Security Misconfiguration (CWE-829): Inclusion of Functionality from Untrusted Control Sphere

Reachability: External

Pin every action to a full commit SHA.

Lines 29, 32, 37, and 41 use mutable references, including dtolnay/rust-toolchain@master. If a referenced action is retargeted or compromised before the tag trigger runs, its code executes with id-token: write and can mint the workflow’s OIDC token for crates.io Trusted Publishing. Replace each uses: value with a 40-character commit SHA.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 28-29: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 29, Update every uses entry in the
release workflow, including actions/checkout and dtolnay/rust-toolchain, to
reference a trusted immutable 40-character commit SHA instead of a tag, branch,
or other mutable reference; preserve each action’s current version and workflow
behavior.

Comment on lines +56 to +61
$arguments = @("publish", "--locked")
foreach ($package in $packages) {
$arguments += @("--package", $package)
}

& cargo @arguments

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
nl -ba .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== packages/manifest names =="
python3 - <<'PY'import os, refor root, dirs, files in os.walk('.'): dirs[:] = [d for d in dirs if d not in {'.git','target','node_modules'}] if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() m=re.search(r'^\[package\].*?^name\s*=\s*"(.*?)"', data, re.M|re.S) if m: print(path, m.group(1))PYecho"== references to opc_classic/opc_comn in repo =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 215


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== package manifests and dependency-like fields =="
python3 - <<'PY'import os, refor root, dirs, files in sorted(os.walk('.')): dirs[:] = sorted([d for d in dirs if d not in {'.git','target','node_modules'}]) if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() name=None lines=[] for line in data.splitlines(): if re.match(r'^name\s*=\s*"([^"]+)"', line): name=re.match(r'^name\s*=\s*"([^"]+)"', line).group(1) stripped=line.strip() if any(key in stripped for key in ['dependencies','dev-dependencies','build-dependencies','[workspace]', 'members', 'workspace.package.version', 'build']): lines.append(line) if name: print("---", path, name, "---") for line in lines: print(line)PYecho"== references to opc_classic/opc_comn in relevant files =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 7119


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== package toml files =="forfin opc_classic_types/Cargo.toml opc_classic_utils/Cargo.toml opc_comn_bindings/Cargo.toml opc_classic_abi/Cargo.toml;doif [ -f"$f" ];thenecho"--- $f ---"
awk '{printf "%6d\t%s\n", NR, $0}'"$f"fidoneecho"== workspace package versions =="
awk '/^\[workspace\.package\]/{flag=1} flag; /^\[workspace\]/{if (NR>1) flag=0}' Cargo.toml | awk '{printf "%6d\t%s\n", NR, $0}'echo"== lockfile relevant packages =="
python3 - <<'PY'from pathlib import Pathimport retext = Path("Cargo.lock").read_text()for pkg in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): m = re.search(r'\n\s*\[\[package\]\]\n(.*?)\nname = "' + re.escape(pkg) + '"\n', text, re.S) print(f"--- {pkg} ---") print(m.group(0)[:500] if m else "not found")PYecho"== Cargo.lock dependency edges for publish packages =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): sections = re.split(r'(?=\[\[package\]\])', lock) pkg = next((s for s in sections if f'name = "{name}"' in s), None) print(f"--- deps for {name} ---") for line in (pkg or "").splitlines(): if "dependencies" in line or ("name =" in line and pkg): pass # concise dependency block m=re.search(r'name = "' + re.escape(name) + '".*?(?=^name = |\Z)', pkg, re.S|re.M) if m: block=m.group(0) for line in block.splitlines(): if line.startswith('dependencies =') or line.startswith('"') or '=' in line: print(line)PYecho"== deterministic publish command shape =="
python3 - <<'PY'import reworkflow = open(".github/workflows/release.yml").read()fn = re.search(r'foreach \(\$package in \$packages\) \{[^}]+\}', workflow, re.S)print(fn.group(0))items = re.findall(r'"([^"]+)"', fn.group(0))print("items:", items)print("final command:", " ".join(["cargo", "publish", "--locked"] + [arg for item in items for arg in ["--package", item]]))PY

Repository: Ronbb/rust_opc

Length of output: 5702


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package section exact =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): print(f"--- {name} lock section ---") p = re.search(r'(?ms)(^\[\[package\]\].*?(?=^\[\[package\]\]|\Z))', lock) for m in re.finditer(r'(?ms)^\[\[package\]\]\n((?:.*?\n)*?)^name = "'+ re.escape(name) +'"', lock): # no, wrong: find containing name line under package passPY# simpler exact:
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()sections = re.split(r'(?=^\[\[package\]\])', lock, flags=re.M)for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): section = next((s for s in sections if re.search(r'^name = "'+re.escape(name)+'"$', s, re.M)), None) print(f"--- {name} sections={section is not None} ---") print(section if section else "NO SECTION")PYecho"== workflow parse exact item order =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()# find package list block inside publish stepstart = text.find('[$packages = @(')end = text.find(')', start)# fallback broadm = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print("m:", bool(m))print("package block:")print(m.group(0))print("quoted package values:")print(sorted(re.findall(r'"([^"]+)"', m.group(0)), key=lambda s: None)) # bad?print(sorted(re.findall(r'"([^"]+)"', m.group(0))))# print whole publish section line numbersfor i,line in enumerate(text.splitlines(), 1): if 'Publish crates' in line or 'cargo `@arguments`' in line: print("line", i, line)PY

Repository: Ronbb/rust_opc

Length of output: 144


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package summary for publish packages =="fornamein opc_classic_types opc_classic_utils opc_comn_bindings;doecho"--- $name version and deps ---"
grep -nE '^(name|version|dependencies|source|dependencies|optional) =|name = "'"$name"'"$|dependencies = \[' Cargo.lock | head -80
done| sed -n '1,120p'echo"== package list in workflow =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()m = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print(m.group(0))print([s for s in m.group(0).split(',') if '"' in s])PYecho"== deterministic publish command shape =="
python3 - <<'PY'packages = ["opc_classic_types", "opc_classic_utils", "opc_comn_bindings"]command = ["cargo", "publish", "--locked"] + [x for p in packages for x in ["--package", p]]print(" ".join(command))PY

Repository: Ronbb/rust_opc

Length of output: 4304


Publish crates one at a time before the retryable upload.

This step sends cargo publish --locked --package opc_classic_types --package opc_classic_utils --package opc_comn_bindings as one command, so it can publish the earlier crates before a later crate fails. Crate releases cannot be overwritten, and index availability can lag after upload. Run cargo publish --locked --package for each package in dependency order. Before each upload, skip the exact package and version only if it already exists on crates.io. Test the partial-failure retry flow against crates.io staging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 56 - 61, Update the release
workflow’s package-publishing loop to invoke `cargo publish --locked --package`
separately for each package in dependency order, rather than accumulating all
packages in `$arguments`. Before each upload, query crates.io and skip only when
that exact package version already exists; otherwise publish it individually
while preserving retry behavior for partial failures.

@Ronbb
Ronbb merged commit da44a99 into masterAug 6, 2026
5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Ronbb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: add OPC Classic integration testing - #25

Merged
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration
Aug 6, 2026
Merged

ci: add OPC Classic integration testing#25
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration

Conversation

@Ronbb

@RonbbRonbb commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an ignored Windows integration test for a real OPC DA server
  • install, verify, register, and clean up OPC Labs Kit Server in GitHub Actions
  • validate server status, group and item handles, quality, timestamp, and synchronous read/write
  • pin Rust 1.97.1 and add formatting, workspace tests, Clippy, and generated-binding checks
  • upload OPC server and test diagnostics on every integration run

Verification

  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • real OPC Kit Server round-trip on windows-latest: Simulation.Register_I4 = I32(12345), quality 0x00c0, non-zero timestamp
  • server unregister, process cleanup, Core Components uninstall, and temporary-file cleanup

GitHub Actions run: https://github.com/Ronbb/rust_opc/actions/runs/31068173343

Summary by CodeRabbit

  • Tests

    • Added Windows integration coverage for OPC DA connections, item reads, writes, permissions, and server state.
    • Expanded automated validation across formatting, workspace tests, linting, generated bindings, and integration scenarios.
    • Added diagnostics, cleanup, and log collection to improve troubleshooting for integration test failures.
  • Documentation

    • Documented how to configure and run the OPC Classic COM integration test locally.
    • Clarified the automated setup and cleanup performed by the integration workflow.
  • Release

    • Added automated publishing for version-tagged releases.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Ronbb, you've reached your PR review limit, so we couldn't start this review.

Next review available in:54 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 162dd4f3-595b-45ee-9c63-4f25310d49f9

📥 Commits

Reviewing files that changed from the base of the PR and between 27ee42a and 99a0b58.

📒 Files selected for processing (2)
  • .github/workflows/rust.yml
  • opc_da_bindings/tests/opc_kit.rs

Walkthrough

The PR adds pinned Windows Rust validation, an ignored OPC DA integration test, OPC Kit Server setup and cleanup automation, and a tag-triggered workflow that publishes selected crates.

Changes

OPC integration CI

Layer / File(s)Summary
Pinned Windows validation
.github/workflows/rust.yml, .github/workflows/rust-clippy.yml
The workflows pin Rust 1.97.1 and the Windows MSVC target. They run formatting, workspace tests, Clippy, binding regeneration, and generated-file validation.
OPC server setup and registration
.github/workflows/rust.yml
The integration job downloads and verifies the OPC Kit Server, installs OPC Classic Core Components, and validates registration in both registry views.
OPC DA round-trip test
opc_da_bindings/tests/opc_kit.rs, README.md, .github/workflows/rust.yml
The ignored Windows test validates the server, group, and items. It performs an I32 write/read round trip. The README documents local execution.
Failure diagnostics and cleanup
.github/workflows/rust.yml, opc_da_bindings/tests/opc_kit.rs
The job captures diagnostics and logs, unregisters the server, removes installed components, verifies cleanup, uploads logs, and safely deletes temporary files.

Release automation

Layer / File(s)Summary
Tagged crate publishing
.github/workflows/release.yml
A version-tag workflow authenticates with crates.io through OIDC and publishes selected crates with locked Cargo metadata in dependency order.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
participant GitHub Actions
participant OPC Kit Server
participant Windows Registry
participant opc_kit integration test
GitHub Actions->>OPC Kit Server: download and verify executable
GitHub Actions->>Windows Registry: register and validate ProgID
GitHub Actions->>opc_kit integration test: run ignored OPC DA test
opc_kit integration test->>OPC Kit Server: connect, read, write, and read back
GitHub Actions->>OPC Kit Server: terminate and unregister server
GitHub Actions->>Windows Registry: verify registration removal
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding OPC Classic integration testing in CI.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/opc-classic-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rust.yml:
- Around line 116-123: Update the post-registration validation around the 32-bit
and 64-bit registry queries so setup fails unless both $reg32Exit and $reg64Exit
indicate successful registration. Keep the existing registry diagnostics log and
failure message, but change the condition from accepting either view to
requiring both views before tests run.
In `@opc_da_bindings/tests/opc_kit.rs`:
- Around line 127-163: Update the write round-trip test around its write
operation to preserve before.value and restore it through cleanup that runs
after every write attempt, including failed writes or assertions. Reuse the
existing group.write API and write_item.server_handle, ensuring the original
value is restored before returning while preserving the current validation
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67695c21-097a-4d3a-bec7-27e633fbff6c

📥 Commits

Reviewing files that changed from the base of the PR and between b077b29 and 0c3d044.

📒 Files selected for processing (4)
  • .github/workflows/rust-clippy.yml
  • .github/workflows/rust.yml
  • README.md
  • opc_da_bindings/tests/opc_kit.rs

Comment thread.github/workflows/rust.yml Outdated
Comment threadopc_da_bindings/tests/opc_kit.rs

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Line 29: Update every uses entry in the release workflow, including
actions/checkout and dtolnay/rust-toolchain, to reference a trusted immutable
40-character commit SHA instead of a tag, branch, or other mutable reference;
preserve each action’s current version and workflow behavior.
- Around line 56-61: Update the release workflow’s package-publishing loop to
invoke `cargo publish --locked --package` separately for each package in
dependency order, rather than accumulating all packages in `$arguments`. Before
each upload, query crates.io and skip only when that exact package version
already exists; otherwise publish it individually while preserving retry
behavior for partial failures.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa5f0342-0e94-467d-8cf9-16b7e9763093

📥 Commits

Reviewing files that changed from the base of the PR and between 0c3d044 and 27ee42a.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

runs-on: windows-latest
steps:
- name: Check out release
uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"## workflow excerpt"
sed -n '1,90p' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## uses references"
rg -n 'uses:\s*[^#]+' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## id-token permission"
rg -n 'id-token|permissions:' .github/workflows/release.yml 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 2068


Security Misconfiguration (CWE-829): Inclusion of Functionality from Untrusted Control Sphere

Reachability: External

Pin every action to a full commit SHA.

Lines 29, 32, 37, and 41 use mutable references, including dtolnay/rust-toolchain@master. If a referenced action is retargeted or compromised before the tag trigger runs, its code executes with id-token: write and can mint the workflow’s OIDC token for crates.io Trusted Publishing. Replace each uses: value with a 40-character commit SHA.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 28-29: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 29, Update every uses entry in the
release workflow, including actions/checkout and dtolnay/rust-toolchain, to
reference a trusted immutable 40-character commit SHA instead of a tag, branch,
or other mutable reference; preserve each action’s current version and workflow
behavior.

Comment on lines +56 to +61
$arguments = @("publish", "--locked")
foreach ($package in $packages) {
$arguments += @("--package", $package)
}

& cargo @arguments

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
nl -ba .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== packages/manifest names =="
python3 - <<'PY'import os, refor root, dirs, files in os.walk('.'): dirs[:] = [d for d in dirs if d not in {'.git','target','node_modules'}] if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() m=re.search(r'^\[package\].*?^name\s*=\s*"(.*?)"', data, re.M|re.S) if m: print(path, m.group(1))PYecho"== references to opc_classic/opc_comn in repo =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 215


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== package manifests and dependency-like fields =="
python3 - <<'PY'import os, refor root, dirs, files in sorted(os.walk('.')): dirs[:] = sorted([d for d in dirs if d not in {'.git','target','node_modules'}]) if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() name=None lines=[] for line in data.splitlines(): if re.match(r'^name\s*=\s*"([^"]+)"', line): name=re.match(r'^name\s*=\s*"([^"]+)"', line).group(1) stripped=line.strip() if any(key in stripped for key in ['dependencies','dev-dependencies','build-dependencies','[workspace]', 'members', 'workspace.package.version', 'build']): lines.append(line) if name: print("---", path, name, "---") for line in lines: print(line)PYecho"== references to opc_classic/opc_comn in relevant files =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 7119


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== package toml files =="forfin opc_classic_types/Cargo.toml opc_classic_utils/Cargo.toml opc_comn_bindings/Cargo.toml opc_classic_abi/Cargo.toml;doif [ -f"$f" ];thenecho"--- $f ---"
awk '{printf "%6d\t%s\n", NR, $0}'"$f"fidoneecho"== workspace package versions =="
awk '/^\[workspace\.package\]/{flag=1} flag; /^\[workspace\]/{if (NR>1) flag=0}' Cargo.toml | awk '{printf "%6d\t%s\n", NR, $0}'echo"== lockfile relevant packages =="
python3 - <<'PY'from pathlib import Pathimport retext = Path("Cargo.lock").read_text()for pkg in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): m = re.search(r'\n\s*\[\[package\]\]\n(.*?)\nname = "' + re.escape(pkg) + '"\n', text, re.S) print(f"--- {pkg} ---") print(m.group(0)[:500] if m else "not found")PYecho"== Cargo.lock dependency edges for publish packages =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): sections = re.split(r'(?=\[\[package\]\])', lock) pkg = next((s for s in sections if f'name = "{name}"' in s), None) print(f"--- deps for {name} ---") for line in (pkg or "").splitlines(): if "dependencies" in line or ("name =" in line and pkg): pass # concise dependency block m=re.search(r'name = "' + re.escape(name) + '".*?(?=^name = |\Z)', pkg, re.S|re.M) if m: block=m.group(0) for line in block.splitlines(): if line.startswith('dependencies =') or line.startswith('"') or '=' in line: print(line)PYecho"== deterministic publish command shape =="
python3 - <<'PY'import reworkflow = open(".github/workflows/release.yml").read()fn = re.search(r'foreach \(\$package in \$packages\) \{[^}]+\}', workflow, re.S)print(fn.group(0))items = re.findall(r'"([^"]+)"', fn.group(0))print("items:", items)print("final command:", " ".join(["cargo", "publish", "--locked"] + [arg for item in items for arg in ["--package", item]]))PY

Repository: Ronbb/rust_opc

Length of output: 5702


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package section exact =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): print(f"--- {name} lock section ---") p = re.search(r'(?ms)(^\[\[package\]\].*?(?=^\[\[package\]\]|\Z))', lock) for m in re.finditer(r'(?ms)^\[\[package\]\]\n((?:.*?\n)*?)^name = "'+ re.escape(name) +'"', lock): # no, wrong: find containing name line under package passPY# simpler exact:
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()sections = re.split(r'(?=^\[\[package\]\])', lock, flags=re.M)for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): section = next((s for s in sections if re.search(r'^name = "'+re.escape(name)+'"$', s, re.M)), None) print(f"--- {name} sections={section is not None} ---") print(section if section else "NO SECTION")PYecho"== workflow parse exact item order =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()# find package list block inside publish stepstart = text.find('[$packages = @(')end = text.find(')', start)# fallback broadm = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print("m:", bool(m))print("package block:")print(m.group(0))print("quoted package values:")print(sorted(re.findall(r'"([^"]+)"', m.group(0)), key=lambda s: None)) # bad?print(sorted(re.findall(r'"([^"]+)"', m.group(0))))# print whole publish section line numbersfor i,line in enumerate(text.splitlines(), 1): if 'Publish crates' in line or 'cargo `@arguments`' in line: print("line", i, line)PY

Repository: Ronbb/rust_opc

Length of output: 144


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package summary for publish packages =="fornamein opc_classic_types opc_classic_utils opc_comn_bindings;doecho"--- $name version and deps ---"
grep -nE '^(name|version|dependencies|source|dependencies|optional) =|name = "'"$name"'"$|dependencies = \[' Cargo.lock | head -80
done| sed -n '1,120p'echo"== package list in workflow =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()m = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print(m.group(0))print([s for s in m.group(0).split(',') if '"' in s])PYecho"== deterministic publish command shape =="
python3 - <<'PY'packages = ["opc_classic_types", "opc_classic_utils", "opc_comn_bindings"]command = ["cargo", "publish", "--locked"] + [x for p in packages for x in ["--package", p]]print(" ".join(command))PY

Repository: Ronbb/rust_opc

Length of output: 4304


Publish crates one at a time before the retryable upload.

This step sends cargo publish --locked --package opc_classic_types --package opc_classic_utils --package opc_comn_bindings as one command, so it can publish the earlier crates before a later crate fails. Crate releases cannot be overwritten, and index availability can lag after upload. Run cargo publish --locked --package for each package in dependency order. Before each upload, skip the exact package and version only if it already exists on crates.io. Test the partial-failure retry flow against crates.io staging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 56 - 61, Update the release
workflow’s package-publishing loop to invoke `cargo publish --locked --package`
separately for each package in dependency order, rather than accumulating all
packages in `$arguments`. Before each upload, query crates.io and skip only when
that exact package version already exists; otherwise publish it individually
while preserving retry behavior for partial failures.

@Ronbb
Ronbb merged commit da44a99 into masterAug 6, 2026
5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Ronbb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: add OPC Classic integration testing - #25

Merged
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration
Aug 6, 2026
Merged

ci: add OPC Classic integration testing#25
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration

Conversation

@Ronbb

@RonbbRonbb commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an ignored Windows integration test for a real OPC DA server
  • install, verify, register, and clean up OPC Labs Kit Server in GitHub Actions
  • validate server status, group and item handles, quality, timestamp, and synchronous read/write
  • pin Rust 1.97.1 and add formatting, workspace tests, Clippy, and generated-binding checks
  • upload OPC server and test diagnostics on every integration run

Verification

  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • real OPC Kit Server round-trip on windows-latest: Simulation.Register_I4 = I32(12345), quality 0x00c0, non-zero timestamp
  • server unregister, process cleanup, Core Components uninstall, and temporary-file cleanup

GitHub Actions run: https://github.com/Ronbb/rust_opc/actions/runs/31068173343

Summary by CodeRabbit

  • Tests

    • Added Windows integration coverage for OPC DA connections, item reads, writes, permissions, and server state.
    • Expanded automated validation across formatting, workspace tests, linting, generated bindings, and integration scenarios.
    • Added diagnostics, cleanup, and log collection to improve troubleshooting for integration test failures.
  • Documentation

    • Documented how to configure and run the OPC Classic COM integration test locally.
    • Clarified the automated setup and cleanup performed by the integration workflow.
  • Release

    • Added automated publishing for version-tagged releases.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Ronbb, you've reached your PR review limit, so we couldn't start this review.

Next review available in:54 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 162dd4f3-595b-45ee-9c63-4f25310d49f9

📥 Commits

Reviewing files that changed from the base of the PR and between 27ee42a and 99a0b58.

📒 Files selected for processing (2)
  • .github/workflows/rust.yml
  • opc_da_bindings/tests/opc_kit.rs

Walkthrough

The PR adds pinned Windows Rust validation, an ignored OPC DA integration test, OPC Kit Server setup and cleanup automation, and a tag-triggered workflow that publishes selected crates.

Changes

OPC integration CI

Layer / File(s)Summary
Pinned Windows validation
.github/workflows/rust.yml, .github/workflows/rust-clippy.yml
The workflows pin Rust 1.97.1 and the Windows MSVC target. They run formatting, workspace tests, Clippy, binding regeneration, and generated-file validation.
OPC server setup and registration
.github/workflows/rust.yml
The integration job downloads and verifies the OPC Kit Server, installs OPC Classic Core Components, and validates registration in both registry views.
OPC DA round-trip test
opc_da_bindings/tests/opc_kit.rs, README.md, .github/workflows/rust.yml
The ignored Windows test validates the server, group, and items. It performs an I32 write/read round trip. The README documents local execution.
Failure diagnostics and cleanup
.github/workflows/rust.yml, opc_da_bindings/tests/opc_kit.rs
The job captures diagnostics and logs, unregisters the server, removes installed components, verifies cleanup, uploads logs, and safely deletes temporary files.

Release automation

Layer / File(s)Summary
Tagged crate publishing
.github/workflows/release.yml
A version-tag workflow authenticates with crates.io through OIDC and publishes selected crates with locked Cargo metadata in dependency order.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
participant GitHub Actions
participant OPC Kit Server
participant Windows Registry
participant opc_kit integration test
GitHub Actions->>OPC Kit Server: download and verify executable
GitHub Actions->>Windows Registry: register and validate ProgID
GitHub Actions->>opc_kit integration test: run ignored OPC DA test
opc_kit integration test->>OPC Kit Server: connect, read, write, and read back
GitHub Actions->>OPC Kit Server: terminate and unregister server
GitHub Actions->>Windows Registry: verify registration removal
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding OPC Classic integration testing in CI.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/opc-classic-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rust.yml:
- Around line 116-123: Update the post-registration validation around the 32-bit
and 64-bit registry queries so setup fails unless both $reg32Exit and $reg64Exit
indicate successful registration. Keep the existing registry diagnostics log and
failure message, but change the condition from accepting either view to
requiring both views before tests run.
In `@opc_da_bindings/tests/opc_kit.rs`:
- Around line 127-163: Update the write round-trip test around its write
operation to preserve before.value and restore it through cleanup that runs
after every write attempt, including failed writes or assertions. Reuse the
existing group.write API and write_item.server_handle, ensuring the original
value is restored before returning while preserving the current validation
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67695c21-097a-4d3a-bec7-27e633fbff6c

📥 Commits

Reviewing files that changed from the base of the PR and between b077b29 and 0c3d044.

📒 Files selected for processing (4)
  • .github/workflows/rust-clippy.yml
  • .github/workflows/rust.yml
  • README.md
  • opc_da_bindings/tests/opc_kit.rs

Comment thread.github/workflows/rust.yml Outdated
Comment threadopc_da_bindings/tests/opc_kit.rs

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Line 29: Update every uses entry in the release workflow, including
actions/checkout and dtolnay/rust-toolchain, to reference a trusted immutable
40-character commit SHA instead of a tag, branch, or other mutable reference;
preserve each action’s current version and workflow behavior.
- Around line 56-61: Update the release workflow’s package-publishing loop to
invoke `cargo publish --locked --package` separately for each package in
dependency order, rather than accumulating all packages in `$arguments`. Before
each upload, query crates.io and skip only when that exact package version
already exists; otherwise publish it individually while preserving retry
behavior for partial failures.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa5f0342-0e94-467d-8cf9-16b7e9763093

📥 Commits

Reviewing files that changed from the base of the PR and between 0c3d044 and 27ee42a.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

runs-on: windows-latest
steps:
- name: Check out release
uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"## workflow excerpt"
sed -n '1,90p' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## uses references"
rg -n 'uses:\s*[^#]+' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## id-token permission"
rg -n 'id-token|permissions:' .github/workflows/release.yml 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 2068


Security Misconfiguration (CWE-829): Inclusion of Functionality from Untrusted Control Sphere

Reachability: External

Pin every action to a full commit SHA.

Lines 29, 32, 37, and 41 use mutable references, including dtolnay/rust-toolchain@master. If a referenced action is retargeted or compromised before the tag trigger runs, its code executes with id-token: write and can mint the workflow’s OIDC token for crates.io Trusted Publishing. Replace each uses: value with a 40-character commit SHA.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 28-29: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 29, Update every uses entry in the
release workflow, including actions/checkout and dtolnay/rust-toolchain, to
reference a trusted immutable 40-character commit SHA instead of a tag, branch,
or other mutable reference; preserve each action’s current version and workflow
behavior.

Comment on lines +56 to +61
$arguments = @("publish", "--locked")
foreach ($package in $packages) {
$arguments += @("--package", $package)
}

& cargo @arguments

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
nl -ba .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== packages/manifest names =="
python3 - <<'PY'import os, refor root, dirs, files in os.walk('.'): dirs[:] = [d for d in dirs if d not in {'.git','target','node_modules'}] if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() m=re.search(r'^\[package\].*?^name\s*=\s*"(.*?)"', data, re.M|re.S) if m: print(path, m.group(1))PYecho"== references to opc_classic/opc_comn in repo =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 215


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== package manifests and dependency-like fields =="
python3 - <<'PY'import os, refor root, dirs, files in sorted(os.walk('.')): dirs[:] = sorted([d for d in dirs if d not in {'.git','target','node_modules'}]) if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() name=None lines=[] for line in data.splitlines(): if re.match(r'^name\s*=\s*"([^"]+)"', line): name=re.match(r'^name\s*=\s*"([^"]+)"', line).group(1) stripped=line.strip() if any(key in stripped for key in ['dependencies','dev-dependencies','build-dependencies','[workspace]', 'members', 'workspace.package.version', 'build']): lines.append(line) if name: print("---", path, name, "---") for line in lines: print(line)PYecho"== references to opc_classic/opc_comn in relevant files =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 7119


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== package toml files =="forfin opc_classic_types/Cargo.toml opc_classic_utils/Cargo.toml opc_comn_bindings/Cargo.toml opc_classic_abi/Cargo.toml;doif [ -f"$f" ];thenecho"--- $f ---"
awk '{printf "%6d\t%s\n", NR, $0}'"$f"fidoneecho"== workspace package versions =="
awk '/^\[workspace\.package\]/{flag=1} flag; /^\[workspace\]/{if (NR>1) flag=0}' Cargo.toml | awk '{printf "%6d\t%s\n", NR, $0}'echo"== lockfile relevant packages =="
python3 - <<'PY'from pathlib import Pathimport retext = Path("Cargo.lock").read_text()for pkg in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): m = re.search(r'\n\s*\[\[package\]\]\n(.*?)\nname = "' + re.escape(pkg) + '"\n', text, re.S) print(f"--- {pkg} ---") print(m.group(0)[:500] if m else "not found")PYecho"== Cargo.lock dependency edges for publish packages =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): sections = re.split(r'(?=\[\[package\]\])', lock) pkg = next((s for s in sections if f'name = "{name}"' in s), None) print(f"--- deps for {name} ---") for line in (pkg or "").splitlines(): if "dependencies" in line or ("name =" in line and pkg): pass # concise dependency block m=re.search(r'name = "' + re.escape(name) + '".*?(?=^name = |\Z)', pkg, re.S|re.M) if m: block=m.group(0) for line in block.splitlines(): if line.startswith('dependencies =') or line.startswith('"') or '=' in line: print(line)PYecho"== deterministic publish command shape =="
python3 - <<'PY'import reworkflow = open(".github/workflows/release.yml").read()fn = re.search(r'foreach \(\$package in \$packages\) \{[^}]+\}', workflow, re.S)print(fn.group(0))items = re.findall(r'"([^"]+)"', fn.group(0))print("items:", items)print("final command:", " ".join(["cargo", "publish", "--locked"] + [arg for item in items for arg in ["--package", item]]))PY

Repository: Ronbb/rust_opc

Length of output: 5702


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package section exact =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): print(f"--- {name} lock section ---") p = re.search(r'(?ms)(^\[\[package\]\].*?(?=^\[\[package\]\]|\Z))', lock) for m in re.finditer(r'(?ms)^\[\[package\]\]\n((?:.*?\n)*?)^name = "'+ re.escape(name) +'"', lock): # no, wrong: find containing name line under package passPY# simpler exact:
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()sections = re.split(r'(?=^\[\[package\]\])', lock, flags=re.M)for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): section = next((s for s in sections if re.search(r'^name = "'+re.escape(name)+'"$', s, re.M)), None) print(f"--- {name} sections={section is not None} ---") print(section if section else "NO SECTION")PYecho"== workflow parse exact item order =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()# find package list block inside publish stepstart = text.find('[$packages = @(')end = text.find(')', start)# fallback broadm = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print("m:", bool(m))print("package block:")print(m.group(0))print("quoted package values:")print(sorted(re.findall(r'"([^"]+)"', m.group(0)), key=lambda s: None)) # bad?print(sorted(re.findall(r'"([^"]+)"', m.group(0))))# print whole publish section line numbersfor i,line in enumerate(text.splitlines(), 1): if 'Publish crates' in line or 'cargo `@arguments`' in line: print("line", i, line)PY

Repository: Ronbb/rust_opc

Length of output: 144


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package summary for publish packages =="fornamein opc_classic_types opc_classic_utils opc_comn_bindings;doecho"--- $name version and deps ---"
grep -nE '^(name|version|dependencies|source|dependencies|optional) =|name = "'"$name"'"$|dependencies = \[' Cargo.lock | head -80
done| sed -n '1,120p'echo"== package list in workflow =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()m = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print(m.group(0))print([s for s in m.group(0).split(',') if '"' in s])PYecho"== deterministic publish command shape =="
python3 - <<'PY'packages = ["opc_classic_types", "opc_classic_utils", "opc_comn_bindings"]command = ["cargo", "publish", "--locked"] + [x for p in packages for x in ["--package", p]]print(" ".join(command))PY

Repository: Ronbb/rust_opc

Length of output: 4304


Publish crates one at a time before the retryable upload.

This step sends cargo publish --locked --package opc_classic_types --package opc_classic_utils --package opc_comn_bindings as one command, so it can publish the earlier crates before a later crate fails. Crate releases cannot be overwritten, and index availability can lag after upload. Run cargo publish --locked --package for each package in dependency order. Before each upload, skip the exact package and version only if it already exists on crates.io. Test the partial-failure retry flow against crates.io staging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 56 - 61, Update the release
workflow’s package-publishing loop to invoke `cargo publish --locked --package`
separately for each package in dependency order, rather than accumulating all
packages in `$arguments`. Before each upload, query crates.io and skip only when
that exact package version already exists; otherwise publish it individually
while preserving retry behavior for partial failures.

@Ronbb
Ronbb merged commit da44a99 into masterAug 6, 2026
5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Ronbb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

ci: add OPC Classic integration testing - #25

Merged
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration
Aug 6, 2026
Merged

ci: add OPC Classic integration testing#25
Ronbb merged 6 commits into
masterfrom
ci/opc-classic-integration

Conversation

@Ronbb

@RonbbRonbb commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an ignored Windows integration test for a real OPC DA server
  • install, verify, register, and clean up OPC Labs Kit Server in GitHub Actions
  • validate server status, group and item handles, quality, timestamp, and synchronous read/write
  • pin Rust 1.97.1 and add formatting, workspace tests, Clippy, and generated-binding checks
  • upload OPC server and test diagnostics on every integration run

Verification

  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • real OPC Kit Server round-trip on windows-latest: Simulation.Register_I4 = I32(12345), quality 0x00c0, non-zero timestamp
  • server unregister, process cleanup, Core Components uninstall, and temporary-file cleanup

GitHub Actions run: https://github.com/Ronbb/rust_opc/actions/runs/31068173343

Summary by CodeRabbit

  • Tests

    • Added Windows integration coverage for OPC DA connections, item reads, writes, permissions, and server state.
    • Expanded automated validation across formatting, workspace tests, linting, generated bindings, and integration scenarios.
    • Added diagnostics, cleanup, and log collection to improve troubleshooting for integration test failures.
  • Documentation

    • Documented how to configure and run the OPC Classic COM integration test locally.
    • Clarified the automated setup and cleanup performed by the integration workflow.
  • Release

    • Added automated publishing for version-tagged releases.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Ronbb, you've reached your PR review limit, so we couldn't start this review.

Next review available in:54 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 162dd4f3-595b-45ee-9c63-4f25310d49f9

📥 Commits

Reviewing files that changed from the base of the PR and between 27ee42a and 99a0b58.

📒 Files selected for processing (2)
  • .github/workflows/rust.yml
  • opc_da_bindings/tests/opc_kit.rs

Walkthrough

The PR adds pinned Windows Rust validation, an ignored OPC DA integration test, OPC Kit Server setup and cleanup automation, and a tag-triggered workflow that publishes selected crates.

Changes

OPC integration CI

Layer / File(s)Summary
Pinned Windows validation
.github/workflows/rust.yml, .github/workflows/rust-clippy.yml
The workflows pin Rust 1.97.1 and the Windows MSVC target. They run formatting, workspace tests, Clippy, binding regeneration, and generated-file validation.
OPC server setup and registration
.github/workflows/rust.yml
The integration job downloads and verifies the OPC Kit Server, installs OPC Classic Core Components, and validates registration in both registry views.
OPC DA round-trip test
opc_da_bindings/tests/opc_kit.rs, README.md, .github/workflows/rust.yml
The ignored Windows test validates the server, group, and items. It performs an I32 write/read round trip. The README documents local execution.
Failure diagnostics and cleanup
.github/workflows/rust.yml, opc_da_bindings/tests/opc_kit.rs
The job captures diagnostics and logs, unregisters the server, removes installed components, verifies cleanup, uploads logs, and safely deletes temporary files.

Release automation

Layer / File(s)Summary
Tagged crate publishing
.github/workflows/release.yml
A version-tag workflow authenticates with crates.io through OIDC and publishes selected crates with locked Cargo metadata in dependency order.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
participant GitHub Actions
participant OPC Kit Server
participant Windows Registry
participant opc_kit integration test
GitHub Actions->>OPC Kit Server: download and verify executable
GitHub Actions->>Windows Registry: register and validate ProgID
GitHub Actions->>opc_kit integration test: run ignored OPC DA test
opc_kit integration test->>OPC Kit Server: connect, read, write, and read back
GitHub Actions->>OPC Kit Server: terminate and unregister server
GitHub Actions->>Windows Registry: verify registration removal
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding OPC Classic integration testing in CI.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/opc-classic-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rust.yml:
- Around line 116-123: Update the post-registration validation around the 32-bit
and 64-bit registry queries so setup fails unless both $reg32Exit and $reg64Exit
indicate successful registration. Keep the existing registry diagnostics log and
failure message, but change the condition from accepting either view to
requiring both views before tests run.
In `@opc_da_bindings/tests/opc_kit.rs`:
- Around line 127-163: Update the write round-trip test around its write
operation to preserve before.value and restore it through cleanup that runs
after every write attempt, including failed writes or assertions. Reuse the
existing group.write API and write_item.server_handle, ensuring the original
value is restored before returning while preserving the current validation
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67695c21-097a-4d3a-bec7-27e633fbff6c

📥 Commits

Reviewing files that changed from the base of the PR and between b077b29 and 0c3d044.

📒 Files selected for processing (4)
  • .github/workflows/rust-clippy.yml
  • .github/workflows/rust.yml
  • README.md
  • opc_da_bindings/tests/opc_kit.rs

Comment thread.github/workflows/rust.yml Outdated
Comment threadopc_da_bindings/tests/opc_kit.rs

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Line 29: Update every uses entry in the release workflow, including
actions/checkout and dtolnay/rust-toolchain, to reference a trusted immutable
40-character commit SHA instead of a tag, branch, or other mutable reference;
preserve each action’s current version and workflow behavior.
- Around line 56-61: Update the release workflow’s package-publishing loop to
invoke `cargo publish --locked --package` separately for each package in
dependency order, rather than accumulating all packages in `$arguments`. Before
each upload, query crates.io and skip only when that exact package version
already exists; otherwise publish it individually while preserving retry
behavior for partial failures.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa5f0342-0e94-467d-8cf9-16b7e9763093

📥 Commits

Reviewing files that changed from the base of the PR and between 0c3d044 and 27ee42a.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

runs-on: windows-latest
steps:
- name: Check out release
uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"## workflow excerpt"
sed -n '1,90p' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## uses references"
rg -n 'uses:\s*[^#]+' .github/workflows/release.yml 2>/dev/null ||trueechoecho"## id-token permission"
rg -n 'id-token|permissions:' .github/workflows/release.yml 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 2068


Security Misconfiguration (CWE-829): Inclusion of Functionality from Untrusted Control Sphere

Reachability: External

Pin every action to a full commit SHA.

Lines 29, 32, 37, and 41 use mutable references, including dtolnay/rust-toolchain@master. If a referenced action is retargeted or compromised before the tag trigger runs, its code executes with id-token: write and can mint the workflow’s OIDC token for crates.io Trusted Publishing. Replace each uses: value with a 40-character commit SHA.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 28-29: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 29, Update every uses entry in the
release workflow, including actions/checkout and dtolnay/rust-toolchain, to
reference a trusted immutable 40-character commit SHA instead of a tag, branch,
or other mutable reference; preserve each action’s current version and workflow
behavior.

Comment on lines +56 to +61
$arguments = @("publish", "--locked")
foreach ($package in $packages) {
$arguments += @("--package", $package)
}

& cargo @arguments

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
nl -ba .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== packages/manifest names =="
python3 - <<'PY'import os, refor root, dirs, files in os.walk('.'): dirs[:] = [d for d in dirs if d not in {'.git','target','node_modules'}] if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() m=re.search(r'^\[package\].*?^name\s*=\s*"(.*?)"', data, re.M|re.S) if m: print(path, m.group(1))PYecho"== references to opc_classic/opc_comn in repo =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 215


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== workflow excerpt =="if [ -f .github/workflows/release.yml ];then
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yml | sed -n '1,140p'elseecho"missing .github/workflows/release.yml"fiecho"== package manifests and dependency-like fields =="
python3 - <<'PY'import os, refor root, dirs, files in sorted(os.walk('.')): dirs[:] = sorted([d for d in dirs if d not in {'.git','target','node_modules'}]) if 'Cargo.toml' in files: path=os.path.join(root,'Cargo.toml') with open(path, encoding='utf-8') as f: data=f.read() name=None lines=[] for line in data.splitlines(): if re.match(r'^name\s*=\s*"([^"]+)"', line): name=re.match(r'^name\s*=\s*"([^"]+)"', line).group(1) stripped=line.strip() if any(key in stripped for key in ['dependencies','dev-dependencies','build-dependencies','[workspace]', 'members', 'workspace.package.version', 'build']): lines.append(line) if name: print("---", path, name, "---") for line in lines: print(line)PYecho"== references to opc_classic/opc_comn in relevant files =="
rg -n "opc_classic|opc_comn|publish|cargo publish|crates.io" .github Cargo.toml Cargo.lock 2>/dev/null ||true

Repository: Ronbb/rust_opc

Length of output: 7119


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== package toml files =="forfin opc_classic_types/Cargo.toml opc_classic_utils/Cargo.toml opc_comn_bindings/Cargo.toml opc_classic_abi/Cargo.toml;doif [ -f"$f" ];thenecho"--- $f ---"
awk '{printf "%6d\t%s\n", NR, $0}'"$f"fidoneecho"== workspace package versions =="
awk '/^\[workspace\.package\]/{flag=1} flag; /^\[workspace\]/{if (NR>1) flag=0}' Cargo.toml | awk '{printf "%6d\t%s\n", NR, $0}'echo"== lockfile relevant packages =="
python3 - <<'PY'from pathlib import Pathimport retext = Path("Cargo.lock").read_text()for pkg in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): m = re.search(r'\n\s*\[\[package\]\]\n(.*?)\nname = "' + re.escape(pkg) + '"\n', text, re.S) print(f"--- {pkg} ---") print(m.group(0)[:500] if m else "not found")PYecho"== Cargo.lock dependency edges for publish packages =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): sections = re.split(r'(?=\[\[package\]\])', lock) pkg = next((s for s in sections if f'name = "{name}"' in s), None) print(f"--- deps for {name} ---") for line in (pkg or "").splitlines(): if "dependencies" in line or ("name =" in line and pkg): pass # concise dependency block m=re.search(r'name = "' + re.escape(name) + '".*?(?=^name = |\Z)', pkg, re.S|re.M) if m: block=m.group(0) for line in block.splitlines(): if line.startswith('dependencies =') or line.startswith('"') or '=' in line: print(line)PYecho"== deterministic publish command shape =="
python3 - <<'PY'import reworkflow = open(".github/workflows/release.yml").read()fn = re.search(r'foreach \(\$package in \$packages\) \{[^}]+\}', workflow, re.S)print(fn.group(0))items = re.findall(r'"([^"]+)"', fn.group(0))print("items:", items)print("final command:", " ".join(["cargo", "publish", "--locked"] + [arg for item in items for arg in ["--package", item]]))PY

Repository: Ronbb/rust_opc

Length of output: 5702


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package section exact =="
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): print(f"--- {name} lock section ---") p = re.search(r'(?ms)(^\[\[package\]\].*?(?=^\[\[package\]\]|\Z))', lock) for m in re.finditer(r'(?ms)^\[\[package\]\]\n((?:.*?\n)*?)^name = "'+ re.escape(name) +'"', lock): # no, wrong: find containing name line under package passPY# simpler exact:
python3 - <<'PY'from pathlib import Pathimport relock = Path("Cargo.lock").read_text()sections = re.split(r'(?=^\[\[package\]\])', lock, flags=re.M)for name in ("opc_classic_types", "opc_classic_utils", "opc_comn_bindings"): section = next((s for s in sections if re.search(r'^name = "'+re.escape(name)+'"$', s, re.M)), None) print(f"--- {name} sections={section is not None} ---") print(section if section else "NO SECTION")PYecho"== workflow parse exact item order =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()# find package list block inside publish stepstart = text.find('[$packages = @(')end = text.find(')', start)# fallback broadm = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print("m:", bool(m))print("package block:")print(m.group(0))print("quoted package values:")print(sorted(re.findall(r'"([^"]+)"', m.group(0)), key=lambda s: None)) # bad?print(sorted(re.findall(r'"([^"]+)"', m.group(0))))# print whole publish section line numbersfor i,line in enumerate(text.splitlines(), 1): if 'Publish crates' in line or 'cargo `@arguments`' in line: print("line", i, line)PY

Repository: Ronbb/rust_opc

Length of output: 144


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== lockfile package summary for publish packages =="fornamein opc_classic_types opc_classic_utils opc_comn_bindings;doecho"--- $name version and deps ---"
grep -nE '^(name|version|dependencies|source|dependencies|optional) =|name = "'"$name"'"$|dependencies = \[' Cargo.lock | head -80
done| sed -n '1,120p'echo"== package list in workflow =="
python3 - <<'PY'from pathlib import Pathimport retext = Path(".github/workflows/release.yml").read_text()m = re.search(r'\$packages\s*=\s*@.*?\)', text, re.S)print(m.group(0))print([s for s in m.group(0).split(',') if '"' in s])PYecho"== deterministic publish command shape =="
python3 - <<'PY'packages = ["opc_classic_types", "opc_classic_utils", "opc_comn_bindings"]command = ["cargo", "publish", "--locked"] + [x for p in packages for x in ["--package", p]]print(" ".join(command))PY

Repository: Ronbb/rust_opc

Length of output: 4304


Publish crates one at a time before the retryable upload.

This step sends cargo publish --locked --package opc_classic_types --package opc_classic_utils --package opc_comn_bindings as one command, so it can publish the earlier crates before a later crate fails. Crate releases cannot be overwritten, and index availability can lag after upload. Run cargo publish --locked --package for each package in dependency order. Before each upload, skip the exact package and version only if it already exists on crates.io. Test the partial-failure retry flow against crates.io staging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 56 - 61, Update the release
workflow’s package-publishing loop to invoke `cargo publish --locked --package`
separately for each package in dependency order, rather than accumulating all
packages in `$arguments`. Before each upload, query crates.io and skip only when
that exact package version already exists; otherwise publish it individually
while preserving retry behavior for partial failures.

@Ronbb
Ronbb merged commit da44a99 into masterAug 6, 2026
5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Ronbb