Repository navigation
Add workflow to trigger wheel releases #22656
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
Changes from all commits
Commits
Show all changes
96 commits
Select commit
Hold shift + click to select a range
073d7a8
Add workflow to trigger wheel releases
dkirov-dd 3cf8491
Fix code injection vulnerability in trigger workflow
dkirov-dd 1b513c1
Use dd-octo-sts for secure cross-repo authentication
dkirov-dd 5303ac1
Pin actions/github-script to specific commit SHA
dkirov-dd 8758b2a
Add pull_request trigger for testing
dkirov-dd 68eacef
Add id-token permission for OIDC authentication
dkirov-dd 93b4d93
Update dd-octo-sts policy name
dkirov-dd cdb39bf
Use different dd-octo-sts policies for PR vs production
dkirov-dd ddd1ea1
trigger
dkirov-dd 2ff5cf2
Remove master from pull_request trigger branches
dkirov-dd f6da664
Use workflow_dispatch for PR testing
dkirov-dd aaea6b2
Fix pull_request trigger for testing
dkirov-dd db5ecd6
Add dispatch-release workflow with chunking support
dkirov-dd e667643
Add chunking test with 4 integrations and batch size of 2
dkirov-dd 9bfcf3d
Rename workflow call
dkirov-dd b80c8b1
Rename redis to redisdb
dkirov-dd 31ffc91
Merge branch 'master' into dk/test-release-trigger
dkirov-dd 4b174ec
Use windows_performance_counters and ibm_mq as test integrations
dkirov-dd 05fe7da
Move logic to Python scripts
dkirov-dd ce43ee3
Improve logic separation
dkirov-dd 7a13da8
Rename trigger job
dkirov-dd 10efada
Add environment condition for workflow dispatch
dkirov-dd 70d8f05
Dispatch release even if nothing was tagged (use the HEAD tags)
dkirov-dd 9bffcc5
fix(release): fix integration glob path and validate unknown integrat…
dkirov-dd a477a91
feat(release): batch integration dispatches
dkirov-dd d643394
Hard code batch size
dkirov-dd a1ccdbd
Use dry-run mode for now
dkirov-dd 978281e
Add dry-run to trigger workflow
dkirov-dd c3eee73
Change dry-run description
dkirov-dd acae08d
Replace dry-run with target
dkirov-dd 8f22903
Merge branch 'master' into dk/test-release-trigger
dkirov-dd 7d62b99
Merge branch 'master' into dk/test-release-trigger
dkirov-dd fc4f016
Add validation step to dispatch
dkirov-dd 1e3235e
Use sys.exit instead of raising error
dkirov-dd 1072bf5
Simplify version checking
dkirov-dd d2856bb
feat(release): rename integration→package and add dry-run mode
dkirov-dd 5c42127
fix(release): restore dry-run input to trigger-release.yml
dkirov-dd b73e121
Add ddev-version input parameter
dkirov-dd fc16d12
TEST trigger dry-run release
dkirov-dd 3e2db32
Fix name detection
dkirov-dd 836f203
Add ddev configuration step
dkirov-dd 9654f11
Merge branch 'master' into dk/test-release-trigger
dkirov-dd 3f341ca
TEST Use test sts policy and delete datadog_checks_base changelog.d
dkirov-dd d50aec1
TEST Use dry-run mode for testing
dkirov-dd 51e8d5d
chore(release): improve detect_packages output verbosity
dkirov-dd fcc40ed
chore(release): improve dispatch output and add run tracking link
dkirov-dd 626ca7f
chore(release): improve validate_release output verbosity
dkirov-dd 6c7b6bf
Docs changes
dkirov-dd c787953
Add job summaries
dkirov-dd f920fde
chore(release): fix formatting issues and f-string inconsistencies
dkirov-dd 1ac9ff4
Prevent printing released integrations twice
dkirov-dd 86de6e7
EXPERIMENTAL rework summary format
dkirov-dd 5b70418
Revert TEST commits
dkirov-dd 118a5e8
refactor(release-scripts): modularize release pipeline scripts and ad…
dkirov-dd ced3eea
fix(release-scripts): address post-review issues
dkirov-dd 98cfd3c
test(release-scripts): trim redundant tests (63 → 48)
dkirov-dd 7c7ea05
fix(release-scripts): address review feedback
dkirov-dd 4874de3
TEST: add dry-run workflow triggered on pull_request
dkirov-dd 5b6460d
Revert "TEST: add dry-run workflow triggered on pull_request"
dkirov-dd 16f348a
fix(release-scripts): address review feedback
dkirov-dd 7b629f9
Update .github/workflows/test-release-scripts.yml
dkirov-dd 69534a0
Improve ddev-version input parsing
dkirov-dd 742aa8c
Remove conftest
dkirov-dd d95ef38
refactor(release-scripts): decouple dispatch functions from global co…
dkirov-dd 8408c89
refactor(release-scripts): clean up dispatch_in_batches and build_sum…
dkirov-dd b87b54b
feat(release-scripts): add is_stable_release dispatch context
dkirov-dd 52d0a1a
refactor(trigger): replace Python branch detection with shell allowlist
dkirov-dd b292c3c
refactor(dispatch): add _urlopen wrapper for mockable HTTP boundary
dkirov-dd ef3e999
test(release-scripts): improve test clarity and parametrize bool env …
dkirov-dd 7cdfb17
chore(workflows): pin pytest, set prod default, remove validated TODOs
dkirov-dd 25f9cf4
docs(workflows): add cross-repo comments to trigger and dispatch work…
dkirov-dd c737aa6
test(packages): assert exact mode strings; pin ddev to 14.3.2
dkirov-dd aeee132
docs(dispatch-release): clarify ddev-version input is pinned by default
dkirov-dd 634c9a7
test(release-scripts): add tests for entry-point scripts
dkirov-dd ea783e6
fix(release-scripts): write summary on stable-on-pre-release failure;…
dkirov-dd 6dc1bfb
refactor(_release/summary): redesign label logic and rename dispatche…
dkirov-dd 7e03c56
refactor(_release/summary): replace dict lookup with if-chain in _row…
dkirov-dd 4a28302
test(_release): strengthen assertions and cover ValueError path
dkirov-dd 50e0fb6
TEST test(workflows): add dry-run PR trigger for release pipeline smo…
dkirov-dd 6fb804d
Revert "TEST test(workflows): add dry-run PR trigger for release pipe…
dkirov-dd 4abf30b
Merge branch 'master' into dk/test-release-trigger
dkirov-dd 96a5a2f
test(_release): use full mock.call assertions instead of index-based …
dkirov-dd 552db17
Rename manual to selected
dkirov-dd a98174d
Use kwargs for resolve_pagages tests
dkirov-dd 5e6db36
test(_release): replace call_args access with full mock_calls assertions
dkirov-dd 5b19ea7
Fix test
dkirov-dd dd6dab4
fix(_release): address PR review findings for release trigger workflow
dkirov-dd 8bd382d
chore: remove .agint-review worktrees from index, add to .gitignore
dkirov-dd e2da099
Fix .gitignore to include root.json
dkirov-dd 9e692bc
chore(_release): rename workflow files to release-* prefix
dkirov-dd 9f68206
chore: remove .agint-review worktrees from index again
dkirov-dd 519d8d3
refactor(_release): merge tag/detect/validate into release_prepare.py
dkirov-dd 580963c
fix(_release): address PR review findings
dkirov-dd ea5bd72
refactor(_release): read dry_run from env var in release_dispatch.py
dkirov-dd b91002a
test(_release): add PR-based dry-run trigger for release dispatch
dkirov-dd f695050
Revert "test(_release): add PR-based dry-run trigger for release disp…
dkirov-dd 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,99 @@ | ||
| name: Dispatch Release | ||
|
|
||
| # Reusable workflow — intended to be called by a release-trigger workflow in an | ||
| # integration repository (integrations-core, integrations-extras, marketplace). | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| source-repo: | ||
| description: "Source repository name (e.g. integrations-core, integrations-extras, marketplace)" | ||
| required: false | ||
| type: string | ||
| packages: | ||
| description: >- | ||
| Packages to release. Accepts a JSON array (e.g. '["postgres","datadog_checks_base"]'), | ||
| 'all' to release every Python package in the repo, or omit to auto-detect from new tags. | ||
| required: false | ||
| type: string | ||
| source-repo-ref: | ||
| description: "Commit SHA or ref to build from" | ||
| required: false | ||
| type: string | ||
| target: | ||
| description: "Target environment (dev = no tag push, dev S3; prod = push tags, prod S3)" | ||
| required: false | ||
| type: string | ||
| default: prod # callers (e.g. release-trigger.yml) typically override this | ||
| dry-run: | ||
| description: >- | ||
| When true, print what would be released and where without pushing tags | ||
| or triggering downstream wheel builds. | ||
| required: false | ||
| type: boolean | ||
| default: false | ||
| ddev-version: | ||
| description: "ddev version, pinned by default." | ||
| required: false | ||
| type: string | ||
| is-stable-release: | ||
| description: "'true' for master/X.X.x (blocks pre-releases), 'false' for alpha/beta/rc (blocks stable). Unset defaults to stable behavior." | ||
| required: false | ||
| type: string | ||
|
|
||
| permissions: | ||
| id-token: write | ||
| contents: write # ddev needs to push tags | ||
|
|
||
| jobs: | ||
| dispatch: | ||
| name: Tag releases and dispatch wheel builds | ||
| runs-on: ubuntu-latest | ||
| environment: release | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| fetch-depth: 0 # ddev needs full tag history | ||
|
|
||
| - name: Install ddev | ||
| run: pip install "ddev${{ inputs.ddev-version && format('=={0}', inputs.ddev-version) || '==14.3.2' }}" | ||
|
|
||
| - name: Configure ddev | ||
| env: | ||
| SOURCE_REPO: ${{ inputs.source-repo || 'integrations-core' }} | ||
| run: | | ||
| REPO_SHORT="${SOURCE_REPO#integrations-}" | ||
| ddev config set upgrade_check false | ||
| ddev config set repos.${REPO_SHORT} . | ||
| ddev config set repo ${REPO_SHORT} | ||
|
|
||
| - name: Prepare dispatch | ||
| id: prepare | ||
| env: | ||
| TARGET: ${{ inputs.target }} | ||
| DRY_RUN: ${{ inputs.dry-run }} | ||
| SELECTED_PACKAGES: ${{ inputs.packages }} | ||
| SOURCE_REPO: ${{ inputs.source-repo || 'integrations-core' }} | ||
| REF: ${{ inputs.source-repo-ref || github.sha }} | ||
| IS_STABLE_RELEASE: ${{ inputs.is-stable-release }} | ||
| run: python .github/workflows/scripts/release_prepare.py | ||
|
|
||
| - name: Get GitHub token via dd-octo-sts | ||
| if: steps.prepare.outputs.has_packages == 'true' && !inputs.dry-run | ||
| id: octo-sts | ||
| uses: DataDog/dd-octo-sts-action@08f2144903ced3254a3dafec2592563409ba2aa0 # v1.0.1 | ||
| with: | ||
| scope: DataDog/agent-integration-wheels-release | ||
| policy: integrations-core.dispatch-wheel-builds | ||
|
|
||
| - name: Dispatch release | ||
| if: steps.prepare.outputs.has_packages == 'true' | ||
| env: | ||
| GH_TOKEN: ${{ steps.octo-sts.outputs.token }} | ||
| PACKAGES: ${{ steps.prepare.outputs.packages }} | ||
| SOURCE_REPO: ${{ inputs.source-repo || 'integrations-core' }} | ||
| REF: ${{ inputs.source-repo-ref || github.sha }} | ||
| TARGET: ${{ inputs.target }} | ||
| DRY_RUN: ${{ inputs.dry-run }} | ||
| run: python .github/workflows/scripts/release_dispatch.py |
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,79 @@ | ||
| name: Trigger Wheel Builds | ||
|
|
||
| # Thin wrapper — all tagging and dispatch logic lives in release-dispatch.yml. | ||
| # The pipeline can build wheels for any Python package in the repo. | ||
| # Equivalent trigger workflows exist in integrations-extras and marketplace. | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - master | ||
| - "[0-9]+.[0-9]+.x" | ||
| - "alpha/*" | ||
| - "beta/*" | ||
| - "rc/*" | ||
| paths: | ||
| - "*/CHANGELOG.md" | ||
| - "*/datadog_checks/*/__about__.py" | ||
| workflow_dispatch: | ||
| inputs: | ||
| packages: | ||
| description: >- | ||
| Packages to release. JSON array (e.g. '["postgres","datadog_checks_base"]'), | ||
| 'all' to release every Python package in the repo, or omit to auto-detect from new tags. | ||
| required: false | ||
| type: string | ||
| source-repo-ref: | ||
| description: "Commit SHA or ref to build from" | ||
| required: true | ||
| type: string | ||
| target: | ||
| description: "Target environment (dev or prod)" | ||
| required: false | ||
| type: choice | ||
| options: | ||
| - dev | ||
| - prod | ||
| default: dev | ||
| dry-run: | ||
| description: "Print what would be released without pushing tags or starting builds" | ||
| required: false | ||
| type: boolean | ||
| default: false | ||
| ddev-version: | ||
| description: "ddev version, pinned by default." | ||
| required: false | ||
| type: string | ||
|
|
||
| jobs: | ||
| context: | ||
| name: Detect release context | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| is-stable-release: ${{ steps.detect.outputs.is-stable-release }} | ||
| steps: | ||
| - id: detect | ||
| # Sets is-stable-release based on the branch: true for master/X.Y.x, false otherwise. | ||
| # Manual runs on master get "true", which blocks pre-release packages — conservative and intentional. | ||
| run: | | ||
| if [[ "$GITHUB_REF" =~ ^refs/heads/(master|[0-9]+\.[0-9]+\.x)$ ]]; then | ||
| echo "is-stable-release=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "is-stable-release=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| dispatch: | ||
| name: Release | ||
| needs: context | ||
| uses: ./.github/workflows/release-dispatch.yml | ||
| with: | ||
| source-repo: integrations-core | ||
| packages: ${{ inputs.packages || '' }} | ||
| source-repo-ref: ${{ github.event_name == 'workflow_dispatch' && inputs.source-repo-ref || github.sha }} | ||
| target: ${{ inputs.target || 'dev' }} | ||
| dry-run: ${{ inputs.dry-run || false }} | ||
| ddev-version: ${{ inputs.ddev-version || '' }} | ||
| is-stable-release: ${{ needs.context.outputs.is-stable-release }} | ||
| permissions: | ||
| id-token: write | ||
| contents: write |
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 @@ | ||
| TARGET_REPO = "DataDog/agent-integration-wheels-release" |
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,102 @@ | ||
| """HTTP dispatch logic for repository_dispatch events.""" | ||
| import http.client | ||
| import json | ||
| import ssl | ||
| import sys | ||
| import time | ||
| import urllib.error | ||
| import urllib.request | ||
|
|
||
| from . import TARGET_REPO | ||
|
|
||
| BATCH_SIZE = 200 | ||
| DISPATCH_URL = f"https://api.github.com/repos/{TARGET_REPO}/dispatches" | ||
| MAX_ATTEMPTS = 5 | ||
|
|
||
|
|
||
| class DispatchError(Exception): | ||
| """Raised when a dispatch request fails after all retry attempts.""" | ||
|
|
||
|
|
||
| def build_payload(batch: list[str], source_repo: str, ref: str, target: str) -> dict: | ||
| """Return the ``repository_dispatch`` payload for one batch.""" | ||
| return { | ||
| "event_type": "build-wheels", | ||
| "client_payload": { | ||
| "packages": batch, | ||
| "source_repo": source_repo, | ||
| "source_repo_ref": ref, | ||
| "target": target, | ||
| }, | ||
| } | ||
|
|
||
|
|
||
| def _urlopen(req: urllib.request.Request) -> http.client.HTTPResponse: | ||
| """Thin urllib wrapper — exists so tests can patch it without touching stdlib.""" | ||
| return urllib.request.urlopen(req) | ||
|
|
||
|
|
||
| def send_dispatch( | ||
| payload: dict, | ||
| token: str, | ||
| *, | ||
| dispatch_url: str = DISPATCH_URL, | ||
| max_attempts: int = MAX_ATTEMPTS, | ||
| ) -> None: | ||
| """POST a single ``repository_dispatch`` event to the wheels-release repo. | ||
|
|
||
| Retries up to ``max_attempts`` times on 5xx errors with exponential backoff. | ||
| Raises ``DispatchError`` on 4xx errors or after exhausting retries. | ||
| """ | ||
| req = urllib.request.Request( | ||
| dispatch_url, | ||
| data=json.dumps(payload).encode(), | ||
| headers={ | ||
| "Authorization": f"Bearer {token}", | ||
| "Accept": "application/vnd.github+json", | ||
| "X-GitHub-Api-Version": "2022-11-28", | ||
| }, | ||
| method="POST", | ||
| ) | ||
| for attempt in range(1, max_attempts + 1): | ||
| try: | ||
| with _urlopen(req) as resp: | ||
| print(f" Dispatched: HTTP {resp.status}") | ||
| return | ||
| except (urllib.error.HTTPError, urllib.error.URLError) as e: | ||
| if isinstance(e, urllib.error.URLError): | ||
| if isinstance(e.reason, ssl.SSLError): | ||
| raise DispatchError(f"SSL error (non-retriable): {e}") from e | ||
| body = str(e) | ||
| code = 503 | ||
| else: | ||
| body = e.read().decode() | ||
| code = e.code | ||
| if code < 500 or attempt == max_attempts: | ||
| print(f"HTTP {code}: {body}", file=sys.stderr) | ||
| raise DispatchError(f"HTTP {code}: {body}") | ||
| print(f" HTTP {code} on attempt {attempt}/{max_attempts}, retrying...", file=sys.stderr) | ||
| time.sleep(2**attempt) | ||
|
|
||
|
|
||
| def dispatch_in_batches( | ||
| packages: list[str], | ||
| source_repo: str, | ||
| ref: str, | ||
| target: str, | ||
| token: str, | ||
| batch_size: int = BATCH_SIZE, | ||
| ) -> None: | ||
| """Dispatch all packages to the wheels-release repo, batching if needed.""" | ||
| if batch_size <= 0: | ||
| raise ValueError("batch_size must be > 0") | ||
| num_packages = len(packages) | ||
| if num_packages == 0: | ||
| return | ||
| total_batches = (num_packages + batch_size - 1) // batch_size | ||
| for batch_num, start in enumerate(range(0, num_packages, batch_size), 1): | ||
| end = min(start + batch_size, num_packages) | ||
| current_batch = packages[start:end] | ||
| print(f"\nBatch {batch_num}/{total_batches}:") | ||
| print("\n".join(f" - {name}" for name in current_batch)) | ||
| send_dispatch(build_payload(current_batch, source_repo, ref, target), token) |
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,36 @@ | ||
| """GitHub Actions I/O helpers.""" | ||
| import os | ||
| from pathlib import Path | ||
|
|
||
|
|
||
| def set_outputs(**kwargs: str) -> None: | ||
| """Write key=value pairs to GITHUB_OUTPUT.""" | ||
| path = os.environ.get("GITHUB_OUTPUT") | ||
| if path: | ||
| with Path(path).open("a") as f: | ||
| for key, value in kwargs.items(): | ||
| f.write(f"{key}={value}\n") | ||
|
|
||
|
|
||
| def write_summary(content: str) -> None: | ||
| """Append markdown content to GITHUB_STEP_SUMMARY.""" | ||
| path = os.environ.get("GITHUB_STEP_SUMMARY") | ||
| if path: | ||
| with Path(path).open("a") as f: | ||
| f.write(content + "\n") | ||
|
|
||
|
|
||
| def parse_bool_env(name: str, default: bool = False) -> bool: | ||
| """Parse a boolean environment variable. | ||
|
|
||
| Accepts 'true'/'1'/'yes' as True and 'false'/'0'/'no' as False (case-insensitive). | ||
| Returns ``default`` when the variable is absent or empty. | ||
| """ | ||
| val = os.environ.get(name, "").strip().lower() | ||
| if not val: | ||
| return default | ||
| if val in ("true", "1", "yes"): | ||
| return True | ||
| if val in ("false", "0", "no"): | ||
| return False | ||
| return default |
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,69 @@ | ||
| """Package detection and resolution logic.""" | ||
| import json | ||
| import re | ||
| import subprocess | ||
| from pathlib import Path | ||
|
|
||
| _VERSION_SUFFIX_RE = re.compile(r"-\d+\.\d+\.\d+.*$") | ||
|
|
||
|
|
||
| def get_all_packages(root: Path = Path(".")) -> list[str]: | ||
| """Return sorted list of all Python packages found under *root*.""" | ||
| return sorted( | ||
| {p.parent.parent.parent.name for p in root.glob("*/datadog_checks/*/__about__.py")} | ||
| ) | ||
|
|
||
|
|
||
| def get_tags_at_head() -> list[str]: | ||
| """Return the list of git tags pointing at HEAD.""" | ||
| try: | ||
| return subprocess.check_output(["git", "tag", "--points-at", "HEAD"], text=True).splitlines() | ||
| except subprocess.CalledProcessError as e: | ||
| raise RuntimeError(f"Failed to get git tags at HEAD: {e}") from e | ||
|
|
||
|
|
||
| def detect_from_tags(tags: list[str]) -> list[str]: | ||
| """Return sorted unique package names extracted from release tags. | ||
|
|
||
| Tags are expected in the form ``<package>-X.Y.Z[...]``. | ||
| The version suffix is stripped to recover the package name. | ||
| """ | ||
| return sorted({_VERSION_SUFFIX_RE.sub("", t) for t in tags if t.strip()}) | ||
|
|
||
|
|
||
| def resolve_packages( | ||
| selected: str, | ||
| all_packages: list[str], | ||
| head_tags: list[str] | None = None, | ||
| ) -> tuple[list[str], str]: | ||
| """Resolve the list of packages to release. | ||
|
|
||
| Resolution order: | ||
| - ``'all'`` / ``'ALL'`` → every package in the repo | ||
| - JSON array → use the provided list verbatim | ||
| - empty string → auto-detect from git tags at HEAD | ||
|
|
||
| Returns ``(packages, mode_description)``. | ||
| Raises ``ValueError`` on invalid input or unknown package names. | ||
| """ | ||
| selected = selected.strip() | ||
|
|
||
| if selected.lower() == "all": | ||
| return all_packages, f"all ({len(all_packages)} packages in repo)" | ||
|
|
||
| if selected: | ||
| try: | ||
| packages = json.loads(selected) | ||
| except json.JSONDecodeError as e: | ||
| raise ValueError(f"SELECTED_PACKAGES is not valid JSON: {e}") from e | ||
| mode = f"manual ({selected})" | ||
| else: | ||
| tags = head_tags if head_tags is not None else get_tags_at_head() | ||
| packages = detect_from_tags(tags) | ||
| mode = "auto-detect from tags at HEAD" | ||
|
|
||
| unknown = sorted(set(packages) - set(all_packages)) | ||
| if unknown: | ||
| raise ValueError(f"Unknown packages: {', '.join(unknown)}") | ||
|
|
||
| return packages, mode |
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.