-
Notifications
You must be signed in to change notification settings - Fork 8
ci: overhaul CI #253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
ci: overhaul CI #253
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # Test group configuration for CI | ||
| # CI will fail if any workspace crate is not assigned to a group or excluded | ||
|
|
||
| groups: | ||
| core: | ||
| - dashcore | ||
| - dashcore_hashes | ||
| - dashcore-private | ||
| - dash-network | ||
|
|
||
| spv: | ||
| - dash-spv | ||
|
|
||
| wallet: | ||
| - key-wallet | ||
| - key-wallet-manager | ||
|
|
||
| ffi: | ||
| - dash-network-ffi | ||
| - dash-spv-ffi | ||
| - key-wallet-ffi | ||
|
|
||
| rpc: | ||
| - dashcore-rpc | ||
| - dashcore-rpc-json | ||
|
|
||
| tools: | ||
| - dashcore-test-utils | ||
| - dash-fuzz | ||
|
|
||
| # Crates intentionally not tested (with reason) | ||
| excluded: | ||
| - integration_test # Requires live Dash node | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # No-std build checks | ||
| # | ||
| # Lists crates that support no-std and their test configurations. | ||
| # Each entry runs: cargo check --no-default-features --features <entry> | ||
|
|
||
| dashcore_hashes: | ||
| - bare # --no-default-features only | ||
| - alloc | ||
| - alloc serde # multiple features | ||
|
|
||
| dashcore-private: | ||
| - bare | ||
| - alloc | ||
|
|
||
| dash-network: | ||
| - no-std |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,241 @@ | ||
| #!/usr/bin/env python3 | ||
| """CI configuration management script. | ||
|
|
||
| Used by GitHub Actions workflows for test management. | ||
|
|
||
| Subcommands: | ||
| verify-groups Check all workspace crates are assigned to test groups | ||
| run-group Run tests for all crates in a group | ||
| run-no-std Run no-std build checks | ||
| """ | ||
|
|
||
| import argparse | ||
| import json | ||
| import os | ||
| import subprocess | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| import yaml | ||
|
|
||
|
|
||
| def get_workspace_metadata(): | ||
| """Get workspace metadata from cargo.""" | ||
| result = subprocess.run( | ||
| ["cargo", "metadata", "--no-deps", "--format-version", "1"], | ||
| capture_output=True, | ||
| text=True, | ||
| check=True, | ||
| ) | ||
| return json.loads(result.stdout) | ||
|
|
||
|
|
||
| def load_yaml(path: Path): | ||
| """Load YAML file with error handling.""" | ||
| try: | ||
| with open(path) as f: | ||
| content = yaml.safe_load(f) | ||
| return content if content is not None else {} | ||
| except FileNotFoundError: | ||
| github_error(f"Configuration file not found: {path}") | ||
| sys.exit(1) | ||
| except yaml.YAMLError as e: | ||
| github_error(f"Invalid YAML in {path}: {e}") | ||
| sys.exit(1) | ||
|
|
||
|
|
||
| def github_error(msg: str): | ||
| """Print GitHub Actions error annotation.""" | ||
| print(f"::error::{msg}") | ||
|
|
||
|
|
||
| def github_notice(msg: str): | ||
| """Print GitHub Actions notice annotation.""" | ||
| print(f"::notice::{msg}") | ||
|
|
||
|
|
||
| def github_group_start(name: str): | ||
| """Start a GitHub Actions log group.""" | ||
| print(f"::group::{name}", flush=True) | ||
|
|
||
|
|
||
| def github_group_end(): | ||
| """End a GitHub Actions log group.""" | ||
| print("::endgroup::", flush=True) | ||
|
|
||
|
|
||
| def github_output(name: str, value: str): | ||
| """Write a GitHub Actions output variable.""" | ||
| output_file = os.environ.get("GITHUB_OUTPUT") | ||
| if output_file: | ||
| with open(output_file, "a") as f: | ||
| f.write(f"{name}={value}\n") | ||
|
|
||
|
|
||
| def verify_groups(args): | ||
| """Verify all workspace crates are assigned to test groups.""" | ||
| metadata = get_workspace_metadata() | ||
| workspace_crates = {pkg["name"] for pkg in metadata["packages"]} | ||
|
|
||
| config = load_yaml(args.groups_file) | ||
| groups = config.get("groups", {}) | ||
|
|
||
| assigned = set() | ||
| for group_crates in groups.values(): | ||
| if group_crates: | ||
| assigned.update(group_crates) | ||
| assigned.update(config.get("excluded", []) or []) | ||
|
|
||
| unassigned = workspace_crates - assigned | ||
| if unassigned: | ||
| github_error( | ||
| f"Crates not assigned to any test group: {', '.join(sorted(unassigned))}" | ||
| ) | ||
| print("\nPlease add them to a group or 'excluded' section in ci-groups.yml") | ||
| return 1 | ||
|
|
||
| print(f"All {len(workspace_crates)} workspace crates are assigned to test groups") | ||
|
|
||
| # Output groups for GitHub Actions matrix | ||
| github_output("groups", json.dumps(list(groups.keys()))) | ||
|
|
||
| return 0 | ||
|
|
||
|
|
||
| def run_no_std(args): | ||
| """Run no-std build checks from ci-no-std.yml. | ||
|
|
||
| Format: crate_name: [list of configs] | ||
| Each config runs: cargo check -p crate --no-default-features --features <config> | ||
| Special: 'bare' means just --no-default-features (no features) | ||
| """ | ||
| config = load_yaml(args.no_std_file) | ||
|
|
||
| failed = [] | ||
|
|
||
| for crate_name, entries in config.items(): | ||
| if not entries: | ||
| continue | ||
|
|
||
| for entry in entries: | ||
| if not isinstance(entry, str) or not entry.strip(): | ||
| continue | ||
|
|
||
| entry_clean = entry.strip() | ||
|
|
||
| # Build cargo flags | ||
| if entry_clean == "bare": | ||
| flags = ["--no-default-features"] | ||
| display_name = "bare" | ||
| elif entry_clean == "no-std": | ||
| flags = ["--no-default-features", "--features", "no-std"] | ||
| display_name = "no-std" | ||
| elif " " in entry_clean: | ||
| # Multiple features (space-separated) | ||
| features = entry_clean.replace(" ", ",") | ||
| flags = ["--no-default-features", "--features", features] | ||
| display_name = entry_clean.replace(" ", "+") | ||
| else: | ||
| # Single feature | ||
| flags = ["--no-default-features", "--features", entry_clean] | ||
| display_name = entry_clean | ||
|
|
||
| github_group_start(f"{crate_name} ({display_name})") | ||
|
|
||
| cmd = ["cargo", "check", "-p", crate_name] + flags | ||
| result = subprocess.run(cmd) | ||
|
|
||
| github_group_end() | ||
|
|
||
| if result.returncode != 0: | ||
| failed.append(f"{crate_name} ({display_name})") | ||
| github_error(f"No-std check failed: {crate_name} with {' '.join(flags)}") | ||
|
|
||
| if failed: | ||
| print("\n" + "=" * 40) | ||
| print("FAILED NO-STD CHECKS:") | ||
| for f in failed: | ||
| print(f" - {f}") | ||
| print("=" * 40) | ||
| return 1 | ||
|
|
||
| return 0 | ||
|
|
||
|
|
||
| def run_group_tests(args): | ||
| """Run tests for all crates in a group.""" | ||
| config = load_yaml(args.groups_file) | ||
| groups = config.get("groups", {}) | ||
|
|
||
| if args.group not in groups: | ||
| github_error(f"Unknown group: {args.group}") | ||
| return 1 | ||
|
|
||
| crates = groups[args.group] or [] | ||
| failed = [] | ||
|
|
||
| for crate in crates: | ||
| # Skip dash-fuzz on Windows | ||
| if args.os == "windows-latest" and crate == "dash-fuzz": | ||
| github_notice(f"Skipping {crate} on Windows (honggfuzz not supported)") | ||
| continue | ||
|
|
||
| github_group_start(f"Testing {crate}") | ||
|
|
||
| cmd = ["cargo", "test", "-p", crate, "--all-features"] | ||
| result = subprocess.run(cmd) | ||
|
|
||
| github_group_end() | ||
|
|
||
| if result.returncode != 0: | ||
| failed.append(crate) | ||
| github_error(f"Test failed for {crate} on {args.os}") | ||
|
|
||
| if failed: | ||
| print("\n" + "=" * 40) | ||
| print(f"FAILED TESTS ({args.group} on {args.os}):") | ||
| for f in failed: | ||
| print(f" - {f}") | ||
| print("=" * 40) | ||
| return 1 | ||
|
|
||
| return 0 | ||
|
|
||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser(description="CI configuration management") | ||
| parser.add_argument( | ||
| "--groups-file", | ||
| type=Path, | ||
| default=Path(".github/ci-groups.yml"), | ||
| help="Path to ci-groups.yml", | ||
| ) | ||
| parser.add_argument( | ||
| "--no-std-file", | ||
| type=Path, | ||
| default=Path(".github/ci-no-std.yml"), | ||
| help="Path to ci-no-std.yml", | ||
| ) | ||
|
|
||
| subparsers = parser.add_subparsers(dest="command", required=True) | ||
|
|
||
| subparsers.add_parser("verify-groups", help="Verify all crates assigned to groups") | ||
| subparsers.add_parser("run-no-std", help="Run no-std checks") | ||
|
|
||
| run_group_parser = subparsers.add_parser("run-group", help="Run tests for a group") | ||
| run_group_parser.add_argument("group", help="Group name") | ||
| run_group_parser.add_argument("--os", default="ubuntu-latest", help="OS name") | ||
|
|
||
| args = parser.parse_args() | ||
|
|
||
| commands = { | ||
| "verify-groups": verify_groups, | ||
| "run-no-std": run_no_std, | ||
| "run-group": run_group_tests, | ||
| } | ||
|
|
||
| return commands[args.command](args) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| name: Build and Test | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| os: | ||
| required: true | ||
| type: string | ||
| groups: | ||
| required: true | ||
| type: string | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| test: | ||
| name: ${{ matrix.group }} | ||
| runs-on: ${{ inputs.os }} | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| group: ${{ fromJson(inputs.groups) }} | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| - uses: dtolnay/rust-toolchain@stable | ||
| - uses: Swatinem/rust-cache@v2 | ||
| with: | ||
| shared-key: "test-${{ inputs.os }}-${{ matrix.group }}" | ||
| - run: pip install pyyaml | ||
| - name: Run tests | ||
| run: python .github/scripts/ci_config.py run-group ${{ matrix.group }} --os ${{ inputs.os }} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.