diff --git a/.github/actions/release-branches/action.yml b/.github/actions/release-branches/action.yml index 26be726205..7734411c73 100644 --- a/.github/actions/release-branches/action.yml +++ b/.github/actions/release-branches/action.yml @@ -22,7 +22,6 @@ runs: MAJOR_VERSION: ${{ inputs.major_version }} LATEST_TAG: ${{ inputs.latest_tag }} run: | - npm ci npx tsx ./pr-checks/release-branches.ts \ --major-version "$MAJOR_VERSION" \ --latest-tag "$LATEST_TAG" diff --git a/.github/actions/release-initialise/action.yml b/.github/actions/release-initialise/action.yml index 057d5a5b6d..239dfa9428 100644 --- a/.github/actions/release-initialise/action.yml +++ b/.github/actions/release-initialise/action.yml @@ -21,16 +21,9 @@ runs: node-version: 24 cache: 'npm' - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install PyGithub==2.3.0 requests + - name: Install JavaScript dependencies shell: bash + run: npm ci - name: Update git config run: | diff --git a/.github/update-release-branch.py b/.github/update-release-branch.py deleted file mode 100644 index 2635126827..0000000000 --- a/.github/update-release-branch.py +++ /dev/null @@ -1,474 +0,0 @@ -import argparse -import datetime -import fileinput -import re -from github import Github -import json -import os -import subprocess - -EMPTY_CHANGELOG = """# CodeQL Action Changelog - -## [UNRELEASED] - -No user facing changes. - -""" - -# NB: This exact commit message is used to find commits for reverting during backports. -# Changing it requires a transition period where both old and new versions are supported. -BACKPORT_COMMIT_MESSAGE = 'Update version and changelog for v' - -# Commit message used for rebuild commits, both those produced by this script and those produced -# by the `Rebuild Action` workflow (`.github/workflows/rebuild.yml`). -REBUILD_COMMIT_MESSAGE = 'Rebuild' - -# Name of the remote -ORIGIN = 'origin' - -# Environment variables to check for a GitHub API token. -TOKEN_ENVIRONMENT_VARIABLES = ('GH_TOKEN', 'GITHUB_TOKEN') - -# Gets a GitHub API token from one of the supported environment variables. -def get_github_token(): - for variable_name in TOKEN_ENVIRONMENT_VARIABLES: - token = os.environ.get(variable_name, '').strip() - if token: - return token - raise Exception('Missing GitHub token. Set GITHUB_TOKEN or GH_TOKEN.') - -# Runs git with the given args and returns the stdout. -# Raises an error if git does not exit successfully (unless passed -# allow_non_zero_exit_code=True). -def run_git(*args, allow_non_zero_exit_code=False): - cmd = ['git', *args] - p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if not allow_non_zero_exit_code and p.returncode != 0: - raise Exception(f'Call to {" ".join(cmd)} exited with code {p.returncode} stderr: {p.stderr.decode("ascii")}.') - return p.stdout.decode('ascii') - -# Runs the given command, streaming output to the console. -# Raises an error if the command does not exit successfully. -def run_command(*args): - cmd = list(args) - print(f'Running `{" ".join(cmd)}`.') - subprocess.run(cmd, check=True) - -# Rebuilds the action and commits any changes. -def rebuild_action(): - # For backports, the only source-level change vs the source branch is the new version number, - # so we just need to refresh the version embedded in `lib/`. - run_command('npm', 'ci') - run_command('npm', 'run', 'build') - - run_git('add', '--all') - # `git diff --cached --quiet` exits 0 if there are no staged changes, 1 if there are. - if subprocess.run(['git', 'diff', '--cached', '--quiet']).returncode == 0: - print('Rebuild produced no changes; skipping Rebuild commit.') - else: - run_git('commit', '-m', REBUILD_COMMIT_MESSAGE) - print('Created Rebuild commit.') - -# Returns true if the given branch exists on the origin remote -def branch_exists_on_remote(branch_name): - return run_git('ls-remote', '--heads', ORIGIN, branch_name).strip() != '' - -# Opens a PR from the given branch to the target branch -def open_pr( - repo, all_commits, source_branch_short_sha, new_branch_name, source_branch, target_branch, - conductor, is_primary_release, conflicted_files): - # Sort the commits into the pull requests that introduced them, - # and any commits that don't have a pull request - pull_requests = [] - commits_without_pull_requests = [] - for commit in all_commits: - pr = get_pr_for_commit(commit) - - if pr is None: - commits_without_pull_requests.append(commit) - elif not any(p for p in pull_requests if p.number == pr.number): - pull_requests.append(pr) - - print(f'Found {len(pull_requests)} pull requests.') - print(f'Found {len(commits_without_pull_requests)} commits not in a pull request.') - - # Sort PRs and commits by age - pull_requests = sorted(pull_requests, key=lambda pr: pr.number) - commits_without_pull_requests = sorted(commits_without_pull_requests, key=lambda c: c.commit.author.date) - - # Start constructing the body text - body = [] - body.append(f'Merging {source_branch_short_sha} into `{target_branch}`.') - - body.append('') - body.append(f'Conductor for this PR is @{conductor}.') - - # List all PRs merged - if len(pull_requests) > 0: - body.append('') - body.append('Contains the following pull requests:') - for pr in pull_requests: - # Use PR author if they are GitHub staff, otherwise use the merger - display_user = get_pr_author_if_staff(pr) or get_merger_of_pr(repo, pr) - body.append(f'- #{pr.number} (@{display_user})') - - # List all commits not part of a PR - if len(commits_without_pull_requests) > 0: - body.append('') - body.append('Contains the following commits not from a pull request:') - for commit in commits_without_pull_requests: - author_description = f' (@{commit.author.login})' if commit.author is not None else '' - body.append(f'- {commit.sha} - {get_truncated_commit_message(commit)}{author_description}') - - body.append('') - body.append('Please do the following:') - if len(conflicted_files) > 0: - body.append(' - [ ] Ensure `package.json` file contains the correct version.') - body.append(' - [ ] Add a commit to this branch to resolve the merge conflicts ' + - 'in the following files:') - body.extend([f' - `{file}`' for file in conflicted_files]) - body.append(' - [ ] Rebuild the Action locally (`npm run build`) and push any changes to the ' + - f'built output in `lib` as a separate commit named exactly `{REBUILD_COMMIT_MESSAGE}`.') - body.append(' - [ ] Ensure another maintainer has reviewed the additional commits you added to this ' + - 'branch to resolve the merge conflicts.') - body.append(' - [ ] Ensure the CHANGELOG displays the correct version and date.') - body.append(' - [ ] Ensure the CHANGELOG includes all relevant, user-facing changes since the last release.') - body.append(f' - [ ] Check that there are not any unexpected commits being merged into the `{target_branch}` branch.') - body.append(' - [ ] Ensure the docs team is aware of any documentation changes that need to be released.') - - body.append(' - [ ] Approve running the full set of PR checks if you have not pushed any changes.') - body.append(' - [ ] Approve and merge this PR. Make sure `Create a merge commit` is selected rather than `Squash and merge` or `Rebase and merge`.') - - if is_primary_release: - body.append(' - [ ] Merge the mergeback PR that will automatically be created once this PR is merged.') - body.append(' - [ ] Merge all backport PRs to older release branches, that will automatically be created once this PR is merged.') - - title = f'Merge {source_branch} into {target_branch}' - - # Create the pull request - pr = repo.create_pull(title=title, body='\n'.join(body), head=new_branch_name, base=target_branch) - print(f'Created PR #{str(pr.number)}') - - # Assign the conductor - pr.add_to_assignees(conductor) - print(f'Assigned PR to {conductor}') - -# Gets a list of the SHAs of all commits that have happened on the source branch -# since the last release to the target branch. -# This will not include any commits that exist on the target branch -# that aren't on the source branch. -def get_commit_difference(repo, source_branch, target_branch): - # Passing split nothing means that the empty string splits to nothing: compare `''.split() == []` - # to `''.split('\n') == ['']`. - commits = run_git('log', '--pretty=format:%H', f'{ORIGIN}/{target_branch}..{ORIGIN}/{source_branch}').strip().split() - - # Convert to full-fledged commit objects - commits = [repo.get_commit(c) for c in commits] - - # Filter out merge commits for PRs - return list(filter(lambda c: not is_pr_merge_commit(c), commits)) - -# Is the given commit the automatic merge commit from when merging a PR -def is_pr_merge_commit(commit): - return commit.committer is not None and commit.committer.login == 'web-flow' and len(commit.parents) > 1 - -# Gets a copy of the commit message that should display nicely -def get_truncated_commit_message(commit): - message = commit.commit.message.split('\n')[0] - if len(message) > 60: - return f'{message[:57]}...' - else: - return message - -# Converts a commit into the PR that introduced it to the source branch. -# Returns the PR object, or None if no PR could be found. -def get_pr_for_commit(commit): - prs = commit.get_pulls() - - if prs.totalCount > 0: - # In the case that there are multiple PRs, return the earliest one - prs = list(prs) - sorted_prs = sorted(prs, key=lambda pr: int(pr.number)) - return sorted_prs[0] - else: - return None - -# Get the person who merged the pull request. -# For most cases this will be the same as the author, but for PRs opened -# by external contributors getting the merger will get us the GitHub -# employee who reviewed and merged the PR. -def get_merger_of_pr(repo, pr): - return repo.get_commit(pr.merge_commit_sha).author.login - -# Get the PR author if they are GitHub staff, otherwise None. -def get_pr_author_if_staff(pr): - if pr.user is None: - return None - if getattr(pr.user, 'site_admin', False): - return pr.user.login - return None - -def get_current_version(): - with open('package.json', 'r') as f: - return json.load(f)['version'] - -# `npm version` doesn't always work because of merge conflicts, so we -# replace the version in package.json textually. -def replace_version_package_json(prev_version, new_version): - prev_line_is_codeql = False - for line in fileinput.input('package.json', inplace = True, encoding='utf-8'): - if prev_line_is_codeql and f'\"version\": \"{prev_version}\"' in line: - print(line.replace(prev_version, new_version), end='') - else: - prev_line_is_codeql = False - print(line, end='') - if '\"name\": \"codeql\",' in line: - prev_line_is_codeql = True - -def get_today_string(): - today = datetime.datetime.today() - return '{:%d %b %Y}'.format(today) - -def process_changelog_for_backports(source_branch_major_version, target_branch_major_version): - - # changelog entries can use the following format to indicate - # that they only apply to newer versions - some_versions_only_regex = re.compile(r'\[v(\d+)\+ only\]') - - output = '' - - with open('CHANGELOG.md', 'r') as f: - - # until we find the first section, just duplicate all lines - found_first_section = False - while not found_first_section: - line = f.readline() - if not line: - raise Exception('Could not find any change sections in CHANGELOG.md') # EOF - - if line.startswith('## '): - line = line.replace(f'## {source_branch_major_version}', f'## {target_branch_major_version}') - found_first_section = True - - output += line - - # found_content tracks whether we hit two headings in a row - found_content = False - output += '\n' - while True: - line = f.readline() - if not line: - break # EOF - line = line.rstrip('\n') - - # filter out changenote entries that apply only to newer versions - match = some_versions_only_regex.search(line) - if match: - if int(target_branch_major_version) < int(match.group(1)): - continue - - if line.startswith('## '): - line = line.replace(f'## {source_branch_major_version}', f'## {target_branch_major_version}') - if found_content == False: - # we have found two headings in a row, so we need to add the placeholder message. - output += 'No user facing changes.\n' - found_content = False - output += f'\n{line}\n\n' - else: - if line.strip() != '': - found_content = True - # we use the original line here, rather than the stripped version - # so that we preserve indentation - output += line + '\n' - - with open('CHANGELOG.md', 'w') as f: - f.write(output) - -def update_changelog(version): - if (os.path.exists('CHANGELOG.md')): - content = '' - with open('CHANGELOG.md', 'r') as f: - content = f.read() - else: - content = EMPTY_CHANGELOG - - newContent = content.replace('[UNRELEASED]', f'{version} - {get_today_string()}', 1) - - with open('CHANGELOG.md', 'w') as f: - f.write(newContent) - - -def main(): - parser = argparse.ArgumentParser('update-release-branch.py') - - parser.add_argument( - '--repository-nwo', - type=str, - required=True, - help='The nwo of the repository, for example github/codeql-action.' - ) - parser.add_argument( - '--source-branch', - type=str, - required=True, - help='Source branch for release branch update.' - ) - parser.add_argument( - '--target-branch', - type=str, - required=True, - help='Target branch for release branch update.' - ) - parser.add_argument( - '--is-primary-release', - action='store_true', - default=False, - help='Whether this update is the primary release for the current major version.' - ) - parser.add_argument( - '--conductor', - type=str, - required=True, - help='The GitHub handle of the person who is conducting the release process.' - ) - - args = parser.parse_args() - - source_branch = args.source_branch - target_branch = args.target_branch - is_primary_release = args.is_primary_release - - repo = Github(get_github_token()).get_repo(args.repository_nwo) - - # the target branch will be of the form releases/vN, where N is the major version number - target_branch_major_version = target_branch.strip('releases/v') - - # split version into major, minor, patch - _, v_minor, v_patch = get_current_version().split('.') - - version = f"{target_branch_major_version}.{v_minor}.{v_patch}" - - # Print what we intend to go - print(f'Considering difference between {source_branch} and {target_branch}...') - source_branch_short_sha = run_git('rev-parse', '--short', f'{ORIGIN}/{source_branch}').strip() - print(f'Current head of {source_branch} is {source_branch_short_sha}.') - - # See if there are any commits to merge in - commits = get_commit_difference(repo=repo, source_branch=source_branch, target_branch=target_branch) - if len(commits) == 0: - print(f'No commits to merge from {source_branch} to {target_branch}.') - return - - # define distinct prefix in order to support specific pr checks on backports - branch_prefix = 'update' if is_primary_release else 'backport' - - # The branch name is based off of the name of branch being merged into - # and the SHA of the branch being merged from. Thus if the branch already - # exists we can assume we don't need to recreate it. - new_branch_name = f'{branch_prefix}-v{version}-{source_branch_short_sha}' - print(f'Branch name is {new_branch_name}.') - - # Check if the branch already exists. If so we can abort as this script - # has already run on this combination of branches. - if branch_exists_on_remote(new_branch_name): - print(f'Branch {new_branch_name} already exists. Nothing to do.') - return - - # Create the new branch and push it to the remote - print(f'Creating branch {new_branch_name}.') - - # The process of creating the v{Older} release can run into merge conflicts. We commit the unresolved - # conflicts so a maintainer can easily resolve them (vs erroring and requiring maintainers to - # reconstruct the release manually) - conflicted_files = [] - - if not is_primary_release: - - # the source branch will be of the form releases/vN, where N is the major version number - source_branch_major_version = source_branch.strip('releases/v') - - # If we're performing a backport, start from the target branch - print(f'Creating {new_branch_name} from the {ORIGIN}/{target_branch} branch') - run_git('checkout', '-b', new_branch_name, f'{ORIGIN}/{target_branch}') - - # Revert the commit that we made as part of the last release that updated the version number and - # changelog to refer to {older}.x.x variants. This avoids merge conflicts in the changelog and - # package.json files when we merge in the v{latest} branch. - # This commit will not exist the first time we release the v{N-1} branch from the v{N} branch, so we - # use `git log --grep` to conditionally revert the commit. - print('Reverting the version number and changelog updates from the last release to avoid conflicts') - vOlder_update_commits = run_git('log', '--grep', f'^{BACKPORT_COMMIT_MESSAGE}', '--format=%H').split() - - if len(vOlder_update_commits) > 0: - print(f' Reverting {vOlder_update_commits[0]}') - # Only revert the newest commit as older ones will already have been reverted in previous - # releases. - run_git('revert', vOlder_update_commits[0], '--no-edit') - - # Also revert the "Rebuild" commit, whether created by this script or by the - # `Rebuild Action` workflow. - rebuild_commit = run_git('log', '--grep', f'^{REBUILD_COMMIT_MESSAGE}$', '--format=%H').split()[0] - print(f' Reverting {rebuild_commit}') - run_git('revert', rebuild_commit, '--no-edit') - - else: - print(' Nothing to revert.') - - print(f'Merging {ORIGIN}/{source_branch} into the release prep branch') - # Commit any conflicts (see the comment for `conflicted_files`) - run_git('merge', f'{ORIGIN}/{source_branch}', allow_non_zero_exit_code=True) - conflicted_files = run_git('diff', '--name-only', '--diff-filter', 'U').splitlines() - if len(conflicted_files) > 0: - run_git('add', '.') - run_git('commit', '--no-edit') - - # Migrate the package version number from a vLatest version number to a vOlder version number. - # `package-lock.json` is updated as part of the subsequent rebuild step (see `rebuild_action`). - print(f'Setting version number to {version} in package.json') - replace_version_package_json(get_current_version(), version) - run_git('add', 'package.json') - - # Migrate the changelog notes from vLatest version numbers to vOlder version numbers - print(f'Migrating changelog notes from v{source_branch_major_version} to v{target_branch_major_version}') - process_changelog_for_backports(source_branch_major_version, target_branch_major_version) - - # Amend the commit generated by `npm version` to update the CHANGELOG - run_git('add', 'CHANGELOG.md') - run_git('commit', '-m', f'{BACKPORT_COMMIT_MESSAGE}{version}') - else: - # If we're performing a standard release, there won't be any new commits on the target branch, - # as these will have already been merged back into the source branch. Therefore we can just - # start from the source branch. - run_git('checkout', '-b', new_branch_name, f'{ORIGIN}/{source_branch}') - - print('Updating changelog') - update_changelog(version) - - # Create a commit that updates the CHANGELOG - run_git('add', 'CHANGELOG.md') - run_git('commit', '-m', f'Update changelog for v{version}') - - if not is_primary_release: - if len(conflicted_files) == 0: - print('Rebuilding the Action.') - rebuild_action() - else: - print(f'Skipping automatic rebuild because the merge produced conflicts in {conflicted_files}.') - - run_git('push', ORIGIN, new_branch_name) - - # Open a PR to update the branch - open_pr( - repo, - commits, - source_branch_short_sha, - new_branch_name, - source_branch=source_branch, - target_branch=target_branch, - conductor=args.conductor, - is_primary_release=is_primary_release, - conflicted_files=conflicted_files - ) - -if __name__ == '__main__': - main() diff --git a/.github/workflows/__all-platform-bundle.yml b/.github/workflows/__all-platform-bundle.yml index a33104efa7..c3cf8d63f3 100644 --- a/.github/workflows/__all-platform-bundle.yml +++ b/.github/workflows/__all-platform-bundle.yml @@ -69,13 +69,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__analysis-kinds.yml b/.github/workflows/__analysis-kinds.yml index 53c8834eea..5d0576e2f6 100644 --- a/.github/workflows/__analysis-kinds.yml +++ b/.github/workflows/__analysis-kinds.yml @@ -67,7 +67,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__analyze-ref-input.yml b/.github/workflows/__analyze-ref-input.yml index 00c12a8e47..7341a41740 100644 --- a/.github/workflows/__analyze-ref-input.yml +++ b/.github/workflows/__analyze-ref-input.yml @@ -65,13 +65,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__autobuild-action.yml b/.github/workflows/__autobuild-action.yml index e79c4994c4..730a387f90 100644 --- a/.github/workflows/__autobuild-action.yml +++ b/.github/workflows/__autobuild-action.yml @@ -59,9 +59,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Prepare test diff --git a/.github/workflows/__autobuild-direct-tracing-with-working-dir.yml b/.github/workflows/__autobuild-direct-tracing-with-working-dir.yml index 2b771914e6..b527638feb 100644 --- a/.github/workflows/__autobuild-direct-tracing-with-working-dir.yml +++ b/.github/workflows/__autobuild-direct-tracing-with-working-dir.yml @@ -61,9 +61,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Java - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 with: java-version: ${{ inputs.java-version || '17' }} distribution: temurin diff --git a/.github/workflows/__autobuild-working-dir.yml b/.github/workflows/__autobuild-working-dir.yml index 71dd9d1df8..fac4ef9f54 100644 --- a/.github/workflows/__autobuild-working-dir.yml +++ b/.github/workflows/__autobuild-working-dir.yml @@ -45,7 +45,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__build-mode-autobuild.yml b/.github/workflows/__build-mode-autobuild.yml index 2ba8ca76dc..5043433ee3 100644 --- a/.github/workflows/__build-mode-autobuild.yml +++ b/.github/workflows/__build-mode-autobuild.yml @@ -61,9 +61,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Java - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 with: java-version: ${{ inputs.java-version || '17' }} distribution: temurin diff --git a/.github/workflows/__build-mode-manual.yml b/.github/workflows/__build-mode-manual.yml index 0d1f57b8a2..bfe92c55ea 100644 --- a/.github/workflows/__build-mode-manual.yml +++ b/.github/workflows/__build-mode-manual.yml @@ -65,13 +65,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__build-mode-none.yml b/.github/workflows/__build-mode-none.yml index dc97aa3d99..da7aa76383 100644 --- a/.github/workflows/__build-mode-none.yml +++ b/.github/workflows/__build-mode-none.yml @@ -47,7 +47,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__build-mode-rollback.yml b/.github/workflows/__build-mode-rollback.yml index 4383024f3b..fcc77ea36e 100644 --- a/.github/workflows/__build-mode-rollback.yml +++ b/.github/workflows/__build-mode-rollback.yml @@ -45,7 +45,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__bundle-from-nightly.yml b/.github/workflows/__bundle-from-nightly.yml index 9ccb507866..6c414fb67e 100644 --- a/.github/workflows/__bundle-from-nightly.yml +++ b/.github/workflows/__bundle-from-nightly.yml @@ -45,7 +45,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__bundle-from-toolcache.yml b/.github/workflows/__bundle-from-toolcache.yml index 036262395e..a1c1fade09 100644 --- a/.github/workflows/__bundle-from-toolcache.yml +++ b/.github/workflows/__bundle-from-toolcache.yml @@ -45,7 +45,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__bundle-toolcache.yml b/.github/workflows/__bundle-toolcache.yml index 0bdfd45082..9cc983a843 100644 --- a/.github/workflows/__bundle-toolcache.yml +++ b/.github/workflows/__bundle-toolcache.yml @@ -49,7 +49,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__bundle-zstd.yml b/.github/workflows/__bundle-zstd.yml deleted file mode 100644 index 7c1f89cfbd..0000000000 --- a/.github/workflows/__bundle-zstd.yml +++ /dev/null @@ -1,120 +0,0 @@ -# Warning: This file is generated automatically, and should not be modified. -# Instead, please modify the template in the pr-checks directory and run: -# pr-checks/sync.sh -# to regenerate this file. - -name: 'PR Check - Bundle: Zstandard checks' -env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GO111MODULE: auto -on: - push: - branches: - - main - - releases/v* - pull_request: {} - merge_group: - types: - - checks_requested - schedule: - - cron: '0 5 * * *' - workflow_dispatch: - inputs: {} - workflow_call: - inputs: {} -defaults: - run: - shell: bash -concurrency: - cancel-in-progress: ${{ github.event_name == 'pull_request' || false }} - group: bundle-zstd-${{github.ref}} -jobs: - bundle-zstd: - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-latest - version: linked - - os: macos-latest - version: linked - - os: windows-latest - version: linked - name: 'Bundle: Zstandard checks' - if: github.triggering_actor != 'dependabot[bot]' - permissions: - contents: read - security-events: read - timeout-minutes: 45 - runs-on: ${{ matrix.os }} - steps: - - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - name: Prepare test - id: prepare-test - uses: ./.github/actions/prepare-test - with: - version: ${{ matrix.version }} - use-all-platform-bundle: 'false' - setup-kotlin: 'true' - - name: Remove CodeQL from toolcache - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const fs = require('fs'); - const path = require('path'); - const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL'); - if (codeqlPath !== undefined) { - fs.rmdirSync(codeqlPath, { recursive: true }); - } - - id: init - uses: ./../action/init - with: - languages: javascript - tools: ${{ steps.prepare-test.outputs.tools-url }} - - uses: ./../action/analyze - with: - output: ${{ runner.temp }}/results - upload-database: false - - name: Upload SARIF - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: ${{ matrix.os }}-zstd-bundle.sarif - path: ${{ runner.temp }}/results/javascript.sarif - retention-days: 7 - - name: Check diagnostic with expected tools URL appears in SARIF - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - SARIF_PATH: ${{ runner.temp }}/results/javascript.sarif - with: - script: | - const fs = require('fs'); - - const sarif = JSON.parse(fs.readFileSync(process.env['SARIF_PATH'], 'utf8')); - const run = sarif.runs[0]; - - const toolExecutionNotifications = run.invocations[0].toolExecutionNotifications; - const downloadTelemetryNotifications = toolExecutionNotifications.filter(n => - n.descriptor.id === 'codeql-action/bundle-download-telemetry' - ); - if (downloadTelemetryNotifications.length !== 1) { - core.setFailed( - 'Expected exactly one reporting descriptor in the ' + - `'runs[].invocations[].toolExecutionNotifications[]' SARIF property, but found ` + - `${downloadTelemetryNotifications.length}. All notification reporting descriptors: ` + - `${JSON.stringify(toolExecutionNotifications)}.` - ); - } - - const toolsUrl = downloadTelemetryNotifications[0].properties.attributes.toolsUrl; - console.log(`Found tools URL: ${toolsUrl}`); - - const expectedExtension = process.env['RUNNER_OS'] === 'Windows' ? '.tar.gz' : '.tar.zst'; - - if (!toolsUrl.endsWith(expectedExtension)) { - core.setFailed( - `Expected the tools URL to be a ${expectedExtension} file, but found ${toolsUrl}.` - ); - } - env: - CODEQL_ACTION_TEST_MODE: true diff --git a/.github/workflows/__cleanup-db-cluster-dir.yml b/.github/workflows/__cleanup-db-cluster-dir.yml index 921228910e..3153041401 100644 --- a/.github/workflows/__cleanup-db-cluster-dir.yml +++ b/.github/workflows/__cleanup-db-cluster-dir.yml @@ -45,7 +45,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__config-export.yml b/.github/workflows/__config-export.yml index dedf559719..0c7a2cc151 100644 --- a/.github/workflows/__config-export.yml +++ b/.github/workflows/__config-export.yml @@ -47,7 +47,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__config-input.yml b/.github/workflows/__config-input.yml index a5da2050ad..4267e00584 100644 --- a/.github/workflows/__config-input.yml +++ b/.github/workflows/__config-input.yml @@ -45,9 +45,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 20.x cache: npm diff --git a/.github/workflows/__cpp-deptrace-disabled.yml b/.github/workflows/__cpp-deptrace-disabled.yml index 5eba27ed63..e2434f4256 100644 --- a/.github/workflows/__cpp-deptrace-disabled.yml +++ b/.github/workflows/__cpp-deptrace-disabled.yml @@ -49,7 +49,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__cpp-deptrace-enabled-on-macos.yml b/.github/workflows/__cpp-deptrace-enabled-on-macos.yml index d26cd7dca7..344ed8d1ea 100644 --- a/.github/workflows/__cpp-deptrace-enabled-on-macos.yml +++ b/.github/workflows/__cpp-deptrace-enabled-on-macos.yml @@ -47,7 +47,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__cpp-deptrace-enabled.yml b/.github/workflows/__cpp-deptrace-enabled.yml index d3b04db26d..ab1a70584b 100644 --- a/.github/workflows/__cpp-deptrace-enabled.yml +++ b/.github/workflows/__cpp-deptrace-enabled.yml @@ -49,7 +49,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__diagnostics-export.yml b/.github/workflows/__diagnostics-export.yml index 7f788ef0a2..c55f3de9b8 100644 --- a/.github/workflows/__diagnostics-export.yml +++ b/.github/workflows/__diagnostics-export.yml @@ -47,7 +47,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__export-file-baseline-information.yml b/.github/workflows/__export-file-baseline-information.yml index 69fb867920..4ce8b40285 100644 --- a/.github/workflows/__export-file-baseline-information.yml +++ b/.github/workflows/__export-file-baseline-information.yml @@ -69,13 +69,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__extractor-ram-threads.yml b/.github/workflows/__extractor-ram-threads.yml index 28487388df..5bd5c8b940 100644 --- a/.github/workflows/__extractor-ram-threads.yml +++ b/.github/workflows/__extractor-ram-threads.yml @@ -45,7 +45,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__global-proxy.yml b/.github/workflows/__global-proxy.yml index e3ba6ff101..9244d1fc8f 100644 --- a/.github/workflows/__global-proxy.yml +++ b/.github/workflows/__global-proxy.yml @@ -47,7 +47,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test @@ -55,17 +55,45 @@ jobs: version: ${{ matrix.version }} use-all-platform-bundle: 'false' setup-kotlin: 'false' + - name: Block direct internet access to force proxy usage + run: | + apt-get update -qq && apt-get install -y -qq iptables >/dev/null 2>&1 + PROXY_IP=$(getent hosts squid-proxy | awk '{ print $1 }') + echo "Squid proxy IP: $PROXY_IP" + # Allow all traffic to the proxy container + iptables -A OUTPUT -d "$PROXY_IP" -j ACCEPT + # Allow DNS resolution + iptables -A OUTPUT -p udp --dport 53 -j ACCEPT + iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT + # Allow loopback + iptables -A OUTPUT -o lo -j ACCEPT + # Allow already-established connections (from checkout/prepare-test) + iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + # Block all other outbound HTTP and HTTPS, ensuring direct access fails + iptables -A OUTPUT -p tcp --dport 80 -j REJECT --reject-with tcp-reset + iptables -A OUTPUT -p tcp --dport 443 -j REJECT --reject-with tcp-reset + echo "Direct HTTP/HTTPS access is now blocked - all traffic must go through the proxy" + + - name: Set proxy environment variables + shell: bash + run: | + echo "http_proxy=http://squid-proxy:3128" >> $GITHUB_ENV + echo "HTTP_PROXY=http://squid-proxy:3128" >> $GITHUB_ENV + echo "https_proxy=http://squid-proxy:3128" >> $GITHUB_ENV + echo "HTTPS_PROXY=http://squid-proxy:3128" >> $GITHUB_ENV + - uses: ./../action/init with: languages: javascript tools: ${{ steps.prepare-test.outputs.tools-url }} + - uses: ./../action/analyze env: - https_proxy: http://squid-proxy:3128 CODEQL_ACTION_TOLERATE_MISSING_GIT_VERSION: true CODEQL_ACTION_TEST_MODE: true container: image: ubuntu:22.04 + options: --cap-add=NET_ADMIN services: squid-proxy: image: ubuntu/squid:latest diff --git a/.github/workflows/__go-custom-queries.yml b/.github/workflows/__go-custom-queries.yml index 001196d112..7b4cd1305b 100644 --- a/.github/workflows/__go-custom-queries.yml +++ b/.github/workflows/__go-custom-queries.yml @@ -67,13 +67,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__go-indirect-tracing-workaround-diagnostic.yml b/.github/workflows/__go-indirect-tracing-workaround-diagnostic.yml index ced2df5982..968caf1e69 100644 --- a/.github/workflows/__go-indirect-tracing-workaround-diagnostic.yml +++ b/.github/workflows/__go-indirect-tracing-workaround-diagnostic.yml @@ -55,9 +55,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false @@ -73,7 +73,7 @@ jobs: languages: go tools: ${{ steps.prepare-test.outputs.tools-url }} # Deliberately change Go after the `init` step - - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: '1.20' - name: Build code diff --git a/.github/workflows/__go-indirect-tracing-workaround-no-file-program.yml b/.github/workflows/__go-indirect-tracing-workaround-no-file-program.yml index 32ebaee34a..0f13b1e663 100644 --- a/.github/workflows/__go-indirect-tracing-workaround-no-file-program.yml +++ b/.github/workflows/__go-indirect-tracing-workaround-no-file-program.yml @@ -55,9 +55,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__go-indirect-tracing-workaround.yml b/.github/workflows/__go-indirect-tracing-workaround.yml index 8696063265..915835c2ab 100644 --- a/.github/workflows/__go-indirect-tracing-workaround.yml +++ b/.github/workflows/__go-indirect-tracing-workaround.yml @@ -55,9 +55,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__go-tracing-autobuilder.yml b/.github/workflows/__go-tracing-autobuilder.yml index d8ef15b5a9..ccbb1b5a6e 100644 --- a/.github/workflows/__go-tracing-autobuilder.yml +++ b/.github/workflows/__go-tracing-autobuilder.yml @@ -75,9 +75,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__go-tracing-custom-build-steps.yml b/.github/workflows/__go-tracing-custom-build-steps.yml index 077382459f..2acc617cb1 100644 --- a/.github/workflows/__go-tracing-custom-build-steps.yml +++ b/.github/workflows/__go-tracing-custom-build-steps.yml @@ -75,9 +75,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__go-tracing-legacy-workflow.yml b/.github/workflows/__go-tracing-legacy-workflow.yml index 2c4031b388..a43705b703 100644 --- a/.github/workflows/__go-tracing-legacy-workflow.yml +++ b/.github/workflows/__go-tracing-legacy-workflow.yml @@ -75,9 +75,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__init-with-registries.yml b/.github/workflows/__init-with-registries.yml index 623afdee97..9293dcc196 100644 --- a/.github/workflows/__init-with-registries.yml +++ b/.github/workflows/__init-with-registries.yml @@ -49,7 +49,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__javascript-source-root.yml b/.github/workflows/__javascript-source-root.yml index 622156ce4d..1dcbd38a85 100644 --- a/.github/workflows/__javascript-source-root.yml +++ b/.github/workflows/__javascript-source-root.yml @@ -49,7 +49,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__job-run-uuid-sarif.yml b/.github/workflows/__job-run-uuid-sarif.yml index 989b28fc53..429a694947 100644 --- a/.github/workflows/__job-run-uuid-sarif.yml +++ b/.github/workflows/__job-run-uuid-sarif.yml @@ -45,7 +45,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test @@ -71,8 +71,8 @@ jobs: run: | cd "$RUNNER_TEMP/results" actual=$(jq -r '.runs[0].properties.jobRunUuid' javascript.sarif) - if [[ "$actual" != "$JOB_RUN_UUID" ]]; then - echo "Expected SARIF output to contain job run UUID '$JOB_RUN_UUID', but found '$actual'." + if [[ "$actual" != "$CODEQL_ACTION_JOB_RUN_UUID" ]]; then + echo "Expected SARIF output to contain job run UUID '$CODEQL_ACTION_JOB_RUN_UUID', but found '$actual'." exit 1 else echo "Found job run UUID '$actual'." diff --git a/.github/workflows/__language-aliases.yml b/.github/workflows/__language-aliases.yml index 3a1656eef7..731d975ce3 100644 --- a/.github/workflows/__language-aliases.yml +++ b/.github/workflows/__language-aliases.yml @@ -45,7 +45,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__local-bundle.yml b/.github/workflows/__local-bundle.yml index 7bc4687ccd..8e448080f3 100644 --- a/.github/workflows/__local-bundle.yml +++ b/.github/workflows/__local-bundle.yml @@ -65,13 +65,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__multi-language-autodetect.yml b/.github/workflows/__multi-language-autodetect.yml index 3a297a62e8..55023cd916 100644 --- a/.github/workflows/__multi-language-autodetect.yml +++ b/.github/workflows/__multi-language-autodetect.yml @@ -99,13 +99,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false @@ -120,7 +120,7 @@ jobs: # We need Python 3.13 for older CLI versions because they are not compatible with Python 3.14 or newer. # See https://github.com/github/codeql-action/pull/3212 if: matrix.version != 'nightly-latest' && matrix.version != 'linked' - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: '3.13' diff --git a/.github/workflows/__overlay-init-fallback.yml b/.github/workflows/__overlay-init-fallback.yml index 9a4ffa71f2..b6c99efcef 100644 --- a/.github/workflows/__overlay-init-fallback.yml +++ b/.github/workflows/__overlay-init-fallback.yml @@ -47,7 +47,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__packaging-codescanning-config-inputs-js.yml b/.github/workflows/__packaging-codescanning-config-inputs-js.yml index 71d98ed10b..409e0a1a65 100644 --- a/.github/workflows/__packaging-codescanning-config-inputs-js.yml +++ b/.github/workflows/__packaging-codescanning-config-inputs-js.yml @@ -69,18 +69,18 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false - name: Install Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 20.x cache: npm diff --git a/.github/workflows/__packaging-config-inputs-js.yml b/.github/workflows/__packaging-config-inputs-js.yml index 2a40f6d04c..34d4ae1bca 100644 --- a/.github/workflows/__packaging-config-inputs-js.yml +++ b/.github/workflows/__packaging-config-inputs-js.yml @@ -69,18 +69,18 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false - name: Install Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 20.x cache: npm diff --git a/.github/workflows/__packaging-config-js.yml b/.github/workflows/__packaging-config-js.yml index 4709085adb..88426cd684 100644 --- a/.github/workflows/__packaging-config-js.yml +++ b/.github/workflows/__packaging-config-js.yml @@ -69,18 +69,18 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false - name: Install Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 20.x cache: npm diff --git a/.github/workflows/__packaging-inputs-js.yml b/.github/workflows/__packaging-inputs-js.yml index 2c1090f02f..2461944dbd 100644 --- a/.github/workflows/__packaging-inputs-js.yml +++ b/.github/workflows/__packaging-inputs-js.yml @@ -69,18 +69,18 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false - name: Install Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 20.x cache: npm diff --git a/.github/workflows/__remote-config.yml b/.github/workflows/__remote-config.yml index edf1ea0c94..e1c3785f6a 100644 --- a/.github/workflows/__remote-config.yml +++ b/.github/workflows/__remote-config.yml @@ -67,13 +67,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__resolve-environment-action.yml b/.github/workflows/__resolve-environment-action.yml index 29a042ae2b..11a31fdabc 100644 --- a/.github/workflows/__resolve-environment-action.yml +++ b/.github/workflows/__resolve-environment-action.yml @@ -49,7 +49,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__rubocop-multi-language.yml b/.github/workflows/__rubocop-multi-language.yml index c93913bd3a..c405b44fed 100644 --- a/.github/workflows/__rubocop-multi-language.yml +++ b/.github/workflows/__rubocop-multi-language.yml @@ -45,7 +45,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test @@ -54,7 +54,7 @@ jobs: use-all-platform-bundle: 'false' setup-kotlin: 'true' - name: Set up Ruby - uses: ruby/setup-ruby@89f90524b88a01fe6e0b732220432cc6142926af # v1.313.0 + uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0 with: ruby-version: 2.6 - name: Install Code Scanning integration diff --git a/.github/workflows/__ruby.yml b/.github/workflows/__ruby.yml index 3558be85e4..98f8ec6a2a 100644 --- a/.github/workflows/__ruby.yml +++ b/.github/workflows/__ruby.yml @@ -55,7 +55,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__rust.yml b/.github/workflows/__rust.yml index e74daa4e4a..b3638ca6df 100644 --- a/.github/workflows/__rust.yml +++ b/.github/workflows/__rust.yml @@ -53,7 +53,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__split-workflow.yml b/.github/workflows/__split-workflow.yml index e8ea7e059b..512058a598 100644 --- a/.github/workflows/__split-workflow.yml +++ b/.github/workflows/__split-workflow.yml @@ -75,13 +75,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__start-proxy.yml b/.github/workflows/__start-proxy.yml index 7ac2dede30..edc6aa1cc5 100644 --- a/.github/workflows/__start-proxy.yml +++ b/.github/workflows/__start-proxy.yml @@ -49,7 +49,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test @@ -57,15 +57,11 @@ jobs: version: ${{ matrix.version }} use-all-platform-bundle: 'false' setup-kotlin: 'true' - - uses: ./../action/init - with: - languages: csharp - tools: ${{ steps.prepare-test.outputs.tools-url }} - - name: Setup proxy for registries id: proxy uses: ./../action/start-proxy with: + language: java registry_secrets: | [ { @@ -94,5 +90,16 @@ jobs: || !contains(steps.proxy.outputs.proxy_urls, 'https://repo.maven.apache.org/maven2/') || !contains(steps.proxy.outputs.proxy_urls, 'https://repo1.maven.org/maven2') run: exit 1 + + - uses: ./../action/init + env: + CODEQL_PROXY_HOST: ${{ steps.proxy.outputs.proxy_host }} + CODEQL_PROXY_PORT: ${{ steps.proxy.outputs.proxy_port }} + CODEQL_PROXY_CA_CERTIFICATE: ${{ steps.proxy.outputs.proxy_ca_certificate }} + with: + languages: java + tools: ${{ steps.prepare-test.outputs.tools-url }} + config-file: codeql-action@main:tests/multi-language-repo/.github/codeql/custom-queries.yml env: + CODEQL_ACTION_PROXY_API_REQUESTS: 'true' CODEQL_ACTION_TEST_MODE: true diff --git a/.github/workflows/__submit-sarif-failure.yml b/.github/workflows/__submit-sarif-failure.yml index 339d6a07cb..099e93001f 100644 --- a/.github/workflows/__submit-sarif-failure.yml +++ b/.github/workflows/__submit-sarif-failure.yml @@ -49,7 +49,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test @@ -57,7 +57,7 @@ jobs: version: ${{ matrix.version }} use-all-platform-bundle: 'false' setup-kotlin: 'true' - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: ./init with: languages: javascript diff --git a/.github/workflows/__swift-autobuild.yml b/.github/workflows/__swift-autobuild.yml index e59a87e3aa..52c189f3a8 100644 --- a/.github/workflows/__swift-autobuild.yml +++ b/.github/workflows/__swift-autobuild.yml @@ -45,7 +45,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/__swift-custom-build.yml b/.github/workflows/__swift-custom-build.yml index f0da240f3d..99fbf7a897 100644 --- a/.github/workflows/__swift-custom-build.yml +++ b/.github/workflows/__swift-custom-build.yml @@ -69,13 +69,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__unset-environment.yml b/.github/workflows/__unset-environment.yml index f4474bf643..8a8796d9a7 100644 --- a/.github/workflows/__unset-environment.yml +++ b/.github/workflows/__unset-environment.yml @@ -67,13 +67,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__upload-ref-sha-input.yml b/.github/workflows/__upload-ref-sha-input.yml index d4b4c6e6e8..76e8f4e3c0 100644 --- a/.github/workflows/__upload-ref-sha-input.yml +++ b/.github/workflows/__upload-ref-sha-input.yml @@ -65,13 +65,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__upload-sarif.yml b/.github/workflows/__upload-sarif.yml index 42b1c41620..77fa0264e5 100644 --- a/.github/workflows/__upload-sarif.yml +++ b/.github/workflows/__upload-sarif.yml @@ -72,13 +72,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false diff --git a/.github/workflows/__with-checkout-path.yml b/.github/workflows/__with-checkout-path.yml index 27f4db4028..59a8edc9d1 100644 --- a/.github/workflows/__with-checkout-path.yml +++ b/.github/workflows/__with-checkout-path.yml @@ -66,13 +66,13 @@ jobs: steps: # This ensures we don't accidentally use the original checkout for any part of the test. - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: ${{ inputs.dotnet-version || '9.x' }} - name: Install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version || '>=1.21.0' }} cache: false @@ -91,7 +91,7 @@ jobs: rm -rf ./* .github .git # Check out the actions repo again, but at a different location. # choose an arbitrary SHA so that we can later test that the commit_oid is not from main - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: 474bbf07f9247ffe1856c6a0f94aeeb10e7afee6 path: x/y/z/some-path diff --git a/.github/workflows/check-expected-release-files.yml b/.github/workflows/check-expected-release-files.yml index 670f146566..6cabd0454b 100644 --- a/.github/workflows/check-expected-release-files.yml +++ b/.github/workflows/check-expected-release-files.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout CodeQL Action - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Check Expected Release Files run: | bundle_version="$(cat "./src/defaults.json" | jq -r ".bundleVersion")" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c5efa1731a..f27de17fd8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -32,7 +32,7 @@ jobs: security-events: read steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up default CodeQL bundle id: setup-default uses: ./setup-codeql @@ -84,7 +84,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Initialize CodeQL uses: ./init id: init @@ -113,7 +113,6 @@ jobs: matrix: include: - language: actions - - language: python permissions: contents: read @@ -121,7 +120,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Initialize CodeQL uses: ./init with: diff --git a/.github/workflows/codescanning-config-cli.yml b/.github/workflows/codescanning-config-cli.yml index 693be12392..7bc6718e35 100644 --- a/.github/workflows/codescanning-config-cli.yml +++ b/.github/workflows/codescanning-config-cli.yml @@ -54,10 +54,10 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 cache: 'npm' diff --git a/.github/workflows/debug-artifacts-failure-safe.yml b/.github/workflows/debug-artifacts-failure-safe.yml index dab3b0533c..f67cef5c75 100644 --- a/.github/workflows/debug-artifacts-failure-safe.yml +++ b/.github/workflows/debug-artifacts-failure-safe.yml @@ -48,17 +48,17 @@ jobs: - name: Dump GitHub event run: cat "${GITHUB_EVENT_PATH}" - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test with: version: ${{ matrix.version }} - - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ^1.13.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: '9.x' - name: Assert best-effort artifact scan completed diff --git a/.github/workflows/debug-artifacts-safe.yml b/.github/workflows/debug-artifacts-safe.yml index 9c2b4a3e6d..c27f195113 100644 --- a/.github/workflows/debug-artifacts-safe.yml +++ b/.github/workflows/debug-artifacts-safe.yml @@ -44,17 +44,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test with: version: ${{ matrix.version }} - - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ^1.13.1 - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: '9.x' - name: Assert best-effort artifact scan completed diff --git a/.github/workflows/post-release-mergeback.yml b/.github/workflows/post-release-mergeback.yml index b0eed4b71b..c493c2a382 100644 --- a/.github/workflows/post-release-mergeback.yml +++ b/.github/workflows/post-release-mergeback.yml @@ -44,16 +44,16 @@ jobs: GITHUB_CONTEXT: '${{ toJson(github) }}' run: echo "${GITHUB_CONTEXT}" - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # ensure we have all tags and can push commits - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 cache: 'npm' - - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 - with: - python-version: '3.12' + + - name: Install JavaScript dependencies + run: npm ci - name: Update git config run: | @@ -127,7 +127,7 @@ jobs: env: PARTIAL_CHANGELOG: "${{ runner.temp }}/partial_changelog.md" run: | - python .github/workflows/script/prepare_changelog.py CHANGELOG.md > $PARTIAL_CHANGELOG + npx tsx pr-checks/prepare-changelog.ts --output="$PARTIAL_CHANGELOG" echo "::group::Partial CHANGELOG" cat $PARTIAL_CHANGELOG diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 45d38d3459..ac61475d62 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -39,10 +39,10 @@ jobs: if: runner.os == 'Windows' run: git config --global core.autocrlf false - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node-version }} cache: 'npm' @@ -88,10 +88,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 cache: 'npm' @@ -161,7 +161,7 @@ jobs: - name: 'Backport: Check out base ref' id: checkout-base if: ${{ startsWith(github.head_ref, 'backport-') }} - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.base_ref }} diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 8bab54557a..4eb300704d 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -44,7 +44,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # Need full history for calculation of diffs diff --git a/.github/workflows/publish-immutable-action.yml b/.github/workflows/publish-immutable-action.yml index ec9a6518aa..5e5623bb07 100644 --- a/.github/workflows/publish-immutable-action.yml +++ b/.github/workflows/publish-immutable-action.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Publish immutable release id: publish diff --git a/.github/workflows/python312-windows.yml b/.github/workflows/python312-windows.yml index 5722289fad..ab169499e2 100644 --- a/.github/workflows/python312-windows.yml +++ b/.github/workflows/python312-windows.yml @@ -32,11 +32,11 @@ jobs: runs-on: windows-latest steps: - - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: 3.12 - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test uses: ./.github/actions/prepare-test diff --git a/.github/workflows/query-filters.yml b/.github/workflows/query-filters.yml index 0343ce2e69..87b934eb6b 100644 --- a/.github/workflows/query-filters.yml +++ b/.github/workflows/query-filters.yml @@ -30,10 +30,10 @@ jobs: contents: read # This permission is needed to allow the GitHub Actions workflow to read the contents of the repository. steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 cache: npm diff --git a/.github/workflows/rebuild.yml b/.github/workflows/rebuild.yml index 39b42d8a02..faa32c65d9 100644 --- a/.github/workflows/rebuild.yml +++ b/.github/workflows/rebuild.yml @@ -24,13 +24,13 @@ jobs: pull-requests: write # needed to comment on the PR steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 ref: ${{ env.HEAD_REF }} - name: Set up Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 cache: 'npm' diff --git a/.github/workflows/rollback-release.yml b/.github/workflows/rollback-release.yml index b830a827dd..c37f8a79ae 100644 --- a/.github/workflows/rollback-release.yml +++ b/.github/workflows/rollback-release.yml @@ -52,7 +52,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # Need full history for calculation of diffs @@ -93,7 +93,7 @@ jobs: LATEST_TAG: ${{ needs.prepare.outputs.latest_tag }} VERSION: "${{ needs.prepare.outputs.version }}" run: | - python .github/workflows/script/rollback_changelog.py \ + npx tsx pr-checks/rollback-changelog.ts \ --target-version "${ROLLBACK_TAG:1}" \ --rollback-version "${LATEST_TAG:1}" \ --new-version "$VERSION" > $NEW_CHANGELOG @@ -128,7 +128,9 @@ jobs: NEW_CHANGELOG: "${{ runner.temp }}/new_changelog.md" PARTIAL_CHANGELOG: "${{ runner.temp }}/partial_changelog.md" run: | - python .github/workflows/script/prepare_changelog.py $NEW_CHANGELOG > $PARTIAL_CHANGELOG + npx tsx pr-checks/prepare-changelog.ts \ + --changelog="$NEW_CHANGELOG" \ + --output="$PARTIAL_CHANGELOG" echo "::group::Partial CHANGELOG" cat $PARTIAL_CHANGELOG diff --git a/.github/workflows/script/bundle_changelog.py b/.github/workflows/script/bundle_changelog.py deleted file mode 100755 index d8ced87d8d..0000000000 --- a/.github/workflows/script/bundle_changelog.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -import os -import re - -cli_version = os.environ['CLI_VERSION'] - -# The GitHub Release for the new bundle version. -bundle_release_url = f"https://github.com/github/codeql-action/releases/tag/codeql-bundle-v{cli_version}" -# Get the PR number from the PR URL. -pr_number = os.environ['PR_URL'].split('/')[-1] -changelog_note = f"- Update default CodeQL bundle version to [{cli_version}]({bundle_release_url}). [#{pr_number}]({os.environ['PR_URL']})" - -# If the "[UNRELEASED]" section starts with "no user facing changes", remove that line. -with open('CHANGELOG.md', 'r') as f: - changelog = f.read() - -changelog = changelog.replace('## [UNRELEASED]\n\nNo user facing changes.', '## [UNRELEASED]\n') - -# Add the changelog note to the bottom of the "[UNRELEASED]" section. -changelog = re.sub(r'\n## (\d+\.\d+\.\d+)', f'{changelog_note}\n\n## \\1', changelog, count=1) - -with open('CHANGELOG.md', 'w') as f: - f.write(changelog) diff --git a/.github/workflows/script/prepare_changelog.py b/.github/workflows/script/prepare_changelog.py deleted file mode 100755 index dafb84b39c..0000000000 --- a/.github/workflows/script/prepare_changelog.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -import os -import sys - -EMPTY_CHANGELOG = 'No changes.\n\n' - -# Prepare the changelog for the new release -# This function will extract the part of the changelog that -# we want to include in the new release. -def extract_changelog_snippet(changelog_file): - output = '' - if (not os.path.exists(changelog_file)): - output = EMPTY_CHANGELOG - - else: - with open(changelog_file, 'r') as f: - lines = f.readlines() - - # Include only the contents of the first section - found_first_section = False - for line in lines: - if line.startswith('## '): - if found_first_section: - break - found_first_section = True - elif found_first_section: - output += line - - return output.strip() - - -if len(sys.argv) < 2: - raise Exception('Expecting argument: changelog_file') -changelog_file = sys.argv[1] -print(extract_changelog_snippet(changelog_file)) diff --git a/.github/workflows/script/rollback_changelog.py b/.github/workflows/script/rollback_changelog.py deleted file mode 100644 index 5e06f83455..0000000000 --- a/.github/workflows/script/rollback_changelog.py +++ /dev/null @@ -1,62 +0,0 @@ -import datetime -import os -import argparse - -EMPTY_CHANGELOG = """# CodeQL Action Changelog - -""" - -def get_today_string(): - today = datetime.datetime.today() - return '{:%d %b %Y}'.format(today) - -# Include everything up to and after the first heading, -# but not the first heading and body. -def drop_unreleased_section(lines: list[str]): - before_first_section = '' - after_first_section = '' - found_first_section = False - skipped_first_section = False - - for i, line in enumerate(lines): - if line.startswith('## ') and not found_first_section: - found_first_section = True - elif line.startswith('## ') and found_first_section: - skipped_first_section = True - - if not found_first_section: - before_first_section += line - if skipped_first_section: - after_first_section += line - - return (before_first_section, after_first_section) - -def update_changelog(target_version, rollback_version, new_version): - before_first_section = EMPTY_CHANGELOG - after_first_section = '' - - if (os.path.exists('CHANGELOG.md')): - with open('CHANGELOG.md', 'r') as f: - (before_first_section, after_first_section) = drop_unreleased_section(f.readlines()) - - newHeader = f'## {new_version} - {get_today_string()}\n' - - print(before_first_section, end="") - print(newHeader) - print(f"This release rolls back {rollback_version} due to issues with that release. It is identical to {target_version}.\n") - print(after_first_section) - -# We expect three version strings as input: -# -# - target_version: the version that we are re-releasing as `new_version` -# - rollback_version: the version that we are rolling back, typically the one that followed `target_version` -# - new_version: the new version that we are releasing `target_version` as, typically the one that follows `rollback_version` -# -# Example: python3 .github/workflows/script/rollback_changelog.py --target-version "1.2.3" --rollback-version "1.2.4" --new-version "1.2.5" -parser = argparse.ArgumentParser(description="Update CHANGELOG.md for a rollback release.") -parser.add_argument("--target-version", "-t", required=True, help="Version to re-release as new_version.") -parser.add_argument("--rollback-version", "-r", required=True, help="Version being rolled back.") -parser.add_argument("--new-version", "-n", required=True, help="New version to publish for target_version.") -args = parser.parse_args() - -update_changelog(args.target_version, args.rollback_version, args.new_version) diff --git a/.github/workflows/test-codeql-bundle-all.yml b/.github/workflows/test-codeql-bundle-all.yml index a44d3137cd..477fdf0524 100644 --- a/.github/workflows/test-codeql-bundle-all.yml +++ b/.github/workflows/test-codeql-bundle-all.yml @@ -38,7 +38,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test @@ -46,7 +46,7 @@ jobs: version: ${{ matrix.version }} use-all-platform-bundle: true - name: Install .NET - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: '9.x' - id: init diff --git a/.github/workflows/update-bundle.yml b/.github/workflows/update-bundle.yml index 04402f6dbe..d3ee924e59 100644 --- a/.github/workflows/update-bundle.yml +++ b/.github/workflows/update-bundle.yml @@ -33,20 +33,15 @@ jobs: GITHUB_CONTEXT: '${{ toJson(github) }}' run: echo "$GITHUB_CONTEXT" - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Update git config run: | git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" - - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 - with: - python-version: '3.12' - - name: Set up Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 cache: 'npm' @@ -120,7 +115,7 @@ jobs: - name: Create changelog note run: | - python .github/workflows/script/bundle_changelog.py + npx tsx pr-checks/bundle-changelog.ts - name: Push changelog note run: | diff --git a/.github/workflows/update-release-branch.yml b/.github/workflows/update-release-branch.yml index 11e97eeca0..9f38f0f0b4 100644 --- a/.github/workflows/update-release-branch.yml +++ b/.github/workflows/update-release-branch.yml @@ -38,7 +38,7 @@ jobs: contents: write # needed to push commits pull-requests: write # needed to create pull request steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # Need full history for calculation of diffs - uses: ./.github/actions/release-initialise @@ -69,7 +69,7 @@ jobs: run: | echo SOURCE_BRANCH=${REF_NAME} echo TARGET_BRANCH=releases/${MAJOR_VERSION} - python .github/update-release-branch.py \ + npx tsx ./pr-checks/update-release-branch.ts \ --repository-nwo ${{ github.repository }} \ --source-branch '${{ env.REF_NAME }}' \ --target-branch 'releases/${{ env.MAJOR_VERSION }}' \ @@ -101,7 +101,7 @@ jobs: private-key: ${{ secrets.AUTOMATION_PRIVATE_KEY }} - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # Need full history for calculation of diffs token: ${{ steps.app-token.outputs.token }} @@ -113,7 +113,7 @@ jobs: run: | echo SOURCE_BRANCH=${SOURCE_BRANCH} echo TARGET_BRANCH=${TARGET_BRANCH} - python .github/update-release-branch.py \ + npx tsx ./pr-checks/update-release-branch.ts \ --repository-nwo ${{ github.repository }} \ --source-branch ${SOURCE_BRANCH} \ --target-branch ${TARGET_BRANCH} \ diff --git a/.github/workflows/update-supported-enterprise-server-versions.yml b/.github/workflows/update-supported-enterprise-server-versions.yml index ed35ff9cab..ee2649ad0e 100644 --- a/.github/workflows/update-supported-enterprise-server-versions.yml +++ b/.github/workflows/update-supported-enterprise-server-versions.yml @@ -22,16 +22,11 @@ jobs: pull-requests: write # needed to create pull request steps: - - name: Setup Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 - with: - python-version: "3.13" - - name: Checkout CodeQL Action - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 cache: 'npm' @@ -40,10 +35,10 @@ jobs: run: npm ci - name: Checkout Enterprise Releases - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: github/enterprise-releases - token: ${{ secrets.ENTERPRISE_RELEASE_TOKEN }} + token: ${{ secrets.CODEQL_CI_ENTERPRISE_RELEASE_PAT }} path: ${{ github.workspace }}/enterprise-releases/ sparse-checkout: releases.json diff --git a/CHANGELOG.md b/CHANGELOG.md index cc4d169933..db809345bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,42 @@ See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs. +## 4.37.7 - 13 Aug 2026 + +- Update default CodeQL bundle version to [2.26.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.3). [#4085](https://github.com/github/codeql-action/pull/4085) + +## 4.37.6 - 04 Aug 2026 + +- Changed the default filepath for the new remote file address format that was introduced in CodeQL Action 4.37.0 / 3.37.0 to `.github/codeql-config.yml` to align it with the suggested path that is used elsewhere. [#4070](https://github.com/github/codeql-action/pull/4070) + +## 4.37.5 - 03 Aug 2026 + +- Fixed a bug where a network error while streaming the download of the CodeQL bundle could terminate the `init` Action instead of falling back to downloading the bundle before extracting it. [#4061](https://github.com/github/codeql-action/pull/4061) + +## 4.37.4 - 29 Jul 2026 + +- This version of the CodeQL Action adds support for the `tools` input for the `codeql-action/init` step to be specified using a `github-codeql-tools` [repository property](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization). This feature will gradually be rolled out following the release of this version. Once rolled out, this allows for the CodeQL CLI version that is used in GitHub-managed workflows, such as Default Setup, to be set to a custom value. For example, customers who run into issues with rate limits when a new CodeQL CLI version is released can set the value to `toolcache` to always use the CodeQL CLI version that is available in the runner toolcache. For Advanced Setup workflows, the value provided for `tools` in the workflow definition always takes precedence unless the value of the repository property starts with `!`. [#4037](https://github.com/github/codeql-action/pull/4037) +- Update default CodeQL bundle version to [2.26.2](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.2). [#4051](https://github.com/github/codeql-action/pull/4051) + +## 4.37.3 - 22 Jul 2026 + +No user facing changes. + +## 4.37.2 - 21 Jul 2026 + +- The new address format for the `config-file` input that was introduced in CodeQL Action 4.37.0 is now enabled by default. In addition to the format described there, the `remote=` prefix can now be used to explicitly indicate that the input refers to a remote file. All previous input formats continue to be accepted as well. [#4023](https://github.com/github/codeql-action/pull/4023) +- The CodeQL Action can now make use of [configured private registries](https://docs.github.com/en/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries) in Default Setup to retrieve CodeQL configuration files from remote repositories that require authentication. This will allow customers to store their CodeQL configuration in a single repository that can then be referenced by Default Setup workflows in other repositories. We expect to roll this and other, related changes out to everyone in July. [#4007](https://github.com/github/codeql-action/pull/4007) + +## 4.37.1 - 16 Jul 2026 + +- _Upcoming breaking change_: Add a deprecation warning for customers using CodeQL version 2.20.6 and earlier. These versions of CodeQL were discontinued on 1 July 2026 alongside GitHub Enterprise Server 3.16, and will be unsupported by the next minor release of the CodeQL Action. [#3956](https://github.com/github/codeql-action/pull/3956) +- Update default CodeQL bundle version to [2.26.1](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.1). [#4019](https://github.com/github/codeql-action/pull/4019) + +## 4.37.0 - 08 Jul 2026 + +- Update default CodeQL bundle version to [2.26.0](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.0). [#3995](https://github.com/github/codeql-action/pull/3995) +- In addition to the existing input format, the `config-file` input for the `codeql-action/init` step will soon support a new `[owner/]repo[@ref][:path]` format. All components except the repository name are optional. If omitted, `owner` defaults to the same owner as the repository the analysis is running for, `ref` to `main`, and `path` to `.github/codeql-action.yaml`. Support for this format ships in this version of the CodeQL Action, but will only be enabled over the coming weeks. [#3973](https://github.com/github/codeql-action/pull/3973) + ## 4.36.3 - 01 Jul 2026 No user facing changes. diff --git a/lib/defaults.json b/lib/defaults.json index 7c82ff2a6e..b5d9f13644 100644 --- a/lib/defaults.json +++ b/lib/defaults.json @@ -1,6 +1,6 @@ { - "bundleVersion": "codeql-bundle-v2.25.6", - "cliVersion": "2.25.6", - "priorBundleVersion": "codeql-bundle-v2.25.5", - "priorCliVersion": "2.25.5" + "bundleVersion": "codeql-bundle-v2.26.3", + "cliVersion": "2.26.3", + "priorBundleVersion": "codeql-bundle-v2.26.2", + "priorCliVersion": "2.26.2" } diff --git a/lib/entry-points.js b/lib/entry-points.js index cdbd6ab82b..af11ed2bb8 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -1301,16 +1301,16 @@ var require_util = __commonJS({ function isStream2(obj) { return obj && typeof obj === "object" && typeof obj.pipe === "function" && typeof obj.on === "function"; } - function isBlobLike(object) { - if (object === null) { + function isBlobLike(object2) { + if (object2 === null) { return false; - } else if (object instanceof Blob2) { + } else if (object2 instanceof Blob2) { return true; - } else if (typeof object !== "object") { + } else if (typeof object2 !== "object") { return false; } else { - const sTag = object[Symbol.toStringTag]; - return (sTag === "Blob" || sTag === "File") && ("stream" in object && typeof object.stream === "function" || "arrayBuffer" in object && typeof object.arrayBuffer === "function"); + const sTag = object2[Symbol.toStringTag]; + return (sTag === "Blob" || sTag === "File") && ("stream" in object2 && typeof object2.stream === "function" || "arrayBuffer" in object2 && typeof object2.arrayBuffer === "function"); } } function buildURL(url2, queryParams) { @@ -1592,8 +1592,8 @@ var require_util = __commonJS({ } ); } - function isFormDataLike(object) { - return object && typeof object === "object" && typeof object.append === "function" && typeof object.delete === "function" && typeof object.get === "function" && typeof object.getAll === "function" && typeof object.has === "function" && typeof object.set === "function" && object[Symbol.toStringTag] === "FormData"; + function isFormDataLike(object2) { + return object2 && typeof object2 === "object" && typeof object2.append === "function" && typeof object2.delete === "function" && typeof object2.get === "function" && typeof object2.getAll === "function" && typeof object2.has === "function" && typeof object2.set === "function" && object2[Symbol.toStringTag] === "FormData"; } function addAbortListener(signal, listener) { if ("addEventListener" in signal) { @@ -2218,7 +2218,11 @@ var require_request = __commonJS({ } else if (typeof val[i] === "object") { throw new InvalidArgumentError(`invalid ${key} header`); } else { - arr.push(`${val[i]}`); + const str = `${val[i]}`; + if (!isValidHeaderValue(str)) { + throw new InvalidArgumentError(`invalid ${key} header`); + } + arr.push(str); } } val = arr; @@ -2230,6 +2234,9 @@ var require_request = __commonJS({ val = ""; } else { val = `${val}`; + if (!isValidHeaderValue(val)) { + throw new InvalidArgumentError(`invalid ${key} header`); + } } if (headerName === "host") { if (request3.host !== null) { @@ -2339,13 +2346,21 @@ var require_dispatcher_base = __commonJS({ var kOnDestroyed = /* @__PURE__ */ Symbol("onDestroyed"); var kOnClosed = /* @__PURE__ */ Symbol("onClosed"); var kInterceptedDispatch = /* @__PURE__ */ Symbol("Intercepted Dispatch"); + var kWebSocketOptions = /* @__PURE__ */ Symbol("webSocketOptions"); var DispatcherBase = class extends Dispatcher { - constructor() { + constructor(opts) { super(); this[kDestroyed] = false; this[kOnDestroyed] = null; this[kClosed] = false; this[kOnClosed] = []; + this[kWebSocketOptions] = opts?.webSocket ?? {}; + } + get webSocketOptions() { + return { + maxFragments: this[kWebSocketOptions].maxFragments ?? 131072, + maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024 + }; } get destroyed() { return this[kDestroyed]; @@ -4347,8 +4362,8 @@ var require_util2 = __commonJS({ } return "allowed"; } - function isErrorLike(object) { - return object instanceof Error || (object?.constructor?.name === "Error" || object?.constructor?.name === "DOMException"); + function isErrorLike(object2) { + return object2 instanceof Error || (object2?.constructor?.name === "Error" || object2?.constructor?.name === "DOMException"); } function isValidReasonPhrase(statusText) { for (let i = 0; i < statusText.length; ++i) { @@ -4773,7 +4788,7 @@ var require_util2 = __commonJS({ return new FastIterableIterator(target, kind); }; } - function iteratorMixin(name, object, kInternalIterator, keyIndex = 0, valueIndex = 1) { + function iteratorMixin(name, object2, kInternalIterator, keyIndex = 0, valueIndex = 1) { const makeIterator = createIterator(name, kInternalIterator, keyIndex, valueIndex); const properties = { keys: { @@ -4781,7 +4796,7 @@ var require_util2 = __commonJS({ enumerable: true, configurable: true, value: function keys() { - webidl.brandCheck(this, object); + webidl.brandCheck(this, object2); return makeIterator(this, "key"); } }, @@ -4790,7 +4805,7 @@ var require_util2 = __commonJS({ enumerable: true, configurable: true, value: function values() { - webidl.brandCheck(this, object); + webidl.brandCheck(this, object2); return makeIterator(this, "value"); } }, @@ -4799,7 +4814,7 @@ var require_util2 = __commonJS({ enumerable: true, configurable: true, value: function entries() { - webidl.brandCheck(this, object); + webidl.brandCheck(this, object2); return makeIterator(this, "key+value"); } }, @@ -4808,7 +4823,7 @@ var require_util2 = __commonJS({ enumerable: true, configurable: true, value: function forEach(callbackfn, thisArg = globalThis) { - webidl.brandCheck(this, object); + webidl.brandCheck(this, object2); webidl.argumentLengthCheck(arguments, 1, `${name}.forEach`); if (typeof callbackfn !== "function") { throw new TypeError( @@ -4821,7 +4836,7 @@ var require_util2 = __commonJS({ } } }; - return Object.defineProperties(object.prototype, { + return Object.defineProperties(object2.prototype, { ...properties, [Symbol.iterator]: { writable: true, @@ -5221,8 +5236,8 @@ var require_file = __commonJS({ } }; webidl.converters.Blob = webidl.interfaceConverter(Blob2); - function isFileLike(object) { - return object instanceof File2 || object && (typeof object.stream === "function" || typeof object.arrayBuffer === "function") && object[Symbol.toStringTag] === "File"; + function isFileLike(object2) { + return object2 instanceof File2 || object2 && (typeof object2.stream === "function" || typeof object2.arrayBuffer === "function") && object2[Symbol.toStringTag] === "File"; } module2.exports = { FileLike, isFileLike }; } @@ -5670,12 +5685,12 @@ var require_body = __commonJS({ } }); } - function extractBody(object, keepalive = false) { + function extractBody(object2, keepalive = false) { let stream2 = null; - if (object instanceof ReadableStream) { - stream2 = object; - } else if (isBlobLike(object)) { - stream2 = object.stream(); + if (object2 instanceof ReadableStream) { + stream2 = object2; + } else if (isBlobLike(object2)) { + stream2 = object2.stream(); } else { stream2 = new ReadableStream({ async pull(controller) { @@ -5695,17 +5710,17 @@ var require_body = __commonJS({ let source = null; let length = null; let type = null; - if (typeof object === "string") { - source = object; + if (typeof object2 === "string") { + source = object2; type = "text/plain;charset=UTF-8"; - } else if (object instanceof URLSearchParams) { - source = object.toString(); + } else if (object2 instanceof URLSearchParams) { + source = object2.toString(); type = "application/x-www-form-urlencoded;charset=UTF-8"; - } else if (isArrayBuffer(object)) { - source = new Uint8Array(object.slice()); - } else if (ArrayBuffer.isView(object)) { - source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength)); - } else if (util3.isFormDataLike(object)) { + } else if (isArrayBuffer(object2)) { + source = new Uint8Array(object2.slice()); + } else if (ArrayBuffer.isView(object2)) { + source = new Uint8Array(object2.buffer.slice(object2.byteOffset, object2.byteOffset + object2.byteLength)); + } else if (util3.isFormDataLike(object2)) { const boundary = `----formdata-undici-0${`${random(1e11)}`.padStart(11, "0")}`; const prefix = `--${boundary}\r Content-Disposition: form-data`; @@ -5715,7 +5730,7 @@ Content-Disposition: form-data`; const rn = new Uint8Array([13, 10]); length = 0; let hasUnknownSizeValue = false; - for (const [name, value] of object) { + for (const [name, value] of object2) { if (typeof value === "string") { const chunk2 = textEncoder.encode(prefix + `; name="${escape3(normalizeLinefeeds(name))}"\r \r @@ -5743,7 +5758,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r if (hasUnknownSizeValue) { length = null; } - source = object; + source = object2; action = async function* () { for (const part of blobParts) { if (part.stream) { @@ -5754,22 +5769,22 @@ Content-Type: ${value.type || "application/octet-stream"}\r } }; type = `multipart/form-data; boundary=${boundary}`; - } else if (isBlobLike(object)) { - source = object; - length = object.size; - if (object.type) { - type = object.type; + } else if (isBlobLike(object2)) { + source = object2; + length = object2.size; + if (object2.type) { + type = object2.type; } - } else if (typeof object[Symbol.asyncIterator] === "function") { + } else if (typeof object2[Symbol.asyncIterator] === "function") { if (keepalive) { throw new TypeError("keepalive"); } - if (util3.isDisturbed(object) || object.locked) { + if (util3.isDisturbed(object2) || object2.locked) { throw new TypeError( "Response body object should not be disturbed or locked" ); } - stream2 = object instanceof ReadableStream ? object : ReadableStreamFrom(object); + stream2 = object2 instanceof ReadableStream ? object2 : ReadableStreamFrom(object2); } if (typeof source === "string" || util3.isBuffer(source)) { length = Buffer.byteLength(source); @@ -5778,7 +5793,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r let iterator2; stream2 = new ReadableStream({ async start() { - iterator2 = action(object)[Symbol.asyncIterator](); + iterator2 = action(object2)[Symbol.asyncIterator](); }, async pull(controller) { const { value, done } = await iterator2.next(); @@ -5806,12 +5821,12 @@ Content-Type: ${value.type || "application/octet-stream"}\r const body = { stream: stream2, source, length }; return [body, type]; } - function safelyExtractBody(object, keepalive = false) { - if (object instanceof ReadableStream) { - assert(!util3.isDisturbed(object), "The body has already been consumed."); - assert(!object.locked, "The stream is locked."); + function safelyExtractBody(object2, keepalive = false) { + if (object2 instanceof ReadableStream) { + assert(!util3.isDisturbed(object2), "The body has already been consumed."); + assert(!object2.locked, "The stream is locked."); } - return extractBody(object, keepalive); + return extractBody(object2, keepalive); } function cloneBody(instance, body) { const [out1, out2] = body.stream.tee(); @@ -5891,12 +5906,12 @@ Content-Type: ${value.type || "application/octet-stream"}\r function mixinBody(prototype) { Object.assign(prototype.prototype, bodyMixinMethods(prototype)); } - async function consumeBody(object, convertBytesToJSValue, instance) { - webidl.brandCheck(object, instance); - if (bodyUnusable(object)) { + async function consumeBody(object2, convertBytesToJSValue, instance) { + webidl.brandCheck(object2, instance); + if (bodyUnusable(object2)) { throw new TypeError("Body is unusable: Body has already been read"); } - throwIfAborted(object[kState]); + throwIfAborted(object2[kState]); const promise = createDeferredPromise(); const errorSteps = (error3) => promise.reject(error3); const successSteps = (data) => { @@ -5906,15 +5921,15 @@ Content-Type: ${value.type || "application/octet-stream"}\r errorSteps(e); } }; - if (object[kState].body == null) { + if (object2[kState].body == null) { successSteps(Buffer.allocUnsafe(0)); return promise.promise; } - await fullyReadBody(object[kState].body, successSteps, errorSteps); + await fullyReadBody(object2[kState].body, successSteps, errorSteps); return promise.promise; } - function bodyUnusable(object) { - const body = object[kState].body; + function bodyUnusable(object2) { + const body = object2[kState].body; return body != null && (body.stream.locked || util3.isDisturbed(body.stream)); } function parseJSONFromBytes(bytes) { @@ -5952,6 +5967,7 @@ var require_client_h1 = __commonJS({ RequestContentLengthMismatchError, ResponseContentLengthMismatchError, RequestAbortedError, + InvalidArgumentError, HeadersTimeoutError, HeadersOverflowError, SocketError, @@ -5998,6 +6014,9 @@ var require_client_h1 = __commonJS({ var FastBuffer = Buffer[Symbol.species]; var addListener = util3.addListener; var removeAllListeners = util3.removeAllListeners; + var kIdleSocketValidation = /* @__PURE__ */ Symbol("kIdleSocketValidation"); + var kIdleSocketValidationTimeout = /* @__PURE__ */ Symbol("kIdleSocketValidationTimeout"); + var kSocketUsed = /* @__PURE__ */ Symbol("kSocketUsed"); var extractBody; async function lazyllhttp() { const llhttpWasmData = process.env.JEST_WORKER_ID ? require_llhttp_wasm() : void 0; @@ -6160,24 +6179,55 @@ var require_client_h1 = __commonJS({ currentBufferRef = null; } const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr; - if (ret === constants.ERROR.PAUSED_UPGRADE) { - this.onUpgrade(data.slice(offset)); - } else if (ret === constants.ERROR.PAUSED) { - this.paused = true; - socket.unshift(data.slice(offset)); - } else if (ret !== constants.ERROR.OK) { - const ptr = llhttp.llhttp_get_error_reason(this.ptr); - let message = ""; - if (ptr) { - const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0); - message = "Response does not match the HTTP/1.1 protocol (" + Buffer.from(llhttp.memory.buffer, ptr, len).toString() + ")"; - } - throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset)); + if (ret !== constants.ERROR.OK) { + const body = data.subarray(offset); + if (ret === constants.ERROR.PAUSED_UPGRADE) { + this.onUpgrade(body); + } else if (ret === constants.ERROR.PAUSED) { + this.paused = true; + socket.unshift(body); + } else { + throw this.createError(ret, body); + } } } catch (err) { util3.destroy(socket, err); } } + finish() { + assert(currentParser === null); + assert(this.ptr != null); + assert(!this.paused); + const { llhttp } = this; + let ret; + try { + currentParser = this; + ret = llhttp.llhttp_finish(this.ptr); + } finally { + currentParser = null; + } + if (ret === constants.ERROR.OK) { + return null; + } + if (ret === constants.ERROR.PAUSED || ret === constants.ERROR.PAUSED_UPGRADE) { + this.paused = true; + return null; + } + return this.createError(ret, EMPTY_BUF); + } + createError(ret, data) { + const { llhttp, contentLength, bytesRead } = this; + if (contentLength && bytesRead !== parseInt(contentLength, 10)) { + return new ResponseContentLengthMismatchError(); + } + const ptr = llhttp.llhttp_get_error_reason(this.ptr); + let message = ""; + if (ptr) { + const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0); + message = "Response does not match the HTTP/1.1 protocol (" + Buffer.from(llhttp.memory.buffer, ptr, len).toString() + ")"; + } + return new HTTPParserError(message, constants.ERROR[ret], data); + } destroy() { assert(this.ptr != null); assert(currentParser == null); @@ -6197,6 +6247,10 @@ var require_client_h1 = __commonJS({ if (socket.destroyed) { return -1; } + if (client[kRunning] === 0) { + util3.destroy(socket, new SocketError("bad response", util3.getSocketInfo(socket))); + return -1; + } const request3 = client[kQueue][client[kRunningIdx]]; if (!request3) { return -1; @@ -6276,6 +6330,10 @@ var require_client_h1 = __commonJS({ if (socket.destroyed) { return -1; } + if (client[kRunning] === 0) { + util3.destroy(socket, new SocketError("bad response", util3.getSocketInfo(socket))); + return -1; + } const request3 = client[kQueue][client[kRunningIdx]]; if (!request3) { return -1; @@ -6401,6 +6459,7 @@ var require_client_h1 = __commonJS({ } request3.onComplete(headers); client[kQueue][client[kRunningIdx]++] = null; + socket[kSocketUsed] = true; if (socket[kWriting]) { assert(client[kRunning] === 0); util3.destroy(socket, new InformationalError("reset")); @@ -6444,12 +6503,19 @@ var require_client_h1 = __commonJS({ socket[kWriting] = false; socket[kReset] = false; socket[kBlocking] = false; + socket[kIdleSocketValidation] = 0; + socket[kIdleSocketValidationTimeout] = null; + socket[kSocketUsed] = false; socket[kParser] = new Parser(client, socket, llhttpInstance); addListener(socket, "error", function(err) { assert(err.code !== "ERR_TLS_CERT_ALTNAME_INVALID"); const parser = this[kParser]; if (err.code === "ECONNRESET" && parser.statusCode && !parser.shouldKeepAlive) { - parser.onMessageComplete(); + const parserErr = parser.finish(); + if (parserErr) { + this[kError] = parserErr; + this[kClient][kOnError](parserErr); + } return; } this[kError] = err; @@ -6464,7 +6530,10 @@ var require_client_h1 = __commonJS({ addListener(socket, "end", function() { const parser = this[kParser]; if (parser.statusCode && !parser.shouldKeepAlive) { - parser.onMessageComplete(); + const parserErr = parser.finish(); + if (parserErr) { + util3.destroy(this, parserErr); + } return; } util3.destroy(this, new SocketError("other side closed", util3.getSocketInfo(this))); @@ -6472,9 +6541,10 @@ var require_client_h1 = __commonJS({ addListener(socket, "close", function() { const client2 = this[kClient]; const parser = this[kParser]; + clearIdleSocketValidation(this); if (parser) { if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) { - parser.onMessageComplete(); + this[kError] = parser.finish() || this[kError]; } this[kParser].destroy(); this[kParser] = null; @@ -6523,7 +6593,7 @@ var require_client_h1 = __commonJS({ return socket.destroyed; }, busy(request3) { - if (socket[kWriting] || socket[kReset] || socket[kBlocking]) { + if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) { return true; } if (request3) { @@ -6541,6 +6611,24 @@ var require_client_h1 = __commonJS({ } }; } + function clearIdleSocketValidation(socket) { + if (socket[kIdleSocketValidationTimeout]) { + clearTimeout(socket[kIdleSocketValidationTimeout]); + socket[kIdleSocketValidationTimeout] = null; + } + socket[kIdleSocketValidation] = 0; + } + function scheduleIdleSocketValidation(client, socket) { + socket[kIdleSocketValidation] = 1; + socket[kIdleSocketValidationTimeout] = setTimeout(() => { + socket[kIdleSocketValidationTimeout] = null; + socket[kIdleSocketValidation] = 2; + if (client[kSocket] === socket && !socket.destroyed) { + client[kResume](); + } + }, 0); + socket[kIdleSocketValidationTimeout].unref?.(); + } function resumeH1(client) { const socket = client[kSocket]; if (socket && !socket.destroyed) { @@ -6553,6 +6641,29 @@ var require_client_h1 = __commonJS({ socket.ref(); socket[kNoRef] = false; } + if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) { + if (socket[kIdleSocketValidation] === 0) { + scheduleIdleSocketValidation(client, socket); + socket[kParser].readMore(); + if (socket.destroyed) { + return; + } + return; + } + if (socket[kIdleSocketValidation] === 1) { + socket[kParser].readMore(); + if (socket.destroyed) { + return; + } + return; + } + } + if (client[kRunning] === 0) { + socket[kParser].readMore(); + if (socket.destroyed) { + return; + } + } if (client[kSize] === 0) { if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) { socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE); @@ -6583,8 +6694,16 @@ var require_client_h1 = __commonJS({ } body = bodyStream.stream; contentLength = bodyStream.length; - } else if (util3.isBlobLike(body) && request3.contentType == null && body.type) { - headers.push("content-type", body.type); + } else if (util3.isBlobLike(body) && request3.contentType == null) { + const contentType = body.type; + if (contentType) { + const contentTypeValue = `${contentType}`; + if (!util3.isValidHeaderValue(contentTypeValue)) { + util3.errorRequest(client, request3, new InvalidArgumentError("invalid content-type header")); + return false; + } + headers.push("content-type", contentTypeValue); + } } if (body && typeof body.read === "function") { body.read(0); @@ -6605,6 +6724,7 @@ var require_client_h1 = __commonJS({ process.emitWarning(new RequestContentLengthMismatchError()); } const socket = client[kSocket]; + clearIdleSocketValidation(socket); const abort = (err) => { if (request3.aborted || request3.completed) { return; @@ -6965,7 +7085,7 @@ var require_client_h2 = __commonJS({ "node_modules/undici/lib/dispatcher/client-h2.js"(exports2, module2) { "use strict"; var assert = require("node:assert"); - var { pipeline } = require("node:stream"); + var { pipeline: pipeline2 } = require("node:stream"); var util3 = require_util(); var { RequestContentLengthMismatchError, @@ -7412,7 +7532,7 @@ var require_client_h2 = __commonJS({ } function writeStream(abort, socket, expectsPayload, h2stream, body, client, request3, contentLength) { assert(contentLength !== 0 || client[kRunning] === 0, "stream body cannot be pipelined"); - const pipe = pipeline( + const pipe = pipeline2( body, h2stream, (err) => { @@ -7784,9 +7904,10 @@ var require_client = __commonJS({ autoSelectFamilyAttemptTimeout, // h2 maxConcurrentStreams, - allowH2 + allowH2, + webSocket } = {}) { - super(); + super({ webSocket }); if (keepAlive !== void 0) { throw new InvalidArgumentError("unsupported keepAlive, use pipelining=0 instead"); } @@ -8292,8 +8413,8 @@ var require_pool_base = __commonJS({ var kRemoveClient = /* @__PURE__ */ Symbol("remove client"); var kStats = /* @__PURE__ */ Symbol("stats"); var PoolBase = class extends DispatcherBase { - constructor() { - super(); + constructor(opts) { + super(opts); this[kQueue] = new FixedQueue(); this[kClients] = []; this[kQueued] = 0; @@ -8464,7 +8585,6 @@ var require_pool = __commonJS({ allowH2, ...options } = {}) { - super(); if (connections != null && (!Number.isFinite(connections) || connections < 0)) { throw new InvalidArgumentError("invalid connections"); } @@ -8485,6 +8605,7 @@ var require_pool = __commonJS({ ...connect }); } + super(options); this[kInterceptors] = options.interceptors?.Pool && Array.isArray(options.interceptors.Pool) ? options.interceptors.Pool : []; this[kConnections] = connections || null; this[kUrl] = util3.parseOrigin(origin); @@ -8684,7 +8805,6 @@ var require_agent = __commonJS({ } var Agent = class extends DispatcherBase { constructor({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) { - super(); if (typeof factory !== "function") { throw new InvalidArgumentError("factory must be a function."); } @@ -8694,6 +8814,7 @@ var require_agent = __commonJS({ if (!Number.isInteger(maxRedirections) || maxRedirections < 0) { throw new InvalidArgumentError("maxRedirections must be a positive number"); } + super(options); if (connect && typeof connect !== "function") { connect = { ...connect }; } @@ -8836,7 +8957,7 @@ var require_proxy_agent = __commonJS({ return this.#client.destroy(err); } }; - var ProxyAgent = class extends DispatcherBase { + var ProxyAgent2 = class extends DispatcherBase { constructor(opts) { super(); if (!opts || typeof opts === "object" && !(opts instanceof URL2) && !opts.uri) { @@ -8977,7 +9098,7 @@ var require_proxy_agent = __commonJS({ throw new InvalidArgumentError("Proxy-Authorization should be sent in ProxyAgent constructor"); } } - module2.exports = ProxyAgent; + module2.exports = ProxyAgent2; } }); @@ -8987,7 +9108,7 @@ var require_env_http_proxy_agent = __commonJS({ "use strict"; var DispatcherBase = require_dispatcher_base(); var { kClose, kDestroy, kClosed, kDestroyed, kDispatch, kNoProxyAgent, kHttpProxyAgent, kHttpsProxyAgent } = require_symbols(); - var ProxyAgent = require_proxy_agent(); + var ProxyAgent2 = require_proxy_agent(); var Agent = require_agent(); var DEFAULT_PORTS = { "http:": 80, @@ -9011,13 +9132,13 @@ var require_env_http_proxy_agent = __commonJS({ this[kNoProxyAgent] = new Agent(agentOpts); const HTTP_PROXY = httpProxy ?? process.env.http_proxy ?? process.env.HTTP_PROXY; if (HTTP_PROXY) { - this[kHttpProxyAgent] = new ProxyAgent({ ...agentOpts, uri: HTTP_PROXY }); + this[kHttpProxyAgent] = new ProxyAgent2({ ...agentOpts, uri: HTTP_PROXY }); } else { this[kHttpProxyAgent] = this[kNoProxyAgent]; } const HTTPS_PROXY = httpsProxy ?? process.env.https_proxy ?? process.env.HTTPS_PROXY; if (HTTPS_PROXY) { - this[kHttpsProxyAgent] = new ProxyAgent({ ...agentOpts, uri: HTTPS_PROXY }); + this[kHttpsProxyAgent] = new ProxyAgent2({ ...agentOpts, uri: HTTPS_PROXY }); } else { this[kHttpsProxyAgent] = this[kHttpProxyAgent]; } @@ -9134,6 +9255,24 @@ var require_retry_handler = __commonJS({ const current = Date.now(); return new Date(retryAfter).getTime() - current; } + function validatePartialResponseContentLength(headers, range2, statusCode, retryCount) { + const contentLength = headers["content-length"]; + if (contentLength == null) { + return null; + } + if (!Number.isFinite(range2.start) || !Number.isFinite(range2.end)) { + return null; + } + const length = Number(contentLength); + const expectedLength = range2.end - range2.start + 1; + if (!Number.isFinite(length) || length !== expectedLength) { + return new RequestRetryError("Content-Length mismatch", statusCode, { + headers, + data: { count: retryCount } + }); + } + return null; + } var RetryHandler = class _RetryHandler { constructor(opts, handlers) { const { retryOptions, ...dispatchOpts } = opts; @@ -9306,6 +9445,11 @@ var require_retry_handler = __commonJS({ ); return false; } + const contentLengthError = validatePartialResponseContentLength(headers, contentRange, statusCode, this.retryCount); + if (contentLengthError != null) { + this.abort(contentLengthError); + return false; + } const { start, size, end = size - 1 } = contentRange; assert(this.start === start, "content-range mismatch"); assert(this.end == null || this.end === end, "content-range mismatch"); @@ -9323,6 +9467,11 @@ var require_retry_handler = __commonJS({ statusMessage ); } + const contentLengthError = validatePartialResponseContentLength(headers, range2, statusCode, this.retryCount); + if (contentLengthError != null) { + this.abort(contentLengthError); + return false; + } const { start, size, end = size - 1 } = range2; assert( start != null && Number.isFinite(start), @@ -10401,7 +10550,7 @@ var require_api_pipeline = __commonJS({ util3.destroy(ret, err); } }; - function pipeline(opts, handler2) { + function pipeline2(opts, handler2) { try { const pipelineHandler = new PipelineHandler(opts, handler2); this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler); @@ -10410,7 +10559,7 @@ var require_api_pipeline = __commonJS({ return new PassThrough3().destroy(err); } } - module2.exports = pipeline; + module2.exports = pipeline2; } }); @@ -11961,10 +12110,10 @@ var require_headers = __commonJS({ while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(i))) ++i; return i === 0 && j === potentialValue.length ? potentialValue : potentialValue.substring(i, j); } - function fill(headers, object) { - if (Array.isArray(object)) { - for (let i = 0; i < object.length; ++i) { - const header = object[i]; + function fill(headers, object2) { + if (Array.isArray(object2)) { + for (let i = 0; i < object2.length; ++i) { + const header = object2[i]; if (header.length !== 2) { throw webidl.errors.exception({ header: "Headers constructor", @@ -11973,10 +12122,10 @@ var require_headers = __commonJS({ } appendHeader(headers, header[0], header[1]); } - } else if (typeof object === "object" && object !== null) { - const keys = Object.keys(object); + } else if (typeof object2 === "object" && object2 !== null) { + const keys = Object.keys(object2); for (let i = 0; i < keys.length; ++i) { - appendHeader(headers, keys[i], object[keys[i]]); + appendHeader(headers, keys[i], object2[keys[i]]); } } else { throw webidl.errors.conversionFailed({ @@ -12012,13 +12161,13 @@ var require_headers = __commonJS({ var HeadersList = class _HeadersList { /** @type {[string, string][]|null} */ cookies = null; - constructor(init) { - if (init instanceof _HeadersList) { - this[kHeadersMap] = new Map(init[kHeadersMap]); - this[kHeadersSortedMap] = init[kHeadersSortedMap]; - this.cookies = init.cookies === null ? null : [...init.cookies]; + constructor(init2) { + if (init2 instanceof _HeadersList) { + this[kHeadersMap] = new Map(init2[kHeadersMap]); + this[kHeadersSortedMap] = init2[kHeadersSortedMap]; + this.cookies = init2.cookies === null ? null : [...init2.cookies]; } else { - this[kHeadersMap] = new Map(init); + this[kHeadersMap] = new Map(init2); this[kHeadersSortedMap] = null; } } @@ -12129,24 +12278,24 @@ var require_headers = __commonJS({ // https://fetch.spec.whatwg.org/#convert-header-names-to-a-sorted-lowercase-set toSortedArray() { const size = this[kHeadersMap].size; - const array = new Array(size); + const array2 = new Array(size); if (size <= 32) { if (size === 0) { - return array; + return array2; } const iterator2 = this[kHeadersMap][Symbol.iterator](); const firstValue = iterator2.next().value; - array[0] = [firstValue[0], firstValue[1].value]; + array2[0] = [firstValue[0], firstValue[1].value]; assert(firstValue[1].value !== null); for (let i = 1, j = 0, right = 0, left = 0, pivot = 0, x, value; i < size; ++i) { value = iterator2.next().value; - x = array[i] = [value[0], value[1].value]; + x = array2[i] = [value[0], value[1].value]; assert(x[1] !== null); left = 0; right = i; while (left < right) { pivot = left + (right - left >> 1); - if (array[pivot][0] <= x[0]) { + if (array2[pivot][0] <= x[0]) { left = pivot + 1; } else { right = pivot; @@ -12155,38 +12304,38 @@ var require_headers = __commonJS({ if (i !== pivot) { j = i; while (j > left) { - array[j] = array[--j]; + array2[j] = array2[--j]; } - array[left] = x; + array2[left] = x; } } if (!iterator2.next().done) { throw new TypeError("Unreachable"); } - return array; + return array2; } else { let i = 0; for (const { 0: name, 1: { value } } of this[kHeadersMap]) { - array[i++] = [name, value]; + array2[i++] = [name, value]; assert(value !== null); } - return array.sort(compareHeaderName); + return array2.sort(compareHeaderName); } } }; var Headers = class _Headers { #guard; #headersList; - constructor(init = void 0) { + constructor(init2 = void 0) { webidl.util.markAsUncloneable(this); - if (init === kConstruct) { + if (init2 === kConstruct) { return; } this.#headersList = new HeadersList(); this.#guard = "none"; - if (init !== void 0) { - init = webidl.converters.HeadersInit(init, "Headers contructor", "init"); - fill(this, init); + if (init2 !== void 0) { + init2 = webidl.converters.HeadersInit(init2, "Headers contructor", "init"); + fill(this, init2); } } // https://fetch.spec.whatwg.org/#dom-headers-append @@ -12417,17 +12566,17 @@ var require_response = __commonJS({ return responseObject; } // https://fetch.spec.whatwg.org/#dom-response-json - static json(data, init = {}) { + static json(data, init2 = {}) { webidl.argumentLengthCheck(arguments, 1, "Response.json"); - if (init !== null) { - init = webidl.converters.ResponseInit(init); + if (init2 !== null) { + init2 = webidl.converters.ResponseInit(init2); } const bytes = textEncoder.encode( serializeJavascriptValueToJSONString(data) ); const body = extractBody(bytes); const responseObject = fromInnerResponse(makeResponse({}), "response"); - initializeResponse(responseObject, init, { body: body[0], type: "application/json" }); + initializeResponse(responseObject, init2, { body: body[0], type: "application/json" }); return responseObject; } // Creates a redirect Response that redirects to url with status status. @@ -12451,7 +12600,7 @@ var require_response = __commonJS({ return responseObject; } // https://fetch.spec.whatwg.org/#dom-response - constructor(body = null, init = {}) { + constructor(body = null, init2 = {}) { webidl.util.markAsUncloneable(this); if (body === kConstruct) { return; @@ -12459,7 +12608,7 @@ var require_response = __commonJS({ if (body !== null) { body = webidl.converters.BodyInit(body); } - init = webidl.converters.ResponseInit(init); + init2 = webidl.converters.ResponseInit(init2); this[kState] = makeResponse({}); this[kHeaders] = new Headers(kConstruct); setHeadersGuard(this[kHeaders], "response"); @@ -12469,7 +12618,7 @@ var require_response = __commonJS({ const [extractedBody, type] = extractBody(body); bodyWithType = { body: extractedBody, type }; } - initializeResponse(this, init, bodyWithType); + initializeResponse(this, init2, bodyWithType); } // Returns response’s type, e.g., "cors". get type() { @@ -12588,7 +12737,7 @@ var require_response = __commonJS({ } return newResponse; } - function makeResponse(init) { + function makeResponse(init2) { return { aborted: false, rangeRequested: false, @@ -12599,9 +12748,9 @@ var require_response = __commonJS({ timingInfo: null, cacheState: "", statusText: "", - ...init, - headersList: init?.headersList ? new HeadersList(init?.headersList) : new HeadersList(), - urlList: init?.urlList ? [...init.urlList] : [] + ...init2, + headersList: init2?.headersList ? new HeadersList(init2?.headersList) : new HeadersList(), + urlList: init2?.urlList ? [...init2.urlList] : [] }; } function makeNetworkError(reason) { @@ -12671,23 +12820,23 @@ var require_response = __commonJS({ assert(isCancelled(fetchParams)); return isAborted(fetchParams) ? makeNetworkError(Object.assign(new DOMException("The operation was aborted.", "AbortError"), { cause: err })) : makeNetworkError(Object.assign(new DOMException("Request was cancelled."), { cause: err })); } - function initializeResponse(response, init, body) { - if (init.status !== null && (init.status < 200 || init.status > 599)) { + function initializeResponse(response, init2, body) { + if (init2.status !== null && (init2.status < 200 || init2.status > 599)) { throw new RangeError('init["status"] must be in the range of 200 to 599, inclusive.'); } - if ("statusText" in init && init.statusText != null) { - if (!isValidReasonPhrase(String(init.statusText))) { + if ("statusText" in init2 && init2.statusText != null) { + if (!isValidReasonPhrase(String(init2.statusText))) { throw new TypeError("Invalid statusText"); } } - if ("status" in init && init.status != null) { - response[kState].status = init.status; + if ("status" in init2 && init2.status != null) { + response[kState].status = init2.status; } - if ("statusText" in init && init.statusText != null) { - response[kState].statusText = init.statusText; + if ("statusText" in init2 && init2.statusText != null) { + response[kState].statusText = init2.statusText; } - if ("headers" in init && init.headers != null) { - fill(response[kHeaders], init.headers); + if ("headers" in init2 && init2.headers != null) { + fill(response[kHeaders], init2.headers); } if (body) { if (nullBodyStatus.includes(response.status)) { @@ -12883,7 +13032,7 @@ var require_request2 = __commonJS({ var patchMethodWarning = false; var Request = class _Request { // https://fetch.spec.whatwg.org/#dom-request - constructor(input, init = {}) { + constructor(input, init2 = {}) { webidl.util.markAsUncloneable(this); if (input === kConstruct) { return; @@ -12891,13 +13040,13 @@ var require_request2 = __commonJS({ const prefix = "Request constructor"; webidl.argumentLengthCheck(arguments, 1, prefix); input = webidl.converters.RequestInfo(input, prefix, "input"); - init = webidl.converters.RequestInit(init, prefix, "init"); + init2 = webidl.converters.RequestInit(init2, prefix, "init"); let request3 = null; let fallbackMode = null; const baseUrl = environmentSettingsObject.settingsObject.baseUrl; let signal = null; if (typeof input === "string") { - this[kDispatcher] = init.dispatcher; + this[kDispatcher] = init2.dispatcher; let parsedURL; try { parsedURL = new URL(input, baseUrl); @@ -12912,7 +13061,7 @@ var require_request2 = __commonJS({ request3 = makeRequest({ urlList: [parsedURL] }); fallbackMode = "cors"; } else { - this[kDispatcher] = init.dispatcher || input[kDispatcher]; + this[kDispatcher] = init2.dispatcher || input[kDispatcher]; assert(input instanceof _Request); request3 = input[kState]; signal = input[kSignal]; @@ -12922,10 +13071,10 @@ var require_request2 = __commonJS({ if (request3.window?.constructor?.name === "EnvironmentSettingsObject" && sameOrigin(request3.window, origin)) { window2 = request3.window; } - if (init.window != null) { + if (init2.window != null) { throw new TypeError(`'window' option '${window2}' must be null`); } - if ("window" in init) { + if ("window" in init2) { window2 = "no-window"; } request3 = makeRequest({ @@ -12971,7 +13120,7 @@ var require_request2 = __commonJS({ // URL list A clone of request’s URL list. urlList: [...request3.urlList] }); - const initHasKey = Object.keys(init).length !== 0; + const initHasKey = Object.keys(init2).length !== 0; if (initHasKey) { if (request3.mode === "navigate") { request3.mode = "same-origin"; @@ -12984,8 +13133,8 @@ var require_request2 = __commonJS({ request3.url = request3.urlList[request3.urlList.length - 1]; request3.urlList = [request3.url]; } - if (init.referrer !== void 0) { - const referrer = init.referrer; + if (init2.referrer !== void 0) { + const referrer = init2.referrer; if (referrer === "") { request3.referrer = "no-referrer"; } else { @@ -13002,12 +13151,12 @@ var require_request2 = __commonJS({ } } } - if (init.referrerPolicy !== void 0) { - request3.referrerPolicy = init.referrerPolicy; + if (init2.referrerPolicy !== void 0) { + request3.referrerPolicy = init2.referrerPolicy; } let mode; - if (init.mode !== void 0) { - mode = init.mode; + if (init2.mode !== void 0) { + mode = init2.mode; } else { mode = fallbackMode; } @@ -13020,28 +13169,28 @@ var require_request2 = __commonJS({ if (mode != null) { request3.mode = mode; } - if (init.credentials !== void 0) { - request3.credentials = init.credentials; + if (init2.credentials !== void 0) { + request3.credentials = init2.credentials; } - if (init.cache !== void 0) { - request3.cache = init.cache; + if (init2.cache !== void 0) { + request3.cache = init2.cache; } if (request3.cache === "only-if-cached" && request3.mode !== "same-origin") { throw new TypeError( "'only-if-cached' can be set only with 'same-origin' mode" ); } - if (init.redirect !== void 0) { - request3.redirect = init.redirect; + if (init2.redirect !== void 0) { + request3.redirect = init2.redirect; } - if (init.integrity != null) { - request3.integrity = String(init.integrity); + if (init2.integrity != null) { + request3.integrity = String(init2.integrity); } - if (init.keepalive !== void 0) { - request3.keepalive = Boolean(init.keepalive); + if (init2.keepalive !== void 0) { + request3.keepalive = Boolean(init2.keepalive); } - if (init.method !== void 0) { - let method = init.method; + if (init2.method !== void 0) { + let method = init2.method; const mayBeNormalized = normalizedMethodRecords[method]; if (mayBeNormalized !== void 0) { request3.method = mayBeNormalized; @@ -13063,8 +13212,8 @@ var require_request2 = __commonJS({ patchMethodWarning = true; } } - if (init.signal !== void 0) { - signal = init.signal; + if (init2.signal !== void 0) { + signal = init2.signal; } this[kState] = request3; const ac = new AbortController(); @@ -13106,7 +13255,7 @@ var require_request2 = __commonJS({ } if (initHasKey) { const headersList = getHeadersList(this[kHeaders]); - const headers = init.headers !== void 0 ? init.headers : new HeadersList(headersList); + const headers = init2.headers !== void 0 ? init2.headers : new HeadersList(headersList); headersList.clear(); if (headers instanceof HeadersList) { for (const { name, value } of headers.rawValues()) { @@ -13118,13 +13267,13 @@ var require_request2 = __commonJS({ } } const inputBody = input instanceof _Request ? input[kState].body : null; - if ((init.body != null || inputBody != null) && (request3.method === "GET" || request3.method === "HEAD")) { + if ((init2.body != null || inputBody != null) && (request3.method === "GET" || request3.method === "HEAD")) { throw new TypeError("Request with GET/HEAD method cannot have body."); } let initBody = null; - if (init.body != null) { + if (init2.body != null) { const [extractedBody, contentType] = extractBody( - init.body, + init2.body, request3.keepalive ); initBody = extractedBody; @@ -13134,7 +13283,7 @@ var require_request2 = __commonJS({ } const inputOrInitBody = initBody ?? inputBody; if (inputOrInitBody != null && inputOrInitBody.source == null) { - if (initBody != null && init.duplex == null) { + if (initBody != null && init2.duplex == null) { throw new TypeError("RequestInit: duplex option is required when sending a body."); } if (request3.mode !== "same-origin" && request3.mode !== "cors") { @@ -13329,46 +13478,46 @@ var require_request2 = __commonJS({ } }; mixinBody(Request); - function makeRequest(init) { + function makeRequest(init2) { return { - method: init.method ?? "GET", - localURLsOnly: init.localURLsOnly ?? false, - unsafeRequest: init.unsafeRequest ?? false, - body: init.body ?? null, - client: init.client ?? null, - reservedClient: init.reservedClient ?? null, - replacesClientId: init.replacesClientId ?? "", - window: init.window ?? "client", - keepalive: init.keepalive ?? false, - serviceWorkers: init.serviceWorkers ?? "all", - initiator: init.initiator ?? "", - destination: init.destination ?? "", - priority: init.priority ?? null, - origin: init.origin ?? "client", - policyContainer: init.policyContainer ?? "client", - referrer: init.referrer ?? "client", - referrerPolicy: init.referrerPolicy ?? "", - mode: init.mode ?? "no-cors", - useCORSPreflightFlag: init.useCORSPreflightFlag ?? false, - credentials: init.credentials ?? "same-origin", - useCredentials: init.useCredentials ?? false, - cache: init.cache ?? "default", - redirect: init.redirect ?? "follow", - integrity: init.integrity ?? "", - cryptoGraphicsNonceMetadata: init.cryptoGraphicsNonceMetadata ?? "", - parserMetadata: init.parserMetadata ?? "", - reloadNavigation: init.reloadNavigation ?? false, - historyNavigation: init.historyNavigation ?? false, - userActivation: init.userActivation ?? false, - taintedOrigin: init.taintedOrigin ?? false, - redirectCount: init.redirectCount ?? 0, - responseTainting: init.responseTainting ?? "basic", - preventNoCacheCacheControlHeaderModification: init.preventNoCacheCacheControlHeaderModification ?? false, - done: init.done ?? false, - timingAllowFailed: init.timingAllowFailed ?? false, - urlList: init.urlList, - url: init.urlList[0], - headersList: init.headersList ? new HeadersList(init.headersList) : new HeadersList() + method: init2.method ?? "GET", + localURLsOnly: init2.localURLsOnly ?? false, + unsafeRequest: init2.unsafeRequest ?? false, + body: init2.body ?? null, + client: init2.client ?? null, + reservedClient: init2.reservedClient ?? null, + replacesClientId: init2.replacesClientId ?? "", + window: init2.window ?? "client", + keepalive: init2.keepalive ?? false, + serviceWorkers: init2.serviceWorkers ?? "all", + initiator: init2.initiator ?? "", + destination: init2.destination ?? "", + priority: init2.priority ?? null, + origin: init2.origin ?? "client", + policyContainer: init2.policyContainer ?? "client", + referrer: init2.referrer ?? "client", + referrerPolicy: init2.referrerPolicy ?? "", + mode: init2.mode ?? "no-cors", + useCORSPreflightFlag: init2.useCORSPreflightFlag ?? false, + credentials: init2.credentials ?? "same-origin", + useCredentials: init2.useCredentials ?? false, + cache: init2.cache ?? "default", + redirect: init2.redirect ?? "follow", + integrity: init2.integrity ?? "", + cryptoGraphicsNonceMetadata: init2.cryptoGraphicsNonceMetadata ?? "", + parserMetadata: init2.parserMetadata ?? "", + reloadNavigation: init2.reloadNavigation ?? false, + historyNavigation: init2.historyNavigation ?? false, + userActivation: init2.userActivation ?? false, + taintedOrigin: init2.taintedOrigin ?? false, + redirectCount: init2.redirectCount ?? 0, + responseTainting: init2.responseTainting ?? "basic", + preventNoCacheCacheControlHeaderModification: init2.preventNoCacheCacheControlHeaderModification ?? false, + done: init2.done ?? false, + timingAllowFailed: init2.timingAllowFailed ?? false, + urlList: init2.urlList, + url: init2.urlList[0], + headersList: init2.headersList ? new HeadersList(init2.headersList) : new HeadersList() }; } function cloneRequest(request3) { @@ -13575,7 +13724,7 @@ var require_fetch = __commonJS({ subresourceSet } = require_constants3(); var EE = require("node:events"); - var { Readable: Readable3, pipeline, finished } = require("node:stream"); + var { Readable: Readable3, pipeline: pipeline2, finished } = require("node:stream"); var { addAbortListener, isErrored, isReadable, bufferToLowerCasedHeaderName } = require_util(); var { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require_data_url(); var { getGlobalDispatcher } = require_global2(); @@ -13617,12 +13766,12 @@ var require_fetch = __commonJS({ function handleFetchDone(response) { finalizeAndReportTiming(response, "fetch"); } - function fetch(input, init = void 0) { + function fetch(input, init2 = void 0) { webidl.argumentLengthCheck(arguments, 1, "globalThis.fetch"); let p = createDeferredPromise(); let requestObject; try { - requestObject = new Request(input, init); + requestObject = new Request(input, init2); } catch (e) { p.reject(e); return p.promise; @@ -14519,7 +14668,7 @@ var require_fetch = __commonJS({ status, statusText, headersList, - body: decoders.length ? pipeline(this.body, ...decoders, (err) => { + body: decoders.length ? pipeline2(this.body, ...decoders, (err) => { if (err) { this.onError(err); } @@ -16168,14 +16317,48 @@ var require_util6 = __commonJS({ for (let i = 0; i < path29.length; ++i) { const code = path29.charCodeAt(i); if (code < 32 || // exclude CTLs (0-31) - code === 127 || // DEL + code > 126 || // exclude DEL and non-ascii code === 59) { throw new Error("Invalid cookie path"); } } } + function isLetterOrDigit(code) { + return code >= 48 && code <= 57 || // 0-9 + code >= 65 && code <= 90 || // A-Z + code >= 97 && code <= 122; + } function validateCookieDomain(domain) { - if (domain.startsWith("-") || domain.endsWith(".") || domain.endsWith("-")) { + if (domain === " ") { + return; + } + if (domain.length > 255) { + throw new Error("Invalid cookie domain"); + } + let labelLength = 0; + for (let i = 0; i < domain.length; ++i) { + const code = domain.charCodeAt(i); + if (code === 46) { + if (labelLength === 0) { + throw new Error("Invalid cookie domain"); + } + if (domain.charCodeAt(i - 1) === 45) { + throw new Error("Invalid cookie domain"); + } + labelLength = 0; + continue; + } + if (labelLength === 0 && !isLetterOrDigit(code)) { + throw new Error("Invalid cookie domain"); + } + if (!isLetterOrDigit(code) && code !== 45) { + throw new Error("Invalid cookie domain"); + } + if (++labelLength > 63) { + throw new Error("Invalid cookie domain"); + } + } + if (labelLength === 0 || domain.charCodeAt(domain.length - 1) === 45) { throw new Error("Invalid cookie domain"); } } @@ -16258,7 +16441,11 @@ var require_util6 = __commonJS({ throw new Error("Invalid unparsed"); } const [key, ...value] = part.split("="); - out.push(`${key.trim()}=${value.join("=")}`); + const trimmedKey = key.trim(); + const joinedValue = value.join("="); + validateCookieName(trimmedKey); + validateCookieValue(joinedValue); + out.push(`${trimmedKey}=${joinedValue}`); } return out.join("; "); } @@ -16388,18 +16575,14 @@ var require_parse = __commonJS({ } else if (attributeNameLowercase === "httponly") { cookieAttributeList.httpOnly = true; } else if (attributeNameLowercase === "samesite") { - let enforcement = "Default"; const attributeValueLowercase = attributeValue.toLowerCase(); - if (attributeValueLowercase.includes("none")) { - enforcement = "None"; - } - if (attributeValueLowercase.includes("strict")) { - enforcement = "Strict"; + if (attributeValueLowercase === "none") { + cookieAttributeList.sameSite = "None"; + } else if (attributeValueLowercase === "strict") { + cookieAttributeList.sameSite = "Strict"; + } else if (attributeValueLowercase === "lax") { + cookieAttributeList.sameSite = "Lax"; } - if (attributeValueLowercase.includes("lax")) { - enforcement = "Lax"; - } - cookieAttributeList.sameSite = enforcement; } else { cookieAttributeList.unparsed ??= []; cookieAttributeList.unparsed.push(`${attributeName}=${attributeValue}`); @@ -16602,9 +16785,9 @@ var require_events = __commonJS({ ports }); } - static createFastMessageEvent(type, init) { - const messageEvent = new _MessageEvent(kConstruct, type, init); - messageEvent.#eventInit = init; + static createFastMessageEvent(type, init2) { + const messageEvent = new _MessageEvent(kConstruct, type, init2); + messageEvent.#eventInit = init2; messageEvent.#eventInit.data ??= null; messageEvent.#eventInit.origin ??= ""; messageEvent.#eventInit.lastEventId ??= ""; @@ -16903,7 +17086,7 @@ var require_util7 = __commonJS({ function isClosed(ws) { return ws[kReadyState] === states.CLOSED; } - function fireEvent(e, target, eventFactory = (type, init) => new Event(type, init), eventInitDict = {}) { + function fireEvent(e, target, eventFactory = (type, init2) => new Event(type, init2), eventInitDict = {}) { const event = eventFactory(e, eventInitDict); target.dispatchEvent(event); } @@ -16981,7 +17164,7 @@ var require_util7 = __commonJS({ response.socket.destroy(); } if (reason) { - fireEvent("error", ws, (type, init) => new ErrorEvent(type, init), { + fireEvent("error", ws, (type, init2) => new ErrorEvent(type, init2), { error: new Error(reason), message: reason }); @@ -17289,7 +17472,7 @@ var require_connection = __commonJS({ code = 1006; } ws[kReadyState] = states.CLOSED; - fireEvent("close", ws, (type, init) => new CloseEvent(type, init), { + fireEvent("close", ws, (type, init2) => new CloseEvent(type, init2), { wasClean, code, reason @@ -17327,27 +17510,26 @@ var require_permessage_deflate = __commonJS({ var tail = Buffer.from([0, 0, 255, 255]); var kBuffer = /* @__PURE__ */ Symbol("kBuffer"); var kLength = /* @__PURE__ */ Symbol("kLength"); - var kDefaultMaxDecompressedSize = 4 * 1024 * 1024; var PerMessageDeflate = class { /** @type {import('node:zlib').InflateRaw} */ #inflate; #options = {}; - /** @type {boolean} */ - #aborted = false; - /** @type {Function|null} */ - #currentCallback = null; + #maxPayloadSize = 0; /** * @param {Map} extensions */ - constructor(extensions) { + constructor(extensions, options) { this.#options.serverNoContextTakeover = extensions.has("server_no_context_takeover"); this.#options.serverMaxWindowBits = extensions.get("server_max_window_bits"); + this.#maxPayloadSize = options.maxPayloadSize; } + /** + * Decompress a compressed payload. + * @param {Buffer} chunk Compressed data + * @param {boolean} fin Final fragment flag + * @param {Function} callback Callback function + */ decompress(chunk, fin, callback) { - if (this.#aborted) { - callback(new MessageSizeExceededError()); - return; - } if (!this.#inflate) { let windowBits = Z_DEFAULT_WINDOWBITS; if (this.#options.serverMaxWindowBits) { @@ -17366,20 +17548,11 @@ var require_permessage_deflate = __commonJS({ this.#inflate[kBuffer] = []; this.#inflate[kLength] = 0; this.#inflate.on("data", (data) => { - if (this.#aborted) { - return; - } this.#inflate[kLength] += data.length; - if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) { - this.#aborted = true; + if (this.#maxPayloadSize > 0 && this.#inflate[kLength] > this.#maxPayloadSize) { + callback(new MessageSizeExceededError()); this.#inflate.removeAllListeners(); - this.#inflate.destroy(); this.#inflate = null; - if (this.#currentCallback) { - const cb = this.#currentCallback; - this.#currentCallback = null; - cb(new MessageSizeExceededError()); - } return; } this.#inflate[kBuffer].push(data); @@ -17389,19 +17562,17 @@ var require_permessage_deflate = __commonJS({ callback(err); }); } - this.#currentCallback = callback; this.#inflate.write(chunk); if (fin) { this.#inflate.write(tail); } this.#inflate.flush(() => { - if (this.#aborted || !this.#inflate) { + if (!this.#inflate) { return; } const full = Buffer.concat(this.#inflate[kBuffer], this.#inflate[kLength]); this.#inflate[kBuffer].length = 0; this.#inflate[kLength] = 0; - this.#currentCallback = null; callback(null, full); }); } @@ -17432,8 +17603,14 @@ var require_receiver = __commonJS({ var { WebsocketFrameSend } = require_frame(); var { closeWebSocketConnection } = require_connection(); var { PerMessageDeflate } = require_permessage_deflate(); + var { MessageSizeExceededError } = require_errors(); + function failWebsocketConnectionWithCode(ws, code, reason) { + closeWebSocketConnection(ws, code, reason, Buffer.byteLength(reason)); + failWebsocketConnection(ws, reason); + } var ByteParser = class extends Writable { #buffers = []; + #fragmentsBytes = 0; #byteOffset = 0; #loop = false; #state = parserStates.INFO; @@ -17441,16 +17618,23 @@ var require_receiver = __commonJS({ #fragments = []; /** @type {Map} */ #extensions; + /** @type {number} */ + #maxFragments; + /** @type {number} */ + #maxPayloadSize; /** * @param {import('./websocket').WebSocket} ws * @param {Map|null} extensions + * @param {{ maxFragments?: number, maxPayloadSize?: number }} [options] */ - constructor(ws, extensions) { + constructor(ws, extensions, options = {}) { super(); this.ws = ws; this.#extensions = extensions == null ? /* @__PURE__ */ new Map() : extensions; + this.#maxFragments = options.maxFragments ?? 0; + this.#maxPayloadSize = options.maxPayloadSize ?? 0; if (this.#extensions.has("permessage-deflate")) { - this.#extensions.set("permessage-deflate", new PerMessageDeflate(extensions)); + this.#extensions.set("permessage-deflate", new PerMessageDeflate(extensions, options)); } } /** @@ -17463,6 +17647,13 @@ var require_receiver = __commonJS({ this.#loop = true; this.run(callback); } + #validatePayloadLength() { + if (this.#maxPayloadSize > 0 && !isControlFrame(this.#info.opcode) && this.#info.payloadLength + this.#fragmentsBytes > this.#maxPayloadSize) { + failWebsocketConnectionWithCode(this.ws, 1009, "Payload size exceeds maximum allowed size"); + return false; + } + return true; + } /** * Runs whenever a new chunk is received. * Callback is called whenever there are no more chunks buffering, @@ -17522,6 +17713,9 @@ var require_receiver = __commonJS({ if (payloadLength <= 125) { this.#info.payloadLength = payloadLength; this.#state = parserStates.READ_DATA; + if (!this.#validatePayloadLength()) { + return; + } } else if (payloadLength === 126) { this.#state = parserStates.PAYLOADLENGTH_16; } else if (payloadLength === 127) { @@ -17542,6 +17736,9 @@ var require_receiver = __commonJS({ const buffer = this.consume(2); this.#info.payloadLength = buffer.readUInt16BE(0); this.#state = parserStates.READ_DATA; + if (!this.#validatePayloadLength()) { + return; + } } else if (this.#state === parserStates.PAYLOADLENGTH_64) { if (this.#byteOffset < 8) { return callback(); @@ -17555,6 +17752,9 @@ var require_receiver = __commonJS({ } this.#info.payloadLength = lower; this.#state = parserStates.READ_DATA; + if (!this.#validatePayloadLength()) { + return; + } } else if (this.#state === parserStates.READ_DATA) { if (this.#byteOffset < this.#info.payloadLength) { return callback(); @@ -17565,32 +17765,46 @@ var require_receiver = __commonJS({ this.#state = parserStates.INFO; } else { if (!this.#info.compressed) { - this.#fragments.push(body); + if (!this.writeFragments(body)) { + return; + } + if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) { + failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message); + return; + } if (!this.#info.fragmented && this.#info.fin) { - const fullMessage = Buffer.concat(this.#fragments); - websocketMessageReceived(this.ws, this.#info.binaryType, fullMessage); - this.#fragments.length = 0; + websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments()); } this.#state = parserStates.INFO; } else { - this.#extensions.get("permessage-deflate").decompress(body, this.#info.fin, (error3, data) => { - if (error3) { - failWebsocketConnection(this.ws, error3.message); - return; - } - this.#fragments.push(data); - if (!this.#info.fin) { - this.#state = parserStates.INFO; + this.#extensions.get("permessage-deflate").decompress( + body, + this.#info.fin, + (error3, data) => { + if (error3) { + const code = error3 instanceof MessageSizeExceededError ? 1009 : 1007; + failWebsocketConnectionWithCode(this.ws, code, error3.message); + return; + } + if (!this.writeFragments(data)) { + return; + } + if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) { + failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message); + return; + } + if (!this.#info.fin) { + this.#state = parserStates.INFO; + this.#loop = true; + this.run(callback); + return; + } + websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments()); this.#loop = true; + this.#state = parserStates.INFO; this.run(callback); - return; } - websocketMessageReceived(this.ws, this.#info.binaryType, Buffer.concat(this.#fragments)); - this.#loop = true; - this.#state = parserStates.INFO; - this.#fragments.length = 0; - this.run(callback); - }); + ); this.#loop = false; break; } @@ -17633,6 +17847,26 @@ var require_receiver = __commonJS({ this.#byteOffset -= n; return buffer; } + writeFragments(fragment) { + if (this.#maxFragments > 0 && this.#fragments.length === this.#maxFragments) { + failWebsocketConnectionWithCode(this.ws, 1008, "Too many message fragments"); + return false; + } + this.#fragmentsBytes += fragment.length; + this.#fragments.push(fragment); + return true; + } + consumeFragments() { + const fragments = this.#fragments; + if (fragments.length === 1) { + this.#fragmentsBytes = 0; + return fragments.shift(); + } + const output = Buffer.concat(fragments, this.#fragmentsBytes); + this.#fragments = []; + this.#fragmentsBytes = 0; + return output; + } parseCloseBody(data) { assert(data.length !== 1); let code; @@ -18070,7 +18304,13 @@ var require_websocket = __commonJS({ */ #onConnectionEstablished(response, parsedExtensions) { this[kResponse] = response; - const parser = new ByteParser(this, parsedExtensions); + const webSocketOptions = this[kController]?.dispatcher?.webSocketOptions; + const maxFragments = webSocketOptions?.maxFragments; + const maxPayloadSize = webSocketOptions?.maxPayloadSize; + const parser = new ByteParser(this, parsedExtensions, { + maxFragments, + maxPayloadSize + }); parser.on("drain", onParserDrain); parser.on("error", onParserError.bind(this)); response.socket.ws = this; @@ -18446,7 +18686,7 @@ ${value}`; var require_eventsource = __commonJS({ "node_modules/undici/lib/web/eventsource/eventsource.js"(exports2, module2) { "use strict"; - var { pipeline } = require("node:stream"); + var { pipeline: pipeline2 } = require("node:stream"); var { fetching } = require_fetch(); var { makeRequest } = require_request2(); var { webidl } = require_webidl(); @@ -18604,7 +18844,7 @@ var require_eventsource = __commonJS({ )); } }); - pipeline( + pipeline2( response.body.stream, eventSourceStream, (error3) => { @@ -18748,7 +18988,7 @@ var require_undici = __commonJS({ var Pool = require_pool(); var BalancedPool = require_balanced_pool(); var Agent = require_agent(); - var ProxyAgent = require_proxy_agent(); + var ProxyAgent2 = require_proxy_agent(); var EnvHttpProxyAgent = require_env_http_proxy_agent(); var RetryAgent = require_retry_agent(); var errors = require_errors(); @@ -18771,7 +19011,7 @@ var require_undici = __commonJS({ module2.exports.Pool = Pool; module2.exports.BalancedPool = BalancedPool; module2.exports.Agent = Agent; - module2.exports.ProxyAgent = ProxyAgent; + module2.exports.ProxyAgent = ProxyAgent2; module2.exports.EnvHttpProxyAgent = EnvHttpProxyAgent; module2.exports.RetryAgent = RetryAgent; module2.exports.RetryHandler = RetryHandler; @@ -18832,9 +19072,9 @@ var require_undici = __commonJS({ module2.exports.setGlobalDispatcher = setGlobalDispatcher; module2.exports.getGlobalDispatcher = getGlobalDispatcher; var fetchImpl = require_fetch().fetch; - module2.exports.fetch = async function fetch(init, options = void 0) { + module2.exports.fetch = async function fetch(init2, options = void 0) { try { - return await fetchImpl(init, options); + return await fetchImpl(init2, options); } catch (err) { if (err && typeof err === "object") { Error.captureStackTrace(err); @@ -21401,7 +21641,7 @@ var require_core = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.platform = exports2.toPlatformPath = exports2.toWin32Path = exports2.toPosixPath = exports2.markdownSummary = exports2.summary = exports2.ExitCode = void 0; - exports2.exportVariable = exportVariable15; + exports2.exportVariable = exportVariable16; exports2.setSecret = setSecret2; exports2.addPath = addPath2; exports2.getInput = getInput2; @@ -21433,7 +21673,7 @@ var require_core = __commonJS({ ExitCode2[ExitCode2["Success"] = 0] = "Success"; ExitCode2[ExitCode2["Failure"] = 1] = "Failure"; })(ExitCode || (exports2.ExitCode = ExitCode = {})); - function exportVariable15(name, val) { + function exportVariable16(name, val) { const convertedVal = (0, utils_1.toCommandValue)(val); process.env[name] = convertedVal; const filePath = process.env["GITHUB_ENV"] || ""; @@ -21891,12 +22131,12 @@ var init_universal_user_agent2 = __esm({ }); // node_modules/@octokit/endpoint/dist-bundle/index.js -function lowercaseKeys(object) { - if (!object) { +function lowercaseKeys(object2) { + if (!object2) { return {}; } - return Object.keys(object).reduce((newObj, key) => { - newObj[key.toLowerCase()] = object[key]; + return Object.keys(object2).reduce((newObj, key) => { + newObj[key.toLowerCase()] = object2[key]; return newObj; }, {}); } @@ -21908,12 +22148,12 @@ function isPlainObject(value) { const Ctor = Object.prototype.hasOwnProperty.call(proto, "constructor") && proto.constructor; return typeof Ctor === "function" && Ctor instanceof Ctor && Function.prototype.call(Ctor) === Function.prototype.call(value); } -function mergeDeep(defaults2, options) { - const result = Object.assign({}, defaults2); +function mergeDeep(defaults3, options) { + const result = Object.assign({}, defaults3); Object.keys(options).forEach((key) => { if (isPlainObject(options[key])) { - if (!(key in defaults2)) Object.assign(result, { [key]: options[key] }); - else result[key] = mergeDeep(defaults2[key], options[key]); + if (!(key in defaults3)) Object.assign(result, { [key]: options[key] }); + else result[key] = mergeDeep(defaults3[key], options[key]); } else { Object.assign(result, { [key]: options[key] }); } @@ -21928,7 +22168,7 @@ function removeUndefinedProperties(obj) { } return obj; } -function merge(defaults2, route, options) { +function merge(defaults3, route, options) { if (typeof route === "string") { let [method, url2] = route.split(" "); options = Object.assign(url2 ? { method, url: url2 } : { url: method }, options); @@ -21938,10 +22178,10 @@ function merge(defaults2, route, options) { options.headers = lowercaseKeys(options.headers); removeUndefinedProperties(options); removeUndefinedProperties(options.headers); - const mergedOptions = mergeDeep(defaults2 || {}, options); + const mergedOptions = mergeDeep(defaults3 || {}, options); if (options.url === "/graphql") { - if (defaults2 && defaults2.mediaType.previews?.length) { - mergedOptions.mediaType.previews = defaults2.mediaType.previews.filter( + if (defaults3 && defaults3.mediaType.previews?.length) { + mergedOptions.mediaType.previews = defaults3.mediaType.previews.filter( (preview) => !mergedOptions.mediaType.previews.includes(preview) ).concat(mergedOptions.mediaType.previews); } @@ -21972,11 +22212,11 @@ function extractUrlVariableNames(url2) { } return matches.map(removeNonChars).reduce((a, b) => a.concat(b), []); } -function omit(object, keysToOmit) { +function omit(object2, keysToOmit) { const result = { __proto__: null }; - for (const key of Object.keys(object)) { + for (const key of Object.keys(object2)) { if (keysToOmit.indexOf(key) === -1) { - result[key] = object[key]; + result[key] = object2[key]; } } return result; @@ -22011,7 +22251,7 @@ function isKeyOperator(operator) { function getValues(context5, operator, key, modifier) { var value = context5[key], result = []; if (isDefined(value) && value !== "") { - if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { + if (typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") { value = value.toString(); if (modifier && modifier !== "*") { value = value.substring(0, parseInt(modifier, 10)); @@ -22174,8 +22414,8 @@ function parse(options) { options.request ? { request: options.request } : null ); } -function endpointWithDefaults(defaults2, route, options) { - return parse(merge(defaults2, route, options)); +function endpointWithDefaults(defaults3, route, options) { + return parse(merge(defaults3, route, options)); } function withDefaults(oldDefaults, newDefaults) { const DEFAULTS2 = merge(oldDefaults, newDefaults); @@ -22224,99 +22464,474 @@ var init_universal_user_agent3 = __esm({ } }); -// node_modules/fast-content-type-parse/index.js -var require_fast_content_type_parse = __commonJS({ - "node_modules/fast-content-type-parse/index.js"(exports2, module2) { +// node_modules/content-type/dist/index.js +var require_dist = __commonJS({ + "node_modules/content-type/dist/index.js"(exports2) { "use strict"; - var NullObject = function NullObject2() { - }; - NullObject.prototype = /* @__PURE__ */ Object.create(null); - var paramRE = /; *([!#$%&'*+.^\w`|~-]+)=("(?:[\v\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\v\u0020-\u00ff])*"|[!#$%&'*+.^\w`|~-]+) */gu; - var quotedPairRE = /\\([\v\u0020-\u00ff])/gu; - var mediaTypeRE = /^[!#$%&'*+.^\w|~-]+\/[!#$%&'*+.^\w|~-]+$/u; - var defaultContentType = { type: "", parameters: new NullObject() }; - Object.freeze(defaultContentType.parameters); - Object.freeze(defaultContentType); - function parse2(header) { - if (typeof header !== "string") { - throw new TypeError("argument header is required and must be a string"); - } - let index2 = header.indexOf(";"); - const type = index2 !== -1 ? header.slice(0, index2).trim() : header.trim(); - if (mediaTypeRE.test(type) === false) { - throw new TypeError("invalid media type"); - } - const result = { - type: type.toLowerCase(), - parameters: new NullObject() - }; - if (index2 === -1) { - return result; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.format = format; + exports2.parse = parse3; + var TEXT_REGEXP = /^[\u0009\u0020-\u007e\u0080-\u00ff]*$/; + var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/; + var QUOTE_REGEXP = /[\\"]/g; + var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/; + var NullObject = /* @__PURE__ */ (() => { + const C = function() { + }; + C.prototype = /* @__PURE__ */ Object.create(null); + return C; + })(); + function format(obj) { + const { type, parameters } = obj; + if (!type || !TYPE_REGEXP.test(type)) { + throw new TypeError(`Invalid type: ${type}`); } - let key; - let match2; - let value; - paramRE.lastIndex = index2; - while (match2 = paramRE.exec(header)) { - if (match2.index !== index2) { - throw new TypeError("invalid parameter format"); + let result = type; + if (parameters) { + for (const param of Object.keys(parameters)) { + if (!TOKEN_REGEXP.test(param)) { + throw new TypeError(`Invalid parameter name: ${param}`); + } + result += `; ${param}=${qstring(parameters[param])}`; } - index2 += match2[0].length; - key = match2[1].toLowerCase(); - value = match2[2]; - if (value[0] === '"') { - value = value.slice(1, value.length - 1); - quotedPairRE.test(value) && (value = value.replace(quotedPairRE, "$1")); + } + return result; + } + function parse3(header, options) { + const len = header.length; + let index2 = skipOWS(header, 0, len); + const valueStart = index2; + index2 = skipValue(header, index2, len); + const valueEnd = trailingOWS(header, valueStart, index2); + const type = header.slice(valueStart, valueEnd).toLowerCase(); + const parameters = options?.parameters === false ? new NullObject() : parseParameters(header, index2, len); + return { type, parameters }; + } + var SP = 32; + var HTAB = 9; + var SEMI = 59; + var EQ = 61; + var DQUOTE = 34; + var BSLASH = 92; + function parseParameters(header, index2, len) { + const parameters = new NullObject(); + parameter: while (index2 < len) { + index2 = skipOWS(header, index2 + 1, len); + const keyStart = index2; + while (index2 < len) { + const code = header.charCodeAt(index2); + if (code === SEMI) + continue parameter; + if (code === EQ) { + const keyEnd = trailingOWS(header, keyStart, index2); + const key = header.slice(keyStart, keyEnd).toLowerCase(); + index2 = skipOWS(header, index2 + 1, len); + if (index2 < len && header.charCodeAt(index2) === DQUOTE) { + index2++; + let value = ""; + while (index2 < len) { + const code2 = header.charCodeAt(index2++); + if (code2 === DQUOTE) { + index2 = skipValue(header, index2, len); + if (parameters[key] === void 0) + parameters[key] = value; + break; + } + if (code2 === BSLASH && index2 < len) { + value += header[index2++]; + continue; + } + value += String.fromCharCode(code2); + } + continue parameter; + } + const valueStart = index2; + index2 = skipValue(header, index2, len); + if (parameters[key] === void 0) { + const valueEnd = trailingOWS(header, valueStart, index2); + parameters[key] = header.slice(valueStart, valueEnd); + } + continue parameter; + } + index2++; } - result.parameters[key] = value; } - if (index2 !== header.length) { - throw new TypeError("invalid parameter format"); + return parameters; + } + function skipValue(str, index2, len) { + while (index2 < len) { + const char = str.charCodeAt(index2); + if (char === SEMI) + break; + index2++; } - return result; + return index2; } - function safeParse2(header) { - if (typeof header !== "string") { - return defaultContentType; + function skipOWS(header, index2, len) { + while (index2 < len) { + const char = header.charCodeAt(index2); + if (char !== SP && char !== HTAB) + break; + index2++; } - let index2 = header.indexOf(";"); - const type = index2 !== -1 ? header.slice(0, index2).trim() : header.trim(); - if (mediaTypeRE.test(type) === false) { - return defaultContentType; + return index2; + } + function trailingOWS(header, start, end) { + while (end > start) { + const char = header.charCodeAt(end - 1); + if (char !== SP && char !== HTAB) + break; + end--; } - const result = { - type: type.toLowerCase(), - parameters: new NullObject() + return end; + } + function qstring(str) { + if (TOKEN_REGEXP.test(str)) + return str; + if (TEXT_REGEXP.test(str)) + return `"${str.replace(QUOTE_REGEXP, "\\$&")}"`; + throw new TypeError(`Invalid parameter value: ${str}`); + } + } +}); + +// node_modules/json-with-bigint/json-with-bigint.js +var intRegex, noiseValue, originalStringify, originalParse, customFormat, bigIntsStringify, noiseStringify, isUnstringifiable, isRawJSON, stringifyIteratively, JSONStringify, featureCache, isContextSourceSupported, convertMarkedBigIntsReviver, JSONParseV2, MAX_INT, MAX_DIGITS, stringsOrLargeNumbers, noiseValueWithQuotes, applyReviverIteratively, serializeBigInts, JSONParse; +var init_json_with_bigint = __esm({ + "node_modules/json-with-bigint/json-with-bigint.js"() { + intRegex = /^-?\d+$/; + noiseValue = /^-?\d+n+$/; + originalStringify = JSON.stringify; + originalParse = JSON.parse; + customFormat = /^-?\d+n$/; + bigIntsStringify = /([\[:])?"(-?\d+)n"($|\s*[,\}\]])/g; + noiseStringify = /([\[:])?("-?\d+n+)n("$|"\s*[,\}\]])/g; + isUnstringifiable = (val) => val === void 0 || typeof val === "function" || typeof val === "symbol"; + isRawJSON = (val) => val !== null && typeof val === "object" && val.constructor && val.constructor.name === "RawJSON"; + stringifyIteratively = (rootValue, replacer, spaceParam) => { + let space2 = ""; + if (typeof spaceParam === "number") { + space2 = " ".repeat(Math.min(10, Math.max(0, Math.floor(spaceParam)))); + } else if (typeof spaceParam === "string") { + space2 = spaceParam.slice(0, 10); + } + const isFunctionReplacer = typeof replacer === "function"; + const propertyList = Array.isArray(replacer) ? new Set(replacer.map(String)) : null; + const prepareVal = (parent, key, val) => { + const isObject2 = val !== null && typeof val === "object"; + const hasToJSON = isObject2 && typeof val.toJSON === "function"; + if (hasToJSON) { + val = val.toJSON(key); + } + const isNoise = typeof val === "string" && noiseValue.test(val); + if (isNoise) return val + "n"; + const isBigInt = typeof val === "bigint"; + if (isBigInt) { + const supportsRawJSON = "rawJSON" in JSON; + if (supportsRawJSON) return JSON.rawJSON(val.toString()); + return val.toString() + "n"; + } + if (isFunctionReplacer) { + val = replacer.call(parent, key, val); + } + const isPostReplacerObject = val !== null && typeof val === "object"; + if (isPostReplacerObject) { + const isPrimitiveWrapper = val instanceof Number || val instanceof String || val instanceof Boolean; + if (isPrimitiveWrapper) { + val = val.valueOf(); + } + } + return val; }; - if (index2 === -1) { - return result; + const rootProcessed = prepareVal({ "": rootValue }, "", rootValue); + if (isUnstringifiable(rootProcessed)) { + return void 0; } - let key; - let match2; - let value; - paramRE.lastIndex = index2; - while (match2 = paramRE.exec(header)) { - if (match2.index !== index2) { - return defaultContentType; + const isRootPrimitive = rootProcessed === null || typeof rootProcessed !== "object"; + const isRootNativeRawJSON = isRawJSON(rootProcessed); + if (isRootPrimitive || isRootNativeRawJSON) { + return originalStringify(rootProcessed); + } + const chunks = []; + let level = 0; + const stack = [ + { + parent: { "": rootProcessed }, + key: "", + val: rootProcessed, + isArray: Array.isArray(rootProcessed), + keys: Array.isArray(rootProcessed) ? null : Object.keys(rootProcessed), + index: 0, + first: true } - index2 += match2[0].length; - key = match2[1].toLowerCase(); - value = match2[2]; - if (value[0] === '"') { - value = value.slice(1, value.length - 1); - quotedPairRE.test(value) && (value = value.replace(quotedPairRE, "$1")); + ]; + const visited = new WeakSet([rootProcessed]); + while (stack.length > 0) { + const node = stack[stack.length - 1]; + if (node.index === 0) { + chunks.push(node.isArray ? "[" : "{"); + level++; + } + let isDone = false; + if (node.isArray) { + if (node.index < node.val.length) { + if (!node.first) chunks.push(","); + if (space2) chunks.push("\n" + space2.repeat(level)); + const childRaw = node.val[node.index]; + const childVal = prepareVal(node.val, String(node.index), childRaw); + if (isUnstringifiable(childVal)) { + chunks.push("null"); + node.first = false; + node.index++; + } else { + const isComplexObject = childVal !== null && typeof childVal === "object"; + const isNativeRaw = isRawJSON(childVal); + if (isComplexObject && !isNativeRaw) { + if (visited.has(childVal)) { + throw new TypeError("Converting circular structure to JSON"); + } + visited.add(childVal); + stack.push({ + parent: node.val, + key: String(node.index), + val: childVal, + isArray: Array.isArray(childVal), + keys: Array.isArray(childVal) ? null : Object.keys(childVal), + index: 0, + first: true + }); + node.first = false; + node.index++; + } else { + chunks.push(originalStringify(childVal)); + node.first = false; + node.index++; + } + } + } else { + isDone = true; + } + } else { + while (node.index < node.keys.length) { + const k = node.keys[node.index++]; + const isFilteredOutByArray = propertyList && !propertyList.has(k); + if (isFilteredOutByArray) continue; + const childRaw = node.val[k]; + const childVal = prepareVal(node.val, k, childRaw); + if (isUnstringifiable(childVal)) continue; + if (!node.first) chunks.push(","); + if (space2) { + chunks.push("\n" + space2.repeat(level) + originalStringify(k) + ": "); + } else { + chunks.push(originalStringify(k) + ":"); + } + const isComplexObject = childVal !== null && typeof childVal === "object"; + const isNativeRaw = isRawJSON(childVal); + if (isComplexObject && !isNativeRaw) { + if (visited.has(childVal)) { + throw new TypeError("Converting circular structure to JSON"); + } + visited.add(childVal); + stack.push({ + parent: node.val, + key: k, + val: childVal, + isArray: Array.isArray(childVal), + keys: Array.isArray(childVal) ? null : Object.keys(childVal), + index: 0, + first: true + }); + node.first = false; + break; + } else { + chunks.push(originalStringify(childVal)); + node.first = false; + } + } + const isNodeFullyProcessed = node.index >= node.keys.length && stack[stack.length - 1] === node; + if (isNodeFullyProcessed) { + isDone = true; + } + } + if (isDone) { + level--; + if (!node.first && space2) chunks.push("\n" + space2.repeat(level)); + chunks.push(node.isArray ? "]" : "}"); + visited.delete(node.val); + stack.pop(); } - result.parameters[key] = value; } - if (index2 !== header.length) { - return defaultContentType; + return chunks.join(""); + }; + JSONStringify = (value, replacer, space2) => { + try { + const supportsRawJSON = "rawJSON" in JSON; + if (supportsRawJSON) { + return originalStringify( + value, + (key, val) => { + if (typeof val === "bigint") return JSON.rawJSON(val.toString()); + const hasFunctionReplacer = typeof replacer === "function"; + if (hasFunctionReplacer) return replacer(key, val); + const isKeyInArrayReplacer = Array.isArray(replacer) && replacer.includes(key); + if (isKeyInArrayReplacer) return val; + return val; + }, + space2 + ); + } + if (!value) return originalStringify(value, replacer, space2); + const convertedToCustomJSON = originalStringify( + value, + (key, val) => { + const isNoise = typeof val === "string" && noiseValue.test(val); + if (isNoise) return val.toString() + "n"; + if (typeof val === "bigint") return val.toString() + "n"; + const hasFunctionReplacer = typeof replacer === "function"; + if (hasFunctionReplacer) return replacer(key, val); + const isKeyInArrayReplacer = Array.isArray(replacer) && replacer.includes(key); + if (isKeyInArrayReplacer) return val; + return val; + }, + space2 + ); + const processedJSON = convertedToCustomJSON.replace( + bigIntsStringify, + "$1$2$3" + ); + const denoisedJSON = processedJSON.replace(noiseStringify, "$1$2$3"); + return denoisedJSON; + } catch (error3) { + if (error3 instanceof RangeError) { + const convertedJSON = stringifyIteratively(value, replacer, space2); + if (convertedJSON === void 0) return void 0; + const supportsRawJSON = "rawJSON" in JSON; + if (supportsRawJSON) return convertedJSON; + const processedJSON = convertedJSON.replace(bigIntsStringify, "$1$2$3"); + return processedJSON.replace(noiseStringify, "$1$2$3"); + } + throw error3; } - return result; - } - module2.exports.default = { parse: parse2, safeParse: safeParse2 }; - module2.exports.parse = parse2; - module2.exports.safeParse = safeParse2; - module2.exports.defaultContentType = defaultContentType; + }; + featureCache = /* @__PURE__ */ new Map(); + isContextSourceSupported = () => { + const parseFingerprint = JSON.parse.toString(); + if (featureCache.has(parseFingerprint)) { + return featureCache.get(parseFingerprint); + } + try { + const result = JSON.parse( + "1", + (_2, __, context5) => !!context5?.source && context5.source === "1" + ); + featureCache.set(parseFingerprint, result); + return result; + } catch { + featureCache.set(parseFingerprint, false); + return false; + } + }; + convertMarkedBigIntsReviver = (key, value, context5, userReviver) => { + const isCustomFormatBigInt = typeof value === "string" && customFormat.test(value); + if (isCustomFormatBigInt) return BigInt(value.slice(0, -1)); + const isNoiseValue = typeof value === "string" && noiseValue.test(value); + if (isNoiseValue) return value.slice(0, -1); + const hasUserReviver = typeof userReviver === "function"; + if (!hasUserReviver) return value; + return userReviver(key, value, context5); + }; + JSONParseV2 = (text, reviver) => { + return JSON.parse(text, (key, value, context5) => { + const isNumber2 = typeof value === "number"; + const isOutOfBounds = value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER; + const isBigNumber = isNumber2 && isOutOfBounds; + const isInt = context5 && intRegex.test(context5.source); + const isBigInt = isBigNumber && isInt; + if (isBigInt) return BigInt(context5.source); + const hasCustomReviver = typeof reviver === "function"; + if (!hasCustomReviver) return value; + return reviver(key, value, context5); + }); + }; + MAX_INT = Number.MAX_SAFE_INTEGER.toString(); + MAX_DIGITS = MAX_INT.length; + stringsOrLargeNumbers = /"(?:\\.|[^"])*"|-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/g; + noiseValueWithQuotes = /^"-?\d+n+"$/; + applyReviverIteratively = (parsed, userReviver) => { + const rootHolder = { "": parsed }; + const stack = [{ parent: rootHolder, key: "", visited: false }]; + while (stack.length > 0) { + const node = stack[stack.length - 1]; + if (!node.visited) { + node.visited = true; + const value = node.parent[node.key]; + const isComplexObject = value !== null && typeof value === "object"; + if (isComplexObject) { + const keys = Object.keys(value); + for (let i = keys.length - 1; i >= 0; i--) { + stack.push({ parent: value, key: keys[i], visited: false }); + } + } + } else { + const { parent, key } = node; + let value = parent[key]; + if (typeof value === "string") { + const isCustomFormatBigInt = customFormat.test(value); + if (isCustomFormatBigInt) { + value = BigInt(value.slice(0, -1)); + } else { + const isNoise = noiseValue.test(value); + if (isNoise) value = value.slice(0, -1); + } + } + const hasUserReviver = typeof userReviver === "function"; + if (hasUserReviver) { + value = userReviver.call(parent, key, value); + } + const isDeleted = value === void 0; + if (isDeleted) { + delete parent[key]; + } else { + parent[key] = value; + } + stack.pop(); + } + } + return rootHolder[""]; + }; + serializeBigInts = (text) => { + return text.replace( + stringsOrLargeNumbers, + (match2, digits, fractional, exponential) => { + const isString3 = match2[0] === '"'; + const isNoise = isString3 && noiseValueWithQuotes.test(match2); + if (isNoise) return match2.substring(0, match2.length - 1) + 'n"'; + const hasFractionalOrExponential = fractional || exponential; + const isLessThanMaxSafeInt = digits && (digits.length < MAX_DIGITS || digits.length === MAX_DIGITS && digits <= MAX_INT); + const isStandardValue = isString3 || hasFractionalOrExponential || isLessThanMaxSafeInt; + if (isStandardValue) return match2; + return '"' + match2 + 'n"'; + } + ); + }; + JSONParse = (text, reviver) => { + if (!text) return originalParse(text, reviver); + try { + if (isContextSourceSupported()) return JSONParseV2(text, reviver); + const serializedData = serializeBigInts(text); + return originalParse( + serializedData, + (key, value, context5) => convertMarkedBigIntsReviver(key, value, context5, reviver) + ); + } catch (error3) { + if (error3 instanceof RangeError) { + const serializedData = serializeBigInts(text); + const parsed = originalParse(serializedData); + return applyReviverIteratively(parsed, reviver); + } + throw error3; + } + }; } }); @@ -22382,7 +22997,7 @@ async function fetchWrapper(requestOptions) { } const log = requestOptions.request?.log || console; const parseSuccessResponseBody = requestOptions.request?.parseSuccessResponseBody !== false; - const body = isPlainObject2(requestOptions.body) || Array.isArray(requestOptions.body) ? JSON.stringify(requestOptions.body) : requestOptions.body; + const body = isPlainObject2(requestOptions.body) || Array.isArray(requestOptions.body) ? JSONStringify(requestOptions.body) : requestOptions.body; const requestHeaders = Object.fromEntries( Object.entries(requestOptions.headers).map(([name, value]) => [ name, @@ -22476,16 +23091,19 @@ async function getResponseData(response) { if (!contentType) { return response.text().catch(noop); } - const mimetype = (0, import_fast_content_type_parse.safeParse)(contentType); + const mimetype = (0, import_content_type.parse)(contentType); if (isJSONResponse(mimetype)) { let text = ""; try { text = await response.text(); - return JSON.parse(text); + return JSONParse(text); } catch (err) { return text; } - } else if (mimetype.type.startsWith("text/") || mimetype.parameters.charset?.toLowerCase() === "utf-8") { + } else if (mimetype.type.startsWith("text/") || // `application/octet-stream` is the canonical "arbitrary binary" type + // (RFC 2046) and must never be decoded as text, even when the response + // carries a (misleading) `charset=utf-8` parameter — see #751. + mimetype.parameters.charset?.toLowerCase() === "utf-8" && mimetype.type !== "application/octet-stream") { return response.text().catch(noop); } else { return response.arrayBuffer().catch( @@ -22504,9 +23122,10 @@ function toErrorMessage(data) { if (data instanceof ArrayBuffer) { return "Unknown error"; } - if ("message" in data) { - const suffix = "documentation_url" in data ? ` - ${data.documentation_url}` : ""; - return Array.isArray(data.errors) ? `${data.message}: ${data.errors.map((v) => JSON.stringify(v)).join(", ")}${suffix}` : `${data.message}${suffix}`; + if (typeof data === "object" && data !== null && "message" in data) { + const objectData = data; + const suffix = "documentation_url" in objectData ? ` - ${objectData.documentation_url}` : ""; + return Array.isArray(objectData.errors) ? `${objectData.message}: ${objectData.errors.map((v) => JSON.stringify(v)).join(", ")}${suffix}` : `${objectData.message}${suffix}`; } return `Unknown error: ${JSON.stringify(data)}`; } @@ -22533,14 +23152,15 @@ function withDefaults2(oldEndpoint, newDefaults) { defaults: withDefaults2.bind(null, endpoint2) }); } -var import_fast_content_type_parse, VERSION2, defaults_default, noop, request; +var import_content_type, VERSION2, defaults_default, noop, request; var init_dist_bundle2 = __esm({ "node_modules/@octokit/request/dist-bundle/index.js"() { init_dist_bundle(); init_universal_user_agent3(); - import_fast_content_type_parse = __toESM(require_fast_content_type_parse(), 1); + import_content_type = __toESM(require_dist(), 1); + init_json_with_bigint(); init_dist_src(); - VERSION2 = "10.0.7"; + VERSION2 = "10.0.13"; defaults_default = { headers: { "user-agent": `octokit-request.js/${VERSION2} ${getUserAgent3()}` @@ -22654,6 +23274,9 @@ var init_dist_bundle3 = __esm({ Error.captureStackTrace(this, this.constructor); } } + request; + headers; + response; name = "GraphqlResponseError"; errors; data; @@ -22734,7 +23357,7 @@ var init_dist_bundle4 = __esm({ var VERSION4; var init_version = __esm({ "node_modules/@octokit/core/dist-src/version.js"() { - VERSION4 = "7.0.6"; + VERSION4 = "7.0.7"; } }); @@ -22774,21 +23397,21 @@ var init_dist_src2 = __esm({ userAgentTrail = `octokit-core.js/${VERSION4} ${getUserAgent()}`; Octokit = class { static VERSION = VERSION4; - static defaults(defaults2) { + static defaults(defaults3) { const OctokitWithDefaults = class extends this { constructor(...args) { const options = args[0] || {}; - if (typeof defaults2 === "function") { - super(defaults2(options)); + if (typeof defaults3 === "function") { + super(defaults3(options)); return; } super( Object.assign( {}, - defaults2, + defaults3, options, - options.userAgent && defaults2.userAgent ? { - userAgent: `${options.userAgent} ${defaults2.userAgent}` + options.userAgent && defaults3.userAgent ? { + userAgent: `${options.userAgent} ${defaults3.userAgent}` } : null ) ); @@ -25200,8 +25823,8 @@ function endpointsToMethods(octokit) { } return newMethods; } -function decorate(octokit, scope, methodName, defaults2, decorations) { - const requestWithDefaults = octokit.request.defaults(defaults2); +function decorate(octokit, scope, methodName, defaults3, decorations) { + const requestWithDefaults = octokit.request.defaults(defaults3); function withDecorations(...args) { let options = requestWithDefaults.endpoint.merge(...args); if (decorations.mapToData) { @@ -25248,14 +25871,14 @@ var init_endpoints_to_methods = __esm({ endpointMethodsMap = /* @__PURE__ */ new Map(); for (const [scope, endpoints] of Object.entries(endpoints_default)) { for (const [methodName, endpoint2] of Object.entries(endpoints)) { - const [route, defaults2, decorations] = endpoint2; + const [route, defaults3, decorations] = endpoint2; const [method, url2] = route.split(/ /); const endpointDefaults = Object.assign( { method, url: url2 }, - defaults2 + defaults3 ); if (!endpointMethodsMap.has(scope)) { endpointMethodsMap.set(scope, /* @__PURE__ */ new Map()); @@ -26352,7 +26975,7 @@ var require_parse2 = __commonJS({ "node_modules/semver/functions/parse.js"(exports2, module2) { "use strict"; var SemVer = require_semver(); - var parse2 = (version, options, throwErrors = false) => { + var parse3 = (version, options, throwErrors = false) => { if (version instanceof SemVer) { return version; } @@ -26365,7 +26988,7 @@ var require_parse2 = __commonJS({ throw er; } }; - module2.exports = parse2; + module2.exports = parse3; } }); @@ -26373,9 +26996,9 @@ var require_parse2 = __commonJS({ var require_valid = __commonJS({ "node_modules/semver/functions/valid.js"(exports2, module2) { "use strict"; - var parse2 = require_parse2(); + var parse3 = require_parse2(); var valid4 = (version, options) => { - const v = parse2(version, options); + const v = parse3(version, options); return v ? v.version : null; }; module2.exports = valid4; @@ -26386,9 +27009,9 @@ var require_valid = __commonJS({ var require_clean = __commonJS({ "node_modules/semver/functions/clean.js"(exports2, module2) { "use strict"; - var parse2 = require_parse2(); + var parse3 = require_parse2(); var clean3 = (version, options) => { - const s = parse2(version.trim().replace(/^[=v]+/, ""), options); + const s = parse3(version.trim().replace(/^[=v]+/, ""), options); return s ? s.version : null; }; module2.exports = clean3; @@ -26423,10 +27046,10 @@ var require_inc = __commonJS({ var require_diff = __commonJS({ "node_modules/semver/functions/diff.js"(exports2, module2) { "use strict"; - var parse2 = require_parse2(); + var parse3 = require_parse2(); var diff = (version1, version2) => { - const v1 = parse2(version1, null, true); - const v2 = parse2(version2, null, true); + const v1 = parse3(version1, null, true); + const v2 = parse3(version2, null, true); const comparison = v1.compare(v2); if (comparison === 0) { return null; @@ -26497,9 +27120,9 @@ var require_patch = __commonJS({ var require_prerelease = __commonJS({ "node_modules/semver/functions/prerelease.js"(exports2, module2) { "use strict"; - var parse2 = require_parse2(); + var parse3 = require_parse2(); var prerelease = (version, options) => { - const parsed = parse2(version, options); + const parsed = parse3(version, options); return parsed && parsed.prerelease.length ? parsed.prerelease : null; }; module2.exports = prerelease; @@ -26685,7 +27308,7 @@ var require_coerce = __commonJS({ "node_modules/semver/functions/coerce.js"(exports2, module2) { "use strict"; var SemVer = require_semver(); - var parse2 = require_parse2(); + var parse3 = require_parse2(); var { safeRe: re, t } = require_re(); var coerce3 = (version, options) => { if (version instanceof SemVer) { @@ -26720,7 +27343,7 @@ var require_coerce = __commonJS({ const patch = match2[4] || "0"; const prerelease = options.includePrerelease && match2[5] ? `-${match2[5]}` : ""; const build2 = options.includePrerelease && match2[6] ? `+${match2[6]}` : ""; - return parse2(`${major}.${minor}.${patch}${prerelease}${build2}`, options); + return parse3(`${major}.${minor}.${patch}${prerelease}${build2}`, options); }; module2.exports = coerce3; } @@ -26730,7 +27353,7 @@ var require_coerce = __commonJS({ var require_truncate = __commonJS({ "node_modules/semver/functions/truncate.js"(exports2, module2) { "use strict"; - var parse2 = require_parse2(); + var parse3 = require_parse2(); var constants = require_constants6(); var SemVer = require_semver(); var truncate = (version, truncation, options) => { @@ -26742,7 +27365,7 @@ var require_truncate = __commonJS({ }; var cloneInputVersion = (version, options) => { const versionStringToParse = version instanceof SemVer ? version.version : version; - return parse2(versionStringToParse, options); + return parse3(versionStringToParse, options); }; var doTruncation = (version, truncation) => { if (isPrerelease(truncation)) { @@ -27000,15 +27623,16 @@ var require_range = __commonJS({ }; var replaceTilde = (comp, options) => { const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]; + const z = options.includePrerelease ? "-0" : ""; return comp.replace(r, (_2, M, m, p, pr) => { debug6("tilde", comp, _2, M, m, p, pr); let ret; if (isX(M)) { ret = ""; } else if (isX(m)) { - ret = `>=${M}.0.0 <${+M + 1}.0.0-0`; + ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`; } else if (isX(p)) { - ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`; + ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`; } else if (pr) { debug6("replaceTilde pr", pr); ret = `>=${M}.${m}.${p}-${pr} <${M}.${+m + 1}.0-0`; @@ -27785,7 +28409,7 @@ var require_semver2 = __commonJS({ var constants = require_constants6(); var SemVer = require_semver(); var identifiers = require_identifiers(); - var parse2 = require_parse2(); + var parse3 = require_parse2(); var valid4 = require_valid(); var clean3 = require_clean(); var inc = require_inc(); @@ -27824,7 +28448,7 @@ var require_semver2 = __commonJS({ var simplifyRange = require_simplify(); var subset = require_subset(); module2.exports = { - parse: parse2, + parse: parse3, valid: valid4, clean: clean3, inc, @@ -27885,19 +28509,19 @@ var require_light = __commonJS({ function getCjsExportFromNamespace(n) { return n && n["default"] || n; } - var load2 = function(received, defaults2, onto = {}) { + var load2 = function(received, defaults3, onto = {}) { var k, ref, v; - for (k in defaults2) { - v = defaults2[k]; + for (k in defaults3) { + v = defaults3[k]; onto[k] = (ref = received[k]) != null ? ref : v; } return onto; }; - var overwrite = function(received, defaults2, onto = {}) { + var overwrite = function(received, defaults3, onto = {}) { var k, v; for (k in received) { v = received[k]; - if (defaults2[k] !== void 0) { + if (defaults3[k] !== void 0) { onto[k] = v; } } @@ -29457,9 +30081,9 @@ var require_helpers = __commonJS({ } } function deepMerge(target, src) { - var array = Array.isArray(src); - var dst = array && [] || {}; - if (array) { + var array2 = Array.isArray(src); + var dst = array2 && [] || {}; + if (array2) { target = target || []; dst = dst.concat(target); src.forEach(deepMerger.bind(null, target, dst)); @@ -29488,13 +30112,13 @@ var require_helpers = __commonJS({ exports2.encodePath = function encodePointer(a) { return a.map(pathEncoder).join(""); }; - exports2.getDecimalPlaces = function getDecimalPlaces(number) { + exports2.getDecimalPlaces = function getDecimalPlaces(number2) { var decimalPlaces = 0; - if (isNaN(number)) return decimalPlaces; - if (typeof number !== "number") { - number = Number(number); + if (isNaN(number2)) return decimalPlaces; + if (typeof number2 !== "number") { + number2 = Number(number2); } - var parts = number.toString().split("e"); + var parts = number2.toString().split("e"); if (parts.length === 2) { if (parts[1][0] !== "-") { return decimalPlaces; @@ -29694,11 +30318,11 @@ var require_attribute = __commonJS({ } return result; }; - function getEnumerableProperty(object, key) { - if (Object.hasOwnProperty.call(object, key)) return object[key]; - if (!(key in object)) return; - while (object = Object.getPrototypeOf(object)) { - if (Object.propertyIsEnumerable.call(object, key)) return object[key]; + function getEnumerableProperty(object2, key) { + if (Object.hasOwnProperty.call(object2, key)) return object2[key]; + if (!(key in object2)) return; + while (object2 = Object.getPrototypeOf(object2)) { + if (Object.propertyIsEnumerable.call(object2, key)) return object2[key]; } } validators.propertyNames = function validatePropertyNames(instance, schema, options, ctx) { @@ -30318,7 +30942,7 @@ var require_validator = __commonJS({ Validator3.prototype.getSchema = function getSchema(urn) { return this.schemas[urn]; }; - Validator3.prototype.validate = function validate(instance, schema, options, ctx) { + Validator3.prototype.validate = function validate2(instance, schema, options, ctx) { if (typeof schema !== "boolean" && typeof schema !== "object" || schema === null) { throw new SchemaError("Expected `schema` to be an object or boolean"); } @@ -30692,7 +31316,7 @@ var require_internal_glob_options_helper = __commonJS({ })(); Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getOptions = getOptions; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); function getOptions(copy) { const result = { followSymbolicLinks: true, @@ -30704,23 +31328,23 @@ var require_internal_glob_options_helper = __commonJS({ if (copy) { if (typeof copy.followSymbolicLinks === "boolean") { result.followSymbolicLinks = copy.followSymbolicLinks; - core30.debug(`followSymbolicLinks '${result.followSymbolicLinks}'`); + core31.debug(`followSymbolicLinks '${result.followSymbolicLinks}'`); } if (typeof copy.implicitDescendants === "boolean") { result.implicitDescendants = copy.implicitDescendants; - core30.debug(`implicitDescendants '${result.implicitDescendants}'`); + core31.debug(`implicitDescendants '${result.implicitDescendants}'`); } if (typeof copy.matchDirectories === "boolean") { result.matchDirectories = copy.matchDirectories; - core30.debug(`matchDirectories '${result.matchDirectories}'`); + core31.debug(`matchDirectories '${result.matchDirectories}'`); } if (typeof copy.omitBrokenSymbolicLinks === "boolean") { result.omitBrokenSymbolicLinks = copy.omitBrokenSymbolicLinks; - core30.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`); + core31.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`); } if (typeof copy.excludeHiddenFiles === "boolean") { result.excludeHiddenFiles = copy.excludeHiddenFiles; - core30.debug(`excludeHiddenFiles '${result.excludeHiddenFiles}'`); + core31.debug(`excludeHiddenFiles '${result.excludeHiddenFiles}'`); } } return result; @@ -31068,6 +31692,8 @@ var require_brace_expansion = __commonJS({ var escClose2 = "\0CLOSE" + Math.random() + "\0"; var escComma2 = "\0COMMA" + Math.random() + "\0"; var escPeriod2 = "\0PERIOD" + Math.random() + "\0"; + var EXPANSION_MAX2 = 1e5; + var EXPANSION_MAX_LENGTH2 = 4e6; function numeric2(str) { return parseInt(str, 10) == str ? parseInt(str, 10) : str.charCodeAt(0); } @@ -31101,11 +31727,12 @@ var require_brace_expansion = __commonJS({ if (!str) return []; options = options || {}; - var max = options.max == null ? Infinity : options.max; + var max = options.max == null ? EXPANSION_MAX2 : options.max; + var maxLength = options.maxLength == null ? EXPANSION_MAX_LENGTH2 : options.maxLength; if (str.substr(0, 2) === "{}") { str = "\\{\\}" + str.substr(2); } - return expand3(escapeBraces2(str), max, true).map(unescapeBraces2); + return expand3(escapeBraces2(str), max, maxLength, true).map(unescapeBraces2); } function embrace2(str) { return "{" + str + "}"; @@ -31119,86 +31746,175 @@ var require_brace_expansion = __commonJS({ function gte7(i, y) { return i >= y; } - function expand3(str, max, isTop) { - var expansions = []; - var m = balanced2("{", "}", str); - if (!m || /\$$/.test(m.pre)) return [str]; - var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); - var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); - var isSequence = isNumericSequence || isAlphaSequence; - var isOptions = m.body.indexOf(",") >= 0; - if (!isSequence && !isOptions) { - if (m.post.match(/,(?!,).*\}/)) { - str = m.pre + "{" + m.body + escClose2 + m.post; - return expand3(str, max, true); - } - return [str]; - } - var n; - if (isSequence) { - n = m.body.split(/\.\./); - } else { - n = parseCommaParts2(m.body); - if (n.length === 1) { - n = expand3(n[0], max, false).map(embrace2); - if (n.length === 1) { - var post = m.post.length ? expand3(m.post, max, false) : [""]; - return post.map(function(p) { - return m.pre + n[0] + p; - }); - } + function combine2(acc, base, pre, values, max, maxLength, dropEmpties, outBase) { + var out = []; + var length = 0; + for (var a = 0; a < acc.length; a++) { + for (var v = 0; v < values.length; v++) { + if (out.length >= max) return out; + var expansion = acc[a] + pre + values[v]; + if (dropEmpties && expansion.length === base[a]) continue; + if (length + expansion.length > maxLength) return out; + out.push(expansion); + outBase.push(base[a]); + length += expansion.length; } } - var pre = m.pre; - var post = m.post.length ? expand3(m.post, max, false) : [""]; - var N; - if (isSequence) { - var x = numeric2(n[0]); - var y = numeric2(n[1]); - var width = Math.max(n[0].length, n[1].length); - var incr = n.length == 3 ? Math.max(Math.abs(numeric2(n[2])), 1) : 1; - var test = lte2; - var reverse = y < x; - if (reverse) { - incr *= -1; - test = gte7; - } - var pad = n.some(isPadded2); - N = []; - for (var i = x; test(i, y); i += incr) { - var c; - if (isAlphaSequence) { - c = String.fromCharCode(i); - if (c === "\\") - c = ""; - } else { - c = String(i); - if (pad) { - var need = width - c.length; - if (need > 0) { - var z = new Array(need + 1).join("0"); - if (i < 0) - c = "-" + z + c.slice(1); - else - c = z + c; + return out; + } + function expandSequence2(body, isAlphaSequence, max, maxLength) { + var n = body.split(/\.\./); + var N = []; + if (n[0] === void 0 || n[1] === void 0) { + return N; + } + var x = numeric2(n[0]); + var y = numeric2(n[1]); + var width = Math.max(n[0].length, n[1].length); + var incr = n.length === 3 && n[2] !== void 0 ? Math.max(Math.abs(numeric2(n[2])), 1) : 1; + var test = lte2; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte7; + } + var pad = n.some(isPadded2); + var length = 0; + for (var i = x; test(i, y) && N.length < max; i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === "\\") { + c = ""; + } + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join("0"); + if (i < 0) { + c = "-" + z + c.slice(1); + } else { + c = z + c; } } } - N.push(c); } - } else { - N = concatMap(n, function(el) { - return expand3(el, max, false); - }); + if (length + c.length > maxLength) break; + N.push(c); + length += c.length; } - for (var j = 0; j < N.length; j++) { - for (var k = 0; k < post.length && expansions.length < max; k++) { - var expansion = pre + N[j] + post[k]; - if (!isTop || isSequence || expansion) - expansions.push(expansion); + return N; + } + function expand3(str, max, maxLength, isTop) { + var acc = [""]; + var accBase = [0]; + var dropEmpties = false; + var firstGroup = true; + var nextBase; + for (; ; ) { + var m = balanced2("{", "}", str); + if (!m) { + return combine2(acc, accBase, str, [""], max, maxLength, dropEmpties, []); + } + var pre = m.pre; + if (/\$$/.test(pre)) { + return combine2(acc, accBase, str, [""], max, maxLength, dropEmpties, []); + } + var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + var isSequence = isNumericSequence || isAlphaSequence; + var isOptions = m.body.indexOf(",") >= 0; + if (!isSequence && !isOptions) { + if (m.post.match(/,(?!,).*\}/)) { + str = m.pre + "{" + m.body + escClose2 + m.post; + isTop = true; + firstGroup = true; + dropEmpties = false; + accBase = []; + for (var b = 0; b < acc.length; b++) { + accBase.push(acc[b].length); + } + continue; + } + return combine2( + acc, + accBase, + pre + "{" + m.body + "}" + m.post, + [""], + max, + maxLength, + dropEmpties, + [] + ); + } + if (firstGroup) { + dropEmpties = isTop && !isSequence; + firstGroup = false; } + var values; + if (isSequence) { + values = expandSequence2(m.body, isAlphaSequence, max, maxLength); + } else { + var n = parseCommaParts2(m.body); + if (n.length === 1 && n[0] !== void 0) { + n = expand3(n[0], max, maxLength, false).map(embrace2); + if (n.length === 1) { + nextBase = []; + acc = combine2( + acc, + accBase, + pre + n[0], + [""], + max, + maxLength, + dropEmpties && !m.post.length, + nextBase + ); + accBase = nextBase; + if (!m.post.length) break; + str = m.post; + continue; + } + } + var dropsEmpties = dropEmpties && !m.post.length && !pre; + for (var d = 0; dropsEmpties && d < acc.length; d++) { + if (acc[d].length !== accBase[d]) { + dropsEmpties = false; + } + } + values = []; + var valuesLength = 0; + outer: for (var j = 0; j < n.length; j++) { + var expanded = expand3(n[j], max, maxLength, false); + for (var k = 0; k < expanded.length; k++) { + var v = expanded[k]; + if (dropsEmpties && !v) continue; + if (values.length >= max || valuesLength + v.length > maxLength) { + break outer; + } + values.push(v); + valuesLength += v.length; + } + } + } + nextBase = []; + acc = combine2( + acc, + accBase, + pre, + values, + max, + maxLength, + dropEmpties && !m.post.length, + nextBase + ); + accBase = nextBase; + if (!m.post.length) break; + str = m.post; } - return expansions; + return acc; } } }); @@ -31267,13 +31983,13 @@ var require_minimatch = __commonJS({ m.Minimatch = function Minimatch3(pattern, options) { return new orig.Minimatch(pattern, ext2(def, options)); }; - m.Minimatch.defaults = function defaults2(options) { + m.Minimatch.defaults = function defaults3(options) { return orig.defaults(ext2(def, options)).Minimatch; }; m.filter = function filter3(pattern, options) { return orig.filter(pattern, ext2(def, options)); }; - m.defaults = function defaults2(options) { + m.defaults = function defaults3(options) { return orig.defaults(ext2(def, options)); }; m.makeRe = function makeRe3(pattern, options) { @@ -31395,9 +32111,9 @@ var require_minimatch = __commonJS({ throw new TypeError("pattern is too long"); } }; - Minimatch2.prototype.parse = parse2; + Minimatch2.prototype.parse = parse3; var SUBPARSE = {}; - function parse2(pattern, isSub) { + function parse3(pattern, isSub) { assertValidPattern2(pattern); var options = this.options; if (pattern === "**") { @@ -32350,7 +33066,7 @@ var require_internal_globber = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.DefaultGlobber = void 0; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var fs31 = __importStar2(require("fs")); var globOptionsHelper = __importStar2(require_internal_glob_options_helper()); var path29 = __importStar2(require("path")); @@ -32403,7 +33119,7 @@ var require_internal_globber = __commonJS({ } const stack = []; for (const searchPath of patternHelper.getSearchPaths(patterns)) { - core30.debug(`Search path '${searchPath}'`); + core31.debug(`Search path '${searchPath}'`); try { yield __await2(fs31.promises.lstat(searchPath)); } catch (err) { @@ -32478,7 +33194,7 @@ var require_internal_globber = __commonJS({ } catch (err) { if (err.code === "ENOENT") { if (options.omitBrokenSymbolicLinks) { - core30.debug(`Broken symlink '${item.path}'`); + core31.debug(`Broken symlink '${item.path}'`); return void 0; } throw new Error(`No information found for the path '${item.path}'. This may indicate a broken symbolic link.`); @@ -32494,7 +33210,7 @@ var require_internal_globber = __commonJS({ traversalChain.pop(); } if (traversalChain.some((x) => x === realPath)) { - core30.debug(`Symlink cycle detected for path '${item.path}' and realpath '${realPath}'`); + core31.debug(`Symlink cycle detected for path '${item.path}' and realpath '${realPath}'`); return void 0; } traversalChain.push(realPath); @@ -32597,7 +33313,7 @@ var require_internal_hash_files = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.hashFiles = hashFiles2; var crypto3 = __importStar2(require("crypto")); - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var fs31 = __importStar2(require("fs")); var stream2 = __importStar2(require("stream")); var util3 = __importStar2(require("util")); @@ -32606,7 +33322,7 @@ var require_internal_hash_files = __commonJS({ return __awaiter2(this, arguments, void 0, function* (globber, currentWorkspace, verbose = false) { var _a2, e_1, _b, _c; var _d; - const writeDelegate = verbose ? core30.info : core30.debug; + const writeDelegate = verbose ? core31.info : core31.debug; let hasMatch = false; const githubWorkspace = currentWorkspace ? currentWorkspace : (_d = process.env["GITHUB_WORKSPACE"]) !== null && _d !== void 0 ? _d : process.cwd(); const result = crypto3.createHash("sha256"); @@ -32626,8 +33342,8 @@ var require_internal_hash_files = __commonJS({ continue; } const hash2 = crypto3.createHash("sha256"); - const pipeline = util3.promisify(stream2.pipeline); - yield pipeline(fs31.createReadStream(file), hash2); + const pipeline2 = util3.promisify(stream2.pipeline); + yield pipeline2(fs31.createReadStream(file), hash2); result.write(hash2.digest()); count++; if (!hasMatch) { @@ -32847,8 +33563,8 @@ var require_semver3 = __commonJS({ } } var i; - exports2.parse = parse2; - function parse2(version, options) { + exports2.parse = parse3; + function parse3(version, options) { if (!options || typeof options !== "object") { options = { loose: !!options, @@ -32876,12 +33592,12 @@ var require_semver3 = __commonJS({ } exports2.valid = valid4; function valid4(version, options) { - var v = parse2(version, options); + var v = parse3(version, options); return v ? v.version : null; } exports2.clean = clean3; function clean3(version, options) { - var s = parse2(version.trim().replace(/^[=v]+/, ""), options); + var s = parse3(version.trim().replace(/^[=v]+/, ""), options); return s ? s.version : null; } exports2.SemVer = SemVer; @@ -33117,8 +33833,8 @@ var require_semver3 = __commonJS({ if (eq(version1, version2)) { return null; } else { - var v1 = parse2(version1); - var v2 = parse2(version2); + var v1 = parse3(version1); + var v2 = parse3(version2); var prefix = ""; if (v1.prerelease.length || v2.prerelease.length) { prefix = "pre"; @@ -33824,7 +34540,7 @@ var require_semver3 = __commonJS({ } exports2.prerelease = prerelease; function prerelease(version, options) { - var parsed = parse2(version, options); + var parsed = parse3(version, options); return parsed && parsed.prerelease.length ? parsed.prerelease : null; } exports2.intersects = intersects; @@ -33861,7 +34577,7 @@ var require_semver3 = __commonJS({ if (match2 === null) { return null; } - return parse2(match2[2] + "." + (match2[3] || "0") + "." + (match2[4] || "0"), options); + return parse3(match2[2] + "." + (match2[3] || "0") + "." + (match2[4] || "0"), options); } } }); @@ -33871,7 +34587,7 @@ var require_constants7 = __commonJS({ "node_modules/@actions/cache/lib/internal/constants.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.CacheFileSizeLimit = exports2.ManifestFilename = exports2.TarFilename = exports2.SystemTarPathOnWindows = exports2.GnuTarPathOnWindows = exports2.SocketTimeout = exports2.DefaultRetryDelay = exports2.DefaultRetryAttempts = exports2.ArchiveToolType = exports2.CompressionMethod = exports2.CacheFilename = void 0; + exports2.CacheReadDeniedMessagePrefix = exports2.CacheFileSizeLimit = exports2.ManifestFilename = exports2.TarFilename = exports2.SystemTarPathOnWindows = exports2.GnuTarPathOnWindows = exports2.SocketTimeout = exports2.DefaultRetryDelay = exports2.DefaultRetryAttempts = exports2.ArchiveToolType = exports2.CompressionMethod = exports2.CacheFilename = void 0; var CacheFilename; (function(CacheFilename2) { CacheFilename2["Gzip"] = "cache.tgz"; @@ -33896,6 +34612,7 @@ var require_constants7 = __commonJS({ exports2.TarFilename = "cache.tar"; exports2.ManifestFilename = "manifest.txt"; exports2.CacheFileSizeLimit = 10 * Math.pow(1024, 3); + exports2.CacheReadDeniedMessagePrefix = "cache read denied:"; } }); @@ -33997,7 +34714,7 @@ var require_cacheUtils = __commonJS({ exports2.assertDefined = assertDefined; exports2.getCacheVersion = getCacheVersion; exports2.getRuntimeToken = getRuntimeToken; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var exec3 = __importStar2(require_exec()); var glob2 = __importStar2(require_glob()); var io9 = __importStar2(require_io()); @@ -34048,7 +34765,7 @@ var require_cacheUtils = __commonJS({ _e = false; const file = _c; const relativeFile = path29.relative(workspace, file).replace(new RegExp(`\\${path29.sep}`, "g"), "/"); - core30.debug(`Matched: ${relativeFile}`); + core31.debug(`Matched: ${relativeFile}`); if (relativeFile === "") { paths.push("."); } else { @@ -34076,7 +34793,7 @@ var require_cacheUtils = __commonJS({ return __awaiter2(this, arguments, void 0, function* (app, additionalArgs = []) { let versionOutput = ""; additionalArgs.push("--version"); - core30.debug(`Checking ${app} ${additionalArgs.join(" ")}`); + core31.debug(`Checking ${app} ${additionalArgs.join(" ")}`); try { yield exec3.exec(`${app}`, additionalArgs, { ignoreReturnCode: true, @@ -34087,10 +34804,10 @@ var require_cacheUtils = __commonJS({ } }); } catch (err) { - core30.debug(err.message); + core31.debug(err.message); } versionOutput = versionOutput.trim(); - core30.debug(versionOutput); + core31.debug(versionOutput); return versionOutput; }); } @@ -34098,7 +34815,7 @@ var require_cacheUtils = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { const versionOutput = yield getVersion("zstd", ["--quiet"]); const version = semver11.clean(versionOutput); - core30.debug(`zstd version: ${version}`); + core31.debug(`zstd version: ${version}`); if (versionOutput === "") { return constants_1.CompressionMethod.Gzip; } else { @@ -35193,12 +35910,12 @@ var require_pipeline = __commonJS({ } sendRequest(httpClient, request3) { const policies = this.getOrderedPolicies(); - const pipeline = policies.reduceRight((next, policy) => { + const pipeline2 = policies.reduceRight((next, policy) => { return (req) => { return policy.sendRequest(req, next); }; }, (req) => httpClient.sendRequest(req)); - return pipeline(request3); + return pipeline2(request3); } getOrderedPolicies() { if (!this._orderedPolicies) { @@ -36560,7 +37277,7 @@ var require_ms = __commonJS({ options = options || {}; var type = typeof val; if (type === "string" && val.length > 0) { - return parse2(val); + return parse3(val); } else if (type === "number" && isFinite(val)) { return options.long ? fmtLong(val) : fmtShort(val); } @@ -36568,7 +37285,7 @@ var require_ms = __commonJS({ "val is not a non-empty string or a valid number. val=" + JSON.stringify(val) ); }; - function parse2(str) { + function parse3(str) { str = String(str); if (str.length > 100) { return; @@ -37130,7 +37847,7 @@ var require_node = __commonJS({ "node_modules/debug/src/node.js"(exports2, module2) { var tty = require("tty"); var util3 = require("util"); - exports2.init = init; + exports2.init = init2; exports2.log = log; exports2.formatArgs = formatArgs; exports2.save = save; @@ -37279,7 +37996,7 @@ var require_node = __commonJS({ function load2() { return process.env.DEBUG; } - function init(debug6) { + function init2(debug6) { debug6.inspectOpts = {}; const keys = Object.keys(exports2.inspectOpts); for (let i = 0; i < keys.length; i++) { @@ -37381,7 +38098,7 @@ var require_helpers3 = __commonJS({ }); // node_modules/agent-base/dist/index.js -var require_dist = __commonJS({ +var require_dist2 = __commonJS({ "node_modules/agent-base/dist/index.js"(exports2) { "use strict"; var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) { @@ -37633,7 +38350,7 @@ var require_parse_proxy_response = __commonJS({ }); // node_modules/https-proxy-agent/dist/index.js -var require_dist2 = __commonJS({ +var require_dist3 = __commonJS({ "node_modules/https-proxy-agent/dist/index.js"(exports2) { "use strict"; var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) { @@ -37672,7 +38389,7 @@ var require_dist2 = __commonJS({ var tls = __importStar2(require("tls")); var assert_1 = __importDefault2(require("assert")); var debug_1 = __importDefault2(require_src()); - var agent_base_1 = require_dist(); + var agent_base_1 = require_dist2(); var url_1 = require("url"); var parse_proxy_response_1 = require_parse_proxy_response(); var debug6 = (0, debug_1.default)("https-proxy-agent"); @@ -37783,7 +38500,7 @@ var require_dist2 = __commonJS({ }); // node_modules/http-proxy-agent/dist/index.js -var require_dist3 = __commonJS({ +var require_dist4 = __commonJS({ "node_modules/http-proxy-agent/dist/index.js"(exports2) { "use strict"; var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) { @@ -37822,7 +38539,7 @@ var require_dist3 = __commonJS({ var tls = __importStar2(require("tls")); var debug_1 = __importDefault2(require_src()); var events_1 = require("events"); - var agent_base_1 = require_dist(); + var agent_base_1 = require_dist2(); var url_1 = require("url"); var debug6 = (0, debug_1.default)("http-proxy-agent"); var HttpProxyAgent = class extends agent_base_1.Agent { @@ -37921,8 +38638,8 @@ var require_proxyPolicy = __commonJS({ exports2.loadNoProxy = loadNoProxy; exports2.getDefaultProxySettings = getDefaultProxySettings; exports2.proxyPolicy = proxyPolicy; - var https_proxy_agent_1 = require_dist2(); - var http_proxy_agent_1 = require_dist3(); + var https_proxy_agent_1 = require_dist3(); + var http_proxy_agent_1 = require_dist4(); var log_js_1 = require_log2(); var HTTPS_PROXY = "HTTPS_PROXY"; var HTTP_PROXY = "HTTP_PROXY"; @@ -38325,26 +39042,26 @@ var require_createPipelineFromOptions = __commonJS({ var tlsPolicy_js_1 = require_tlsPolicy(); var multipartPolicy_js_1 = require_multipartPolicy(); function createPipelineFromOptions(options) { - const pipeline = (0, pipeline_js_1.createEmptyPipeline)(); + const pipeline2 = (0, pipeline_js_1.createEmptyPipeline)(); if (checkEnvironment_js_1.isNodeLike) { if (options.agent) { - pipeline.addPolicy((0, agentPolicy_js_1.agentPolicy)(options.agent)); + pipeline2.addPolicy((0, agentPolicy_js_1.agentPolicy)(options.agent)); } if (options.tlsOptions) { - pipeline.addPolicy((0, tlsPolicy_js_1.tlsPolicy)(options.tlsOptions)); + pipeline2.addPolicy((0, tlsPolicy_js_1.tlsPolicy)(options.tlsOptions)); } - pipeline.addPolicy((0, proxyPolicy_js_1.proxyPolicy)(options.proxyOptions)); - pipeline.addPolicy((0, decompressResponsePolicy_js_1.decompressResponsePolicy)()); + pipeline2.addPolicy((0, proxyPolicy_js_1.proxyPolicy)(options.proxyOptions)); + pipeline2.addPolicy((0, decompressResponsePolicy_js_1.decompressResponsePolicy)()); } - pipeline.addPolicy((0, formDataPolicy_js_1.formDataPolicy)(), { beforePolicies: [multipartPolicy_js_1.multipartPolicyName] }); - pipeline.addPolicy((0, userAgentPolicy_js_1.userAgentPolicy)(options.userAgentOptions)); - pipeline.addPolicy((0, multipartPolicy_js_1.multipartPolicy)(), { afterPhase: "Deserialize" }); - pipeline.addPolicy((0, defaultRetryPolicy_js_1.defaultRetryPolicy)(options.retryOptions), { phase: "Retry" }); + pipeline2.addPolicy((0, formDataPolicy_js_1.formDataPolicy)(), { beforePolicies: [multipartPolicy_js_1.multipartPolicyName] }); + pipeline2.addPolicy((0, userAgentPolicy_js_1.userAgentPolicy)(options.userAgentOptions)); + pipeline2.addPolicy((0, multipartPolicy_js_1.multipartPolicy)(), { afterPhase: "Deserialize" }); + pipeline2.addPolicy((0, defaultRetryPolicy_js_1.defaultRetryPolicy)(options.retryOptions), { phase: "Retry" }); if (checkEnvironment_js_1.isNodeLike) { - pipeline.addPolicy((0, redirectPolicy_js_1.redirectPolicy)(options.redirectOptions), { afterPhase: "Retry" }); + pipeline2.addPolicy((0, redirectPolicy_js_1.redirectPolicy)(options.redirectOptions), { afterPhase: "Retry" }); } - pipeline.addPolicy((0, logPolicy_js_1.logPolicy)(options.loggingOptions), { afterPhase: "Sign" }); - return pipeline; + pipeline2.addPolicy((0, logPolicy_js_1.logPolicy)(options.loggingOptions), { afterPhase: "Sign" }); + return pipeline2; } } }); @@ -38566,21 +39283,21 @@ var require_clientHelpers = __commonJS({ var oauth2AuthenticationPolicy_js_1 = require_oauth2AuthenticationPolicy(); var cachedHttpClient; function createDefaultPipeline(options = {}) { - const pipeline = (0, createPipelineFromOptions_js_1.createPipelineFromOptions)(options); - pipeline.addPolicy((0, apiVersionPolicy_js_1.apiVersionPolicy)(options)); + const pipeline2 = (0, createPipelineFromOptions_js_1.createPipelineFromOptions)(options); + pipeline2.addPolicy((0, apiVersionPolicy_js_1.apiVersionPolicy)(options)); const { credential, authSchemes, allowInsecureConnection } = options; if (credential) { if ((0, credentials_js_1.isApiKeyCredential)(credential)) { - pipeline.addPolicy((0, apiKeyAuthenticationPolicy_js_1.apiKeyAuthenticationPolicy)({ authSchemes, credential, allowInsecureConnection })); + pipeline2.addPolicy((0, apiKeyAuthenticationPolicy_js_1.apiKeyAuthenticationPolicy)({ authSchemes, credential, allowInsecureConnection })); } else if ((0, credentials_js_1.isBasicCredential)(credential)) { - pipeline.addPolicy((0, basicAuthenticationPolicy_js_1.basicAuthenticationPolicy)({ authSchemes, credential, allowInsecureConnection })); + pipeline2.addPolicy((0, basicAuthenticationPolicy_js_1.basicAuthenticationPolicy)({ authSchemes, credential, allowInsecureConnection })); } else if ((0, credentials_js_1.isBearerTokenCredential)(credential)) { - pipeline.addPolicy((0, bearerAuthenticationPolicy_js_1.bearerAuthenticationPolicy)({ authSchemes, credential, allowInsecureConnection })); + pipeline2.addPolicy((0, bearerAuthenticationPolicy_js_1.bearerAuthenticationPolicy)({ authSchemes, credential, allowInsecureConnection })); } else if ((0, credentials_js_1.isOAuth2TokenCredential)(credential)) { - pipeline.addPolicy((0, oauth2AuthenticationPolicy_js_1.oauth2AuthenticationPolicy)({ authSchemes, credential, allowInsecureConnection })); + pipeline2.addPolicy((0, oauth2AuthenticationPolicy_js_1.oauth2AuthenticationPolicy)({ authSchemes, credential, allowInsecureConnection })); } } - return pipeline; + return pipeline2; } function getCachedDefaultHttpsClient() { if (!cachedHttpClient) { @@ -38716,11 +39433,11 @@ var require_sendRequest = __commonJS({ var clientHelpers_js_1 = require_clientHelpers(); var typeGuards_js_1 = require_typeGuards(); var multipart_js_1 = require_multipart(); - async function sendRequest(method, url2, pipeline, options = {}, customHttpClient) { + async function sendRequest(method, url2, pipeline2, options = {}, customHttpClient) { const httpClient = customHttpClient ?? (0, clientHelpers_js_1.getCachedDefaultHttpsClient)(); const request3 = buildPipelineRequest(method, url2, options); try { - const response = await pipeline.sendRequest(httpClient, request3); + const response = await pipeline2.sendRequest(httpClient, request3); const headers = response.headers.toJSON(); const stream2 = response.readableStreamBody ?? response.browserStreamBody; const parsedBody = options.responseAsStream || stream2 !== void 0 ? void 0 : getResponseBody(response); @@ -38983,11 +39700,11 @@ var require_getClient = __commonJS({ var urlHelpers_js_1 = require_urlHelpers(); var checkEnvironment_js_1 = require_checkEnvironment(); function getClient(endpoint2, clientOptions = {}) { - const pipeline = clientOptions.pipeline ?? (0, clientHelpers_js_1.createDefaultPipeline)(clientOptions); + const pipeline2 = clientOptions.pipeline ?? (0, clientHelpers_js_1.createDefaultPipeline)(clientOptions); if (clientOptions.additionalPolicies?.length) { for (const { policy, position } of clientOptions.additionalPolicies) { const afterPhase = position === "perRetry" ? "Sign" : void 0; - pipeline.addPolicy(policy, { + pipeline2.addPolicy(policy, { afterPhase }); } @@ -38998,53 +39715,53 @@ var require_getClient = __commonJS({ const getUrl = (requestOptions) => (0, urlHelpers_js_1.buildRequestUrl)(endpointUrl, path29, args, { allowInsecureConnection, ...requestOptions }); return { get: (requestOptions = {}) => { - return buildOperation("GET", getUrl(requestOptions), pipeline, requestOptions, allowInsecureConnection, httpClient); + return buildOperation("GET", getUrl(requestOptions), pipeline2, requestOptions, allowInsecureConnection, httpClient); }, post: (requestOptions = {}) => { - return buildOperation("POST", getUrl(requestOptions), pipeline, requestOptions, allowInsecureConnection, httpClient); + return buildOperation("POST", getUrl(requestOptions), pipeline2, requestOptions, allowInsecureConnection, httpClient); }, put: (requestOptions = {}) => { - return buildOperation("PUT", getUrl(requestOptions), pipeline, requestOptions, allowInsecureConnection, httpClient); + return buildOperation("PUT", getUrl(requestOptions), pipeline2, requestOptions, allowInsecureConnection, httpClient); }, patch: (requestOptions = {}) => { - return buildOperation("PATCH", getUrl(requestOptions), pipeline, requestOptions, allowInsecureConnection, httpClient); + return buildOperation("PATCH", getUrl(requestOptions), pipeline2, requestOptions, allowInsecureConnection, httpClient); }, delete: (requestOptions = {}) => { - return buildOperation("DELETE", getUrl(requestOptions), pipeline, requestOptions, allowInsecureConnection, httpClient); + return buildOperation("DELETE", getUrl(requestOptions), pipeline2, requestOptions, allowInsecureConnection, httpClient); }, head: (requestOptions = {}) => { - return buildOperation("HEAD", getUrl(requestOptions), pipeline, requestOptions, allowInsecureConnection, httpClient); + return buildOperation("HEAD", getUrl(requestOptions), pipeline2, requestOptions, allowInsecureConnection, httpClient); }, options: (requestOptions = {}) => { - return buildOperation("OPTIONS", getUrl(requestOptions), pipeline, requestOptions, allowInsecureConnection, httpClient); + return buildOperation("OPTIONS", getUrl(requestOptions), pipeline2, requestOptions, allowInsecureConnection, httpClient); }, trace: (requestOptions = {}) => { - return buildOperation("TRACE", getUrl(requestOptions), pipeline, requestOptions, allowInsecureConnection, httpClient); + return buildOperation("TRACE", getUrl(requestOptions), pipeline2, requestOptions, allowInsecureConnection, httpClient); } }; }; return { path: client, pathUnchecked: client, - pipeline + pipeline: pipeline2 }; } - function buildOperation(method, url2, pipeline, options, allowInsecureConnection, httpClient) { + function buildOperation(method, url2, pipeline2, options, allowInsecureConnection, httpClient) { allowInsecureConnection = options.allowInsecureConnection ?? allowInsecureConnection; return { then: function(onFulfilled, onrejected) { - return (0, sendRequest_js_1.sendRequest)(method, url2, pipeline, { ...options, allowInsecureConnection }, httpClient).then(onFulfilled, onrejected); + return (0, sendRequest_js_1.sendRequest)(method, url2, pipeline2, { ...options, allowInsecureConnection }, httpClient).then(onFulfilled, onrejected); }, async asBrowserStream() { if (checkEnvironment_js_1.isNodeLike) { throw new Error("`asBrowserStream` is supported only in the browser environment. Use `asNodeStream` instead to obtain the response body stream. If you require a Web stream of the response in Node, consider using `Readable.toWeb` on the result of `asNodeStream`."); } else { - return (0, sendRequest_js_1.sendRequest)(method, url2, pipeline, { ...options, allowInsecureConnection, responseAsStream: true }, httpClient); + return (0, sendRequest_js_1.sendRequest)(method, url2, pipeline2, { ...options, allowInsecureConnection, responseAsStream: true }, httpClient); } }, async asNodeStream() { if (checkEnvironment_js_1.isNodeLike) { - return (0, sendRequest_js_1.sendRequest)(method, url2, pipeline, { ...options, allowInsecureConnection, responseAsStream: true }, httpClient); + return (0, sendRequest_js_1.sendRequest)(method, url2, pipeline2, { ...options, allowInsecureConnection, responseAsStream: true }, httpClient); } else { throw new Error("`isNodeStream` is not supported in the browser environment. Use `asBrowserStream` to obtain the response body stream."); } @@ -40534,31 +41251,31 @@ var require_createPipelineFromOptions2 = __commonJS({ var tracingPolicy_js_1 = require_tracingPolicy(); var wrapAbortSignalLikePolicy_js_1 = require_wrapAbortSignalLikePolicy(); function createPipelineFromOptions(options) { - const pipeline = (0, pipeline_js_1.createEmptyPipeline)(); + const pipeline2 = (0, pipeline_js_1.createEmptyPipeline)(); if (core_util_1.isNodeLike) { if (options.agent) { - pipeline.addPolicy((0, agentPolicy_js_1.agentPolicy)(options.agent)); + pipeline2.addPolicy((0, agentPolicy_js_1.agentPolicy)(options.agent)); } if (options.tlsOptions) { - pipeline.addPolicy((0, tlsPolicy_js_1.tlsPolicy)(options.tlsOptions)); - } - pipeline.addPolicy((0, proxyPolicy_js_1.proxyPolicy)(options.proxyOptions)); - pipeline.addPolicy((0, decompressResponsePolicy_js_1.decompressResponsePolicy)()); - } - pipeline.addPolicy((0, wrapAbortSignalLikePolicy_js_1.wrapAbortSignalLikePolicy)()); - pipeline.addPolicy((0, formDataPolicy_js_1.formDataPolicy)(), { beforePolicies: [multipartPolicy_js_1.multipartPolicyName] }); - pipeline.addPolicy((0, userAgentPolicy_js_1.userAgentPolicy)(options.userAgentOptions)); - pipeline.addPolicy((0, setClientRequestIdPolicy_js_1.setClientRequestIdPolicy)(options.telemetryOptions?.clientRequestIdHeaderName)); - pipeline.addPolicy((0, multipartPolicy_js_1.multipartPolicy)(), { afterPhase: "Deserialize" }); - pipeline.addPolicy((0, defaultRetryPolicy_js_1.defaultRetryPolicy)(options.retryOptions), { phase: "Retry" }); - pipeline.addPolicy((0, tracingPolicy_js_1.tracingPolicy)({ ...options.userAgentOptions, ...options.loggingOptions }), { + pipeline2.addPolicy((0, tlsPolicy_js_1.tlsPolicy)(options.tlsOptions)); + } + pipeline2.addPolicy((0, proxyPolicy_js_1.proxyPolicy)(options.proxyOptions)); + pipeline2.addPolicy((0, decompressResponsePolicy_js_1.decompressResponsePolicy)()); + } + pipeline2.addPolicy((0, wrapAbortSignalLikePolicy_js_1.wrapAbortSignalLikePolicy)()); + pipeline2.addPolicy((0, formDataPolicy_js_1.formDataPolicy)(), { beforePolicies: [multipartPolicy_js_1.multipartPolicyName] }); + pipeline2.addPolicy((0, userAgentPolicy_js_1.userAgentPolicy)(options.userAgentOptions)); + pipeline2.addPolicy((0, setClientRequestIdPolicy_js_1.setClientRequestIdPolicy)(options.telemetryOptions?.clientRequestIdHeaderName)); + pipeline2.addPolicy((0, multipartPolicy_js_1.multipartPolicy)(), { afterPhase: "Deserialize" }); + pipeline2.addPolicy((0, defaultRetryPolicy_js_1.defaultRetryPolicy)(options.retryOptions), { phase: "Retry" }); + pipeline2.addPolicy((0, tracingPolicy_js_1.tracingPolicy)({ ...options.userAgentOptions, ...options.loggingOptions }), { afterPhase: "Retry" }); if (core_util_1.isNodeLike) { - pipeline.addPolicy((0, redirectPolicy_js_1.redirectPolicy)(options.redirectOptions), { afterPhase: "Retry" }); + pipeline2.addPolicy((0, redirectPolicy_js_1.redirectPolicy)(options.redirectOptions), { afterPhase: "Retry" }); } - pipeline.addPolicy((0, logPolicy_js_1.logPolicy)(options.loggingOptions), { afterPhase: "Sign" }); - return pipeline; + pipeline2.addPolicy((0, logPolicy_js_1.logPolicy)(options.loggingOptions), { afterPhase: "Sign" }); + return pipeline2; } } }); @@ -41472,8 +42189,8 @@ var require_disableKeepAlivePolicy = __commonJS({ } }; } - function pipelineContainsDisableKeepAlivePolicy(pipeline) { - return pipeline.getOrderedPolicies().some((policy) => policy.name === exports2.disableKeepAlivePolicyName); + function pipelineContainsDisableKeepAlivePolicy(pipeline2) { + return pipeline2.getOrderedPolicies().some((policy) => policy.name === exports2.disableKeepAlivePolicyName); } } }); @@ -41670,7 +42387,7 @@ var require_serializer = __commonJS({ * * @returns A valid serialized Javascript object */ - serialize(mapper, object, objectName, options = { xml: {} }) { + serialize(mapper, object2, objectName, options = { xml: {} }) { const updatedOptions = { xml: { rootName: options.xml.rootName ?? "", @@ -41687,40 +42404,40 @@ var require_serializer = __commonJS({ payload = []; } if (mapper.isConstant) { - object = mapper.defaultValue; + object2 = mapper.defaultValue; } const { required, nullable } = mapper; - if (required && nullable && object === void 0) { + if (required && nullable && object2 === void 0) { throw new Error(`${objectName} cannot be undefined.`); } - if (required && !nullable && (object === void 0 || object === null)) { + if (required && !nullable && (object2 === void 0 || object2 === null)) { throw new Error(`${objectName} cannot be null or undefined.`); } - if (!required && nullable === false && object === null) { + if (!required && nullable === false && object2 === null) { throw new Error(`${objectName} cannot be null.`); } - if (object === void 0 || object === null) { - payload = object; + if (object2 === void 0 || object2 === null) { + payload = object2; } else { if (mapperType.match(/^any$/i) !== null) { - payload = object; + payload = object2; } else if (mapperType.match(/^(Number|String|Boolean|Object|Stream|Uuid)$/i) !== null) { - payload = serializeBasicTypes(mapperType, objectName, object); + payload = serializeBasicTypes(mapperType, objectName, object2); } else if (mapperType.match(/^Enum$/i) !== null) { const enumMapper = mapper; - payload = serializeEnumType(objectName, enumMapper.type.allowedValues, object); + payload = serializeEnumType(objectName, enumMapper.type.allowedValues, object2); } else if (mapperType.match(/^(Date|DateTime|TimeSpan|DateTimeRfc1123|UnixTime)$/i) !== null) { - payload = serializeDateTypes(mapperType, object, objectName); + payload = serializeDateTypes(mapperType, object2, objectName); } else if (mapperType.match(/^ByteArray$/i) !== null) { - payload = serializeByteArrayType(objectName, object); + payload = serializeByteArrayType(objectName, object2); } else if (mapperType.match(/^Base64Url$/i) !== null) { - payload = serializeBase64UrlType(objectName, object); + payload = serializeBase64UrlType(objectName, object2); } else if (mapperType.match(/^Sequence$/i) !== null) { - payload = serializeSequenceType(this, mapper, object, objectName, Boolean(this.isXML), updatedOptions); + payload = serializeSequenceType(this, mapper, object2, objectName, Boolean(this.isXML), updatedOptions); } else if (mapperType.match(/^Dictionary$/i) !== null) { - payload = serializeDictionaryType(this, mapper, object, objectName, Boolean(this.isXML), updatedOptions); + payload = serializeDictionaryType(this, mapper, object2, objectName, Boolean(this.isXML), updatedOptions); } else if (mapperType.match(/^Composite$/i) !== null) { - payload = serializeCompositeType(this, mapper, object, objectName, Boolean(this.isXML), updatedOptions); + payload = serializeCompositeType(this, mapper, object2, objectName, Boolean(this.isXML), updatedOptions); } } return payload; @@ -41960,8 +42677,8 @@ var require_serializer = __commonJS({ } return value; } - function serializeSequenceType(serializer, mapper, object, objectName, isXml, options) { - if (!Array.isArray(object)) { + function serializeSequenceType(serializer, mapper, object2, objectName, isXml, options) { + if (!Array.isArray(object2)) { throw new Error(`${objectName} must be of type Array.`); } let elementType = mapper.type.element; @@ -41972,8 +42689,8 @@ var require_serializer = __commonJS({ elementType = serializer.modelMappers[elementType.type.className] ?? elementType; } const tempArray = []; - for (let i = 0; i < object.length; i++) { - const serializedValue = serializer.serialize(elementType, object[i], objectName, options); + for (let i = 0; i < object2.length; i++) { + const serializedValue = serializer.serialize(elementType, object2[i], objectName, options); if (isXml && elementType.xmlNamespace) { const xmlnsKey = elementType.xmlNamespacePrefix ? `xmlns:${elementType.xmlNamespacePrefix}` : "xmlns"; if (elementType.type.name === "Composite") { @@ -41990,8 +42707,8 @@ var require_serializer = __commonJS({ } return tempArray; } - function serializeDictionaryType(serializer, mapper, object, objectName, isXml, options) { - if (typeof object !== "object") { + function serializeDictionaryType(serializer, mapper, object2, objectName, isXml, options) { + if (typeof object2 !== "object") { throw new Error(`${objectName} must be of type object.`); } const valueType = mapper.type.value; @@ -41999,8 +42716,8 @@ var require_serializer = __commonJS({ throw new Error(`"value" metadata for a Dictionary must be defined in the mapper and it must of type "object" in ${objectName}.`); } const tempDictionary = {}; - for (const key of Object.keys(object)) { - const serializedValue = serializer.serialize(valueType, object[key], objectName, options); + for (const key of Object.keys(object2)) { + const serializedValue = serializer.serialize(valueType, object2[key], objectName, options); tempDictionary[key] = getXmlObjectValue(valueType, serializedValue, isXml, options); } if (isXml && mapper.xmlNamespace) { @@ -42040,11 +42757,11 @@ var require_serializer = __commonJS({ } return modelProps; } - function serializeCompositeType(serializer, mapper, object, objectName, isXml, options) { + function serializeCompositeType(serializer, mapper, object2, objectName, isXml, options) { if (getPolymorphicDiscriminatorRecursively(serializer, mapper)) { - mapper = getPolymorphicMapper(serializer, mapper, object, "clientName"); + mapper = getPolymorphicMapper(serializer, mapper, object2, "clientName"); } - if (object !== void 0 && object !== null) { + if (object2 !== void 0 && object2 !== null) { const payload = {}; const modelProps = resolveModelProperties(serializer, mapper, objectName); for (const key of Object.keys(modelProps)) { @@ -42065,7 +42782,7 @@ var require_serializer = __commonJS({ propName = paths.pop(); for (const pathName of paths) { const childObject = parentObject[pathName]; - if ((childObject === void 0 || childObject === null) && (object[key] !== void 0 && object[key] !== null || propertyMapper.defaultValue !== void 0)) { + if ((childObject === void 0 || childObject === null) && (object2[key] !== void 0 && object2[key] !== null || propertyMapper.defaultValue !== void 0)) { parentObject[pathName] = {}; } parentObject = parentObject[pathName]; @@ -42080,7 +42797,7 @@ var require_serializer = __commonJS({ }; } const propertyObjectName = propertyMapper.serializedName !== "" ? objectName + "." + propertyMapper.serializedName : objectName; - let toSerialize = object[key]; + let toSerialize = object2[key]; const polymorphicDiscriminator = getPolymorphicDiscriminatorRecursively(serializer, mapper); if (polymorphicDiscriminator && polymorphicDiscriminator.clientName === key && (toSerialize === void 0 || toSerialize === null)) { toSerialize = mapper.serializedName; @@ -42102,16 +42819,16 @@ var require_serializer = __commonJS({ const additionalPropertiesMapper = resolveAdditionalProperties(serializer, mapper, objectName); if (additionalPropertiesMapper) { const propNames = Object.keys(modelProps); - for (const clientPropName in object) { + for (const clientPropName in object2) { const isAdditionalProperty = propNames.every((pn) => pn !== clientPropName); if (isAdditionalProperty) { - payload[clientPropName] = serializer.serialize(additionalPropertiesMapper, object[clientPropName], objectName + '["' + clientPropName + '"]', options); + payload[clientPropName] = serializer.serialize(additionalPropertiesMapper, object2[clientPropName], objectName + '["' + clientPropName + '"]', options); } } } return payload; } - return object; + return object2; } function getXmlObjectValue(propertyMapper, serializedValue, isXml, options) { if (!isXml || !propertyMapper.xmlNamespace) { @@ -42295,7 +43012,7 @@ var require_serializer = __commonJS({ } return void 0; } - function getPolymorphicMapper(serializer, mapper, object, polymorphicPropertyName) { + function getPolymorphicMapper(serializer, mapper, object2, polymorphicPropertyName) { const polymorphicDiscriminator = getPolymorphicDiscriminatorRecursively(serializer, mapper); if (polymorphicDiscriminator) { let discriminatorName = polymorphicDiscriminator[polymorphicPropertyName]; @@ -42303,7 +43020,7 @@ var require_serializer = __commonJS({ if (polymorphicPropertyName === "serializedName") { discriminatorName = discriminatorName.replace(/\\/gi, ""); } - const discriminatorValue = object[discriminatorName]; + const discriminatorValue = object2[discriminatorName]; const typeName = mapper.type.uberParent ?? mapper.type.className; if (typeof discriminatorValue === "string" && typeName) { const polymorphicMapper = getIndexDiscriminator(serializer.modelMappers.discriminators, discriminatorValue, typeName); @@ -42504,7 +43221,7 @@ var require_deserializationPolicy = __commonJS({ return result; } async function deserializeResponseBody(jsonContentTypes, xmlContentTypes, response, options, parseXML) { - const parsedResponse = await parse2(jsonContentTypes, xmlContentTypes, response, options, parseXML); + const parsedResponse = await parse3(jsonContentTypes, xmlContentTypes, response, options, parseXML); if (!shouldDeserializeResponse(parsedResponse)) { return parsedResponse; } @@ -42605,7 +43322,7 @@ var require_deserializationPolicy = __commonJS({ } return { error: error3, shouldReturnResponse: false }; } - async function parse2(jsonContentTypes, xmlContentTypes, operationResponse, opts, parseXML) { + async function parse3(jsonContentTypes, xmlContentTypes, operationResponse, opts, parseXML) { if (!operationResponse.request.streamResponseStatusCodes?.has(operationResponse.status) && operationResponse.bodyAsText) { const text = operationResponse.bodyAsText; const contentType = operationResponse.headers.get("Content-Type") || ""; @@ -42812,18 +43529,18 @@ var require_pipeline3 = __commonJS({ var core_rest_pipeline_1 = require_commonjs6(); var serializationPolicy_js_1 = require_serializationPolicy(); function createClientPipeline(options = {}) { - const pipeline = (0, core_rest_pipeline_1.createPipelineFromOptions)(options ?? {}); + const pipeline2 = (0, core_rest_pipeline_1.createPipelineFromOptions)(options ?? {}); if (options.credentialOptions) { - pipeline.addPolicy((0, core_rest_pipeline_1.bearerTokenAuthenticationPolicy)({ + pipeline2.addPolicy((0, core_rest_pipeline_1.bearerTokenAuthenticationPolicy)({ credential: options.credentialOptions.credential, scopes: options.credentialOptions.credentialScopes })); } - pipeline.addPolicy((0, serializationPolicy_js_1.serializationPolicy)(options.serializationOptions), { phase: "Serialize" }); - pipeline.addPolicy((0, deserializationPolicy_js_1.deserializationPolicy)(options.deserializationOptions), { + pipeline2.addPolicy((0, serializationPolicy_js_1.serializationPolicy)(options.serializationOptions), { phase: "Serialize" }); + pipeline2.addPolicy((0, deserializationPolicy_js_1.deserializationPolicy)(options.deserializationOptions), { phase: "Deserialize" }); - return pipeline; + return pipeline2; } } }); @@ -47136,8 +47853,8 @@ var require_StorageSharedKeyCredentialPolicy = __commonJS({ headersArray.sort((a, b) => { return (0, SharedKeyComparator_js_1.compareHeader)(a.name.toLowerCase(), b.name.toLowerCase()); }); - headersArray = headersArray.filter((value, index2, array) => { - if (index2 > 0 && value.name.toLowerCase() === array[index2 - 1].name.toLowerCase()) { + headersArray = headersArray.filter((value, index2, array2) => { + if (index2 > 0 && value.name.toLowerCase() === array2[index2 - 1].name.toLowerCase()) { return false; } return true; @@ -48888,8 +49605,8 @@ var require_StorageSharedKeyCredentialPolicy2 = __commonJS({ headersArray.sort((a, b) => { return (0, SharedKeyComparator_js_1.compareHeader)(a.name.toLowerCase(), b.name.toLowerCase()); }); - headersArray = headersArray.filter((value, index2, array) => { - if (index2 > 0 && value.name.toLowerCase() === array[index2 - 1].name.toLowerCase()) { + headersArray = headersArray.filter((value, index2, array2) => { + if (index2 > 0 && value.name.toLowerCase() === array2[index2 - 1].name.toLowerCase()) { return false; } return true; @@ -49525,8 +50242,8 @@ var require_StorageSharedKeyCredentialPolicyV2 = __commonJS({ headersArray.sort((a, b) => { return (0, SharedKeyComparator_js_1.compareHeader)(a.name.toLowerCase(), b.name.toLowerCase()); }); - headersArray = headersArray.filter((value, index2, array) => { - if (index2 > 0 && value.name.toLowerCase() === array[index2 - 1].name.toLowerCase()) { + headersArray = headersArray.filter((value, index2, array2) => { + if (index2 > 0 && value.name.toLowerCase() === array2[index2 - 1].name.toLowerCase()) { return false; } return true; @@ -49872,8 +50589,8 @@ var require_StorageSharedKeyCredentialPolicyV22 = __commonJS({ headersArray.sort((a, b) => { return (0, SharedKeyComparator_js_1.compareHeader)(a.name.toLowerCase(), b.name.toLowerCase()); }); - headersArray = headersArray.filter((value, index2, array) => { - if (index2 > 0 && value.name.toLowerCase() === array[index2 - 1].name.toLowerCase()) { + headersArray = headersArray.filter((value, index2, array2) => { + if (index2 > 0 && value.name.toLowerCase() === array2[index2 - 1].name.toLowerCase()) { return false; } return true; @@ -50041,11 +50758,11 @@ var require_Pipeline = __commonJS({ var StorageSharedKeyCredentialPolicyV2_js_1 = require_StorageSharedKeyCredentialPolicyV22(); var StorageBrowserPolicyFactory_js_1 = require_StorageBrowserPolicyFactory2(); var StorageCorrectContentLengthPolicy_js_1 = require_StorageCorrectContentLengthPolicy2(); - function isPipelineLike(pipeline) { - if (!pipeline || typeof pipeline !== "object") { + function isPipelineLike(pipeline2) { + if (!pipeline2 || typeof pipeline2 !== "object") { return false; } - const castPipeline = pipeline; + const castPipeline = pipeline2; return Array.isArray(castPipeline.factories) && typeof castPipeline.options === "object" && typeof castPipeline.toServiceClientOptions === "function"; } var Pipeline = class { @@ -50085,11 +50802,11 @@ var require_Pipeline = __commonJS({ if (!credential) { credential = new AnonymousCredential_js_1.AnonymousCredential(); } - const pipeline = new Pipeline([], pipelineOptions); - pipeline._credential = credential; - return pipeline; + const pipeline2 = new Pipeline([], pipelineOptions); + pipeline2._credential = credential; + return pipeline2; } - function processDownlevelPipeline(pipeline) { + function processDownlevelPipeline(pipeline2) { const knownFactoryFunctions = [ isAnonymousCredential, isStorageSharedKeyCredential, @@ -50099,8 +50816,8 @@ var require_Pipeline = __commonJS({ isStorageTelemetryPolicyFactory, isCoreHttpPolicyFactory ]; - if (pipeline.factories.length) { - const novelFactories = pipeline.factories.filter((factory) => { + if (pipeline2.factories.length) { + const novelFactories = pipeline2.factories.filter((factory) => { return !knownFactoryFunctions.some((knownFactory) => knownFactory(factory)); }); if (novelFactories.length) { @@ -50113,14 +50830,14 @@ var require_Pipeline = __commonJS({ } return void 0; } - function getCoreClientOptions(pipeline) { - const { httpClient: v1Client, ...restOptions } = pipeline.options; - let httpClient = pipeline._coreHttpClient; + function getCoreClientOptions(pipeline2) { + const { httpClient: v1Client, ...restOptions } = pipeline2.options; + let httpClient = pipeline2._coreHttpClient; if (!httpClient) { httpClient = v1Client ? (0, core_http_compat_1.convertHttpClient)(v1Client) : (0, storage_common_1.getCachedDefaultHttpClient)(); - pipeline._coreHttpClient = httpClient; + pipeline2._coreHttpClient = httpClient; } - let corePipeline = pipeline._corePipeline; + let corePipeline = pipeline2._corePipeline; if (!corePipeline) { const packageDetails = `azsdk-js-azure-storage-blob/${constants_js_1.SDK_VERSION}`; const userAgentPrefix = restOptions.userAgentOptions && restOptions.userAgentOptions.userAgentPrefix ? `${restOptions.userAgentOptions.userAgentPrefix} ${packageDetails}` : `${packageDetails}`; @@ -50161,11 +50878,11 @@ var require_Pipeline = __commonJS({ corePipeline.addPolicy((0, StorageRetryPolicyV2_js_1.storageRetryPolicy)(restOptions.retryOptions), { phase: "Retry" }); corePipeline.addPolicy((0, storage_common_1.storageRequestFailureDetailsParserPolicy)()); corePipeline.addPolicy((0, StorageBrowserPolicyV2_js_1.storageBrowserPolicy)()); - const downlevelResults = processDownlevelPipeline(pipeline); + const downlevelResults = processDownlevelPipeline(pipeline2); if (downlevelResults) { corePipeline.addPolicy(downlevelResults.wrappedPolicies, downlevelResults.afterRetry ? { afterPhase: "Retry" } : void 0); } - const credential = getCredentialFromPipeline(pipeline); + const credential = getCredentialFromPipeline(pipeline2); if ((0, core_auth_1.isTokenCredential)(credential)) { corePipeline.addPolicy((0, core_rest_pipeline_1.bearerTokenAuthenticationPolicy)({ credential, @@ -50178,7 +50895,7 @@ var require_Pipeline = __commonJS({ accountKey: credential.accountKey }), { phase: "Sign" }); } - pipeline._corePipeline = corePipeline; + pipeline2._corePipeline = corePipeline; } return { ...restOptions, @@ -50187,12 +50904,12 @@ var require_Pipeline = __commonJS({ pipeline: corePipeline }; } - function getCredentialFromPipeline(pipeline) { - if (pipeline._credential) { - return pipeline._credential; + function getCredentialFromPipeline(pipeline2) { + if (pipeline2._credential) { + return pipeline2._credential; } let credential = new AnonymousCredential_js_1.AnonymousCredential(); - for (const factory of pipeline.factories) { + for (const factory of pipeline2.factories) { if ((0, core_auth_1.isTokenCredential)(factory.credential)) { credential = factory.credential; } else if (isStorageSharedKeyCredential(factory)) { @@ -63546,13 +64263,13 @@ var require_storageClient = __commonJS({ if (!options) { options = {}; } - const defaults2 = { + const defaults3 = { requestContentType: "application/json; charset=utf-8" }; const packageDetails = `azsdk-js-azure-storage-blob/12.29.1`; const userAgentPrefix = options.userAgentOptions && options.userAgentOptions.userAgentPrefix ? `${options.userAgentOptions.userAgentPrefix} ${packageDetails}` : `${packageDetails}`; const optionsWithDefaults = { - ...defaults2, + ...defaults3, ...options, userAgentOptions: { userAgentPrefix @@ -63717,13 +64434,13 @@ var require_StorageClient = __commonJS({ * @param url - url to resource * @param pipeline - request policy pipeline. */ - constructor(url2, pipeline) { + constructor(url2, pipeline2) { this.url = (0, utils_common_js_1.escapeURLPath)(url2); this.accountName = (0, utils_common_js_1.getAccountNameFromUrl)(url2); - this.pipeline = pipeline; - this.storageClientContext = new StorageContextClient_js_1.StorageContextClient(this.url, (0, Pipeline_js_1.getCoreClientOptions)(pipeline)); + this.pipeline = pipeline2; + this.storageClientContext = new StorageContextClient_js_1.StorageContextClient(this.url, (0, Pipeline_js_1.getCoreClientOptions)(pipeline2)); this.isHttps = (0, utils_common_js_1.iEqual)((0, utils_common_js_1.getURLScheme)(this.url) || "", "https"); - this.credential = (0, Pipeline_js_1.getCredentialFromPipeline)(pipeline); + this.credential = (0, Pipeline_js_1.getCredentialFromPipeline)(pipeline2); const storageClientContext = this.storageClientContext; storageClientContext.requestContentType = void 0; } @@ -67055,8 +67772,8 @@ var require_operation = __commonJS({ return processResult ? processResult(response, state) : response; } async function initOperation(inputs) { - const { init, stateProxy, processResult, getOperationStatus, withOperationLocation, setErrorAsResult } = inputs; - const { operationLocation, resourceLocation, metadata, response } = await init(); + const { init: init2, stateProxy, processResult, getOperationStatus, withOperationLocation, setErrorAsResult } = inputs; + const { operationLocation, resourceLocation, metadata, response } = await init2(); if (operationLocation) withOperationLocation === null || withOperationLocation === void 0 ? void 0 : withOperationLocation(operationLocation, false); const config = { @@ -67450,7 +68167,7 @@ var require_poller = __commonJS({ }); function buildCreatePoller(inputs) { const { getOperationLocation, getStatusFromInitialResponse, getStatusFromPollResponse, isOperationError, getResourceLocation, getPollingInterval, getError, resolveOnUnsuccessful } = inputs; - return async ({ init, poll }, options) => { + return async ({ init: init2, poll }, options) => { const { processResult, updateState, withOperationLocation: withOperationLocationCallback, intervalInMs = constants_js_1.POLL_INTERVAL_IN_MS, restoreFrom } = options || {}; const stateProxy = createStateProxy(); const withOperationLocation = withOperationLocationCallback ? /* @__PURE__ */ (() => { @@ -67464,7 +68181,7 @@ var require_poller = __commonJS({ }; })() : void 0; const state = restoreFrom ? (0, operation_js_1.deserializeState)(restoreFrom) : await (0, operation_js_1.initOperation)({ - init, + init: init2, stateProxy, processResult, getOperationStatus: getStatusFromInitialResponse, @@ -68506,21 +69223,21 @@ var require_Clients = __commonJS({ } constructor(urlOrConnectionString, credentialOrPipelineOrContainerName, blobNameOrOptions, options) { options = options || {}; - let pipeline; + let pipeline2; let url2; if ((0, Pipeline_js_1.isPipelineLike)(credentialOrPipelineOrContainerName)) { url2 = urlOrConnectionString; - pipeline = credentialOrPipelineOrContainerName; + pipeline2 = credentialOrPipelineOrContainerName; } else if (core_util_1.isNodeLike && credentialOrPipelineOrContainerName instanceof StorageSharedKeyCredential_js_1.StorageSharedKeyCredential || credentialOrPipelineOrContainerName instanceof AnonymousCredential_js_1.AnonymousCredential || (0, core_auth_1.isTokenCredential)(credentialOrPipelineOrContainerName)) { url2 = urlOrConnectionString; options = blobNameOrOptions; - pipeline = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options); } else if (!credentialOrPipelineOrContainerName && typeof credentialOrPipelineOrContainerName !== "string") { url2 = urlOrConnectionString; if (blobNameOrOptions && typeof blobNameOrOptions !== "string") { options = blobNameOrOptions; } - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else if (credentialOrPipelineOrContainerName && typeof credentialOrPipelineOrContainerName === "string" && blobNameOrOptions && typeof blobNameOrOptions === "string") { const containerName = credentialOrPipelineOrContainerName; const blobName = blobNameOrOptions; @@ -68532,20 +69249,20 @@ var require_Clients = __commonJS({ if (!options.proxyOptions) { options.proxyOptions = (0, core_rest_pipeline_1.getDefaultProxySettings)(extractedCreds.proxyUri); } - pipeline = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); } else { throw new Error("Account connection string is only supported in Node.js environment"); } } else if (extractedCreds.kind === "SASConnString") { url2 = (0, utils_common_js_1.appendToURLPath)((0, utils_common_js_1.appendToURLPath)(extractedCreds.url, encodeURIComponent(containerName)), encodeURIComponent(blobName)) + "?" + extractedCreds.accountSas; - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else { throw new Error("Connection string must be either an Account connection string or a SAS connection string"); } } else { throw new Error("Expecting non-empty strings for containerName and blobName parameters"); } - super(url2, pipeline); + super(url2, pipeline2); ({ blobName: this._name, containerName: this._containerName } = this.getBlobAndContainerNamesFromUrl()); this.blobContext = this.storageClientContext.blob; this._snapshot = (0, utils_common_js_1.getURLParameter)(this.url, constants_js_1.URLConstants.Parameters.SNAPSHOT); @@ -69531,19 +70248,19 @@ var require_Clients = __commonJS({ */ appendBlobContext; constructor(urlOrConnectionString, credentialOrPipelineOrContainerName, blobNameOrOptions, options) { - let pipeline; + let pipeline2; let url2; options = options || {}; if ((0, Pipeline_js_1.isPipelineLike)(credentialOrPipelineOrContainerName)) { url2 = urlOrConnectionString; - pipeline = credentialOrPipelineOrContainerName; + pipeline2 = credentialOrPipelineOrContainerName; } else if (core_util_1.isNodeLike && credentialOrPipelineOrContainerName instanceof StorageSharedKeyCredential_js_1.StorageSharedKeyCredential || credentialOrPipelineOrContainerName instanceof AnonymousCredential_js_1.AnonymousCredential || (0, core_auth_1.isTokenCredential)(credentialOrPipelineOrContainerName)) { url2 = urlOrConnectionString; options = blobNameOrOptions; - pipeline = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options); } else if (!credentialOrPipelineOrContainerName && typeof credentialOrPipelineOrContainerName !== "string") { url2 = urlOrConnectionString; - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else if (credentialOrPipelineOrContainerName && typeof credentialOrPipelineOrContainerName === "string" && blobNameOrOptions && typeof blobNameOrOptions === "string") { const containerName = credentialOrPipelineOrContainerName; const blobName = blobNameOrOptions; @@ -69555,20 +70272,20 @@ var require_Clients = __commonJS({ if (!options.proxyOptions) { options.proxyOptions = (0, core_rest_pipeline_1.getDefaultProxySettings)(extractedCreds.proxyUri); } - pipeline = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); } else { throw new Error("Account connection string is only supported in Node.js environment"); } } else if (extractedCreds.kind === "SASConnString") { url2 = (0, utils_common_js_1.appendToURLPath)((0, utils_common_js_1.appendToURLPath)(extractedCreds.url, encodeURIComponent(containerName)), encodeURIComponent(blobName)) + "?" + extractedCreds.accountSas; - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else { throw new Error("Connection string must be either an Account connection string or a SAS connection string"); } } else { throw new Error("Expecting non-empty strings for containerName and blobName parameters"); } - super(url2, pipeline); + super(url2, pipeline2); this.appendBlobContext = this.storageClientContext.appendBlob; } /** @@ -69804,22 +70521,22 @@ var require_Clients = __commonJS({ */ blockBlobContext; constructor(urlOrConnectionString, credentialOrPipelineOrContainerName, blobNameOrOptions, options) { - let pipeline; + let pipeline2; let url2; options = options || {}; if ((0, Pipeline_js_1.isPipelineLike)(credentialOrPipelineOrContainerName)) { url2 = urlOrConnectionString; - pipeline = credentialOrPipelineOrContainerName; + pipeline2 = credentialOrPipelineOrContainerName; } else if (core_util_1.isNodeLike && credentialOrPipelineOrContainerName instanceof StorageSharedKeyCredential_js_1.StorageSharedKeyCredential || credentialOrPipelineOrContainerName instanceof AnonymousCredential_js_1.AnonymousCredential || (0, core_auth_1.isTokenCredential)(credentialOrPipelineOrContainerName)) { url2 = urlOrConnectionString; options = blobNameOrOptions; - pipeline = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options); } else if (!credentialOrPipelineOrContainerName && typeof credentialOrPipelineOrContainerName !== "string") { url2 = urlOrConnectionString; if (blobNameOrOptions && typeof blobNameOrOptions !== "string") { options = blobNameOrOptions; } - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else if (credentialOrPipelineOrContainerName && typeof credentialOrPipelineOrContainerName === "string" && blobNameOrOptions && typeof blobNameOrOptions === "string") { const containerName = credentialOrPipelineOrContainerName; const blobName = blobNameOrOptions; @@ -69831,20 +70548,20 @@ var require_Clients = __commonJS({ if (!options.proxyOptions) { options.proxyOptions = (0, core_rest_pipeline_1.getDefaultProxySettings)(extractedCreds.proxyUri); } - pipeline = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); } else { throw new Error("Account connection string is only supported in Node.js environment"); } } else if (extractedCreds.kind === "SASConnString") { url2 = (0, utils_common_js_1.appendToURLPath)((0, utils_common_js_1.appendToURLPath)(extractedCreds.url, encodeURIComponent(containerName)), encodeURIComponent(blobName)) + "?" + extractedCreds.accountSas; - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else { throw new Error("Connection string must be either an Account connection string or a SAS connection string"); } } else { throw new Error("Expecting non-empty strings for containerName and blobName parameters"); } - super(url2, pipeline); + super(url2, pipeline2); this.blockBlobContext = this.storageClientContext.blockBlob; this._blobContext = this.storageClientContext.blob; } @@ -70416,19 +71133,19 @@ var require_Clients = __commonJS({ */ pageBlobContext; constructor(urlOrConnectionString, credentialOrPipelineOrContainerName, blobNameOrOptions, options) { - let pipeline; + let pipeline2; let url2; options = options || {}; if ((0, Pipeline_js_1.isPipelineLike)(credentialOrPipelineOrContainerName)) { url2 = urlOrConnectionString; - pipeline = credentialOrPipelineOrContainerName; + pipeline2 = credentialOrPipelineOrContainerName; } else if (core_util_1.isNodeLike && credentialOrPipelineOrContainerName instanceof StorageSharedKeyCredential_js_1.StorageSharedKeyCredential || credentialOrPipelineOrContainerName instanceof AnonymousCredential_js_1.AnonymousCredential || (0, core_auth_1.isTokenCredential)(credentialOrPipelineOrContainerName)) { url2 = urlOrConnectionString; options = blobNameOrOptions; - pipeline = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options); } else if (!credentialOrPipelineOrContainerName && typeof credentialOrPipelineOrContainerName !== "string") { url2 = urlOrConnectionString; - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else if (credentialOrPipelineOrContainerName && typeof credentialOrPipelineOrContainerName === "string" && blobNameOrOptions && typeof blobNameOrOptions === "string") { const containerName = credentialOrPipelineOrContainerName; const blobName = blobNameOrOptions; @@ -70440,20 +71157,20 @@ var require_Clients = __commonJS({ if (!options.proxyOptions) { options.proxyOptions = (0, core_rest_pipeline_1.getDefaultProxySettings)(extractedCreds.proxyUri); } - pipeline = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); } else { throw new Error("Account connection string is only supported in Node.js environment"); } } else if (extractedCreds.kind === "SASConnString") { url2 = (0, utils_common_js_1.appendToURLPath)((0, utils_common_js_1.appendToURLPath)(extractedCreds.url, encodeURIComponent(containerName)), encodeURIComponent(blobName)) + "?" + extractedCreds.accountSas; - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else { throw new Error("Connection string must be either an Account connection string or a SAS connection string"); } } else { throw new Error("Expecting non-empty strings for containerName and blobName parameters"); } - super(url2, pipeline); + super(url2, pipeline2); this.pageBlobContext = this.storageClientContext.pageBlob; } /** @@ -71518,10 +72235,10 @@ var require_BlobBatch = __commonJS({ accountKey: credential.accountKey }), { phase: "Sign" }); } - const pipeline = new Pipeline_js_1.Pipeline([]); - pipeline._credential = credential; - pipeline._corePipeline = corePipeline; - return pipeline; + const pipeline2 = new Pipeline_js_1.Pipeline([]); + pipeline2._credential = credential; + pipeline2._corePipeline = corePipeline; + return pipeline2; } appendSubRequestToBody(request3) { this.body += [ @@ -71613,15 +72330,15 @@ var require_BlobBatchClient = __commonJS({ var BlobBatchClient = class { serviceOrContainerContext; constructor(url2, credentialOrPipeline, options) { - let pipeline; + let pipeline2; if ((0, Pipeline_js_1.isPipelineLike)(credentialOrPipeline)) { - pipeline = credentialOrPipeline; + pipeline2 = credentialOrPipeline; } else if (!credentialOrPipeline) { - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else { - pipeline = (0, Pipeline_js_1.newPipeline)(credentialOrPipeline, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(credentialOrPipeline, options); } - const storageClientContext = new StorageContextClient_js_1.StorageContextClient(url2, (0, Pipeline_js_1.getCoreClientOptions)(pipeline)); + const storageClientContext = new StorageContextClient_js_1.StorageContextClient(url2, (0, Pipeline_js_1.getCoreClientOptions)(pipeline2)); const path29 = (0, utils_common_js_1.getURLPath)(url2); if (path29 && path29 !== "/") { this.serviceOrContainerContext = storageClientContext.container; @@ -71784,18 +72501,18 @@ var require_ContainerClient = __commonJS({ return this._containerName; } constructor(urlOrConnectionString, credentialOrPipelineOrContainerName, options) { - let pipeline; + let pipeline2; let url2; options = options || {}; if ((0, Pipeline_js_1.isPipelineLike)(credentialOrPipelineOrContainerName)) { url2 = urlOrConnectionString; - pipeline = credentialOrPipelineOrContainerName; + pipeline2 = credentialOrPipelineOrContainerName; } else if (core_util_1.isNodeLike && credentialOrPipelineOrContainerName instanceof StorageSharedKeyCredential_js_1.StorageSharedKeyCredential || credentialOrPipelineOrContainerName instanceof AnonymousCredential_js_1.AnonymousCredential || (0, core_auth_1.isTokenCredential)(credentialOrPipelineOrContainerName)) { url2 = urlOrConnectionString; - pipeline = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options); } else if (!credentialOrPipelineOrContainerName && typeof credentialOrPipelineOrContainerName !== "string") { url2 = urlOrConnectionString; - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else if (credentialOrPipelineOrContainerName && typeof credentialOrPipelineOrContainerName === "string") { const containerName = credentialOrPipelineOrContainerName; const extractedCreds = (0, utils_common_js_1.extractConnectionStringParts)(urlOrConnectionString); @@ -71806,20 +72523,20 @@ var require_ContainerClient = __commonJS({ if (!options.proxyOptions) { options.proxyOptions = (0, core_rest_pipeline_1.getDefaultProxySettings)(extractedCreds.proxyUri); } - pipeline = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); } else { throw new Error("Account connection string is only supported in Node.js environment"); } } else if (extractedCreds.kind === "SASConnString") { url2 = (0, utils_common_js_1.appendToURLPath)(extractedCreds.url, encodeURIComponent(containerName)) + "?" + extractedCreds.accountSas; - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } else { throw new Error("Connection string must be either an Account connection string or a SAS connection string"); } } else { throw new Error("Expecting non-empty strings for containerName parameter"); } - super(url2, pipeline); + super(url2, pipeline2); this._containerName = this.getContainerNameFromUrl(); this.containerContext = this.storageClientContext.container; } @@ -73497,28 +74214,28 @@ var require_BlobServiceClient = __commonJS({ if (!options.proxyOptions) { options.proxyOptions = (0, core_rest_pipeline_1.getDefaultProxySettings)(extractedCreds.proxyUri); } - const pipeline = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); - return new _BlobServiceClient(extractedCreds.url, pipeline); + const pipeline2 = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options); + return new _BlobServiceClient(extractedCreds.url, pipeline2); } else { throw new Error("Account connection string is only supported in Node.js environment"); } } else if (extractedCreds.kind === "SASConnString") { - const pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); - return new _BlobServiceClient(extractedCreds.url + "?" + extractedCreds.accountSas, pipeline); + const pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + return new _BlobServiceClient(extractedCreds.url + "?" + extractedCreds.accountSas, pipeline2); } else { throw new Error("Connection string must be either an Account connection string or a SAS connection string"); } } constructor(url2, credentialOrPipeline, options) { - let pipeline; + let pipeline2; if ((0, Pipeline_js_1.isPipelineLike)(credentialOrPipeline)) { - pipeline = credentialOrPipeline; + pipeline2 = credentialOrPipeline; } else if (core_util_1.isNodeLike && credentialOrPipeline instanceof StorageSharedKeyCredential_js_1.StorageSharedKeyCredential || credentialOrPipeline instanceof AnonymousCredential_js_1.AnonymousCredential || (0, core_auth_1.isTokenCredential)(credentialOrPipeline)) { - pipeline = (0, Pipeline_js_1.newPipeline)(credentialOrPipeline, options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(credentialOrPipeline, options); } else { - pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); + pipeline2 = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options); } - super(url2, pipeline); + super(url2, pipeline2); this.serviceContext = this.storageClientContext.service; } /** @@ -74396,7 +75113,7 @@ var require_uploadUtils = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.UploadProgress = void 0; exports2.uploadCacheArchiveSDK = uploadCacheArchiveSDK; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var storage_blob_1 = require_commonjs15(); var errors_1 = require_errors2(); var UploadProgress = class { @@ -74438,7 +75155,7 @@ var require_uploadUtils = __commonJS({ const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); const elapsedTime = Date.now() - this.startTime; const uploadSpeed = (transferredBytes / (1024 * 1024) / (elapsedTime / 1e3)).toFixed(1); - core30.info(`Sent ${transferredBytes} of ${this.contentLength} (${percentage}%), ${uploadSpeed} MBs/sec`); + core31.info(`Sent ${transferredBytes} of ${this.contentLength} (${percentage}%), ${uploadSpeed} MBs/sec`); if (this.isDone()) { this.displayedComplete = true; } @@ -74495,14 +75212,14 @@ var require_uploadUtils = __commonJS({ }; try { uploadProgress.startDisplayTimer(); - core30.debug(`BlobClient: ${blobClient.name}:${blobClient.accountName}:${blobClient.containerName}`); + core31.debug(`BlobClient: ${blobClient.name}:${blobClient.accountName}:${blobClient.containerName}`); const response = yield blockBlobClient.uploadFile(archivePath, uploadOptions); if (response._response.status >= 400) { throw new errors_1.InvalidResponseError(`uploadCacheArchiveSDK: upload failed with status code ${response._response.status}`); } return response; } catch (error3) { - core30.warning(`uploadCacheArchiveSDK: internal error uploading cache archive: ${error3.message}`); + core31.warning(`uploadCacheArchiveSDK: internal error uploading cache archive: ${error3.message}`); throw error3; } finally { uploadProgress.stopDisplayTimer(); @@ -74587,7 +75304,7 @@ var require_requestUtils = __commonJS({ exports2.retry = retry2; exports2.retryTypedResponse = retryTypedResponse; exports2.retryHttpClientResponse = retryHttpClientResponse; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var http_client_1 = require_lib(); var constants_1 = require_constants7(); function isSuccessStatusCode(statusCode) { @@ -74645,9 +75362,9 @@ var require_requestUtils = __commonJS({ isRetryable = isRetryableStatusCode(statusCode); errorMessage = `Cache service responded with ${statusCode}`; } - core30.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); + core31.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); if (!isRetryable) { - core30.debug(`${name} - Error is not retryable`); + core31.debug(`${name} - Error is not retryable`); break; } yield sleep(delay2); @@ -74690,7 +75407,7 @@ var require_requestUtils = __commonJS({ }); // node_modules/@azure/abort-controller/dist/index.js -var require_dist4 = __commonJS({ +var require_dist5 = __commonJS({ "node_modules/@azure/abort-controller/dist/index.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); @@ -74906,7 +75623,7 @@ var require_downloadUtils = __commonJS({ exports2.downloadCacheHttpClient = downloadCacheHttpClient; exports2.downloadCacheHttpClientConcurrent = downloadCacheHttpClientConcurrent; exports2.downloadCacheStorageSDK = downloadCacheStorageSDK; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var http_client_1 = require_lib(); var storage_blob_1 = require_commonjs15(); var buffer = __importStar2(require("buffer")); @@ -74916,11 +75633,11 @@ var require_downloadUtils = __commonJS({ var utils = __importStar2(require_cacheUtils()); var constants_1 = require_constants7(); var requestUtils_1 = require_requestUtils(); - var abort_controller_1 = require_dist4(); + var abort_controller_1 = require_dist5(); function pipeResponseToStream(response, output) { return __awaiter2(this, void 0, void 0, function* () { - const pipeline = util3.promisify(stream2.pipeline); - yield pipeline(response.message, output); + const pipeline2 = util3.promisify(stream2.pipeline); + yield pipeline2(response.message, output); }); } var DownloadProgress = class { @@ -74944,7 +75661,7 @@ var require_downloadUtils = __commonJS({ this.segmentIndex = this.segmentIndex + 1; this.segmentSize = segmentSize; this.receivedBytes = 0; - core30.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); + core31.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); } /** * Sets the number of bytes received for the current segment. @@ -74978,7 +75695,7 @@ var require_downloadUtils = __commonJS({ const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); const elapsedTime = Date.now() - this.startTime; const downloadSpeed = (transferredBytes / (1024 * 1024) / (elapsedTime / 1e3)).toFixed(1); - core30.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); + core31.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); if (this.isDone()) { this.displayedComplete = true; } @@ -75028,7 +75745,7 @@ var require_downloadUtils = __commonJS({ })); downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { downloadResponse.message.destroy(); - core30.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); + core31.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); }); yield pipeResponseToStream(downloadResponse, writeStream); const contentLengthHeader = downloadResponse.message.headers["content-length"]; @@ -75039,7 +75756,7 @@ var require_downloadUtils = __commonJS({ throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); } } else { - core30.debug("Unable to validate download, no Content-Length header"); + core31.debug("Unable to validate download, no Content-Length header"); } }); } @@ -75157,7 +75874,7 @@ var require_downloadUtils = __commonJS({ const properties = yield client.getProperties(); const contentLength = (_a2 = properties.contentLength) !== null && _a2 !== void 0 ? _a2 : -1; if (contentLength < 0) { - core30.debug("Unable to determine content length, downloading file with http-client..."); + core31.debug("Unable to determine content length, downloading file with http-client..."); yield downloadCacheHttpClient(archiveLocation, archivePath); } else { const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); @@ -75247,7 +75964,7 @@ var require_options = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getUploadOptions = getUploadOptions; exports2.getDownloadOptions = getDownloadOptions; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); function getUploadOptions(copy) { const result = { useAzureSdk: false, @@ -75267,9 +75984,9 @@ var require_options = __commonJS({ } result.uploadConcurrency = !isNaN(Number(process.env["CACHE_UPLOAD_CONCURRENCY"])) ? Math.min(32, Number(process.env["CACHE_UPLOAD_CONCURRENCY"])) : result.uploadConcurrency; result.uploadChunkSize = !isNaN(Number(process.env["CACHE_UPLOAD_CHUNK_SIZE"])) ? Math.min(128 * 1024 * 1024, Number(process.env["CACHE_UPLOAD_CHUNK_SIZE"]) * 1024 * 1024) : result.uploadChunkSize; - core30.debug(`Use Azure SDK: ${result.useAzureSdk}`); - core30.debug(`Upload concurrency: ${result.uploadConcurrency}`); - core30.debug(`Upload chunk size: ${result.uploadChunkSize}`); + core31.debug(`Use Azure SDK: ${result.useAzureSdk}`); + core31.debug(`Upload concurrency: ${result.uploadConcurrency}`); + core31.debug(`Upload chunk size: ${result.uploadChunkSize}`); return result; } function getDownloadOptions(copy) { @@ -75305,12 +76022,12 @@ var require_options = __commonJS({ if (segmentDownloadTimeoutMins && !isNaN(Number(segmentDownloadTimeoutMins)) && isFinite(Number(segmentDownloadTimeoutMins))) { result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1e3; } - core30.debug(`Use Azure SDK: ${result.useAzureSdk}`); - core30.debug(`Download concurrency: ${result.downloadConcurrency}`); - core30.debug(`Request timeout (ms): ${result.timeoutInMs}`); - core30.debug(`Cache segment download timeout mins env var: ${process.env["SEGMENT_DOWNLOAD_TIMEOUT_MINS"]}`); - core30.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); - core30.debug(`Lookup only: ${result.lookupOnly}`); + core31.debug(`Use Azure SDK: ${result.useAzureSdk}`); + core31.debug(`Download concurrency: ${result.downloadConcurrency}`); + core31.debug(`Request timeout (ms): ${result.timeoutInMs}`); + core31.debug(`Cache segment download timeout mins env var: ${process.env["SEGMENT_DOWNLOAD_TIMEOUT_MINS"]}`); + core31.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); + core31.debug(`Lookup only: ${result.lookupOnly}`); return result; } } @@ -75323,6 +76040,9 @@ var require_config = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.isGhes = isGhes; exports2.getCacheServiceVersion = getCacheServiceVersion; + exports2.getCacheMode = getCacheMode; + exports2.isCacheReadable = isCacheReadable; + exports2.isCacheWritable = isCacheWritable; exports2.getCacheServiceURL = getCacheServiceURL; function isGhes() { const ghUrl = new URL(process.env["GITHUB_SERVER_URL"] || "https://github.com"); @@ -75337,6 +76057,20 @@ var require_config = __commonJS({ return "v1"; return process.env["ACTIONS_CACHE_SERVICE_V2"] ? "v2" : "v1"; } + var KNOWN_CACHE_MODES = ["none", "read", "write", "write-only"]; + function getCacheMode() { + return (process.env["ACTIONS_CACHE_MODE"] || "").trim().toLowerCase(); + } + function isCacheReadable(mode) { + if (!KNOWN_CACHE_MODES.includes(mode)) + return true; + return mode === "read" || mode === "write"; + } + function isCacheWritable(mode) { + if (!KNOWN_CACHE_MODES.includes(mode)) + return true; + return mode === "write" || mode === "write-only"; + } function getCacheServiceURL() { const version = getCacheServiceVersion(); switch (version) { @@ -75356,7 +76090,7 @@ var require_package = __commonJS({ "node_modules/@actions/cache/package.json"(exports2, module2) { module2.exports = { name: "@actions/cache", - version: "5.1.0", + version: "5.2.0", preview: true, description: "Actions cache lib", keywords: [ @@ -75504,7 +76238,7 @@ var require_cacheHttpClient = __commonJS({ exports2.downloadCache = downloadCache; exports2.reserveCache = reserveCache; exports2.saveCache = saveCache5; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var http_client_1 = require_lib(); var auth_1 = require_auth(); var fs31 = __importStar2(require("fs")); @@ -75515,6 +76249,7 @@ var require_cacheHttpClient = __commonJS({ var options_1 = require_options(); var requestUtils_1 = require_requestUtils(); var config_1 = require_config(); + var constants_1 = require_constants7(); var user_agent_1 = require_user_agent(); function getCacheApiUrl(resource) { const baseUrl = (0, config_1.getCacheServiceURL)(); @@ -75522,7 +76257,7 @@ var require_cacheHttpClient = __commonJS({ throw new Error("Cache Service Url not found, unable to restore cache."); } const url2 = `${baseUrl}_apis/artifactcache/${resource}`; - core30.debug(`Resource Url: ${url2}`); + core31.debug(`Resource Url: ${url2}`); return url2; } function createAcceptHeader(type, apiVersion) { @@ -75543,6 +76278,7 @@ var require_cacheHttpClient = __commonJS({ } function getCacheEntry(keys, paths, options) { return __awaiter2(this, void 0, void 0, function* () { + var _a2; const httpClient = createHttpClient(); const version = utils.getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); const resource = `cache?keys=${encodeURIComponent(keys.join(","))}&version=${version}`; @@ -75550,12 +76286,16 @@ var require_cacheHttpClient = __commonJS({ return httpClient.getJson(getCacheApiUrl(resource)); })); if (response.statusCode === 204) { - if (core30.isDebug()) { + if (core31.isDebug()) { yield printCachesListForDiagnostics(keys[0], httpClient, version); } return null; } if (!(0, requestUtils_1.isSuccessStatusCode)(response.statusCode)) { + const errorMessage = (_a2 = response.error) === null || _a2 === void 0 ? void 0 : _a2.message; + if (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes(constants_1.CacheReadDeniedMessagePrefix)) { + throw new Error(errorMessage); + } throw new Error(`Cache service responded with ${response.statusCode}`); } const cacheResult = response.result; @@ -75563,9 +76303,9 @@ var require_cacheHttpClient = __commonJS({ if (!cacheDownloadUrl) { throw new Error("Cache not found."); } - core30.setSecret(cacheDownloadUrl); - core30.debug(`Cache Result:`); - core30.debug(JSON.stringify(cacheResult)); + core31.setSecret(cacheDownloadUrl); + core31.debug(`Cache Result:`); + core31.debug(JSON.stringify(cacheResult)); return cacheResult; }); } @@ -75579,10 +76319,10 @@ var require_cacheHttpClient = __commonJS({ const cacheListResult = response.result; const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; if (totalCount && totalCount > 0) { - core30.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env["GITHUB_REF"]}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key + core31.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env["GITHUB_REF"]}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key Other caches with similar key:`); for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { - core30.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); + core31.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); } } } @@ -75625,7 +76365,7 @@ Other caches with similar key:`); } function uploadChunk(httpClient, resourceUrl, openStream, start, end) { return __awaiter2(this, void 0, void 0, function* () { - core30.debug(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); + core31.debug(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); const additionalHeaders = { "Content-Type": "application/octet-stream", "Content-Range": getContentRange(start, end) @@ -75647,7 +76387,7 @@ Other caches with similar key:`); const concurrency = utils.assertDefined("uploadConcurrency", uploadOptions.uploadConcurrency); const maxChunkSize = utils.assertDefined("uploadChunkSize", uploadOptions.uploadChunkSize); const parallelUploads = [...new Array(concurrency).keys()]; - core30.debug("Awaiting all uploads"); + core31.debug("Awaiting all uploads"); let offset = 0; try { yield Promise.all(parallelUploads.map(() => __awaiter2(this, void 0, void 0, function* () { @@ -75690,16 +76430,16 @@ Other caches with similar key:`); yield (0, uploadUtils_1.uploadCacheArchiveSDK)(signedUploadURL, archivePath, options); } else { const httpClient = createHttpClient(); - core30.debug("Upload cache"); + core31.debug("Upload cache"); yield uploadFile(httpClient, cacheId, archivePath, options); - core30.debug("Commiting cache"); + core31.debug("Commiting cache"); const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); - core30.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); + core31.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); if (!(0, requestUtils_1.isSuccessStatusCode)(commitCacheResponse.statusCode)) { throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); } - core30.info("Cache saved successfully"); + core31.info("Cache saved successfully"); } }); } @@ -78574,9 +79314,9 @@ var require_enum_object = __commonJS({ if (!isEnumObject(enumObject)) throw new Error("not a typescript enum object"); let values = []; - for (let [name, number] of Object.entries(enumObject)) - if (typeof number == "number") - values.push({ name, number }); + for (let [name, number2] of Object.entries(enumObject)) + if (typeof number2 == "number") + values.push({ name, number: number2 }); return values; } exports2.listEnumValues = listEnumValues; @@ -78893,28 +79633,28 @@ var require_rpc_options = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.mergeRpcOptions = void 0; var runtime_1 = require_commonjs16(); - function mergeRpcOptions(defaults2, options) { + function mergeRpcOptions(defaults3, options) { if (!options) - return defaults2; + return defaults3; let o = {}; - copy(defaults2, o); + copy(defaults3, o); copy(options, o); for (let key of Object.keys(options)) { let val = options[key]; switch (key) { case "jsonOptions": - o.jsonOptions = runtime_1.mergeJsonOptions(defaults2.jsonOptions, o.jsonOptions); + o.jsonOptions = runtime_1.mergeJsonOptions(defaults3.jsonOptions, o.jsonOptions); break; case "binaryOptions": - o.binaryOptions = runtime_1.mergeBinaryOptions(defaults2.binaryOptions, o.binaryOptions); + o.binaryOptions = runtime_1.mergeBinaryOptions(defaults3.binaryOptions, o.binaryOptions); break; case "meta": o.meta = {}; - copy(defaults2.meta, o.meta); + copy(defaults3.meta, o.meta); copy(options.meta, o.meta); break; case "interceptors": - o.interceptors = defaults2.interceptors ? defaults2.interceptors.concat(val) : val.concat(); + o.interceptors = defaults3.interceptors ? defaults3.interceptors.concat(val) : val.concat(); break; } } @@ -81178,11 +81918,11 @@ var require_cache4 = __commonJS({ }); }; Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.FinalizeCacheError = exports2.CacheWriteDeniedError = exports2.CACHE_WRITE_DENIED_PREFIX = exports2.ReserveCacheError = exports2.ValidationError = void 0; + exports2.FinalizeCacheError = exports2.CacheReadDeniedError = exports2.CACHE_READ_DENIED_PREFIX = exports2.CacheWriteDeniedError = exports2.CACHE_WRITE_DENIED_PREFIX = exports2.ReserveCacheError = exports2.ValidationError = void 0; exports2.isFeatureAvailable = isFeatureAvailable; exports2.restoreCache = restoreCache5; exports2.saveCache = saveCache5; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var path29 = __importStar2(require("path")); var utils = __importStar2(require_cacheUtils()); var cacheHttpClient = __importStar2(require_cacheHttpClient()); @@ -81190,6 +81930,7 @@ var require_cache4 = __commonJS({ var config_1 = require_config(); var tar_1 = require_tar(); var http_client_1 = require_lib(); + var constants_1 = require_constants7(); var ValidationError = class _ValidationError extends Error { constructor(message) { super(message); @@ -81215,6 +81956,15 @@ var require_cache4 = __commonJS({ } }; exports2.CacheWriteDeniedError = CacheWriteDeniedError; + exports2.CACHE_READ_DENIED_PREFIX = constants_1.CacheReadDeniedMessagePrefix; + var CacheReadDeniedError = class _CacheReadDeniedError extends Error { + constructor(message) { + super(message); + this.name = "CacheReadDeniedError"; + Object.setPrototypeOf(this, _CacheReadDeniedError.prototype); + } + }; + exports2.CacheReadDeniedError = CacheReadDeniedError; var FinalizeCacheError = class _FinalizeCacheError extends Error { constructor(message) { super(message); @@ -81250,8 +82000,14 @@ var require_cache4 = __commonJS({ function restoreCache5(paths_1, primaryKey_1, restoreKeys_1, options_1) { return __awaiter2(this, arguments, void 0, function* (paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) { const cacheServiceVersion = (0, config_1.getCacheServiceVersion)(); - core30.debug(`Cache service version: ${cacheServiceVersion}`); + core31.debug(`Cache service version: ${cacheServiceVersion}`); checkPaths(paths); + const cacheMode = (0, config_1.getCacheMode)(); + if (!(0, config_1.isCacheReadable)(cacheMode)) { + core31.info(`Cache restore skipped: the effective cache-mode '${cacheMode}' does not permit reads.`); + core31.debug(`Skipped restore for paths [${paths.join(", ")}] with primary key '${primaryKey}'.`); + return void 0; + } switch (cacheServiceVersion) { case "v2": return yield restoreCacheV2(paths, primaryKey, restoreKeys, options, enableCrossOsArchive); @@ -81263,10 +82019,11 @@ var require_cache4 = __commonJS({ } function restoreCacheV1(paths_1, primaryKey_1, restoreKeys_1, options_1) { return __awaiter2(this, arguments, void 0, function* (paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) { + var _a2; restoreKeys = restoreKeys || []; const keys = [primaryKey, ...restoreKeys]; - core30.debug("Resolved Keys:"); - core30.debug(JSON.stringify(keys)); + core31.debug("Resolved Keys:"); + core31.debug(JSON.stringify(keys)); if (keys.length > 10) { throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); } @@ -81276,27 +82033,36 @@ var require_cache4 = __commonJS({ const compressionMethod = yield utils.getCompressionMethod(); let archivePath = ""; try { - const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, { - compressionMethod, - enableCrossOsArchive - }); + let cacheEntry; + try { + cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, { + compressionMethod, + enableCrossOsArchive + }); + } catch (error3) { + const errorMessage = (_a2 = error3 === null || error3 === void 0 ? void 0 : error3.message) !== null && _a2 !== void 0 ? _a2 : ""; + if (errorMessage.includes(exports2.CACHE_READ_DENIED_PREFIX)) { + throw new CacheReadDeniedError(errorMessage); + } + throw error3; + } if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) { return void 0; } if (options === null || options === void 0 ? void 0 : options.lookupOnly) { - core30.info("Lookup only - skipping download"); + core31.info("Lookup only - skipping download"); return cacheEntry.cacheKey; } archivePath = path29.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); - core30.debug(`Archive Path: ${archivePath}`); + core31.debug(`Archive Path: ${archivePath}`); yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options); - if (core30.isDebug()) { + if (core31.isDebug()) { yield (0, tar_1.listTar)(archivePath, compressionMethod); } const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core30.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); + core31.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); yield (0, tar_1.extractTar)(archivePath, compressionMethod); - core30.info("Cache restored successfully"); + core31.info("Cache restored successfully"); return cacheEntry.cacheKey; } catch (error3) { const typedError = error3; @@ -81304,16 +82070,16 @@ var require_cache4 = __commonJS({ throw error3; } else { if (typedError instanceof http_client_1.HttpClientError && typeof typedError.statusCode === "number" && typedError.statusCode >= 500) { - core30.error(`Failed to restore: ${error3.message}`); + core31.error(`Failed to restore: ${error3.message}`); } else { - core30.warning(`Failed to restore: ${error3.message}`); + core31.warning(`Failed to restore: ${error3.message}`); } } } finally { try { yield utils.unlinkFile(archivePath); } catch (error3) { - core30.debug(`Failed to delete archive: ${error3}`); + core31.debug(`Failed to delete archive: ${error3}`); } } return void 0; @@ -81321,11 +82087,12 @@ var require_cache4 = __commonJS({ } function restoreCacheV2(paths_1, primaryKey_1, restoreKeys_1, options_1) { return __awaiter2(this, arguments, void 0, function* (paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) { + var _a2; options = Object.assign(Object.assign({}, options), { useAzureSdk: true }); restoreKeys = restoreKeys || []; const keys = [primaryKey, ...restoreKeys]; - core30.debug("Resolved Keys:"); - core30.debug(JSON.stringify(keys)); + core31.debug("Resolved Keys:"); + core31.debug(JSON.stringify(keys)); if (keys.length > 10) { throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); } @@ -81341,32 +82108,41 @@ var require_cache4 = __commonJS({ restoreKeys, version: utils.getCacheVersion(paths, compressionMethod, enableCrossOsArchive) }; - const response = yield twirpClient.GetCacheEntryDownloadURL(request3); + let response; + try { + response = yield twirpClient.GetCacheEntryDownloadURL(request3); + } catch (error3) { + const errorMessage = (_a2 = error3 === null || error3 === void 0 ? void 0 : error3.message) !== null && _a2 !== void 0 ? _a2 : ""; + if (errorMessage.includes(exports2.CACHE_READ_DENIED_PREFIX)) { + throw new CacheReadDeniedError(errorMessage); + } + throw error3; + } if (!response.ok) { - core30.debug(`Cache not found for version ${request3.version} of keys: ${keys.join(", ")}`); + core31.debug(`Cache not found for version ${request3.version} of keys: ${keys.join(", ")}`); return void 0; } const isRestoreKeyMatch = request3.key !== response.matchedKey; if (isRestoreKeyMatch) { - core30.info(`Cache hit for restore-key: ${response.matchedKey}`); + core31.info(`Cache hit for restore-key: ${response.matchedKey}`); } else { - core30.info(`Cache hit for: ${response.matchedKey}`); + core31.info(`Cache hit for: ${response.matchedKey}`); } if (options === null || options === void 0 ? void 0 : options.lookupOnly) { - core30.info("Lookup only - skipping download"); + core31.info("Lookup only - skipping download"); return response.matchedKey; } archivePath = path29.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); - core30.debug(`Archive path: ${archivePath}`); - core30.debug(`Starting download of archive to: ${archivePath}`); + core31.debug(`Archive path: ${archivePath}`); + core31.debug(`Starting download of archive to: ${archivePath}`); yield cacheHttpClient.downloadCache(response.signedDownloadUrl, archivePath, options); const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core30.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); - if (core30.isDebug()) { + core31.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); + if (core31.isDebug()) { yield (0, tar_1.listTar)(archivePath, compressionMethod); } yield (0, tar_1.extractTar)(archivePath, compressionMethod); - core30.info("Cache restored successfully"); + core31.info("Cache restored successfully"); return response.matchedKey; } catch (error3) { const typedError = error3; @@ -81374,9 +82150,9 @@ var require_cache4 = __commonJS({ throw error3; } else { if (typedError instanceof http_client_1.HttpClientError && typeof typedError.statusCode === "number" && typedError.statusCode >= 500) { - core30.error(`Failed to restore: ${error3.message}`); + core31.error(`Failed to restore: ${error3.message}`); } else { - core30.warning(`Failed to restore: ${error3.message}`); + core31.warning(`Failed to restore: ${error3.message}`); } } } finally { @@ -81385,7 +82161,7 @@ var require_cache4 = __commonJS({ yield utils.unlinkFile(archivePath); } } catch (error3) { - core30.debug(`Failed to delete archive: ${error3}`); + core31.debug(`Failed to delete archive: ${error3}`); } } return void 0; @@ -81394,9 +82170,15 @@ var require_cache4 = __commonJS({ function saveCache5(paths_1, key_1, options_1) { return __awaiter2(this, arguments, void 0, function* (paths, key, options, enableCrossOsArchive = false) { const cacheServiceVersion = (0, config_1.getCacheServiceVersion)(); - core30.debug(`Cache service version: ${cacheServiceVersion}`); + core31.debug(`Cache service version: ${cacheServiceVersion}`); checkPaths(paths); checkKey(key); + const cacheMode = (0, config_1.getCacheMode)(); + if (!(0, config_1.isCacheWritable)(cacheMode)) { + core31.info(`Cache save skipped: the effective cache-mode '${cacheMode}' does not permit writes.`); + core31.debug(`Skipped save for paths [${paths.join(", ")}] with key '${key}'.`); + return -1; + } switch (cacheServiceVersion) { case "v2": return yield saveCacheV2(paths, key, options, enableCrossOsArchive); @@ -81412,26 +82194,26 @@ var require_cache4 = __commonJS({ const compressionMethod = yield utils.getCompressionMethod(); let cacheId = -1; const cachePaths = yield utils.resolvePaths(paths); - core30.debug("Cache Paths:"); - core30.debug(`${JSON.stringify(cachePaths)}`); + core31.debug("Cache Paths:"); + core31.debug(`${JSON.stringify(cachePaths)}`); if (cachePaths.length === 0) { throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); } const archiveFolder = yield utils.createTempDirectory(); const archivePath = path29.join(archiveFolder, utils.getCacheFileName(compressionMethod)); - core30.debug(`Archive Path: ${archivePath}`); + core31.debug(`Archive Path: ${archivePath}`); try { yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); - if (core30.isDebug()) { + if (core31.isDebug()) { yield (0, tar_1.listTar)(archivePath, compressionMethod); } const fileSizeLimit = 10 * 1024 * 1024 * 1024; const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core30.debug(`File Size: ${archiveFileSize}`); + core31.debug(`File Size: ${archiveFileSize}`); if (archiveFileSize > fileSizeLimit && !(0, config_1.isGhes)()) { throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); } - core30.debug("Reserving Cache"); + core31.debug("Reserving Cache"); const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { compressionMethod, enableCrossOsArchive, @@ -81448,28 +82230,28 @@ var require_cache4 = __commonJS({ } throw new ReserveCacheError2(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${detailMessage}`); } - core30.debug(`Saving Cache (ID: ${cacheId})`); + core31.debug(`Saving Cache (ID: ${cacheId})`); yield cacheHttpClient.saveCache(cacheId, archivePath, "", options); } catch (error3) { const typedError = error3; if (typedError.name === ValidationError.name) { throw error3; } else if (typedError.name === CacheWriteDeniedError.name) { - core30.warning(`Failed to save: ${typedError.message}`); + core31.warning(`Failed to save: ${typedError.message}`); } else if (typedError.name === ReserveCacheError2.name) { - core30.info(`Failed to save: ${typedError.message}`); + core31.info(`Failed to save: ${typedError.message}`); } else { if (typedError instanceof http_client_1.HttpClientError && typeof typedError.statusCode === "number" && typedError.statusCode >= 500) { - core30.error(`Failed to save: ${typedError.message}`); + core31.error(`Failed to save: ${typedError.message}`); } else { - core30.warning(`Failed to save: ${typedError.message}`); + core31.warning(`Failed to save: ${typedError.message}`); } } } finally { try { yield utils.unlinkFile(archivePath); } catch (error3) { - core30.debug(`Failed to delete archive: ${error3}`); + core31.debug(`Failed to delete archive: ${error3}`); } } return cacheId; @@ -81483,23 +82265,23 @@ var require_cache4 = __commonJS({ const twirpClient = cacheTwirpClient.internalCacheTwirpClient(); let cacheId = -1; const cachePaths = yield utils.resolvePaths(paths); - core30.debug("Cache Paths:"); - core30.debug(`${JSON.stringify(cachePaths)}`); + core31.debug("Cache Paths:"); + core31.debug(`${JSON.stringify(cachePaths)}`); if (cachePaths.length === 0) { throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); } const archiveFolder = yield utils.createTempDirectory(); const archivePath = path29.join(archiveFolder, utils.getCacheFileName(compressionMethod)); - core30.debug(`Archive Path: ${archivePath}`); + core31.debug(`Archive Path: ${archivePath}`); try { yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); - if (core30.isDebug()) { + if (core31.isDebug()) { yield (0, tar_1.listTar)(archivePath, compressionMethod); } const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core30.debug(`File Size: ${archiveFileSize}`); + core31.debug(`File Size: ${archiveFileSize}`); options.archiveSizeBytes = archiveFileSize; - core30.debug("Reserving Cache"); + core31.debug("Reserving Cache"); const version = utils.getCacheVersion(paths, compressionMethod, enableCrossOsArchive); const request3 = { key, @@ -81510,20 +82292,20 @@ var require_cache4 = __commonJS({ const response = yield twirpClient.CreateCacheEntry(request3); if (!response.ok) { if (response.message && !response.message.startsWith(exports2.CACHE_WRITE_DENIED_PREFIX)) { - core30.warning(`Cache reservation failed: ${response.message}`); + core31.warning(`Cache reservation failed: ${response.message}`); } throw new Error(response.message || "Response was not ok"); } signedUploadUrl = response.signedUploadUrl; } catch (error3) { - core30.debug(`Failed to reserve cache: ${error3}`); + core31.debug(`Failed to reserve cache: ${error3}`); const errorMessage = (_a2 = error3 === null || error3 === void 0 ? void 0 : error3.message) !== null && _a2 !== void 0 ? _a2 : ""; if (errorMessage.startsWith(exports2.CACHE_WRITE_DENIED_PREFIX)) { throw new CacheWriteDeniedError(`Unable to reserve cache with key ${key}. More details: ${errorMessage}`); } throw new ReserveCacheError2(`Unable to reserve cache with key ${key}, another job may be creating this cache.`); } - core30.debug(`Attempting to upload cache located at: ${archivePath}`); + core31.debug(`Attempting to upload cache located at: ${archivePath}`); yield cacheHttpClient.saveCache(cacheId, archivePath, signedUploadUrl, options); const finalizeRequest = { key, @@ -81531,7 +82313,7 @@ var require_cache4 = __commonJS({ sizeBytes: `${archiveFileSize}` }; const finalizeResponse = yield twirpClient.FinalizeCacheEntryUpload(finalizeRequest); - core30.debug(`FinalizeCacheEntryUploadResponse: ${finalizeResponse.ok}`); + core31.debug(`FinalizeCacheEntryUploadResponse: ${finalizeResponse.ok}`); if (!finalizeResponse.ok) { if (finalizeResponse.message) { throw new FinalizeCacheError(finalizeResponse.message); @@ -81544,23 +82326,23 @@ var require_cache4 = __commonJS({ if (typedError.name === ValidationError.name) { throw error3; } else if (typedError.name === CacheWriteDeniedError.name) { - core30.warning(`Failed to save: ${typedError.message}`); + core31.warning(`Failed to save: ${typedError.message}`); } else if (typedError.name === ReserveCacheError2.name) { - core30.info(`Failed to save: ${typedError.message}`); + core31.info(`Failed to save: ${typedError.message}`); } else if (typedError.name === FinalizeCacheError.name) { - core30.warning(typedError.message); + core31.warning(typedError.message); } else { if (typedError instanceof http_client_1.HttpClientError && typeof typedError.statusCode === "number" && typedError.statusCode >= 500) { - core30.error(`Failed to save: ${typedError.message}`); + core31.error(`Failed to save: ${typedError.message}`); } else { - core30.warning(`Failed to save: ${typedError.message}`); + core31.warning(`Failed to save: ${typedError.message}`); } } } finally { try { yield utils.unlinkFile(archivePath); } catch (error3) { - core30.debug(`Failed to delete archive: ${error3}`); + core31.debug(`Failed to delete archive: ${error3}`); } } return cacheId; @@ -81787,7 +82569,7 @@ var require_retry_helper = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.RetryHelper = void 0; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var RetryHelper = class { constructor(maxAttempts, minSeconds, maxSeconds) { if (maxAttempts < 1) { @@ -81810,10 +82592,10 @@ var require_retry_helper = __commonJS({ if (isRetryable && !isRetryable(err)) { throw err; } - core30.info(err.message); + core31.info(err.message); } const seconds = this.getSleepAmount(); - core30.info(`Waiting ${seconds} seconds before trying again`); + core31.info(`Waiting ${seconds} seconds before trying again`); yield this.sleep(seconds); attempt++; } @@ -81916,7 +82698,7 @@ var require_tool_cache = __commonJS({ exports2.findFromManifest = findFromManifest; exports2.isExplicitVersion = isExplicitVersion; exports2.evaluateVersions = evaluateVersions; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var io9 = __importStar2(require_io()); var crypto3 = __importStar2(require("crypto")); var fs31 = __importStar2(require("fs")); @@ -81945,8 +82727,8 @@ var require_tool_cache = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { dest = dest || path29.join(_getTempDirectory(), crypto3.randomUUID()); yield io9.mkdirP(path29.dirname(dest)); - core30.debug(`Downloading ${url2}`); - core30.debug(`Destination ${dest}`); + core31.debug(`Downloading ${url2}`); + core31.debug(`Destination ${dest}`); const maxAttempts = 3; const minSeconds = _getGlobal("TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS", 10); const maxSeconds = _getGlobal("TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS", 20); @@ -81972,7 +82754,7 @@ var require_tool_cache = __commonJS({ allowRetries: false }); if (auth2) { - core30.debug("set auth"); + core31.debug("set auth"); if (headers === void 0) { headers = {}; } @@ -81981,25 +82763,25 @@ var require_tool_cache = __commonJS({ const response = yield http.get(url2, headers); if (response.message.statusCode !== 200) { const err = new HTTPError2(response.message.statusCode); - core30.debug(`Failed to download from "${url2}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`); + core31.debug(`Failed to download from "${url2}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`); throw err; } - const pipeline = util3.promisify(stream2.pipeline); + const pipeline2 = util3.promisify(stream2.pipeline); const responseMessageFactory = _getGlobal("TEST_DOWNLOAD_TOOL_RESPONSE_MESSAGE_FACTORY", () => response.message); const readStream = responseMessageFactory(); let succeeded = false; try { - yield pipeline(readStream, fs31.createWriteStream(dest)); - core30.debug("download complete"); + yield pipeline2(readStream, fs31.createWriteStream(dest)); + core31.debug("download complete"); succeeded = true; return dest; } finally { if (!succeeded) { - core30.debug("download failed"); + core31.debug("download failed"); try { yield io9.rmRF(dest); } catch (err) { - core30.debug(`Failed to delete '${dest}'. ${err.message}`); + core31.debug(`Failed to delete '${dest}'. ${err.message}`); } } } @@ -82014,7 +82796,7 @@ var require_tool_cache = __commonJS({ process.chdir(dest); if (_7zPath) { try { - const logLevel = core30.isDebug() ? "-bb1" : "-bb0"; + const logLevel = core31.isDebug() ? "-bb1" : "-bb0"; const args = [ "x", // eXtract files with full paths @@ -82067,7 +82849,7 @@ var require_tool_cache = __commonJS({ throw new Error("parameter 'file' is required"); } dest = yield _createExtractFolder(dest); - core30.debug("Checking tar --version"); + core31.debug("Checking tar --version"); let versionOutput = ""; yield (0, exec_1.exec)("tar --version", [], { ignoreReturnCode: true, @@ -82077,7 +82859,7 @@ var require_tool_cache = __commonJS({ stderr: (data) => versionOutput += data.toString() } }); - core30.debug(versionOutput.trim()); + core31.debug(versionOutput.trim()); const isGnuTar = versionOutput.toUpperCase().includes("GNU TAR"); let args; if (flags instanceof Array) { @@ -82085,7 +82867,7 @@ var require_tool_cache = __commonJS({ } else { args = [flags]; } - if (core30.isDebug() && !flags.includes("v")) { + if (core31.isDebug() && !flags.includes("v")) { args.push("-v"); } let destArg = dest; @@ -82116,7 +82898,7 @@ var require_tool_cache = __commonJS({ args = [flags]; } args.push("-x", "-C", dest, "-f", file); - if (core30.isDebug()) { + if (core31.isDebug()) { args.push("-v"); } const xarPath = yield io9.which("xar", true); @@ -82159,7 +82941,7 @@ var require_tool_cache = __commonJS({ "-Command", pwshCommand ]; - core30.debug(`Using pwsh at path: ${pwshPath}`); + core31.debug(`Using pwsh at path: ${pwshPath}`); yield (0, exec_1.exec)(`"${pwshPath}"`, args); } else { const powershellCommand = [ @@ -82179,7 +82961,7 @@ var require_tool_cache = __commonJS({ powershellCommand ]; const powershellPath = yield io9.which("powershell", true); - core30.debug(`Using powershell at path: ${powershellPath}`); + core31.debug(`Using powershell at path: ${powershellPath}`); yield (0, exec_1.exec)(`"${powershellPath}"`, args); } }); @@ -82188,7 +82970,7 @@ var require_tool_cache = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { const unzipPath = yield io9.which("unzip", true); const args = [file]; - if (!core30.isDebug()) { + if (!core31.isDebug()) { args.unshift("-q"); } args.unshift("-o"); @@ -82199,8 +82981,8 @@ var require_tool_cache = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { version = semver11.clean(version) || version; arch2 = arch2 || os7.arch(); - core30.debug(`Caching tool ${tool} ${version} ${arch2}`); - core30.debug(`source dir: ${sourceDir}`); + core31.debug(`Caching tool ${tool} ${version} ${arch2}`); + core31.debug(`source dir: ${sourceDir}`); if (!fs31.statSync(sourceDir).isDirectory()) { throw new Error("sourceDir is not a directory"); } @@ -82217,14 +82999,14 @@ var require_tool_cache = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { version = semver11.clean(version) || version; arch2 = arch2 || os7.arch(); - core30.debug(`Caching tool ${tool} ${version} ${arch2}`); - core30.debug(`source file: ${sourceFile}`); + core31.debug(`Caching tool ${tool} ${version} ${arch2}`); + core31.debug(`source file: ${sourceFile}`); if (!fs31.statSync(sourceFile).isFile()) { throw new Error("sourceFile is not a file"); } const destFolder = yield _createToolPath(tool, version, arch2); const destPath = path29.join(destFolder, targetFile); - core30.debug(`destination file ${destPath}`); + core31.debug(`destination file ${destPath}`); yield io9.cp(sourceFile, destPath); _completeToolPath(tool, version, arch2); return destFolder; @@ -82247,12 +83029,12 @@ var require_tool_cache = __commonJS({ if (versionSpec) { versionSpec = semver11.clean(versionSpec) || ""; const cachePath = path29.join(_getCacheDirectory(), toolName, versionSpec, arch2); - core30.debug(`checking cache: ${cachePath}`); + core31.debug(`checking cache: ${cachePath}`); if (fs31.existsSync(cachePath) && fs31.existsSync(`${cachePath}.complete`)) { - core30.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch2}`); + core31.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch2}`); toolPath = cachePath; } else { - core30.debug("not found"); + core31.debug("not found"); } } return toolPath; @@ -82281,7 +83063,7 @@ var require_tool_cache = __commonJS({ const http = new httpm.HttpClient("tool-cache"); const headers = {}; if (auth2) { - core30.debug("set auth"); + core31.debug("set auth"); headers.authorization = auth2; } const response = yield http.getJson(treeUrl, headers); @@ -82302,7 +83084,7 @@ var require_tool_cache = __commonJS({ try { releases = JSON.parse(versionsRaw); } catch (_a2) { - core30.debug("Invalid json"); + core31.debug("Invalid json"); } } return releases; @@ -82326,7 +83108,7 @@ var require_tool_cache = __commonJS({ function _createToolPath(tool, version, arch2) { return __awaiter2(this, void 0, void 0, function* () { const folderPath = path29.join(_getCacheDirectory(), tool, semver11.clean(version) || version, arch2 || ""); - core30.debug(`destination ${folderPath}`); + core31.debug(`destination ${folderPath}`); const markerPath = `${folderPath}.complete`; yield io9.rmRF(folderPath); yield io9.rmRF(markerPath); @@ -82338,18 +83120,18 @@ var require_tool_cache = __commonJS({ const folderPath = path29.join(_getCacheDirectory(), tool, semver11.clean(version) || version, arch2 || ""); const markerPath = `${folderPath}.complete`; fs31.writeFileSync(markerPath, ""); - core30.debug("finished caching tool"); + core31.debug("finished caching tool"); } function isExplicitVersion(versionSpec) { const c = semver11.clean(versionSpec) || ""; - core30.debug(`isExplicit: ${c}`); + core31.debug(`isExplicit: ${c}`); const valid4 = semver11.valid(c) != null; - core30.debug(`explicit? ${valid4}`); + core31.debug(`explicit? ${valid4}`); return valid4; } function evaluateVersions(versions, versionSpec) { let version = ""; - core30.debug(`evaluating ${versions.length} versions`); + core31.debug(`evaluating ${versions.length} versions`); versions = versions.sort((a, b) => { if (semver11.gt(a, b)) { return 1; @@ -82365,9 +83147,9 @@ var require_tool_cache = __commonJS({ } } if (version) { - core30.debug(`matched: ${version}`); + core31.debug(`matched: ${version}`); } else { - core30.debug("match not found"); + core31.debug("match not found"); } return version; } @@ -87960,14 +88742,14 @@ var require_retention = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getExpiration = void 0; var generated_1 = require_generated(); - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); function getExpiration(retentionDays) { if (!retentionDays) { return void 0; } const maxRetentionDays = getRetentionDays(); if (maxRetentionDays && maxRetentionDays < retentionDays) { - core30.warning(`Retention days cannot be greater than the maximum allowed retention set within the repository. Using ${maxRetentionDays} instead.`); + core31.warning(`Retention days cannot be greater than the maximum allowed retention set within the repository. Using ${maxRetentionDays} instead.`); retentionDays = maxRetentionDays; } const expirationDate = /* @__PURE__ */ new Date(); @@ -88305,7 +89087,7 @@ var require_util11 = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.maskSecretUrls = exports2.maskSigUrl = exports2.getBackendIdsFromToken = void 0; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var config_1 = require_config2(); var jwt_decode_1 = __importDefault2(require_jwt_decode_cjs()); var core_1 = require_core(); @@ -88332,8 +89114,8 @@ var require_util11 = __commonJS({ workflowRunBackendId: scopeParts[1], workflowJobRunBackendId: scopeParts[2] }; - core30.debug(`Workflow Run Backend ID: ${ids.workflowRunBackendId}`); - core30.debug(`Workflow Job Run Backend ID: ${ids.workflowJobRunBackendId}`); + core31.debug(`Workflow Run Backend ID: ${ids.workflowRunBackendId}`); + core31.debug(`Workflow Job Run Backend ID: ${ids.workflowJobRunBackendId}`); return ids; } throw InvalidJwtError; @@ -88693,7 +89475,7 @@ var require_blob_upload = __commonJS({ exports2.uploadZipToBlobStorage = void 0; var storage_blob_1 = require_commonjs15(); var config_1 = require_config2(); - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var crypto3 = __importStar2(require("crypto")); var stream2 = __importStar2(require("stream")); var errors_1 = require_errors3(); @@ -88719,9 +89501,9 @@ var require_blob_upload = __commonJS({ const bufferSize = (0, config_1.getUploadChunkSize)(); const blobClient = new storage_blob_1.BlobClient(authenticatedUploadURL); const blockBlobClient = blobClient.getBlockBlobClient(); - core30.debug(`Uploading artifact zip to blob storage with maxConcurrency: ${maxConcurrency}, bufferSize: ${bufferSize}`); + core31.debug(`Uploading artifact zip to blob storage with maxConcurrency: ${maxConcurrency}, bufferSize: ${bufferSize}`); const uploadCallback = (progress) => { - core30.info(`Uploaded bytes ${progress.loadedBytes}`); + core31.info(`Uploaded bytes ${progress.loadedBytes}`); uploadByteCount = progress.loadedBytes; lastProgressTime = Date.now(); }; @@ -88735,7 +89517,7 @@ var require_blob_upload = __commonJS({ const hashStream = crypto3.createHash("sha256"); zipUploadStream.pipe(uploadStream); zipUploadStream.pipe(hashStream).setEncoding("hex"); - core30.info("Beginning upload of artifact content to blob storage"); + core31.info("Beginning upload of artifact content to blob storage"); try { yield Promise.race([ blockBlobClient.uploadStream(uploadStream, bufferSize, maxConcurrency, options), @@ -88749,12 +89531,12 @@ var require_blob_upload = __commonJS({ } finally { abortController.abort(); } - core30.info("Finished uploading artifact content to blob storage!"); + core31.info("Finished uploading artifact content to blob storage!"); hashStream.end(); sha256Hash = hashStream.read(); - core30.info(`SHA256 digest of uploaded artifact zip is ${sha256Hash}`); + core31.info(`SHA256 digest of uploaded artifact zip is ${sha256Hash}`); if (uploadByteCount === 0) { - core30.warning(`No data was uploaded to blob storage. Reported upload byte count is 0.`); + core31.warning(`No data was uploaded to blob storage. Reported upload byte count is 0.`); } return { uploadSize: uploadByteCount, @@ -88784,6 +89566,8 @@ var require_brace_expansion2 = __commonJS({ var escClose2 = "\0CLOSE" + Math.random() + "\0"; var escComma2 = "\0COMMA" + Math.random() + "\0"; var escPeriod2 = "\0PERIOD" + Math.random() + "\0"; + var EXPANSION_MAX2 = 1e5; + var EXPANSION_MAX_LENGTH2 = 4e6; function numeric2(str) { return parseInt(str, 10) == str ? parseInt(str, 10) : str.charCodeAt(0); } @@ -88817,11 +89601,12 @@ var require_brace_expansion2 = __commonJS({ if (!str) return []; options = options || {}; - var max = options.max == null ? Infinity : options.max; + var max = options.max == null ? EXPANSION_MAX2 : options.max; + var maxLength = options.maxLength == null ? EXPANSION_MAX_LENGTH2 : options.maxLength; if (str.substr(0, 2) === "{}") { str = "\\{\\}" + str.substr(2); } - return expand3(escapeBraces2(str), max, true).map(unescapeBraces2); + return expand3(escapeBraces2(str), max, maxLength, true).map(unescapeBraces2); } function embrace2(str) { return "{" + str + "}"; @@ -88835,18 +89620,90 @@ var require_brace_expansion2 = __commonJS({ function gte7(i, y) { return i >= y; } - function expand3(str, max, isTop) { - var expansions = []; - var m = balanced2("{", "}", str); - if (!m) return [str]; - var pre = m.pre; - var post = m.post.length ? expand3(m.post, max, false) : [""]; - if (/\$$/.test(m.pre)) { - for (var k = 0; k < post.length && k < max; k++) { - var expansion = pre + "{" + m.body + "}" + post[k]; - expansions.push(expansion); + function combine2(acc, pre, values, max, maxLength, dropEmpties) { + var out = []; + var length = 0; + for (var a = 0; a < acc.length; a++) { + for (var v = 0; v < values.length; v++) { + if (out.length >= max) return out; + var expansion = acc[a] + pre + values[v]; + if (dropEmpties && !expansion) continue; + if (length + expansion.length > maxLength) return out; + out.push(expansion); + length += expansion.length; + } + } + return out; + } + function expandSequence2(body, isAlphaSequence, max, maxLength) { + var n = body.split(/\.\./); + var N = []; + if (n[0] === void 0 || n[1] === void 0) { + return N; + } + var x = numeric2(n[0]); + var y = numeric2(n[1]); + var width = Math.max(n[0].length, n[1].length); + var incr = n.length === 3 && n[2] !== void 0 ? Math.max(Math.abs(numeric2(n[2])), 1) : 1; + var test = lte2; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte7; + } + var pad = n.some(isPadded2); + var length = 0; + for (var i = x; test(i, y) && N.length < max; i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === "\\") { + c = ""; + } + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join("0"); + if (i < 0) { + c = "-" + z + c.slice(1); + } else { + c = z + c; + } + } + } + } + if (length + c.length > maxLength) break; + N.push(c); + length += c.length; + } + return N; + } + function expand3(str, max, maxLength, isTop) { + var acc = [""]; + var dropEmpties = false; + var firstGroup = true; + for (; ; ) { + const m = balanced2("{", "}", str); + if (!m) { + return combine2(acc, str, [""], max, maxLength, dropEmpties); + } + const pre = m.pre; + if (/\$$/.test(pre)) { + acc = combine2( + acc, + pre + "{" + m.body + "}", + [""], + max, + maxLength, + dropEmpties && !m.post.length + ); + firstGroup = false; + if (!m.post.length) break; + str = m.post; + continue; } - } else { var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); var isSequence = isNumericSequence || isAlphaSequence; @@ -88854,74 +89711,69 @@ var require_brace_expansion2 = __commonJS({ if (!isSequence && !isOptions) { if (m.post.match(/,(?!,).*\}/)) { str = m.pre + "{" + m.body + escClose2 + m.post; - return expand3(str, max, true); + isTop = true; + continue; } - return [str]; + return combine2( + acc, + pre + "{" + m.body + "}" + m.post, + [""], + max, + maxLength, + dropEmpties + ); + } + if (firstGroup) { + dropEmpties = isTop && !isSequence; + firstGroup = false; } - var n; + var values; if (isSequence) { - n = m.body.split(/\.\./); + values = expandSequence2(m.body, isAlphaSequence, max, maxLength); } else { - n = parseCommaParts2(m.body); - if (n.length === 1) { - n = expand3(n[0], max, false).map(embrace2); + var n = parseCommaParts2(m.body); + if (n.length === 1 && n[0] !== void 0) { + n = expand3(n[0], max, maxLength, false).map(embrace2); if (n.length === 1) { - return post.map(function(p) { - return m.pre + n[0] + p; - }); + acc = combine2( + acc, + pre + n[0], + [""], + max, + maxLength, + dropEmpties && !m.post.length + ); + if (!m.post.length) break; + str = m.post; + continue; } } - } - var N; - if (isSequence) { - var x = numeric2(n[0]); - var y = numeric2(n[1]); - var width = Math.max(n[0].length, n[1].length); - var incr = n.length == 3 ? Math.max(Math.abs(numeric2(n[2])), 1) : 1; - var test = lte2; - var reverse = y < x; - if (reverse) { - incr *= -1; - test = gte7; - } - var pad = n.some(isPadded2); - N = []; - for (var i = x; test(i, y) && N.length < max; i += incr) { - var c; - if (isAlphaSequence) { - c = String.fromCharCode(i); - if (c === "\\") - c = ""; - } else { - c = String(i); - if (pad) { - var need = width - c.length; - if (need > 0) { - var z = new Array(need + 1).join("0"); - if (i < 0) - c = "-" + z + c.slice(1); - else - c = z + c; - } - } + var dropsEmpties = dropEmpties && !m.post.length && !pre; + for (var d = 0; dropsEmpties && d < acc.length; d++) { + if (acc[d]) { + dropsEmpties = false; } - N.push(c); - } - } else { - N = []; - for (var j = 0; j < n.length; j++) { - N.push.apply(N, expand3(n[j], max, false)); } - } - for (var j = 0; j < N.length; j++) { - for (var k = 0; k < post.length && expansions.length < max; k++) { - var expansion = pre + N[j] + post[k]; - if (!isTop || isSequence || expansion) - expansions.push(expansion); + values = []; + var valuesLength = 0; + outer: for (var j = 0; j < n.length; j++) { + var expanded = expand3(n[j], max, maxLength, false); + for (var k = 0; k < expanded.length; k++) { + var v = expanded[k]; + if (dropsEmpties && !v) continue; + if (values.length >= max || valuesLength + v.length > maxLength) { + break outer; + } + values.push(v); + valuesLength += v.length; + } } } + acc = combine2(acc, pre, values, max, maxLength, dropEmpties && !m.post.length); + if (!m.post.length) break; + str = m.post; } - return expansions; + return acc; } } }); @@ -91167,8 +92019,8 @@ var require_async = __commonJS({ } } var race$1 = awaitify(race, 2); - function reduceRight(array, memo, iteratee, callback) { - var reversed = [...array].reverse(); + function reduceRight(array2, memo, iteratee, callback) { + var reversed = [...array2].reverse(); return reduce$1(reversed, memo, iteratee, callback); } function reflect(fn) { @@ -92580,10 +93432,10 @@ var require_util12 = __commonJS({ return objectToString(arg) === "[object Array]"; } exports2.isArray = isArray2; - function isBoolean(arg) { + function isBoolean2(arg) { return typeof arg === "boolean"; } - exports2.isBoolean = isBoolean; + exports2.isBoolean = isBoolean2; function isNull(arg) { return arg === null; } @@ -92592,10 +93444,10 @@ var require_util12 = __commonJS({ return arg == null; } exports2.isNullOrUndefined = isNullOrUndefined; - function isNumber(arg) { + function isNumber2(arg) { return typeof arg === "number"; } - exports2.isNumber = isNumber; + exports2.isNumber = isNumber2; function isString3(arg) { return typeof arg === "string"; } @@ -92939,15 +93791,15 @@ var require_stream_writable = __commonJS({ if (typeof Symbol === "function" && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === "function") { realHasInstance = Function.prototype[Symbol.hasInstance]; Object.defineProperty(Writable, Symbol.hasInstance, { - value: function(object) { - if (realHasInstance.call(this, object)) return true; + value: function(object2) { + if (realHasInstance.call(this, object2)) return true; if (this !== Writable) return false; - return object && object._writableState instanceof WritableState; + return object2 && object2._writableState instanceof WritableState; } }); } else { - realHasInstance = function(object) { - return object instanceof this; + realHasInstance = function(object2) { + return object2 instanceof this; }; } function Writable(options) { @@ -94542,16 +95394,16 @@ var require_overRest = __commonJS({ function overRest(func, start, transform) { start = nativeMax(start === void 0 ? func.length - 1 : start, 0); return function() { - var args = arguments, index2 = -1, length = nativeMax(args.length - start, 0), array = Array(length); + var args = arguments, index2 = -1, length = nativeMax(args.length - start, 0), array2 = Array(length); while (++index2 < length) { - array[index2] = args[start + index2]; + array2[index2] = args[start + index2]; } index2 = -1; var otherArgs = Array(start + 1); while (++index2 < start) { otherArgs[index2] = args[index2]; } - otherArgs[start] = transform(array); + otherArgs[start] = transform(array2); return apply(func, this, otherArgs); }; } @@ -94765,8 +95617,8 @@ var require_baseIsNative = __commonJS({ // node_modules/lodash/_getValue.js var require_getValue = __commonJS({ "node_modules/lodash/_getValue.js"(exports2, module2) { - function getValue(object, key) { - return object == null ? void 0 : object[key]; + function getValue(object2, key) { + return object2 == null ? void 0 : object2[key]; } module2.exports = getValue; } @@ -94777,8 +95629,8 @@ var require_getNative = __commonJS({ "node_modules/lodash/_getNative.js"(exports2, module2) { var baseIsNative = require_baseIsNative(); var getValue = require_getValue(); - function getNative(object, key) { - var value = getValue(object, key); + function getNative(object2, key) { + var value = getValue(object2, key); return baseIsNative(value) ? value : void 0; } module2.exports = getNative; @@ -94921,13 +95773,13 @@ var require_isIterateeCall = __commonJS({ var isArrayLike = require_isArrayLike(); var isIndex = require_isIndex(); var isObject2 = require_isObject(); - function isIterateeCall(value, index2, object) { - if (!isObject2(object)) { + function isIterateeCall(value, index2, object2) { + if (!isObject2(object2)) { return false; } var type = typeof index2; - if (type == "number" ? isArrayLike(object) && isIndex(index2, object.length) : type == "string" && index2 in object) { - return eq(object[index2], value); + if (type == "number" ? isArrayLike(object2) && isIndex(index2, object2.length) : type == "string" && index2 in object2) { + return eq(object2[index2], value); } return false; } @@ -95151,10 +96003,10 @@ var require_isPrototype = __commonJS({ // node_modules/lodash/_nativeKeysIn.js var require_nativeKeysIn = __commonJS({ "node_modules/lodash/_nativeKeysIn.js"(exports2, module2) { - function nativeKeysIn(object) { + function nativeKeysIn(object2) { var result = []; - if (object != null) { - for (var key in Object(object)) { + if (object2 != null) { + for (var key in Object(object2)) { result.push(key); } } @@ -95172,13 +96024,13 @@ var require_baseKeysIn = __commonJS({ var nativeKeysIn = require_nativeKeysIn(); var objectProto = Object.prototype; var hasOwnProperty = objectProto.hasOwnProperty; - function baseKeysIn(object) { - if (!isObject2(object)) { - return nativeKeysIn(object); + function baseKeysIn(object2) { + if (!isObject2(object2)) { + return nativeKeysIn(object2); } - var isProto = isPrototype(object), result = []; - for (var key in object) { - if (!(key == "constructor" && (isProto || !hasOwnProperty.call(object, key)))) { + var isProto = isPrototype(object2), result = []; + for (var key in object2) { + if (!(key == "constructor" && (isProto || !hasOwnProperty.call(object2, key)))) { result.push(key); } } @@ -95194,8 +96046,8 @@ var require_keysIn = __commonJS({ var arrayLikeKeys = require_arrayLikeKeys(); var baseKeysIn = require_baseKeysIn(); var isArrayLike = require_isArrayLike(); - function keysIn(object) { - return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); + function keysIn(object2) { + return isArrayLike(object2) ? arrayLikeKeys(object2, true) : baseKeysIn(object2); } module2.exports = keysIn; } @@ -95210,8 +96062,8 @@ var require_defaults = __commonJS({ var keysIn = require_keysIn(); var objectProto = Object.prototype; var hasOwnProperty = objectProto.hasOwnProperty; - var defaults2 = baseRest(function(object, sources) { - object = Object(object); + var defaults3 = baseRest(function(object2, sources) { + object2 = Object(object2); var index2 = -1; var length = sources.length; var guard = length > 2 ? sources[2] : void 0; @@ -95225,15 +96077,15 @@ var require_defaults = __commonJS({ var propsLength = props.length; while (++propsIndex < propsLength) { var key = props[propsIndex]; - var value = object[key]; - if (value === void 0 || eq(value, objectProto[key]) && !hasOwnProperty.call(object, key)) { - object[key] = source[key]; + var value = object2[key]; + if (value === void 0 || eq(value, objectProto[key]) && !hasOwnProperty.call(object2, key)) { + object2[key] = source[key]; } } } - return object; + return object2; }); - module2.exports = defaults2; + module2.exports = defaults3; } }); @@ -96653,7 +97505,7 @@ var require_validators = __commonJS({ throw new ERR_INVALID_ARG_TYPE(name, "a dictionary", value); } }); - var validateArray = hideStackFrames((value, name, minLength = 0) => { + var validateArray2 = hideStackFrames((value, name, minLength = 0) => { if (!ArrayIsArray(value)) { throw new ERR_INVALID_ARG_TYPE(name, "Array", value); } @@ -96663,19 +97515,19 @@ var require_validators = __commonJS({ } }); function validateStringArray(value, name) { - validateArray(value, name); + validateArray2(value, name); for (let i = 0; i < value.length; i++) { validateString(value[i], `${name}[${i}]`); } } function validateBooleanArray(value, name) { - validateArray(value, name); + validateArray2(value, name); for (let i = 0; i < value.length; i++) { validateBoolean(value[i], `${name}[${i}]`); } } function validateAbortSignalArray(value, name) { - validateArray(value, name); + validateArray2(value, name); for (let i = 0; i < value.length; i++) { const signal = value[i]; const indexedName = `${name}[${i}]`; @@ -96771,7 +97623,7 @@ var require_validators = __commonJS({ isInt32, isUint32, parseFileMode, - validateArray, + validateArray: validateArray2, validateStringArray, validateBooleanArray, validateAbortSignalArray, @@ -99328,10 +100180,10 @@ var require_writable = __commonJS({ } ObjectDefineProperty(Writable, SymbolHasInstance, { __proto__: null, - value: function(object) { - if (FunctionPrototypeSymbolHasInstance(this, object)) return true; + value: function(object2) { + if (FunctionPrototypeSymbolHasInstance(this, object2)) return true; if (this !== Writable) return false; - return object && object._writableState instanceof WritableState; + return object2 && object2._writableState instanceof WritableState; } }); Writable.prototype.pipe = function() { @@ -100577,7 +101429,7 @@ var require_pipeline4 = __commonJS({ } } } - function pipeline(...streams) { + function pipeline2(...streams) { return pipelineImpl(streams, once(popCallback(streams))); } function pipelineImpl(streams, callback, opts) { @@ -100843,7 +101695,7 @@ var require_pipeline4 = __commonJS({ } module2.exports = { pipelineImpl, - pipeline + pipeline: pipeline2 }; } }); @@ -100852,7 +101704,7 @@ var require_pipeline4 = __commonJS({ var require_compose = __commonJS({ "node_modules/readable-stream/lib/internal/streams/compose.js"(exports2, module2) { "use strict"; - var { pipeline } = require_pipeline4(); + var { pipeline: pipeline2 } = require_pipeline4(); var Duplex = require_duplex(); var { destroyer } = require_destroy2(); var { @@ -100912,7 +101764,7 @@ var require_compose = __commonJS({ } } const head = streams[0]; - const tail = pipeline(streams, onfinished); + const tail = pipeline2(streams, onfinished); const writable = !!(isWritable(head) || isWritableStream(head) || isTransformStream(head)); const readable = !!(isReadable(tail) || isReadableStream(tail) || isTransformStream(tail)); d = new Duplex({ @@ -101365,24 +102217,24 @@ var require_operators = __commonJS({ } }.call(this); } - function toIntegerOrInfinity(number) { - number = Number2(number); - if (NumberIsNaN(number)) { + function toIntegerOrInfinity(number2) { + number2 = Number2(number2); + if (NumberIsNaN(number2)) { return 0; } - if (number < 0) { - throw new ERR_OUT_OF_RANGE("number", ">= 0", number); + if (number2 < 0) { + throw new ERR_OUT_OF_RANGE("number", ">= 0", number2); } - return number; + return number2; } - function drop(number, options = void 0) { + function drop(number2, options = void 0) { if (options != null) { validateObject(options, "options"); } if ((options === null || options === void 0 ? void 0 : options.signal) != null) { validateAbortSignal(options.signal, "options.signal"); } - number = toIntegerOrInfinity(number); + number2 = toIntegerOrInfinity(number2); return async function* drop2() { var _options$signal5; if (options !== null && options !== void 0 && (_options$signal5 = options.signal) !== null && _options$signal5 !== void 0 && _options$signal5.aborted) { @@ -101393,20 +102245,20 @@ var require_operators = __commonJS({ if (options !== null && options !== void 0 && (_options$signal6 = options.signal) !== null && _options$signal6 !== void 0 && _options$signal6.aborted) { throw new AbortError(); } - if (number-- <= 0) { + if (number2-- <= 0) { yield val; } } }.call(this); } - function take(number, options = void 0) { + function take(number2, options = void 0) { if (options != null) { validateObject(options, "options"); } if ((options === null || options === void 0 ? void 0 : options.signal) != null) { validateAbortSignal(options.signal, "options.signal"); } - number = toIntegerOrInfinity(number); + number2 = toIntegerOrInfinity(number2); return async function* take2() { var _options$signal7; if (options !== null && options !== void 0 && (_options$signal7 = options.signal) !== null && _options$signal7 !== void 0 && _options$signal7.aborted) { @@ -101417,10 +102269,10 @@ var require_operators = __commonJS({ if (options !== null && options !== void 0 && (_options$signal8 = options.signal) !== null && _options$signal8 !== void 0 && _options$signal8.aborted) { throw new AbortError(); } - if (number-- > 0) { + if (number2-- > 0) { yield val; } - if (number <= 0) { + if (number2 <= 0) { return; } } @@ -101455,7 +102307,7 @@ var require_promises = __commonJS({ var { pipelineImpl: pl } = require_pipeline4(); var { finished } = require_end_of_stream(); require_stream2(); - function pipeline(...streams) { + function pipeline2(...streams) { return new Promise2((resolve14, reject) => { let signal; let end; @@ -101483,7 +102335,7 @@ var require_promises = __commonJS({ } module2.exports = { finished, - pipeline + pipeline: pipeline2 }; } }); @@ -101503,7 +102355,7 @@ var require_stream2 = __commonJS({ } = require_errors4(); var compose = require_compose(); var { setDefaultHighWaterMark, getDefaultHighWaterMark } = require_state3(); - var { pipeline } = require_pipeline4(); + var { pipeline: pipeline2 } = require_pipeline4(); var { destroyer } = require_destroy2(); var eos = require_end_of_stream(); var promises6 = require_promises(); @@ -101567,7 +102419,7 @@ var require_stream2 = __commonJS({ Stream.Duplex = require_duplex(); Stream.Transform = require_transform(); Stream.PassThrough = require_passthrough2(); - Stream.pipeline = pipeline; + Stream.pipeline = pipeline2; var { addAbortSignal } = require_add_abort_signal(); Stream.addAbortSignal = addAbortSignal; Stream.finished = eos; @@ -101583,7 +102435,7 @@ var require_stream2 = __commonJS({ return promises6; } }); - ObjectDefineProperty(pipeline, customPromisify, { + ObjectDefineProperty(pipeline2, customPromisify, { __proto__: null, enumerable: true, get() { @@ -101674,12 +102526,12 @@ var require_ours = __commonJS({ // node_modules/lodash/_arrayPush.js var require_arrayPush = __commonJS({ "node_modules/lodash/_arrayPush.js"(exports2, module2) { - function arrayPush(array, values) { - var index2 = -1, length = values.length, offset = array.length; + function arrayPush(array2, values) { + var index2 = -1, length = values.length, offset = array2.length; while (++index2 < length) { - array[offset + index2] = values[index2]; + array2[offset + index2] = values[index2]; } - return array; + return array2; } module2.exports = arrayPush; } @@ -101704,12 +102556,12 @@ var require_baseFlatten = __commonJS({ "node_modules/lodash/_baseFlatten.js"(exports2, module2) { var arrayPush = require_arrayPush(); var isFlattenable = require_isFlattenable(); - function baseFlatten(array, depth, predicate, isStrict, result) { - var index2 = -1, length = array.length; + function baseFlatten(array2, depth, predicate, isStrict, result) { + var index2 = -1, length = array2.length; predicate || (predicate = isFlattenable); result || (result = []); while (++index2 < length) { - var value = array[index2]; + var value = array2[index2]; if (depth > 0 && predicate(value)) { if (depth > 1) { baseFlatten(value, depth - 1, predicate, isStrict, result); @@ -101730,9 +102582,9 @@ var require_baseFlatten = __commonJS({ var require_flatten = __commonJS({ "node_modules/lodash/flatten.js"(exports2, module2) { var baseFlatten = require_baseFlatten(); - function flatten(array) { - var length = array == null ? 0 : array.length; - return length ? baseFlatten(array, 1) : []; + function flatten(array2) { + var length = array2 == null ? 0 : array2.length; + return length ? baseFlatten(array2, 1) : []; } module2.exports = flatten; } @@ -101859,10 +102711,10 @@ var require_listCacheClear = __commonJS({ var require_assocIndexOf = __commonJS({ "node_modules/lodash/_assocIndexOf.js"(exports2, module2) { var eq = require_eq2(); - function assocIndexOf(array, key) { - var length = array.length; + function assocIndexOf(array2, key) { + var length = array2.length; while (length--) { - if (eq(array[length][0], key)) { + if (eq(array2[length][0], key)) { return length; } } @@ -102131,10 +102983,10 @@ var require_SetCache = __commonJS({ // node_modules/lodash/_baseFindIndex.js var require_baseFindIndex = __commonJS({ "node_modules/lodash/_baseFindIndex.js"(exports2, module2) { - function baseFindIndex(array, predicate, fromIndex, fromRight) { - var length = array.length, index2 = fromIndex + (fromRight ? 1 : -1); + function baseFindIndex(array2, predicate, fromIndex, fromRight) { + var length = array2.length, index2 = fromIndex + (fromRight ? 1 : -1); while (fromRight ? index2-- : ++index2 < length) { - if (predicate(array[index2], index2, array)) { + if (predicate(array2[index2], index2, array2)) { return index2; } } @@ -102157,10 +103009,10 @@ var require_baseIsNaN = __commonJS({ // node_modules/lodash/_strictIndexOf.js var require_strictIndexOf = __commonJS({ "node_modules/lodash/_strictIndexOf.js"(exports2, module2) { - function strictIndexOf(array, value, fromIndex) { - var index2 = fromIndex - 1, length = array.length; + function strictIndexOf(array2, value, fromIndex) { + var index2 = fromIndex - 1, length = array2.length; while (++index2 < length) { - if (array[index2] === value) { + if (array2[index2] === value) { return index2; } } @@ -102176,8 +103028,8 @@ var require_baseIndexOf = __commonJS({ var baseFindIndex = require_baseFindIndex(); var baseIsNaN = require_baseIsNaN(); var strictIndexOf = require_strictIndexOf(); - function baseIndexOf(array, value, fromIndex) { - return value === value ? strictIndexOf(array, value, fromIndex) : baseFindIndex(array, baseIsNaN, fromIndex); + function baseIndexOf(array2, value, fromIndex) { + return value === value ? strictIndexOf(array2, value, fromIndex) : baseFindIndex(array2, baseIsNaN, fromIndex); } module2.exports = baseIndexOf; } @@ -102187,9 +103039,9 @@ var require_baseIndexOf = __commonJS({ var require_arrayIncludes = __commonJS({ "node_modules/lodash/_arrayIncludes.js"(exports2, module2) { var baseIndexOf = require_baseIndexOf(); - function arrayIncludes(array, value) { - var length = array == null ? 0 : array.length; - return !!length && baseIndexOf(array, value, 0) > -1; + function arrayIncludes(array2, value) { + var length = array2 == null ? 0 : array2.length; + return !!length && baseIndexOf(array2, value, 0) > -1; } module2.exports = arrayIncludes; } @@ -102198,10 +103050,10 @@ var require_arrayIncludes = __commonJS({ // node_modules/lodash/_arrayIncludesWith.js var require_arrayIncludesWith = __commonJS({ "node_modules/lodash/_arrayIncludesWith.js"(exports2, module2) { - function arrayIncludesWith(array, value, comparator) { - var index2 = -1, length = array == null ? 0 : array.length; + function arrayIncludesWith(array2, value, comparator) { + var index2 = -1, length = array2 == null ? 0 : array2.length; while (++index2 < length) { - if (comparator(value, array[index2])) { + if (comparator(value, array2[index2])) { return true; } } @@ -102214,10 +103066,10 @@ var require_arrayIncludesWith = __commonJS({ // node_modules/lodash/_arrayMap.js var require_arrayMap = __commonJS({ "node_modules/lodash/_arrayMap.js"(exports2, module2) { - function arrayMap(array, iteratee) { - var index2 = -1, length = array == null ? 0 : array.length, result = Array(length); + function arrayMap(array2, iteratee) { + var index2 = -1, length = array2 == null ? 0 : array2.length, result = Array(length); while (++index2 < length) { - result[index2] = iteratee(array[index2], index2, array); + result[index2] = iteratee(array2[index2], index2, array2); } return result; } @@ -102245,8 +103097,8 @@ var require_baseDifference = __commonJS({ var baseUnary = require_baseUnary(); var cacheHas = require_cacheHas(); var LARGE_ARRAY_SIZE = 200; - function baseDifference(array, values, iteratee, comparator) { - var index2 = -1, includes = arrayIncludes, isCommon = true, length = array.length, result = [], valuesLength = values.length; + function baseDifference(array2, values, iteratee, comparator) { + var index2 = -1, includes = arrayIncludes, isCommon = true, length = array2.length, result = [], valuesLength = values.length; if (!length) { return result; } @@ -102263,7 +103115,7 @@ var require_baseDifference = __commonJS({ } outer: while (++index2 < length) { - var value = array[index2], computed = iteratee == null ? value : iteratee(value); + var value = array2[index2], computed = iteratee == null ? value : iteratee(value); value = comparator || value !== 0 ? value : 0; if (isCommon && computed === computed) { var valuesIndex = valuesLength; @@ -102302,8 +103154,8 @@ var require_difference = __commonJS({ var baseFlatten = require_baseFlatten(); var baseRest = require_baseRest(); var isArrayLikeObject = require_isArrayLikeObject(); - var difference = baseRest(function(array, values) { - return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) : []; + var difference = baseRest(function(array2, values) { + return isArrayLikeObject(array2) ? baseDifference(array2, baseFlatten(values, 1, isArrayLikeObject, true)) : []; }); module2.exports = difference; } @@ -102366,13 +103218,13 @@ var require_baseUniq = __commonJS({ var createSet = require_createSet(); var setToArray = require_setToArray(); var LARGE_ARRAY_SIZE = 200; - function baseUniq(array, iteratee, comparator) { - var index2 = -1, includes = arrayIncludes, length = array.length, isCommon = true, result = [], seen = result; + function baseUniq(array2, iteratee, comparator) { + var index2 = -1, includes = arrayIncludes, length = array2.length, isCommon = true, result = [], seen = result; if (comparator) { isCommon = false; includes = arrayIncludesWith; } else if (length >= LARGE_ARRAY_SIZE) { - var set = iteratee ? null : createSet(array); + var set = iteratee ? null : createSet(array2); if (set) { return setToArray(set); } @@ -102384,7 +103236,7 @@ var require_baseUniq = __commonJS({ } outer: while (++index2 < length) { - var value = array[index2], computed = iteratee ? iteratee(value) : value; + var value = array2[index2], computed = iteratee ? iteratee(value) : value; value = comparator || value !== 0 ? value : 0; if (isCommon && computed === computed) { var seenIndex = seen.length; @@ -105656,7 +106508,7 @@ var require_archiver_utils = __commonJS({ var isStream2 = require_is_stream(); var lazystream = require_lazystream(); var normalizePath4 = require_normalize_path(); - var defaults2 = require_defaults(); + var defaults3 = require_defaults(); var Stream = require("stream").Stream; var PassThrough3 = require_ours().PassThrough; var utils = module2.exports = {}; @@ -105690,10 +106542,10 @@ var require_archiver_utils = __commonJS({ } return dateish; }; - utils.defaults = function(object, source, guard) { + utils.defaults = function(object2, source, guard) { var args = arguments; args[0] = args[0] || {}; - return defaults2(...args); + return defaults3(...args); }; utils.isStream = function(source) { return isStream2(source); @@ -108806,13 +109658,13 @@ var require_streamx = __commonJS({ } function pipelinePromise(...streams) { return new Promise((resolve14, reject) => { - return pipeline(...streams, (err) => { + return pipeline2(...streams, (err) => { if (err) return reject(err); resolve14(); }); }); } - function pipeline(stream2, ...streams) { + function pipeline2(stream2, ...streams) { const all = Array.isArray(stream2) ? [...stream2, ...streams] : [stream2, ...streams]; const done = all.length && typeof all[all.length - 1] === "function" ? all.pop() : null; if (all.length < 2) throw new Error("Pipeline requires at least 2 streams"); @@ -108897,7 +109749,7 @@ var require_streamx = __commonJS({ return s._writev !== Writable.prototype._writev && s._writev !== Duplex.prototype._writev; } module2.exports = { - pipeline, + pipeline: pipeline2, pipelinePromise, isStream: isStream2, isStreamx, @@ -109851,7 +110703,7 @@ var require_tar2 = __commonJS({ }); // node_modules/buffer-crc32/dist/index.cjs -var require_dist5 = __commonJS({ +var require_dist6 = __commonJS({ "node_modules/buffer-crc32/dist/index.cjs"(exports2, module2) { "use strict"; function getDefaultExportFromCjs(x) { @@ -110163,7 +111015,7 @@ var require_json = __commonJS({ "node_modules/@actions/artifact/node_modules/archiver/lib/plugins/json.js"(exports2, module2) { var inherits = require("util").inherits; var Transform5 = require_ours().Transform; - var crc325 = require_dist5(); + var crc325 = require_dist6(); var util3 = require_archiver_utils(); var Json2 = function(options) { if (!(this instanceof Json2)) { @@ -110318,7 +111170,7 @@ var require_zip2 = __commonJS({ var stream2 = __importStar2(require("stream")); var promises_1 = require("fs/promises"); var archiver = __importStar2(require_archiver()); - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var config_1 = require_config2(); exports2.DEFAULT_COMPRESSION_LEVEL = 6; var ZipUploadStream = class extends stream2.Transform { @@ -110335,7 +111187,7 @@ var require_zip2 = __commonJS({ exports2.ZipUploadStream = ZipUploadStream; function createZipUploadStream(uploadSpecification_1) { return __awaiter2(this, arguments, void 0, function* (uploadSpecification, compressionLevel = exports2.DEFAULT_COMPRESSION_LEVEL) { - core30.debug(`Creating Artifact archive with compressionLevel: ${compressionLevel}`); + core31.debug(`Creating Artifact archive with compressionLevel: ${compressionLevel}`); const zip = archiver.create("zip", { highWaterMark: (0, config_1.getUploadChunkSize)(), zlib: { level: compressionLevel } @@ -110359,8 +111211,8 @@ var require_zip2 = __commonJS({ } const bufferSize = (0, config_1.getUploadChunkSize)(); const zipUploadStream = new ZipUploadStream(bufferSize); - core30.debug(`Zip write high watermark value ${zipUploadStream.writableHighWaterMark}`); - core30.debug(`Zip read high watermark value ${zipUploadStream.readableHighWaterMark}`); + core31.debug(`Zip write high watermark value ${zipUploadStream.writableHighWaterMark}`); + core31.debug(`Zip read high watermark value ${zipUploadStream.readableHighWaterMark}`); zip.pipe(zipUploadStream); zip.finalize(); return zipUploadStream; @@ -110368,24 +111220,24 @@ var require_zip2 = __commonJS({ } exports2.createZipUploadStream = createZipUploadStream; var zipErrorCallback = (error3) => { - core30.error("An error has occurred while creating the zip file for upload"); - core30.info(error3); + core31.error("An error has occurred while creating the zip file for upload"); + core31.info(error3); throw new Error("An error has occurred during zip creation for the artifact"); }; var zipWarningCallback = (error3) => { if (error3.code === "ENOENT") { - core30.warning("ENOENT warning during artifact zip creation. No such file or directory"); - core30.info(error3); + core31.warning("ENOENT warning during artifact zip creation. No such file or directory"); + core31.info(error3); } else { - core30.warning(`A non-blocking warning has occurred during artifact zip creation: ${error3.code}`); - core30.info(error3); + core31.warning(`A non-blocking warning has occurred during artifact zip creation: ${error3.code}`); + core31.info(error3); } }; var zipFinishCallback = () => { - core30.debug("Zip stream for upload has finished."); + core31.debug("Zip stream for upload has finished."); }; var zipEndCallback = () => { - core30.debug("Zip stream for upload has ended."); + core31.debug("Zip stream for upload has ended."); }; } }); @@ -110450,7 +111302,7 @@ var require_upload_artifact = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.uploadArtifact = void 0; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var retention_1 = require_retention(); var path_and_artifact_name_validation_1 = require_path_and_artifact_name_validation(); var artifact_twirp_client_1 = require_artifact_twirp_client2(); @@ -110497,13 +111349,13 @@ var require_upload_artifact = __commonJS({ value: `sha256:${uploadResult.sha256Hash}` }); } - core30.info(`Finalizing artifact upload`); + core31.info(`Finalizing artifact upload`); const finalizeArtifactResp = yield artifactClient.FinalizeArtifact(finalizeArtifactReq); if (!finalizeArtifactResp.ok) { throw new errors_1.InvalidResponseError("FinalizeArtifact: response from backend was not ok"); } const artifactId = BigInt(finalizeArtifactResp.artifactId); - core30.info(`Artifact ${name}.zip successfully finalized. Artifact ID ${artifactId}`); + core31.info(`Artifact ${name}.zip successfully finalized. Artifact ID ${artifactId}`); return { size: uploadResult.uploadSize, digest: uploadResult.sha256Hash, @@ -111572,12 +112424,12 @@ var require_dist_node2 = __commonJS({ format: "" } }; - function lowercaseKeys2(object) { - if (!object) { + function lowercaseKeys2(object2) { + if (!object2) { return {}; } - return Object.keys(object).reduce((newObj, key) => { - newObj[key.toLowerCase()] = object[key]; + return Object.keys(object2).reduce((newObj, key) => { + newObj[key.toLowerCase()] = object2[key]; return newObj; }, {}); } @@ -111592,14 +112444,14 @@ var require_dist_node2 = __commonJS({ const Ctor = Object.prototype.hasOwnProperty.call(proto, "constructor") && proto.constructor; return typeof Ctor === "function" && Ctor instanceof Ctor && Function.prototype.call(Ctor) === Function.prototype.call(value); } - function mergeDeep2(defaults2, options) { - const result = Object.assign({}, defaults2); + function mergeDeep2(defaults3, options) { + const result = Object.assign({}, defaults3); Object.keys(options).forEach((key) => { if (isPlainObject4(options[key])) { - if (!(key in defaults2)) + if (!(key in defaults3)) Object.assign(result, { [key]: options[key] }); else - result[key] = mergeDeep2(defaults2[key], options[key]); + result[key] = mergeDeep2(defaults3[key], options[key]); } else { Object.assign(result, { [key]: options[key] }); } @@ -111614,7 +112466,7 @@ var require_dist_node2 = __commonJS({ } return obj; } - function merge2(defaults2, route, options) { + function merge2(defaults3, route, options) { if (typeof route === "string") { let [method, url2] = route.split(" "); options = Object.assign(url2 ? { method, url: url2 } : { url: method }, options); @@ -111624,10 +112476,10 @@ var require_dist_node2 = __commonJS({ options.headers = lowercaseKeys2(options.headers); removeUndefinedProperties2(options); removeUndefinedProperties2(options.headers); - const mergedOptions = mergeDeep2(defaults2 || {}, options); + const mergedOptions = mergeDeep2(defaults3 || {}, options); if (options.url === "/graphql") { - if (defaults2 && defaults2.mediaType.previews?.length) { - mergedOptions.mediaType.previews = defaults2.mediaType.previews.filter( + if (defaults3 && defaults3.mediaType.previews?.length) { + mergedOptions.mediaType.previews = defaults3.mediaType.previews.filter( (preview) => !mergedOptions.mediaType.previews.includes(preview) ).concat(mergedOptions.mediaType.previews); } @@ -111659,11 +112511,11 @@ var require_dist_node2 = __commonJS({ } return matches.map(removeNonChars2).reduce((a, b) => a.concat(b), []); } - function omit2(object, keysToOmit) { + function omit2(object2, keysToOmit) { const result = { __proto__: null }; - for (const key of Object.keys(object)) { + for (const key of Object.keys(object2)) { if (keysToOmit.indexOf(key) === -1) { - result[key] = object[key]; + result[key] = object2[key]; } } return result; @@ -111798,7 +112650,7 @@ var require_dist_node2 = __commonJS({ return template.replace(/\/$/, ""); } } - function parse2(options) { + function parse3(options) { let method = options.method.toUpperCase(); let url2 = (options.url || "/").replace(/:([a-z]\w+)/g, "{$1}"); let headers = Object.assign({}, options.headers); @@ -111861,8 +112713,8 @@ var require_dist_node2 = __commonJS({ options.request ? { request: options.request } : null ); } - function endpointWithDefaults2(defaults2, route, options) { - return parse2(merge2(defaults2, route, options)); + function endpointWithDefaults2(defaults3, route, options) { + return parse3(merge2(defaults3, route, options)); } function withDefaults4(oldDefaults, newDefaults) { const DEFAULTS22 = merge2(oldDefaults, newDefaults); @@ -111871,7 +112723,7 @@ var require_dist_node2 = __commonJS({ DEFAULTS: DEFAULTS22, defaults: withDefaults4.bind(null, DEFAULTS22), merge: merge2.bind(null, DEFAULTS22), - parse: parse2 + parse: parse3 }); } var endpoint2 = withDefaults4(null, DEFAULTS2); @@ -112536,21 +113388,21 @@ var require_dist_node8 = __commonJS({ static { this.VERSION = VERSION8; } - static defaults(defaults2) { + static defaults(defaults3) { const OctokitWithDefaults = class extends this { constructor(...args) { const options = args[0] || {}; - if (typeof defaults2 === "function") { - super(defaults2(options)); + if (typeof defaults3 === "function") { + super(defaults3(options)); return; } super( Object.assign( {}, - defaults2, + defaults3, options, - options.userAgent && defaults2.userAgent ? { - userAgent: `${options.userAgent} ${defaults2.userAgent}` + options.userAgent && defaults3.userAgent ? { + userAgent: `${options.userAgent} ${defaults3.userAgent}` } : null ) ); @@ -114666,14 +115518,14 @@ var require_dist_node9 = __commonJS({ var endpointMethodsMap2 = /* @__PURE__ */ new Map(); for (const [scope, endpoints] of Object.entries(endpoints_default2)) { for (const [methodName, endpoint2] of Object.entries(endpoints)) { - const [route, defaults2, decorations] = endpoint2; + const [route, defaults3, decorations] = endpoint2; const [method, url2] = route.split(/ /); const endpointDefaults = Object.assign( { method, url: url2 }, - defaults2 + defaults3 ); if (!endpointMethodsMap2.has(scope)) { endpointMethodsMap2.set(scope, /* @__PURE__ */ new Map()); @@ -114743,8 +115595,8 @@ var require_dist_node9 = __commonJS({ } return newMethods; } - function decorate2(octokit, scope, methodName, defaults2, decorations) { - const requestWithDefaults = octokit.request.defaults(defaults2); + function decorate2(octokit, scope, methodName, defaults3, decorations) { + const requestWithDefaults = octokit.request.defaults(defaults3); function withDecorations(...args) { let options = requestWithDefaults.endpoint.merge(...args); if (decorations.mapToData) { @@ -115322,9 +116174,9 @@ var require_traverse = __commonJS({ this.value = walk(this.value, cb, false); return this.value; }; - Traverse.prototype.reduce = function(cb, init) { + Traverse.prototype.reduce = function(cb, init2) { var skip = arguments.length === 1; - var acc = skip ? this.value : init; + var acc = skip ? this.value : init2; this.forEach(function(x) { if (!this.isRoot || !skip) { acc = cb.call(this, acc, x); @@ -116103,7 +116955,7 @@ var require_binary = __commonJS({ }); return stream2; }; - exports2.parse = function parse2(buffer) { + exports2.parse = function parse3(buffer) { var self2 = words(function(bytes, cb) { return function(name) { if (offset + bytes <= buffer.length) { @@ -117227,7 +118079,7 @@ var require_download_artifact = __commonJS({ var crypto3 = __importStar2(require("crypto")); var stream2 = __importStar2(require("stream")); var github5 = __importStar2(require_github2()); - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var httpClient = __importStar2(require_lib()); var unzip_stream_1 = __importDefault2(require_unzip()); var user_agent_1 = require_user_agent2(); @@ -117263,7 +118115,7 @@ var require_download_artifact = __commonJS({ return yield streamExtractExternal(url2, directory); } catch (error3) { retryCount++; - core30.debug(`Failed to download artifact after ${retryCount} retries due to ${error3.message}. Retrying in 5 seconds...`); + core31.debug(`Failed to download artifact after ${retryCount} retries due to ${error3.message}. Retrying in 5 seconds...`); yield new Promise((resolve14) => setTimeout(resolve14, 5e3)); } } @@ -117293,7 +118145,7 @@ var require_download_artifact = __commonJS({ extractStream.on("data", () => { timer.refresh(); }).on("error", (error3) => { - core30.debug(`response.message: Artifact download failed: ${error3.message}`); + core31.debug(`response.message: Artifact download failed: ${error3.message}`); clearTimeout(timer); reject(error3); }).pipe(unzip_stream_1.default.Extract({ path: directory })).on("close", () => { @@ -117301,7 +118153,7 @@ var require_download_artifact = __commonJS({ if (hashStream) { hashStream.end(); sha256Digest = hashStream.read(); - core30.info(`SHA256 digest of downloaded artifact is ${sha256Digest}`); + core31.info(`SHA256 digest of downloaded artifact is ${sha256Digest}`); } resolve14({ sha256Digest: `sha256:${sha256Digest}` }); }).on("error", (error3) => { @@ -117316,7 +118168,7 @@ var require_download_artifact = __commonJS({ const downloadPath = yield resolveOrCreateDirectory(options === null || options === void 0 ? void 0 : options.path); const api = github5.getOctokit(token); let digestMismatch = false; - core30.info(`Downloading artifact '${artifactId}' from '${repositoryOwner}/${repositoryName}'`); + core31.info(`Downloading artifact '${artifactId}' from '${repositoryOwner}/${repositoryName}'`); const { headers, status } = yield api.rest.actions.downloadArtifact({ owner: repositoryOwner, repo: repositoryName, @@ -117333,16 +118185,16 @@ var require_download_artifact = __commonJS({ if (!location) { throw new Error(`Unable to redirect to artifact download url`); } - core30.info(`Redirecting to blob download url: ${scrubQueryParameters(location)}`); + core31.info(`Redirecting to blob download url: ${scrubQueryParameters(location)}`); try { - core30.info(`Starting download of artifact to: ${downloadPath}`); + core31.info(`Starting download of artifact to: ${downloadPath}`); const extractResponse = yield streamExtract(location, downloadPath); - core30.info(`Artifact download completed successfully.`); + core31.info(`Artifact download completed successfully.`); if (options === null || options === void 0 ? void 0 : options.expectedHash) { if ((options === null || options === void 0 ? void 0 : options.expectedHash) !== extractResponse.sha256Digest) { digestMismatch = true; - core30.debug(`Computed digest: ${extractResponse.sha256Digest}`); - core30.debug(`Expected digest: ${options.expectedHash}`); + core31.debug(`Computed digest: ${extractResponse.sha256Digest}`); + core31.debug(`Expected digest: ${options.expectedHash}`); } } } catch (error3) { @@ -117369,7 +118221,7 @@ var require_download_artifact = __commonJS({ Are you trying to download from a different run? Try specifying a github-token with \`actions:read\` scope.`); } if (artifacts.length > 1) { - core30.warning("Multiple artifacts found, defaulting to first."); + core31.warning("Multiple artifacts found, defaulting to first."); } const signedReq = { workflowRunBackendId: artifacts[0].workflowRunBackendId, @@ -117377,16 +118229,16 @@ Are you trying to download from a different run? Try specifying a github-token w name: artifacts[0].name }; const { signedUrl } = yield artifactClient.GetSignedArtifactURL(signedReq); - core30.info(`Redirecting to blob download url: ${scrubQueryParameters(signedUrl)}`); + core31.info(`Redirecting to blob download url: ${scrubQueryParameters(signedUrl)}`); try { - core30.info(`Starting download of artifact to: ${downloadPath}`); + core31.info(`Starting download of artifact to: ${downloadPath}`); const extractResponse = yield streamExtract(signedUrl, downloadPath); - core30.info(`Artifact download completed successfully.`); + core31.info(`Artifact download completed successfully.`); if (options === null || options === void 0 ? void 0 : options.expectedHash) { if ((options === null || options === void 0 ? void 0 : options.expectedHash) !== extractResponse.sha256Digest) { digestMismatch = true; - core30.debug(`Computed digest: ${extractResponse.sha256Digest}`); - core30.debug(`Expected digest: ${options.expectedHash}`); + core31.debug(`Computed digest: ${extractResponse.sha256Digest}`); + core31.debug(`Expected digest: ${options.expectedHash}`); } } } catch (error3) { @@ -117399,10 +118251,10 @@ Are you trying to download from a different run? Try specifying a github-token w function resolveOrCreateDirectory() { return __awaiter2(this, arguments, void 0, function* (downloadPath = (0, config_1.getGitHubWorkspaceDir)()) { if (!(yield exists(downloadPath))) { - core30.debug(`Artifact destination folder does not exist, creating: ${downloadPath}`); + core31.debug(`Artifact destination folder does not exist, creating: ${downloadPath}`); yield promises_1.default.mkdir(downloadPath, { recursive: true }); } else { - core30.debug(`Artifact destination folder already exists: ${downloadPath}`); + core31.debug(`Artifact destination folder already exists: ${downloadPath}`); } return downloadPath; }); @@ -117443,7 +118295,7 @@ var require_retry_options = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getRetryOptions = void 0; - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var defaultMaxRetryNumber = 5; var defaultExemptStatusCodes = [400, 401, 403, 404, 422]; function getRetryOptions(defaultOptions, retries = defaultMaxRetryNumber, exemptStatusCodes = defaultExemptStatusCodes) { @@ -117458,7 +118310,7 @@ var require_retry_options = __commonJS({ retryOptions.doNotRetry = exemptStatusCodes; } const requestOptions = Object.assign(Object.assign({}, defaultOptions.request), { retries }); - core30.debug(`GitHub client configured with: (retries: ${requestOptions.retries}, retry-exempt-status-code: ${(_a2 = retryOptions.doNotRetry) !== null && _a2 !== void 0 ? _a2 : "octokit default: [400, 401, 403, 404, 422]"})`); + core31.debug(`GitHub client configured with: (retries: ${requestOptions.retries}, retry-exempt-status-code: ${(_a2 = retryOptions.doNotRetry) !== null && _a2 !== void 0 ? _a2 : "octokit default: [400, 401, 403, 404, 422]"})`); return [retryOptions, requestOptions]; } exports2.getRetryOptions = getRetryOptions; @@ -117615,7 +118467,7 @@ var require_get_artifact = __commonJS({ exports2.getArtifactInternal = exports2.getArtifactPublic = void 0; var github_1 = require_github2(); var plugin_retry_1 = require_dist_node12(); - var core30 = __importStar2(require_core()); + var core31 = __importStar2(require_core()); var utils_1 = require_utils9(); var retry_options_1 = require_retry_options(); var plugin_request_log_1 = require_dist_node11(); @@ -117653,7 +118505,7 @@ var require_get_artifact = __commonJS({ let artifact2 = getArtifactResp.data.artifacts[0]; if (getArtifactResp.data.artifacts.length > 1) { artifact2 = getArtifactResp.data.artifacts.sort((a, b) => b.id - a.id)[0]; - core30.debug(`More than one artifact found for a single name, returning newest (id: ${artifact2.id})`); + core31.debug(`More than one artifact found for a single name, returning newest (id: ${artifact2.id})`); } return { artifact: { @@ -117686,7 +118538,7 @@ var require_get_artifact = __commonJS({ let artifact2 = res.artifacts[0]; if (res.artifacts.length > 1) { artifact2 = res.artifacts.sort((a, b) => Number(b.databaseId) - Number(a.databaseId))[0]; - core30.debug(`More than one artifact found for a single name, returning newest (id: ${artifact2.databaseId})`); + core31.debug(`More than one artifact found for a single name, returning newest (id: ${artifact2.databaseId})`); } return { artifact: { @@ -120794,7 +121646,7 @@ var require_core3 = __commonJS({ ExitCode2[ExitCode2["Success"] = 0] = "Success"; ExitCode2[ExitCode2["Failure"] = 1] = "Failure"; })(ExitCode || (exports2.ExitCode = ExitCode = {})); - function exportVariable15(name, val) { + function exportVariable16(name, val) { const convertedVal = (0, utils_1.toCommandValue)(val); process.env[name] = convertedVal; const filePath = process.env["GITHUB_ENV"] || ""; @@ -120803,7 +121655,7 @@ var require_core3 = __commonJS({ } (0, command_1.issueCommand)("set-env", { name }, convertedVal); } - exports2.exportVariable = exportVariable15; + exports2.exportVariable = exportVariable16; function setSecret2(secret) { (0, command_1.issueCommand)("add-mask", {}, secret); } @@ -122440,7 +123292,7 @@ var require_requestUtils2 = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.retryHttpClientRequest = exports2.retry = void 0; var utils_1 = require_utils11(); - var core30 = __importStar2(require_core3()); + var core31 = __importStar2(require_core3()); var config_variables_1 = require_config_variables(); function retry2(name, operation, customErrorMessages, maxAttempts) { return __awaiter2(this, void 0, void 0, function* () { @@ -122467,13 +123319,13 @@ var require_requestUtils2 = __commonJS({ errorMessage = error3.message; } if (!isRetryable) { - core30.info(`${name} - Error is not retryable`); + core31.info(`${name} - Error is not retryable`); if (response) { (0, utils_1.displayHttpDiagnostics)(response); } break; } - core30.info(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); + core31.info(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); yield (0, utils_1.sleep)((0, utils_1.getExponentialRetryTimeInMilliseconds)(attempt)); attempt++; } @@ -122557,7 +123409,7 @@ var require_upload_http_client = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.UploadHttpClient = void 0; var fs31 = __importStar2(require("fs")); - var core30 = __importStar2(require_core3()); + var core31 = __importStar2(require_core3()); var tmp = __importStar2(require_tmp_promise()); var stream2 = __importStar2(require("stream")); var utils_1 = require_utils11(); @@ -122622,7 +123474,7 @@ var require_upload_http_client = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { const FILE_CONCURRENCY = (0, config_variables_1.getUploadFileConcurrency)(); const MAX_CHUNK_SIZE = (0, config_variables_1.getUploadChunkSize)(); - core30.debug(`File Concurrency: ${FILE_CONCURRENCY}, and Chunk Size: ${MAX_CHUNK_SIZE}`); + core31.debug(`File Concurrency: ${FILE_CONCURRENCY}, and Chunk Size: ${MAX_CHUNK_SIZE}`); const parameters = []; let continueOnError = true; if (options) { @@ -122659,15 +123511,15 @@ var require_upload_http_client = __commonJS({ } const startTime = perf_hooks_1.performance.now(); const uploadFileResult = yield this.uploadFileAsync(index2, currentFileParameters); - if (core30.isDebug()) { - core30.debug(`File: ${++completedFiles}/${filesToUpload.length}. ${currentFileParameters.file} took ${(perf_hooks_1.performance.now() - startTime).toFixed(3)} milliseconds to finish upload`); + if (core31.isDebug()) { + core31.debug(`File: ${++completedFiles}/${filesToUpload.length}. ${currentFileParameters.file} took ${(perf_hooks_1.performance.now() - startTime).toFixed(3)} milliseconds to finish upload`); } uploadFileSize += uploadFileResult.successfulUploadSize; totalFileSize += uploadFileResult.totalSize; if (uploadFileResult.isSuccess === false) { failedItemsToReport.push(currentFileParameters.file); if (!continueOnError) { - core30.error(`aborting artifact upload`); + core31.error(`aborting artifact upload`); abortPendingFileUploads = true; } } @@ -122676,7 +123528,7 @@ var require_upload_http_client = __commonJS({ }))); this.statusReporter.stop(); this.uploadHttpManager.disposeAndReplaceAllClients(); - core30.info(`Total size of all the files uploaded is ${uploadFileSize} bytes`); + core31.info(`Total size of all the files uploaded is ${uploadFileSize} bytes`); return { uploadSize: uploadFileSize, totalSize: totalFileSize, @@ -122702,16 +123554,16 @@ var require_upload_http_client = __commonJS({ let uploadFileSize = 0; let isGzip = true; if (!isFIFO && totalFileSize < 65536) { - core30.debug(`${parameters.file} is less than 64k in size. Creating a gzip file in-memory to potentially reduce the upload size`); + core31.debug(`${parameters.file} is less than 64k in size. Creating a gzip file in-memory to potentially reduce the upload size`); const buffer = yield (0, upload_gzip_1.createGZipFileInBuffer)(parameters.file); let openUploadStream; if (totalFileSize < buffer.byteLength) { - core30.debug(`The gzip file created for ${parameters.file} did not help with reducing the size of the file. The original file will be uploaded as-is`); + core31.debug(`The gzip file created for ${parameters.file} did not help with reducing the size of the file. The original file will be uploaded as-is`); openUploadStream = () => fs31.createReadStream(parameters.file); isGzip = false; uploadFileSize = totalFileSize; } else { - core30.debug(`A gzip file created for ${parameters.file} helped with reducing the size of the original file. The file will be uploaded using gzip.`); + core31.debug(`A gzip file created for ${parameters.file} helped with reducing the size of the original file. The file will be uploaded using gzip.`); openUploadStream = () => { const passThrough = new stream2.PassThrough(); passThrough.end(buffer); @@ -122723,7 +123575,7 @@ var require_upload_http_client = __commonJS({ if (!result) { isUploadSuccessful = false; failedChunkSizes += uploadFileSize; - core30.warning(`Aborting upload for ${parameters.file} due to failure`); + core31.warning(`Aborting upload for ${parameters.file} due to failure`); } return { isSuccess: isUploadSuccessful, @@ -122732,16 +123584,16 @@ var require_upload_http_client = __commonJS({ }; } else { const tempFile = yield tmp.file(); - core30.debug(`${parameters.file} is greater than 64k in size. Creating a gzip file on-disk ${tempFile.path} to potentially reduce the upload size`); + core31.debug(`${parameters.file} is greater than 64k in size. Creating a gzip file on-disk ${tempFile.path} to potentially reduce the upload size`); uploadFileSize = yield (0, upload_gzip_1.createGZipFileOnDisk)(parameters.file, tempFile.path); let uploadFilePath = tempFile.path; if (!isFIFO && totalFileSize < uploadFileSize) { - core30.debug(`The gzip file created for ${parameters.file} did not help with reducing the size of the file. The original file will be uploaded as-is`); + core31.debug(`The gzip file created for ${parameters.file} did not help with reducing the size of the file. The original file will be uploaded as-is`); uploadFileSize = totalFileSize; uploadFilePath = parameters.file; isGzip = false; } else { - core30.debug(`The gzip file created for ${parameters.file} is smaller than the original file. The file will be uploaded using gzip.`); + core31.debug(`The gzip file created for ${parameters.file} is smaller than the original file. The file will be uploaded using gzip.`); } let abortFileUpload = false; while (offset < uploadFileSize) { @@ -122761,7 +123613,7 @@ var require_upload_http_client = __commonJS({ if (!result) { isUploadSuccessful = false; failedChunkSizes += chunkSize; - core30.warning(`Aborting upload for ${parameters.file} due to failure`); + core31.warning(`Aborting upload for ${parameters.file} due to failure`); abortFileUpload = true; } else { if (uploadFileSize > 8388608) { @@ -122769,7 +123621,7 @@ var require_upload_http_client = __commonJS({ } } } - core30.debug(`deleting temporary gzip file ${tempFile.path}`); + core31.debug(`deleting temporary gzip file ${tempFile.path}`); yield tempFile.cleanup(); return { isSuccess: isUploadSuccessful, @@ -122808,7 +123660,7 @@ var require_upload_http_client = __commonJS({ if (response) { (0, utils_1.displayHttpDiagnostics)(response); } - core30.info(`Retry limit has been reached for chunk at offset ${start} to ${resourceUrl}`); + core31.info(`Retry limit has been reached for chunk at offset ${start} to ${resourceUrl}`); return true; } return false; @@ -122816,14 +123668,14 @@ var require_upload_http_client = __commonJS({ const backOff = (retryAfterValue) => __awaiter2(this, void 0, void 0, function* () { this.uploadHttpManager.disposeAndReplaceClient(httpClientIndex); if (retryAfterValue) { - core30.info(`Backoff due to too many requests, retry #${retryCount}. Waiting for ${retryAfterValue} milliseconds before continuing the upload`); + core31.info(`Backoff due to too many requests, retry #${retryCount}. Waiting for ${retryAfterValue} milliseconds before continuing the upload`); yield (0, utils_1.sleep)(retryAfterValue); } else { const backoffTime = (0, utils_1.getExponentialRetryTimeInMilliseconds)(retryCount); - core30.info(`Exponential backoff for retry #${retryCount}. Waiting for ${backoffTime} milliseconds before continuing the upload at offset ${start}`); + core31.info(`Exponential backoff for retry #${retryCount}. Waiting for ${backoffTime} milliseconds before continuing the upload at offset ${start}`); yield (0, utils_1.sleep)(backoffTime); } - core30.info(`Finished backoff for retry #${retryCount}, continuing with upload`); + core31.info(`Finished backoff for retry #${retryCount}, continuing with upload`); return; }); while (retryCount <= retryLimit) { @@ -122831,7 +123683,7 @@ var require_upload_http_client = __commonJS({ try { response = yield uploadChunkRequest(); } catch (error3) { - core30.info(`An error has been caught http-client index ${httpClientIndex}, retrying the upload`); + core31.info(`An error has been caught http-client index ${httpClientIndex}, retrying the upload`); console.log(error3); if (incrementAndCheckRetryLimit()) { return false; @@ -122843,13 +123695,13 @@ var require_upload_http_client = __commonJS({ if ((0, utils_1.isSuccessStatusCode)(response.message.statusCode)) { return true; } else if ((0, utils_1.isRetryableStatusCode)(response.message.statusCode)) { - core30.info(`A ${response.message.statusCode} status code has been received, will attempt to retry the upload`); + core31.info(`A ${response.message.statusCode} status code has been received, will attempt to retry the upload`); if (incrementAndCheckRetryLimit(response)) { return false; } (0, utils_1.isThrottledStatusCode)(response.message.statusCode) ? yield backOff((0, utils_1.tryGetRetryAfterValueTimeInMilliseconds)(response.message.headers)) : yield backOff(); } else { - core30.error(`Unexpected response. Unable to upload chunk to ${resourceUrl}`); + core31.error(`Unexpected response. Unable to upload chunk to ${resourceUrl}`); (0, utils_1.displayHttpDiagnostics)(response); return false; } @@ -122867,7 +123719,7 @@ var require_upload_http_client = __commonJS({ resourceUrl.searchParams.append("artifactName", artifactName); const parameters = { Size: size }; const data = JSON.stringify(parameters, null, 2); - core30.debug(`URL is ${resourceUrl.toString()}`); + core31.debug(`URL is ${resourceUrl.toString()}`); const client = this.uploadHttpManager.getClient(0); const headers = (0, utils_1.getUploadHeaders)("application/json", false); const customErrorMessages = /* @__PURE__ */ new Map([ @@ -122880,7 +123732,7 @@ var require_upload_http_client = __commonJS({ return client.patch(resourceUrl.toString(), data, headers); }), customErrorMessages); yield response.readBody(); - core30.debug(`Artifact ${artifactName} has been successfully uploaded, total size in bytes: ${size}`); + core31.debug(`Artifact ${artifactName} has been successfully uploaded, total size in bytes: ${size}`); }); } }; @@ -122949,7 +123801,7 @@ var require_download_http_client = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.DownloadHttpClient = void 0; var fs31 = __importStar2(require("fs")); - var core30 = __importStar2(require_core3()); + var core31 = __importStar2(require_core3()); var zlib3 = __importStar2(require("zlib")); var utils_1 = require_utils11(); var url_1 = require("url"); @@ -123003,11 +123855,11 @@ var require_download_http_client = __commonJS({ downloadSingleArtifact(downloadItems) { return __awaiter2(this, void 0, void 0, function* () { const DOWNLOAD_CONCURRENCY = (0, config_variables_1.getDownloadFileConcurrency)(); - core30.debug(`Download file concurrency is set to ${DOWNLOAD_CONCURRENCY}`); + core31.debug(`Download file concurrency is set to ${DOWNLOAD_CONCURRENCY}`); const parallelDownloads = [...new Array(DOWNLOAD_CONCURRENCY).keys()]; let currentFile = 0; let downloadedFiles = 0; - core30.info(`Total number of files that will be downloaded: ${downloadItems.length}`); + core31.info(`Total number of files that will be downloaded: ${downloadItems.length}`); this.statusReporter.setTotalNumberOfFilesToProcess(downloadItems.length); this.statusReporter.start(); yield Promise.all(parallelDownloads.map((index2) => __awaiter2(this, void 0, void 0, function* () { @@ -123016,8 +123868,8 @@ var require_download_http_client = __commonJS({ currentFile += 1; const startTime = perf_hooks_1.performance.now(); yield this.downloadIndividualFile(index2, currentFileToDownload.sourceLocation, currentFileToDownload.targetPath); - if (core30.isDebug()) { - core30.debug(`File: ${++downloadedFiles}/${downloadItems.length}. ${currentFileToDownload.targetPath} took ${(perf_hooks_1.performance.now() - startTime).toFixed(3)} milliseconds to finish downloading`); + if (core31.isDebug()) { + core31.debug(`File: ${++downloadedFiles}/${downloadItems.length}. ${currentFileToDownload.targetPath} took ${(perf_hooks_1.performance.now() - startTime).toFixed(3)} milliseconds to finish downloading`); } this.statusReporter.incrementProcessedCount(); } @@ -123055,19 +123907,19 @@ var require_download_http_client = __commonJS({ } else { this.downloadHttpManager.disposeAndReplaceClient(httpClientIndex); if (retryAfterValue) { - core30.info(`Backoff due to too many requests, retry #${retryCount}. Waiting for ${retryAfterValue} milliseconds before continuing the download`); + core31.info(`Backoff due to too many requests, retry #${retryCount}. Waiting for ${retryAfterValue} milliseconds before continuing the download`); yield (0, utils_1.sleep)(retryAfterValue); } else { const backoffTime = (0, utils_1.getExponentialRetryTimeInMilliseconds)(retryCount); - core30.info(`Exponential backoff for retry #${retryCount}. Waiting for ${backoffTime} milliseconds before continuing the download`); + core31.info(`Exponential backoff for retry #${retryCount}. Waiting for ${backoffTime} milliseconds before continuing the download`); yield (0, utils_1.sleep)(backoffTime); } - core30.info(`Finished backoff for retry #${retryCount}, continuing with download`); + core31.info(`Finished backoff for retry #${retryCount}, continuing with download`); } }); const isAllBytesReceived = (expected, received) => { if (!expected || !received || process.env["ACTIONS_ARTIFACT_SKIP_DOWNLOAD_VALIDATION"]) { - core30.info("Skipping download validation."); + core31.info("Skipping download validation."); return true; } return parseInt(expected) === received; @@ -123088,7 +123940,7 @@ var require_download_http_client = __commonJS({ try { response = yield makeDownloadRequest(); } catch (error3) { - core30.info("An error occurred while attempting to download a file"); + core31.info("An error occurred while attempting to download a file"); console.log(error3); yield backOff(); continue; @@ -123108,7 +123960,7 @@ var require_download_http_client = __commonJS({ } } if (forceRetry || (0, utils_1.isRetryableStatusCode)(response.message.statusCode)) { - core30.info(`A ${response.message.statusCode} response code has been received while attempting to download an artifact`); + core31.info(`A ${response.message.statusCode} response code has been received while attempting to download an artifact`); resetDestinationStream(downloadPath); (0, utils_1.isThrottledStatusCode)(response.message.statusCode) ? yield backOff((0, utils_1.tryGetRetryAfterValueTimeInMilliseconds)(response.message.headers)) : yield backOff(); } else { @@ -123130,29 +123982,29 @@ var require_download_http_client = __commonJS({ if (isGzip) { const gunzip = zlib3.createGunzip(); response.message.on("error", (error3) => { - core30.info(`An error occurred while attempting to read the response stream`); + core31.info(`An error occurred while attempting to read the response stream`); gunzip.close(); destinationStream.close(); reject(error3); }).pipe(gunzip).on("error", (error3) => { - core30.info(`An error occurred while attempting to decompress the response stream`); + core31.info(`An error occurred while attempting to decompress the response stream`); destinationStream.close(); reject(error3); }).pipe(destinationStream).on("close", () => { resolve14(); }).on("error", (error3) => { - core30.info(`An error occurred while writing a downloaded file to ${destinationStream.path}`); + core31.info(`An error occurred while writing a downloaded file to ${destinationStream.path}`); reject(error3); }); } else { response.message.on("error", (error3) => { - core30.info(`An error occurred while attempting to read the response stream`); + core31.info(`An error occurred while attempting to read the response stream`); destinationStream.close(); reject(error3); }).pipe(destinationStream).on("close", () => { resolve14(); }).on("error", (error3) => { - core30.info(`An error occurred while writing a downloaded file to ${destinationStream.path}`); + core31.info(`An error occurred while writing a downloaded file to ${destinationStream.path}`); reject(error3); }); } @@ -123291,7 +124143,7 @@ var require_artifact_client = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.DefaultArtifactClient = void 0; - var core30 = __importStar2(require_core3()); + var core31 = __importStar2(require_core3()); var upload_specification_1 = require_upload_specification(); var upload_http_client_1 = require_upload_http_client(); var utils_1 = require_utils11(); @@ -123312,7 +124164,7 @@ var require_artifact_client = __commonJS({ */ uploadArtifact(name, files, rootDirectory, options) { return __awaiter2(this, void 0, void 0, function* () { - core30.info(`Starting artifact upload + core31.info(`Starting artifact upload For more detailed logs during the artifact upload process, enable step-debugging: https://docs.github.com/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging#enabling-step-debug-logging`); (0, path_and_artifact_name_validation_1.checkArtifactName)(name); const uploadSpecification = (0, upload_specification_1.getUploadSpecification)(name, rootDirectory, files); @@ -123324,24 +124176,24 @@ For more detailed logs during the artifact upload process, enable step-debugging }; const uploadHttpClient = new upload_http_client_1.UploadHttpClient(); if (uploadSpecification.length === 0) { - core30.warning(`No files found that can be uploaded`); + core31.warning(`No files found that can be uploaded`); } else { const response = yield uploadHttpClient.createArtifactInFileContainer(name, options); if (!response.fileContainerResourceUrl) { - core30.debug(response.toString()); + core31.debug(response.toString()); throw new Error("No URL provided by the Artifact Service to upload an artifact to"); } - core30.debug(`Upload Resource URL: ${response.fileContainerResourceUrl}`); - core30.info(`Container for artifact "${name}" successfully created. Starting upload of file(s)`); + core31.debug(`Upload Resource URL: ${response.fileContainerResourceUrl}`); + core31.info(`Container for artifact "${name}" successfully created. Starting upload of file(s)`); const uploadResult = yield uploadHttpClient.uploadArtifactToFileContainer(response.fileContainerResourceUrl, uploadSpecification, options); - core30.info(`File upload process has finished. Finalizing the artifact upload`); + core31.info(`File upload process has finished. Finalizing the artifact upload`); yield uploadHttpClient.patchArtifactSize(uploadResult.totalSize, name); if (uploadResult.failedItems.length > 0) { - core30.info(`Upload finished. There were ${uploadResult.failedItems.length} items that failed to upload`); + core31.info(`Upload finished. There were ${uploadResult.failedItems.length} items that failed to upload`); } else { - core30.info(`Artifact has been finalized. All files have been successfully uploaded!`); + core31.info(`Artifact has been finalized. All files have been successfully uploaded!`); } - core30.info(` + core31.info(` The raw size of all the files that were specified for upload is ${uploadResult.totalSize} bytes The size of all the files that were uploaded is ${uploadResult.uploadSize} bytes. This takes into account any gzip compression used to reduce the upload size, time and storage @@ -123375,10 +124227,10 @@ Note: The size of downloaded zips can differ significantly from the reported siz path29 = (0, path_1.resolve)(path29); const downloadSpecification = (0, download_specification_1.getDownloadSpecification)(name, items.value, path29, (options === null || options === void 0 ? void 0 : options.createArtifactFolder) || false); if (downloadSpecification.filesToDownload.length === 0) { - core30.info(`No downloadable files were found for the artifact: ${artifactToDownload.name}`); + core31.info(`No downloadable files were found for the artifact: ${artifactToDownload.name}`); } else { yield (0, utils_1.createDirectoriesForArtifact)(downloadSpecification.directoryStructure); - core30.info("Directory structure has been set up for the artifact"); + core31.info("Directory structure has been set up for the artifact"); yield (0, utils_1.createEmptyFilesForArtifact)(downloadSpecification.emptyFilesToCreate); yield downloadHttpClient.downloadSingleArtifact(downloadSpecification.filesToDownload); } @@ -123394,7 +124246,7 @@ Note: The size of downloaded zips can differ significantly from the reported siz const response = []; const artifacts = yield downloadHttpClient.listArtifacts(); if (artifacts.count === 0) { - core30.info("Unable to find any artifacts for the associated workflow"); + core31.info("Unable to find any artifacts for the associated workflow"); return response; } if (!path29) { @@ -123406,11 +124258,11 @@ Note: The size of downloaded zips can differ significantly from the reported siz while (downloadedArtifacts < artifacts.count) { const currentArtifactToDownload = artifacts.value[downloadedArtifacts]; downloadedArtifacts += 1; - core30.info(`starting download of artifact ${currentArtifactToDownload.name} : ${downloadedArtifacts}/${artifacts.count}`); + core31.info(`starting download of artifact ${currentArtifactToDownload.name} : ${downloadedArtifacts}/${artifacts.count}`); const items = yield downloadHttpClient.getContainerItems(currentArtifactToDownload.name, currentArtifactToDownload.fileContainerResourceUrl); const downloadSpecification = (0, download_specification_1.getDownloadSpecification)(currentArtifactToDownload.name, items.value, path29, true); if (downloadSpecification.filesToDownload.length === 0) { - core30.info(`No downloadable files were found for any artifact ${currentArtifactToDownload.name}`); + core31.info(`No downloadable files were found for any artifact ${currentArtifactToDownload.name}`); } else { yield (0, utils_1.createDirectoriesForArtifact)(downloadSpecification.directoryStructure); yield (0, utils_1.createEmptyFilesForArtifact)(downloadSpecification.emptyFilesToCreate); @@ -124734,8 +125586,8 @@ var require_util16 = __commonJS({ parts.push(format.substring(last)); return parts.join(""); }; - util3.formatNumber = function(number, decimals, dec_point, thousands_sep) { - var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals; + util3.formatNumber = function(number2, decimals, dec_point, thousands_sep) { + var n = number2, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals; var d = dec_point === void 0 ? "," : dec_point; var t = thousands_sep === void 0 ? "." : thousands_sep, s = n < 0 ? "-" : ""; var i = parseInt(n = Math.abs(+n || 0).toFixed(c), 10) + ""; @@ -125762,7 +126614,7 @@ var require_aes = __commonJS({ }); }; forge.aes.Algorithm = function(name, mode) { - if (!init) { + if (!init2) { initialize(); } var self2 = this; @@ -125815,7 +126667,7 @@ var require_aes = __commonJS({ this._init = true; }; forge.aes._expandKey = function(key, decrypt) { - if (!init) { + if (!init2) { initialize(); } return _expandKey(key, decrypt); @@ -125833,7 +126685,7 @@ var require_aes = __commonJS({ }; forge.cipher.registerAlgorithm(name, factory); } - var init = false; + var init2 = false; var Nb = 4; var sbox; var isbox; @@ -125841,7 +126693,7 @@ var require_aes = __commonJS({ var mix; var imix; function initialize() { - init = true; + init2 = true; rcon = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54]; var xtime = new Array(256); for (var i = 0; i < 128; ++i) { @@ -139431,11 +140283,11 @@ var require_ed25519 = __commonJS({ q[i] ^= t; } } - function gf(init) { + function gf(init2) { var i, r = new Float64Array(16); - if (init) { - for (i = 0; i < init.length; ++i) { - r[i] = init[i]; + if (init2) { + for (i = 0; i < init2.length; ++i) { + r[i] = init2[i]; } } return r; @@ -141290,7 +142142,10 @@ module.exports = __toCommonJS(entry_points_exports); var fs22 = __toESM(require("fs")); var import_path4 = __toESM(require("path")); var import_perf_hooks4 = require("perf_hooks"); -var core15 = __toESM(require_core()); +var core16 = __toESM(require_core()); + +// src/action-common.ts +var core8 = __toESM(require_core()); // src/actions-util.ts var fs2 = __toESM(require("fs")); @@ -141300,6 +142155,69 @@ var toolrunner = __toESM(require_toolrunner()); var github = __toESM(require_github()); var io2 = __toESM(require_io()); +// src/environment.ts +function getRequiredEnvVar(env, paramName) { + const value = env[paramName]; + if (value === void 0 || value.length === 0) { + throw new Error(`${paramName} environment variable must be set`); + } + return value; +} +function getRequiredEnvParam(paramName) { + return getRequiredEnvVar(process.env, paramName); +} +function getOptionalEnvVarFrom(env, paramName) { + const value = env[paramName]; + if (value?.trim().length === 0) { + return void 0; + } + return value; +} +function getOptionalEnvVar(paramName) { + return getOptionalEnvVarFrom(process.env, paramName); +} +var ReadOnlyEnv = class { + constructor(vars) { + this.vars = vars; + } + vars; + /** Clones the object while detaching the underlying environment from the original. */ + clone() { + return Object.create(this, { vars: { value: { ...this.vars } } }); + } + /** Gets a copy of the underlying environment. */ + get() { + return { ...this.vars }; + } + /** Tries to get the value for `name` and throws if there isn't one. */ + getRequired(name) { + return getRequiredEnvVar(this.vars, name); + } + /** Gets the value for `name`, or `undefined` if it isn't set or empty. */ + getOptional(name) { + return getOptionalEnvVarFrom(this.vars, name); + } + /** Gets the entries of the underlying `ProcessEnv`. */ + entries() { + return Object.entries(this.vars); + } +}; +var Env = class extends ReadOnlyEnv { + changed = false; + /** Sets an environment variable. */ + set(name, value) { + this.vars[name] = value; + this.changed = true; + } + /** Gets a value indicating whether `set` was called at least once. */ + hasChanged() { + return this.changed; + } +}; +function getEnv(env = process.env) { + return new Env(env); +} + // src/util.ts var fs = __toESM(require("fs")); var fsPromises = __toESM(require("fs/promises")); @@ -141380,6 +142298,7 @@ function defineScalarTag(tagName, options) { }; } function defineSequenceTag(tagName, options) { + const carrierIsResult = options.finalize === void 0; return { tagName, nodeKind: "sequence", @@ -141387,12 +142306,15 @@ function defineSequenceTag(tagName, options) { matchByTagPrefix: options.matchByTagPrefix ?? false, create: options.create, addItem: options.addItem, + finalize: options.finalize ?? ((carrier) => carrier), + carrierIsResult, identify: options.identify ?? null, represent: options.represent ?? ((data) => data), representTagName: options.representTagName ?? null }; } function defineMappingTag(tagName, options) { + const carrierIsResult = options.finalize === void 0; return { tagName, nodeKind: "mapping", @@ -141403,6 +142325,8 @@ function defineMappingTag(tagName, options) { has: options.has, keys: options.keys, get: options.get, + finalize: options.finalize ?? ((carrier) => carrier), + carrierIsResult, identify: options.identify ?? null, represent: options.represent ?? ((data) => data), representTagName: options.representTagName ?? null @@ -141431,7 +142355,7 @@ var nullCoreTag = defineScalarTag("tag:yaml.org,2002:null", { if (NULL_VALUES$1.indexOf(source) !== -1) return null; return NOT_RESOLVED; }, - identify: (object) => object === null, + identify: (object2) => object2 === null, represent: () => "null" }); var nullJsonTag = defineScalarTag("tag:yaml.org,2002:null", { @@ -141441,7 +142365,7 @@ var nullJsonTag = defineScalarTag("tag:yaml.org,2002:null", { if (source === "null" || isExplicit && source === "") return null; return NOT_RESOLVED; }, - identify: (object) => object === null, + identify: (object2) => object2 === null, represent: () => "null" }); var NULL_VALUES = [ @@ -141463,7 +142387,7 @@ var nullYaml11Tag = defineScalarTag("tag:yaml.org,2002:null", { if (NULL_VALUES.indexOf(source) !== -1) return null; return NOT_RESOLVED; }, - identify: (object) => object === null, + identify: (object2) => object2 === null, represent: () => "null" }); var TRUE_VALUES$2 = [ @@ -141489,8 +142413,8 @@ var boolCoreTag = defineScalarTag("tag:yaml.org,2002:bool", { if (FALSE_VALUES$2.indexOf(source) !== -1) return false; return NOT_RESOLVED; }, - identify: (object) => Object.prototype.toString.call(object) === "[object Boolean]", - represent: (object) => object ? "true" : "false" + identify: (object2) => Object.prototype.toString.call(object2) === "[object Boolean]", + represent: (object2) => object2 ? "true" : "false" }); var TRUE_VALUES$1 = ["true"]; var FALSE_VALUES$1 = ["false"]; @@ -141502,8 +142426,8 @@ var boolJsonTag = defineScalarTag("tag:yaml.org,2002:bool", { if (FALSE_VALUES$1.indexOf(source) !== -1) return false; return NOT_RESOLVED; }, - identify: (object) => Object.prototype.toString.call(object) === "[object Boolean]", - represent: (object) => object ? "true" : "false" + identify: (object2) => Object.prototype.toString.call(object2) === "[object Boolean]", + represent: (object2) => object2 ? "true" : "false" }); var TRUE_VALUES = [ "true", @@ -141550,8 +142474,8 @@ var boolYaml11Tag = defineScalarTag("tag:yaml.org,2002:bool", { if (FALSE_VALUES.indexOf(source) !== -1) return false; return NOT_RESOLVED; }, - identify: (object) => Object.prototype.toString.call(object) === "[object Boolean]", - represent: (object) => object ? "true" : "false" + identify: (object2) => Object.prototype.toString.call(object2) === "[object Boolean]", + represent: (object2) => object2 ? "true" : "false" }); var YAML_INTEGER_IMPLICIT_PATTERN$1 = /* @__PURE__ */ new RegExp("^(?:0o[0-7]+|0x[0-9a-fA-F]+|[-+]?[0-9]+)$"); var YAML_INTEGER_EXPLICIT_PATTERN$1 = /* @__PURE__ */ new RegExp("^(?:[-+]?0b[0-1]+|[-+]?0o[0-7]+|[-+]?0x[0-9a-fA-F]+|[-+]?[0-9]+)$"); @@ -141582,8 +142506,8 @@ var intCoreTag = defineScalarTag("tag:yaml.org,2002:int", { ..."0123456789" ], resolve: resolveYamlInteger$2, - identify: (object) => Object.prototype.toString.call(object) === "[object Number]" && object % 1 === 0 && !Object.is(object, -0), - represent: (object) => object.toString(10) + identify: (object2) => Number.isInteger(object2) && !Object.is(object2, -0) && object2.toString(10).indexOf("e") < 0, + represent: (object2) => object2.toString(10) }); var YAML_INTEGER_IMPLICIT_PATTERN = /* @__PURE__ */ new RegExp("^-?(?:0|[1-9][0-9]*)$"); var YAML_INTEGER_EXPLICIT_PATTERN = /* @__PURE__ */ new RegExp("^(?:[-+]?0b[0-1]+|[-+]?0o[0-7]+|[-+]?0x[0-9a-fA-F]+|[-+]?[0-9]+)$"); @@ -141610,8 +142534,8 @@ var intJsonTag = defineScalarTag("tag:yaml.org,2002:int", { implicit: true, implicitFirstChars: ["-", ..."0123456789"], resolve: resolveYamlInteger$1, - identify: (object) => Object.prototype.toString.call(object) === "[object Number]" && object % 1 === 0 && !Object.is(object, -0), - represent: (object) => object.toString(10) + identify: (object2) => Number.isInteger(object2) && !Object.is(object2, -0) && object2.toString(10).indexOf("e") < 0, + represent: (object2) => object2.toString(10) }); var YAML_INTEGER_PATTERN = /* @__PURE__ */ new RegExp("^(?:[-+]?0b[0-1_]+|[-+]?0[0-7_]+|[-+]?0x[0-9a-fA-F_]+|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+|[-+]?(?:0|[1-9][0-9_]*))$"); function parseYamlInteger(source) { @@ -141644,8 +142568,8 @@ var intYaml11Tag = defineScalarTag("tag:yaml.org,2002:int", { ..."0123456789" ], resolve: resolveYamlInteger, - identify: (object) => Object.prototype.toString.call(object) === "[object Number]" && object % 1 === 0 && !Object.is(object, -0), - represent: (object) => object.toString(10) + identify: (object2) => Number.isInteger(object2) && !Object.is(object2, -0) && object2.toString(10).indexOf("e") < 0, + represent: (object2) => object2.toString(10) }); var YAML_FLOAT_PATTERN$1 = /* @__PURE__ */ new RegExp("^(?:[-+]?[0-9]+(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?|[-+]?\\.[0-9]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"); var YAML_FLOAT_SPECIAL_PATTERN$1 = /* @__PURE__ */ new RegExp("^(?:[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"); @@ -141660,12 +142584,12 @@ function resolveYamlFloat$2(source) { if (Number.isFinite(result) || YAML_FLOAT_SPECIAL_PATTERN$1.test(source)) return result; return NOT_RESOLVED; } -function representYamlFloat$2(object) { - if (isNaN(object)) return ".nan"; - if (object === Number.POSITIVE_INFINITY) return ".inf"; - if (object === Number.NEGATIVE_INFINITY) return "-.inf"; - if (Object.is(object, -0)) return "-0.0"; - const result = object.toString(10); +function representYamlFloat$2(object2) { + if (isNaN(object2)) return ".nan"; + if (object2 === Number.POSITIVE_INFINITY) return ".inf"; + if (object2 === Number.NEGATIVE_INFINITY) return "-.inf"; + if (Object.is(object2, -0)) return "-0.0"; + const result = object2.toString(10); return /^[-+]?[0-9]+e/.test(result) ? result.replace("e", ".e") : result; } var floatCoreTag = defineScalarTag("tag:yaml.org,2002:float", { @@ -141677,7 +142601,7 @@ var floatCoreTag = defineScalarTag("tag:yaml.org,2002:float", { ..."0123456789" ], resolve: resolveYamlFloat$2, - identify: (object) => Object.prototype.toString.call(object) === "[object Number]" && (object % 1 !== 0 || Object.is(object, -0)), + identify: (object2) => typeof object2 === "number" && (!Number.isInteger(object2) || Object.is(object2, -0) || object2.toString(10).indexOf("e") >= 0), represent: representYamlFloat$2 }); var YAML_FLOAT_IMPLICIT_PATTERN = /* @__PURE__ */ new RegExp("^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$"); @@ -141698,19 +142622,19 @@ function resolveYamlFloat$1(source, isExplicit) { if (Number.isFinite(result)) return result; return NOT_RESOLVED; } -function representYamlFloat$1(object) { - if (isNaN(object)) return ".nan"; - if (object === Number.POSITIVE_INFINITY) return ".inf"; - if (object === Number.NEGATIVE_INFINITY) return "-.inf"; - if (Object.is(object, -0)) return "-0.0"; - const result = object.toString(10); +function representYamlFloat$1(object2) { + if (isNaN(object2)) return ".nan"; + if (object2 === Number.POSITIVE_INFINITY) return ".inf"; + if (object2 === Number.NEGATIVE_INFINITY) return "-.inf"; + if (Object.is(object2, -0)) return "-0.0"; + const result = object2.toString(10); return /^[-+]?[0-9]+e/.test(result) ? result.replace("e", ".e") : result; } var floatJsonTag = defineScalarTag("tag:yaml.org,2002:float", { implicit: true, implicitFirstChars: ["-", ..."0123456789"], resolve: resolveYamlFloat$1, - identify: (object) => Object.prototype.toString.call(object) === "[object Number]" && (object % 1 !== 0 || Object.is(object, -0)), + identify: (object2) => typeof object2 === "number" && (!Number.isInteger(object2) || Object.is(object2, -0) || object2.toString(10).indexOf("e") >= 0), represent: representYamlFloat$1 }); var YAML_FLOAT_PATTERN = /* @__PURE__ */ new RegExp("^(?:[-+]?(?:(?:[0-9][0-9_]*)?\\.[0-9_]*)(?:[eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"); @@ -141730,12 +142654,12 @@ function resolveYamlFloat(source) { if (Number.isFinite(result) || YAML_FLOAT_SPECIAL_PATTERN.test(source)) return result; return NOT_RESOLVED; } -function representYamlFloat(object) { - if (isNaN(object)) return ".nan"; - if (object === Number.POSITIVE_INFINITY) return ".inf"; - if (object === Number.NEGATIVE_INFINITY) return "-.inf"; - if (Object.is(object, -0)) return "-0.0"; - const result = object.toString(10); +function representYamlFloat(object2) { + if (isNaN(object2)) return ".nan"; + if (object2 === Number.POSITIVE_INFINITY) return ".inf"; + if (object2 === Number.NEGATIVE_INFINITY) return "-.inf"; + if (Object.is(object2, -0)) return "-0.0"; + const result = object2.toString(10); return /^[-+]?[0-9]+e/.test(result) ? result.replace("e", ".e") : result; } var floatYaml11Tag = defineScalarTag("tag:yaml.org,2002:float", { @@ -141747,7 +142671,7 @@ var floatYaml11Tag = defineScalarTag("tag:yaml.org,2002:float", { ..."0123456789" ], resolve: resolveYamlFloat, - identify: (object) => Object.prototype.toString.call(object) === "[object Number]" && (object % 1 !== 0 || Object.is(object, -0)), + identify: (object2) => typeof object2 === "number" && (!Number.isInteger(object2) || Object.is(object2, -0) || object2.toString(10).indexOf("e") >= 0), represent: representYamlFloat }); var mergeTag = defineScalarTag("tag:yaml.org,2002:merge", { @@ -141767,18 +142691,23 @@ function resolveYamlBinary(source) { for (let index2 = 0; index2 < binary.length; index2++) result[index2] = binary.charCodeAt(index2); return result; } -function representYamlBinary(object) { +function representYamlBinary(object2) { let binary = ""; - for (let index2 = 0; index2 < object.length; index2++) binary += String.fromCharCode(object[index2]); + for (let index2 = 0; index2 < object2.length; index2++) binary += String.fromCharCode(object2[index2]); return btoa(binary); } var binaryTag = defineScalarTag("tag:yaml.org,2002:binary", { resolve: resolveYamlBinary, - identify: (object) => Object.prototype.toString.call(object) === "[object Uint8Array]", + identify: (object2) => Object.prototype.toString.call(object2) === "[object Uint8Array]", represent: representYamlBinary }); var YAML_DATE_REGEXP = /* @__PURE__ */ new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"); var YAML_TIMESTAMP_REGEXP = /* @__PURE__ */ new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$"); +function makeUtcDate(year, month, day, hour = 0, minute = 0, second = 0, fraction = 0) { + const date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction)); + date.setUTCFullYear(year, month, day); + return date; +} function resolveYamlTimestamp(source) { let match2 = YAML_DATE_REGEXP.exec(source); if (match2 === null) match2 = YAML_TIMESTAMP_REGEXP.exec(source); @@ -141787,7 +142716,7 @@ function resolveYamlTimestamp(source) { const month = +match2[2] - 1; const day = +match2[3]; if (!match2[4]) { - const date2 = new Date(Date.UTC(year, month, day)); + const date2 = makeUtcDate(year, month, day); if (date2.getUTCFullYear() !== year || date2.getUTCMonth() !== month || date2.getUTCDate() !== day) return NOT_RESOLVED; return date2; } @@ -141801,7 +142730,7 @@ function resolveYamlTimestamp(source) { while (value.length < 3) value += "0"; fraction = +value; } - const date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction)); + const date = makeUtcDate(year, month, day, hour, minute, second, fraction); if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month || date.getUTCDate() !== day) return NOT_RESOLVED; if (match2[9]) { const offsetHour = +match2[10]; @@ -141816,8 +142745,8 @@ var timestampTag = defineScalarTag("tag:yaml.org,2002:timestamp", { implicit: true, implicitFirstChars: [..."0123456789"], resolve: resolveYamlTimestamp, - identify: (object) => object instanceof Date, - represent: (object) => object.toISOString() + identify: (object2) => object2 instanceof Date, + represent: (object2) => object2.toISOString() }); var seqTag = defineSequenceTag("tag:yaml.org,2002:seq", { create: () => [], @@ -141826,17 +142755,37 @@ var seqTag = defineSequenceTag("tag:yaml.org,2002:seq", { }, identify: Array.isArray }); +function isPlainObject3(data) { + if (data === null || typeof data !== "object" || Array.isArray(data)) return false; + const prototype = Object.getPrototypeOf(data); + return prototype === null || prototype === Object.prototype; +} +function pick(object2, keys) { + const result = {}; + for (const key of keys) if (object2[key] !== void 0) result[key] = object2[key]; + return result; +} var omapTag = defineSequenceTag("tag:yaml.org,2002:omap", { - create: () => [], - addItem: (container, item) => { - if (Object.prototype.toString.call(item) !== "[object Object]") return "cannot resolve an ordered map item"; - const object = item; - const itemKeys = Object.keys(object); - if (itemKeys.length !== 1) return "cannot resolve an ordered map item"; - for (const existing of container) if (Object.prototype.hasOwnProperty.call(existing, itemKeys[0])) return "cannot resolve an ordered map item"; - container.push(object); + create: () => ({ + list: [], + seen: /* @__PURE__ */ new Set() + }), + addItem: (carrier, item) => { + let key; + if (item instanceof Map) { + if (item.size !== 1) return "cannot resolve an ordered map item"; + key = item.keys().next().value; + } else if (isPlainObject3(item)) { + const itemKeys = Object.keys(item); + if (itemKeys.length !== 1) return "cannot resolve an ordered map item"; + key = itemKeys[0]; + } else return "cannot resolve an ordered map item"; + if (carrier.seen.has(key)) return "duplicate key in ordered map"; + carrier.seen.add(key); + carrier.list.push(item); return ""; - } + }, + finalize: (carrier) => carrier.list }); var pairsTag = defineSequenceTag("tag:yaml.org,2002:pairs", { create: () => [], @@ -141847,23 +142796,13 @@ var pairsTag = defineSequenceTag("tag:yaml.org,2002:pairs", { return ""; } if (Object.prototype.toString.call(item) !== "[object Object]") return "cannot resolve a pairs item"; - const object = item; - const keys = Object.keys(object); + const object2 = item; + const keys = Object.keys(object2); if (keys.length !== 1) return "cannot resolve a pairs item"; - container.push([keys[0], object[keys[0]]]); + container.push([keys[0], object2[keys[0]]]); return ""; } }); -function isPlainObject3(data) { - if (data === null || typeof data !== "object" || Array.isArray(data)) return false; - const prototype = Object.getPrototypeOf(data); - return prototype === null || prototype === Object.prototype; -} -function pick(object, keys) { - const result = {}; - for (const key of keys) if (object[key] !== void 0) result[key] = object[key]; - return result; -} var mapTag = defineMappingTag("tag:yaml.org,2002:map", { create: () => ({}), identify: isPlainObject3, @@ -141889,7 +142828,11 @@ var mapTag = defineMappingTag("tag:yaml.org,2002:map", { return Object.prototype.hasOwnProperty.call(container, String(key)); }, keys: (container) => Object.keys(container), - get: (container, key) => container[String(key)] + get: (container, key) => { + const normalizedKey = String(key); + if (!Object.prototype.hasOwnProperty.call(container, normalizedKey)) return null; + return container[normalizedKey]; + } }); var setTag = defineMappingTag("tag:yaml.org,2002:set", { create: () => /* @__PURE__ */ new Set(), @@ -141910,9 +142853,9 @@ var setTag = defineMappingTag("tag:yaml.org,2002:set", { }); function createTagDefinitionMap() { return { - scalar: {}, - sequence: {}, - mapping: {} + scalar: /* @__PURE__ */ Object.create(null), + sequence: /* @__PURE__ */ Object.create(null), + mapping: /* @__PURE__ */ Object.create(null) }; } function createTagDefinitionListMap() { @@ -142047,12 +142990,12 @@ var realMapTag = defineMappingTag("tag:yaml.org,2002:map", { }); function normalizeKey(key) { if (Array.isArray(key)) { - const array = Array.prototype.slice.call(key); - for (let index2 = 0; index2 < array.length; index2++) { - if (Array.isArray(array[index2])) return null; - if (typeof array[index2] === "object" && Object.prototype.toString.call(array[index2]) === "[object Object]") array[index2] = "[object Object]"; + const array2 = Array.prototype.slice.call(key); + for (let index2 = 0; index2 < array2.length; index2++) { + if (Array.isArray(array2[index2])) return null; + if (typeof array2[index2] === "object" && Object.prototype.toString.call(array2[index2]) === "[object Object]") array2[index2] = "[object Object]"; } - return String(array); + return String(array2); } if (typeof key === "object" && Object.prototype.toString.call(key) === "[object Object]") return "[object Object]"; return String(key); @@ -142082,7 +143025,11 @@ var legacyMapTag = defineMappingTag("tag:yaml.org,2002:map", { return normalizedKey !== null && Object.prototype.hasOwnProperty.call(container, normalizedKey); }, keys: (container) => Object.keys(container), - get: (container, key) => container[String(key)] + get: (container, key) => { + const normalizedKey = String(key); + if (!Object.prototype.hasOwnProperty.call(container, normalizedKey)) return null; + return container[normalizedKey]; + } }); var DEFAULT_SNIPPET_OPTIONS = { maxLength: 79, @@ -142418,10 +143365,10 @@ function getScalarValue(input, scalar) { return getPlainValue(input, valueStart, valueEnd); } } -var DEFAULT_TAG_HANDLERS = { +var DEFAULT_TAG_HANDLERS = Object.assign(/* @__PURE__ */ Object.create(null), { "!": "!", "!!": "tag:yaml.org,2002:" -}; +}); function tagPercentEncode(source) { return encodeURI(source).replace(/!/g, "%21"); } @@ -142446,7 +143393,8 @@ var DEFAULT_CONSTRUCTOR_OPTIONS = { filename: "", schema: CORE_SCHEMA, json: false, - maxMergeSeqLength: 20 + maxTotalMergeKeys: 1e4, + maxAliases: -1 }; function eventPosition$1(event) { if ("tagStart" in event && event.tagStart !== NO_RANGE$2) return event.tagStart; @@ -142458,6 +143406,14 @@ function eventPosition$1(event) { function throwError$1(state, message) { throwErrorAt(state.source, state.position, message, state.filename); } +function finalizeCollection(state, position, tag, carrier) { + try { + return tag.finalize(carrier); + } catch (error3) { + if (error3 instanceof YAMLException) throw error3; + throwErrorAt(state.source, position, error3 instanceof Error ? error3.message : String(error3), state.filename); + } +} function lookupTag(exact, prefix, tagName) { const exactTag = exact[tagName]; if (exactTag) return exactTag; @@ -142490,8 +143446,9 @@ function constructScalar(state, event) { const collectionTagDef = lookupTag(state.schema.exact.mapping, state.schema.prefix.mapping, tagName) ?? lookupTag(state.schema.exact.sequence, state.schema.prefix.sequence, tagName); if (collectionTagDef) { if (source !== "") throwError$1(state, `cannot resolve a node with !<${tagName}> explicit tag`); + const carrier = collectionTagDef.create(tagName); return { - value: collectionTagDef.create(tagName), + value: collectionTagDef.carrierIsResult ? carrier : finalizeCollection(state, state.position, collectionTagDef, carrier), tag: collectionTagDef }; } @@ -142525,6 +143482,7 @@ function isMappingTag(tag) { } function mergeKeys(state, frame, source, sourceTag) { for (const sourceKey of sourceTag.keys(source)) { + if (state.maxTotalMergeKeys !== -1 && ++state.totalMergeKeys > state.maxTotalMergeKeys) throwError$1(state, `merge keys exceeded maxTotalMergeKeys (${state.maxTotalMergeKeys})`); if (frame.tag.has(frame.value, sourceKey)) continue; const err = frame.tag.addPair(frame.value, sourceKey, sourceTag.get(source, sourceKey)); if (err) throwError$1(state, err); @@ -142534,14 +143492,8 @@ function mergeKeys(state, frame, source, sourceTag) { function mergeSource(state, frame, source, sourceTag) { state.position = frame.keyPosition; if (isMappingTag(sourceTag)) mergeKeys(state, frame, source, sourceTag); - else if (sourceTag.nodeKind === "sequence" && Array.isArray(source)) { - const seen = /* @__PURE__ */ new Set(); - for (const element of source) { - if (seen.has(element)) continue; - seen.add(element); - mergeKeys(state, frame, element, frame.tag); - } - } else throwError$1(state, "cannot merge mappings; the provided source object is unacceptable"); + else if (sourceTag.nodeKind === "sequence" && Array.isArray(source)) for (const element of source) mergeKeys(state, frame, element, frame.tag); + else throwError$1(state, "cannot merge mappings; the provided source object is unacceptable"); } function addMappingValue(state, frame, key, value, tag) { state.position = frame.keyPosition; @@ -142562,7 +143514,6 @@ function addValue(state, value, tag) { } else if (frame.kind === "sequence") { if (frame.merge) { if (!isMappingTag(tag)) throwError$1(state, "cannot merge mappings; the provided source object is unacceptable"); - if (frame.index >= state.maxMergeSeqLength) throwError$1(state, `merge sequence length exceeded maxMergeSeqLength (${state.maxMergeSeqLength})`); } const err = frame.tag.addItem(frame.value, value, frame.index++); if (err) throwError$1(state, err); @@ -142577,11 +143528,17 @@ function addValue(state, value, tag) { frame.hasKey = true; } } -function storeAnchor(state, event, value, tag) { - if (event.anchorStart !== NO_RANGE$2) state.anchors.set(state.source.slice(event.anchorStart, event.anchorEnd), { - value, - tag - }); +function storeAnchor(state, event, value, tag, isValueFinal) { + if (event.anchorStart !== NO_RANGE$2) { + const anchor = { + value, + tag, + isValueFinal + }; + state.anchors.set(state.source.slice(event.anchorStart, event.anchorEnd), anchor); + return anchor; + } + return null; } function constructFromEvents(events, options) { const state = { @@ -142593,7 +143550,9 @@ function constructFromEvents(events, options) { position: 0, frames: [], anchors: /* @__PURE__ */ new Map(), - tagHandlers: /* @__PURE__ */ Object.create(null) + tagHandlers: /* @__PURE__ */ Object.create(null), + totalMergeKeys: 0, + aliasCount: 0 }; while (state.eventIndex < state.events.length) { const event = state.events[state.eventIndex++]; @@ -142601,6 +143560,7 @@ function constructFromEvents(events, options) { switch (event.type) { case 1: state.anchors = /* @__PURE__ */ new Map(); + state.aliasCount = 0; state.tagHandlers = /* @__PURE__ */ Object.create(null); for (const directive of event.directives) if (directive.kind === "tag") state.tagHandlers[directive.handle] = directive.prefix; state.frames.push({ @@ -142612,14 +143572,14 @@ function constructFromEvents(events, options) { break; case 4: { const { value, tag } = constructScalar(state, event); - storeAnchor(state, event, value, tag); + storeAnchor(state, event, value, tag, true); addValue(state, value, tag); break; } case 2: { const definition = collectionTag(state, event, state.schema.exact.sequence, state.schema.prefix.sequence, "tag:yaml.org,2002:seq", "sequence"); const value = definition.tag.create(definition.tagName); - storeAnchor(state, event, value, definition.tag); + const anchor = storeAnchor(state, event, value, definition.tag, definition.tag.carrierIsResult); const parent = state.frames[state.frames.length - 1]; const merge2 = parent !== void 0 && parent.kind === "mapping" && parent.hasKey && parent.key === MERGE_KEY; state.frames.push({ @@ -142627,6 +143587,7 @@ function constructFromEvents(events, options) { position: state.position, value, tag: definition.tag, + anchor, index: 0, merge: merge2 }); @@ -142635,12 +143596,13 @@ function constructFromEvents(events, options) { case 3: { const definition = collectionTag(state, event, state.schema.exact.mapping, state.schema.prefix.mapping, "tag:yaml.org,2002:map", "mapping"); const value = definition.tag.create(definition.tagName); - storeAnchor(state, event, value, definition.tag); + const anchor = storeAnchor(state, event, value, definition.tag, definition.tag.carrierIsResult); state.frames.push({ kind: "mapping", position: state.position, value, tag: definition.tag, + anchor, key: void 0, keyPosition: state.position, hasKey: false, @@ -142649,16 +143611,29 @@ function constructFromEvents(events, options) { break; } case 5: { + if (state.maxAliases !== -1 && ++state.aliasCount > state.maxAliases) throwError$1(state, `aliases exceeded maxAliases (${state.maxAliases})`); const name = state.source.slice(event.anchorStart, event.anchorEnd); const anchor = state.anchors.get(name); if (!anchor) throwError$1(state, `unidentified alias "${name}"`); + if (!anchor.isValueFinal) throwError$1(state, `recursive alias "${name}" is not supported for tag ${anchor.tag.tagName} because it uses finalize()`); addValue(state, anchor.value, anchor.tag); break; } case 6: { const frame = state.frames.pop(); + if (frame.kind === "mapping" && frame.hasKey) { + state.position = frame.keyPosition; + throwError$1(state, "incomplete mapping pair in event stream"); + } if (frame.kind === "document") state.documents.push(frame.value); - else addValue(state, frame.value, frame.tag); + else { + const value = frame.tag.carrierIsResult ? frame.value : finalizeCollection(state, frame.position, frame.tag, frame.value); + if (frame.anchor) { + frame.anchor.value = value; + frame.anchor.isValueFinal = true; + } + addValue(state, value, frame.tag); + } break; } } @@ -142713,6 +143688,17 @@ function addMappingEvent(state, start, anchorStart, anchorEnd, tagStart, tagEnd, style }); } +function insertFlowPairMappingEvent(state, snapshot) { + state.events.splice(snapshot.eventsLength, 0, { + type: 3, + start: snapshot.position, + anchorStart: NO_RANGE$1, + anchorEnd: NO_RANGE$1, + tagStart: NO_RANGE$1, + tagEnd: NO_RANGE$1, + style: 2 + }); +} function addScalarEvent(state, valueStart, valueEnd, anchorStart, anchorEnd, tagStart, tagEnd, style, chomping = 1, indent = -1, fast = false) { state.events.push({ type: 4, @@ -143156,12 +144142,8 @@ function readFlowCollection(state, nodeIndent, props) { state.position++; skipFlowSeparationSpace(state, nodeIndent); if (!isMapping) { - restoreState(state, entryStart); - addMappingEvent(state, entryStart.position, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, 2); - if (!parseNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true)) addEmptyScalarEvent(state); - skipFlowSeparationSpace(state, nodeIndent); - state.position++; - skipFlowSeparationSpace(state, nodeIndent); + insertFlowPairMappingEvent(state, entryStart); + if (!keyWasRead) addEmptyScalarEvent(state); } else if (!keyWasRead) addEmptyScalarEvent(state); if (!parseNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true)) addEmptyScalarEvent(state); skipFlowSeparationSpace(state, nodeIndent); @@ -143171,9 +144153,8 @@ function readFlowCollection(state, nodeIndent, props) { addEmptyScalarEvent(state); } else if (isMapping) addEmptyScalarEvent(state); else if (isPair) { - restoreState(state, entryStart); - addMappingEvent(state, entryStart.position, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, 2); - parseNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); + insertFlowPairMappingEvent(state, entryStart); + if (!keyWasRead) addEmptyScalarEvent(state); addEmptyScalarEvent(state); addPopEvent(state); } @@ -143314,10 +144295,6 @@ function parseNode(state, parentIndent, nodeContext, allowToSeek, allowCompact, else if (state.lineIndent === parentIndent) indentStatus = 0; else indentStatus = -1; } - if (state.position === state.lineStart && testDocumentSeparator(state)) { - state.depth--; - return false; - } if (indentStatus === 1) while (true) { const ch = state.input.charCodeAt(state.position); const propertyState = snapshotState(state); @@ -143537,12 +144514,12 @@ function buildRepresentTypes(schema) { })) ]; } -function matchTag(state, object) { +function matchTag(state, object2) { for (let index2 = 0, length = state.representTypes.length; index2 < length; index2 += 1) { const { tag, implicitTag } = state.representTypes[index2]; - if (tag.identify && tag.identify(object)) { + if (tag.identify && tag.identify(object2)) { let tagName; - if (tag.matchByTagPrefix && tag.representTagName) tagName = tag.representTagName(object); + if (tag.matchByTagPrefix && tag.representTagName) tagName = tag.representTagName(object2); else tagName = tag.tagName; return { tag, @@ -143553,9 +144530,9 @@ function matchTag(state, object) { } return null; } -function build(state, object) { - if (!state.noRefs && object !== null && typeof object === "object") { - const existing = state.refs.get(object); +function build(state, object2) { + if (!state.noRefs && object2 !== null && typeof object2 === "object") { + const existing = state.refs.get(object2); if (existing) { if (existing.anchor === void 0) existing.anchor = `ref_${state.refCounter++}`; return { @@ -143566,11 +144543,11 @@ function build(state, object) { }; } } - const matched = matchTag(state, object); + const matched = matchTag(state, object2); if (!matched) { - if (object === void 0) return INVALID; + if (object2 === void 0) return INVALID; if (state.skipInvalid) return INVALID; - throw new YAMLException(`unacceptable kind of an object to dump ${Object.prototype.toString.call(object)}`); + throw new YAMLException(`unacceptable kind of an object to dump ${Object.prototype.toString.call(object2)}`); } const { tag, tagName, implicitTag } = matched; const nodeTagName = implicitTag ? tagName : tagNameShort(tagName); @@ -143581,11 +144558,11 @@ function build(state, object) { kind: "scalar", tag: nodeTagName, style: style2, - value: tag.represent(object) + value: tag.represent(object2) }; } if (tag.nodeKind === "sequence") { - const container = tag.represent(object); + const container = tag.represent(object2); const style2 = new Style(); style2.tagged = !implicitTag; const node2 = { @@ -143594,7 +144571,7 @@ function build(state, object) { style: style2, items: [] }; - if (!state.noRefs) state.refs.set(object, node2); + if (!state.noRefs) state.refs.set(object2, node2); for (let index2 = 0, length = container.length; index2 < length; index2 += 1) { let item = build(state, container[index2]); if (item === INVALID && container[index2] === void 0) item = build(state, null); @@ -143603,7 +144580,7 @@ function build(state, object) { } return node2; } - const map = tag.represent(object); + const map = tag.represent(object2); const style = new Style(); style.tagged = !implicitTag; const node = { @@ -143612,7 +144589,7 @@ function build(state, object) { style, items: [] }; - if (!state.noRefs) state.refs.set(object, node); + if (!state.noRefs) state.refs.set(object2, node); for (const [objectKey, objectValue] of map) { const key = build(state, objectKey); if (key === INVALID) continue; @@ -143728,7 +144705,8 @@ var DEFAULT_PRESENTER_OPTIONS = { flowSkipCommaSpace: false, flowSkipColonSpace: false, quoteFlowKeys: false, - quoteStyle: "auto", + quoteStyle: "single", + forceQuotes: false, tagBeforeAnchor: false }; function nodeTagShort(node) { @@ -143809,7 +144787,7 @@ function isNsCharOrWhitespace(c) { function isPlainSafe(c, prev, inblock) { const cIsNsCharOrWhitespace = isNsCharOrWhitespace(c); const cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c); - return (inblock ? cIsNsCharOrWhitespace : cIsNsCharOrWhitespace && c !== CHAR_COMMA && c !== CHAR_LEFT_SQUARE_BRACKET && c !== CHAR_RIGHT_SQUARE_BRACKET && c !== CHAR_LEFT_CURLY_BRACKET && c !== CHAR_RIGHT_CURLY_BRACKET) && c !== CHAR_SHARP && !(prev === CHAR_COLON && !cIsNsChar) || isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP || prev === CHAR_COLON && cIsNsChar; + return (inblock ? cIsNsCharOrWhitespace : cIsNsCharOrWhitespace && c !== CHAR_COMMA && c !== CHAR_LEFT_SQUARE_BRACKET && c !== CHAR_RIGHT_SQUARE_BRACKET && c !== CHAR_LEFT_CURLY_BRACKET && c !== CHAR_RIGHT_CURLY_BRACKET) && c !== CHAR_SHARP && !(prev === CHAR_COLON && !cIsNsChar) || isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP || prev === CHAR_COLON && cIsNsChar && (inblock || c !== CHAR_COMMA && c !== CHAR_LEFT_SQUARE_BRACKET && c !== CHAR_RIGHT_SQUARE_BRACKET && c !== CHAR_LEFT_CURLY_BRACKET && c !== CHAR_RIGHT_CURLY_BRACKET); } function isPlainSafeFirst(c) { return isPrintable(c) && c !== CHAR_BOM && !isWhitespace(c) && c !== CHAR_MINUS && c !== CHAR_QUESTION && c !== CHAR_COLON && c !== CHAR_COMMA && c !== CHAR_LEFT_SQUARE_BRACKET && c !== CHAR_RIGHT_SQUARE_BRACKET && c !== CHAR_LEFT_CURLY_BRACKET && c !== CHAR_RIGHT_CURLY_BRACKET && c !== CHAR_SHARP && c !== CHAR_AMPERSAND && c !== CHAR_ASTERISK && c !== CHAR_EXCLAMATION && c !== CHAR_VERTICAL_LINE && c !== CHAR_EQUALS && c !== CHAR_GREATER_THAN && c !== CHAR_SINGLE_QUOTE && c !== CHAR_DOUBLE_QUOTE && c !== CHAR_PERCENT && c !== CHAR_COMMERCIAL_AT && c !== CHAR_GRAVE_ACCENT; @@ -143843,9 +144821,8 @@ var STYLE_SINGLE = 2; var STYLE_LITERAL = 3; var STYLE_FOLDED = 4; var STYLE_DOUBLE = 5; -function chooseScalarStyle(state, string2, layout, singleLineOnly, inblock) { +function chooseScalarStyle(state, string2, layout, singleLineOnly, forceQuote, inblock) { const { blockIndent, lineWidth } = layout; - const forceQuote = state.quoteStyle !== "auto"; let i; let char = 0; let prevChar = -1; @@ -143866,14 +144843,14 @@ function chooseScalarStyle(state, string2, layout, singleLineOnly, inblock) { if (char === CHAR_LINE_FEED) { hasLineBreak = true; if (shouldTrackWidth) { - hasFoldableLine = hasFoldableLine || i - previousLineBreak - 1 > lineWidth && string2[previousLineBreak + 1] !== " "; + hasFoldableLine = hasFoldableLine || i - previousLineBreak - 1 > lineWidth && !isMoreIndented(string2[previousLineBreak + 1]); previousLineBreak = i; } } else if (!isPrintable(char)) return STYLE_DOUBLE; plain = plain && isPlainSafe(char, prevChar, inblock); prevChar = char; } - hasFoldableLine = hasFoldableLine || shouldTrackWidth && i - previousLineBreak - 1 > lineWidth && string2[previousLineBreak + 1] !== " "; + hasFoldableLine = hasFoldableLine || shouldTrackWidth && i - previousLineBreak - 1 > lineWidth && !isMoreIndented(string2[previousLineBreak + 1]); } if (!hasLineBreak && !hasFoldableLine) { if (plain && !forceQuote) return STYLE_PLAIN; @@ -143907,11 +144884,11 @@ function resolveScalarStyle(state, node, layout, iskey, inblock) { } const string2 = node.value; if (string2.length === 0) { - if (state.quoteStyle === "auto" && (node.style.tagged || resolveImplicitTag(state, string2) === node.tag)) return STYLE_PLAIN; + if (node.style.tagged || resolveImplicitTag(state, string2) === node.tag) return STYLE_PLAIN; return state.quoteStyle === "double" ? STYLE_DOUBLE : STYLE_SINGLE; } - const style = chooseScalarStyle(state, string2, layout, singleLineOnly, inblock); - if (style === STYLE_PLAIN && !node.style.tagged && resolveImplicitTag(state, string2) !== node.tag) return STYLE_SINGLE; + const style = chooseScalarStyle(state, string2, layout, singleLineOnly, state.forceQuotes && !iskey, inblock); + if (style === STYLE_PLAIN && !node.style.tagged && resolveImplicitTag(state, string2) !== node.tag) return state.quoteStyle === "double" ? STYLE_DOUBLE : STYLE_SINGLE; return style; } function blockHeader(string2, indentPerLevel) { @@ -143938,27 +144915,30 @@ function encodeFlowBreaks(string2, indent) { function dropEndingNewline(string2) { return string2[string2.length - 1] === "\n" ? string2.slice(0, -1) : string2; } +function isMoreIndented(char) { + return char === " " || char === " "; +} function foldBlockScalar(string2, width) { const lineRe = /(\n+)([^\n]*)/g; let nextLF = string2.indexOf("\n"); if (nextLF === -1) nextLF = string2.length; lineRe.lastIndex = nextLF; let result = foldLine(string2.slice(0, nextLF), width); - let prevMoreIndented = string2[0] === "\n" || string2[0] === " "; + let prevMoreIndented = string2[0] === "\n" || isMoreIndented(string2[0]); let moreIndented; let match2; while (match2 = lineRe.exec(string2)) { const prefix = match2[1]; const line = match2[2]; - moreIndented = line[0] === " "; + moreIndented = line !== "" && isMoreIndented(line[0]); result += prefix + (!prevMoreIndented && !moreIndented && line !== "" ? "\n" : "") + foldLine(line, width); prevMoreIndented = moreIndented; } return result; } function foldLine(line, width) { - if (line === "" || line[0] === " ") return line; - const breakRe = / [^ ]/g; + if (line === "" || isMoreIndented(line[0])) return line; + const breakRe = / [^ \t]/g; let match2; let start = 0; let end; @@ -144031,7 +145011,7 @@ function writeFlowMapping(state, level, node) { for (const { key, value } of items) { let pairBuffer = ""; if (result !== "") pairBuffer += `,${!state.flowSkipCommaSpace ? " " : ""}`; - const keyText = writeNode(state, level, key, {}); + const keyText = writeNode(state, level, key, { iskey: true }); const explicitPair = keyText.length > 1024; if (explicitPair) pairBuffer += "? "; else if (state.quoteFlowKeys) pairBuffer += '"'; @@ -144240,7 +145220,7 @@ var semver = __toESM(require_semver2()); // src/api-compatibility.json var maximumVersion = "3.22"; -var minimumVersion = "3.16"; +var minimumVersion = "3.17"; // src/json/index.ts function parseString(data) { @@ -144255,35 +145235,173 @@ function isArray(value) { function isString(value) { return typeof value === "string"; } +function isNumber(value) { + return typeof value === "number"; +} +function isBoolean(value) { + return typeof value === "boolean"; +} function isStringOrUndefined(value) { return value === void 0 || isString(value); } -var string = { - validate: isString, - required: true -}; -function optional(validator) { +function defaultCheck(validate2) { + return (arg) => ({ unknownKeys: [], invalidKeys: [], valid: validate2(arg) }); +} +function makeValidator(validate2) { + return { + validate: validate2, + check: defaultCheck(validate2), + required: true + }; +} +var string = makeValidator(isString); +var number = makeValidator(isNumber); +var boolean = makeValidator(isBoolean); +function array(validator) { + const validate2 = (val) => { + return isArray(val) && val.every((e) => validator.validate(e)); + }; + return { + validate: validate2, + check: (val, opts, path29) => { + const result = successfulCheckSchema(); + if (!isArray(val)) { + result.valid = false; + return result; + } + let index2 = 0; + for (const e of val) { + const elementPath = `${path29}[${index2}]`; + const eResult = validator.check(e, opts, `${elementPath}`); + result.invalidKeys.push(...eResult.invalidKeys); + result.unknownKeys.push(...eResult.unknownKeys); + index2++; + if (!eResult.valid) { + result.valid = false; + if (eResult.invalidKeys.length === 0) { + result.invalidKeys.push(elementPath); + } + if (opts.failFast) { + return result; + } + continue; + } + } + return result; + }, + required: true + }; +} +function object(schema) { + return { + validate: (val) => { + return isObject(val) && validateSchema(schema, val); + }, + check: (val, opts, path29) => { + if (!isObject(val)) { + return invalidCheckSchema(); + } + return checkSchema(schema, val, opts, path29); + }, + required: true + }; +} +function optionalOrNull(validator) { return { validate: (val) => { return val === void 0 || val === null || validator.validate(val); }, + check: (val, opts, path29) => { + if (val === void 0 || val === null) { + return successfulCheckSchema(); + } + return validator.check(val, opts, path29); + }, + required: false + }; +} +function optional(validator) { + return { + validate: (val) => { + return val === void 0 || validator.validate(val); + }, + check: (val, opts, path29) => { + if (val === void 0) { + return successfulCheckSchema(); + } + return validator.check(val, opts, path29); + }, required: false }; } function validateSchema(schema, obj) { + const result = checkSchema(schema, obj, { failFast: true }); + return result.valid; +} +function validateArray(elementSchema, arr) { + const elementValidator = object(elementSchema); + return array(elementValidator).validate(arr); +} +function successfulCheckSchema() { + return { + valid: true, + unknownKeys: [], + invalidKeys: [] + }; +} +function invalidCheckSchema() { + return { + valid: false, + unknownKeys: [], + invalidKeys: [] + }; +} +function checkSchema(schema, obj, options = {}, path29 = "") { + const result = successfulCheckSchema(); + const inputKeys = new Set(Object.keys(obj)); + const invalidKeys = /* @__PURE__ */ new Set(); for (const [key, validator] of Object.entries(schema)) { const hasKey = key in obj; + inputKeys.delete(key); + invalidKeys.add(key); if (validator.required && !hasKey) { - return false; + result.valid = false; + if (options.failFast) { + break; + } + continue; } if (validator.required && (obj[key] === void 0 || obj[key] === null)) { - return false; + result.valid = false; + if (options.failFast) { + break; + } + continue; } - if (hasKey && !validator.validate(obj[key])) { - return false; + if (hasKey) { + const checkResult = validator.check(obj[key], options, `${path29}.${key}`); + result.unknownKeys.push(...checkResult.unknownKeys); + result.invalidKeys.push(...checkResult.invalidKeys); + if (checkResult.invalidKeys.length > 0) { + invalidKeys.delete(key); + } + if (!checkResult.valid) { + result.valid = false; + if (options.failFast) { + break; + } + continue; + } } + invalidKeys.delete(key); } - return true; + for (const remainingKey of inputKeys) { + result.unknownKeys.push(`${path29}.${remainingKey}`); + } + for (const invalidKey of invalidKeys) { + result.invalidKeys.push(`${path29}.${invalidKey}`); + } + return result; } // src/util.ts @@ -144583,20 +145701,6 @@ function initializeEnvironment(version) { core2.exportVariable("CODEQL_ACTION_FEATURE_WILL_UPLOAD" /* FEATURE_WILL_UPLOAD */, "true"); core2.exportVariable("CODEQL_ACTION_VERSION" /* VERSION */, version); } -function getRequiredEnvParam(paramName) { - const value = process.env[paramName]; - if (value === void 0 || value.length === 0) { - throw new Error(`${paramName} environment variable must be set`); - } - return value; -} -function getOptionalEnvVar(paramName) { - const value = process.env[paramName]; - if (value?.trim().length === 0) { - return void 0; - } - return value; -} var HTTPError = class extends Error { status; constructor(message, status) { @@ -144877,28 +145981,28 @@ async function isBinaryAccessible(binary, logger) { return false; } } -async function asyncFilter(array, predicate) { - const results = await Promise.all(array.map(predicate)); - return array.filter((_2, index2) => results[index2]); +async function asyncFilter(array2, predicate) { + const results = await Promise.all(array2.map(predicate)); + return array2.filter((_2, index2) => results[index2]); } -async function asyncSome(array, predicate) { - const results = await Promise.all(array.map(predicate)); +async function asyncSome(array2, predicate) { + const results = await Promise.all(array2.map(predicate)); return results.some((result) => result); } function isDefined2(value) { return value !== void 0 && value !== null; } -function unsafeEntriesInvariant(object) { - return Object.entries(object).filter( +function unsafeEntriesInvariant(object2) { + return Object.entries(object2).filter( ([_2, val]) => val !== void 0 ); } -function joinAtMost(array, separator, limit) { - if (limit > 0 && array.length > limit) { - array = array.slice(0, limit); - array.push("..."); +function joinAtMost(array2, separator, limit) { + if (limit > 0 && array2.length > limit) { + array2 = array2.slice(0, limit); + array2.push("..."); } - return array.join(separator); + return array2.join(separator); } var Success = class { constructor(value) { @@ -144933,7 +146037,11 @@ var Failure = class { // src/actions-util.ts function getActionsEnv() { - return { getOptionalInput }; + return { + getRequiredInput, + getOptionalInput, + exportVariable: core3.exportVariable + }; } var getRequiredInput = function(name) { const value = core3.getInput(name); @@ -144946,31 +146054,30 @@ var getOptionalInput = function(name) { const value = core3.getInput(name); return value.length > 0 ? value : void 0; }; -function getTemporaryDirectory() { - const value = process.env["CODEQL_ACTION_TEMP"]; - return value !== void 0 && value !== "" ? value : getRequiredEnvParam("RUNNER_TEMP"); +function getTemporaryDirectory(env = getEnv()) { + return env.getOptional("CODEQL_ACTION_TEMP" /* TEMP */) ?? env.getRequired("RUNNER_TEMP" /* RUNNER_TEMP */); } var PR_DIFF_RANGE_JSON_FILENAME = "pr-diff-range.json"; -function getDiffRangesJsonFilePath() { - return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); +function getDiffRangesJsonFilePath(env = getEnv()) { + return path2.join(getTemporaryDirectory(env), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.36.3"; + return "4.37.7"; } -function getWorkflowEventName() { - return getRequiredEnvParam("GITHUB_EVENT_NAME"); +function getWorkflowEventName(env = getEnv()) { + return env.getRequired("GITHUB_EVENT_NAME" /* GITHUB_EVENT_NAME */); } -function isRunningLocalAction() { - const relativeScriptPath = getRelativeScriptPath(); +function isRunningLocalAction(env = getEnv()) { + const relativeScriptPath = getRelativeScriptPath(env); return relativeScriptPath.startsWith("..") || path2.isAbsolute(relativeScriptPath); } -function getRelativeScriptPath() { - const runnerTemp = getRequiredEnvParam("RUNNER_TEMP"); +function getRelativeScriptPath(env) { + const runnerTemp = env.getRequired("RUNNER_TEMP" /* RUNNER_TEMP */); const actionsDirectory = path2.join(path2.dirname(runnerTemp), "_actions"); return path2.relative(actionsDirectory, __filename); } -function getWorkflowEvent() { - const eventJsonFile = getRequiredEnvParam("GITHUB_EVENT_PATH"); +function getWorkflowEvent(env = getEnv()) { + const eventJsonFile = env.getRequired("GITHUB_EVENT_PATH" /* GITHUB_EVENT_PATH */); try { return JSON.parse(fs2.readFileSync(eventJsonFile, "utf-8")); } catch (e) { @@ -145026,32 +146133,34 @@ function getUploadValue(input) { return "always"; } } -function getWorkflowRunID() { - const workflowRunIdString = getRequiredEnvParam("GITHUB_RUN_ID"); +function getWorkflowRunID(env = getEnv()) { + const workflowRunIdString = env.getRequired("GITHUB_RUN_ID" /* GITHUB_RUN_ID */); const workflowRunID = parseInt(workflowRunIdString, 10); if (Number.isNaN(workflowRunID)) { throw new Error( - `GITHUB_RUN_ID must define a non NaN workflow run ID. Current value is ${workflowRunIdString}` + `${"GITHUB_RUN_ID" /* GITHUB_RUN_ID */} must define a non NaN workflow run ID. Current value is ${workflowRunIdString}` ); } if (workflowRunID < 0) { throw new Error( - `GITHUB_RUN_ID must be a non-negative integer. Current value is ${workflowRunIdString}` + `${"GITHUB_RUN_ID" /* GITHUB_RUN_ID */} must be a non-negative integer. Current value is ${workflowRunIdString}` ); } return workflowRunID; } -function getWorkflowRunAttempt() { - const workflowRunAttemptString = getRequiredEnvParam("GITHUB_RUN_ATTEMPT"); +function getWorkflowRunAttempt(env = getEnv()) { + const workflowRunAttemptString = env.getRequired( + "GITHUB_RUN_ATTEMPT" /* GITHUB_RUN_ATTEMPT */ + ); const workflowRunAttempt = parseInt(workflowRunAttemptString, 10); if (Number.isNaN(workflowRunAttempt)) { throw new Error( - `GITHUB_RUN_ATTEMPT must define a non NaN workflow run attempt. Current value is ${workflowRunAttemptString}` + `${"GITHUB_RUN_ATTEMPT" /* GITHUB_RUN_ATTEMPT */} must define a non NaN workflow run attempt. Current value is ${workflowRunAttemptString}` ); } if (workflowRunAttempt <= 0) { throw new Error( - `GITHUB_RUN_ATTEMPT must be a positive integer. Current value is ${workflowRunAttemptString}` + `${"GITHUB_RUN_ATTEMPT" /* GITHUB_RUN_ATTEMPT */} must be a positive integer. Current value is ${workflowRunAttemptString}` ); } return workflowRunAttempt; @@ -145093,14 +146202,14 @@ var getFileType = async (filePath) => { throw e; } }; -function isSelfHostedRunner() { - return process.env.RUNNER_ENVIRONMENT === "self-hosted"; +function isSelfHostedRunner(env = getEnv()) { + return env.getOptional("RUNNER_ENVIRONMENT" /* RUNNER_ENVIRONMENT */) === "self-hosted"; } -function isDynamicWorkflow() { - return getWorkflowEventName() === "dynamic"; +function isDynamicWorkflow(env = getEnv()) { + return getWorkflowEventName(env) === "dynamic"; } -function isDefaultSetup() { - return isDynamicWorkflow(); +function isDefaultSetup(env = getEnv()) { + return isDynamicWorkflow(env); } function prettyPrintInvocation(cmd, args) { return [cmd, ...args].map((x) => x.includes(" ") ? `'${x}'` : x).join(" "); @@ -145164,8 +146273,9 @@ async function runTool(cmd, args = [], opts = {}) { return stdout; } var persistedInputsKey = "persisted_inputs"; -var persistInputs = function() { - const inputEnvironmentVariables = Object.entries(process.env).filter( +var persistInputs = function(env = getEnv()) { + const entries = env.entries(); + const inputEnvironmentVariables = entries.filter( ([name]) => name.startsWith("INPUT_") ); core3.saveState(persistedInputsKey, JSON.stringify(inputEnvironmentVariables)); @@ -145178,7 +146288,7 @@ var restoreInputs = function() { } } }; -function getPullRequestBranches() { +function getPullRequestBranches(env = getEnv()) { const pullRequest = github.context.payload.pull_request; if (pullRequest) { return { @@ -145189,8 +146299,10 @@ function getPullRequestBranches() { head: pullRequest.head.label }; } - const codeScanningRef = process.env.CODE_SCANNING_REF; - const codeScanningBaseBranch = process.env.CODE_SCANNING_BASE_BRANCH; + const codeScanningRef = env.getOptional("CODE_SCANNING_REF" /* CODE_SCANNING_REF */); + const codeScanningBaseBranch = env.getOptional( + "CODE_SCANNING_BASE_BRANCH" /* CODE_SCANNING_BASE_BRANCH */ + ); if (codeScanningRef && codeScanningBaseBranch) { return { base: codeScanningBaseBranch, @@ -145201,8 +146313,8 @@ function getPullRequestBranches() { } return void 0; } -function isAnalyzingPullRequest() { - return getPullRequestBranches() !== void 0; +function isAnalyzingPullRequest(env = getEnv()) { + return getPullRequestBranches(env) !== void 0; } var qualityCategoryMapping = { "c#": "csharp", @@ -145214,8 +146326,8 @@ var qualityCategoryMapping = { typescript: "javascript-typescript", kotlin: "java-kotlin" }; -function fixCodeQualityCategory(logger, category) { - if (category !== void 0 && isDefaultSetup() && category.startsWith("/language:")) { +function fixCodeQualityCategory(logger, category, env = getEnv()) { + if (category !== void 0 && isDefaultSetup(env) && category.startsWith("/language:")) { const language = category.substring("/language:".length); const mappedLanguage = qualityCategoryMapping[language]; if (mappedLanguage) { @@ -145229,13 +146341,106 @@ function fixCodeQualityCategory(logger, category) { return category; } -// src/feature-flags.ts -var fs5 = __toESM(require("fs")); -var path5 = __toESM(require("path")); -var semver4 = __toESM(require_semver2()); +// src/logging.ts +var core4 = __toESM(require_core()); +function getActionsLogger() { + return { + debug: core4.debug, + info: core4.info, + warning: core4.warning, + error: core4.error, + isDebug: core4.isDebug, + startGroup: core4.startGroup, + endGroup: core4.endGroup + }; +} +function withGroup(groupName, f) { + core4.startGroup(groupName); + try { + return f(); + } finally { + core4.endGroup(); + } +} +async function withGroupAsync(groupName, f) { + core4.startGroup(groupName); + try { + return await f(); + } finally { + core4.endGroup(); + } +} +function formatDuration(durationMs) { + if (durationMs < 1e3) { + return `${durationMs}ms`; + } + if (durationMs < 60 * 1e3) { + return `${(durationMs / 1e3).toFixed(1)}s`; + } + const minutes = Math.floor(durationMs / (60 * 1e3)); + const seconds = Math.floor(durationMs % (60 * 1e3) / 1e3); + return `${minutes}m${seconds}s`; +} + +// src/status-report.ts +var os3 = __toESM(require("os")); +var core7 = __toESM(require_core()); + +// node_modules/uuid/dist-node/regex.js +var regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i; + +// node_modules/uuid/dist-node/validate.js +function validate(uuid) { + return typeof uuid === "string" && regex_default.test(uuid); +} +var validate_default = validate; + +// node_modules/uuid/dist-node/stringify.js +var byteToHex = []; +for (let i = 0; i < 256; ++i) { + byteToHex.push((i + 256).toString(16).slice(1)); +} +function unsafeStringify(arr, offset = 0) { + return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); +} + +// node_modules/uuid/dist-node/rng.js +var rnds8 = new Uint8Array(16); +function rng() { + return crypto.getRandomValues(rnds8); +} + +// node_modules/uuid/dist-node/v4.js +function v4(options, buf, offset) { + if (!buf && !options && crypto.randomUUID) { + return crypto.randomUUID(); + } + return _v4(options, buf, offset); +} +function _v4(options, buf, offset) { + options = options || {}; + const rnds = options.random ?? options.rng?.() ?? rng(); + if (rnds.length < 16) { + throw new Error("Random bytes length must be >= 16"); + } + rnds[6] = rnds[6] & 15 | 64; + rnds[8] = rnds[8] & 63 | 128; + if (buf) { + offset = offset || 0; + if (offset < 0 || offset + 16 > buf.length) { + throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`); + } + for (let i = 0; i < 16; ++i) { + buf[offset + i] = rnds[i]; + } + return buf; + } + return unsafeStringify(rnds); +} +var v4_default = v4; // src/api-client.ts -var core4 = __toESM(require_core()); +var core5 = __toESM(require_core()); var githubUtils = __toESM(require_utils4()); // node_modules/@octokit/plugin-retry/dist-bundle/index.js @@ -145313,6 +146518,9 @@ function retry(octokit, octokitOptions) { } retry.VERSION = VERSION7; +// src/api-client.ts +var import_undici = __toESM(require_undici()); + // src/repository.ts function getRepositoryNwo() { return getRepositoryNwoFromEnv("GITHUB_REPOSITORY"); @@ -145340,37 +146548,73 @@ function parseRepositoryNwo(input) { // src/api-client.ts var GITHUB_ENTERPRISE_VERSION_HEADER = "x-github-enterprise-version"; var DO_NOT_RETRY_STATUSES = [400, 410, 422, 451]; -function createApiClientWithDetails(apiDetails, { allowExternal = false } = {}) { +function getRegistryProxyConfig(action) { + return { + host: action.env.getOptional("CODEQL_PROXY_HOST" /* PROXY_HOST */), + port: action.env.getOptional("CODEQL_PROXY_PORT" /* PROXY_PORT */), + ca: action.env.getOptional("CODEQL_PROXY_CA_CERTIFICATE" /* PROXY_CA_CERTIFICATE */) + }; +} +function getRegistryProxy(action) { + const { host, port, ca } = getRegistryProxyConfig(action); + if (host && port) { + const uri = `http://${host}:${port}`; + action.logger.debug( + `Using private registry proxy at '${uri}' for API client.` + ); + return new import_undici.ProxyAgent({ + uri, + keepAliveTimeout: 10, + keepAliveMaxTimeout: 10, + requestTls: ca ? { ca } : void 0 + }); + } + return void 0; +} +function makeProxyRequestOptions(dispatcher) { + if (dispatcher === void 0) { + return githubUtils.defaults.request; + } + return { + ...githubUtils.defaults.request, + fetch: (req, init2) => { + return (0, import_undici.fetch)(req, { ...init2, dispatcher }); + } + }; +} +function createApiClientWithDetails(apiDetails, { allowExternal = false, proxy = void 0 } = {}) { const auth2 = allowExternal && apiDetails.externalRepoAuth || apiDetails.auth; const retryingOctokit = githubUtils.GitHub.plugin(retry); + const requestOptions = makeProxyRequestOptions(proxy); return new retryingOctokit( githubUtils.getOctokitOptions(auth2, { baseUrl: apiDetails.apiURL, userAgent: `CodeQL-Action/${getActionVersion()}`, log: { - debug: core4.debug, - info: core4.info, - warn: core4.warning, - error: core4.error + debug: core5.debug, + info: core5.info, + warn: core5.warning, + error: core5.error }, + request: requestOptions, retry: { doNotRetry: DO_NOT_RETRY_STATUSES } }) ); } -function getApiDetails() { +function getApiDetails(env = getEnv()) { return { auth: getRequiredInput("token"), - url: getRequiredEnvParam("GITHUB_SERVER_URL"), - apiURL: getRequiredEnvParam("GITHUB_API_URL") + url: env.getRequired("GITHUB_SERVER_URL" /* GITHUB_SERVER_URL */), + apiURL: env.getRequired("GITHUB_API_URL" /* GITHUB_API_URL */) }; } -function getApiClient() { - return createApiClientWithDetails(getApiDetails()); +function getApiClient(env = getEnv()) { + return createApiClientWithDetails(getApiDetails(env)); } -function getApiClientWithExternalAuth(apiDetails) { - return createApiClientWithDetails(apiDetails, { allowExternal: true }); +function getApiClientWithExternalAuth(apiDetails, proxy) { + return createApiClientWithDetails(apiDetails, { allowExternal: true, proxy }); } function getAuthorizationHeaderFor(logger, apiDetails, url2) { if (url2.startsWith(`${apiDetails.url}/`) || apiDetails.apiURL && url2.startsWith(`${apiDetails.apiURL}/`)) { @@ -145432,7 +146676,7 @@ async function getAnalysisKey() { const workflowPath = await getWorkflowRelativePath(); const jobName = getRequiredEnvParam("GITHUB_JOB"); analysisKey = `${workflowPath}:${jobName}`; - core4.exportVariable("CODEQL_ACTION_ANALYSIS_KEY" /* ANALYSIS_KEY */, analysisKey); + core5.exportVariable("CODEQL_ACTION_ANALYSIS_KEY" /* ANALYSIS_KEY */, analysisKey); return analysisKey; } async function getAutomationID() { @@ -145519,19 +146763,28 @@ function wrapApiConfigurationError(e) { return e; } -// src/defaults.json -var bundleVersion = "codeql-bundle-v2.25.6"; -var cliVersion = "2.25.6"; - -// src/overlay/index.ts -var fs4 = __toESM(require("fs")); -var path4 = __toESM(require("path")); +// src/config/pack-registries.ts +function parseRegistries(registriesInput) { + try { + return registriesInput ? load(registriesInput) : void 0; + } catch { + throw new ConfigurationError( + "Invalid registries input. Must be a YAML string." + ); + } +} +function parseRegistriesWithoutCredentials(registriesInput) { + return parseRegistries(registriesInput)?.map((r) => { + const { url: url2, packages, kind } = r; + return { url: url2, packages, kind }; + }); +} // src/git-utils.ts var fs3 = __toESM(require("fs")); var os2 = __toESM(require("os")); var path3 = __toESM(require("path")); -var core5 = __toESM(require_core()); +var core6 = __toESM(require_core()); var toolrunner2 = __toESM(require_toolrunner()); var io3 = __toESM(require_io()); var semver2 = __toESM(require_semver2()); @@ -145562,7 +146815,7 @@ async function getGitVersionOrThrow() { var runGitCommand = async function(workingDirectory, args, customErrorMessage, options) { let stdout = ""; let stderr = ""; - core5.debug(`Running git command: git ${args.join(" ")}`); + core6.debug(`Running git command: git ${args.join(" ")}`); try { await new toolrunner2.ToolRunner(await io3.which("git", true), args, { silent: true, @@ -145583,7 +146836,7 @@ var runGitCommand = async function(workingDirectory, args, customErrorMessage, o if (stderr.includes("not a git repository")) { reason = "The checkout path provided to the action does not appear to be a git repository."; } - core5.info(`git call failed. ${customErrorMessage} Error: ${reason}`); + core6.info(`git call failed. ${customErrorMessage} Error: ${reason}`); throw error3; } }; @@ -145746,7 +146999,7 @@ async function getRef() { ) !== head; if (hasChangedRef) { const newRef = ref.replace(pull_ref_regex, "refs/pull/$1/head"); - core5.debug( + core6.debug( `No longer on merge commit, rewriting ref from ${ref} to ${newRef}.` ); return newRef; @@ -145797,7 +147050,512 @@ async function getGeneratedFiles(workingDirectory) { return generatedFiles; } +// src/start-proxy/types.ts +var usernameSchema = { + /** The username needed to authenticate to the package registry, if any. */ + username: optionalOrNull(string) +}; +function hasUsername(config) { + return "username" in config; +} +var usernamePasswordSchema = { + /** The password needed to authenticate to the package registry, if any. */ + password: optionalOrNull(string), + ...usernameSchema +}; +function hasUsernameAndPassword(config) { + return hasUsername(config) && "password" in config; +} +var tokenSchema = { + /** The token needed to authenticate to the package registry, if any. */ + token: optionalOrNull(string), + ...usernameSchema +}; +function hasToken(config) { + return "token" in config; +} +function isToken(config) { + return "token" in config && validateSchema(tokenSchema, config); +} +var azureConfigSchema = { + "tenant-id": string, + "client-id": string +}; +function isAzureConfig(config) { + return validateSchema(azureConfigSchema, config); +} +var awsConfigSchema = { + "aws-region": string, + "account-id": string, + "role-name": string, + domain: string, + "domain-owner": string, + audience: optionalOrNull(string) +}; +function isAWSConfig(config) { + return validateSchema(awsConfigSchema, config); +} +var jfrogConfigSchema = { + "jfrog-oidc-provider-name": string, + audience: optionalOrNull(string), + "identity-mapping-name": optionalOrNull(string) +}; +function isJFrogConfig(config) { + return validateSchema(jfrogConfigSchema, config); +} +var cloudsmithConfigSchema = { + namespace: string, + "service-slug": string, + "api-host": string +}; +function isCloudsmithConfig(config) { + return validateSchema(cloudsmithConfigSchema, config); +} +var gcpConfigSchema = { + "workload-identity-provider": string, + "service-account": optionalOrNull(string), + audience: optionalOrNull(string) +}; +function isGCPConfig(config) { + return validateSchema(gcpConfigSchema, config); +} +var oidcSchemas = [ + { schema: azureConfigSchema, name: "Azure" }, + { schema: awsConfigSchema, name: "AWS" }, + { schema: jfrogConfigSchema, name: "JFrog" }, + { schema: cloudsmithConfigSchema, name: "Cloudsmith" }, + { schema: gcpConfigSchema, name: "GCP" } +]; +function credentialToStr(credential) { + let result = `Type: ${credential.type};`; + const appendIfDefined = (name, val) => { + if (isDefined2(val)) { + result += ` ${name}: ${val};`; + } + }; + appendIfDefined("Url", credential.url); + appendIfDefined("Host", credential.host); + if (hasUsername(credential)) { + appendIfDefined("Username", credential.username); + } + if ("password" in credential) { + appendIfDefined( + "Password", + isDefined2(credential.password) ? "***" : void 0 + ); + } + if (hasToken(credential)) { + appendIfDefined("Token", isDefined2(credential.token) ? "***" : void 0); + } + if (isAzureConfig(credential)) { + appendIfDefined("Tenant", credential["tenant-id"]); + appendIfDefined("Client", credential["client-id"]); + } else if (isAWSConfig(credential)) { + appendIfDefined("AWS Region", credential["aws-region"]); + appendIfDefined("AWS Account", credential["account-id"]); + appendIfDefined("AWS Role", credential["role-name"]); + appendIfDefined("AWS Domain", credential.domain); + appendIfDefined("AWS Domain Owner", credential["domain-owner"]); + appendIfDefined("AWS Audience", credential.audience); + } else if (isJFrogConfig(credential)) { + appendIfDefined("JFrog Provider", credential["jfrog-oidc-provider-name"]); + appendIfDefined( + "JFrog Identity Mapping", + credential["identity-mapping-name"] + ); + appendIfDefined("JFrog Audience", credential.audience); + } else if (isCloudsmithConfig(credential)) { + appendIfDefined("Cloudsmith Namespace", credential.namespace); + appendIfDefined("Cloudsmith Service Slug", credential["service-slug"]); + appendIfDefined("Cloudsmith API Host", credential["api-host"]); + } else if (isGCPConfig(credential)) { + appendIfDefined( + "GCP Workload Identity Provider", + credential["workload-identity-provider"] + ); + appendIfDefined("GCP Service Account", credential["service-account"]); + appendIfDefined("GCP Audience", credential.audience); + } + return result; +} +var registryBaseSchema = { + /** The type of the package registry. */ + type: string, + /** Whether the registry replaces the base registry for the ecosystem. */ + "replaces-base": optional(boolean) +}; +function getAddressString(address) { + if (address.url === void 0) { + return address.host; + } else { + return address.url; + } +} + +// src/status-report.ts +function getDisplayActionName(actionName) { + if (actionName === "finish" /* Analyze */) { + return "analyze"; + } + return actionName; +} +function getJobUUID(action) { + const existingJobRunUuid = action.env.getOptional("CODEQL_ACTION_JOB_RUN_UUID" /* JOB_RUN_UUID */); + if (existingJobRunUuid !== void 0 && validate_default(existingJobRunUuid)) { + action.logger.info(`Existing job run UUID is ${existingJobRunUuid}.`); + return existingJobRunUuid; + } + const jobRunUuid = v4_default(); + action.logger.info(`Job run UUID is ${jobRunUuid}.`); + action.actions.exportVariable("CODEQL_ACTION_JOB_RUN_UUID" /* JOB_RUN_UUID */, jobRunUuid); + return jobRunUuid; +} +function isFirstPartyAnalysis(actionName) { + if (actionName !== "upload-sarif" /* UploadSarif */) { + return true; + } + return process.env["CODEQL_ACTION_INIT_HAS_RUN" /* INIT_ACTION_HAS_RUN */] === "true"; +} +function isThirdPartyAnalysis(actionName) { + return !isFirstPartyAnalysis(actionName); +} +var JobStatus = /* @__PURE__ */ ((JobStatus2) => { + JobStatus2["UnknownStatus"] = "JOB_STATUS_UNKNOWN"; + JobStatus2["SuccessStatus"] = "JOB_STATUS_SUCCESS"; + JobStatus2["FailureStatus"] = "JOB_STATUS_FAILURE"; + JobStatus2["ConfigErrorStatus"] = "JOB_STATUS_CONFIGURATION_ERROR"; + return JobStatus2; +})(JobStatus || {}); +function getActionsStatus(error3, otherFailureCause) { + if (error3 || otherFailureCause) { + return error3 instanceof ConfigurationError ? "user-error" : "failure"; + } else { + return "success"; + } +} +function getJobStatusDisplayName(status) { + switch (status) { + case "JOB_STATUS_SUCCESS" /* SuccessStatus */: + return "success"; + case "JOB_STATUS_FAILURE" /* FailureStatus */: + return "failure"; + case "JOB_STATUS_CONFIGURATION_ERROR" /* ConfigErrorStatus */: + return "configuration error"; + case "JOB_STATUS_UNKNOWN" /* UnknownStatus */: + return "unknown"; + default: + assertNever(status); + } +} +function setJobStatusIfUnsuccessful(actionStatus) { + if (actionStatus === "user-error") { + core7.exportVariable( + "CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, + process.env["CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */] ?? "JOB_STATUS_CONFIGURATION_ERROR" /* ConfigErrorStatus */ + ); + } else if (actionStatus === "failure" || actionStatus === "aborted") { + core7.exportVariable( + "CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, + process.env["CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */] ?? "JOB_STATUS_FAILURE" /* FailureStatus */ + ); + } +} +function getRegistryTypesFromEnv(logger, env = getEnv()) { + const value = env.getOptional("CODEQL_PROXY_URLS" /* PROXY_URLS */); + if (value === void 0) { + return void 0; + } + try { + const data = JSON.parse(value); + if (!isArray(data)) { + logger.debug( + `Expected '${"CODEQL_PROXY_URLS" /* PROXY_URLS */}' to contain a JSON array, but got '${typeof data}'.` + ); + return void 0; + } + if (!validateArray(registryBaseSchema, data)) { + logger.debug( + `Expected '${"CODEQL_PROXY_URLS" /* PROXY_URLS */}' to contain a JSON array of registry objects, but got something else.` + ); + return void 0; + } + const types2 = new Set(data.map((r) => r.type)); + return Array.from(types2).sort().join(","); + } catch (err) { + logger.debug( + `Failed to parse '${"CODEQL_PROXY_URLS" /* PROXY_URLS */}': ${getErrorMessage(err)}.` + ); + return void 0; + } +} +async function createStatusReportBase(actionName, status, actionStartedAt, config, diskInfo, logger, cause, exception) { + try { + const commitOid = getOptionalInput("sha") || process.env["GITHUB_SHA"] || ""; + const ref = await getRef(); + const jobRunUUID = process.env["CODEQL_ACTION_JOB_RUN_UUID" /* JOB_RUN_UUID */] || ""; + const workflowRunID = getWorkflowRunID(); + const workflowRunAttempt = getWorkflowRunAttempt(); + const workflowName = process.env["GITHUB_WORKFLOW"] || ""; + const jobName = process.env["GITHUB_JOB"] || ""; + const analysis_key = await getAnalysisKey(); + let workflowStartedAt = process.env["CODEQL_WORKFLOW_STARTED_AT" /* WORKFLOW_STARTED_AT */]; + if (workflowStartedAt === void 0) { + workflowStartedAt = actionStartedAt.toISOString(); + core7.exportVariable("CODEQL_WORKFLOW_STARTED_AT" /* WORKFLOW_STARTED_AT */, workflowStartedAt); + } + const runnerOs = getRequiredEnvParam("RUNNER_OS"); + const codeQlCliVersion = getCachedCodeQlVersion(); + const actionRef = process.env["GITHUB_ACTION_REF"] || ""; + const testingEnvironment = getTestingEnvironment(); + if (testingEnvironment) { + core7.exportVariable("CODEQL_ACTION_TESTING_ENVIRONMENT" /* TESTING_ENVIRONMENT */, testingEnvironment); + } + const isSteadyStateDefaultSetupRun = process.env["CODE_SCANNING_IS_STEADY_STATE_DEFAULT_SETUP"] === "true"; + const statusReport = { + action_name: actionName, + action_oid: "unknown", + // TODO decide if it's possible to fill this in + action_ref: actionRef, + action_started_at: actionStartedAt.toISOString(), + action_version: getActionVersion(), + analysis_kinds: config?.analysisKinds?.join(","), + analysis_key, + build_mode: config?.buildMode, + commit_oid: commitOid, + computed_inputs: {}, + first_party_analysis: isFirstPartyAnalysis(actionName), + job_name: jobName, + job_run_uuid: jobRunUUID, + ref, + registry_types: getRegistryTypesFromEnv(logger), + runner_os: runnerOs, + started_at: workflowStartedAt, + status, + steady_state_default_setup: isSteadyStateDefaultSetupRun, + testing_environment: testingEnvironment || "", + workflow_name: workflowName, + workflow_run_attempt: workflowRunAttempt, + workflow_run_id: workflowRunID + }; + try { + statusReport.actions_event_name = getWorkflowEventName(); + } catch (e) { + logger.warning( + `Could not determine the workflow event name: ${getErrorMessage(e)}.` + ); + } + if (config) { + statusReport.languages = config.languages?.join(","); + } + if (diskInfo) { + statusReport.runner_available_disk_space_bytes = diskInfo.numAvailableBytes; + statusReport.runner_total_disk_space_bytes = diskInfo.numTotalBytes; + } + if (cause) { + statusReport.cause = cause; + } + if (exception) { + statusReport.exception = exception; + } + if (status === "success" || status === "failure" || status === "aborted" || status === "user-error") { + statusReport.completed_at = (/* @__PURE__ */ new Date()).toISOString(); + } + const matrix = getRequiredInput("matrix"); + if (matrix) { + statusReport.matrix_vars = matrix; + } + if ("RUNNER_ARCH" in process.env) { + statusReport.runner_arch = process.env["RUNNER_ARCH"]; + } + if (!(runnerOs === "Linux" && isSelfHostedRunner())) { + statusReport.runner_os_release = os3.release(); + } + if (codeQlCliVersion !== void 0) { + statusReport.codeql_version = codeQlCliVersion.version; + } + const imageVersion = process.env["ImageVersion"]; + if (imageVersion) { + statusReport.runner_image_version = imageVersion; + } + return statusReport; + } catch (e) { + logger.warning( + `Failed to gather information for telemetry: ${getErrorMessage(e)}. Will skip sending status report.` + ); + if (isInTestMode()) { + throw e; + } + return void 0; + } +} +var OUT_OF_DATE_MSG = "CodeQL Action is out-of-date. Please upgrade to the latest version of `codeql-action`."; +var INCOMPATIBLE_MSG = "CodeQL Action version is incompatible with the API endpoint. Please update to a compatible version of `codeql-action`."; +async function sendStatusReport(statusReport) { + setJobStatusIfUnsuccessful(statusReport.status); + const statusReportJSON = JSON.stringify(statusReport); + core7.debug(`Sending status report: ${statusReportJSON}`); + if (isInTestMode()) { + core7.debug("In test mode. Status reports are not uploaded."); + return; + } + const nwo = getRepositoryNwo(); + const client = getApiClient(); + try { + await client.request( + "PUT /repos/:owner/:repo/code-scanning/analysis/status", + { + owner: nwo.owner, + repo: nwo.repo, + data: statusReportJSON + } + ); + } catch (e) { + const httpError = asHTTPError(e); + if (httpError !== void 0) { + switch (httpError.status) { + case 403: + if (getWorkflowEventName() === "push" && process.env["GITHUB_ACTOR"] === "dependabot[bot]") { + core7.warning( + `Workflows triggered by Dependabot on the "push" event run with read-only access. Uploading CodeQL results requires write access. To use CodeQL with Dependabot, please ensure you are using the "pull_request" event for this workflow and avoid triggering on the "push" event for Dependabot branches. See ${"https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#scanning-on-push" /* SCANNING_ON_PUSH */} for more information on how to configure these events.` + ); + } else { + core7.warning( + `This run of the CodeQL Action does not have permission to access the CodeQL Action API endpoints. This could be because the Action is running on a pull request from a fork. If not, please ensure the workflow has at least the 'security-events: read' permission. Details: ${httpError.message}` + ); + } + return; + case 404: + core7.warning(httpError.message); + return; + case 422: + if (getRequiredEnvParam("GITHUB_SERVER_URL") !== GITHUB_DOTCOM_URL) { + core7.debug(INCOMPATIBLE_MSG); + } else { + core7.debug(OUT_OF_DATE_MSG); + } + return; + } + } + core7.warning( + `An unexpected error occurred when sending a status report: ${getErrorMessage( + e + )}` + ); + } +} +async function createInitWithConfigStatusReport(config, initStatusReport, configFile, totalCacheSize, overlayBaseDatabaseStats, dependencyCachingResults) { + const languages = config.languages.join(","); + const paths = (config.originalUserInput.paths || []).join(","); + const pathsIgnore = (config.originalUserInput["paths-ignore"] || []).join( + "," + ); + const disableDefaultQueries = config.originalUserInput["disable-default-queries"] ? languages : ""; + const queries = []; + let queriesInput = getOptionalInput("queries")?.trim(); + if (queriesInput === void 0 || queriesInput.startsWith("+")) { + queries.push( + ...(config.originalUserInput.queries || []).map((q) => q.uses) + ); + } + if (queriesInput !== void 0) { + queriesInput = queriesInput.startsWith("+") ? queriesInput.slice(1) : queriesInput; + queries.push(...queriesInput.split(",")); + } + let packs = {}; + if (Array.isArray(config.computedConfig.packs)) { + packs[config.languages[0]] = config.computedConfig.packs; + } else if (config.computedConfig.packs !== void 0) { + packs = config.computedConfig.packs; + } + return { + ...initStatusReport, + config_file: configFile ?? "", + disable_default_queries: disableDefaultQueries, + paths, + paths_ignore: pathsIgnore, + queries: queries.join(","), + packs: JSON.stringify(packs), + trap_cache_languages: Object.keys(config.trapCaches).join(","), + trap_cache_download_size_bytes: totalCacheSize, + trap_cache_download_duration_ms: Math.round(config.trapCacheDownloadTime), + overlay_base_database_download_size_bytes: overlayBaseDatabaseStats?.databaseSizeBytes, + overlay_base_database_download_duration_ms: overlayBaseDatabaseStats?.databaseDownloadDurationMs, + dependency_caching_restore_results: dependencyCachingResults, + query_filters: JSON.stringify( + config.originalUserInput["query-filters"] ?? [] + ), + registries: JSON.stringify( + parseRegistriesWithoutCredentials(getOptionalInput("registries")) ?? [] + ) + }; +} +async function sendUnhandledErrorStatusReport(actionName, actionStartedAt, error3, logger) { + try { + const statusReport = await createStatusReportBase( + actionName, + "failure", + actionStartedAt, + void 0, + void 0, + logger, + `Unhandled CodeQL Action error: ${getErrorMessage(error3)}`, + error3 instanceof Error ? error3.stack : void 0 + ); + if (statusReport !== void 0) { + await sendStatusReport(statusReport); + } + } catch (e) { + logger.warning( + `Failed to send the unhandled error status report: ${getErrorMessage(e)}.` + ); + if (isInTestMode()) { + throw e; + } + } +} + +// src/action-common.ts +async function runInActions(action) { + const startedAt = /* @__PURE__ */ new Date(); + const logger = getActionsLogger(); + const env = getEnv(); + const actionsEnv = getActionsEnv(); + try { + const actionState = { + name: action.name, + startedAt, + logger, + env, + actions: actionsEnv + }; + getJobUUID(actionState); + await action.run(actionState); + } catch (error3) { + core8.setFailed( + `${getDisplayActionName(action.name)} action failed: ${getErrorMessage(error3)}` + ); + const statusReportError = action.transformTelemetryError !== void 0 ? action.transformTelemetryError(wrapError(error3)) : error3; + await sendUnhandledErrorStatusReport( + action.name, + startedAt, + statusReportError, + logger + ); + } +} + +// src/feature-flags.ts +var fs5 = __toESM(require("fs")); +var path5 = __toESM(require("path")); +var semver4 = __toESM(require_semver2()); + +// src/defaults.json +var bundleVersion = "codeql-bundle-v2.26.3"; +var cliVersion = "2.26.3"; + // src/overlay/index.ts +var fs4 = __toESM(require("fs")); +var path4 = __toESM(require("path")); var CODEQL_OVERLAY_MINIMUM_VERSION = "2.23.8"; var CODEQL_OVERLAY_MINIMUM_VERSION_CPP = "2.25.0"; var CODEQL_OVERLAY_MINIMUM_VERSION_CSHARP = "2.24.1"; @@ -145927,14 +147685,14 @@ var LINKED_CODEQL_VERSION = { tagName: bundleVersion }; var featureConfig = { - ["allow_multiple_analysis_kinds" /* AllowMultipleAnalysisKinds */]: { + ["allow_merge_config_files" /* AllowMergeConfigFiles */]: { defaultValue: false, - envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS", + envVar: "CODEQL_ACTION_ALLOW_MERGE_CONFIG_FILES", minimumVersion: void 0 }, - ["allow_toolcache_input" /* AllowToolcacheInput */]: { + ["allow_multiple_analysis_kinds" /* AllowMultipleAnalysisKinds */]: { defaultValue: false, - envVar: "CODEQL_ACTION_ALLOW_TOOLCACHE_INPUT", + envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS", minimumVersion: void 0 }, ["cleanup_trap_caches" /* CleanupTrapCaches */]: { @@ -146105,11 +147863,6 @@ var featureConfig = { envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION_DRY_RUN", minimumVersion: void 0 }, - ["overlay_analysis_resource_checks_v2" /* OverlayAnalysisResourceChecksV2 */]: { - defaultValue: false, - envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_RESOURCE_CHECKS_V2", - minimumVersion: void 0 - }, ["overlay_analysis_status_check" /* OverlayAnalysisStatusCheck */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_STATUS_CHECK", @@ -146131,6 +147884,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["proxy_api_requests" /* ProxyApiRequests */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_PROXY_API_REQUESTS", + minimumVersion: void 0 + }, ["skip_file_coverage_on_prs" /* SkipFileCoverageOnPrs */]: { defaultValue: false, envVar: "CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS", @@ -146142,6 +147900,11 @@ var featureConfig = { envVar: "CODEQL_ACTION_START_PROXY_USE_FEATURES_RELEASE", minimumVersion: void 0 }, + ["tools_repository_property" /* ToolsRepositoryProperty */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_TOOLS_REPOSITORY_PROPERTY", + minimumVersion: void 0 + }, ["upload_overlay_db_to_api" /* UploadOverlayDbToApi */]: { defaultValue: false, envVar: "CODEQL_ACTION_UPLOAD_OVERLAY_DB_TO_API", @@ -146634,12 +148397,12 @@ var import_perf_hooks3 = require("perf_hooks"); var io5 = __toESM(require_io()); // src/autobuild.ts -var core11 = __toESM(require_core()); +var core13 = __toESM(require_core()); // src/codeql.ts var fs15 = __toESM(require("fs")); var path14 = __toESM(require("path")); -var core10 = __toESM(require_core()); +var core12 = __toESM(require_core()); var toolrunner3 = __toESM(require_toolrunner()); // src/cli-errors.ts @@ -146894,11 +148657,11 @@ function wrapCliConfigurationError(cliError) { var fs9 = __toESM(require("fs")); var path10 = __toESM(require("path")); var import_perf_hooks = require("perf_hooks"); -var core8 = __toESM(require_core()); +var core10 = __toESM(require_core()); // src/caching-utils.ts var crypto2 = __toESM(require("crypto")); -var core6 = __toESM(require_core()); +var core9 = __toESM(require_core()); async function getTotalCacheSize(paths, logger, quiet = false) { const sizes = await Promise.all( paths.map((cacheDir2) => tryGetFolderBytes(cacheDir2, logger, quiet)) @@ -146927,7 +148690,7 @@ function getCachingKind(input) { case "restore": return "restore" /* Restore */; default: - core6.warning( + core9.warning( `Unrecognized 'dependency-caching' input: ${input}. Defaulting to 'none'.` ); return "none" /* None */; @@ -146947,10 +148710,112 @@ function getDependencyCachingEnabled() { } // src/config/db-config.ts -var path6 = __toESM(require("path")); +var path7 = __toESM(require("path")); var jsonschema = __toESM(require_lib2()); var semver5 = __toESM(require_semver2()); +// src/diagnostics.ts +var import_fs = require("fs"); +var import_path = __toESM(require("path")); +var unwrittenDiagnostics = []; +var unwrittenDefaultLanguageDiagnostics = []; +var diagnosticCounter = 0; +function makeDiagnostic(id, name, data = void 0) { + return { + ...data, + timestamp: data?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(), + source: { ...data?.source, id, name } + }; +} +function addDiagnostic(config, language, diagnostic) { + const logger = getActionsLogger(); + const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; + if ((0, import_fs.existsSync)(databasePath)) { + writeDiagnostic(config, language, diagnostic); + } else { + logger.debug( + `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.` + ); + unwrittenDiagnostics.push({ diagnostic, language }); + } +} +function addNoLanguageDiagnostic(config, diagnostic) { + if (config !== void 0) { + addDiagnostic( + config, + // Arbitrarily choose the first language. We could also choose all languages, but that + // increases the risk of misinterpreting the data. + config.languages[0], + diagnostic + ); + } else { + unwrittenDefaultLanguageDiagnostics.push(diagnostic); + } +} +function writeDiagnostic(config, language, diagnostic) { + const logger = getActionsLogger(); + const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; + const diagnosticsPath = import_path.default.resolve( + databasePath, + "diagnostic", + "codeql-action" + ); + try { + (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); + const uniqueSuffix = (diagnosticCounter++).toString(); + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "" + ); + const jsonPath = import_path.default.resolve( + diagnosticsPath, + `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json` + ); + (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); + } catch (err) { + logger.warning(`Unable to write diagnostic message to database: ${err}`); + logger.debug(JSON.stringify(diagnostic)); + } +} +function logUnwrittenDiagnostics() { + const logger = getActionsLogger(); + const num = unwrittenDiagnostics.length; + if (num > 0) { + logger.warning( + `${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.` + ); + for (const unwritten of unwrittenDiagnostics) { + logger.debug(JSON.stringify(unwritten.diagnostic)); + } + } +} +function flushDiagnostics(config) { + const logger = getActionsLogger(); + const diagnosticsCount = unwrittenDiagnostics.length + unwrittenDefaultLanguageDiagnostics.length; + logger.debug(`Writing ${diagnosticsCount} diagnostic(s) to database.`); + for (const unwritten of unwrittenDiagnostics) { + writeDiagnostic(config, unwritten.language, unwritten.diagnostic); + } + for (const unwritten of unwrittenDefaultLanguageDiagnostics) { + addNoLanguageDiagnostic(config, unwritten); + } + unwrittenDiagnostics = []; + unwrittenDefaultLanguageDiagnostics = []; +} +function makeTelemetryDiagnostic(id, name, attributes, tags) { + return makeDiagnostic(id, name, { + attributes, + visibility: { + cliSummaryTable: false, + statusPage: false, + telemetry: true + }, + source: { + tags + } + }); +} + // src/error-messages.ts var PACKS_PROPERTY = "packs"; function getConfigFileOutsideWorkspaceErrorMessage(configFile) { @@ -146968,7 +148833,7 @@ function getInvalidConfigFileMessage(configFile, messages) { } function getConfigFileRepoFormatInvalidMessage(configFile) { let error3 = `The configuration file "${configFile}" is not a supported remote file reference.`; - error3 += " Expected format //@"; + error3 += " Expected format [/][@][:]"; return error3; } function getConfigFileFormatInvalidMessage(configFile) { @@ -147005,12 +148870,14 @@ function getUnknownLanguagesError(languages) { } // src/feature-flags/properties.ts +var github2 = __toESM(require_github()); var GITHUB_CODEQL_PROPERTY_PREFIX = "github-codeql-"; var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["CONFIG_FILE"] = "github-codeql-config-file"; RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); function isString2(value) { @@ -147029,7 +148896,8 @@ var repositoryPropertyParsers = { ["github-codeql-config-file" /* CONFIG_FILE */]: stringProperty, ["github-codeql-disable-overlay" /* DISABLE_OVERLAY */]: booleanProperty, ["github-codeql-extra-queries" /* EXTRA_QUERIES */]: stringProperty, - ["github-codeql-file-coverage-on-prs" /* FILE_COVERAGE_ON_PRS */]: booleanProperty + ["github-codeql-file-coverage-on-prs" /* FILE_COVERAGE_ON_PRS */]: booleanProperty, + ["github-codeql-tools" /* TOOLS */]: stringProperty }; async function loadPropertiesFromApi(logger, repositoryNwo) { try { @@ -147109,8 +148977,105 @@ var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( function isKnownPropertyName(name) { return KNOWN_REPOSITORY_PROPERTY_NAMES.has(name); } +async function loadRepositoryProperties(repositoryNwo, logger) { + const repositoryOwnerType = github2.context.payload.repository?.owner.type; + logger.debug( + `Repository owner type is '${repositoryOwnerType ?? "unknown"}'.` + ); + if (repositoryOwnerType === "User") { + logger.debug( + "Skipping loading repository properties because the repository is owned by a user and therefore cannot have repository properties." + ); + return new Success({}); + } + try { + return new Success(await loadPropertiesFromApi(logger, repositoryNwo)); + } catch (error3) { + logger.warning( + `Failed to load repository properties: ${getErrorMessage(error3)}` + ); + return new Failure(error3); + } +} // src/config/db-config.ts +var ORG_SCHEMA = { + /** An array of model pack names. */ + "model-packs": optional(array(string)) +}; +var DEFAULT_SETUP_SCHEMA = { + org: optional(object(ORG_SCHEMA)) +}; +var DEFAULT_SETUP_CONFIG_SCHEMA = { + "threat-models": optional(array(string)), + "default-setup": optional( + object(DEFAULT_SETUP_SCHEMA) + ) +}; +function mergeDefaultSetupAndUserConfigs(logger, fromConfigInput, fromConfigFile) { + logger.debug( + "Combining configuration files from 'config' and 'config-file' inputs" + ); + const schemaCheckResult = checkSchema( + DEFAULT_SETUP_CONFIG_SCHEMA, + fromConfigInput + ); + if (schemaCheckResult.invalidKeys.length > 0) { + logger.warning( + `Invalid keys in Default Setup configuration: ${schemaCheckResult.invalidKeys.join(", ")}` + ); + addNoLanguageDiagnostic( + void 0, + makeTelemetryDiagnostic( + "codeql-action/invalid-default-setup-config-keys", + "Invalid Default Setup configuration keys", + { + invalidKeys: schemaCheckResult.invalidKeys + }, + ["internal-error"] + ) + ); + } + if (schemaCheckResult.unknownKeys.length > 0) { + logger.warning( + `Unrecognised keys in Default Setup configuration: ${schemaCheckResult.unknownKeys.join(", ")}` + ); + addNoLanguageDiagnostic( + void 0, + makeTelemetryDiagnostic( + "codeql-action/unrecognised-default-setup-config-keys", + "Unrecognised Default Setup configuration keys", + { + unrecognisedKeys: schemaCheckResult.unknownKeys + }, + ["internal-error"] + ) + ); + } + const threatModels = new Set(fromConfigInput["threat-models"] || []); + for (const configFileThreatModel of fromConfigFile["threat-models"] || []) { + threatModels.add(configFileThreatModel); + } + if (fromConfigFile["default-setup"]) { + logger.warning( + `The 'default-setup' configuration key is not supported in user-supplied configuration files and will be ignored.` + ); + } + const result = { ...fromConfigFile }; + delete result["threat-models"]; + delete result["default-setup"]; + if (fromConfigInput["default-setup"]?.org?.["model-packs"]) { + result["default-setup"] = { + org: { + "model-packs": fromConfigInput["default-setup"].org["model-packs"] + } + }; + } + if (threatModels.size > 0) { + result["threat-models"] = Array.from(threatModels); + } + return result; +} function shouldCombine(inputValue) { return !!inputValue?.trim().startsWith("+"); } @@ -147150,11 +149115,11 @@ function parsePacksSpecification(packStr) { throw new ConfigurationError(getPacksStrInvalid(packStr)); } } - if (packPath && (path6.isAbsolute(packPath) || // Permit using "/" instead of "\" on Windows + if (packPath && (path7.isAbsolute(packPath) || // Permit using "/" instead of "\" on Windows // Use `x.split(y).join(z)` as a polyfill for `x.replaceAll(y, z)` since // if we used a regex we'd need to escape the path separator on Windows // which seems more awkward. - path6.normalize(packPath).split(path6.sep).join("/") !== packPath.split(path6.sep).join("/"))) { + path7.normalize(packPath).split(path7.sep).join("/") !== packPath.split(path7.sep).join("/"))) { throw new ConfigurationError(getPacksStrInvalid(packStr)); } if (!packPath && pathStart) { @@ -147349,146 +149314,145 @@ function parseUserConfig(logger, pathInput, contents, validateConfig) { } } -// src/diagnostics.ts -var import_fs = require("fs"); -var import_path = __toESM(require("path")); - -// src/logging.ts -var core7 = __toESM(require_core()); -function getActionsLogger() { - return { - debug: core7.debug, - info: core7.info, - warning: core7.warning, - error: core7.error, - isDebug: core7.isDebug, - startGroup: core7.startGroup, - endGroup: core7.endGroup - }; -} -function withGroup(groupName, f) { - core7.startGroup(groupName); - try { - return f(); - } finally { - core7.endGroup(); - } -} -async function withGroupAsync(groupName, f) { - core7.startGroup(groupName); - try { - return await f(); - } finally { - core7.endGroup(); +// src/config/remote-file.ts +var DEFAULT_CONFIG_FILE_NAME = ".github/codeql-config.yml"; +var DEFAULT_CONFIG_FILE_REF = "main"; +function getDefaultOwner(env) { + const currentRepoNwo = env.getRequired("GITHUB_REPOSITORY" /* GITHUB_REPOSITORY */); + const nwoParts = currentRepoNwo.split("/"); + if (nwoParts.length !== 2 || nwoParts[0].trim().length === 0) { + throw new Error( + `Expected ${"GITHUB_REPOSITORY" /* GITHUB_REPOSITORY */} to contain a name with owner, but got '${currentRepoNwo}'.` + ); } + return nwoParts[0].trim(); } -function formatDuration(durationMs) { - if (durationMs < 1e3) { - return `${durationMs}ms`; - } - if (durationMs < 60 * 1e3) { - return `${(durationMs / 1e3).toFixed(1)}s`; +var OLD_REMOTE_ADDRESS_FORMAT = new RegExp( + "(?[^/]+)/(?[^/]+)/(?[^@]+)@(?.*)" +); +function parseOldRemoteFileAddress(input) { + const pieces = OLD_REMOTE_ADDRESS_FORMAT.exec(input); + if (pieces?.groups === void 0 || pieces.length < 5) { + return new Failure(void 0); } - const minutes = Math.floor(durationMs / (60 * 1e3)); - const seconds = Math.floor(durationMs % (60 * 1e3) / 1e3); - return `${minutes}m${seconds}s`; -} - -// src/diagnostics.ts -var unwrittenDiagnostics = []; -var unwrittenDefaultLanguageDiagnostics = []; -var diagnosticCounter = 0; -function makeDiagnostic(id, name, data = void 0) { - return { - ...data, - timestamp: data?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(), - source: { ...data?.source, id, name } - }; + return new Success({ + owner: pieces.groups.owner.trim(), + repo: pieces.groups.repo.trim(), + path: pieces.groups.path.trim(), + ref: pieces.groups.ref.trim() + }); } -function addDiagnostic(config, language, diagnostic) { - const logger = getActionsLogger(); - const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; - if ((0, import_fs.existsSync)(databasePath)) { - writeDiagnostic(config, language, diagnostic); - } else { - logger.debug( - `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.` - ); - unwrittenDiagnostics.push({ diagnostic, language }); - } +function parseNewRemoteFileAddress(env, configFile) { + const format = new RegExp( + "^((?[^:@/]+)/)?(?[^:@/]+)(@(?[^:]+))?(:(?.+))?$" + ); + const pieces = format.exec(configFile.trim()); + const repo = pieces?.groups?.repo?.trim(); + if (!pieces?.groups || !repo || repo.length === 0) { + return new Failure(void 0); + } + const owner = pieces.groups.owner?.trim(); + const path29 = pieces.groups.path?.trim(); + const ref = pieces.groups.ref?.trim(); + return new Success({ + owner: owner || getDefaultOwner(env), + repo, + path: path29 || DEFAULT_CONFIG_FILE_NAME, + ref: ref || DEFAULT_CONFIG_FILE_REF + }); } -function addNoLanguageDiagnostic(config, diagnostic) { - if (config !== void 0) { - addDiagnostic( - config, - // Arbitrarily choose the first language. We could also choose all languages, but that - // increases the risk of misinterpreting the data. - config.languages[0], - diagnostic - ); - } else { - unwrittenDefaultLanguageDiagnostics.push(diagnostic); +async function parseRemoteFileAddress(actionState, configFile) { + const oldFormatAddressResult = parseOldRemoteFileAddress(configFile); + if (oldFormatAddressResult.isSuccess()) { + return oldFormatAddressResult.value; } -} -function writeDiagnostic(config, language, diagnostic) { - const logger = getActionsLogger(); - const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; - const diagnosticsPath = import_path.default.resolve( - databasePath, - "diagnostic", - "codeql-action" + const newFormatAddressResult = parseNewRemoteFileAddress( + actionState.env, + configFile ); - try { - (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); - const uniqueSuffix = (diagnosticCounter++).toString(); - const sanitizedTimestamp = diagnostic.timestamp.replace( - /[^a-zA-Z0-9.-]/g, - "" - ); - const jsonPath = import_path.default.resolve( - diagnosticsPath, - `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json` + if (newFormatAddressResult.isFailure()) { + throw new ConfigurationError( + getConfigFileRepoFormatInvalidMessage(configFile) ); - (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); - } catch (err) { - logger.warning(`Unable to write diagnostic message to database: ${err}`); - logger.debug(JSON.stringify(diagnostic)); } -} -function logUnwrittenDiagnostics() { - const logger = getActionsLogger(); - const num = unwrittenDiagnostics.length; - if (num > 0) { - logger.warning( - `${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.` + const address = newFormatAddressResult.value; + if (address.path.startsWith("/")) { + throw new ConfigurationError( + `The path component of '${configFile}' cannot be an absolute path.` ); - for (const unwritten of unwrittenDiagnostics) { - logger.debug(JSON.stringify(unwritten.diagnostic)); - } } + return address; } -function flushDiagnostics(config) { - const logger = getActionsLogger(); - const diagnosticsCount = unwrittenDiagnostics.length + unwrittenDefaultLanguageDiagnostics.length; - logger.debug(`Writing ${diagnosticsCount} diagnostic(s) to database.`); - for (const unwritten of unwrittenDiagnostics) { - writeDiagnostic(config, unwritten.language, unwritten.diagnostic); + +// src/config/file.ts +var LOCAL_PATH_PREFIX = "./"; +var REMOTE_PATH_PREFIX = "remote="; +async function getConfigFileInput({ + logger, + actions, + features +}, repositoryProperties, analysisKinds) { + const input = actions.getOptionalInput("config-file"); + if (input !== void 0) { + logger.info(`Using configuration file input from workflow: ${input}`); + return input; } - for (const unwritten of unwrittenDefaultLanguageDiagnostics) { - addNoLanguageDiagnostic(config, unwritten); + const propertyValue = repositoryProperties["github-codeql-config-file" /* CONFIG_FILE */]; + const analysisKindSupported = analysisKinds === void 0 || analysisKinds.includes("code-scanning" /* CodeScanning */) && analysisKinds.length === 1; + if (propertyValue !== void 0 && propertyValue.trim().length > 0) { + const useRepositoryProperty = await features.getValue( + "config_file_repository_property" /* ConfigFileRepositoryProperty */ + ); + if (analysisKindSupported && useRepositoryProperty) { + logger.info( + `Using configuration file input from repository property: ${propertyValue}` + ); + return propertyValue; + } else if (!analysisKindSupported) { + logger.info( + "Ignoring configuration file input from repository property, because it is unsupported for the current analysis kind." + ); + } else { + logger.info( + "Ignoring configuration file input from repository property, because the corresponding feature flag is disabled." + ); + } } - unwrittenDiagnostics = []; - unwrittenDefaultLanguageDiagnostics = []; + return void 0; } -function makeTelemetryDiagnostic(id, name, attributes) { - return makeDiagnostic(id, name, { - attributes, - visibility: { - cliSummaryTable: false, - statusPage: false, - telemetry: true - } +async function getRemoteConfig(actionState, configFile, apiDetails) { + const address = await parseRemoteFileAddress(actionState, configFile); + const shouldProxyRequest = await actionState.features.getValue( + "proxy_api_requests" /* ProxyApiRequests */ + ); + const proxy = shouldProxyRequest ? getRegistryProxy(actionState) : void 0; + const response = await getApiClientWithExternalAuth(apiDetails, proxy).rest.repos.getContent({ + owner: address.owner, + repo: address.repo, + path: address.path, + ref: address.ref }); + let fileContents; + if ("content" in response.data && response.data.content !== void 0) { + fileContents = response.data.content; + } else if (Array.isArray(response.data)) { + throw new ConfigurationError( + getConfigFileDirectoryGivenMessage(configFile) + ); + } else { + throw new ConfigurationError( + getConfigFileFormatInvalidMessage(configFile) + ); + } + const validateConfig = await actionState.features.getValue( + "validate_db_config" /* ValidateDbConfig */ + ); + return parseUserConfig( + actionState.logger, + configFile, + Buffer.from(fileContents, "base64").toString("binary"), + validateConfig + ); } // src/diff-informed-analysis-utils.ts @@ -148129,10 +150093,8 @@ async function cachePrefix(codeql, language) { } // src/config-utils.ts -var OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_MB = 2e4; +var OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_MB = 14e3; var OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_BYTES = OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_MB * 1e6; -var OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_V2_MB = 14e3; -var OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_V2_BYTES = OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_V2_MB * 1e6; var OVERLAY_MINIMUM_MEMORY_MB = 5 * 1024; var CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE = "2.24.3"; async function getSupportedLanguageMap(codeql, logger) { @@ -148318,7 +150280,7 @@ async function downloadCacheWithTime(codeQL, languages, logger) { const trapCacheDownloadTime = import_perf_hooks.performance.now() - start; return { trapCaches, trapCacheDownloadTime }; } -async function loadUserConfig(logger, configFile, workspacePath, apiDetails, tempDir, validateConfig) { +async function loadUserConfig(actionState, configFile, workspacePath, apiDetails, tempDir) { if (isLocal(configFile)) { if (configFile !== userConfigFromActionPath(tempDir)) { configFile = path10.resolve(workspacePath, configFile); @@ -148328,14 +150290,15 @@ async function loadUserConfig(logger, configFile, workspacePath, apiDetails, tem ); } } - return getLocalConfig(logger, configFile, validateConfig); - } else { - return await getRemoteConfig( - logger, - configFile, - apiDetails, - validateConfig + const validateConfig = await actionState.features.getValue( + "validate_db_config" /* ValidateDbConfig */ ); + return getLocalConfig(actionState.logger, configFile, validateConfig); + } else { + if (isExplicitRemotePath(configFile)) { + configFile = configFile.substring(REMOTE_PATH_PREFIX.length); + } + return await getRemoteConfig(actionState, configFile, apiDetails); } } var OVERLAY_ANALYSIS_FEATURES = { @@ -148381,8 +150344,8 @@ async function checkOverlayAnalysisFeatureEnabled(features, codeql, languages, c } return new Success(void 0); } -function runnerHasSufficientDiskSpace(diskUsage, logger, useV2ResourceChecks) { - const minimumDiskSpaceBytes = useV2ResourceChecks ? OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_V2_BYTES : OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_BYTES; +function runnerHasSufficientDiskSpace(diskUsage, logger) { + const minimumDiskSpaceBytes = OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_BYTES; if (diskUsage.numAvailableBytes < minimumDiskSpaceBytes) { const diskSpaceMb = Math.round(diskUsage.numAvailableBytes / 1e6); const minimumDiskSpaceMb = Math.round(minimumDiskSpaceBytes / 1e6); @@ -148415,8 +150378,8 @@ async function runnerHasSufficientMemory(codeql, ramInput, logger) { ); return true; } -async function checkRunnerResources(codeql, diskUsage, ramInput, logger, useV2ResourceChecks) { - if (!runnerHasSufficientDiskSpace(diskUsage, logger, useV2ResourceChecks)) { +async function checkRunnerResources(codeql, diskUsage, ramInput, logger) { + if (!runnerHasSufficientDiskSpace(diskUsage, logger)) { return new Failure("insufficient-disk-space" /* InsufficientDiskSpace */); } if (!await runnerHasSufficientMemory(codeql, ramInput, logger)) { @@ -148464,9 +150427,6 @@ async function checkOverlayEnablement(codeql, features, languages, sourceRoot, b "overlay_analysis_skip_resource_checks" /* OverlayAnalysisSkipResourceChecks */, codeql ); - const useV2ResourceChecks = await features.getValue( - "overlay_analysis_resource_checks_v2" /* OverlayAnalysisResourceChecksV2 */ - ); const checkOverlayStatus = await features.getValue( "overlay_analysis_status_check" /* OverlayAnalysisStatusCheck */ ); @@ -148478,13 +150438,7 @@ async function checkOverlayEnablement(codeql, features, languages, sourceRoot, b ); return new Failure("unable-to-determine-disk-usage" /* UnableToDetermineDiskUsage */); } - const resourceResult = performResourceChecks && diskUsage !== void 0 ? await checkRunnerResources( - codeql, - diskUsage, - ramInput, - logger, - useV2ResourceChecks - ) : new Success(void 0); + const resourceResult = performResourceChecks && diskUsage !== void 0 ? await checkRunnerResources(codeql, diskUsage, ramInput, logger) : new Success(void 0); if (resourceResult.isFailure()) { return resourceResult; } @@ -148586,10 +150540,10 @@ async function setCppTrapCachingEnvironmentVariables(config, logger) { ); } else if (config.trapCaches["cpp" /* cpp */]) { logger.info("Enabling TRAP caching for C/C++."); - core8.exportVariable(envVar, "true"); + core10.exportVariable(envVar, "true"); } else { logger.debug(`Disabling TRAP caching for C/C++.`); - core8.exportVariable(envVar, "false"); + core10.exportVariable(envVar, "false"); } } } @@ -148621,33 +150575,69 @@ async function applyIncrementalAnalysisSettings(config, hasDiffRanges, codeql, l }); } } -async function initConfig(features, inputs) { - const { logger, tempDir } = inputs; +async function determineUserConfig(action, tempDir, inputs) { + const validateConfig = await action.features.getValue( + "validate_db_config" /* ValidateDbConfig */ + ); if (inputs.configInput) { - if (inputs.configFile) { - logger.warning( - `Both a config file and config input were provided. Ignoring config file.` + const computedConfigPath = userConfigFromActionPath(tempDir); + const allowMergeConfigs = () => action.features.getValue("allow_merge_config_files" /* AllowMergeConfigFiles */); + if (inputs.configFile && isDefaultSetup(action.env) && await allowMergeConfigs()) { + const fromConfigInput = parseUserConfig( + action.logger, + "`config` input", + inputs.configInput, + validateConfig + ); + const fromConfigFile = await loadUserConfig( + action, + inputs.configFile, + inputs.workspacePath, + inputs.apiDetails, + tempDir + ); + const mergedConfig = mergeDefaultSetupAndUserConfigs( + action.logger, + fromConfigInput, + fromConfigFile + ); + fs9.writeFileSync(computedConfigPath, dump(mergedConfig)); + action.logger.debug( + `Using merged configurations from 'config' input with configuration from '${inputs.configFile}': ${computedConfigPath}` + ); + inputs.configFile = computedConfigPath; + return mergedConfig; + } else { + if (inputs.configFile) { + action.logger.warning( + `Both a config file and config input were provided. Ignoring config file.` + ); + } + fs9.writeFileSync(computedConfigPath, inputs.configInput); + inputs.configFile = computedConfigPath; + action.logger.debug( + `Using config from action input: ${inputs.configFile}` ); } - inputs.configFile = userConfigFromActionPath(tempDir); - fs9.writeFileSync(inputs.configFile, inputs.configInput); - logger.debug(`Using config from action input: ${inputs.configFile}`); } - let userConfig = {}; if (!inputs.configFile) { - logger.debug("No configuration file was provided"); + action.logger.debug("No configuration file was provided"); + return {}; } else { - logger.debug(`Using configuration file: ${inputs.configFile}`); - const validateConfig = await features.getValue("validate_db_config" /* ValidateDbConfig */); - userConfig = await loadUserConfig( - logger, + action.logger.debug(`Using configuration file: ${inputs.configFile}`); + return await loadUserConfig( + action, inputs.configFile, inputs.workspacePath, inputs.apiDetails, - tempDir, - validateConfig + tempDir ); } +} +async function initConfig(actionState, inputs) { + const { logger, features } = actionState; + const { tempDir } = inputs; + const userConfig = await determineUserConfig(actionState, tempDir, inputs); const config = await initActionState(inputs, userConfig); if (config.analysisKinds.length === 1 && isCodeQualityEnabled(config)) { if (hasQueryCustomisation(config.computedConfig)) { @@ -148664,7 +150654,6 @@ async function initConfig(features, inputs) { try { gitVersion = await getGitVersionOrThrow(); logger.info(`Using Git version ${gitVersion.fullVersion}`); - await logGitVersionTelemetry(config, gitVersion); } catch (e) { logger.warning(`Could not determine Git version: ${getErrorMessage(e)}`); if (isInTestMode() && process.env["CODEQL_ACTION_TOLERATE_MISSING_GIT_VERSION" /* TOLERATE_MISSING_GIT_VERSION */] !== "true") { @@ -148758,26 +150747,23 @@ async function initConfig(features, inputs) { await setCppTrapCachingEnvironmentVariables(config, logger); return config; } -function parseRegistries(registriesInput) { - try { - return registriesInput ? load(registriesInput) : void 0; - } catch { - throw new ConfigurationError( - "Invalid registries input. Must be a YAML string." - ); - } +function isExplicitLocalPath(configPath) { + return configPath.startsWith(LOCAL_PATH_PREFIX); } -function parseRegistriesWithoutCredentials(registriesInput) { - return parseRegistries(registriesInput)?.map((r) => { - const { url: url2, packages, kind } = r; - return { url: url2, packages, kind }; - }); +function isExplicitRemotePath(configPath) { + return configPath.startsWith(REMOTE_PATH_PREFIX); +} +function containsAtRef(configPath) { + return configPath.includes("@"); } function isLocal(configPath) { - if (configPath.indexOf("./") === 0) { + if (isExplicitLocalPath(configPath)) { return true; } - return configPath.indexOf("@") === -1; + if (isExplicitRemotePath(configPath)) { + return false; + } + return !containsAtRef(configPath); } function getLocalConfig(logger, configFile, validateConfig) { if (!fs9.existsSync(configFile)) { @@ -148792,41 +150778,6 @@ function getLocalConfig(logger, configFile, validateConfig) { validateConfig ); } -async function getRemoteConfig(logger, configFile, apiDetails, validateConfig) { - const format = new RegExp( - "(?[^/]+)/(?[^/]+)/(?[^@]+)@(?.*)" - ); - const pieces = format.exec(configFile); - if (pieces?.groups === void 0 || pieces.length < 5) { - throw new ConfigurationError( - getConfigFileRepoFormatInvalidMessage(configFile) - ); - } - const response = await getApiClientWithExternalAuth(apiDetails).rest.repos.getContent({ - owner: pieces.groups.owner, - repo: pieces.groups.repo, - path: pieces.groups.path, - ref: pieces.groups.ref - }); - let fileContents; - if ("content" in response.data && response.data.content !== void 0) { - fileContents = response.data.content; - } else if (Array.isArray(response.data)) { - throw new ConfigurationError( - getConfigFileDirectoryGivenMessage(configFile) - ); - } else { - throw new ConfigurationError( - getConfigFileFormatInvalidMessage(configFile) - ); - } - return parseUserConfig( - logger, - configFile, - Buffer.from(fileContents, "base64").toString("binary"), - validateConfig - ); -} function getPathToParsedConfigFile(tempDir) { return path10.join(tempDir, "config"); } @@ -148978,21 +150929,6 @@ function getPrimaryAnalysisKind(config) { function getPrimaryAnalysisConfig(config) { return getAnalysisConfig(getPrimaryAnalysisKind(config)); } -async function logGitVersionTelemetry(config, gitVersion) { - if (config.languages.length > 0) { - addNoLanguageDiagnostic( - config, - makeTelemetryDiagnostic( - "codeql-action/git-version-telemetry", - "Git version telemetry", - { - fullVersion: gitVersion.fullVersion, - truncatedVersion: gitVersion.truncatedVersion - } - ) - ); - } -} async function logGeneratedFilesTelemetry(config, duration, generatedFilesCount) { if (config.languages.length < 1) { return; @@ -149017,50 +150953,6 @@ var toolcache3 = __toESM(require_tool_cache()); var import_fast_deep_equal = __toESM(require_fast_deep_equal()); var semver9 = __toESM(require_semver2()); -// node_modules/uuid/dist-node/stringify.js -var byteToHex = []; -for (let i = 0; i < 256; ++i) { - byteToHex.push((i + 256).toString(16).slice(1)); -} -function unsafeStringify(arr, offset = 0) { - return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); -} - -// node_modules/uuid/dist-node/rng.js -var rnds8 = new Uint8Array(16); -function rng() { - return crypto.getRandomValues(rnds8); -} - -// node_modules/uuid/dist-node/v4.js -function v4(options, buf, offset) { - if (!buf && !options && crypto.randomUUID) { - return crypto.randomUUID(); - } - return _v4(options, buf, offset); -} -function _v4(options, buf, offset) { - options = options || {}; - const rnds = options.random ?? options.rng?.() ?? rng(); - if (rnds.length < 16) { - throw new Error("Random bytes length must be >= 16"); - } - rnds[6] = rnds[6] & 15 | 64; - rnds[8] = rnds[8] & 63 | 128; - if (buf) { - offset = offset || 0; - if (offset < 0 || offset + 16 > buf.length) { - throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`); - } - for (let i = 0; i < 16; ++i) { - buf[offset + i] = rnds[i]; - } - return buf; - } - return unsafeStringify(rnds); -} -var v4_default = v4; - // src/overlay/caching.ts var fs10 = __toESM(require("fs")); var actionsCache3 = __toESM(require_cache4()); @@ -149478,10 +151370,12 @@ async function extractTarZst(tar, dest, tarVersion, logger) { reject(new Error(`Error while extracting tar: ${err}`)); }); if (tar instanceof stream.Readable) { - tar.pipe(tarProcess.stdin).on("error", (err) => { - reject( - new Error(`Error while downloading and extracting tar: ${err}`) - ); + stream.pipeline(tar, tarProcess.stdin, (err) => { + if (err) { + reject( + new Error(`Error while downloading and extracting tar: ${err}`) + ); + } }); } tarProcess.on("exit", (code) => { @@ -149519,32 +151413,17 @@ function inferCompressionMethod(tarPath) { // src/tools-download.ts var fs12 = __toESM(require("fs")); -var os3 = __toESM(require("os")); +var os4 = __toESM(require("os")); var path11 = __toESM(require("path")); var import_perf_hooks2 = require("perf_hooks"); -var core9 = __toESM(require_core()); +var core11 = __toESM(require_core()); var import_http_client = __toESM(require_lib()); var toolcache2 = __toESM(require_tool_cache()); var import_follow_redirects = __toESM(require_follow_redirects()); var semver8 = __toESM(require_semver2()); var STREAMING_HIGH_WATERMARK_BYTES = 4 * 1024 * 1024; +var STREAMING_STALL_TIMEOUT_MS = 5 * 60 * 1e3; var TOOLCACHE_TOOL_NAME = "CodeQL"; -function makeDownloadFirstToolsDownloadDurations(downloadDurationMs, extractionDurationMs) { - return { - combinedDurationMs: downloadDurationMs + extractionDurationMs, - downloadDurationMs, - extractionDurationMs, - streamExtraction: false - }; -} -function makeStreamedToolsDownloadDurations(combinedDurationMs) { - return { - combinedDurationMs, - downloadDurationMs: void 0, - extractionDurationMs: void 0, - streamExtraction: true - }; -} async function downloadAndExtract(codeqlURL, compressionMethod, dest, authorization, headers, tarVersion, logger) { logger.info( `Downloading CodeQL tools from ${codeqlURL} . This may take a while.` @@ -149569,17 +151448,13 @@ async function downloadAndExtract(codeqlURL, compressionMethod, dest, authorizat combinedDurationMs )}).` ); - return { - compressionMethod, - toolsUrl: sanitizeUrlForStatusReport(codeqlURL), - ...makeStreamedToolsDownloadDurations(combinedDurationMs) - }; + return {}; } } catch (e) { - core9.warning( + core11.warning( `Failed to download and extract CodeQL bundle using streaming with error: ${getErrorMessage(e)}` ); - core9.warning(`Falling back to downloading the bundle before extracting.`); + core11.warning(`Falling back to downloading the bundle before extracting.`); await cleanUpPath(dest, "CodeQL bundle", logger); } const toolsDownloadStart = import_perf_hooks2.performance.now(); @@ -149615,14 +151490,7 @@ async function downloadAndExtract(codeqlURL, compressionMethod, dest, authorizat } finally { await cleanUpPath(archivedBundlePath, "CodeQL bundle archive", logger); } - return { - compressionMethod, - toolsUrl: sanitizeUrlForStatusReport(codeqlURL), - ...makeDownloadFirstToolsDownloadDurations( - downloadDurationMs, - extractionDurationMs - ) - }; + return { downloadDurationMs }; } async function downloadAndExtractZstdWithStreaming(codeqlURL, dest, authorization, headers, tarVersion, logger) { fs12.mkdirSync(dest, { recursive: true }); @@ -149632,8 +151500,8 @@ async function downloadAndExtractZstdWithStreaming(codeqlURL, dest, authorizatio authorization ? { authorization } : {}, headers ); - const response = await new Promise( - (resolve14) => import_follow_redirects.https.get( + const response = await new Promise((resolve14, reject) => { + const request3 = import_follow_redirects.https.get( codeqlURL, { headers, @@ -149643,9 +151511,18 @@ async function downloadAndExtractZstdWithStreaming(codeqlURL, dest, authorizatio agent }, (r) => resolve14(r) - ) - ); + ); + request3.on("error", reject); + request3.setTimeout(STREAMING_STALL_TIMEOUT_MS, () => { + request3.destroy( + new Error( + `No data received for ${formatDuration(STREAMING_STALL_TIMEOUT_MS)}.` + ) + ); + }); + }); if (response.statusCode !== 200) { + response.resume(); throw new Error( `Failed to download CodeQL bundle from ${codeqlURL}. HTTP status code: ${response.statusCode}.` ); @@ -149657,7 +151534,7 @@ function getToolcacheDirectory(version) { getRequiredEnvParam("RUNNER_TOOL_CACHE"), TOOLCACHE_TOOL_NAME, semver8.clean(version) || version, - os3.arch() || "" + os4.arch() || "" ); } function writeToolcacheMarkerFile(extractedPath, logger) { @@ -149665,11 +151542,6 @@ function writeToolcacheMarkerFile(extractedPath, logger) { fs12.writeFileSync(markerFilePath, ""); logger.info(`Created toolcache marker file ${markerFilePath}`); } -function sanitizeUrlForStatusReport(url2) { - return ["github/codeql-action", "dsp-testing/codeql-cli-nightlies"].some( - (repo) => url2.startsWith(`https://github.com/${repo}/releases/download/`) - ) ? url2 : "sanitized-value"; -} // src/setup-codeql.ts var CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action"; @@ -149961,10 +151833,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO } } else if (toolsInput !== void 0 && toolsInput === CODEQL_TOOLCACHE_INPUT) { let latestToolcacheVersion; - const allowToolcacheValueFF = await features.getValue( - "allow_toolcache_input" /* AllowToolcacheInput */ - ); - const allowToolcacheValue = allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); + const allowToolcacheValue = isDynamicWorkflow() || isInTestMode(); if (allowToolcacheValue) { logger.info( `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: ${toolsInput}'.` @@ -149980,15 +151849,9 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` ); } else { - if (allowToolcacheValueFF) { - logger.warning( - `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.` - ); - } else { - logger.info( - `Ignoring 'tools: ${toolsInput}' because the feature is not enabled.` - ); - } + logger.warning( + `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.` + ); } const version = await resolveDefaultCliVersion( defaultCliVersion, @@ -150278,8 +152141,7 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defau codeqlFolder, toolsDownloadStatusReport, toolsSource, - toolsVersion, - zstdAvailability + toolsVersion }; } async function useZstdBundle(cliVersion2, tarSupportsZstd) { @@ -150403,9 +152265,9 @@ async function getCombinedTracerConfig(codeql, config) { // src/codeql.ts var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.19.4"; -var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; -var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; -var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; +var CODEQL_NEXT_MINIMUM_VERSION = "2.20.7"; +var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.16"; +var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-07-01"; var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, features, logger, checkVersion) { try { @@ -150413,8 +152275,7 @@ async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliV codeqlFolder, toolsDownloadStatusReport, toolsSource, - toolsVersion, - zstdAvailability + toolsVersion } = await setupCodeQLBundle( toolsInput, apiDetails, @@ -150426,11 +152287,6 @@ async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliV features, logger ); - logger.debug( - `Bundle download status report: ${JSON.stringify( - toolsDownloadStatusReport - )}` - ); let codeqlCmd = path14.join(codeqlFolder, "codeql", "codeql"); if (process.platform === "win32") { codeqlCmd += ".exe"; @@ -150439,13 +152295,12 @@ async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliV `Unsupported platform: ${process.platform}` ); } - cachedCodeQL = await getCodeQLForCmd(codeqlCmd, checkVersion); + cachedCodeQL = await getCodeQLForCmd(logger, codeqlCmd, checkVersion); return { codeql: cachedCodeQL, toolsDownloadStatusReport, toolsSource, - toolsVersion, - zstdAvailability + toolsVersion }; } catch (rawError) { const e = wrapApiConfigurationError(rawError); @@ -150457,13 +152312,13 @@ Details: ${e.stack}` : ""}` ); } } -async function getCodeQL(cmd) { +async function getCodeQL(logger, cmd) { if (cachedCodeQL === void 0) { - cachedCodeQL = await getCodeQLForCmd(cmd, true); + cachedCodeQL = await getCodeQLForCmd(logger, cmd, true); } return cachedCodeQL; } -async function getCodeQLForCmd(cmd, checkVersion) { +async function getCodeQLForCmd(logger, cmd, checkVersion) { const codeql = { getPath() { return cmd; @@ -150471,22 +152326,19 @@ async function getCodeQLForCmd(cmd, checkVersion) { async getVersion() { let result = getCachedCodeQlVersion(cmd); if (result === void 0) { - const output = await runCli(cmd, ["version", "--format=json"], { - noStreamStdout: true - }); - try { - result = JSON.parse(output); - } catch { - throw Error( - `Invalid JSON output from \`version --format=json\`: ${output}` - ); - } + result = await runCliJson( + cmd, + ["version", "--format=json"], + { + noStreamStdout: true + } + ); cacheCodeQlVersion(cmd, result); } return result; }, async printVersion() { - core10.info(JSON.stringify(await this.getVersion(), null, 2)); + core12.info(JSON.stringify(await this.getVersion(), null, 2)); }, async supportsFeature(feature) { return isSupportedToolsFeature(await this.getVersion(), feature); @@ -150503,7 +152355,7 @@ async function getCodeQLForCmd(cmd, checkVersion) { async isScannedLanguage(language) { return !await this.isTracedLanguage(language); }, - async databaseInitCluster(config, sourceRoot, processName, qlconfigFile, logger) { + async databaseInitCluster(config, sourceRoot, processName, qlconfigFile) { const extraArgs = config.languages.map( (language) => `--language=${language}` ); @@ -150640,23 +152492,18 @@ async function getCodeQLForCmd(cmd, checkVersion) { async resolveLanguages({ filterToLanguagesWithQueries } = { filterToLanguagesWithQueries: false }) { - const codeqlArgs = [ + return runCliJson(cmd, [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + // TODO: Unconditionally include `--filter-to-languages-with-queries` + // once CODEQL_MINIMUM_VERSION is at least v2.23.0 + // — the first version to support this flag. ...filterToLanguagesWithQueries ? ["--filter-to-languages-with-queries"] : [], ...getExtraOptionsFromEnv(["resolve", "languages"]) - ]; - const output = await runCli(cmd, codeqlArgs); - try { - return JSON.parse(output); - } catch (e) { - throw new Error( - `Unexpected output from codeql resolve languages with --format=betterjson: ${e}` - ); - } + ]); }, async resolveBuildEnvironment(workingDir, language) { const codeqlArgs = [ @@ -150669,15 +152516,7 @@ async function getCodeQLForCmd(cmd, checkVersion) { if (workingDir !== void 0) { codeqlArgs.push("--working-dir", workingDir); } - const output = await runCli(cmd, codeqlArgs); - try { - return JSON.parse(output); - } catch (e) { - throw new Error( - `Unexpected output from codeql resolve build-environment: ${e} in -${output}` - ); - } + return await runCliJson(cmd, codeqlArgs); }, async databaseRunQueries(databasePath, flags, queries = []) { const codeqlArgs = [ @@ -150843,14 +152682,9 @@ ${output}` ...getExtraOptionsFromEnv(["resolve", "queries"]), ...queries ]; - const output = await runCli(cmd, codeqlArgs, { noStreamStdout: true }); - try { - return JSON.parse(output); - } catch (e) { - throw new Error( - `Unexpected output from codeql resolve queries --format=startingpacks: ${e}` - ); - } + return await runCliJson(cmd, codeqlArgs, { + noStreamStdout: true + }); }, async resolveDatabase(databasePath) { const codeqlArgs = [ @@ -150860,14 +152694,9 @@ ${output}` "--format=json", ...getExtraOptionsFromEnv(["resolve", "database"]) ]; - const output = await runCli(cmd, codeqlArgs, { noStreamStdout: true }); - try { - return JSON.parse(output); - } catch (e) { - throw new Error( - `Unexpected output from codeql resolve database --format=json: ${e}` - ); - } + return await runCliJson(cmd, codeqlArgs, { + noStreamStdout: true + }); }, async mergeResults(sarifFiles, outputFile, { mergeRunsFromEqualCategory = false @@ -150894,12 +152723,12 @@ ${output}` ); } else if (checkVersion && process.env["CODEQL_ACTION_SUPPRESS_DEPRECATED_SOON_WARNING" /* SUPPRESS_DEPRECATED_SOON_WARNING */] !== "true" && !await codeQlVersionAtLeast(codeql, CODEQL_NEXT_MINIMUM_VERSION)) { const result = await codeql.getVersion(); - core10.warning( + core12.warning( `CodeQL CLI version ${result.version} was discontinued on ${GHES_MOST_RECENT_DEPRECATION_DATE} alongside GitHub Enterprise Server ${GHES_VERSION_MOST_RECENTLY_DEPRECATED} and will not be supported by the next minor release of the CodeQL Action. Please update to CodeQL CLI version ${CODEQL_NEXT_MINIMUM_VERSION} or later. For instance, if you have specified a custom version of the CLI using the 'tools' input to the 'init' Action, you can remove this input to use the default version. Alternatively, if you want to continue using CodeQL CLI version ${result.version}, you can replace 'github/codeql-action/*@v${getActionVersion().split(".")[0]}' by 'github/codeql-action/*@v${getActionVersion()}' in your code scanning workflow to continue using this version of the CodeQL Action.` ); - core10.exportVariable("CODEQL_ACTION_SUPPRESS_DEPRECATED_SOON_WARNING" /* SUPPRESS_DEPRECATED_SOON_WARNING */, "true"); + core12.exportVariable("CODEQL_ACTION_SUPPRESS_DEPRECATED_SOON_WARNING" /* SUPPRESS_DEPRECATED_SOON_WARNING */, "true"); } return codeql; } @@ -150949,6 +152778,16 @@ async function runCli(cmd, args = [], opts = {}) { throw e; } } +async function runCliJson(cmd, args = [], opts = {}) { + const output = await runCli(cmd, args, opts); + try { + return JSON.parse(output); + } catch (e) { + throw Error( + `Unexpected output from codeql ${args.join(" ")}: ${getErrorMessage(e)}` + ); + } +} async function writeCodeScanningConfigFile(config, logger) { const codeScanningConfigFile = getGeneratedCodeScanningConfigPath(config); const augmentedConfig = appendExtraQueryExclusions( @@ -150998,7 +152837,7 @@ function applyAutobuildAzurePipelinesTimeoutFix() { ].join(" "); } async function getJobRunUuidSarifOptions() { - const jobRunUuid = process.env["JOB_RUN_UUID" /* JOB_RUN_UUID */]; + const jobRunUuid = process.env["CODEQL_ACTION_JOB_RUN_UUID" /* JOB_RUN_UUID */]; return jobRunUuid ? [`--sarif-run-property=jobRunUuid=${jobRunUuid}`] : []; } @@ -151054,25 +152893,25 @@ async function setupCppAutobuild(codeql, logger) { logger ); if (await features.getValue("cpp_dependency_installation_enabled" /* CppDependencyInstallation */, codeql)) { - if (process.env["RUNNER_ENVIRONMENT"] === "self-hosted" && process.env[envVar] !== "true") { + if (process.env["RUNNER_ENVIRONMENT" /* RUNNER_ENVIRONMENT */] === "self-hosted" && process.env[envVar] !== "true") { logger.info( `Disabling ${featureName} as we are on a self-hosted runner.${getWorkflowEventName() !== "dynamic" ? ` To override this, set the ${envVar} environment variable to 'true' in your workflow. See ${"https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow" /* DEFINE_ENV_VARIABLES */} for more information.` : ""}` ); - core11.exportVariable(envVar, "false"); + core13.exportVariable(envVar, "false"); } else { logger.info( `Enabling ${featureName}. This can be disabled by setting the ${envVar} environment variable to 'false'. See ${"https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow" /* DEFINE_ENV_VARIABLES */} for more information.` ); - core11.exportVariable(envVar, "true"); + core13.exportVariable(envVar, "true"); } } else { logger.info(`Disabling ${featureName}.`); - core11.exportVariable(envVar, "false"); + core13.exportVariable(envVar, "false"); } } async function runAutobuild(config, language, logger) { logger.startGroup(`Attempting to automatically build ${language} code`); - const codeQL = await getCodeQL(config.codeQLCmd); + const codeQL = await getCodeQL(logger, config.codeQLCmd); if (language === "cpp" /* cpp */) { await setupCppAutobuild(codeQL, logger); } @@ -151082,13 +152921,13 @@ async function runAutobuild(config, language, logger) { await codeQL.runAutobuild(config, language); } if (language === "go" /* go */) { - core11.exportVariable("CODEQL_ACTION_DID_AUTOBUILD_GOLANG" /* DID_AUTOBUILD_GOLANG */, "true"); + core13.exportVariable("CODEQL_ACTION_DID_AUTOBUILD_GOLANG" /* DID_AUTOBUILD_GOLANG */, "true"); } logger.endGroup(); } // src/dependency-caching.ts -var os4 = __toESM(require("os")); +var os5 = __toESM(require("os")); var import_path2 = require("path"); var actionsCache4 = __toESM(require_cache4()); var glob = __toESM(require_glob()); @@ -151100,9 +152939,9 @@ function getJavaTempDependencyDir() { async function getJavaDependencyDirs() { return [ // Maven - (0, import_path2.join)(os4.homedir(), ".m2", "repository"), + (0, import_path2.join)(os5.homedir(), ".m2", "repository"), // Gradle - (0, import_path2.join)(os4.homedir(), ".gradle", "caches"), + (0, import_path2.join)(os5.homedir(), ".gradle", "caches"), // CodeQL Java build-mode: none getJavaTempDependencyDir() ]; @@ -151113,7 +152952,7 @@ function getCsharpTempDependencyDir() { async function getCsharpDependencyDirs(codeql, features) { const dirs = [ // Nuget - (0, import_path2.join)(os4.homedir(), ".nuget", "packages") + (0, import_path2.join)(os5.homedir(), ".nuget", "packages") ]; if (await features.getValue("csharp_cache_bmn" /* CsharpCacheBuildModeNone */, codeql)) { dirs.push(getCsharpTempDependencyDir()); @@ -151168,7 +153007,7 @@ var defaultCacheConfigs = { getHashPatterns: getCsharpHashPatterns }, go: { - getDependencyPaths: async () => [(0, import_path2.join)(os4.homedir(), "go", "pkg", "mod")], + getDependencyPaths: async () => [(0, import_path2.join)(os5.homedir(), "go", "pkg", "mod")], getHashPatterns: async () => internal.makePatternCheck(["**/go.sum"]) } }; @@ -151493,7 +153332,7 @@ extensions: `; let data = ranges.map((range2) => { const filename = path15.join(checkoutPath, range2.path).replaceAll(path15.sep, "/"); - return ` - [${dump(filename, { quoteStyle: "single" }).trim()}, ${range2.startLine}, ${range2.endLine}] + return ` - [${dump(filename, { forceQuotes: true, quoteStyle: "single" }).trim()}, ${range2.startLine}, ${range2.endLine}] `; }).join(""); if (!data) { @@ -151913,283 +153752,6 @@ async function uploadBundledDatabase(repositoryNwo, language, commitOid, bundled } } -// src/status-report.ts -var os5 = __toESM(require("os")); -var core12 = __toESM(require_core()); -function isFirstPartyAnalysis(actionName) { - if (actionName !== "upload-sarif" /* UploadSarif */) { - return true; - } - return process.env["CODEQL_ACTION_INIT_HAS_RUN" /* INIT_ACTION_HAS_RUN */] === "true"; -} -function isThirdPartyAnalysis(actionName) { - return !isFirstPartyAnalysis(actionName); -} -var JobStatus = /* @__PURE__ */ ((JobStatus2) => { - JobStatus2["UnknownStatus"] = "JOB_STATUS_UNKNOWN"; - JobStatus2["SuccessStatus"] = "JOB_STATUS_SUCCESS"; - JobStatus2["FailureStatus"] = "JOB_STATUS_FAILURE"; - JobStatus2["ConfigErrorStatus"] = "JOB_STATUS_CONFIGURATION_ERROR"; - return JobStatus2; -})(JobStatus || {}); -function getActionsStatus(error3, otherFailureCause) { - if (error3 || otherFailureCause) { - return error3 instanceof ConfigurationError ? "user-error" : "failure"; - } else { - return "success"; - } -} -function getJobStatusDisplayName(status) { - switch (status) { - case "JOB_STATUS_SUCCESS" /* SuccessStatus */: - return "success"; - case "JOB_STATUS_FAILURE" /* FailureStatus */: - return "failure"; - case "JOB_STATUS_CONFIGURATION_ERROR" /* ConfigErrorStatus */: - return "configuration error"; - case "JOB_STATUS_UNKNOWN" /* UnknownStatus */: - return "unknown"; - default: - assertNever(status); - } -} -function setJobStatusIfUnsuccessful(actionStatus) { - if (actionStatus === "user-error") { - core12.exportVariable( - "CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, - process.env["CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */] ?? "JOB_STATUS_CONFIGURATION_ERROR" /* ConfigErrorStatus */ - ); - } else if (actionStatus === "failure" || actionStatus === "aborted") { - core12.exportVariable( - "CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, - process.env["CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */] ?? "JOB_STATUS_FAILURE" /* FailureStatus */ - ); - } -} -async function createStatusReportBase(actionName, status, actionStartedAt, config, diskInfo, logger, cause, exception) { - try { - const commitOid = getOptionalInput("sha") || process.env["GITHUB_SHA"] || ""; - const ref = await getRef(); - const jobRunUUID = process.env["JOB_RUN_UUID" /* JOB_RUN_UUID */] || ""; - const workflowRunID = getWorkflowRunID(); - const workflowRunAttempt = getWorkflowRunAttempt(); - const workflowName = process.env["GITHUB_WORKFLOW"] || ""; - const jobName = process.env["GITHUB_JOB"] || ""; - const analysis_key = await getAnalysisKey(); - let workflowStartedAt = process.env["CODEQL_WORKFLOW_STARTED_AT" /* WORKFLOW_STARTED_AT */]; - if (workflowStartedAt === void 0) { - workflowStartedAt = actionStartedAt.toISOString(); - core12.exportVariable("CODEQL_WORKFLOW_STARTED_AT" /* WORKFLOW_STARTED_AT */, workflowStartedAt); - } - const runnerOs = getRequiredEnvParam("RUNNER_OS"); - const codeQlCliVersion = getCachedCodeQlVersion(); - const actionRef = process.env["GITHUB_ACTION_REF"] || ""; - const testingEnvironment = getTestingEnvironment(); - if (testingEnvironment) { - core12.exportVariable("CODEQL_ACTION_TESTING_ENVIRONMENT" /* TESTING_ENVIRONMENT */, testingEnvironment); - } - const isSteadyStateDefaultSetupRun = process.env["CODE_SCANNING_IS_STEADY_STATE_DEFAULT_SETUP"] === "true"; - const statusReport = { - action_name: actionName, - action_oid: "unknown", - // TODO decide if it's possible to fill this in - action_ref: actionRef, - action_started_at: actionStartedAt.toISOString(), - action_version: getActionVersion(), - analysis_kinds: config?.analysisKinds?.join(","), - analysis_key, - build_mode: config?.buildMode, - commit_oid: commitOid, - first_party_analysis: isFirstPartyAnalysis(actionName), - job_name: jobName, - job_run_uuid: jobRunUUID, - ref, - runner_os: runnerOs, - started_at: workflowStartedAt, - status, - steady_state_default_setup: isSteadyStateDefaultSetupRun, - testing_environment: testingEnvironment || "", - workflow_name: workflowName, - workflow_run_attempt: workflowRunAttempt, - workflow_run_id: workflowRunID - }; - try { - statusReport.actions_event_name = getWorkflowEventName(); - } catch (e) { - logger.warning( - `Could not determine the workflow event name: ${getErrorMessage(e)}.` - ); - } - if (config) { - statusReport.languages = config.languages?.join(","); - } - if (diskInfo) { - statusReport.runner_available_disk_space_bytes = diskInfo.numAvailableBytes; - statusReport.runner_total_disk_space_bytes = diskInfo.numTotalBytes; - } - if (cause) { - statusReport.cause = cause; - } - if (exception) { - statusReport.exception = exception; - } - if (status === "success" || status === "failure" || status === "aborted" || status === "user-error") { - statusReport.completed_at = (/* @__PURE__ */ new Date()).toISOString(); - } - const matrix = getRequiredInput("matrix"); - if (matrix) { - statusReport.matrix_vars = matrix; - } - if ("RUNNER_ARCH" in process.env) { - statusReport.runner_arch = process.env["RUNNER_ARCH"]; - } - if (!(runnerOs === "Linux" && isSelfHostedRunner())) { - statusReport.runner_os_release = os5.release(); - } - if (codeQlCliVersion !== void 0) { - statusReport.codeql_version = codeQlCliVersion.version; - } - const imageVersion = process.env["ImageVersion"]; - if (imageVersion) { - statusReport.runner_image_version = imageVersion; - } - return statusReport; - } catch (e) { - logger.warning( - `Failed to gather information for telemetry: ${getErrorMessage(e)}. Will skip sending status report.` - ); - if (isInTestMode()) { - throw e; - } - return void 0; - } -} -var OUT_OF_DATE_MSG = "CodeQL Action is out-of-date. Please upgrade to the latest version of `codeql-action`."; -var INCOMPATIBLE_MSG = "CodeQL Action version is incompatible with the API endpoint. Please update to a compatible version of `codeql-action`."; -async function sendStatusReport(statusReport) { - setJobStatusIfUnsuccessful(statusReport.status); - const statusReportJSON = JSON.stringify(statusReport); - core12.debug(`Sending status report: ${statusReportJSON}`); - if (isInTestMode()) { - core12.debug("In test mode. Status reports are not uploaded."); - return; - } - const nwo = getRepositoryNwo(); - const client = getApiClient(); - try { - await client.request( - "PUT /repos/:owner/:repo/code-scanning/analysis/status", - { - owner: nwo.owner, - repo: nwo.repo, - data: statusReportJSON - } - ); - } catch (e) { - const httpError = asHTTPError(e); - if (httpError !== void 0) { - switch (httpError.status) { - case 403: - if (getWorkflowEventName() === "push" && process.env["GITHUB_ACTOR"] === "dependabot[bot]") { - core12.warning( - `Workflows triggered by Dependabot on the "push" event run with read-only access. Uploading CodeQL results requires write access. To use CodeQL with Dependabot, please ensure you are using the "pull_request" event for this workflow and avoid triggering on the "push" event for Dependabot branches. See ${"https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#scanning-on-push" /* SCANNING_ON_PUSH */} for more information on how to configure these events.` - ); - } else { - core12.warning( - `This run of the CodeQL Action does not have permission to access the CodeQL Action API endpoints. This could be because the Action is running on a pull request from a fork. If not, please ensure the workflow has at least the 'security-events: read' permission. Details: ${httpError.message}` - ); - } - return; - case 404: - core12.warning(httpError.message); - return; - case 422: - if (getRequiredEnvParam("GITHUB_SERVER_URL") !== GITHUB_DOTCOM_URL) { - core12.debug(INCOMPATIBLE_MSG); - } else { - core12.debug(OUT_OF_DATE_MSG); - } - return; - } - } - core12.warning( - `An unexpected error occurred when sending a status report: ${getErrorMessage( - e - )}` - ); - } -} -async function createInitWithConfigStatusReport(config, initStatusReport, configFile, totalCacheSize, overlayBaseDatabaseStats, dependencyCachingResults) { - const languages = config.languages.join(","); - const paths = (config.originalUserInput.paths || []).join(","); - const pathsIgnore = (config.originalUserInput["paths-ignore"] || []).join( - "," - ); - const disableDefaultQueries = config.originalUserInput["disable-default-queries"] ? languages : ""; - const queries = []; - let queriesInput = getOptionalInput("queries")?.trim(); - if (queriesInput === void 0 || queriesInput.startsWith("+")) { - queries.push( - ...(config.originalUserInput.queries || []).map((q) => q.uses) - ); - } - if (queriesInput !== void 0) { - queriesInput = queriesInput.startsWith("+") ? queriesInput.slice(1) : queriesInput; - queries.push(...queriesInput.split(",")); - } - let packs = {}; - if (Array.isArray(config.computedConfig.packs)) { - packs[config.languages[0]] = config.computedConfig.packs; - } else if (config.computedConfig.packs !== void 0) { - packs = config.computedConfig.packs; - } - return { - ...initStatusReport, - config_file: configFile ?? "", - disable_default_queries: disableDefaultQueries, - paths, - paths_ignore: pathsIgnore, - queries: queries.join(","), - packs: JSON.stringify(packs), - trap_cache_languages: Object.keys(config.trapCaches).join(","), - trap_cache_download_size_bytes: totalCacheSize, - trap_cache_download_duration_ms: Math.round(config.trapCacheDownloadTime), - overlay_base_database_download_size_bytes: overlayBaseDatabaseStats?.databaseSizeBytes, - overlay_base_database_download_duration_ms: overlayBaseDatabaseStats?.databaseDownloadDurationMs, - dependency_caching_restore_results: dependencyCachingResults, - query_filters: JSON.stringify( - config.originalUserInput["query-filters"] ?? [] - ), - registries: JSON.stringify( - parseRegistriesWithoutCredentials(getOptionalInput("registries")) ?? [] - ) - }; -} -async function sendUnhandledErrorStatusReport(actionName, actionStartedAt, error3, logger) { - try { - const statusReport = await createStatusReportBase( - actionName, - "failure", - actionStartedAt, - void 0, - void 0, - logger, - `Unhandled CodeQL Action error: ${getErrorMessage(error3)}`, - error3 instanceof Error ? error3.stack : void 0 - ); - if (statusReport !== void 0) { - await sendStatusReport(statusReport); - } - } catch (e) { - logger.warning( - `Failed to send the unhandled error status report: ${getErrorMessage(e)}.` - ); - if (isInTestMode()) { - throw e; - } - } -} - // src/upload-lib.ts var upload_lib_exports = {}; __export(upload_lib_exports, { @@ -152216,7 +153778,7 @@ var fs21 = __toESM(require("fs")); var path18 = __toESM(require("path")); var url = __toESM(require("url")); var import_zlib = __toESM(require("zlib")); -var core14 = __toESM(require_core()); +var core15 = __toESM(require_core()); var jsonschema2 = __toESM(require_lib2()); // src/fingerprints.ts @@ -153344,19 +154906,13 @@ async function addFingerprints(sarifLog, sourceRoot, logger) { // src/init.ts var fs19 = __toESM(require("fs")); var path17 = __toESM(require("path")); -var core13 = __toESM(require_core()); +var core14 = __toESM(require_core()); var toolrunner4 = __toESM(require_toolrunner()); -var github2 = __toESM(require_github()); +var github3 = __toESM(require_github()); var io6 = __toESM(require_io()); async function initCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, features, logger) { logger.startGroup("Setup CodeQL tools"); - const { - codeql, - toolsDownloadStatusReport, - toolsSource, - toolsVersion, - zstdAvailability - } = await setupCodeQL( + const { codeql, toolsDownloadStatusReport, toolsSource, toolsVersion } = await setupCodeQL( toolsInput, apiDetails, tempDir, @@ -153374,16 +154930,15 @@ async function initCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVe codeql, toolsDownloadStatusReport, toolsSource, - toolsVersion, - zstdAvailability + toolsVersion }; } -async function initConfig2(features, inputs) { +async function initConfig2(actionState, inputs) { return await withGroupAsync("Load language configuration", async () => { - return await initConfig(features, inputs); + return await initConfig(actionState, inputs); }); } -async function runDatabaseInitCluster(databaseInitEnvironment, codeql, config, sourceRoot, processName, qlconfigFile, logger) { +async function runDatabaseInitCluster(databaseInitEnvironment, codeql, config, sourceRoot, processName, qlconfigFile) { fs19.mkdirSync(config.dbLocation, { recursive: true }); await wrapEnvironment( databaseInitEnvironment, @@ -153391,8 +154946,7 @@ async function runDatabaseInitCluster(databaseInitEnvironment, codeql, config, s config, sourceRoot, processName, - qlconfigFile, - logger + qlconfigFile ) ); } @@ -153550,7 +155104,7 @@ function logFileCoverageOnPrsDeprecationWarning(logger) { if (process.env["CODEQL_ACTION_DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION" /* DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION */]) { return; } - const repositoryOwnerType = github2.context.payload.repository?.owner.type; + const repositoryOwnerType = github3.context.payload.repository?.owner.type; let message = "Starting April 2026, the CodeQL Action will skip computing file coverage information on pull requests to improve analysis performance. File coverage information will still be computed on non-PR analyses."; const envVarOptOut = "set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true`."; const repoPropertyOptOut = 'create a custom repository property with the name `github-codeql-file-coverage-on-prs` and the type "True/false", then set this property to `true` in the repository\'s settings.'; @@ -153574,7 +155128,7 @@ To opt out of this change, switch to an advanced setup workflow and ${envVarOptO To opt out of this change, ${envVarOptOut}`; } logger.warning(message); - core13.exportVariable("CODEQL_ACTION_DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION" /* DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION */, "true"); + core14.exportVariable("CODEQL_ACTION_DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION" /* DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION */, "true"); } // src/sarif/index.ts @@ -153688,7 +155242,7 @@ async function combineSarifFilesUsingCLI(sarifFiles, gitHubVersion, features, lo logger.warning( `Uploading multiple SARIF runs with the same category is deprecated ${deprecationWarningMessage}. Please update your workflow to upload a single run per category. ${deprecationMoreInformationMessage}` ); - core14.exportVariable("CODEQL_MERGE_SARIF_DEPRECATION_WARNING", "true"); + core15.exportVariable("CODEQL_MERGE_SARIF_DEPRECATION_WARNING", "true"); } return combineSarifFiles(sarifFiles, logger); } @@ -153696,7 +155250,7 @@ async function combineSarifFilesUsingCLI(sarifFiles, gitHubVersion, features, lo let tempDir = getTemporaryDirectory(); const config = await getConfig(tempDir, logger); if (config !== void 0) { - codeQL = await getCodeQL(config.codeQLCmd); + codeQL = await getCodeQL(logger, config.codeQLCmd); tempDir = config.tempDir; } else { logger.info( @@ -153789,13 +155343,13 @@ async function uploadPayload(payload, repositoryNwo, logger, analysis) { if (httpError !== void 0) { switch (httpError.status) { case 403: - core14.warning(httpError.message || GENERIC_403_MSG); + core15.warning(httpError.message || GENERIC_403_MSG); break; case 404: - core14.warning(httpError.message || GENERIC_404_MSG); + core15.warning(httpError.message || GENERIC_404_MSG); break; default: - core14.warning(httpError.message); + core15.warning(httpError.message); break; } } @@ -154233,7 +155787,7 @@ function validateUniqueCategory(sarifLog, sentinelPrefix) { `Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job per tool/category. The easiest fix is to specify a unique value for the \`category\` input. If .runs[].automationDetails.id is specified in the sarif file, that will take precedence over your configured \`category\`. Category: (${id ? id : "none"}) Tool: (${tool ? tool : "none"})` ); } - core14.exportVariable(sentinelEnvVar, sentinelEnvVar); + core15.exportVariable(sentinelEnvVar, sentinelEnvVar); } } function sanitize(str) { @@ -154403,7 +155957,7 @@ async function runAutobuildIfLegacyGoWorkflow(config, logger) { ); await runAutobuild(config, "go" /* go */, logger); } -async function run(startedAt) { +async function run({ startedAt, logger }) { let uploadResults = void 0; let runStats = void 0; let config = void 0; @@ -154413,7 +155967,6 @@ async function run(startedAt) { let didUploadTrapCaches = false; let dependencyCacheResults; let databaseUploadResults = []; - const logger = getActionsLogger(); try { initializeEnvironment(getActionVersion()); persistInputs(); @@ -154434,7 +155987,7 @@ async function run(startedAt) { "Config file could not be found at expected location. Has the 'init' action been called?" ); } - const codeql = await getCodeQL(config.codeQLCmd); + const codeql = await getCodeQL(logger, config.codeQLCmd); if (hasBadExpectErrorInput()) { throw new ConfigurationError( "`expect-error` input parameter is for internal use only. It should only be set by codeql-action or a fork." @@ -154452,7 +156005,7 @@ async function run(startedAt) { } const apiDetails = getApiDetails(); const outputDir = getRequiredInput("output"); - core15.exportVariable("CODEQL_ACTION_SARIF_RESULTS_OUTPUT_DIR" /* SARIF_RESULTS_OUTPUT_DIR */, outputDir); + core16.exportVariable("CODEQL_ACTION_SARIF_RESULTS_OUTPUT_DIR" /* SARIF_RESULTS_OUTPUT_DIR */, outputDir); const threads = getThreadsFlag( getOptionalInput("threads") || process.env["CODEQL_THREADS"], logger @@ -154504,8 +156057,8 @@ async function run(startedAt) { for (const language of config.languages) { dbLocations[language] = getCodeQLDatabasePath(config, language); } - core15.setOutput("db-locations", dbLocations); - core15.setOutput("sarif-output", import_path4.default.resolve(outputDir)); + core16.setOutput("db-locations", dbLocations); + core16.setOutput("sarif-output", import_path4.default.resolve(outputDir)); const uploadKind = getUploadValue( getOptionalInput("upload") ); @@ -154522,13 +156075,13 @@ async function run(startedAt) { getOptionalInput("post-processed-sarif-path") ); if (uploadResults["code-scanning" /* CodeScanning */] !== void 0) { - core15.setOutput( + core16.setOutput( "sarif-id", uploadResults["code-scanning" /* CodeScanning */].sarifID ); } if (uploadResults["code-quality" /* CodeQuality */] !== void 0) { - core15.setOutput( + core16.setOutput( "quality-sarif-id", uploadResults["code-quality" /* CodeQuality */].sarifID ); @@ -154571,15 +156124,15 @@ async function run(startedAt) { ); } if (getOptionalInput("expect-error") === "true") { - core15.setFailed( + core16.setFailed( `expect-error input was set to true but no error was thrown.` ); } - core15.exportVariable("CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY" /* ANALYZE_DID_COMPLETE_SUCCESSFULLY */, "true"); + core16.exportVariable("CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY" /* ANALYZE_DID_COMPLETE_SUCCESSFULLY */, "true"); } catch (unwrappedError) { const error3 = wrapError(unwrappedError); if (getOptionalInput("expect-error") !== "true" || hasBadExpectErrorInput()) { - core15.setFailed(error3.message); + core16.setFailed(error3.message); } await sendStatusReport2( startedAt, @@ -154643,33 +156196,25 @@ async function run(startedAt) { ); } } +var analyze = { + name: "finish" /* Analyze */, + run +}; async function runWrapper() { - const startedAt = /* @__PURE__ */ new Date(); - const logger = getActionsLogger(); - try { - await run(startedAt); - } catch (error3) { - core15.setFailed(`analyze action failed: ${getErrorMessage(error3)}`); - await sendUnhandledErrorStatusReport( - "finish" /* Analyze */, - startedAt, - error3, - logger - ); - } + await runInActions(analyze); await checkForTimeout(); } // src/analyze-action-post.ts var fs26 = __toESM(require("fs")); -var core17 = __toESM(require_core()); +var core18 = __toESM(require_core()); // src/debug-artifacts.ts var fs25 = __toESM(require("fs")); var path22 = __toESM(require("path")); var artifact = __toESM(require_artifact2()); var artifactLegacy = __toESM(require_artifact_client2()); -var core16 = __toESM(require_core()); +var core17 = __toESM(require_core()); // node_modules/archiver/lib/core.js var import_fs2 = require("fs"); @@ -154753,6 +156298,7 @@ var closePattern = /\\}/g; var commaPattern = /\\,/g; var periodPattern = /\\\./g; var EXPANSION_MAX = 1e5; +var EXPANSION_MAX_LENGTH = 4e6; function numeric(str) { return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0); } @@ -154787,11 +156333,11 @@ function expand2(str, options = {}) { if (!str) { return []; } - const { max = EXPANSION_MAX } = options; + const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH } = options; if (str.slice(0, 2) === "{}") { str = "\\{\\}" + str.slice(2); } - return expand_(escapeBraces(str), max, true).map(unescapeBraces); + return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces); } function embrace(str) { return "{" + str + "}"; @@ -154805,19 +156351,88 @@ function lte(i, y) { function gte6(i, y) { return i >= y; } -function expand_(str, max, isTop) { - const expansions = []; - const m = balanced("{", "}", str); - if (!m) - return [str]; - const pre = m.pre; - const post = m.post.length ? expand_(m.post, max, false) : [""]; - if (/\$$/.test(m.pre)) { - for (let k = 0; k < post.length && k < max; k++) { - const expansion = pre + "{" + m.body + "}" + post[k]; - expansions.push(expansion); +function combine(acc, pre, values, max, maxLength, dropEmpties) { + const out = []; + let length = 0; + for (let a = 0; a < acc.length; a++) { + for (let v = 0; v < values.length; v++) { + if (out.length >= max) + return out; + const expansion = acc[a] + pre + values[v]; + if (dropEmpties && !expansion) + continue; + if (length + expansion.length > maxLength) + return out; + out.push(expansion); + length += expansion.length; + } + } + return out; +} +function expandSequence(body, isAlphaSequence, max, maxLength) { + const n = body.split(/\.\./); + const N = []; + if (n[0] === void 0 || n[1] === void 0) { + return N; + } + const x = numeric(n[0]); + const y = numeric(n[1]); + const width = Math.max(n[0].length, n[1].length); + let incr = n.length === 3 && n[2] !== void 0 ? Math.max(Math.abs(numeric(n[2])), 1) : 1; + let test = lte; + const reverse = y < x; + if (reverse) { + incr *= -1; + test = gte6; + } + const pad = n.some(isPadded); + let length = 0; + for (let i = x; test(i, y) && N.length < max; i += incr) { + let c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === "\\") { + c = ""; + } + } else { + c = String(i); + if (pad) { + const need = width - c.length; + if (need > 0) { + const z = new Array(need + 1).join("0"); + if (i < 0) { + c = "-" + z + c.slice(1); + } else { + c = z + c; + } + } + } + } + if (length + c.length > maxLength) + break; + N.push(c); + length += c.length; + } + return N; +} +function expand_(str, max, maxLength, isTop) { + let acc = [""]; + let dropEmpties = false; + let firstGroup = true; + for (; ; ) { + const m = balanced("{", "}", str); + if (!m) { + return combine(acc, str, [""], max, maxLength, dropEmpties); + } + const pre = m.pre; + if (/\$$/.test(pre)) { + acc = combine(acc, pre + "{" + m.body + "}", [""], max, maxLength, dropEmpties && !m.post.length); + firstGroup = false; + if (!m.post.length) + break; + str = m.post; + continue; } - } else { const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); const isSequence = isNumericSequence || isAlphaSequence; @@ -154825,75 +156440,58 @@ function expand_(str, max, isTop) { if (!isSequence && !isOptions) { if (m.post.match(/,(?!,).*\}/)) { str = m.pre + "{" + m.body + escClose + m.post; - return expand_(str, max, true); + isTop = true; + continue; } - return [str]; + return combine(acc, pre + "{" + m.body + "}" + m.post, [""], max, maxLength, dropEmpties); } - let n; + if (firstGroup) { + dropEmpties = isTop && !isSequence; + firstGroup = false; + } + let values; if (isSequence) { - n = m.body.split(/\.\./); + values = expandSequence(m.body, isAlphaSequence, max, maxLength); } else { - n = parseCommaParts(m.body); + let n = parseCommaParts(m.body); if (n.length === 1 && n[0] !== void 0) { - n = expand_(n[0], max, false).map(embrace); + n = expand_(n[0], max, maxLength, false).map(embrace); if (n.length === 1) { - return post.map((p) => m.pre + n[0] + p); + acc = combine(acc, pre + n[0], [""], max, maxLength, dropEmpties && !m.post.length); + if (!m.post.length) + break; + str = m.post; + continue; } } - } - let N; - if (isSequence && n[0] !== void 0 && n[1] !== void 0) { - const x = numeric(n[0]); - const y = numeric(n[1]); - const width = Math.max(n[0].length, n[1].length); - let incr = n.length === 3 && n[2] !== void 0 ? Math.max(Math.abs(numeric(n[2])), 1) : 1; - let test = lte; - const reverse = y < x; - if (reverse) { - incr *= -1; - test = gte6; - } - const pad = n.some(isPadded); - N = []; - for (let i = x; test(i, y) && N.length < max; i += incr) { - let c; - if (isAlphaSequence) { - c = String.fromCharCode(i); - if (c === "\\") { - c = ""; - } - } else { - c = String(i); - if (pad) { - const need = width - c.length; - if (need > 0) { - const z = new Array(need + 1).join("0"); - if (i < 0) { - c = "-" + z + c.slice(1); - } else { - c = z + c; - } - } - } + let dropsEmpties = dropEmpties && !m.post.length && !pre; + for (let d = 0; dropsEmpties && d < acc.length; d++) { + if (acc[d]) { + dropsEmpties = false; } - N.push(c); - } - } else { - N = []; - for (let j = 0; j < n.length; j++) { - N.push.apply(N, expand_(n[j], max, false)); } - } - for (let j = 0; j < N.length; j++) { - for (let k = 0; k < post.length && expansions.length < max; k++) { - const expansion = pre + N[j] + post[k]; - if (!isTop || isSequence || expansion) { - expansions.push(expansion); + values = []; + let valuesLength = 0; + outer: for (let j = 0; j < n.length; j++) { + const expanded = expand_(n[j], max, maxLength, false); + for (let k = 0; k < expanded.length; k++) { + const v = expanded[k]; + if (dropsEmpties && !v) + continue; + if (values.length >= max || valuesLength + v.length > maxLength) { + break outer; + } + values.push(v); + valuesLength += v.length; } } } + acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length); + if (!m.post.length) + break; + str = m.post; } - return expansions; + return acc; } // node_modules/readdir-glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js @@ -155750,7 +157348,7 @@ var twoStarNoDot = "(?:(?!(?:\\/|^)\\.).)*?"; var filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options); minimatch.filter = filter; var ext = (a, b = {}) => Object.assign({}, a, b); -var defaults = (def) => { +var defaults2 = (def) => { if (!def || typeof def !== "object" || !Object.keys(def).length) { return minimatch; } @@ -155786,7 +157384,7 @@ var defaults = (def) => { GLOBSTAR }); }; -minimatch.defaults = defaults; +minimatch.defaults = defaults2; var braceExpand = (pattern, options = {}) => { assertValidPattern(pattern); if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) { @@ -156682,7 +158280,7 @@ var import_async = __toESM(require_async(), 1); var import_path6 = require("path"); // node_modules/archiver/lib/error.js -var import_util28 = __toESM(require("util"), 1); +var import_util34 = __toESM(require("util"), 1); var ERROR_CODES = { ABORTED: "archive was aborted", DIRECTORYDIRPATHREQUIRED: "diretory dirpath argument must be a non-empty string value", @@ -156707,7 +158305,7 @@ function ArchiverError(code, data) { this.code = code; this.data = data; } -import_util28.default.inherits(ArchiverError, Error); +import_util34.default.inherits(ArchiverError, Error); // node_modules/archiver/lib/core.js var import_readable_stream2 = __toESM(require_ours(), 1); @@ -159402,10 +161000,10 @@ function getArtifactSuffix(matrix) { for (const matrixKey of Object.keys(matrixObject).sort()) suffix += `-${matrixObject[matrixKey]}`; } else { - core16.warning("User-specified `matrix` input is not an object."); + core17.warning("User-specified `matrix` input is not an object."); } } catch { - core16.warning( + core17.warning( "Could not parse user-specified `matrix` input into JSON. The debug artifact will not be named with the user's `matrix` input." ); } @@ -159415,7 +161013,7 @@ function getArtifactSuffix(matrix) { async function uploadDebugArtifacts(logger, toUpload, rootDir, artifactName, ghVariant, codeQlVersion) { const uploadSupported = isSafeArtifactUpload(codeQlVersion); if (!uploadSupported) { - core16.info( + core17.info( `Skipping debug artifact upload because the current CLI does not support safe upload. Please upgrade to CLI v${SafeArtifactUploadVersion} or later.` ); return "upload-not-supported"; @@ -159428,7 +161026,7 @@ async function uploadArtifacts(logger, toUpload, rootDir, artifactName, ghVarian } if (isInTestMode()) { await scanArtifactsForTokens(toUpload, logger); - core16.exportVariable("CODEQL_ACTION_ARTIFACT_SCAN_FINISHED", "true"); + core17.exportVariable("CODEQL_ACTION_ARTIFACT_SCAN_FINISHED", "true"); } const suffix = getArtifactSuffix(getOptionalInput("matrix")); const artifactUploader = await getArtifactUploaderClient(logger, ghVariant); @@ -159444,7 +161042,7 @@ async function uploadArtifacts(logger, toUpload, rootDir, artifactName, ghVarian ); return "upload-successful"; } catch (e) { - core16.warning(`Failed to upload debug artifacts: ${e}`); + core17.warning(`Failed to upload debug artifacts: ${e}`); return "upload-failed"; } } @@ -159467,7 +161065,7 @@ async function createPartialDatabaseBundle(config, language) { config.dbLocation, `${config.debugDatabaseName}-${language}-partial.zip` ); - core16.info( + core17.info( `${config.debugDatabaseName}-${language} is not finalized. Uploading partial database bundle at ${databaseBundlePath}...` ); if (fs25.existsSync(databaseBundlePath)) { @@ -159512,7 +161110,7 @@ async function runWrapper2() { logger ); if (config !== void 0) { - const codeql = await getCodeQL(config.codeQLCmd); + const codeql = await getCodeQL(logger, config.codeQLCmd); const version = await codeql.getVersion(); await uploadCombinedSarifArtifacts( logger, @@ -159537,14 +161135,14 @@ async function runWrapper2() { } } } catch (error3) { - core17.setFailed( + core18.setFailed( `analyze post-action step failed: ${getErrorMessage(error3)}` ); } } // src/autobuild-action.ts -var core18 = __toESM(require_core()); +var core19 = __toESM(require_core()); async function sendCompletedStatusReport(config, logger, startedAt, allLanguages, failingLanguage, cause) { initializeEnvironment(getActionVersion()); const status = getActionsStatus(cause, failingLanguage); @@ -159567,8 +161165,7 @@ async function sendCompletedStatusReport(config, logger, startedAt, allLanguages await sendStatusReport(statusReport); } } -async function run2(startedAt) { - const logger = getActionsLogger(); +async function run2({ startedAt, logger }) { let config; let currentLanguage; let languages; @@ -159593,7 +161190,7 @@ async function run2(startedAt) { "Config file could not be found at expected location. Has the 'init' action been called?" ); } - const codeql = await getCodeQL(config.codeQLCmd); + const codeql = await getCodeQL(logger, config.codeQLCmd); languages = await determineAutobuildLanguages(codeql, config, logger); if (languages !== void 0) { const workingDirectory = getOptionalInput("working-directory"); @@ -159611,7 +161208,7 @@ async function run2(startedAt) { await endTracingForCluster(codeql, config, logger); } catch (unwrappedError) { const error3 = wrapError(unwrappedError); - core18.setFailed( + core19.setFailed( `We were unable to automatically build your code. Please replace the call to the autobuild action with your custom build steps. ${error3.message}` ); await sendCompletedStatusReport( @@ -159624,52 +161221,54 @@ async function run2(startedAt) { ); return; } - core18.exportVariable("CODEQL_ACTION_AUTOBUILD_DID_COMPLETE_SUCCESSFULLY" /* AUTOBUILD_DID_COMPLETE_SUCCESSFULLY */, "true"); + core19.exportVariable("CODEQL_ACTION_AUTOBUILD_DID_COMPLETE_SUCCESSFULLY" /* AUTOBUILD_DID_COMPLETE_SUCCESSFULLY */, "true"); await sendCompletedStatusReport(config, logger, startedAt, languages ?? []); } +var autobuild = { + name: "autobuild" /* Autobuild */, + run: run2 +}; async function runWrapper3() { - const startedAt = /* @__PURE__ */ new Date(); - const logger = getActionsLogger(); - try { - await run2(startedAt); - } catch (error3) { - core18.setFailed(`autobuild action failed. ${getErrorMessage(error3)}`); - await sendUnhandledErrorStatusReport( - "autobuild" /* Autobuild */, - startedAt, - error3, - logger - ); - } + await runInActions(autobuild); } // src/init-action.ts var fs28 = __toESM(require("fs")); var path24 = __toESM(require("path")); -var core20 = __toESM(require_core()); -var github3 = __toESM(require_github()); +var core21 = __toESM(require_core()); var io7 = __toESM(require_io()); var semver10 = __toESM(require_semver2()); -// src/config/file.ts -function getConfigFileInput(logger, actions, repositoryProperties, useRepositoryProperty) { - const input = actions.getOptionalInput("config-file"); +// src/config/inputs.ts +async function getToolsInput(action, repositoryProperties) { + const name = "tools" /* Tools */; + const input = action.actions.getOptionalInput(name); + const propertyValue = repositoryProperties["github-codeql-tools" /* TOOLS */]; + const allowRepositoryProperty = await action.features.getValue( + "tools_repository_property" /* ToolsRepositoryProperty */ + ); + if (allowRepositoryProperty && propertyValue?.startsWith("!")) { + action.logger.info( + `Using ${name} input from repository property (enforced): ${propertyValue}` + ); + return { + // Drop the '!' from the value. + value: propertyValue.substring(1), + source: "repository-property" /* RepositoryProperty */ + }; + } if (input !== void 0) { - logger.info(`Using configuration file input from workflow: ${input}`); - return input; + action.logger.info(`Using ${name} input from workflow: ${input}`); + return { value: input, source: "workflow" /* Workflow */ }; } - const propertyValue = repositoryProperties["github-codeql-config-file" /* CONFIG_FILE */]; - if (propertyValue !== void 0 && propertyValue.trim().length > 0) { - if (useRepositoryProperty) { - logger.info( - `Using configuration file input from repository property: ${propertyValue}` - ); - return propertyValue; - } else { - logger.info( - "Ignoring configuration file input from repository property, because the corresponding feature flag is disabled." - ); - } + if (allowRepositoryProperty && propertyValue !== void 0) { + action.logger.info( + `Using ${name} input from repository property: ${propertyValue}` + ); + return { + value: propertyValue, + source: "repository-property" /* RepositoryProperty */ + }; } return void 0; } @@ -159678,7 +161277,7 @@ function getConfigFileInput(logger, actions, repositoryProperties, useRepository var fs27 = __toESM(require("fs")); var path23 = __toESM(require("path")); var import_zlib3 = __toESM(require("zlib")); -var core19 = __toESM(require_core()); +var core20 = __toESM(require_core()); function toCodedErrors(errors) { return Object.entries(errors).reduce( (acc, [code, message]) => { @@ -159801,7 +161400,7 @@ async function validateWorkflow(codeql, logger) { } catch (e) { return `error: formatWorkflowErrors() failed: ${String(e)}`; } - core19.warning(message); + core20.warning(message); } return formatWorkflowCause(workflowErrors); } @@ -159930,7 +161529,7 @@ function getCheckoutPathInputOrThrow(workflow, jobName, matrixVars) { } async function checkWorkflow(logger, codeql) { if (!isDynamicWorkflow() && process.env["CODEQL_ACTION_SKIP_WORKFLOW_VALIDATION" /* SKIP_WORKFLOW_VALIDATION */] !== "true") { - core19.startGroup("Validating workflow"); + core20.startGroup("Validating workflow"); const validateWorkflowResult = await internal2.validateWorkflow( codeql, logger @@ -159942,7 +161541,7 @@ async function checkWorkflow(logger, codeql) { `Unable to validate code scanning workflow: ${validateWorkflowResult}` ); } - core19.endGroup(); + core20.endGroup(); } } var internal2 = { @@ -159964,7 +161563,7 @@ async function sendStartingStatusReport(startedAt, config, logger) { await sendStatusReport(statusReportBase); } } -async function sendCompletedStatusReport2(startedAt, config, configFile, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, overlayBaseDatabaseStats, dependencyCachingResults, logger, error3) { +async function sendCompletedStatusReport2(startedAt, config, configFile, toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, overlayBaseDatabaseStats, dependencyCachingResults, logger, error3) { const statusReportBase = await createStatusReportBase( "init" /* Init */, getActionsStatus(error3), @@ -159981,11 +161580,14 @@ async function sendCompletedStatusReport2(startedAt, config, configFile, toolsDo const workflowLanguages = getOptionalInput("languages"); const initStatusReport = { ...statusReportBase, - tools_input: getOptionalInput("tools") || "", + tools_input: toolsInput?.value || "", tools_resolved_version: toolsVersion, tools_source: toolsSource || "UNKNOWN" /* Unknown */, workflow_languages: workflowLanguages || "" }; + if (toolsInput !== void 0) { + initStatusReport.computed_inputs.tools = toolsInput; + } const initToolsDownloadFields = {}; if (toolsDownloadStatusReport?.downloadDurationMs !== void 0) { initToolsDownloadFields.tools_download_duration_ms = toolsDownloadStatusReport.downloadDurationMs; @@ -160012,20 +161614,20 @@ async function sendCompletedStatusReport2(startedAt, config, configFile, toolsDo await sendStatusReport({ ...initStatusReport, ...initToolsDownloadFields }); } } -async function run3(startedAt) { - const logger = getActionsLogger(); - const actionsEnv = getActionsEnv(); +async function run3(actionState) { + const startedAt = actionState.startedAt; + const logger = actionState.logger; let apiDetails; let config; let configFile; let codeql; let features; let sourceRoot; + let toolsInput; let toolsDownloadStatusReport; let toolsFeatureFlagsValid; let toolsSource; let toolsVersion; - let zstdAvailability; try { initializeEnvironment(getActionVersion()); persistInputs(); @@ -160050,19 +161652,7 @@ async function run3(startedAt) { logger ); const repositoryProperties = repositoryPropertiesResult.orElse({}); - const jobRunUuid = v4_default(); - logger.info(`Job run UUID is ${jobRunUuid}.`); - core20.exportVariable("JOB_RUN_UUID" /* JOB_RUN_UUID */, jobRunUuid); - core20.exportVariable("CODEQL_ACTION_INIT_HAS_RUN" /* INIT_ACTION_HAS_RUN */, "true"); - const useConfigFileProperty = await features.getValue( - "config_file_repository_property" /* ConfigFileRepositoryProperty */ - ); - configFile = getConfigFileInput( - logger, - actionsEnv, - repositoryProperties, - useConfigFileProperty - ); + core21.exportVariable("CODEQL_ACTION_INIT_HAS_RUN" /* INIT_ACTION_HAS_RUN */, "true"); sourceRoot = path24.resolve( getRequiredEnvParam("GITHUB_WORKSPACE"), getOptionalInput("source-root") || "" @@ -160075,12 +161665,22 @@ async function run3(startedAt) { `Failed to parse analysis kinds for 'starting' status report: ${getErrorMessage(err)}` ); } + const actionStateWithFeatures = { ...actionState, features }; + configFile = await getConfigFileInput( + actionStateWithFeatures, + repositoryProperties, + analysisKinds + ); await sendStartingStatusReport(startedAt, { analysisKinds }, logger); if (process.env["CODEQL_ACTION_SETUP_CODEQL_HAS_RUN" /* SETUP_CODEQL_ACTION_HAS_RUN */] === "true") { throw new ConfigurationError( `The 'init' action should not be run in the same workflow as 'setup-codeql'.` ); } + toolsInput = await getToolsInput( + actionStateWithFeatures, + repositoryProperties + ); const codeQLDefaultVersionInfo = await features.getEnabledDefaultCliVersions(gitHubVersion.type); toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid; const rawLanguages = getRawLanguagesNoAutodetect( @@ -160088,7 +161688,7 @@ async function run3(startedAt) { ); const useOverlayAwareDefaultCliVersion = analysisKinds?.length === 1 && analysisKinds[0] === "code-scanning" /* CodeScanning */; const initCodeQLResult = await initCodeQL( - getOptionalInput("tools"), + toolsInput?.value, apiDetails, getTemporaryDirectory(), gitHubVersion.type, @@ -160102,7 +161702,6 @@ async function run3(startedAt) { toolsDownloadStatusReport = initCodeQLResult.toolsDownloadStatusReport; toolsVersion = initCodeQLResult.toolsVersion; toolsSource = initCodeQLResult.toolsSource; - zstdAvailability = initCodeQLResult.zstdAvailability; await checkWorkflow(logger, codeql); if ( // Only enable the experimental features env variable for Rust analysis if the user has explicitly @@ -160118,19 +161717,19 @@ async function run3(startedAt) { ); } if (semver10.lt(actualVer, publicPreview)) { - core20.exportVariable("CODEQL_ENABLE_EXPERIMENTAL_FEATURES" /* EXPERIMENTAL_FEATURES */, "true"); + core21.exportVariable("CODEQL_ENABLE_EXPERIMENTAL_FEATURES" /* EXPERIMENTAL_FEATURES */, "true"); logger.info("Experimental Rust analysis enabled"); } } analysisKinds = await getAnalysisKinds(logger, features); - const debugMode = getOptionalInput("debug") === "true" || core20.isDebug(); + const debugMode = getOptionalInput("debug") === "true" || core21.isDebug(); const fileCoverageResult = await getFileCoverageInformationEnabled( debugMode, codeql, features, repositoryProperties ); - config = await initConfig2(features, { + config = await initConfig2(actionStateWithFeatures, { analysisKinds, languagesInput: getOptionalInput("languages"), queriesInput: getOptionalInput("queries"), @@ -160193,7 +161792,7 @@ async function run3(startedAt) { await checkInstallPython311(config.languages, codeql); } catch (unwrappedError) { const error3 = wrapError(unwrappedError); - core20.setFailed(error3.message); + core21.setFailed(error3.message); const statusReportBase = await createStatusReportBase( "init" /* Init */, error3 instanceof ConfigurationError ? "user-error" : "aborted", @@ -160233,23 +161832,10 @@ async function run3(startedAt) { if (config.overlayDatabaseMode !== "overlay" /* Overlay */) { cleanupDatabaseClusterDirectory(config, logger); } - if (zstdAvailability) { - await recordZstdAvailability(config, zstdAvailability); - } - if (toolsDownloadStatusReport) { - addNoLanguageDiagnostic( - config, - makeTelemetryDiagnostic( - "codeql-action/bundle-download-telemetry", - "CodeQL bundle download telemetry", - toolsDownloadStatusReport - ) - ); - } const goFlags = process.env["GOFLAGS"]; if (goFlags) { - core20.exportVariable("GOFLAGS", goFlags); - core20.warning( + core21.exportVariable("GOFLAGS", goFlags); + core21.warning( "Passing the GOFLAGS env parameter to the init action is deprecated. Please move this to the analyze action." ); } @@ -160268,7 +161854,7 @@ async function run3(startedAt) { "bin" ); fs28.mkdirSync(tempBinPath, { recursive: true }); - core20.addPath(tempBinPath); + core21.addPath(tempBinPath); const goWrapperPath = path24.resolve(tempBinPath, "go"); fs28.writeFileSync( goWrapperPath, @@ -160277,14 +161863,14 @@ async function run3(startedAt) { exec ${goBinaryPath} "$@"` ); fs28.chmodSync(goWrapperPath, "755"); - core20.exportVariable("CODEQL_ACTION_GO_BINARY" /* GO_BINARY_LOCATION */, goWrapperPath); + core21.exportVariable("CODEQL_ACTION_GO_BINARY" /* GO_BINARY_LOCATION */, goWrapperPath); } catch (e) { logger.warning( `Analyzing Go on Linux, but failed to install wrapper script. Tracing custom builds may fail: ${e}` ); } } else { - core20.exportVariable("CODEQL_ACTION_GO_BINARY" /* GO_BINARY_LOCATION */, goBinaryPath); + core21.exportVariable("CODEQL_ACTION_GO_BINARY" /* GO_BINARY_LOCATION */, goBinaryPath); } } catch (e) { logger.warning( @@ -160311,23 +161897,23 @@ exec ${goBinaryPath} "$@"` } } } - core20.exportVariable( + core21.exportVariable( "CODEQL_RAM", process.env["CODEQL_RAM"] || getCodeQLMemoryLimit(getOptionalInput("ram"), logger).toString() ); - core20.exportVariable( + core21.exportVariable( "CODEQL_THREADS", process.env["CODEQL_THREADS"] || getThreadsFlagValue(getOptionalInput("threads"), logger).toString() ); if (await features.getValue("disable_kotlin_analysis_enabled" /* DisableKotlinAnalysisEnabled */)) { - core20.exportVariable("CODEQL_EXTRACTOR_JAVA_AGENT_DISABLE_KOTLIN", "true"); + core21.exportVariable("CODEQL_EXTRACTOR_JAVA_AGENT_DISABLE_KOTLIN", "true"); } if (await features.getValue("force_jgit" /* ForceJGit */)) { - core20.exportVariable("CODEQL_GIT_BACKEND", "jgit"); + core21.exportVariable("CODEQL_GIT_BACKEND", "jgit"); } const kotlinLimitVar = "CODEQL_EXTRACTOR_KOTLIN_OVERRIDE_MAXIMUM_VERSION_LIMIT"; if (await codeQlVersionAtLeast(codeql, "2.20.3") && !await codeQlVersionAtLeast(codeql, "2.20.4")) { - core20.exportVariable(kotlinLimitVar, "2.1.20"); + core21.exportVariable(kotlinLimitVar, "2.1.20"); } if (shouldRestoreCache(config.dependencyCachingEnabled)) { const dependencyCachingResult = await downloadDependencyCaches( @@ -160354,7 +161940,7 @@ exec ${goBinaryPath} "$@"` `${"CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS" /* JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS */} is already set to '${process.env["CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS" /* JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS */]}', so the Action will not override it.` ); } else if (await codeQlVersionAtLeast(codeql, CODEQL_VERSION_JAR_MINIMIZATION) && config.dependencyCachingEnabled && config.buildMode === "none" /* None */ && config.languages.includes("java" /* java */)) { - core20.exportVariable( + core21.exportVariable( "CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS" /* JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS */, "true" ); @@ -160374,8 +161960,7 @@ exec ${goBinaryPath} "$@"` config, sourceRoot, "Runner.Worker.exe", - qlconfigFile, - logger + qlconfigFile ); if (config.overlayDatabaseMode !== "none" /* None */ && !await checkPacksForOverlayCompatibility(codeql, config, logger)) { logger.info( @@ -160391,35 +161976,35 @@ exec ${goBinaryPath} "$@"` config, sourceRoot, "Runner.Worker.exe", - qlconfigFile, - logger + qlconfigFile ); } const tracerConfig = await getCombinedTracerConfig(codeql, config); if (tracerConfig !== void 0) { for (const [key, value] of Object.entries(tracerConfig.env)) { - core20.exportVariable(key, value); + core21.exportVariable(key, value); } } if (await features.getValue("java_network_debugging" /* JavaNetworkDebugging */)) { const existingJavaToolOptions = getOptionalEnvVar("JAVA_TOOL_OPTIONS" /* JAVA_TOOL_OPTIONS */) || ""; - core20.exportVariable( + core21.exportVariable( "JAVA_TOOL_OPTIONS" /* JAVA_TOOL_OPTIONS */, `${existingJavaToolOptions} -Djavax.net.debug=all` ); } flushDiagnostics(config); await saveConfig(config, logger); - core20.setOutput("codeql-path", config.codeQLCmd); - core20.setOutput("codeql-version", (await codeql.getVersion()).version); + core21.setOutput("codeql-path", config.codeQLCmd); + core21.setOutput("codeql-version", (await codeql.getVersion()).version); } catch (unwrappedError) { const error3 = wrapError(unwrappedError); - core20.setFailed(error3.message); + core21.setFailed(error3.message); await sendCompletedStatusReport2( startedAt, config, void 0, // We only report config info on success. + toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, @@ -160437,6 +162022,7 @@ exec ${goBinaryPath} "$@"` startedAt, config, configFile, + toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, @@ -160446,55 +162032,17 @@ exec ${goBinaryPath} "$@"` logger ); } -async function loadRepositoryProperties(repositoryNwo, logger) { - const repositoryOwnerType = github3.context.payload.repository?.owner.type; - logger.debug( - `Repository owner type is '${repositoryOwnerType ?? "unknown"}'.` - ); - if (repositoryOwnerType === "User") { - logger.debug( - "Skipping loading repository properties because the repository is owned by a user and therefore cannot have repository properties." - ); - return new Success({}); - } - try { - return new Success(await loadPropertiesFromApi(logger, repositoryNwo)); - } catch (error3) { - logger.warning( - `Failed to load repository properties: ${getErrorMessage(error3)}` - ); - return new Failure(error3); - } -} -async function recordZstdAvailability(config, zstdAvailability) { - addNoLanguageDiagnostic( - config, - makeTelemetryDiagnostic( - "codeql-action/zstd-availability", - "Zstandard availability", - zstdAvailability - ) - ); -} +var init = { + name: "init" /* Init */, + run: run3 +}; async function runWrapper4() { - const startedAt = /* @__PURE__ */ new Date(); - const logger = getActionsLogger(); - try { - await run3(startedAt); - } catch (error3) { - core20.setFailed(`init action failed: ${getErrorMessage(error3)}`); - await sendUnhandledErrorStatusReport( - "init" /* Init */, - startedAt, - error3, - logger - ); - } + await runInActions(init); await checkForTimeout(); } // src/init-action-post.ts -var core21 = __toESM(require_core()); +var core22 = __toESM(require_core()); // src/init-action-post-helper.ts var fs29 = __toESM(require("fs")); @@ -160530,6 +162078,7 @@ async function prepareFailedSarif(logger, features, config) { const category = `/language:${language}`; const checkoutPath = "."; const result = await generateFailedSarif( + logger, features, config, category, @@ -160550,6 +162099,7 @@ async function prepareFailedSarif(logger, features, config) { const category = getCategoryInputOrThrow(workflow, jobName, matrix); const checkoutPath = getCheckoutPathInputOrThrow(workflow, jobName, matrix); const result = await generateFailedSarif( + logger, features, config, category, @@ -160558,9 +162108,9 @@ async function prepareFailedSarif(logger, features, config) { return new Success(result); } } -async function generateFailedSarif(features, config, category, checkoutPath, sarifFile) { +async function generateFailedSarif(logger, features, config, category, checkoutPath, sarifFile) { const databasePath = config.dbLocation; - const codeql = await getCodeQL(config.codeQLCmd); + const codeql = await getCodeQL(logger, config.codeQLCmd); if (sarifFile === void 0) { sarifFile = "../codeql-failed-run.sarif"; } @@ -160826,7 +162376,7 @@ async function run4(startedAt) { "Debugging artifacts are unavailable since the 'init' Action failed before it could produce any." ); } else { - const codeql = await getCodeQL(config.codeQLCmd); + const codeql = await getCodeQL(logger, config.codeQLCmd); uploadFailedSarifResult = await uploadFailureInfo( tryUploadAllAvailableDebugArtifacts, printDebugLogs, @@ -160842,7 +162392,7 @@ async function run4(startedAt) { } } catch (unwrappedError) { const error3 = wrapError(unwrappedError); - core21.setFailed(error3.message); + core22.setFailed(error3.message); const statusReportBase2 = await createStatusReportBase( "init-post" /* InitPost */, getActionsStatus(error3), @@ -160887,14 +162437,14 @@ function getFinalJobStatus(config) { } let jobStatus; if (process.env["CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY" /* ANALYZE_DID_COMPLETE_SUCCESSFULLY */] === "true") { - core21.exportVariable("CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, "JOB_STATUS_SUCCESS" /* SuccessStatus */); + core22.exportVariable("CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, "JOB_STATUS_SUCCESS" /* SuccessStatus */); jobStatus = "JOB_STATUS_SUCCESS" /* SuccessStatus */; } else if (config !== void 0) { jobStatus = "JOB_STATUS_CONFIGURATION_ERROR" /* ConfigErrorStatus */; } else { jobStatus = "JOB_STATUS_UNKNOWN" /* UnknownStatus */; } - core21.exportVariable("CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, jobStatus); + core22.exportVariable("CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, jobStatus); return jobStatus; } function getJobStatusFromEnvironment() { @@ -160913,7 +162463,7 @@ async function runWrapper5() { try { await run4(startedAt); } catch (error3) { - core21.setFailed(`init post action failed: ${wrapError(error3).message}`); + core22.setFailed(`init post action failed: ${wrapError(error3).message}`); await sendUnhandledErrorStatusReport( "init-post" /* InitPost */, startedAt, @@ -160924,12 +162474,12 @@ async function runWrapper5() { } // src/resolve-environment-action.ts -var core22 = __toESM(require_core()); +var core23 = __toESM(require_core()); // src/resolve-environment.ts async function runResolveBuildEnvironment(cmd, logger, workingDir, language) { logger.startGroup(`Attempting to resolve build environment for ${language}`); - const codeql = await getCodeQL(cmd); + const codeql = await getCodeQL(logger, cmd); if (workingDir !== void 0) { logger.info(`Using ${workingDir} as the working directory.`); } @@ -160971,16 +162521,16 @@ async function run5(startedAt) { workingDirectory, getRequiredInput("language") ); - core22.setOutput(ENVIRONMENT_OUTPUT_NAME, result); + core23.setOutput(ENVIRONMENT_OUTPUT_NAME, result); } catch (unwrappedError) { const error3 = wrapError(unwrappedError); if (error3 instanceof CliError) { - core22.setOutput(ENVIRONMENT_OUTPUT_NAME, {}); + core23.setOutput(ENVIRONMENT_OUTPUT_NAME, {}); logger.warning( `Failed to resolve a build environment suitable for automatically building your code. ${error3.message}` ); } else { - core22.setFailed( + core23.setFailed( `Failed to resolve a build environment suitable for automatically building your code. ${error3.message}` ); const statusReportBase2 = await createStatusReportBase( @@ -161017,7 +162567,7 @@ async function runWrapper6() { try { await run5(startedAt); } catch (error3) { - core22.setFailed( + core23.setFailed( `${"resolve-environment" /* ResolveEnvironment */} action failed: ${getErrorMessage( error3 )}` @@ -161033,8 +162583,8 @@ async function runWrapper6() { } // src/setup-codeql-action.ts -var core23 = __toESM(require_core()); -async function sendCompletedStatusReport3(startedAt, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, logger, error3) { +var core24 = __toESM(require_core()); +async function sendCompletedStatusReport3(startedAt, toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, logger, error3) { const statusReportBase = await createStatusReportBase( "setup-codeql" /* SetupCodeQL */, getActionsStatus(error3), @@ -161050,11 +162600,14 @@ async function sendCompletedStatusReport3(startedAt, toolsDownloadStatusReport, } const initStatusReport = { ...statusReportBase, - tools_input: getOptionalInput("tools") || "", + tools_input: toolsInput?.value || "", tools_resolved_version: toolsVersion, tools_source: toolsSource || "UNKNOWN" /* Unknown */, workflow_languages: "" }; + if (toolsInput !== void 0) { + initStatusReport.computed_inputs.tools = toolsInput; + } const initToolsDownloadFields = {}; if (toolsDownloadStatusReport?.downloadDurationMs !== void 0) { initToolsDownloadFields.tools_download_duration_ms = toolsDownloadStatusReport.downloadDurationMs; @@ -161064,9 +162617,10 @@ async function sendCompletedStatusReport3(startedAt, toolsDownloadStatusReport, } await sendStatusReport({ ...initStatusReport, ...initToolsDownloadFields }); } -async function run6(startedAt) { - const logger = getActionsLogger(); +async function run6(actionState) { + const { logger, startedAt } = actionState; let codeql; + let toolsInput; let toolsDownloadStatusReport; let toolsFeatureFlagsValid; let toolsSource; @@ -161089,9 +162643,12 @@ async function run6(startedAt) { getTemporaryDirectory(), logger ); - const jobRunUuid = v4_default(); - logger.info(`Job run UUID is ${jobRunUuid}.`); - core23.exportVariable("JOB_RUN_UUID" /* JOB_RUN_UUID */, jobRunUuid); + const repositoryPropertiesResult = await loadRepositoryProperties( + repositoryNwo, + logger + ); + const repositoryProperties = repositoryPropertiesResult.orElse({}); + const actionStateWithFeatures = { ...actionState, features }; const statusReportBase = await createStatusReportBase( "setup-codeql" /* SetupCodeQL */, "starting", @@ -161103,6 +162660,10 @@ async function run6(startedAt) { if (statusReportBase !== void 0) { await sendStatusReport(statusReportBase); } + toolsInput = await getToolsInput( + actionStateWithFeatures, + repositoryProperties + ); const codeQLDefaultVersionInfo = await features.getEnabledDefaultCliVersions(gitHubVersion.type); toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid; const rawLanguages = getRawLanguagesNoAutodetect( @@ -161110,7 +162671,7 @@ async function run6(startedAt) { ); const analysisKinds = await getAnalysisKinds(logger, features); const initCodeQLResult = await initCodeQL( - getOptionalInput("tools"), + toolsInput?.value, apiDetails, getTemporaryDirectory(), gitHubVersion.type, @@ -161124,12 +162685,12 @@ async function run6(startedAt) { toolsDownloadStatusReport = initCodeQLResult.toolsDownloadStatusReport; toolsVersion = initCodeQLResult.toolsVersion; toolsSource = initCodeQLResult.toolsSource; - core23.setOutput("codeql-path", codeql.getPath()); - core23.setOutput("codeql-version", (await codeql.getVersion()).version); - core23.exportVariable("CODEQL_ACTION_SETUP_CODEQL_HAS_RUN" /* SETUP_CODEQL_ACTION_HAS_RUN */, "true"); + core24.setOutput("codeql-path", codeql.getPath()); + core24.setOutput("codeql-version", (await codeql.getVersion()).version); + core24.exportVariable("CODEQL_ACTION_SETUP_CODEQL_HAS_RUN" /* SETUP_CODEQL_ACTION_HAS_RUN */, "true"); } catch (unwrappedError) { const error3 = wrapError(unwrappedError); - core23.setFailed(error3.message); + core24.setFailed(error3.message); const statusReportBase = await createStatusReportBase( "setup-codeql" /* SetupCodeQL */, error3 instanceof ConfigurationError ? "user-error" : "failure", @@ -161147,6 +162708,7 @@ async function run6(startedAt) { } await sendCompletedStatusReport3( startedAt, + toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, @@ -161154,171 +162716,27 @@ async function run6(startedAt) { logger ); } +var setupCodeQL2 = { + name: "setup-codeql" /* SetupCodeQL */, + run: run6 +}; async function runWrapper7() { - const startedAt = /* @__PURE__ */ new Date(); - const logger = getActionsLogger(); - try { - await run6(startedAt); - } catch (error3) { - core23.setFailed(`setup-codeql action failed: ${getErrorMessage(error3)}`); - await sendUnhandledErrorStatusReport( - "setup-codeql" /* SetupCodeQL */, - startedAt, - error3, - logger - ); - } + await runInActions(setupCodeQL2); await checkForTimeout(); } // src/start-proxy-action.ts var import_child_process2 = require("child_process"); var path28 = __toESM(require("path")); -var core26 = __toESM(require_core()); +var core27 = __toESM(require_core()); // src/start-proxy.ts var path26 = __toESM(require("path")); -var core25 = __toESM(require_core()); +var core26 = __toESM(require_core()); var toolcache4 = __toESM(require_tool_cache()); -// src/start-proxy/types.ts -var usernameSchema = { - /** The username needed to authenticate to the package registry, if any. */ - username: optional(string) -}; -function hasUsername(config) { - return "username" in config; -} -var usernamePasswordSchema = { - /** The password needed to authenticate to the package registry, if any. */ - password: optional(string), - ...usernameSchema -}; -function hasUsernameAndPassword(config) { - return hasUsername(config) && "password" in config; -} -var tokenSchema = { - /** The token needed to authenticate to the package registry, if any. */ - token: optional(string), - ...usernameSchema -}; -function hasToken(config) { - return "token" in config; -} -function isToken(config) { - return "token" in config && validateSchema(tokenSchema, config); -} -var azureConfigSchema = { - "tenant-id": string, - "client-id": string -}; -function isAzureConfig(config) { - return validateSchema(azureConfigSchema, config); -} -var awsConfigSchema = { - "aws-region": string, - "account-id": string, - "role-name": string, - domain: string, - "domain-owner": string, - audience: optional(string) -}; -function isAWSConfig(config) { - return validateSchema(awsConfigSchema, config); -} -var jfrogConfigSchema = { - "jfrog-oidc-provider-name": string, - audience: optional(string), - "identity-mapping-name": optional(string) -}; -function isJFrogConfig(config) { - return validateSchema(jfrogConfigSchema, config); -} -var cloudsmithConfigSchema = { - namespace: string, - "service-slug": string, - "api-host": string -}; -function isCloudsmithConfig(config) { - return validateSchema(cloudsmithConfigSchema, config); -} -var gcpConfigSchema = { - "workload-identity-provider": string, - "service-account": optional(string), - audience: optional(string) -}; -function isGCPConfig(config) { - return validateSchema(gcpConfigSchema, config); -} -var oidcSchemas = [ - { schema: azureConfigSchema, name: "Azure" }, - { schema: awsConfigSchema, name: "AWS" }, - { schema: jfrogConfigSchema, name: "JFrog" }, - { schema: cloudsmithConfigSchema, name: "Cloudsmith" }, - { schema: gcpConfigSchema, name: "GCP" } -]; -function credentialToStr(credential) { - let result = `Type: ${credential.type};`; - const appendIfDefined = (name, val) => { - if (isDefined2(val)) { - result += ` ${name}: ${val};`; - } - }; - appendIfDefined("Url", credential.url); - appendIfDefined("Host", credential.host); - if (hasUsername(credential)) { - appendIfDefined("Username", credential.username); - } - if ("password" in credential) { - appendIfDefined( - "Password", - isDefined2(credential.password) ? "***" : void 0 - ); - } - if (hasToken(credential)) { - appendIfDefined("Token", isDefined2(credential.token) ? "***" : void 0); - } - if (isAzureConfig(credential)) { - appendIfDefined("Tenant", credential["tenant-id"]); - appendIfDefined("Client", credential["client-id"]); - } else if (isAWSConfig(credential)) { - appendIfDefined("AWS Region", credential["aws-region"]); - appendIfDefined("AWS Account", credential["account-id"]); - appendIfDefined("AWS Role", credential["role-name"]); - appendIfDefined("AWS Domain", credential.domain); - appendIfDefined("AWS Domain Owner", credential["domain-owner"]); - appendIfDefined("AWS Audience", credential.audience); - } else if (isJFrogConfig(credential)) { - appendIfDefined("JFrog Provider", credential["jfrog-oidc-provider-name"]); - appendIfDefined( - "JFrog Identity Mapping", - credential["identity-mapping-name"] - ); - appendIfDefined("JFrog Audience", credential.audience); - } else if (isCloudsmithConfig(credential)) { - appendIfDefined("Cloudsmith Namespace", credential.namespace); - appendIfDefined("Cloudsmith Service Slug", credential["service-slug"]); - appendIfDefined("Cloudsmith API Host", credential["api-host"]); - } else if (isGCPConfig(credential)) { - appendIfDefined( - "GCP Workload Identity Provider", - credential["workload-identity-provider"] - ); - appendIfDefined("GCP Service Account", credential["service-account"]); - appendIfDefined("GCP Audience", credential.audience); - } - return result; -} -function getAddressString(address) { - if (address.url === void 0) { - return address.host; - } else { - return address.url; - } -} - // src/start-proxy/validation.ts -var core24 = __toESM(require_core()); +var core25 = __toESM(require_core()); function cloneCredential(schema, obj) { const result = {}; for (const key of Object.keys(schema)) { @@ -161337,14 +162755,14 @@ function getAuthConfig(config) { } if (isToken(config)) { if (isDefined2(config.token)) { - core24.setSecret(config.token); + core25.setSecret(config.token); } return cloneCredential(tokenSchema, config); } else { let username = void 0; let password = void 0; if ("password" in config && isString(config.password)) { - core24.setSecret(config.password); + core25.setSecret(config.password); password = config.password; } if ("username" in config && isString(config.username)) { @@ -161400,7 +162818,7 @@ function getSafeErrorMessage(error3) { } async function sendFailedStatusReport(logger, startedAt, language, unwrappedError) { const error3 = wrapError(unwrappedError); - core25.setFailed(`start-proxy action failed: ${error3.message}`); + core26.setFailed(`start-proxy action failed: ${error3.message}`); const statusReportMessage = getSafeErrorMessage(error3); const errorStatusReportBase = await createStatusReportBase( "start-proxy" /* StartProxy */, @@ -161426,6 +162844,10 @@ function isPAT(value) { GITHUB_PAT_FINE_GRAINED_PATTERN ]); } +var ALWAYS_ENABLED_REGISTRY_TYPE = [ + "git_source", + "docker_registry" +]; var LANGUAGE_TO_REGISTRY_TYPE = { actions: [], cpp: [], @@ -161490,7 +162912,7 @@ function getCredentials(logger, registrySecrets, registriesCredentials, language } const authConfig = getAuthConfig(e); const address = getRegistryAddress(e); - if (registryTypeForLanguage && !registryTypeForLanguage.some((t) => t === e.type)) { + if (!ALWAYS_ENABLED_REGISTRY_TYPE.some((t) => t === e.type) && registryTypeForLanguage && !registryTypeForLanguage.some((t) => t === e.type)) { continue; } const isPrintable2 = (str) => { @@ -161855,7 +163277,7 @@ async function checkProxyEnvironment(logger, language) { // src/start-proxy/reachability.ts var https2 = __toESM(require("https")); -var import_https_proxy_agent = __toESM(require_dist2()); +var import_https_proxy_agent = __toESM(require_dist3()); var connectionTestConfig = { nuget_feed: { path: "v3/index.json" } }; @@ -161961,15 +163383,16 @@ async function checkConnections(logger, proxy, backend) { } // src/start-proxy-action.ts -async function run7(startedAt) { - const logger = getActionsLogger(); +async function run7(action) { + const startedAt = action.startedAt; + const logger = action.logger; let features; let language; try { persistInputs(); const tempDir = getTemporaryDirectory(); const proxyLogFilePath = path28.resolve(tempDir, "proxy.log"); - core26.saveState("proxy-log-file", proxyLogFilePath); + core27.saveState("proxy-log-file", proxyLogFilePath); const repositoryNwo = getRepositoryNwo(); const gitHubVersion = await getGitHubVersion(); features = initFeatures( @@ -161994,7 +163417,7 @@ async function run7(startedAt) { `Credentials loaded for the following registries: ${credentials.map((c) => credentialToStr(c)).join("\n")}` ); - if (core26.isDebug() || isInTestMode()) { + if (core27.isDebug() || isInTestMode()) { try { await checkProxyEnvironment(logger, language); } catch (err) { @@ -162028,20 +163451,13 @@ async function run7(startedAt) { await sendFailedStatusReport(logger, startedAt, language, unwrappedError); } } +var startProxyAction = { + name: "start-proxy" /* StartProxy */, + run: run7, + transformTelemetryError: getSafeErrorMessage +}; async function runWrapper8() { - const startedAt = /* @__PURE__ */ new Date(); - const logger = getActionsLogger(); - try { - await run7(startedAt); - } catch (error3) { - core26.setFailed(`start-proxy action failed: ${getErrorMessage(error3)}`); - await sendUnhandledErrorStatusReport( - "start-proxy" /* StartProxy */, - startedAt, - getSafeErrorMessage(wrapError(error3)), - logger - ); - } + await runInActions(startProxyAction); } async function startProxy(binPath, config, logFilePath, logger) { const host = "127.0.0.1"; @@ -162060,7 +163476,7 @@ async function startProxy(binPath, config, logFilePath, logger) { ); subprocess.unref(); if (subprocess.pid) { - core26.saveState("proxy-process-pid", `${subprocess.pid}`); + core27.saveState("proxy-process-pid", `${subprocess.pid}`); } subprocess.on("error", (error3) => { subprocessError = error3; @@ -162079,25 +163495,25 @@ async function startProxy(binPath, config, logFilePath, logger) { throw subprocessError; } logger.info(`Proxy started on ${host}:${port}`); - core26.setOutput("proxy_host", host); - core26.setOutput("proxy_port", port.toString()); - core26.setOutput("proxy_ca_certificate", config.ca.cert); + core27.setOutput("proxy_host", host); + core27.setOutput("proxy_port", port.toString()); + core27.setOutput("proxy_ca_certificate", config.ca.cert); const registry_urls = config.all_credentials.filter((credential) => credential.url !== void 0).map((credential) => ({ type: credential.type, url: credential.url, "replaces-base": credential["replaces-base"] })); - core26.setOutput("proxy_urls", JSON.stringify(registry_urls)); + core27.setOutput("proxy_urls", JSON.stringify(registry_urls)); return { host, port, cert: config.ca.cert, registries: registry_urls }; } // src/start-proxy-action-post.ts -var core27 = __toESM(require_core()); +var core28 = __toESM(require_core()); async function runWrapper9() { const logger = getActionsLogger(); try { restoreInputs(); - const pid = core27.getState("proxy-process-pid"); + const pid = core28.getState("proxy-process-pid"); if (pid) { process.kill(Number(pid)); } @@ -162105,8 +163521,8 @@ async function runWrapper9() { getTemporaryDirectory(), logger ); - if (config?.debugMode || core27.isDebug()) { - const logFilePath = core27.getState("proxy-log-file"); + if (config?.debugMode || core28.isDebug()) { + const logFilePath = core28.getState("proxy-log-file"); logger.info( "Debug mode is on. Uploading proxy log as Actions debugging artifact..." ); @@ -162134,7 +163550,7 @@ async function runWrapper9() { } // src/upload-sarif-action.ts -var core28 = __toESM(require_core()); +var core29 = __toESM(require_core()); async function sendSuccessStatusReport2(startedAt, uploadStats, logger) { const statusReportBase = await createStatusReportBase( "upload-sarif" /* UploadSarif */, @@ -162152,8 +163568,7 @@ async function sendSuccessStatusReport2(startedAt, uploadStats, logger) { await sendStatusReport(statusReport); } } -async function run8(startedAt) { - const logger = getActionsLogger(); +async function run8({ startedAt, logger }) { try { initializeEnvironment(getActionVersion()); const gitHubVersion = await getGitHubVersion(); @@ -162195,11 +163610,11 @@ async function run8(startedAt) { } const codeScanningResult = uploadResults["code-scanning" /* CodeScanning */]; if (codeScanningResult !== void 0) { - core28.setOutput("sarif-id", codeScanningResult.sarifID); + core29.setOutput("sarif-id", codeScanningResult.sarifID); } - core28.setOutput("sarif-ids", JSON.stringify(uploadResults)); + core29.setOutput("sarif-ids", JSON.stringify(uploadResults)); if (shouldSkipSarifUpload()) { - core28.debug( + core29.debug( "SARIF upload disabled by an environment variable. Waiting for processing is disabled." ); } else if (getRequiredInput("wait-for-processing") === "true") { @@ -162219,7 +163634,7 @@ async function run8(startedAt) { } catch (unwrappedError) { const error3 = isThirdPartyAnalysis("upload-sarif" /* UploadSarif */) && unwrappedError instanceof InvalidSarifUploadError ? new ConfigurationError(unwrappedError.message) : wrapError(unwrappedError); const message = error3.message; - core28.setFailed(message); + core29.setFailed(message); const errorStatusReportBase = await createStatusReportBase( "upload-sarif" /* UploadSarif */, getActionsStatus(error3), @@ -162236,26 +163651,16 @@ async function run8(startedAt) { return; } } +var uploadSarif = { + name: "upload-sarif" /* UploadSarif */, + run: run8 +}; async function runWrapper10() { - const startedAt = /* @__PURE__ */ new Date(); - const logger = getActionsLogger(); - try { - await run8(startedAt); - } catch (error3) { - core28.setFailed( - `codeql/upload-sarif action failed: ${getErrorMessage(error3)}` - ); - await sendUnhandledErrorStatusReport( - "upload-sarif" /* UploadSarif */, - startedAt, - error3, - logger - ); - } + await runInActions(uploadSarif); } // src/upload-sarif-action-post.ts -var core29 = __toESM(require_core()); +var core30 = __toESM(require_core()); async function runWrapper11() { try { restoreInputs(); @@ -162264,7 +163669,7 @@ async function runWrapper11() { checkGitHubVersionInRange(gitHubVersion, logger); if (process.env["CODEQL_ACTION_INIT_HAS_RUN" /* INIT_ACTION_HAS_RUN */] !== "true") { if (gitHubVersion.type === void 0) { - core29.warning( + core30.warning( `Did not upload debug artifacts because cannot determine the GitHub variant running.` ); return; @@ -162281,7 +163686,7 @@ async function runWrapper11() { ); } } catch (error3) { - core29.setFailed( + core30.setFailed( `upload-sarif post-action step failed: ${getErrorMessage(error3)}` ); } @@ -162344,6 +163749,13 @@ undici/lib/web/fetch/body.js: undici/lib/web/websocket/frame.js: (*! ws. MIT License. Einar Otto Stangvik *) +content-type/dist/index.js: + (*! + * content-type + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + *) + @octokit/request-error/dist-src/index.js: (* v8 ignore else -- @preserve -- Bug with vitest coverage where it sees an else branch that doesn't exist *) @@ -162351,6 +163763,9 @@ undici/lib/web/websocket/frame.js: (* v8 ignore next -- @preserve *) (* v8 ignore else -- @preserve *) +@octokit/graphql/dist-bundle/index.js: + (* v8 ignore if -- @preserve *) + normalize-path/index.js: (*! * normalize-path @@ -162434,7 +163849,7 @@ tmp/lib/tmp.js: *) js-yaml/dist/js-yaml.mjs: - (*! js-yaml 5.0.0 https://github.com/nodeca/js-yaml @license MIT *) + (*! js-yaml 5.2.3 https://github.com/nodeca/js-yaml @license MIT *) long/index.js: (** diff --git a/package-lock.json b/package-lock.json index f8b8f39b13..50ebd990cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codeql", - "version": "4.36.3", + "version": "4.37.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codeql", - "version": "4.36.3", + "version": "4.37.7", "license": "MIT", "workspaces": [ "pr-checks" @@ -14,7 +14,7 @@ "dependencies": { "@actions/artifact": "^5.0.3", "@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2", - "@actions/cache": "^5.1.0", + "@actions/cache": "^5.2.0", "@actions/core": "^2.0.3", "@actions/exec": "^2.0.0", "@actions/github": "^8.0.1", @@ -22,18 +22,22 @@ "@actions/http-client": "^3.0.0", "@actions/io": "^2.0.0", "@actions/tool-cache": "^3.0.1", - "@octokit/plugin-retry": "^8.1.0", + "@octokit/core": "^7.0.7", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0", + "@octokit/plugin-retry": "^8.1.1", "archiver": "^8.0.0", "fast-deep-equal": "^3.1.3", "follow-redirects": "^1.16.0", "get-folder-size": "^5.0.0", "https-proxy-agent": "^7.0.6", - "js-yaml": "^5.0.0", + "js-yaml": "^5.2.3", "jsonschema": "1.5.0", "long": "^5.3.2", "node-forge": "^1.4.0", - "semver": "^7.8.4", - "uuid": "^14.0.0" + "semver": "^7.8.5", + "undici": "^6.28.0", + "uuid": "^14.0.1" }, "devDependencies": { "@ava/typescript": "6.0.0", @@ -46,22 +50,22 @@ "@types/node": "^20.19.43", "@types/node-forge": "^1.3.14", "@types/sarif": "^2.1.7", - "@types/semver": "^7.7.1", - "@types/sinon": "^21.0.1", + "@types/semver": "^7.8.0", + "@types/sinon": "^22.0.0", "ava": "^6.4.1", "esbuild": "^0.28.1", - "eslint": "^9.39.4", + "eslint": "^9.39.5", "eslint-import-resolver-typescript": "^4.4.5", - "eslint-plugin-github": "^6.0.0", - "eslint-plugin-import-x": "^4.16.2", + "eslint-plugin-github": "^6.1.2", + "eslint-plugin-import-x": "^4.17.1", "eslint-plugin-jsdoc": "^62.9.0", "eslint-plugin-no-async-foreach": "^0.1.1", "glob": "^13.0.6", - "globals": "^17.6.0", - "nock": "^14.0.15", - "sinon": "^22.0.0", + "globals": "^17.9.0", + "nock": "^14.0.17", + "sinon": "^22.1.0", "typescript": "^6.0.3", - "typescript-eslint": "^8.61.1" + "typescript-eslint": "^8.66.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -370,9 +374,9 @@ "license": "Apache-2.0" }, "node_modules/@actions/artifact/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -455,9 +459,9 @@ } }, "node_modules/@actions/cache": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-5.1.0.tgz", - "integrity": "sha512-kTIj4YPrjjRPKSGlj7f8eq+Pijoy/SKBEbJcAwNsQTFGEF29NGqj1mqD02/PmhV6r4bRAixycexAWpmUJ2aCwg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-5.2.0.tgz", + "integrity": "sha512-1R1Oc8cuDNCygsIP7gLiKLGCymOw/k5FkGQkXZFcLz6/RWyMImkfP0dZX6kjA9SRAmANcKNocI2XrsIaZ1it8w==", "license": "MIT", "dependencies": { "@actions/core": "^2.0.0", @@ -1494,9 +1498,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, "license": "MIT", "dependencies": { @@ -1506,7 +1510,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", + "js-yaml": "^4.3.0", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, @@ -1522,6 +1526,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -1530,9 +1535,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "funding": [ { @@ -1553,9 +1558,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, "license": "MIT", "engines": { @@ -1975,9 +1980,9 @@ } }, "node_modules/@microsoft/eslint-formatter-sarif/node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "funding": [ { @@ -2082,16 +2087,16 @@ } }, "node_modules/@octokit/core": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", - "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.7.tgz", + "integrity": "sha512-DcB0M3KFgr9ECI328lhBMVsyFT2DnmNucSBTqEN3exyNKUzkkpUSCHmTRcunF41Eou2TIQKW4seewri8ON9bSA==", "license": "MIT", "dependencies": { "@octokit/auth-token": "^6.0.0", - "@octokit/graphql": "^9.0.3", - "@octokit/request": "^10.0.6", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", + "@octokit/graphql": "^9.0.4", + "@octokit/request": "^10.0.13", + "@octokit/request-error": "^7.1.1", + "@octokit/types": "^17.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" }, @@ -2099,6 +2104,21 @@ "node": ">= 20" } }, + "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { + "version": "28.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", + "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "license": "MIT" + }, + "node_modules/@octokit/core/node_modules/@octokit/types": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz", + "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^28.0.0" + } + }, "node_modules/@octokit/core/node_modules/universal-user-agent": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", @@ -2106,18 +2126,33 @@ "license": "ISC" }, "node_modules/@octokit/endpoint": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz", - "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.4.tgz", + "integrity": "sha512-f1cOWoHPmxryJFknxbtDdjODWfV8A9tc8Aae6ermXPNgHFZ/x91AtHIz4gicEjL8hkJiip+u21QHJORfBv/qiA==", "license": "MIT", "dependencies": { - "@octokit/types": "^16.0.0", + "@octokit/types": "^17.0.0", "universal-user-agent": "^7.0.2" }, "engines": { "node": ">= 20" } }, + "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { + "version": "28.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", + "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "license": "MIT" + }, + "node_modules/@octokit/endpoint/node_modules/@octokit/types": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz", + "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^28.0.0" + } + }, "node_modules/@octokit/endpoint/node_modules/universal-user-agent": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", @@ -2125,19 +2160,34 @@ "license": "ISC" }, "node_modules/@octokit/graphql": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", - "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.4.tgz", + "integrity": "sha512-5s15CCiY8XXQ+FG+b1YQcl6Z2FA++nwAz/tg2VUrTmnMncP+2nnGUEYANImdnxsA2Fnq+Mbl7hDjUTw7cFAwcg==", "license": "MIT", "dependencies": { - "@octokit/request": "^10.0.6", - "@octokit/types": "^16.0.0", + "@octokit/request": "^10.0.13", + "@octokit/types": "^17.0.0", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 20" } }, + "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { + "version": "28.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", + "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "license": "MIT" + }, + "node_modules/@octokit/graphql/node_modules/@octokit/types": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz", + "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^28.0.0" + } + }, "node_modules/@octokit/graphql/node_modules/universal-user-agent": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", @@ -2190,13 +2240,13 @@ } }, "node_modules/@octokit/plugin-retry": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.1.0.tgz", - "integrity": "sha512-O1FZgXeiGb2sowEr/hYTr6YunGdSAFWnr2fyW39Ah85H8O33ELASQxcvOFF5LE6Tjekcyu2ms4qAzJVhSaJxTw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.1.1.tgz", + "integrity": "sha512-VCVvZ/R1+u3WuiBWpNavZ0mY4aaJNAsENrpBP9aLSR2QyOpQgd7DhM5j4AW7z4MQpnJYgwBPf0XqPQoNBRdQwg==", "license": "MIT", "dependencies": { - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", + "@octokit/request-error": "^7.1.1", + "@octokit/types": "^17.0.0", "bottleneck": "^2.15.3" }, "engines": { @@ -2206,16 +2256,32 @@ "@octokit/core": ">=7" } }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { + "version": "28.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", + "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz", + "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^28.0.0" + } + }, "node_modules/@octokit/request": { - "version": "10.0.7", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz", - "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", + "version": "10.0.13", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.13.tgz", + "integrity": "sha512-v2269YxL9Yf+x3d+gRI63FP0vFQEiWgLyBzxe/Y+0yFDg2B/Tzf5dhh9VNfccVAQnfcfwQWyk/y6Bn7rUXXs7A==", "license": "MIT", "dependencies": { - "@octokit/endpoint": "^11.0.2", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "fast-content-type-parse": "^3.0.0", + "@octokit/endpoint": "^11.0.3", + "@octokit/request-error": "^7.1.1", + "@octokit/types": "^17.0.0", + "content-type": "^2.0.0", + "json-with-bigint": "^3.5.3", "universal-user-agent": "^7.0.2" }, "engines": { @@ -2223,17 +2289,47 @@ } }, "node_modules/@octokit/request-error": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", - "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.1.tgz", + "integrity": "sha512-+eaY7G2VVpSf2pc5Gn1+mph837V/d/TYTJAgWL9Tb0ogGYcpN3IlAVFgjL+Vv93F/sevrxkvsYCedtpLdcFLzA==", "license": "MIT", "dependencies": { - "@octokit/types": "^16.0.0" + "@octokit/types": "^17.0.0" }, "engines": { "node": ">= 20" } }, + "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { + "version": "28.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", + "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "license": "MIT" + }, + "node_modules/@octokit/request-error/node_modules/@octokit/types": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz", + "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^28.0.0" + } + }, + "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { + "version": "28.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", + "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "license": "MIT" + }, + "node_modules/@octokit/request/node_modules/@octokit/types": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz", + "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^28.0.0" + } + }, "node_modules/@octokit/request/node_modules/universal-user-agent": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", @@ -2274,13 +2370,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@package-json/types": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@package-json/types/-/types-0.0.12.tgz", - "integrity": "sha512-uu43FGU34B5VM9mCNjXCwLaGHYjXdNincqKLaraaCW+7S2+SmiBg1Nv8bPnmschrIfZmfKNY9f3fC376MRrObw==", - "dev": true, - "license": "MIT" - }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -2572,16 +2661,16 @@ "license": "MIT" }, "node_modules/@types/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ==", "dev": true, "license": "MIT" }, "node_modules/@types/sinon": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.1.tgz", - "integrity": "sha512-5yoJSqLbjH8T9V2bksgRayuhpZy+723/z6wBOR+Soe4ZlXC0eW8Na71TeaZPUWDQvM7LYKa9UGFc6LRqxiR5fQ==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-22.0.0.tgz", + "integrity": "sha512-TDbVpbccc2HfiqHR09Argj3mHV1KMW7sCCKj52fsl8lbRLkEn7fB1966EWhOKWUBcqfBueZuPoA7/OK1CKiy3g==", "dev": true, "license": "MIT", "dependencies": { @@ -2594,17 +2683,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz", - "integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.66.0.tgz", + "integrity": "sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.61.1", - "@typescript-eslint/type-utils": "8.61.1", - "@typescript-eslint/utils": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/scope-manager": "8.66.0", + "@typescript-eslint/type-utils": "8.66.0", + "@typescript-eslint/utils": "8.66.0", + "@typescript-eslint/visitor-keys": "8.66.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -2617,7 +2706,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.61.1", + "@typescript-eslint/parser": "^8.66.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -2633,16 +2722,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz", - "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.66.0.tgz", + "integrity": "sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/scope-manager": "8.66.0", + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/typescript-estree": "8.66.0", + "@typescript-eslint/visitor-keys": "8.66.0", "debug": "^4.4.3" }, "engines": { @@ -2676,14 +2765,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz", - "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.66.0.tgz", + "integrity": "sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.61.1", - "@typescript-eslint/types": "^8.61.1", + "@typescript-eslint/tsconfig-utils": "^8.66.0", + "@typescript-eslint/types": "^8.66.0", "debug": "^4.4.3" }, "engines": { @@ -2716,14 +2805,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz", - "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.66.0.tgz", + "integrity": "sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1" + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/visitor-keys": "8.66.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2734,9 +2823,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz", - "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.66.0.tgz", + "integrity": "sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==", "dev": true, "license": "MIT", "engines": { @@ -2751,15 +2840,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz", - "integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.66.0.tgz", + "integrity": "sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1", - "@typescript-eslint/utils": "8.61.1", + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/typescript-estree": "8.66.0", + "@typescript-eslint/utils": "8.66.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -2794,9 +2883,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz", - "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.66.0.tgz", + "integrity": "sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==", "dev": true, "license": "MIT", "engines": { @@ -2808,16 +2897,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz", - "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.66.0.tgz", + "integrity": "sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.61.1", - "@typescript-eslint/tsconfig-utils": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/project-service": "8.66.0", + "@typescript-eslint/tsconfig-utils": "8.66.0", + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/visitor-keys": "8.66.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2846,16 +2935,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { @@ -2877,13 +2966,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -2893,16 +2982,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz", - "integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.66.0.tgz", + "integrity": "sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1" + "@typescript-eslint/scope-manager": "8.66.0", + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/typescript-estree": "8.66.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2917,13 +3006,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", - "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.66.0.tgz", + "integrity": "sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/types": "8.66.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -3867,9 +3956,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -4296,6 +4385,19 @@ "node": "^14.18.0 || >=16.10.0" } }, + "node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/convert-to-spaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", @@ -4769,9 +4871,9 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "dev": true, "license": "MIT", "dependencies": { @@ -4780,8 +4882,8 @@ "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -4991,15 +5093,15 @@ } }, "node_modules/eslint-plugin-github": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-6.0.0.tgz", - "integrity": "sha512-J8MvUoiR/TU/Y9NnEmg1AnbvMUj9R6IO260z47zymMLLvso7B4c80IKjd8diqmqtSmeXXlbIus4i0SvK84flag==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-6.1.2.tgz", + "integrity": "sha512-XU1fVItfnwYWXG0GqH0MV2VY9EzvgbPxDnUJ9I1915Cpn24z13Vgx1pttrdQy6bhLmDYp+Wl7pX/L1YMKdG+6g==", "dev": true, "license": "MIT", "dependencies": { - "@eslint/compat": "^1.2.3", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.14.0", + "@eslint/compat": "^2.0.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "^9.39.5", "@github/browserslist-config": "^1.0.0", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", @@ -5014,79 +5116,18 @@ "eslint-plugin-no-only-tests": "^3.0.0", "eslint-plugin-prettier": "^5.2.1", "eslint-rule-documentation": ">=1.0.0", - "globals": "^16.0.0", + "globals": "^17.7.0", "jsx-ast-utils": "^3.3.2", "prettier": "^3.0.0", "svg-element-attributes": "^1.3.1", - "typescript": "^5.7.3", + "typescript": "^6.0.3", "typescript-eslint": "^8.14.0" }, "bin": { "eslint-ignore-errors": "bin/eslint-ignore-errors.js" }, "peerDependencies": { - "eslint": "^8 || ^9" - } - }, - "node_modules/eslint-plugin-github/node_modules/@eslint/compat": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.4.1.tgz", - "integrity": "sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": "^8.40 || 9" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-github/node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-github/node_modules/globals": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", - "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-github/node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" + "eslint": "^8 || ^9 || ^10" } }, "node_modules/eslint-plugin-i18n-text": { @@ -5132,13 +5173,12 @@ } }, "node_modules/eslint-plugin-import-x": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.2.tgz", - "integrity": "sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.17.1.tgz", + "integrity": "sha512-4cdstYkKCyjumM2Q9NSI03K8D2a9F4Ssz33K2lv2hQa4KmR9jPLwk3uWGtNvclfqBrPGfGuMBwsGMbe6dMRbfg==", "dev": true, "license": "MIT", "dependencies": { - "@package-json/types": "^0.0.12", "@typescript-eslint/types": "^8.56.0", "comment-parser": "^1.4.1", "debug": "^4.4.1", @@ -5180,16 +5220,16 @@ } }, "node_modules/eslint-plugin-import-x/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/eslint-plugin-import-x/node_modules/minimatch": { @@ -5655,22 +5695,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/fast-content-type-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", - "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "license": "MIT" @@ -6116,15 +6140,15 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/glob/node_modules/minimatch": { @@ -6143,9 +6167,9 @@ } }, "node_modules/globals": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", - "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "version": "17.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.9.0.tgz", + "integrity": "sha512-m/MvAW61QVU5VDNF1Vj8axt016h8w7L5TU1e9zlab7XIttAT2YAlCwl75K1fOqvMM9apmD7lbCIRhpfkhmxhCg==", "dev": true, "license": "MIT", "engines": { @@ -6986,9 +7010,9 @@ } }, "node_modules/js-yaml": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.0.0.tgz", - "integrity": "sha512-GSvaPUbk1U+FMZ7rJzF+F8e5YVtu7KnD40et/5rBXXRBv2jCO9L3qCewvIDDdudC0QycTFlf6EAA+h3kxBsuUw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.3.tgz", + "integrity": "sha512-n+mUVyUX5bVv7G/G2zyIHOhdxfuU1dY2NOFzTQUWiMUbFss8b57NFlgCCaggU78wSw5KVS9cllzeLyzyR+n5nw==", "funding": [ { "type": "github", @@ -7049,6 +7073,12 @@ "dev": true, "license": "ISC" }, + "node_modules/json-with-bigint": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.10.tgz", + "integrity": "sha512-Vcx+JVNEBts/xfcoCS69sKrOhOk/3TVlvlT+XzUOefVKnnrbYSCKpDCm10pohsJFtsJVYnwa/cXRZ4eElzaM6w==", + "license": "MIT" + }, "node_modules/json5": { "version": "1.0.2", "dev": true, @@ -7451,9 +7481,9 @@ "license": "MIT" }, "node_modules/nock": { - "version": "14.0.15", - "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.15.tgz", - "integrity": "sha512-S0a47C9pLvcYx/Ugf0H30BVBEcUgMMBDk9VJIDlJ8XGrfH2QDUD4Tgdp45qDIiHttokBG+IbsOtsvIjGR/j3bg==", + "version": "14.0.17", + "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.17.tgz", + "integrity": "sha512-EjRr1weMa4ALQX35AgZTEnP+weJJjlW1KGDiNM2IQC2069YDHas4f4B4UUYR+TTLyKWxJvOz2wObDKQs/LNreA==", "dev": true, "license": "MIT", "dependencies": { @@ -8095,15 +8125,15 @@ } }, "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/readdir-glob/node_modules/minimatch": { @@ -8367,9 +8397,9 @@ } }, "node_modules/semver": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", - "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -8561,9 +8591,9 @@ } }, "node_modules/sinon": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-22.0.0.tgz", - "integrity": "sha512-sq/6DpdXOrLyfbKlXLg/Usc7xu8YXPeLkOFZRvA3bNUSA2lhbrZ06yuXbH1fkzBPCbz9O10+7hznzUsjaYNm0Q==", + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-22.1.0.tgz", + "integrity": "sha512-n1ajF2rBWMTtEwbKcw4UdFg4nCnDdq/U6RDoxtOd7oapOlRoJ5ynwFx60owROyhDpA9QhMZi0pCO/xtmwFjG7w==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -8904,9 +8934,9 @@ } }, "node_modules/supertap/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", "dev": true, "license": "MIT", "dependencies": { @@ -8983,9 +9013,9 @@ } }, "node_modules/tar": { - "version": "7.5.16", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz", - "integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==", + "version": "7.5.20", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.20.tgz", + "integrity": "sha512-9FcyK4PA6+WbzlTM9WhQm6vB5W7cP7dUiPsv1g7YDwEQnQ1CGpK3MGlKk/ITVWMk05kHZuBhmVhiv8LZoy/PFQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -9173,9 +9203,9 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", - "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "version": "4.23.8", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.8.tgz", + "integrity": "sha512-8W675THjbzfFmLOQzjDBIBna+WjqMGIxmSZ1mMc1+o9qoVsEuAgQu5j5ueLhau8inOkDu9OslVg0FmfBs1RIHw==", "dev": true, "license": "MIT", "dependencies": { @@ -9325,16 +9355,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.1.tgz", - "integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.66.0.tgz", + "integrity": "sha512-QlEbBPz/RuJ1XUHj29nm3t0F/O/cSlEnntozqPOYHnnTGAXFamnMBu5i9Vn6vhUPHGAjR+Vl+5J8vPN/BMUrJw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.61.1", - "@typescript-eslint/parser": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1", - "@typescript-eslint/utils": "8.61.1" + "@typescript-eslint/eslint-plugin": "8.66.0", + "@typescript-eslint/parser": "8.66.0", + "@typescript-eslint/typescript-estree": "8.66.0", + "@typescript-eslint/utils": "8.66.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9368,9 +9398,9 @@ } }, "node_modules/undici": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", - "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz", + "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==", "license": "MIT", "engines": { "node": ">=18.17" @@ -9498,9 +9528,9 @@ "license": "MIT" }, "node_modules/uuid": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", - "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -9815,15 +9845,15 @@ "dependencies": { "@actions/core": "^2.0.3", "@actions/github": "^8.0.1", - "@octokit/core": "^7.0.6", + "@octokit/core": "^7.0.7", "@octokit/plugin-paginate-rest": ">=9.2.2", "@octokit/plugin-rest-endpoint-methods": "^17.0.0", - "semver": "^7.8.0", + "semver": "^7.8.5", "yaml": "^2.9.0" }, "devDependencies": { "@types/node": "^20.19.43", - "tsx": "^4.22.4" + "tsx": "^4.23.8" } } } diff --git a/package.json b/package.json index 10d09e81bd..17229b4b7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codeql", - "version": "4.36.3", + "version": "4.37.7", "private": true, "description": "CodeQL action", "scripts": { @@ -22,7 +22,7 @@ "dependencies": { "@actions/artifact": "^5.0.3", "@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2", - "@actions/cache": "^5.1.0", + "@actions/cache": "^5.2.0", "@actions/core": "^2.0.3", "@actions/exec": "^2.0.0", "@actions/github": "^8.0.1", @@ -30,18 +30,22 @@ "@actions/http-client": "^3.0.0", "@actions/io": "^2.0.0", "@actions/tool-cache": "^3.0.1", - "@octokit/plugin-retry": "^8.1.0", + "@octokit/core": "^7.0.7", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0", + "@octokit/plugin-retry": "^8.1.1", "archiver": "^8.0.0", "fast-deep-equal": "^3.1.3", "follow-redirects": "^1.16.0", "get-folder-size": "^5.0.0", "https-proxy-agent": "^7.0.6", - "js-yaml": "^5.0.0", + "js-yaml": "^5.2.3", "jsonschema": "1.5.0", "long": "^5.3.2", "node-forge": "^1.4.0", - "semver": "^7.8.4", - "uuid": "^14.0.0" + "semver": "^7.8.5", + "uuid": "^14.0.1", + "undici": "^6.28.0" }, "devDependencies": { "@ava/typescript": "6.0.0", @@ -54,22 +58,22 @@ "@types/node": "^20.19.43", "@types/node-forge": "^1.3.14", "@types/sarif": "^2.1.7", - "@types/semver": "^7.7.1", - "@types/sinon": "^21.0.1", + "@types/semver": "^7.8.0", + "@types/sinon": "^22.0.0", "ava": "^6.4.1", "esbuild": "^0.28.1", - "eslint": "^9.39.4", + "eslint": "^9.39.5", "eslint-import-resolver-typescript": "^4.4.5", - "eslint-plugin-github": "^6.0.0", - "eslint-plugin-import-x": "^4.16.2", + "eslint-plugin-github": "^6.1.2", + "eslint-plugin-import-x": "^4.17.1", "eslint-plugin-jsdoc": "^62.9.0", "eslint-plugin-no-async-foreach": "^0.1.1", "glob": "^13.0.6", - "globals": "^17.6.0", - "nock": "^14.0.15", - "sinon": "^22.0.0", + "globals": "^17.9.0", + "nock": "^14.0.17", + "sinon": "^22.1.0", "typescript": "^6.0.3", - "typescript-eslint": "^8.61.1" + "typescript-eslint": "^8.66.0" }, "overrides": { "@actions/tool-cache": { @@ -91,6 +95,6 @@ "semver": ">=6.3.1" }, "glob": "^13.0.6", - "undici": "^6.24.0" + "undici": "^6.28.0" } } diff --git a/pr-checks/bundle-changelog.test.ts b/pr-checks/bundle-changelog.test.ts new file mode 100644 index 0000000000..6cc4d096ba --- /dev/null +++ b/pr-checks/bundle-changelog.test.ts @@ -0,0 +1,142 @@ +/** + * Tests for `bundle-changelog.ts`. + */ + +import * as assert from "node:assert/strict"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import { afterEach, beforeEach, describe, it } from "node:test"; + +import { + CLI_VERSION_ENV_VAR, + getCLIVersion, + getPRNumber, + getPRUrl, + PR_URL_ENV_VAR, + updateChangelog, +} from "./bundle-changelog"; +import { + EMPTY_CHANGELOG, + NO_CHANGES_STR, + UNRELEASED_PLACEHOLDER, +} from "./changelog"; + +let testDir: string; + +beforeEach(() => { + // Set up a temporary directory for testing + testDir = fs.mkdtempSync(path.join(os.tmpdir(), "bundle-changelog-test-")); +}); + +afterEach(() => { + /** Clean up temporary directories. */ + fs.rmSync(testDir, { recursive: true, force: true }); +}); + +describe("getCLIVersion", async () => { + await it("throws if the environment variable is not set", async () => { + delete process.env[CLI_VERSION_ENV_VAR]; + assert.throws(() => getCLIVersion()); + }); + + await it("throws if the environment variable is empty", async () => { + process.env[CLI_VERSION_ENV_VAR] = " "; + assert.throws(() => getCLIVersion()); + }); + + await it("returns value of the environment variable if set", async () => { + const testValue = "1.2.3"; + process.env[CLI_VERSION_ENV_VAR] = testValue; + assert.deepEqual(getCLIVersion(), testValue); + }); +}); + +const testPrUrl = "https://github.com/github/codeql-action/pulls/42"; + +describe("getPRUrl", async () => { + await it("throws if the environment variable is not set", async () => { + delete process.env[PR_URL_ENV_VAR]; + assert.throws(() => getPRUrl()); + }); + + await it("throws if the environment variable is empty", async () => { + process.env[PR_URL_ENV_VAR] = " "; + assert.throws(() => getPRUrl()); + }); + + await it("returns value of the environment variable if set", async () => { + process.env[PR_URL_ENV_VAR] = testPrUrl; + assert.deepEqual(getPRUrl(), testPrUrl); + }); +}); + +describe("getPRNumber", async () => { + await it("throws if the last part of the input is not a number", async () => { + assert.throws(() => getPRNumber(`${testPrUrl}/foo`)); + }); + + await it("throws if the last part of the input is not a positive number", async () => { + assert.throws(() => getPRNumber(`${testPrUrl}/-100`)); + }); + + await it("returns the PR number from an URL", async () => { + assert.equal(getPRNumber(testPrUrl), 42); + }); +}); + +const testChangelog = `${EMPTY_CHANGELOG.trimEnd()} + +## 4.23.7 + +- Other change + +## 4.23.6 + +${NO_CHANGES_STR}`; + +const expectedChangelog = `# CodeQL Action Changelog + +## ${UNRELEASED_PLACEHOLDER} + +- Update default CodeQL bundle version to + +## 4.23.7 + +- Other change + +## 4.23.6 + +${NO_CHANGES_STR}`; + +describe("updateChangelog", async () => { + await it("removes `NO_CHANGES_STR` if present in [UNRELEASED] section", async () => { + const result = updateChangelog(EMPTY_CHANGELOG, ""); + assert.ok(!result.includes(NO_CHANGES_STR.trim())); + }); + + await it("doesn't remove `NO_CHANGES_STR` if present in versioned section", async () => { + const result = updateChangelog( + EMPTY_CHANGELOG.replace(UNRELEASED_PLACEHOLDER, "1.2.3"), + "", + ); + assert.ok(result.includes(NO_CHANGES_STR.trim())); + }); + + await it("throws if there are no sections", async () => { + assert.throws(() => { + updateChangelog( + "# CodeQL Action Changelog", + "- Update default CodeQL bundle version to", + ); + }); + }); + + await it("adds note at the end of the first section", async () => { + const result = updateChangelog( + testChangelog, + "- Update default CodeQL bundle version to", + ); + assert.deepEqual(result, expectedChangelog); + }); +}); diff --git a/pr-checks/bundle-changelog.ts b/pr-checks/bundle-changelog.ts new file mode 100755 index 0000000000..557a8556c1 --- /dev/null +++ b/pr-checks/bundle-changelog.ts @@ -0,0 +1,127 @@ +#!/usr/bin/env npx tsx + +/** + * Updates the changelog with a change note for an updated CodeQL CLI bundle. + */ + +import * as fs from "node:fs"; + +import { + parseChangelog, + renderChangelog, + UNRELEASED_PLACEHOLDER, +} from "./changelog"; +import { CHANGELOG_FILE, CLI_BUNDLE_RELEASE_URL_PREFIX } from "./config"; +import { getErrorMessage } from "./util"; + +export const CLI_VERSION_ENV_VAR = "CLI_VERSION"; +export const PR_URL_ENV_VAR = "PR_URL"; + +/** Gets the CLI version from the environment. */ +export function getCLIVersion() { + const cliVersion = process.env[CLI_VERSION_ENV_VAR]; + + if (cliVersion === undefined || cliVersion.trim() === "") { + throw new Error(`No CLI version was set in '${CLI_VERSION_ENV_VAR}'.`); + } + + return cliVersion; +} + +/** Gets the PR URL from the environment. */ +export function getPRUrl() { + const prUrl = process.env[PR_URL_ENV_VAR]; + + if (prUrl === undefined || prUrl.trim() === "") { + throw new Error(`No PR URL was set in '${PR_URL_ENV_VAR}'.`); + } + + return prUrl; +} + +/** + * Gets the PR number from something like a PR URL. + */ +export function getPRNumber(prUrl: string) { + const prUrlParts = prUrl.split("/"); + const prNumberStr = prUrlParts[prUrlParts.length - 1]; + + const prNumber = Number.parseInt(prNumberStr, 10); + + if (!Number.isInteger(prNumber) || prNumber <= 0) { + throw new Error( + `Invalid PR URL '${prUrl}': last part is not a positive number`, + ); + } + + return prNumber; +} + +/** + * Updates `changelog` by adding `changelogNote` to the first section. + * + * @param contents The existing changelog contents. + * @param changelogNote The note to add to the first section. + */ +export function updateChangelog(contents: string, changelogNote: string) { + // If the "[UNRELEASED]" section starts with "no user facing changes", remove that line. + contents = contents.replace( + `## ${UNRELEASED_PLACEHOLDER}\n\nNo user facing changes.`, + `## ${UNRELEASED_PLACEHOLDER}\n`, + ); + + const changelog = parseChangelog(contents); + + if (changelog.sections.length === 0) { + throw new Error("The changelog contains no existing sections."); + } + + // Add the changelog note to the bottom of the first section. + const firstSection = changelog.sections[0]; + const lastLine = firstSection.bodyLines.pop(); + + if (lastLine !== undefined && lastLine.trim() !== "") { + // We expect the last line to be empty. If it isn't for some reason, + // add it back. + firstSection.bodyLines.push(lastLine); + } + + firstSection.bodyLines.push(changelogNote); + + // If the last line is empty as expected, then add it back in after the new note. + if (lastLine?.trim() === "") { + firstSection.bodyLines.push(lastLine); + } + + return renderChangelog(changelog); +} + +function main() { + try { + const cliVersion = getCLIVersion(); + const prUrl = getPRUrl(); + + // The GitHub Release for the new bundle version. + const bundleReleaseUrl = `${CLI_BUNDLE_RELEASE_URL_PREFIX}${cliVersion}`; + + // Get the PR number from the PR URL. + const prNumber = getPRNumber(prUrl); + const changelogNote = `- Update default CodeQL bundle version to [${cliVersion}](${bundleReleaseUrl}). [#${prNumber}](${prUrl})`; + + let changelog = fs.readFileSync(CHANGELOG_FILE, "utf-8"); + + changelog = updateChangelog(changelog, changelogNote); + + fs.writeFileSync(CHANGELOG_FILE, changelog); + + return 0; + } catch (err) { + console.error(`Failed to bundle changelog: ${getErrorMessage(err)}`); + return -1; + } +} + +// Only call `main` if this script was run directly. +if (require.main === module) { + process.exit(main()); +} diff --git a/pr-checks/changelog.test.ts b/pr-checks/changelog.test.ts new file mode 100755 index 0000000000..817852e3e1 --- /dev/null +++ b/pr-checks/changelog.test.ts @@ -0,0 +1,72 @@ +#!/usr/bin/env npx tsx + +/** + * Tests for `changelog.ts`. + */ + +import * as assert from "node:assert/strict"; +import * as fs from "node:fs"; +import { describe, it } from "node:test"; + +import { + EMPTY_CHANGELOG, + getReleaseDateString, + parseChangelog, + processChangelogForBackports, + renderChangelog, + setVersionAndDate, +} from "./changelog"; +import { CHANGELOG_FILE } from "./config"; + +const testDate = new Date(2026, 7, 14); + +describe("getReleaseDateString", async () => { + await it("formats dates as expected", async () => { + assert.equal(getReleaseDateString(testDate), "14 Aug 2026"); + }); +}); + +const emptyChangelogExpected = `# CodeQL Action Changelog + +## 9.99.9 - 14 Aug 2026 + +No user facing changes. + +`; + +describe("setVersionAndDate", async () => { + await it("replaces the placeholder", async () => { + const result = setVersionAndDate("9.99.9", EMPTY_CHANGELOG, testDate); + assert.equal(result, emptyChangelogExpected); + }); +}); + +describe("parseChangelog + renderChangelog", async () => { + await it("renderChangelog(parseChangelog(c)) == c", async () => { + const actualChangelog = fs.readFileSync(CHANGELOG_FILE, "utf-8"); + const roundtrip = renderChangelog(parseChangelog(actualChangelog)); + assert.deepEqual(roundtrip.split("\n"), actualChangelog.split("\n")); + }); +}); + +const testChangelog = `# CodeQL Action Changelog + +## 4.12.3 - 14 Aug 2026 + +No user facing changes. +`; + +const testChangelogResult: string = `# CodeQL Action Changelog + +## 3.12.3 - 14 Aug 2026 + +No user facing changes. +`; + +describe("processChangelogForBackports", async () => { + await it("replaces major versions", async () => { + const result = processChangelogForBackports("4", "3", testChangelog); + + assert.deepEqual(result.split("\n"), testChangelogResult.split("\n")); + }); +}); diff --git a/pr-checks/changelog.ts b/pr-checks/changelog.ts new file mode 100644 index 0000000000..4cf1e75494 --- /dev/null +++ b/pr-checks/changelog.ts @@ -0,0 +1,212 @@ +import * as fs from "node:fs"; + +import { CHANGELOG_FILE, DryRunOption } from "./config"; + +/** The placeholder in the header for unreleased changes. */ +export const UNRELEASED_PLACEHOLDER = "[UNRELEASED]"; + +/** The default contents for a section in the changelog. */ +export const NO_CHANGES_STR = "No user facing changes.\n\n"; + +/** Placeholder changelog content for a new release. */ +export const EMPTY_CHANGELOG = `# CodeQL Action Changelog + +## ${UNRELEASED_PLACEHOLDER} + +${NO_CHANGES_STR}`; + +/** + * Represents sections in a changelog. + */ +export interface ChangelogSection { + headerLine: string; + bodyLines: string[]; +} + +/** + * Represents a changelog. + */ +export interface Changelog { + preamble: string[]; + sections: ChangelogSection[]; +} + +/** Returns `date` formatted as `DD Mon YYYY`. */ +export function getReleaseDateString(today: Date = new Date()): string { + return today.toLocaleDateString("en-GB", { + day: "2-digit", + month: "short", + year: "numeric", + }); +} + +export interface OpenChangelogOptions { + initChangelog?: boolean; +} + +export function withChangelog( + transformer: (contents: string) => string, + options: DryRunOption & OpenChangelogOptions, +): void { + let content: string; + + if (options.initChangelog && !fs.existsSync(CHANGELOG_FILE)) { + content = EMPTY_CHANGELOG; + } else { + content = fs.readFileSync(CHANGELOG_FILE, "utf8"); + } + + if (!options.dryRun) { + fs.writeFileSync(CHANGELOG_FILE, transformer(content), "utf8"); + } else { + console.info(`[DRY RUN] Would have written updated changelog.`); + } +} + +/** + * Updates the `[UNRELEASED]` marker in `CHANGELOG.md` with the given version + * and today's date. + */ +export function setVersionAndDate( + version: string, + content: string, + date: Date = new Date(), +): string { + const versionAndDate = `${version} - ${getReleaseDateString(date)}`; + return content.replace(UNRELEASED_PLACEHOLDER, versionAndDate); +} + +/** + * Parses `content` into a structured representation of a changelog. + * + * @param content The contents of the changelog file. + */ +export function parseChangelog(content: string): Changelog { + const lines = content.split("\n"); + let i = 0; + + const preamble: string[] = []; + const sections: ChangelogSection[] = []; + let currentSection: ChangelogSection | undefined = undefined; + + // Process all lines of the input file. + while (i < lines.length) { + const line = lines[i]; + + // Sections of the changelog start with `## `. + if (line.startsWith("## ")) { + // We have discovered a new section. If `currentSection` is already defined, + // then this marks the end of that section. Push it to the array of sections + // in the changelog. + if (currentSection !== undefined) { + sections.push(currentSection); + } + + // Initialise the new section. + currentSection = { headerLine: line, bodyLines: [] }; + } else if (currentSection !== undefined) { + // Add lines between the section header and the next to the current section. + currentSection.bodyLines.push(line); + } else { + // This is neither a section header nor are we in a section already, + // so this line is part of the preamble. + preamble.push(line); + } + + i++; + } + + // Push the current section to the array of completed sections, if there is + // still one unfinished. + if (currentSection !== undefined) { + sections.push(currentSection); + } + + return { preamble, sections }; +} + +/** + * Combines an array of lines into a single string by adding line breaks. + */ +export function unlines(lines: string[]): string { + return `${lines.join("\n")}`; +} + +/** + * Renders a given changelog to a string. + */ +export function renderChangelog(changelog: Changelog): string { + let result = unlines(changelog.preamble); + + for (const section of changelog.sections) { + result += `\n${section.headerLine}\n${unlines(section.bodyLines)}`; + } + + return result; +} + +/** + * Processes changelog entries for a backport, converting version references + * from the source major version to the target major version and filtering + * entries that only apply to newer versions. + */ +export function processChangelogForBackports( + sourceBranchMajorVersion: string, + targetBranchMajorVersion: string, + content: string, +): string { + // Changelog entries can use the following format to indicate + // that they only apply to newer versions + const someVersionsOnlyRegex = /\[v(\d+)\+ only\]/; + + // Parse the changelog. + const changelog = parseChangelog(content); + + if (changelog.sections.length === 0) { + throw new Error("Could not find any change sections in CHANGELOG.md"); + } + + // Filter out changelog entries that only apply to newer versions and + // update the section headings with the backport major version for + // sections we keep. + for (const section of changelog.sections) { + // Update the section headings with the backport major version. + section.headerLine = section.headerLine.replace( + `## ${sourceBranchMajorVersion}`, + `## ${targetBranchMajorVersion}`, + ); + + const filteredEntries: string[] = []; + let foundContent = false; + + for (const line of section.bodyLines) { + // Skip the entry if `someVersionsOnlyRegex` matches and the major version + // of the target branch is smaller than the required version. + const match = someVersionsOnlyRegex.exec(line); + if ( + match && + Number.parseInt(targetBranchMajorVersion) < Number.parseInt(match[1]) + ) { + continue; + } + + // Keep the line. + filteredEntries.push(line); + + // Set `foundContent` to `true` if the line is not empty. + if (line.trim() !== "") { + foundContent = true; + } + } + + // Update the section with the retained entries. + section.bodyLines = filteredEntries; + + // Add an entry if we didn't keep any. + if (!foundContent) { + section.bodyLines.push(NO_CHANGES_STR.trim()); + } + } + + return renderChangelog(changelog); +} diff --git a/pr-checks/checks/bundle-zstd.yml b/pr-checks/checks/bundle-zstd.yml deleted file mode 100644 index a961af3c36..0000000000 --- a/pr-checks/checks/bundle-zstd.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: "Bundle: Zstandard checks" -description: "A Zstandard CodeQL bundle should be extracted on supported operating systems" -versions: - - linked -operatingSystems: - - ubuntu - - macos - - windows -steps: - - name: Remove CodeQL from toolcache - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const fs = require('fs'); - const path = require('path'); - const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL'); - if (codeqlPath !== undefined) { - fs.rmdirSync(codeqlPath, { recursive: true }); - } - - id: init - uses: ./../action/init - with: - languages: javascript - tools: ${{ steps.prepare-test.outputs.tools-url }} - - uses: ./../action/analyze - with: - output: ${{ runner.temp }}/results - upload-database: false - - name: Upload SARIF - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: ${{ matrix.os }}-zstd-bundle.sarif - path: ${{ runner.temp }}/results/javascript.sarif - retention-days: 7 - - name: Check diagnostic with expected tools URL appears in SARIF - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - SARIF_PATH: ${{ runner.temp }}/results/javascript.sarif - with: - script: | - const fs = require('fs'); - - const sarif = JSON.parse(fs.readFileSync(process.env['SARIF_PATH'], 'utf8')); - const run = sarif.runs[0]; - - const toolExecutionNotifications = run.invocations[0].toolExecutionNotifications; - const downloadTelemetryNotifications = toolExecutionNotifications.filter(n => - n.descriptor.id === 'codeql-action/bundle-download-telemetry' - ); - if (downloadTelemetryNotifications.length !== 1) { - core.setFailed( - 'Expected exactly one reporting descriptor in the ' + - `'runs[].invocations[].toolExecutionNotifications[]' SARIF property, but found ` + - `${downloadTelemetryNotifications.length}. All notification reporting descriptors: ` + - `${JSON.stringify(toolExecutionNotifications)}.` - ); - } - - const toolsUrl = downloadTelemetryNotifications[0].properties.attributes.toolsUrl; - console.log(`Found tools URL: ${toolsUrl}`); - - const expectedExtension = process.env['RUNNER_OS'] === 'Windows' ? '.tar.gz' : '.tar.zst'; - - if (!toolsUrl.endsWith(expectedExtension)) { - core.setFailed( - `Expected the tools URL to be a ${expectedExtension} file, but found ${toolsUrl}.` - ); - } diff --git a/pr-checks/checks/global-proxy.yml b/pr-checks/checks/global-proxy.yml index 5f90022c04..9d9653c13c 100644 --- a/pr-checks/checks/global-proxy.yml +++ b/pr-checks/checks/global-proxy.yml @@ -5,17 +5,45 @@ versions: - nightly-latest container: image: ubuntu:22.04 + options: --cap-add=NET_ADMIN services: squid-proxy: image: ubuntu/squid:latest ports: - 3128:3128 env: - https_proxy: http://squid-proxy:3128 CODEQL_ACTION_TOLERATE_MISSING_GIT_VERSION: true steps: + - name: Block direct internet access to force proxy usage + run: | + apt-get update -qq && apt-get install -y -qq iptables >/dev/null 2>&1 + PROXY_IP=$(getent hosts squid-proxy | awk '{ print $1 }') + echo "Squid proxy IP: $PROXY_IP" + # Allow all traffic to the proxy container + iptables -A OUTPUT -d "$PROXY_IP" -j ACCEPT + # Allow DNS resolution + iptables -A OUTPUT -p udp --dport 53 -j ACCEPT + iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT + # Allow loopback + iptables -A OUTPUT -o lo -j ACCEPT + # Allow already-established connections (from checkout/prepare-test) + iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + # Block all other outbound HTTP and HTTPS, ensuring direct access fails + iptables -A OUTPUT -p tcp --dport 80 -j REJECT --reject-with tcp-reset + iptables -A OUTPUT -p tcp --dport 443 -j REJECT --reject-with tcp-reset + echo "Direct HTTP/HTTPS access is now blocked - all traffic must go through the proxy" + + - name: Set proxy environment variables + shell: bash + run: | + echo "http_proxy=http://squid-proxy:3128" >> $GITHUB_ENV + echo "HTTP_PROXY=http://squid-proxy:3128" >> $GITHUB_ENV + echo "https_proxy=http://squid-proxy:3128" >> $GITHUB_ENV + echo "HTTPS_PROXY=http://squid-proxy:3128" >> $GITHUB_ENV + - uses: ./../action/init with: languages: javascript tools: ${{ steps.prepare-test.outputs.tools-url }} + - uses: ./../action/analyze diff --git a/pr-checks/checks/go-indirect-tracing-workaround-diagnostic.yml b/pr-checks/checks/go-indirect-tracing-workaround-diagnostic.yml index 895dba2b6c..f0b4097d7b 100644 --- a/pr-checks/checks/go-indirect-tracing-workaround-diagnostic.yml +++ b/pr-checks/checks/go-indirect-tracing-workaround-diagnostic.yml @@ -12,7 +12,7 @@ steps: languages: go tools: ${{ steps.prepare-test.outputs.tools-url }} # Deliberately change Go after the `init` step - - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: "1.20" - name: Build code diff --git a/pr-checks/checks/job-run-uuid-sarif.yml b/pr-checks/checks/job-run-uuid-sarif.yml index dc1dd02d43..b86725d944 100644 --- a/pr-checks/checks/job-run-uuid-sarif.yml +++ b/pr-checks/checks/job-run-uuid-sarif.yml @@ -21,8 +21,8 @@ steps: run: | cd "$RUNNER_TEMP/results" actual=$(jq -r '.runs[0].properties.jobRunUuid' javascript.sarif) - if [[ "$actual" != "$JOB_RUN_UUID" ]]; then - echo "Expected SARIF output to contain job run UUID '$JOB_RUN_UUID', but found '$actual'." + if [[ "$actual" != "$CODEQL_ACTION_JOB_RUN_UUID" ]]; then + echo "Expected SARIF output to contain job run UUID '$CODEQL_ACTION_JOB_RUN_UUID', but found '$actual'." exit 1 else echo "Found job run UUID '$actual'." diff --git a/pr-checks/checks/multi-language-autodetect.yml b/pr-checks/checks/multi-language-autodetect.yml index 801f4521a4..b57e90ab4c 100644 --- a/pr-checks/checks/multi-language-autodetect.yml +++ b/pr-checks/checks/multi-language-autodetect.yml @@ -23,7 +23,7 @@ steps: # We need Python 3.13 for older CLI versions because they are not compatible with Python 3.14 or newer. # See https://github.com/github/codeql-action/pull/3212 if: matrix.version != 'nightly-latest' && matrix.version != 'linked' - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.13" diff --git a/pr-checks/checks/rubocop-multi-language.yml b/pr-checks/checks/rubocop-multi-language.yml index 5ae18526af..37c5d36e90 100644 --- a/pr-checks/checks/rubocop-multi-language.yml +++ b/pr-checks/checks/rubocop-multi-language.yml @@ -5,7 +5,7 @@ versions: - default steps: - name: Set up Ruby - uses: ruby/setup-ruby@89f90524b88a01fe6e0b732220432cc6142926af # v1.313.0 + uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0 with: ruby-version: 2.6 - name: Install Code Scanning integration diff --git a/pr-checks/checks/start-proxy.yml b/pr-checks/checks/start-proxy.yml index a4bf794873..675fc013a2 100644 --- a/pr-checks/checks/start-proxy.yml +++ b/pr-checks/checks/start-proxy.yml @@ -6,16 +6,14 @@ operatingSystems: - windows versions: - linked +env: + CODEQL_ACTION_PROXY_API_REQUESTS: "true" steps: - - uses: ./../action/init - with: - languages: csharp - tools: ${{ steps.prepare-test.outputs.tools-url }} - - name: Setup proxy for registries id: proxy uses: ./../action/start-proxy with: + language: java registry_secrets: | [ { @@ -44,3 +42,13 @@ steps: || !contains(steps.proxy.outputs.proxy_urls, 'https://repo.maven.apache.org/maven2/') || !contains(steps.proxy.outputs.proxy_urls, 'https://repo1.maven.org/maven2') run: exit 1 + + - uses: ./../action/init + env: + CODEQL_PROXY_HOST: ${{ steps.proxy.outputs.proxy_host }} + CODEQL_PROXY_PORT: ${{ steps.proxy.outputs.proxy_port }} + CODEQL_PROXY_CA_CERTIFICATE: ${{ steps.proxy.outputs.proxy_ca_certificate }} + with: + languages: java + tools: ${{ steps.prepare-test.outputs.tools-url }} + config-file: codeql-action@main:tests/multi-language-repo/.github/codeql/custom-queries.yml diff --git a/pr-checks/checks/submit-sarif-failure.yml b/pr-checks/checks/submit-sarif-failure.yml index 9212a5dc79..c33e1322f7 100644 --- a/pr-checks/checks/submit-sarif-failure.yml +++ b/pr-checks/checks/submit-sarif-failure.yml @@ -21,7 +21,7 @@ permissions: security-events: write # needed to upload the SARIF file steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: ./init with: languages: javascript diff --git a/pr-checks/checks/with-checkout-path.yml b/pr-checks/checks/with-checkout-path.yml index 7a5866b783..a6cde895b6 100644 --- a/pr-checks/checks/with-checkout-path.yml +++ b/pr-checks/checks/with-checkout-path.yml @@ -14,7 +14,7 @@ steps: rm -rf ./* .github .git # Check out the actions repo again, but at a different location. # choose an arbitrary SHA so that we can later test that the commit_oid is not from main - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: 474bbf07f9247ffe1856c6a0f94aeeb10e7afee6 path: x/y/z/some-path diff --git a/pr-checks/config.ts b/pr-checks/config.ts index 75cd0a1515..356fe665f9 100644 --- a/pr-checks/config.ts +++ b/pr-checks/config.ts @@ -12,6 +12,12 @@ export const REPO_ROOT = path.join(PR_CHECKS_DIR, ".."); /** The path of the file configuring which checks shouldn't be required. */ export const PR_CHECK_EXCLUDED_FILE = path.join(PR_CHECKS_DIR, "excluded.yml"); +/** The path of the main `package.json`. */ +export const PACKAGE_JSON = path.join(REPO_ROOT, "package.json"); + +/** The path of the changelog. */ +export const CHANGELOG_FILE = path.join(REPO_ROOT, "CHANGELOG.md"); + /** The path to the esbuild metadata file. */ export const BUNDLE_METADATA_FILE = path.join(REPO_ROOT, "meta.json"); @@ -30,3 +36,13 @@ export const API_COMPATIBILITY_FILE = path.join( SOURCE_ROOT, "api-compatibility.json", ); + +/** The prefix of CodeQL CLI bundle release URLs. */ +export const CLI_BUNDLE_RELEASE_URL_PREFIX = + "https://github.com/github/codeql-action/releases/tag/codeql-bundle-v"; + +/** A common interface for operations that support dry runs. */ +export interface DryRunOption { + /** A value indicating whether to perform operations with side effects. */ + dryRun?: boolean; +} diff --git a/pr-checks/excluded.yml b/pr-checks/excluded.yml index d8d643d107..1a5262fc0b 100644 --- a/pr-checks/excluded.yml +++ b/pr-checks/excluded.yml @@ -10,6 +10,7 @@ is: - "check-expected-release-files" - "Cleanup artifacts" - "CodeQL" + - "copilot-pull-request-reviewer" - "Dependabot" - "Label PR with size" - "Post repo size comment" diff --git a/pr-checks/package.json b/pr-checks/package.json index eeb63afa87..6c23d847f2 100644 --- a/pr-checks/package.json +++ b/pr-checks/package.json @@ -4,14 +4,14 @@ "dependencies": { "@actions/core": "^2.0.3", "@actions/github": "^8.0.1", - "@octokit/core": "^7.0.6", + "@octokit/core": "^7.0.7", "@octokit/plugin-paginate-rest": ">=9.2.2", "@octokit/plugin-rest-endpoint-methods": "^17.0.0", - "semver": "^7.8.0", + "semver": "^7.8.5", "yaml": "^2.9.0" }, "devDependencies": { "@types/node": "^20.19.43", - "tsx": "^4.22.4" + "tsx": "^4.23.8" } } diff --git a/pr-checks/prepare-changelog.test.ts b/pr-checks/prepare-changelog.test.ts new file mode 100644 index 0000000000..13a302c8f6 --- /dev/null +++ b/pr-checks/prepare-changelog.test.ts @@ -0,0 +1,54 @@ +/** + * Tests for `prepare-changelog.ts`. + */ + +import * as assert from "node:assert/strict"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import { afterEach, beforeEach, describe, it } from "node:test"; + +import { EMPTY_CHANGELOG, NO_CHANGES_STR } from "./changelog"; +import { extractChangelogSnippet } from "./prepare-changelog"; + +let testDir: string; + +beforeEach(() => { + // Set up a temporary directory for testing + testDir = fs.mkdtempSync(path.join(os.tmpdir(), "prepare-changelog-test-")); +}); + +afterEach(() => { + /** Clean up temporary directories. */ + fs.rmSync(testDir, { recursive: true, force: true }); +}); + +const testBody = `- Test change`; +const testChangelog = `${EMPTY_CHANGELOG.replace(NO_CHANGES_STR, testBody)} + +## Another section + +- Other change`; + +describe("extractChangelogSnippet", async () => { + await it("returns the default body if the input doesn't exist", async () => { + const result = extractChangelogSnippet(path.join(testDir, "not-here.md")); + assert.deepEqual(result, NO_CHANGES_STR); + }); + + await it("returns the first section if the input exists", async () => { + const changelogPath = path.join(testDir, "test-readme.md"); + fs.writeFileSync(changelogPath, testChangelog); + + const result = extractChangelogSnippet(changelogPath); + assert.deepEqual(result, testBody); + }); + + await it("returns an empty string if there is no first section", async () => { + const changelogPath = path.join(testDir, "test-readme.md"); + fs.writeFileSync(changelogPath, "# CodeQL Action Changelog\n"); + + const result = extractChangelogSnippet(changelogPath); + assert.deepEqual(result, ""); + }); +}); diff --git a/pr-checks/prepare-changelog.ts b/pr-checks/prepare-changelog.ts new file mode 100755 index 0000000000..0c89699fc8 --- /dev/null +++ b/pr-checks/prepare-changelog.ts @@ -0,0 +1,82 @@ +#!/usr/bin/env npx tsx + +/** + * Extracts the body of the first changelog section and outputs it to either + * stdout or a file. + */ + +import * as fs from "node:fs"; +import { parseArgs } from "node:util"; + +import { NO_CHANGES_STR, parseChangelog } from "./changelog"; +import { CHANGELOG_FILE } from "./config"; +import { getErrorMessage } from "./util"; + +/** + * Prepare the changelog for the new release + * This function will extract the part of the changelog that + * we want to include in the new release. + * + * @param changelogPath The path to the changelog file. + */ +export function extractChangelogSnippet(changelogPath: string) { + try { + const content = fs.readFileSync(changelogPath, "utf-8"); + const changelog = parseChangelog(content); + + // Return an empty string if we couldn't find the first section. + if (changelog.sections.length === 0) { + return ""; + } + + return changelog.sections[0].bodyLines.join("\n").trim(); + } catch (err) { + if (err instanceof Error && "code" in err && err.code === "ENOENT") { + console.error(`Changelog file at '${changelogPath}' does not exist.`); + return NO_CHANGES_STR; + } else { + throw Error( + `Failed to open changelog file at '${changelogPath}': ${getErrorMessage(err)}`, + ); + } + } +} + +function main() { + try { + const { values } = parseArgs({ + options: { + changelog: { + type: "string", + short: "f", + default: CHANGELOG_FILE, + }, + output: { + type: "string", + short: "o", + }, + }, + strict: true, + }); + + const body = extractChangelogSnippet(values.changelog); + + // If no `output` argument was provided, output to stdout. Otherwise, + // write a file to the specified path. + if (values.output === undefined) { + console.info(body); + } else { + fs.writeFileSync(values.output, body); + } + + return 0; + } catch (err) { + console.error(`Failed to prepare changelog: ${getErrorMessage(err)}`); + return -1; + } +} + +// Only call `main` if this script was run directly. +if (require.main === module) { + process.exit(main()); +} diff --git a/pr-checks/rollback-changelog.test.ts b/pr-checks/rollback-changelog.test.ts new file mode 100644 index 0000000000..5264755a69 --- /dev/null +++ b/pr-checks/rollback-changelog.test.ts @@ -0,0 +1,45 @@ +/** + * Tests for `rollback-changelog.ts`. + */ + +import * as assert from "node:assert/strict"; +import * as fs from "node:fs"; +import { describe, it } from "node:test"; + +import { getReleaseDateString, parseChangelog } from "./changelog"; +import { CHANGELOG_FILE } from "./config"; +import { updateChangelog } from "./rollback-changelog"; + +describe("updateChangelog", async () => { + await it("replaces the first section with one for the rollback release", async () => { + const actualChangelog = parseChangelog( + fs.readFileSync(CHANGELOG_FILE, "utf-8"), + ); + const existingFirstSection = actualChangelog.sections[0]; + + const today = new Date(); + updateChangelog(actualChangelog, { + "new-version": "Test.1.3", + "rollback-version": "Test.1.2", + "target-version": "Test.1.1", + today, + }); + + // Check that the old, first section is gone. + for (const section of actualChangelog.sections) { + assert.notDeepEqual(section, existingFirstSection); + } + + // Check that the new, first section matches our expectations. + const newFirstSection = actualChangelog.sections[0]; + assert.deepEqual( + newFirstSection.headerLine, + `## Test.1.3 - ${getReleaseDateString(today)}`, + ); + assert.equal(newFirstSection.bodyLines.length, 3); + assert.deepEqual( + newFirstSection.bodyLines[1], + `This release rolls back Test.1.2 due to issues with that release. It is identical to Test.1.1.`, + ); + }); +}); diff --git a/pr-checks/rollback-changelog.ts b/pr-checks/rollback-changelog.ts new file mode 100755 index 0000000000..15a37b1b7c --- /dev/null +++ b/pr-checks/rollback-changelog.ts @@ -0,0 +1,84 @@ +#!/usr/bin/env npx tsx + +/** + * Replaces the current, first section of the changelog with a new one for the rollback release. + */ + +import * as fs from "node:fs"; +import { parseArgs } from "node:util"; + +import { + Changelog, + ChangelogSection, + getReleaseDateString, + parseChangelog, + renderChangelog, +} from "./changelog"; +import { CHANGELOG_FILE } from "./config"; +import { getErrorMessage } from "./util"; + +export interface RollbackChangelogInputs { + "target-version": string; + "rollback-version": string; + "new-version": string; + today?: Date; +} + +/** + * Replaces the current, first section of the changelog with a new one for the rollback release. + */ +export function updateChangelog( + changelog: Changelog, + versions: RollbackChangelogInputs, +) { + // Drop the existing first section. + changelog.sections.shift(); + + // Construct the section for the rollback version. + const newSection: ChangelogSection = { + headerLine: `## ${versions["new-version"]} - ${getReleaseDateString(versions.today)}`, + bodyLines: [ + "", + `This release rolls back ${versions["rollback-version"]} due to issues with that release. It is identical to ${versions["target-version"]}.`, + "", + ], + }; + + // Add the new section at the top of the changelog. + changelog.sections.unshift(newSection); +} + +function main() { + try { + const options = { + "target-version": { type: "string", short: "t" }, + "rollback-version": { type: "string", short: "r" }, + "new-version": { type: "string", short: "n" }, + } as const; + + const { values } = parseArgs({ options, strict: true }); + + for (const key of Object.keys(options)) { + const val = values[key as keyof typeof values]; + if (val === undefined || val.trim() === "") { + throw new Error(`Argument '--${key}' is required.`); + } + } + + const changelog = parseChangelog(fs.readFileSync(CHANGELOG_FILE, "utf-8")); + updateChangelog(changelog, values as RollbackChangelogInputs); + console.info(renderChangelog(changelog)); + + return 0; + } catch (err) { + console.error( + `Failed to prepare rollback changelog: ${getErrorMessage(err)}`, + ); + return -1; + } +} + +// Only call `main` if this script was run directly. +if (require.main === module) { + process.exit(main()); +} diff --git a/pr-checks/sync.ts b/pr-checks/sync.ts index b9a5bfea0f..9dcce16fe5 100755 --- a/pr-checks/sync.ts +++ b/pr-checks/sync.ts @@ -211,8 +211,8 @@ const languageSetups: LanguageSetups = { name: "Install Node.js", uses: pinnedUses( "actions/setup-node", - "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", - "v6.4.0", + "820762786026740c76f36085b0efc47a31fe5020", + "v7.0.0", ), with: { "node-version": defaultLanguageVersions.javascript, @@ -233,8 +233,8 @@ const languageSetups: LanguageSetups = { name: "Install Go", uses: pinnedUses( "actions/setup-go", - "924ae3a1cded613372ab5595356fb5720e22ba16", - "v6.5.0", + "b7ad1dad31e06c5925ef5d2fc7ad053ef454303e", + "v7.0.0", ), with: { "go-version": `\${{ inputs.go-version || '${defaultLanguageVersions.go}' }}`, @@ -253,8 +253,8 @@ const languageSetups: LanguageSetups = { name: "Install Java", uses: pinnedUses( "actions/setup-java", - "ad2b38190b15e4d6bdf0c97fb4fca8412226d287", - "v5.3.0", + "b6effb05e454b25005698d916606bdc6ffcbf961", + "v5.7.0", ), with: { "java-version": `\${{ inputs.java-version || '${defaultLanguageVersions.java}' }}`, @@ -271,8 +271,8 @@ const languageSetups: LanguageSetups = { name: "Install Python", uses: pinnedUses( "actions/setup-python", - "ece7cb06caefa5fff74198d8649806c4678c61a1", - "v6.3.0", + "5fda3b95a4ea91299a34e894583c3862153e4b97", + "v7.0.0", ), with: { "python-version": `\${{ inputs.python-version || '${defaultLanguageVersions.python}' }}`, @@ -288,8 +288,8 @@ const languageSetups: LanguageSetups = { name: "Install .NET", uses: pinnedUses( "actions/setup-dotnet", - "9a946fdbd5fb07b82b2f5a4466058b876ab72bb2", - "v5.3.0", + "a98b56852c35b8e3190ac28c8c2271da59106c68", + "v6.0.0", ), with: { "dotnet-version": `\${{ inputs.dotnet-version || '${defaultLanguageVersions.csharp}' }}`, @@ -529,8 +529,8 @@ function generateJob( name: "Check out repository", uses: pinnedUses( "actions/checkout", - "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", - "v7.0.0", + "3d3c42e5aac5ba805825da76410c181273ba90b1", + "v7.0.1", ), }, ...setupInfo.steps, diff --git a/pr-checks/update-release-branch.ts b/pr-checks/update-release-branch.ts new file mode 100755 index 0000000000..088da59281 --- /dev/null +++ b/pr-checks/update-release-branch.ts @@ -0,0 +1,840 @@ +#!/usr/bin/env npx tsx + +/** + * Creates a release preparation branch and opens a PR to merge changes from a + * source branch into a target release branch. + * + * For primary releases this merges `main` into the latest `releases/vN` branch. + * For backports this merges a newer release branch into an older one, handling + * version number and changelog migration automatically. + * + * Usage: + * update-release-branch.ts \ + * --repository-nwo github/codeql-action \ + * --source-branch main \ + * --target-branch releases/v4 \ + * --conductor username \ + * [--is-primary-release] \ + * [--dry-run] + */ + +import { execFileSync, type ExecFileSyncOptions } from "node:child_process"; +import { parseArgs } from "node:util"; + +import { type ApiClient, getApiClient } from "./api-client"; +import * as changelog from "./changelog"; +import { DryRunOption, REPO_ROOT } from "./config"; +import { + getCurrentVersion, + replaceVersionInPackageJson, + withPackageJson, +} from "./versions"; + +/** + * NB: This exact commit message is used to find commits for reverting during backports. + * Changing it requires a transition period where both old and new versions are supported. + */ +export const BACKPORT_COMMIT_MESSAGE = "Update version and changelog for v"; + +/** + * Commit message used for rebuild commits, both those produced by this script and those produced + * by the `Rebuild Action` workflow (`.github/workflows/rebuild.yml`). + */ +export const REBUILD_COMMIT_MESSAGE = "Rebuild"; + +/** The name of the git remote. */ +const ORIGIN = "origin"; + +/** Environment variables checked (in order) for a GitHub API token. */ +const TOKEN_ENVIRONMENT_VARIABLES = ["GH_TOKEN", "GITHUB_TOKEN"] as const; + +/** The expected prefix for release branch names. */ +const RELEASE_BRANCH_PREFIX = "releases/v"; + +/** + * Gets a GitHub API token from one of the supported environment variables. + * @throws If none of the supported environment variables is set. + */ +export function getGitHubToken(): string { + for (const name of TOKEN_ENVIRONMENT_VARIABLES) { + const token = process.env[name]?.trim(); + if (token) { + return token; + } + } + throw new Error("Missing GitHub token. Set GITHUB_TOKEN or GH_TOKEN."); +} + +/** Options for {@link runCommand}. */ +export interface RunCommandOptions extends DryRunOption { + /** Options for `execFileSync`. */ + execOptions?: ExecFileSyncOptions; +} + +/** + * Runs a command, streaming output to the console by default. + * + * @param command The name of the command to run. + * @param args The arguments for the command. + * @throws When the process exits with a non-zero exit code. + * @param options How to run the command. + */ +export function runCommand( + command: string, + args: string[], + options?: RunCommandOptions, +) { + if (!options?.dryRun) { + console.log(`Running \`${command} ${args.join(" ")}\`.`); + return execFileSync(command, args, { + stdio: "inherit", + cwd: REPO_ROOT, + ...options?.execOptions, + }); + } else { + console.info( + `[DRY RUN] Would have executed '${command} ${args.join(" ")}'`, + ); + return ""; + } +} + +/** Options for {@link runGit}. */ +export interface RunGitOptions extends DryRunOption { + /** When true, non-zero exit codes will not throw. */ + allowNonZeroExitCode?: boolean; +} + +/** + * Runs `git` with the given `args` and returns the stdout. + * + * @param args - Arguments to pass to `git`. + * @param options - Optional settings. + * @throws If `git` does not exit successfully, unless + * `options.allowNonZeroExitCode` is `true`. + * @returns The trimmed stdout output. + */ +export function runGit(args: string[], options?: RunGitOptions): string { + const execOptions: ExecFileSyncOptions = { + encoding: "utf8", + stdio: ["pipe", "pipe", "pipe"], + }; + + try { + const result = runCommand("git", args, { + dryRun: options?.dryRun, + execOptions, + }) as string; + return result.trimEnd(); + } catch (error: unknown) { + if (options?.allowNonZeroExitCode) { + // execFileSync throws an object with `stdout` when the process exits + // with a non-zero code. + const execError = error as { stdout?: Buffer | string }; + if (typeof execError.stdout === "string") { + return execError.stdout.trimEnd(); + } + if (Buffer.isBuffer(execError.stdout)) { + return execError.stdout.toString("utf8").trimEnd(); + } + return ""; + } + throw error; + } +} + +/** Returns true if the given branch exists on the origin remote. */ +export function branchExistsOnRemote(branchName: string): boolean { + const result = runGit(["ls-remote", "--heads", ORIGIN, branchName]); + return result !== ""; +} + +/** Represents commits returned by the GitHub API (relevant fields only). */ +export interface GitHubCommit { + sha: string; + commit: { message: string; author: { date?: string } | null }; + author: { login: string } | null; + committer: { login: string } | null; + parents: Array<{ sha: string }>; +} + +/** Returns true if the commit is an automatic PR merge commit made by GitHub. */ +export function isPrMergeCommit(commit: GitHubCommit): boolean { + return commit.committer?.login === "web-flow" && commit.parents.length > 1; +} + +/** + * Gets a list of commits on the source branch that are not on the target branch, + * excluding automatic PR merge commits. This will not include any commits that + * exist on the target branch that aren't on the source branch. + * + * Uses `git log` to find the SHAs, then fetches each commit from the GitHub API + * to obtain full metadata (author, parents, associated PRs, etc.). + * + * @param client - An authenticated GitHub API client. + * @param owner - The repository owner. + * @param repo - The repository name. + * @param sourceBranch - The source branch name (without `origin/` prefix). + * @param targetBranch - The target branch name (without `origin/` prefix). + * @returns The list of non-merge commits unique to the source branch. + */ +export async function getCommitDifference( + client: ApiClient, + owner: string, + repo: string, + sourceBranch: string, + targetBranch: string, +): Promise { + const logOutput = runGit([ + "log", + "--pretty=format:%H", + `${ORIGIN}/${targetBranch}..${ORIGIN}/${sourceBranch}`, + ]); + + // An empty log output means no commits to merge. + if (logOutput === "") { + return []; + } + + const shas = logOutput.split("\n"); + + // Fetch full commit objects from the API. + console.info( + `Fetching information about ${shas.length} commits from the API...`, + ); + + const commits: GitHubCommit[] = []; + for (const sha of shas) { + const { data } = await client.rest.repos.getCommit({ + owner, + repo, + ref: sha, + }); + commits.push(data as GitHubCommit); + } + + // Filter out automatic PR merge commits. + return commits.filter((c) => !isPrMergeCommit(c)); +} + +/** Truncates a commit message for display. */ +export function getTruncatedCommitMessage(message: string): string { + const firstLine = message.split("\n")[0]; + if (firstLine.length > 60) { + return `${firstLine.slice(0, 57)}...`; + } + return firstLine; +} + +/** Represents pull requests associated with a commit (relevant fields only). */ +export interface AssociatedPullRequest { + number: number; + user: { login: string; site_admin: boolean } | null; + merge_commit_sha: string | null; +} + +/** + * Gets the pull request that introduced a commit to the source branch. + * Returns the earliest PR by number if multiple are associated. + */ +export async function getPrForCommit( + client: ApiClient, + owner: string, + repo: string, + commit: GitHubCommit, +): Promise { + const prs = await client.paginate( + client.rest.repos.listPullRequestsAssociatedWithCommit, + { + owner, + repo, + commit_sha: commit.sha, + }, + ); + + if (prs.length === 0) { + return undefined; + } + + // Return the earliest PR by number. + const sorted = [...prs].sort((a, b) => a.number - b.number); + return sorted[0]; +} + +/** + * Get the login of the person who merged a pull request. + * Falls back to the commit author of the merge commit. + * For most cases this will be the same as the author, but for PRs opened + * by external contributors getting the merger will get us the GitHub + * employee who reviewed and merged the PR. + */ +export async function getMergerOfPr( + client: ApiClient, + owner: string, + repo: string, + pr: AssociatedPullRequest, +): Promise { + if (!pr.merge_commit_sha) { + return "unknown"; + } + const { data: commit } = await client.rest.repos.getCommit({ + owner, + repo, + ref: pr.merge_commit_sha, + }); + return commit.author?.login ?? "unknown"; +} + +/** + * Returns the PR author's login if they are GitHub staff (site_admin), + * otherwise undefined. + */ +export function getPrAuthorIfStaff( + pr: AssociatedPullRequest, +): string | undefined { + if (pr.user?.site_admin) { + return pr.user.login; + } + return undefined; +} + +/** Parameters for {@link openPr}. */ +interface OpenPrParams { + client: ApiClient; + owner: string; + repo: string; + commits: GitHubCommit[]; + sourceBranchShortSha: string; + newBranchName: string; + sourceBranch: string; + targetBranch: string; + conductor: string; + isPrimaryRelease: boolean; + conflictedFiles: string[]; + dryRun: boolean; +} + +/** + * Opens a pull request from the new branch to the target branch and assigns + * the conductor. + */ +export async function openPr(params: OpenPrParams): Promise { + const { + client, + owner, + repo, + commits, + sourceBranchShortSha, + newBranchName, + sourceBranch, + targetBranch, + conductor, + isPrimaryRelease, + conflictedFiles, + dryRun, + } = params; + + // Sort the commits into those with and without associated PRs. + const pullRequests: AssociatedPullRequest[] = []; + const commitsWithoutPrs: GitHubCommit[] = []; + + console.info(`Finding PRs for ${commits.length} commits...`); + + for (const commit of commits) { + const pr = await getPrForCommit(client, owner, repo, commit); + if (!pr) { + commitsWithoutPrs.push(commit); + } else if (!pullRequests.some((p) => p.number === pr.number)) { + pullRequests.push(pr); + } + } + + console.log(`Found ${pullRequests.length} pull requests.`); + console.log( + `Found ${commitsWithoutPrs.length} commits not in a pull request.`, + ); + + // Sort PRs by number (ascending) and commits by date. + pullRequests.sort((a, b) => a.number - b.number); + commitsWithoutPrs.sort((a, b) => { + const dateA = a.commit.author?.date ?? ""; + const dateB = b.commit.author?.date ?? ""; + return dateA.localeCompare(dateB); + }); + + // Build the PR body. + const body: string[] = []; + body.push(`Merging ${sourceBranchShortSha} into \`${targetBranch}\`.`); + body.push(""); + body.push(`Conductor for this PR is @${conductor}.`); + + if (pullRequests.length > 0) { + body.push(""); + body.push("Contains the following pull requests:"); + for (const pr of pullRequests) { + const displayUser = + getPrAuthorIfStaff(pr) ?? + (await getMergerOfPr(client, owner, repo, pr)); + body.push(`- #${pr.number} (@${displayUser})`); + } + } + + if (commitsWithoutPrs.length > 0) { + body.push(""); + body.push("Contains the following commits not from a pull request:"); + for (const commit of commitsWithoutPrs) { + const authorDesc = commit.author ? ` (@${commit.author.login})` : ""; + body.push( + `- ${commit.sha} - ${getTruncatedCommitMessage(commit.commit.message)}${authorDesc}`, + ); + } + } + + body.push(""); + body.push("Please do the following:"); + if (conflictedFiles.length > 0) { + body.push( + " - [ ] Ensure `package.json` file contains the correct version.", + ); + body.push( + " - [ ] Add a commit to this branch to resolve the merge conflicts in the following files:", + ); + for (const file of conflictedFiles) { + body.push(` - \`${file}\``); + } + body.push( + ` - [ ] Rebuild the Action locally (\`npm run build\`) and push any changes to the built output in \`lib\` as a separate commit named exactly \`${REBUILD_COMMIT_MESSAGE}\`.`, + ); + body.push( + " - [ ] Ensure another maintainer has reviewed the additional commits you added to this branch to resolve the merge conflicts.", + ); + } + body.push( + " - [ ] Ensure the CHANGELOG displays the correct version and date.", + ); + body.push( + " - [ ] Ensure the CHANGELOG includes all relevant, user-facing changes since the last release.", + ); + body.push( + ` - [ ] Check that there are not any unexpected commits being merged into the \`${targetBranch}\` branch.`, + ); + body.push( + " - [ ] Ensure the docs team is aware of any documentation changes that need to be released.", + ); + body.push( + " - [ ] Approve running the full set of PR checks if you have not pushed any changes.", + ); + body.push( + " - [ ] Approve and merge this PR. Make sure `Create a merge commit` is selected rather than `Squash and merge` or `Rebase and merge`.", + ); + + if (isPrimaryRelease) { + body.push( + " - [ ] Merge the mergeback PR that will automatically be created once this PR is merged.", + ); + body.push( + " - [ ] Merge all backport PRs to older release branches, that will automatically be created once this PR is merged.", + ); + } + + const title = `Merge ${sourceBranch} into ${targetBranch}`; + + if (dryRun) { + console.info(`[DRY RUN] Would create PR: "${title}" with body:`); + + for (const line of body) { + console.info(`[DRY RUN] > ${line}`); + } + + console.info(`[DRY RUN] and assign it to @${conductor}`); + + return; + } + + // Create the pull request. + const { data: pr } = await client.rest.pulls.create({ + owner, + repo, + title, + body: body.join("\n"), + head: newBranchName, + base: targetBranch, + }); + console.log(`Created PR #${pr.number}`); + + // Assign the conductor. + await client.rest.issues.addAssignees({ + owner, + repo, + issue_number: pr.number, + assignees: [conductor], + }); + console.log(`Assigned PR to ${conductor}`); +} + +interface MainOptions { + dryRun: boolean; + repositoryNwo: string; + sourceBranch: string; + targetBranch: string; + isPrimaryRelease: boolean; + conductor: string; +} + +function parseCliOptions(): MainOptions { + const { values } = parseArgs({ + options: { + "dry-run": { type: "boolean", default: false }, + "repository-nwo": { type: "string" }, + "source-branch": { type: "string" }, + "target-branch": { type: "string" }, + "is-primary-release": { type: "boolean", default: false }, + conductor: { type: "string" }, + }, + strict: true, + }); + + if (!values["repository-nwo"]) { + throw new Error("--repository-nwo is required"); + } + if (!values["source-branch"]) { + throw new Error("--source-branch is required"); + } + if (!values["target-branch"]) { + throw new Error("--target-branch is required"); + } + if (!values["conductor"]) { + throw new Error("--conductor is required"); + } + + return { + dryRun: values["dry-run"], + repositoryNwo: values["repository-nwo"], + sourceBranch: values["source-branch"], + targetBranch: values["target-branch"], + isPrimaryRelease: values["is-primary-release"] ?? false, + conductor: values["conductor"], + }; +} + +/** + * Rebuilds the action (npm ci + npm run build) and commits any changes. + */ +export function rebuildAction(options: MainOptions): void { + // For backports, the only source-level change vs the source branch is the new version number, + // so we just need to refresh the version embedded in `lib/`. + runCommand("npm", ["ci"]); + runCommand("npm", ["run", "build"]); + + runGit(["add", "--all"], { dryRun: options.dryRun }); + + // `git diff --cached --quiet` exits 0 if there are no staged changes. + try { + execFileSync("git", ["diff", "--cached", "--quiet"]); + console.log("Rebuild produced no changes; skipping Rebuild commit."); + } catch { + runGit(["commit", "-m", REBUILD_COMMIT_MESSAGE], { + dryRun: options.dryRun, + }); + console.log("Created Rebuild commit."); + } +} + +/** + * Prepares the new update/backport branch. + * + * @param options The options we are running with. + * @param newBranchName The name of the new branch to create. + * @param targetBranchMajorVersion The target branch's major version. + * @param version The target version. + */ +export async function prepareNewBranch( + options: MainOptions, + newBranchName: string, + targetBranchMajorVersion: string, + version: string, +): Promise { + // The process of creating the v{Older} release can run into merge conflicts. We commit the unresolved + // conflicts so a maintainer can easily resolve them (vs erroring and requiring maintainers to + // reconstruct the release manually) + let conflictedFiles: string[] = []; + + if (!options.isPrimaryRelease) { + // For backports, the source branch is also a release branch. + const sourceBranchMajorVersion = options.sourceBranch.replace( + RELEASE_BRANCH_PREFIX, + "", + ); + + // Start from the target branch. + console.log( + `Creating ${newBranchName} from the ${ORIGIN}/${options.targetBranch} branch`, + ); + + runGit( + ["checkout", "-b", newBranchName, `${ORIGIN}/${options.targetBranch}`], + { dryRun: options.dryRun }, + ); + + // Revert the commit that we made as part of the last release that updated the version number and + // changelog to refer to {older}.x.x variants. This avoids merge conflicts in the changelog and + // package.json files when we merge in the v{latest} branch. + // This commit will not exist the first time we release the v{N-1} branch from the v{N} branch, so we + // use `git log --grep` to conditionally revert the commit. + console.log( + "Reverting the version number and changelog updates from the last release to avoid conflicts", + ); + const vOlderUpdateCommits = runGit([ + "log", + "--grep", + `^${BACKPORT_COMMIT_MESSAGE}`, + "--format=%H", + ]) + .split("\n") + .filter((s) => s !== ""); + + if (vOlderUpdateCommits.length > 0) { + // Only revert the newest commit as older ones will already have been + // reverted in previous releases. + console.log(` Reverting ${vOlderUpdateCommits[0]}`); + runGit(["revert", vOlderUpdateCommits[0], "--no-edit"], { + dryRun: options.dryRun, + }); + + // Also revert the "Rebuild" commit, whether created by this script or + // by the `Rebuild Action` workflow. + const rebuildCommits = runGit([ + "log", + "--grep", + `^${REBUILD_COMMIT_MESSAGE}$`, + "--format=%H", + ]) + .split("\n") + .filter((s) => s !== ""); + const rebuildCommit = rebuildCommits[0]; + console.log(` Reverting ${rebuildCommit}`); + runGit(["revert", rebuildCommit, "--no-edit"], { + dryRun: options.dryRun, + }); + } else { + console.log(" Nothing to revert."); + } + + // Merge the source branch into the release prep branch. + console.log( + `Merging ${ORIGIN}/${options.sourceBranch} into the release prep branch`, + ); + runGit(["merge", `${ORIGIN}/${options.sourceBranch}`], { + allowNonZeroExitCode: true, + dryRun: options.dryRun, + }); + conflictedFiles = runGit(["diff", "--name-only", "--diff-filter", "U"]) + .split("\n") + .filter((s) => s !== ""); + if (conflictedFiles.length > 0) { + runGit(["add", "."], { + dryRun: options.dryRun, + }); + runGit(["commit", "--no-edit"], { + dryRun: options.dryRun, + }); + } + + // Migrate the package version number. + console.log(`Setting version number to '${version}' in package.json`); + withPackageJson((content) => { + const currentPkgVersion = getCurrentVersion(content); + if (currentPkgVersion) { + return { + content: replaceVersionInPackageJson( + currentPkgVersion, + version, + content, + ), + value: currentPkgVersion, + }; + } + return { value: currentPkgVersion }; + }, options); + runGit(["add", "package.json"], { + dryRun: options.dryRun, + }); + + // Migrate the changelog notes from the source major version to the target. + console.log( + `Migrating changelog notes from v${sourceBranchMajorVersion} to v${targetBranchMajorVersion}`, + ); + changelog.withChangelog( + (contents) => + changelog.processChangelogForBackports( + sourceBranchMajorVersion, + targetBranchMajorVersion, + contents, + ), + options, + ); + + runGit(["add", "CHANGELOG.md"], { + dryRun: options.dryRun, + }); + runGit(["commit", "-m", `${BACKPORT_COMMIT_MESSAGE}${version}`], { + dryRun: options.dryRun, + }); + } else { + // For a standard (primary) release, there won't be new commits on the + // target branch that aren't already on the source branch, so we can just + // start from the source branch. + runGit( + ["checkout", "-b", newBranchName, `${ORIGIN}/${options.sourceBranch}`], + { + dryRun: options.dryRun, + }, + ); + + console.log("Updating changelog"); + changelog.withChangelog( + (contents) => changelog.setVersionAndDate(version, contents), + { ...options, initChangelog: true }, + ); + + runGit(["add", "CHANGELOG.md"], { + dryRun: options.dryRun, + }); + runGit(["commit", "-m", `Update changelog for v${version}`], { + dryRun: options.dryRun, + }); + } + + // For backports, rebuild the action unless there were merge conflicts. + if (!options.isPrimaryRelease) { + if (conflictedFiles.length === 0) { + console.log("Rebuilding the Action."); + rebuildAction(options); + } else { + console.log( + `Skipping automatic rebuild because the merge produced conflicts in: ${conflictedFiles.join(", ")}`, + ); + } + } + + return conflictedFiles; +} + +async function main(): Promise { + const options = parseCliOptions(); + const token = getGitHubToken(); + const client = getApiClient(token); + + if (!options.targetBranch.startsWith(RELEASE_BRANCH_PREFIX)) { + throw new Error( + `Expected target branch to start with '${RELEASE_BRANCH_PREFIX}', but got '${options.targetBranch}'.`, + ); + } + if ( + !options.isPrimaryRelease && + !options.sourceBranch.startsWith(RELEASE_BRANCH_PREFIX) + ) { + throw new Error( + `Expected source branch to start with '${RELEASE_BRANCH_PREFIX}' for backports, but got '${options.sourceBranch}'.`, + ); + } + if (!options.repositoryNwo.includes("/")) { + throw new Error( + `Expected repository name with owner in 'owner/repo' format, but got '${options.repositoryNwo}'`, + ); + } + + const targetBranchMajorVersion = options.targetBranch.replace( + RELEASE_BRANCH_PREFIX, + "", + ); + + const currentVersion = withPackageJson((content) => { + return { value: getCurrentVersion(content) }; + }, options); + + if (!currentVersion) { + throw new Error("Failed to read current version from package.json"); + } + + const [, vMinor, vPatch] = currentVersion.split("."); + const version = `${targetBranchMajorVersion}.${vMinor}.${vPatch}`; + + console.log( + `Considering difference between ${options.sourceBranch} and ${options.targetBranch}...`, + ); + + const sourceBranchShortSha = runGit([ + "rev-parse", + "--short", + `${ORIGIN}/${options.sourceBranch}`, + ]); + console.log( + `Current head of ${options.sourceBranch} is ${sourceBranchShortSha}.`, + ); + + const [owner, repo] = options.repositoryNwo.split("/"); + const commits = await getCommitDifference( + client, + owner, + repo, + options.sourceBranch, + options.targetBranch, + ); + + if (commits.length === 0) { + console.log( + `No commits to merge from ${options.sourceBranch} to ${options.targetBranch}.`, + ); + return; + } + + // Use a distinct branch prefix to support specific PR checks on backports. + const branchPrefix = options.isPrimaryRelease ? "update" : "backport"; + + // The branch name is based on the target version and the SHA of the source + // branch head. If the branch already exists we can assume this script has + // already run for this combination. + const newBranchName = `${branchPrefix}-v${version}-${sourceBranchShortSha}`; + console.log(`Branch name is '${newBranchName}'.`); + + // Check if the branch already exists. If so we can abort as this script + // has already run on this combination of branches. + if (branchExistsOnRemote(newBranchName)) { + console.log(`Branch '${newBranchName}' already exists. Nothing to do.`); + return; + } + + // Prepare the update/backport branch. + const conflictedFiles = await prepareNewBranch( + options, + newBranchName, + targetBranchMajorVersion, + version, + ); + + // Push the new branch to the remote. + console.log(`Creating branch ${newBranchName}.`); + runGit(["push", ORIGIN, newBranchName], { dryRun: options.dryRun }); + + // Open a PR to merge the new branch into the target branch. + await openPr({ + client, + owner, + repo, + commits, + sourceBranchShortSha, + newBranchName, + sourceBranch: options.sourceBranch, + targetBranch: options.targetBranch, + conductor: options.conductor, + isPrimaryRelease: options.isPrimaryRelease, + conflictedFiles, + dryRun: options.dryRun, + }); +} + +// Only call `main` if this script was run directly. +if (require.main === module) { + void main(); +} diff --git a/pr-checks/util.ts b/pr-checks/util.ts new file mode 100644 index 0000000000..353b2a9654 --- /dev/null +++ b/pr-checks/util.ts @@ -0,0 +1,9 @@ +/** + * Returns an appropriate message for the error. + * + * If the error is an `Error` instance, this returns the error message without + * an `Error: ` prefix. + */ +export function getErrorMessage(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} diff --git a/pr-checks/versions.test.ts b/pr-checks/versions.test.ts new file mode 100755 index 0000000000..6697710f83 --- /dev/null +++ b/pr-checks/versions.test.ts @@ -0,0 +1,44 @@ +#!/usr/bin/env npx tsx + +/** + * Tests for `versions.ts`. + */ + +import * as assert from "node:assert/strict"; +import { describe, it } from "node:test"; + +import { getCurrentVersion, replaceVersionInPackageJson } from "./versions"; + +describe("getCurrentVersion", async () => { + await it("reads versions", async () => { + const result = getCurrentVersion(`{ "version": "1.23.4" }`); + assert.deepEqual(result, "1.23.4"); + }); +}); + +const packageJsonContents = `{ + "name": "codeql", + "version": "1.23.4" +} +`; + +const packageJsonContentsExpected = `{ + "name": "codeql", + "version": "2.23.4" +} +`; + +describe("replaceVersionInPackageJson", async () => { + await it("replaces versions", async () => { + const result = replaceVersionInPackageJson( + "1.23.4", + "2.23.4", + packageJsonContents, + ); + assert.deepEqual( + result.split("\n"), + packageJsonContentsExpected.split("\n"), + ); + assert.deepEqual(JSON.parse(result), { name: "codeql", version: "2.23.4" }); + }); +}); diff --git a/pr-checks/versions.ts b/pr-checks/versions.ts new file mode 100644 index 0000000000..4abc7faa17 --- /dev/null +++ b/pr-checks/versions.ts @@ -0,0 +1,54 @@ +import * as fs from "node:fs"; + +import { DryRunOption, PACKAGE_JSON } from "./config"; + +export function withPackageJson( + transformer: (content: string) => { value: T; content?: string }, + options: DryRunOption, +): T { + const content = fs.readFileSync(PACKAGE_JSON, "utf8"); + const result = transformer(content); + + if (result.content !== undefined) { + if (!options.dryRun) { + fs.writeFileSync(PACKAGE_JSON, result.content, "utf8"); + } else { + console.info(`[DRY RUN] Would have written an updated package.json`); + } + } + + return result.value; +} + +/** Reads the current version from `package.json`. */ +export function getCurrentVersion(content: string): string | undefined { + const pkg: { version: string } = JSON.parse(content); + return pkg.version; +} + +/** + * Replaces the version in `package.json` textually. Only updates the version + * field that immediately follows the `"name": "codeql"` line. + * `npm version` doesn't always work because of merge conflicts, so we + * replace the version in package.json textually. + */ +export function replaceVersionInPackageJson( + prevVersion: string, + newVersion: string, + content: string, +): string { + const lines = content.split("\n"); + let prevLineIsCodeql = false; + const output: string[] = []; + + for (const line of lines) { + if (prevLineIsCodeql && line.includes(`"version": "${prevVersion}"`)) { + output.push(line.replace(prevVersion, newVersion)); + } else { + output.push(line); + } + prevLineIsCodeql = line.includes('"name": "codeql",'); + } + + return output.join("\n"); +} diff --git a/src/action-common.test.ts b/src/action-common.test.ts new file mode 100644 index 0000000000..fc2e0a9aaa --- /dev/null +++ b/src/action-common.test.ts @@ -0,0 +1,123 @@ +import * as core from "@actions/core"; +import test from "ava"; +import sinon from "sinon"; + +import * as common from "./action-common"; +import * as actionsUtil from "./actions-util"; +import * as environment from "./environment"; +import * as logging from "./logging"; +import { ActionName } from "./status-report"; +import * as statusReport from "./status-report"; +import { + getTestActionsEnv, + getTestEnv, + makeMacro, + RecordingLogger, + setupTests, +} from "./testing-utils"; +import { getErrorMessage } from "./util"; + +setupTests(test); + +interface RunInActionsTestOpts { + runFn?: () => Promise; + expectedErrorMessage?: string; + expectedTelemetryError?: string; +} + +const runInActionsMacro = makeMacro({ + exec: async (t, opts: RunInActionsTestOpts) => { + const expectFailure = opts?.expectedErrorMessage !== undefined; + + const logger = new RecordingLogger(); + const getActionsLogger = sinon + .stub(logging, "getActionsLogger") + .returns(logger); + + const env = getTestEnv(); + const getEnv = sinon.stub(environment, "getEnv").returns(env); + + const actionsEnv = getTestActionsEnv(env); + const getActionsEnv = sinon + .stub(actionsUtil, "getActionsEnv") + .returns(actionsEnv); + + const getJobUUID = sinon + .stub(statusReport, "getJobUUID") + .returns("test-job-uuid"); + + const setFailed = sinon.stub(core, "setFailed"); + const sendUnhandledErrorStatusReport = sinon.stub( + statusReport, + "sendUnhandledErrorStatusReport", + ); + + const name = ActionName.Init; + const run = sinon.stub(); + + if (opts?.runFn) { + run.callsFake(opts.runFn); + } + + const transformTelemetryError = sinon + .stub() + .callsFake((err) => opts?.expectedTelemetryError ?? getErrorMessage(err)); + const testAction: common.Action = { + name, + run, + transformTelemetryError, + }; + + await common.runInActions(testAction); + + // These always should have been called once. + t.true(getActionsLogger.calledOnce); + t.true(getEnv.calledOnce); + t.true(getActionsEnv.calledOnce); + + const expectedActionState = { + actions: actionsEnv, + env, + logger, + name: ActionName.Init, + }; + + t.true(getJobUUID.calledOnceWithExactly(sinon.match(expectedActionState))); + t.true(run.calledOnceWithExactly(sinon.match(expectedActionState))); + + t.is(setFailed.calledOnce, expectFailure ?? false); + t.is(sendUnhandledErrorStatusReport.calledOnce, expectFailure ?? false); + + if (expectFailure) { + t.true( + setFailed.calledOnceWithExactly( + `${statusReport.getDisplayActionName(name)} action failed: ${opts?.expectedErrorMessage}`, + ), + ); + t.true( + sendUnhandledErrorStatusReport.calledOnceWithExactly( + name, + sinon.match.any, + opts?.expectedTelemetryError ?? opts?.expectedErrorMessage, + logger, + ), + ); + } + }, + title: (providedTitle) => `runInActions - ${providedTitle}`, +}); + +runInActionsMacro.serial("calls run", {}); +runInActionsMacro.serial("handles run exceptions", { + runFn: () => { + throw new Error("Test failure"); + }, + expectedErrorMessage: "Test failure", +}); +runInActionsMacro.serial("transforms run exceptions", { + runFn: () => { + throw new Error("Test failure"); + }, + expectedErrorMessage: "Test failure", + expectedTelemetryError: "Transformed failure message", +}); diff --git a/src/action-common.ts b/src/action-common.ts new file mode 100644 index 0000000000..95323e7f2a --- /dev/null +++ b/src/action-common.ts @@ -0,0 +1,126 @@ +import * as core from "@actions/core"; + +import { ActionsEnv, getActionsEnv } from "./actions-util"; +import type { ApiClient } from "./api-client"; +import { Env, ReadOnlyEnv } from "./environment"; +import type { FeatureEnablement } from "./feature-flags"; +import { getActionsLogger, Logger } from "./logging"; +import { + ActionName, + getDisplayActionName, + getJobUUID, + sendUnhandledErrorStatusReport, +} from "./status-report"; +import { getEnv, getErrorMessage, wrapError } from "./util"; + +/** Base state that is available to an Action on startup. */ +export interface BaseState { + /** The name of the Action. */ + name: ActionName; + /** When the Action was started. */ + startedAt: Date; +} + +/** Describes different state features that an Action may have. */ +export interface FeatureState { + Base: BaseState; + Logger: { + /** The logger that is in use. */ + logger: Logger; + }; + Env: { + /** Information about environment variables. */ + env: Env; + }; + ReadOnlyEnv: { + env: ReadOnlyEnv; + }; + Actions: { + /** Access to Actions-related functionality. */ + actions: ActionsEnv; + }; + Api: { + /** A GitHub API client. */ + apiClient: ApiClient; + }; + FeatureFlags: { + /** Information about enabled feature flags. */ + features: FeatureEnablement; + }; +} + +/** Identifies a type of state an Action may have. */ +export type StateFeature = keyof FeatureState; + +/** Constructs the intersection of all state types identifies by `Fs`. */ +export type FieldsOf = Fs extends [] + ? Record + : Fs extends [ + infer Head extends StateFeature, + ...infer Tail extends readonly StateFeature[], + ] + ? FeatureState[Head] & FieldsOf + : never; + +/** Describes the state of an Action that has access to the state corresponding to `Fs`. */ +export type ActionState = FieldsOf; + +/** The type of an Action's main entry point. This is a function that is provided + * with a basic `ActionState` object with features that are always available. + * Each Action can then augment the `state` further if additional features are required. + */ +export type ActionMain = ( + state: ActionState<["Base", "Logger", "Env", "Actions"]>, +) => Promise; + +/** A specification for a CodeQL Action step. */ +export interface Action { + /** The name of the Action. */ + name: ActionName; + /** The entry point for the Action. */ + run: ActionMain; + /** + * An optional function that transforms a caught error into a message suitable for + * inclusion in a status report. This is primarily intended for the `start-proxy` + * action to replace the thrown `Error`'s message with a safe one. + */ + transformTelemetryError?: (error: Error) => string; +} + +/** A generic entry point that sets up the basic environment for the `action` and runs it. */ +export async function runInActions(action: Action) { + const startedAt = new Date(); + const logger = getActionsLogger(); + const env = getEnv(); + const actionsEnv = getActionsEnv(); + + try { + const actionState = { + name: action.name, + startedAt, + logger, + env, + actions: actionsEnv, + }; + + // Create a unique identifier for this run. + getJobUUID(actionState); + + await action.run(actionState); + } catch (error) { + core.setFailed( + `${getDisplayActionName(action.name)} action failed: ${getErrorMessage(error)}`, + ); + + const statusReportError = + action.transformTelemetryError !== undefined + ? action.transformTelemetryError(wrapError(error)) + : error; + await sendUnhandledErrorStatusReport( + action.name, + startedAt, + statusReportError, + logger, + ); + } +} diff --git a/src/actions-util.ts b/src/actions-util.ts index dea22d5c57..dd5124620d 100644 --- a/src/actions-util.ts +++ b/src/actions-util.ts @@ -7,12 +7,13 @@ import * as github from "@actions/github"; import * as io from "@actions/io"; import type { Config } from "./config-utils"; +import { Env, EnvVar, ActionsEnvVars } from "./environment"; import { Logger } from "./logging"; import { doesDirectoryExist, getCodeQLDatabasePath, - getRequiredEnvParam, ConfigurationError, + getEnv, } from "./util"; /** @@ -26,14 +27,20 @@ declare const __CODEQL_ACTION_VERSION__: string; * global functions in tests. */ export interface ActionsEnv { + getRequiredInput: (name: string) => string; getOptionalInput: (name: string) => string | undefined; + exportVariable: (name: string, value: string) => void; } /** * Gets the real `ActionsEnv` used by production code. */ export function getActionsEnv(): ActionsEnv { - return { getOptionalInput }; + return { + getRequiredInput, + getOptionalInput, + exportVariable: core.exportVariable, + }; } /** @@ -61,17 +68,21 @@ export const getOptionalInput = function (name: string): string | undefined { return value.length > 0 ? value : undefined; }; -export function getTemporaryDirectory(): string { - const value = process.env["CODEQL_ACTION_TEMP"]; - return value !== undefined && value !== "" - ? value - : getRequiredEnvParam("RUNNER_TEMP"); +/** + * Gets the temporary directory used by the CodeQL Action. This will either be the temporary + * directory that has been set in `CODEQL_ACTION_TEMP` by e.g. a previous step, or the + * value of `RUNNER_TEMP` otherwise. + */ +export function getTemporaryDirectory(env: Env = getEnv()): string { + return ( + env.getOptional(EnvVar.TEMP) ?? env.getRequired(ActionsEnvVars.RUNNER_TEMP) + ); } const PR_DIFF_RANGE_JSON_FILENAME = "pr-diff-range.json"; -export function getDiffRangesJsonFilePath(): string { - return path.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); +export function getDiffRangesJsonFilePath(env: Env = getEnv()): string { + return path.join(getTemporaryDirectory(env), PR_DIFF_RANGE_JSON_FILENAME); } export function getActionVersion(): string { @@ -83,16 +94,16 @@ export function getActionVersion(): string { * * This will be "dynamic" for default setup workflow runs. */ -export function getWorkflowEventName() { - return getRequiredEnvParam("GITHUB_EVENT_NAME"); +export function getWorkflowEventName(env: Env = getEnv()) { + return env.getRequired(ActionsEnvVars.GITHUB_EVENT_NAME); } /** * Returns whether the current workflow is executing a local copy of the Action, e.g. we're running * a workflow on the codeql-action repo itself. */ -export function isRunningLocalAction(): boolean { - const relativeScriptPath = getRelativeScriptPath(); +export function isRunningLocalAction(env: Env = getEnv()): boolean { + const relativeScriptPath = getRelativeScriptPath(env); return ( relativeScriptPath.startsWith("..") || path.isAbsolute(relativeScriptPath) ); @@ -103,15 +114,15 @@ export function isRunningLocalAction(): boolean { * * This can be used to get the Action's name or tell if we're running a local Action. */ -function getRelativeScriptPath(): string { - const runnerTemp = getRequiredEnvParam("RUNNER_TEMP"); +function getRelativeScriptPath(env: Env): string { + const runnerTemp = env.getRequired(ActionsEnvVars.RUNNER_TEMP); const actionsDirectory = path.join(path.dirname(runnerTemp), "_actions"); return path.relative(actionsDirectory, __filename); } /** Returns the contents of `GITHUB_EVENT_PATH` as a JSON object. */ -export function getWorkflowEvent(): any { - const eventJsonFile = getRequiredEnvParam("GITHUB_EVENT_PATH"); +export function getWorkflowEvent(env: Env = getEnv()): any { + const eventJsonFile = env.getRequired(ActionsEnvVars.GITHUB_EVENT_PATH); try { return JSON.parse(fs.readFileSync(eventJsonFile, "utf-8")); } catch (e) { @@ -180,17 +191,17 @@ export function getUploadValue(input: string | undefined): UploadKind { /** * Get the workflow run ID. */ -export function getWorkflowRunID(): number { - const workflowRunIdString = getRequiredEnvParam("GITHUB_RUN_ID"); +export function getWorkflowRunID(env: Env = getEnv()): number { + const workflowRunIdString = env.getRequired(ActionsEnvVars.GITHUB_RUN_ID); const workflowRunID = parseInt(workflowRunIdString, 10); if (Number.isNaN(workflowRunID)) { throw new Error( - `GITHUB_RUN_ID must define a non NaN workflow run ID. Current value is ${workflowRunIdString}`, + `${ActionsEnvVars.GITHUB_RUN_ID} must define a non NaN workflow run ID. Current value is ${workflowRunIdString}`, ); } if (workflowRunID < 0) { throw new Error( - `GITHUB_RUN_ID must be a non-negative integer. Current value is ${workflowRunIdString}`, + `${ActionsEnvVars.GITHUB_RUN_ID} must be a non-negative integer. Current value is ${workflowRunIdString}`, ); } return workflowRunID; @@ -199,17 +210,19 @@ export function getWorkflowRunID(): number { /** * Get the workflow run attempt number. */ -export function getWorkflowRunAttempt(): number { - const workflowRunAttemptString = getRequiredEnvParam("GITHUB_RUN_ATTEMPT"); +export function getWorkflowRunAttempt(env: Env = getEnv()): number { + const workflowRunAttemptString = env.getRequired( + ActionsEnvVars.GITHUB_RUN_ATTEMPT, + ); const workflowRunAttempt = parseInt(workflowRunAttemptString, 10); if (Number.isNaN(workflowRunAttempt)) { throw new Error( - `GITHUB_RUN_ATTEMPT must define a non NaN workflow run attempt. Current value is ${workflowRunAttemptString}`, + `${ActionsEnvVars.GITHUB_RUN_ATTEMPT} must define a non NaN workflow run attempt. Current value is ${workflowRunAttemptString}`, ); } if (workflowRunAttempt <= 0) { throw new Error( - `GITHUB_RUN_ATTEMPT must be a positive integer. Current value is ${workflowRunAttemptString}`, + `${ActionsEnvVars.GITHUB_RUN_ATTEMPT} must be a positive integer. Current value is ${workflowRunAttemptString}`, ); } return workflowRunAttempt; @@ -266,18 +279,18 @@ export const getFileType = async (filePath: string): Promise => { } }; -export function isSelfHostedRunner() { - return process.env.RUNNER_ENVIRONMENT === "self-hosted"; +export function isSelfHostedRunner(env: Env = getEnv()) { + return env.getOptional(ActionsEnvVars.RUNNER_ENVIRONMENT) === "self-hosted"; } /** Determines whether the workflow trigger is `dynamic`. */ -export function isDynamicWorkflow(): boolean { - return getWorkflowEventName() === "dynamic"; +export function isDynamicWorkflow(env: Env = getEnv()): boolean { + return getWorkflowEventName(env) === "dynamic"; } /** Determines whether we are running in default setup. */ -export function isDefaultSetup(): boolean { - return isDynamicWorkflow(); +export function isDefaultSetup(env: Env = getEnv()): boolean { + return isDynamicWorkflow(env); } export function prettyPrintInvocation(cmd: string, args: string[]): string { @@ -375,9 +388,10 @@ const persistedInputsKey = "persisted_inputs"; * This would be simplified if actions/runner#3514 is addressed. * https://github.com/actions/runner/issues/3514 */ -export const persistInputs = function () { - const inputEnvironmentVariables = Object.entries(process.env).filter( - ([name]) => name.startsWith("INPUT_"), +export const persistInputs = function (env: Env = getEnv()) { + const entries = env.entries(); + const inputEnvironmentVariables = entries.filter(([name]) => + name.startsWith("INPUT_"), ); core.saveState(persistedInputsKey, JSON.stringify(inputEnvironmentVariables)); }; @@ -405,7 +419,9 @@ export interface PullRequestBranches { * @returns the base and head branches of the pull request, or undefined if * we are not analyzing a pull request. */ -export function getPullRequestBranches(): PullRequestBranches | undefined { +export function getPullRequestBranches( + env: Env = getEnv(), +): PullRequestBranches | undefined { const pullRequest = github.context.payload.pull_request; if (pullRequest) { return { @@ -419,8 +435,10 @@ export function getPullRequestBranches(): PullRequestBranches | undefined { // PR analysis under Default Setup does not have the pull_request context, // but it should set CODE_SCANNING_REF and CODE_SCANNING_BASE_BRANCH. - const codeScanningRef = process.env.CODE_SCANNING_REF; - const codeScanningBaseBranch = process.env.CODE_SCANNING_BASE_BRANCH; + const codeScanningRef = env.getOptional(EnvVar.CODE_SCANNING_REF); + const codeScanningBaseBranch = env.getOptional( + EnvVar.CODE_SCANNING_BASE_BRANCH, + ); if (codeScanningRef && codeScanningBaseBranch) { return { base: codeScanningBaseBranch, @@ -435,8 +453,8 @@ export function getPullRequestBranches(): PullRequestBranches | undefined { /** * Returns whether we are analyzing a pull request. */ -export function isAnalyzingPullRequest(): boolean { - return getPullRequestBranches() !== undefined; +export function isAnalyzingPullRequest(env: Env = getEnv()): boolean { + return getPullRequestBranches(env) !== undefined; } /** @@ -460,13 +478,14 @@ const qualityCategoryMapping: Record = { export function fixCodeQualityCategory( logger: Logger, category?: string, + env: Env = getEnv(), ): string | undefined { // The `category` should always be set by Default Setup. We perform this check // to avoid potential issues if Code Quality supports Advanced Setup in the future // and before this workaround is removed. if ( category !== undefined && - isDefaultSetup() && + isDefaultSetup(env) && category.startsWith("/language:") ) { const language = category.substring("/language:".length); diff --git a/src/analyze-action-post.ts b/src/analyze-action-post.ts index fe8fbea61c..732b52af19 100644 --- a/src/analyze-action-post.ts +++ b/src/analyze-action-post.ts @@ -38,7 +38,7 @@ export async function runWrapper() { logger, ); if (config !== undefined) { - const codeql = await getCodeQL(config.codeQLCmd); + const codeql = await getCodeQL(logger, config.codeQLCmd); const version = await codeql.getVersion(); await debugArtifacts.uploadCombinedSarifArtifacts( logger, diff --git a/src/analyze-action.ts b/src/analyze-action.ts index cc2777bc5f..c3c2e40e7f 100644 --- a/src/analyze-action.ts +++ b/src/analyze-action.ts @@ -4,6 +4,7 @@ import { performance } from "perf_hooks"; import * as core from "@actions/core"; +import { Action, ActionState, runInActions } from "./action-common"; import * as actionsUtil from "./actions-util"; import * as analyses from "./analyses"; import { @@ -40,7 +41,6 @@ import { createStatusReportBase, DatabaseCreationTimings, getActionsStatus, - sendUnhandledErrorStatusReport, StatusReportBase, } from "./status-report"; import { @@ -212,7 +212,7 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) { await runAutobuild(config, BuiltInLanguage.go, logger); } -async function run(startedAt: Date) { +async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) { // To capture errors appropriately, keep as much code within the try-catch as // possible, and only use safe functions outside. @@ -228,7 +228,6 @@ async function run(startedAt: Date) { let didUploadTrapCaches = false; let dependencyCacheResults: DependencyCacheUploadStatusReport | undefined; let databaseUploadResults: DatabaseUploadResult[] = []; - const logger = getActionsLogger(); try { util.initializeEnvironment(actionsUtil.getActionVersion()); @@ -256,7 +255,7 @@ async function run(startedAt: Date) { ); } - const codeql = await getCodeQL(config.codeQLCmd); + const codeql = await getCodeQL(logger, config.codeQLCmd); if (hasBadExpectErrorInput()) { throw new util.ConfigurationError( @@ -523,19 +522,13 @@ async function run(startedAt: Date) { } } +/** Defines the `analyze` Action. */ +const analyze: Action = { + name: ActionName.Analyze, + run, +}; + export async function runWrapper() { - const startedAt = new Date(); - const logger = getActionsLogger(); - try { - await run(startedAt); - } catch (error) { - core.setFailed(`analyze action failed: ${util.getErrorMessage(error)}`); - await sendUnhandledErrorStatusReport( - ActionName.Analyze, - startedAt, - error, - logger, - ); - } + await runInActions(analyze); await util.checkForTimeout(); } diff --git a/src/analyze.ts b/src/analyze.ts index 6a668f19ad..411477b597 100644 --- a/src/analyze.ts +++ b/src/analyze.ts @@ -285,7 +285,7 @@ extensions: // characters are escaped, and that the path is always rendered as a // quoted string on a single line. return ( - ` - [${yaml.dump(filename, { quoteStyle: "single" }).trim()}, ` + + ` - [${yaml.dump(filename, { forceQuotes: true, quoteStyle: "single" }).trim()}, ` + `${range.startLine}, ${range.endLine}]\n` ); }) diff --git a/src/api-client.test.ts b/src/api-client.test.ts index 29cad338f7..ae8c6269b1 100644 --- a/src/api-client.test.ts +++ b/src/api-client.test.ts @@ -2,11 +2,13 @@ import * as github from "@actions/github"; import * as githubUtils from "@actions/github/lib/utils"; import test from "ava"; import * as sinon from "sinon"; +import { ProxyAgent } from "undici"; import * as actionsUtil from "./actions-util"; import * as api from "./api-client"; import { DO_NOT_RETRY_STATUSES } from "./api-client"; -import { setupTests } from "./testing-utils"; +import { ActionsEnvVars, RegistryProxyVars } from "./environment"; +import { callee, getTestEnv, setupTests } from "./testing-utils"; import * as util from "./util"; setupTests(test); @@ -20,23 +22,23 @@ test.serial("getApiClient", async (t) => { const githubStub: sinon.SinonStub = sinon.stub(); pluginStub.returns(githubStub); + const env = getTestEnv(); + env.set(ActionsEnvVars.GITHUB_SERVER_URL, "http://github.localhost"); + env.set(ActionsEnvVars.GITHUB_API_URL, "http://api.github.localhost"); + sinon.stub(actionsUtil, "getRequiredInput").withArgs("token").returns("xyz"); - const requiredEnvParamStub = sinon.stub(util, "getRequiredEnvParam"); - requiredEnvParamStub - .withArgs("GITHUB_SERVER_URL") - .returns("http://github.localhost"); - requiredEnvParamStub - .withArgs("GITHUB_API_URL") - .returns("http://api.github.localhost"); - api.getApiClient(); + const apiClient = api.getApiClient(env); + t.truthy(apiClient); + t.true(githubStub.calledOnce); t.assert( githubStub.calledOnceWithExactly({ auth: "token xyz", baseUrl: "http://api.github.localhost", log: sinon.match.any, userAgent: `CodeQL-Action/${actionsUtil.getActionVersion()}`, + request: sinon.match.any, retry: { doNotRetry: DO_NOT_RETRY_STATUSES, }, @@ -206,3 +208,67 @@ test.serial( } }, ); + +test("getRegistryProxy - returns undefined if the proxy is not configured", async (t) => { + const target = callee(api.getRegistryProxy).withArgs(); + + // Empty environment. + await target.passes(t.is, undefined); + // Only the host. + await target + .withEnv(getTestEnv({ [RegistryProxyVars.PROXY_HOST]: "localhost" })) + .passes(t.is, undefined); + // Only the port. + await target + .withEnv(getTestEnv({ [RegistryProxyVars.PROXY_PORT]: "1234" })) + .passes(t.is, undefined); +}); + +test("getRegistryProxy - returns value when both vars are set", async (t) => { + await callee(api.getRegistryProxy) + .withArgs() + .withEnv( + getTestEnv({ + [RegistryProxyVars.PROXY_HOST]: "localhost", + [RegistryProxyVars.PROXY_PORT]: "1234", + }), + ) + .passes(t.truthy); +}); + +test("getRegistryProxyConfig - gets the configuration from the env vars", async (t) => { + const host = "localhost"; + const port = "1234"; + const ca = "cert"; + + await callee(api.getRegistryProxyConfig) + .withArgs() + .withEnv( + getTestEnv({ + [RegistryProxyVars.PROXY_HOST]: host, + [RegistryProxyVars.PROXY_PORT]: port, + [RegistryProxyVars.PROXY_CA_CERTIFICATE]: ca, + }), + ) + .passes(t.like, { host, port, ca }); +}); + +test("makeProxyRequestOptions - returns defaults without custom proxy", async (t) => { + t.deepEqual( + api.makeProxyRequestOptions(undefined), + githubUtils.defaults.request, + ); +}); + +test("makeProxyRequestOptions - returns fetch with custom proxy", async (t) => { + const opts = api.makeProxyRequestOptions( + new ProxyAgent("http://localhost:1080"), + ); + // Fetch should be different from the defaults. + t.notDeepEqual(opts?.fetch, githubUtils.defaults.request?.fetch); + // The options should be the same aside from that. + t.deepEqual( + { ...opts, fetch: githubUtils.defaults.request?.fetch }, + githubUtils.defaults.request, + ); +}); diff --git a/src/api-client.ts b/src/api-client.ts index 333280f8e7..ba800a2587 100644 --- a/src/api-client.ts +++ b/src/api-client.ts @@ -1,9 +1,26 @@ import * as core from "@actions/core"; import * as githubUtils from "@actions/github/lib/utils"; +import { type Octokit } from "@octokit/core"; +import { type PaginateInterface } from "@octokit/plugin-paginate-rest"; +import { type Api } from "@octokit/plugin-rest-endpoint-methods"; import * as retry from "@octokit/plugin-retry"; +import { RequestRequestOptions } from "@octokit/types"; +import { + ProxyAgent, + RequestInfo, + RequestInit, + fetch as undiciFetch, +} from "undici"; +import type { ActionState } from "./action-common"; import { getActionVersion, getRequiredInput } from "./actions-util"; -import { EnvVar } from "./environment"; +import { + ActionsEnvVars, + EnvVar, + ReadOnlyEnv, + RegistryProxyVars, + getEnv, +} from "./environment"; import { Logger } from "./logging"; import { getRepositoryNwo, RepositoryNwo } from "./repository"; import { @@ -43,13 +60,90 @@ export interface GitHubApiExternalRepoDetails { apiURL: string | undefined; } +/** + * Gets the configuration for the private registry authentication proxy, + * if it is available in the environment. + * + * @param action The required Action state. + * @returns The hostname, port, and CA retrieved from the corresponding environment variables. + */ +export function getRegistryProxyConfig(action: ActionState<["ReadOnlyEnv"]>) { + return { + host: action.env.getOptional(RegistryProxyVars.PROXY_HOST), + port: action.env.getOptional(RegistryProxyVars.PROXY_PORT), + ca: action.env.getOptional(RegistryProxyVars.PROXY_CA_CERTIFICATE), + }; +} + +/** + * Gets the configuration for the private registry authentication proxy, + * and uses it to initialise a corresponding `ProxyAgent`. + * + * @param action The required Action state. + * @returns A `ProxyAgent` corresponding to the private registry proxy, + * or `undefined` if we couldn't retrieve the host and port. + */ +export function getRegistryProxy( + action: ActionState<["Logger", "ReadOnlyEnv"]>, +): ProxyAgent | undefined { + const { host, port, ca } = getRegistryProxyConfig(action); + + if (host && port) { + const uri = `http://${host}:${port}`; + action.logger.debug( + `Using private registry proxy at '${uri}' for API client.`, + ); + return new ProxyAgent({ + uri, + keepAliveTimeout: 10, + keepAliveMaxTimeout: 10, + requestTls: ca ? { ca } : undefined, + }); + } + + return undefined; +} + +/** + * Constructs a `RequestRequestOptions` with a custom `fetch` implementation + * that uses `dispatcher` as a proxy for requests. + * + * @param dispatcher The proxy to use, if any. + */ +export function makeProxyRequestOptions( + dispatcher: ProxyAgent | undefined, +): RequestRequestOptions | undefined { + // If we don't have a custom `ProxyAgent`, return the defaults. + if (dispatcher === undefined) { + return githubUtils.defaults.request; + } + + // Otherwise, construct the custom `fetch` and add it onto the defaults. + return { + ...githubUtils.defaults.request, + fetch: (req: RequestInfo, init?: RequestInit) => { + return undiciFetch(req, { ...init, dispatcher }); + }, + }; +} + +/** The type of GitHub API client we use. */ +export type ApiClient = Octokit & Api & { paginate: PaginateInterface }; + +/** Options for `createApiClientWithDetails`. */ +interface CreateApiClientOptions { + allowExternal?: boolean; + proxy?: ProxyAgent; +} + function createApiClientWithDetails( apiDetails: GitHubApiCombinedDetails, - { allowExternal = false } = {}, -) { + { allowExternal = false, proxy = undefined }: CreateApiClientOptions = {}, +): ApiClient { const auth = (allowExternal && apiDetails.externalRepoAuth) || apiDetails.auth; const retryingOctokit = githubUtils.GitHub.plugin(retry.retry); + const requestOptions = makeProxyRequestOptions(proxy); return new retryingOctokit( githubUtils.getOctokitOptions(auth, { baseUrl: apiDetails.apiURL, @@ -60,6 +154,7 @@ function createApiClientWithDetails( warn: core.warning, error: core.error, }, + request: requestOptions, retry: { doNotRetry: DO_NOT_RETRY_STATUSES, }, @@ -67,22 +162,23 @@ function createApiClientWithDetails( ); } -export function getApiDetails(): GitHubApiDetails { +export function getApiDetails(env: ReadOnlyEnv = getEnv()): GitHubApiDetails { return { auth: getRequiredInput("token"), - url: getRequiredEnvParam("GITHUB_SERVER_URL"), - apiURL: getRequiredEnvParam("GITHUB_API_URL"), + url: env.getRequired(ActionsEnvVars.GITHUB_SERVER_URL), + apiURL: env.getRequired(ActionsEnvVars.GITHUB_API_URL), }; } -export function getApiClient() { - return createApiClientWithDetails(getApiDetails()); +export function getApiClient(env: ReadOnlyEnv = getEnv()) { + return createApiClientWithDetails(getApiDetails(env)); } export function getApiClientWithExternalAuth( apiDetails: GitHubApiCombinedDetails, + proxy?: ProxyAgent, ) { - return createApiClientWithDetails(apiDetails, { allowExternal: true }); + return createApiClientWithDetails(apiDetails, { allowExternal: true, proxy }); } /** diff --git a/src/api-compatibility.json b/src/api-compatibility.json index 435f8f1d6b..7569440194 100644 --- a/src/api-compatibility.json +++ b/src/api-compatibility.json @@ -1 +1 @@ -{"maximumVersion": "3.22", "minimumVersion": "3.16"} +{"maximumVersion": "3.22", "minimumVersion": "3.17"} diff --git a/src/autobuild-action.ts b/src/autobuild-action.ts index dc20211379..9fa8016578 100644 --- a/src/autobuild-action.ts +++ b/src/autobuild-action.ts @@ -1,5 +1,6 @@ import * as core from "@actions/core"; +import { Action, ActionState, runInActions } from "./action-common"; import { getActionVersion, getOptionalInput, @@ -11,13 +12,12 @@ import { getCodeQL } from "./codeql"; import { Config, getConfig } from "./config-utils"; import { EnvVar } from "./environment"; import { Language } from "./languages"; -import { Logger, getActionsLogger } from "./logging"; +import { Logger } from "./logging"; import { StatusReportBase, getActionsStatus, createStatusReportBase, sendStatusReport, - sendUnhandledErrorStatusReport, ActionName, } from "./status-report"; import { endTracingForCluster } from "./tracer-config"; @@ -26,7 +26,6 @@ import { checkDiskUsage, checkGitHubVersionInRange, ConfigurationError, - getErrorMessage, initializeEnvironment, wrapError, } from "./util"; @@ -69,11 +68,10 @@ async function sendCompletedStatusReport( } } -async function run(startedAt: Date) { +async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) { // To capture errors appropriately, keep as much code within the try-catch as // possible, and only use safe functions outside. - const logger = getActionsLogger(); let config: Config | undefined; let currentLanguage: Language | undefined; let languages: Language[] | undefined; @@ -101,7 +99,7 @@ async function run(startedAt: Date) { ); } - const codeql = await getCodeQL(config.codeQLCmd); + const codeql = await getCodeQL(logger, config.codeQLCmd); languages = await determineAutobuildLanguages(codeql, config, logger); if (languages !== undefined) { @@ -142,18 +140,12 @@ async function run(startedAt: Date) { await sendCompletedStatusReport(config, logger, startedAt, languages ?? []); } +/** Defines the `autobuild` Action. */ +const autobuild: Action = { + name: ActionName.Autobuild, + run, +}; + export async function runWrapper() { - const startedAt = new Date(); - const logger = getActionsLogger(); - try { - await run(startedAt); - } catch (error) { - core.setFailed(`autobuild action failed. ${getErrorMessage(error)}`); - await sendUnhandledErrorStatusReport( - ActionName.Autobuild, - startedAt, - error, - logger, - ); - } + await runInActions(autobuild); } diff --git a/src/autobuild.ts b/src/autobuild.ts index fc4983f4ef..49b790102d 100644 --- a/src/autobuild.ts +++ b/src/autobuild.ts @@ -5,7 +5,7 @@ import { getGitHubVersion } from "./api-client"; import { CodeQL, getCodeQL } from "./codeql"; import * as configUtils from "./config-utils"; import { DocUrl } from "./doc-url"; -import { EnvVar } from "./environment"; +import { ActionsEnvVars, EnvVar } from "./environment"; import { Feature, featureConfig, initFeatures } from "./feature-flags"; import { BuiltInLanguage, Language } from "./languages"; import { Logger } from "./logging"; @@ -126,7 +126,7 @@ export async function setupCppAutobuild(codeql: CodeQL, logger: Logger) { if (await features.getValue(Feature.CppDependencyInstallation, codeql)) { // disable autoinstall on self-hosted runners unless explicitly requested if ( - process.env["RUNNER_ENVIRONMENT"] === "self-hosted" && + process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] === "self-hosted" && process.env[envVar] !== "true" ) { logger.info( @@ -155,7 +155,7 @@ export async function runAutobuild( logger: Logger, ) { logger.startGroup(`Attempting to automatically build ${language} code`); - const codeQL = await getCodeQL(config.codeQLCmd); + const codeQL = await getCodeQL(logger, config.codeQLCmd); if (language === BuiltInLanguage.cpp) { await setupCppAutobuild(codeQL, logger); } diff --git a/src/codeql.test.ts b/src/codeql.test.ts index dea4cf04af..e8208888e7 100644 --- a/src/codeql.test.ts +++ b/src/codeql.test.ts @@ -156,6 +156,7 @@ test.serial( t.assert(toolcache.find("CodeQL", `0.0.0-${version}`)); t.is(result.toolsVersion, `0.0.0-${version}`); t.is(result.toolsSource, ToolsSource.Download); + assertDownloadDurationInteger(t, result.toolsDownloadStatusReport); } t.is(toolcache.findAllVersions("CodeQL").length, 2); @@ -191,9 +192,7 @@ test.serial( t.assert(toolcache.find("CodeQL", `2.15.0`)); t.is(result.toolsVersion, `2.15.0`); t.is(result.toolsSource, ToolsSource.Download); - if (result.toolsDownloadStatusReport) { - assertDurationsInteger(t, result.toolsDownloadStatusReport); - } + assertDownloadDurationInteger(t, result.toolsDownloadStatusReport); }); }, ); @@ -230,9 +229,7 @@ test.serial( t.assert(toolcache.find("CodeQL", "0.0.0-20200610")); t.deepEqual(result.toolsVersion, "0.0.0-20200610"); t.is(result.toolsSource, ToolsSource.Download); - if (result.toolsDownloadStatusReport) { - assertDurationsInteger(t, result.toolsDownloadStatusReport); - } + assertDownloadDurationInteger(t, result.toolsDownloadStatusReport); }); }, ); @@ -282,11 +279,7 @@ for (const { t.assert(toolcache.find("CodeQL", expectedToolcacheVersion)); t.deepEqual(result.toolsVersion, expectedToolcacheVersion); t.is(result.toolsSource, ToolsSource.Download); - t.assert( - Number.isInteger( - result.toolsDownloadStatusReport?.downloadDurationMs, - ), - ); + assertDownloadDurationInteger(t, result.toolsDownloadStatusReport); }); }, ); @@ -330,9 +323,7 @@ for (const toolcacheVersion of [ SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion, ); t.is(result.toolsSource, ToolsSource.Toolcache); - t.is(result.toolsDownloadStatusReport?.combinedDurationMs, undefined); - t.is(result.toolsDownloadStatusReport?.downloadDurationMs, undefined); - t.is(result.toolsDownloadStatusReport?.extractionDurationMs, undefined); + t.is(result.toolsDownloadStatusReport, undefined); }); }, ); @@ -373,9 +364,7 @@ test.serial( ); t.deepEqual(result.toolsVersion, "0.0.0-20200601"); t.is(result.toolsSource, ToolsSource.Toolcache); - t.is(result.toolsDownloadStatusReport?.combinedDurationMs, undefined); - t.is(result.toolsDownloadStatusReport?.downloadDurationMs, undefined); - t.is(result.toolsDownloadStatusReport?.extractionDurationMs, undefined); + t.is(result.toolsDownloadStatusReport, undefined); const cachedVersions = toolcache.findAllVersions("CodeQL"); t.is(cachedVersions.length, 1); @@ -421,9 +410,7 @@ test.serial( ); t.deepEqual(result.toolsVersion, defaults.cliVersion); t.is(result.toolsSource, ToolsSource.Download); - if (result.toolsDownloadStatusReport) { - assertDurationsInteger(t, result.toolsDownloadStatusReport); - } + t.truthy(result.toolsDownloadStatusReport); const cachedVersions = toolcache.findAllVersions("CodeQL"); t.is(cachedVersions.length, 2); @@ -462,9 +449,7 @@ test.serial( ); t.deepEqual(result.toolsVersion, defaults.cliVersion); t.is(result.toolsSource, ToolsSource.Download); - if (result.toolsDownloadStatusReport) { - assertDurationsInteger(t, result.toolsDownloadStatusReport); - } + t.truthy(result.toolsDownloadStatusReport); const cachedVersions = toolcache.findAllVersions("CodeQL"); t.is(cachedVersions.length, 2); @@ -506,9 +491,7 @@ test.serial( t.is(result.toolsVersion, "0.0.0-20230203"); t.is(result.toolsSource, ToolsSource.Download); - if (result.toolsDownloadStatusReport) { - assertDurationsInteger(t, result.toolsDownloadStatusReport); - } + assertDownloadDurationInteger(t, result.toolsDownloadStatusReport); const cachedVersions = toolcache.findAllVersions("CodeQL"); t.is(cachedVersions.length, 1); @@ -519,15 +502,11 @@ test.serial( }, ); -function assertDurationsInteger( +function assertDownloadDurationInteger( t: ExecutionContext, - statusReport: ToolsDownloadStatusReport, + statusReport: ToolsDownloadStatusReport | undefined, ) { - t.assert(Number.isInteger(statusReport?.combinedDurationMs)); - if (statusReport.downloadDurationMs !== undefined) { - t.assert(Number.isInteger(statusReport?.downloadDurationMs)); - t.assert(Number.isInteger(statusReport?.extractionDurationMs)); - } + t.assert(Number.isInteger(statusReport?.downloadDurationMs)); } test.serial("getExtraOptions works for explicit paths", (t) => { @@ -601,7 +580,6 @@ const injectedConfigMacro = makeMacro({ "", undefined, undefined, - getRunnerLogger(true), ); const args = runnerConstructorStub.firstCall.args[1] as string[]; @@ -877,7 +855,6 @@ test.serial( "", undefined, "/path/to/qlconfig.yml", - getRunnerLogger(true), ); const args = runnerConstructorStub.firstCall.args[1] as string[]; @@ -908,7 +885,6 @@ test.serial( "", undefined, undefined, // undefined qlconfigFile - getRunnerLogger(true), ); const args = runnerConstructorStub.firstCall.args[1] as any[]; @@ -1087,7 +1063,6 @@ test.serial( "sourceRoot", undefined, undefined, - getRunnerLogger(false), ); t.true(runnerConstructorStub.calledOnce); diff --git a/src/codeql.ts b/src/codeql.ts index 5d78337d2e..9b064620eb 100644 --- a/src/codeql.ts +++ b/src/codeql.ts @@ -23,11 +23,10 @@ import { } from "./feature-flags"; import { isAnalyzingDefaultBranch } from "./git-utils"; import { Language } from "./languages"; -import { Logger } from "./logging"; +import { getRunnerLogger, Logger } from "./logging"; import { writeBaseDatabaseOidsFile, writeOverlayChangesFile } from "./overlay"; import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; import * as setupCodeql from "./setup-codeql"; -import { ZstdAvailability } from "./tar"; import { ToolsDownloadStatusReport } from "./tools-download"; import { ToolsFeature, isSupportedToolsFeature } from "./tools-features"; import { shouldEnableIndirectTracing } from "./tracer-config"; @@ -92,7 +91,6 @@ export interface CodeQL { sourceRoot: string, processName: string | undefined, qlconfigFile: string | undefined, - logger: Logger, ): Promise; /** * Runs the autobuilder for the given language. @@ -272,17 +270,17 @@ const CODEQL_MINIMUM_VERSION = "2.19.4"; /** * This version will shortly become the oldest version of CodeQL that the Action will run with. */ -const CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; +const CODEQL_NEXT_MINIMUM_VERSION = "2.20.7"; /** * This is the version of GHES that was most recently deprecated. */ -const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; +const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.16"; /** * This is the deprecation date for the version of GHES that was most recently deprecated. */ -const GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; +const GHES_MOST_RECENT_DEPRECATION_DATE = "2026-07-01"; /** The CLI verbosity level to use for extraction in debug mode. */ const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; @@ -319,7 +317,6 @@ export async function setupCodeQL( toolsDownloadStatusReport?: ToolsDownloadStatusReport; toolsSource: setupCodeql.ToolsSource; toolsVersion: string; - zstdAvailability: ZstdAvailability; }> { try { const { @@ -327,7 +324,6 @@ export async function setupCodeQL( toolsDownloadStatusReport, toolsSource, toolsVersion, - zstdAvailability, } = await setupCodeql.setupCodeQLBundle( toolsInput, apiDetails, @@ -340,12 +336,6 @@ export async function setupCodeQL( logger, ); - logger.debug( - `Bundle download status report: ${JSON.stringify( - toolsDownloadStatusReport, - )}`, - ); - let codeqlCmd = path.join(codeqlFolder, "codeql", "codeql"); if (process.platform === "win32") { codeqlCmd += ".exe"; @@ -355,13 +345,12 @@ export async function setupCodeQL( ); } - cachedCodeQL = await getCodeQLForCmd(codeqlCmd, checkVersion); + cachedCodeQL = await getCodeQLForCmd(logger, codeqlCmd, checkVersion); return { codeql: cachedCodeQL, toolsDownloadStatusReport, toolsSource, toolsVersion, - zstdAvailability, }; } catch (rawError) { const e = api.wrapApiConfigurationError(rawError); @@ -382,9 +371,9 @@ export async function setupCodeQL( /** * Use the CodeQL executable located at the given path. */ -export async function getCodeQL(cmd: string): Promise { +export async function getCodeQL(logger: Logger, cmd: string): Promise { if (cachedCodeQL === undefined) { - cachedCodeQL = await getCodeQLForCmd(cmd, true); + cachedCodeQL = await getCodeQLForCmd(logger, cmd, true); } return cachedCodeQL; } @@ -491,8 +480,9 @@ export function createStubCodeQL(partialCodeql: Partial): CodeQL { */ export async function getCodeQLForTesting( cmd = "codeql-for-testing", + logger: Logger = getRunnerLogger(true), ): Promise { - return getCodeQLForCmd(cmd, false); + return getCodeQLForCmd(logger, cmd, false); } /** @@ -504,6 +494,7 @@ export async function getCodeQLForTesting( * @returns A new CodeQL object */ async function getCodeQLForCmd( + logger: Logger, cmd: string, checkVersion: boolean, ): Promise { @@ -514,16 +505,13 @@ async function getCodeQLForCmd( async getVersion() { let result = util.getCachedCodeQlVersion(cmd); if (result === undefined) { - const output = await runCli(cmd, ["version", "--format=json"], { - noStreamStdout: true, - }); - try { - result = JSON.parse(output) as VersionInfo; - } catch { - throw Error( - `Invalid JSON output from \`version --format=json\`: ${output}`, - ); - } + result = await runCliJson( + cmd, + ["version", "--format=json"], + { + noStreamStdout: true, + }, + ); util.cacheCodeQlVersion(cmd, result); } return result; @@ -552,7 +540,6 @@ async function getCodeQLForCmd( sourceRoot: string, processName: string | undefined, qlconfigFile: string | undefined, - logger: Logger, ) { const extraArgs = config.languages.map( (language) => `--language=${language}`, @@ -731,26 +718,20 @@ async function getCodeQLForCmd( filterToLanguagesWithQueries: boolean; } = { filterToLanguagesWithQueries: false }, ) { - const codeqlArgs = [ + return runCliJson(cmd, [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + // TODO: Unconditionally include `--filter-to-languages-with-queries` + // once CODEQL_MINIMUM_VERSION is at least v2.23.0 + // — the first version to support this flag. ...(filterToLanguagesWithQueries ? ["--filter-to-languages-with-queries"] : []), ...getExtraOptionsFromEnv(["resolve", "languages"]), - ]; - const output = await runCli(cmd, codeqlArgs); - - try { - return JSON.parse(output) as ResolveLanguagesOutput; - } catch (e) { - throw new Error( - `Unexpected output from codeql resolve languages with --format=betterjson: ${e}`, - ); - } + ]); }, async resolveBuildEnvironment( workingDir: string | undefined, @@ -766,15 +747,7 @@ async function getCodeQLForCmd( if (workingDir !== undefined) { codeqlArgs.push("--working-dir", workingDir); } - const output = await runCli(cmd, codeqlArgs); - - try { - return JSON.parse(output) as ResolveBuildEnvironmentOutput; - } catch (e) { - throw new Error( - `Unexpected output from codeql resolve build-environment: ${e} in\n${output}`, - ); - } + return await runCliJson(cmd, codeqlArgs); }, async databaseRunQueries( databasePath: string, @@ -976,15 +949,9 @@ async function getCodeQLForCmd( ...getExtraOptionsFromEnv(["resolve", "queries"]), ...queries, ]; - const output = await runCli(cmd, codeqlArgs, { noStreamStdout: true }); - - try { - return JSON.parse(output) as string[]; - } catch (e) { - throw new Error( - `Unexpected output from codeql resolve queries --format=startingpacks: ${e}`, - ); - } + return await runCliJson(cmd, codeqlArgs, { + noStreamStdout: true, + }); }, async resolveDatabase( databasePath: string, @@ -996,15 +963,9 @@ async function getCodeQLForCmd( "--format=json", ...getExtraOptionsFromEnv(["resolve", "database"]), ]; - const output = await runCli(cmd, codeqlArgs, { noStreamStdout: true }); - - try { - return JSON.parse(output) as ResolveDatabaseOutput; - } catch (e) { - throw new Error( - `Unexpected output from codeql resolve database --format=json: ${e}`, - ); - } + return await runCliJson(cmd, codeqlArgs, { + noStreamStdout: true, + }); }, async mergeResults( sarifFiles: string[], @@ -1160,6 +1121,30 @@ async function runCli( } } +/** + * Wraps the command executor {@link runCli} and tries to parse the output as JSON. + * @param cmd The command to run. + * @param args The arguments to pass to the command. + * @param opts The options for running the command. + * @param opts.stdin Optional string to pass to the command's standard input. + * @param opts.noStreamStdout Optional boolean to indicate whether to stream the command's standard output. + * @returns The parsed JSON output from the command. + */ +async function runCliJson( + cmd: string, + args: string[] = [], + opts: { stdin?: string; noStreamStdout?: boolean } = {}, +): Promise { + const output = await runCli(cmd, args, opts); + try { + return JSON.parse(output) as T; + } catch (e) { + throw Error( + `Unexpected output from codeql ${args.join(" ")}: ${getErrorMessage(e)}`, + ); + } +} + /** * Writes the code scanning configuration that is to be used by the CLI. * diff --git a/src/config-utils.test.ts b/src/config-utils.test.ts index 27de780ad5..aec214cd64 100644 --- a/src/config-utils.test.ts +++ b/src/config-utils.test.ts @@ -6,12 +6,14 @@ import test, { ExecutionContext } from "ava"; import * as yaml from "js-yaml"; import * as sinon from "sinon"; +import { ActionState } from "./action-common"; import * as actionsUtil from "./actions-util"; import { AnalysisKind, supportedAnalysisKinds } from "./analyses"; import * as api from "./api-client"; import { CachingKind } from "./caching-utils"; import { createStubCodeQL } from "./codeql"; import { UserConfig } from "./config/db-config"; +import * as file from "./config/file"; import * as configUtils from "./config-utils"; import * as errorMessages from "./error-messages"; import { Feature } from "./feature-flags"; @@ -36,6 +38,10 @@ import { mockCodeQLVersion, createTestConfig, makeMacro, + initAllState, + callee, + SAMPLE_DOTCOM_API_DETAILS, + AssertableTarget, } from "./testing-utils"; import { GitHubVariant, @@ -160,8 +166,9 @@ test.serial("load empty config", async (t) => { }, }); + const state = initAllState({ logger }); const config = await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ languagesInput: languages, repository: { owner: "github", repo: "example" }, @@ -202,8 +209,9 @@ test.serial("load code quality config", async (t) => { }, }); + const state = initAllState({ logger }); const config = await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ analysisKinds: [AnalysisKind.CodeQuality], languagesInput: languages, @@ -280,9 +288,10 @@ test.serial( repositoryProperties, }); + const state = initAllState({ logger }); await t.notThrowsAsync(async () => { const config = await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ analysisKinds: [AnalysisKind.CodeQuality], languagesInput: languages, @@ -321,8 +330,9 @@ test.serial("loading a saved config produces the same config", async (t) => { // Sanity check that getConfig returns undefined before we have called initConfig t.deepEqual(await configUtils.getConfig(tempDir, logger), undefined); + const state = initAllState({ logger }); const config1 = await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ languagesInput: "javascript,python", tempDir, @@ -373,8 +383,9 @@ test.serial("loading config with version mismatch throws", async (t) => { .stub(actionsUtil, "getActionVersion") .returns("does-not-exist"); + const state = initAllState({ logger }); const config = await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ languagesInput: "javascript,python", tempDir, @@ -402,8 +413,9 @@ test.serial("loading config with version mismatch throws", async (t) => { test.serial("load input outside of workspace", async (t) => { return await withTmpDir(async (tempDir) => { try { + const state = initAllState(); await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ configFile: "../input", tempDir, @@ -424,34 +436,6 @@ test.serial("load input outside of workspace", async (t) => { }); }); -test.serial("load non-local input with invalid repo syntax", async (t) => { - return await withTmpDir(async (tempDir) => { - // no filename given, just a repo - const configFile = "octo-org/codeql-config@main"; - - try { - await configUtils.initConfig( - createFeatures([]), - createTestInitConfigInputs({ - configFile, - tempDir, - workspacePath: tempDir, - }), - ); - throw new Error("initConfig did not throw error"); - } catch (err) { - t.deepEqual( - err, - new ConfigurationError( - errorMessages.getConfigFileRepoFormatInvalidMessage( - "octo-org/codeql-config@main", - ), - ), - ); - } - }); -}); - test.serial("load non-existent input", async (t) => { return await withTmpDir(async (tempDir) => { const languagesInput = "javascript"; @@ -459,8 +443,9 @@ test.serial("load non-existent input", async (t) => { t.false(fs.existsSync(path.join(tempDir, configFile))); try { + const state = initAllState(); await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ languagesInput, configFile, @@ -482,6 +467,24 @@ test.serial("load non-existent input", async (t) => { }); }); +/** A non-empty, but fairly minimal configuration file. */ +const simpleConfigFileContents = ` + name: my config + queries: + - uses: ./foo_file`; + +/** A less minimal configuration file. */ +const otherConfigFileContents = ` + name: my config + disable-default-queries: true + queries: + - uses: ./foo + paths-ignore: + - a + - b + paths: + - c/d`; + test.serial("load non-empty input", async (t) => { return await withTmpDir(async (tempDir) => { setupActionsVars(tempDir, tempDir); @@ -496,18 +499,6 @@ test.serial("load non-empty input", async (t) => { }, }); - // Just create a generic config object with non-default values for all fields - const inputFileContents = ` - name: my config - disable-default-queries: true - queries: - - uses: ./foo - paths-ignore: - - a - - b - paths: - - c/d`; - fs.mkdirSync(path.join(tempDir, "foo")); const userConfig: UserConfig = { @@ -534,10 +525,11 @@ test.serial("load non-empty input", async (t) => { }); const languagesInput = "javascript"; - const configFilePath = createConfigFile(inputFileContents, tempDir); + const configFilePath = createConfigFile(otherConfigFileContents, tempDir); + const state = initAllState(); const actualConfig = await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ languagesInput, buildModeInput: "none", @@ -559,14 +551,12 @@ test.serial( "Using config input and file together, config input should be used.", async (t) => { return await withTmpDir(async (tempDir) => { - process.env["RUNNER_TEMP"] = tempDir; - process.env["GITHUB_WORKSPACE"] = tempDir; + setupActionsVars(tempDir, tempDir); - const inputFileContents = ` - name: my config - queries: - - uses: ./foo_file`; - const configFilePath = createConfigFile(inputFileContents, tempDir); + const configFilePath = createConfigFile( + simpleConfigFileContents, + tempDir, + ); const configInput = ` name: my config @@ -595,8 +585,9 @@ test.serial( // Only JS, python packs will be ignored const languagesInput = "javascript"; + const state = initAllState({ env: util.getEnv() }); const config = await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ languagesInput, configFile: configFilePath, @@ -647,8 +638,9 @@ test.serial("API client used when reading remote config", async (t) => { const configFile = "octo-org/codeql-config/config.yaml@main"; const languagesInput = "javascript"; + const state = initAllState(); await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ languagesInput, configFile, @@ -669,9 +661,10 @@ test.serial( mockGetContents(dummyResponse); const repoReference = "octo-org/codeql-config/config.yaml@main"; + const state = initAllState(); try { await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ configFile: repoReference, tempDir, @@ -699,9 +692,10 @@ test.serial("Invalid format of remote config handled correctly", async (t) => { mockGetContents(dummyResponse); const repoReference = "octo-org/codeql-config/config.yaml@main"; + const state = initAllState(); try { await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ configFile: repoReference, tempDir, @@ -729,9 +723,10 @@ test.serial("No detected languages", async (t) => { }, }); + const state = initAllState(); try { await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ tempDir, codeql, @@ -752,9 +747,10 @@ test.serial("Unknown languages", async (t) => { return await withTmpDir(async (tempDir) => { const languagesInput = "rubbish,english"; + const state = initAllState(); try { await configUtils.initConfig( - createFeatures([]), + state, createTestInitConfigInputs({ languagesInput, tempDir, @@ -1299,13 +1295,12 @@ checkOverlayEnablementMacro.serial( ); checkOverlayEnablementMacro.serial( - "No overlay-base database on default branch if runner disk space is below v2 limit and v2 resource checks enabled", + "No overlay-base database on default branch if runner disk space is below minimum", { languages: [BuiltInLanguage.javascript], features: [ Feature.OverlayAnalysis, Feature.OverlayAnalysisCodeScanningJavascript, - Feature.OverlayAnalysisResourceChecksV2, ], isDefaultBranch: true, diskUsage: { @@ -1319,13 +1314,12 @@ checkOverlayEnablementMacro.serial( ); checkOverlayEnablementMacro.serial( - "Overlay-base database on default branch if runner disk space is between v2 and v1 limits and v2 resource checks enabled", + "Overlay-base database on default branch if runner disk space is above minimum", { languages: [BuiltInLanguage.javascript], features: [ Feature.OverlayAnalysis, Feature.OverlayAnalysisCodeScanningJavascript, - Feature.OverlayAnalysisResourceChecksV2, ], isDefaultBranch: true, diskUsage: { @@ -1339,25 +1333,6 @@ checkOverlayEnablementMacro.serial( }, ); -checkOverlayEnablementMacro.serial( - "No overlay-base database on default branch if runner disk space is between v2 and v1 limits and v2 resource checks not enabled", - { - languages: [BuiltInLanguage.javascript], - features: [ - Feature.OverlayAnalysis, - Feature.OverlayAnalysisCodeScanningJavascript, - ], - isDefaultBranch: true, - diskUsage: { - numAvailableBytes: 15_000_000_000, - numTotalBytes: 100_000_000_000, - }, - }, - { - disabledReason: OverlayDisabledReason.InsufficientDiskSpace, - }, -); - checkOverlayEnablementMacro.serial( "No overlay-base database on default branch if memory flag is too low", { @@ -2272,3 +2247,440 @@ test("applyIncrementalAnalysisSettings: adds exclusions for diff-informed-only r { exclude: { tags: "exclude-from-incremental" } }, ]); }); + +test("determineUserConfig - empty config when neither input is specified", async (t) => { + await withTmpDir(async (tmpDir) => { + const target = callee(configUtils.determineUserConfig) + .withDefaultActionsEnv() + .withFeatures([]) + .withArgs( + tmpDir, + createTestInitConfigInputs({ + configInput: undefined, + configFile: undefined, + workspacePath: tmpDir, + }), + ); + + // The returned configuration should be empty. + await target + // The fact that no configuration was provided should have been logged, + .logs(t, "No configuration file was provided") + // But not the messages for the two input sources + // or the warning about both inputs. + .notLogs( + t, + "Using config from action input:", + "Using configuration file:", + "Both a config file and config input were provided. Ignoring config file.", + ) + .passes(t.deepEqual, {}); + }); +}); + +test("determineUserConfig - loads config file", async (t) => { + await withTmpDir(async (tmpDir) => { + const configFilePath = createConfigFile(simpleConfigFileContents, tmpDir); + + const inputs = createTestInitConfigInputs({ + configInput: undefined, + configFile: configFilePath, + workspacePath: tmpDir, + }); + const target = callee(configUtils.determineUserConfig) + .withDefaultActionsEnv() + .withArgs(tmpDir, inputs); + + await target + // The path of the input config file should have been logged, + .logs(t, `Using configuration file: ${configFilePath}`) + .notLogs( + t, + // The other two origin messages and the warning about both inputs should + // not have been logged. + "No configuration file was provided", + "Using config from action input:", + "Both a config file and config input were provided. Ignoring config file.", + ) + // The loaded configuration should match `simpleConfigFileContents`. + .passes(t.deepEqual, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); + + // The `configFile` input should not have changed. + t.is(inputs.configFile, configFilePath); + }); +}); + +test("determineUserConfig - loads config input", async (t) => { + await withTmpDir(async (tmpDir) => { + const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); + + const inputs = createTestInitConfigInputs({ + configInput: simpleConfigFileContents, + configFile: undefined, + workspacePath: tmpDir, + }); + const target = callee(configUtils.determineUserConfig) + .withDefaultActionsEnv() + .withArgs(tmpDir, inputs); + + await target + // The input source and path of the generated config file should have been logged. + .logs( + t, + "Using config from action input:", + `Using configuration file: ${expectedConfigPath}`, + ) + // The message about no configuration input and + // the warning about both inputs should not have been logged. + .notLogs( + t, + "No configuration file was provided", + "Both a config file and config input were provided. Ignoring config file.", + ) + // The loaded configuration should match `simpleConfigFileContents`. + .passes(t.deepEqual, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); + + // The `configFile` input should have been mutated to the generated path. + t.is(inputs.configFile, expectedConfigPath); + }); +}); + +test("determineUserConfig - ignores config file input when both specified", async (t) => { + await withTmpDir(async (tmpDir) => { + const configFilePath = createConfigFile(otherConfigFileContents, tmpDir); + const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); + + const inputs = createTestInitConfigInputs({ + configInput: simpleConfigFileContents, + configFile: configFilePath, + workspacePath: tmpDir, + }); + const target = callee(configUtils.determineUserConfig) + .withDefaultActionsEnv() + .withArgs(tmpDir, inputs); + + await target + // The path of the generated config file and + // the warning about both inputs should have been logged. + .logs( + t, + `Using config from action input: ${expectedConfigPath}`, + `Using configuration file: ${expectedConfigPath}`, + "Both a config file and config input were provided. Ignoring config file.", + ) + .notLogs(t, "No configuration file was provided") + // The loaded configuration should match `simpleConfigFileContents`. + .passes(t.deepEqual, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); + + // The `configFile` input should have been mutated to the generated path. + t.is(inputs.configFile, expectedConfigPath); + }); +}); + +/** A `config` input that we might get from Default Setup. */ +const defaultSetupConfigInput = ` + threat-models: [local, remote] + default-setup: + org: + model-packs: [foo, bar]`; + +test("determineUserConfig - merges configs if FF is enabled in Default Setup", async (t) => { + await withTmpDir(async (tmpDir) => { + const configFilePath = createConfigFile(simpleConfigFileContents, tmpDir); + const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); + + const inputs = createTestInitConfigInputs({ + configInput: defaultSetupConfigInput, + configFile: configFilePath, + workspacePath: tmpDir, + }); + const target = callee(configUtils.determineUserConfig) + .withDefaultActionsEnv({ GITHUB_EVENT_NAME: "dynamic" }) + .withFeatures([Feature.AllowMergeConfigFiles]) + .withArgs(tmpDir, inputs); + + // The loaded configuration should match the result of merging + // `defaultSetupConfigInput` and `simpleConfigFileContents`. + const expectedConfig = { + name: "my config", + queries: [{ uses: "./foo_file" }], + "threat-models": ["local", "remote"], + "default-setup": { + org: { + "model-packs": ["foo", "bar"], + }, + }, + } satisfies UserConfig; + + await target + .logs( + t, + `Using merged configurations from 'config' input with configuration from '${configFilePath}': ${expectedConfigPath}`, + ) + .notLogs( + t, + `Using configuration file: ${expectedConfigPath}`, + "No configuration file was provided", + `Using config from action input: ${expectedConfigPath}`, + "Both a config file and config input were provided. Ignoring config file.", + ) + .passes(t.deepEqual, expectedConfig); + + // The `configFile` input should have been mutated to the generated path. + t.is(inputs.configFile, expectedConfigPath); + + // Since `result` is the result of merging the configurations in-memory, + // also check whether loading the configuration from disk that was written + // by `determineUserConfig` matches our expectations. + const loadedFromDisk = configUtils.getLocalConfig( + getRunnerLogger(true), + expectedConfigPath, + false, + ); + t.deepEqual(loadedFromDisk, expectedConfig); + }); +}); + +test("determineUserConfig - ignores config file input in Default Setup if FF is off", async (t) => { + await withTmpDir(async (tmpDir) => { + const configFilePath = createConfigFile(otherConfigFileContents, tmpDir); + const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); + + const target = callee(configUtils.determineUserConfig) + .withDefaultActionsEnv({ GITHUB_EVENT_NAME: "dynamic" }) + .withArgs( + tmpDir, + createTestInitConfigInputs({ + configInput: simpleConfigFileContents, + configFile: configFilePath, + workspacePath: tmpDir, + }), + ); + + await target + .logs( + t, + `Using config from action input: ${expectedConfigPath}`, + `Using configuration file: ${expectedConfigPath}`, + "Both a config file and config input were provided. Ignoring config file.", + ) + .notLogs(t, "No configuration file was provided") + .passes(t.deepEqual, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); + }); +}); + +test("determineUserConfig - ignores config file input outside Default Setup if FF is on", async (t) => { + await withTmpDir(async (tmpDir) => { + const configFilePath = createConfigFile(otherConfigFileContents, tmpDir); + const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); + + const target = callee(configUtils.determineUserConfig) + .withDefaultActionsEnv() + .withFeatures([Feature.AllowMergeConfigFiles]) + .withArgs( + tmpDir, + createTestInitConfigInputs({ + configInput: simpleConfigFileContents, + configFile: configFilePath, + workspacePath: tmpDir, + }), + ); + + await target + .logs( + t, + `Using config from action input: ${expectedConfigPath}`, + `Using configuration file: ${expectedConfigPath}`, + "Both a config file and config input were provided. Ignoring config file.", + ) + .notLogs(t, "No configuration file was provided") + .passes(t.deepEqual, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); + }); +}); + +test("loadUserConfig - loads local configuration files", async (t) => { + await withTmpDir(async (workspaceDir) => { + await withTmpDir(async (tmpDir) => { + // Construct the test target. + const loadUserConfig = ( + actionState: ActionState<["Logger", "Env", "FeatureFlags"]>, + filePath: string, + ) => + configUtils.loadUserConfig( + actionState, + filePath, + workspaceDir, + SAMPLE_DOTCOM_API_DETAILS, + tmpDir, + ); + const target = callee(loadUserConfig); + + // `loadUserConfig` should load local configuration files if they are inside the workspace: + const insideOfWorkspace = path.join(workspaceDir, "some-file.yml"); + fs.writeFileSync(insideOfWorkspace, "test-key: present", "utf8"); + + await target + .withArgs(insideOfWorkspace) + .passes(t.deepEqual, { "test-key": "present" }); + + // `loadUserConfig` should normally throw if the path is outside of the workspace: + const outsideOfWorkspace = path.join( + tmpDir, + "not-the-generated-file.yml", + ); + fs.writeFileSync(outsideOfWorkspace, "test-key: present", "utf8"); + + await target + .withArgs(outsideOfWorkspace) + .throws(t, { instanceOf: ConfigurationError }); + + // `loadUserConfig` does not throw if the path is the result of `userConfigFromActionPath`: + const generatedPath = configUtils.userConfigFromActionPath(tmpDir); + fs.writeFileSync(generatedPath, "test-key: present", "utf8"); + + await target + .withArgs(generatedPath) + .passes(t.deepEqual, { "test-key": "present" }); + }); + }); +}); + +test.serial("loadUserConfig - loads remote configuration files", async (t) => { + await withTmpDir(async (tmpDir) => { + const getRemoteConfig = sinon.stub(file, "getRemoteConfig").resolves({}); + + const remoteAddress = "owner/repo/file@ref"; + await callee(configUtils.loadUserConfig) + .withArgs(remoteAddress, tmpDir, SAMPLE_DOTCOM_API_DETAILS, tmpDir) + .passes(t.deepEqual, {}); + + t.true( + getRemoteConfig.calledOnceWithExactly( + sinon.match.any, + remoteAddress, + SAMPLE_DOTCOM_API_DETAILS, + ), + ); + }); +}); + +test.serial( + "loadUserConfig - loads remote configuration files (new format, partial)", + async (t) => { + await withTmpDir(async (tmpDir) => { + const getRemoteConfig = sinon.stub(file, "getRemoteConfig").resolves({}); + + // Construct the basic test target. + const target = callee(configUtils.loadUserConfig).withDefaultActionsEnv(); + + // Utility function to assert that `targetWithArgs` has identified + // the input as a remote file address. + const checkIsRemote = + (address: string) => + async (targetWithArgs: AssertableTarget) => { + // We have stubbed `getRemoteConfig` to resolve to `{}`, so we + // expect that result. + await targetWithArgs.passes(t.deepEqual, {}); + + // And `getRemoteConfig` should have been called exactly once. + t.is(getRemoteConfig.callCount, 1); + + // Get the arguments for the call and check that there were three. + // We don't care about the first, but check that the other two + // match our expectations. We break it down like this to get + // more useful test output. + const args = getRemoteConfig.getCalls()[0].args; + t.is(args.length, 3); + t.deepEqual(args[1], address); + t.deepEqual(args[2], SAMPLE_DOTCOM_API_DETAILS); + }; + + // Utility function to assert that `targetWithArgs` has not identified + // the input as a remote file address. + const checkIsNotRemote = async ( + targetWithArgs: AssertableTarget, + ) => { + // We expect `loadUserConfig` to have thrown if it thinks the path is local, + // since the inputs we provide aren't for files that exist. + await targetWithArgs.throws(t); + + // Additionally, we expect that `getRemoteConfig` wasn't called. + t.is(getRemoteConfig.callCount, 0); + }; + + // Utility function to add the explicit `REMOTE_PATH_PREFIX` to the input. + const withExplicitPrefix = (str: string) => + `${file.REMOTE_PATH_PREFIX}${str}`; + + // Utility to set up a call to `loadUserConfig` with the provided `address` + // and pass it to `assertion`. + const testTargetWith = async ( + address: string, + assertion: ( + targetWithArgs: AssertableTarget>, + ) => Promise, + ) => { + // Reset the stub's history since we re-use it. + getRemoteConfig.resetHistory(); + + // Log the input we are testing so that, in the event of a failure, + // it is easier to see which input was responsible. + t.log(`testTargetWith("${address}")`); + + // Prepare the test call to `loadUserConfig`. + const targetWithArgs = target.withArgs( + address, + tmpDir, + SAMPLE_DOTCOM_API_DETAILS, + tmpDir, + ); + + // Pass it to the provided assertion function. + await assertion(targetWithArgs); + }; + + // Since this input contains an '@' character, it is treated as a remote path + // by the old logic even without the explicit prefix. + const remoteWithoutPrefix = "repo@main"; + await testTargetWith( + remoteWithoutPrefix, + checkIsRemote(remoteWithoutPrefix), + ); + await testTargetWith( + withExplicitPrefix(remoteWithoutPrefix), + checkIsRemote(remoteWithoutPrefix), + ); + // It is only treated as a local path with the corresponding prefix. + await testTargetWith(`./${remoteWithoutPrefix}`, checkIsNotRemote); + + // The following test inputs are examples of ambiguous paths. They could refer to + // valid local or remote paths. For each, we check that they are treated as remote + // paths if the explicit remote file prefix is used and as local paths otherwise. + const testInputs = ["repo:file", "input", "../input"]; + + for (const testInput of testInputs) { + for (const addPrefix of [true, false]) { + await testTargetWith( + addPrefix ? withExplicitPrefix(testInput) : testInput, + addPrefix ? checkIsRemote(testInput) : checkIsNotRemote, + ); + } + } + }); + }, +); diff --git a/src/config-utils.ts b/src/config-utils.ts index 972734877a..6d1efaa1ba 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -5,10 +5,12 @@ import { performance } from "perf_hooks"; import * as core from "@actions/core"; import * as yaml from "js-yaml"; +import { ActionState } from "./action-common"; import { getActionVersion, getOptionalInput, isAnalyzingPullRequest, + isDefaultSetup, isDynamicWorkflow, } from "./actions-util"; import { @@ -18,15 +20,27 @@ import { getAnalysisConfig, } from "./analyses"; import * as api from "./api-client"; -import { CachingKind, getCachingKind } from "./caching-utils"; +import { getCachingKind } from "./caching-utils"; import { type CodeQL } from "./codeql"; +import { type Config } from "./config/action-config"; import { calculateAugmentation, ExcludeQueryFilter, generateCodeScanningConfig, + mergeDefaultSetupAndUserConfigs, parseUserConfig, UserConfig, } from "./config/db-config"; +import { + getRemoteConfig, + LOCAL_PATH_PREFIX, + REMOTE_PATH_PREFIX, +} from "./config/file"; +import { + parseRegistries, + type RegistryConfigNoCredentials, + type RegistryConfigWithCredentials, +} from "./config/pack-registries"; import { addNoLanguageDiagnostic, makeTelemetryDiagnostic, @@ -79,6 +93,8 @@ import { isHostedRunner, } from "./util"; +export { type Config } from "./config/action-config"; + /** * The minimum available disk space (in MB) required to perform overlay analysis. * If the available disk space on the runner is below the threshold when deciding @@ -86,19 +102,10 @@ import { * analysis unless overlay analysis has been explicitly enabled via environment * variable. */ -const OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_MB = 20000; +const OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_MB = 14000; const OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_BYTES = OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_MB * 1_000_000; -/** - * The v2 minimum available disk space (in MB) required to perform overlay - * analysis. This is a lower threshold than the v1 limit, allowing overlay - * analysis to run on runners with less available disk space. - */ -const OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_V2_MB = 14000; -const OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_V2_BYTES = - OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_V2_MB * 1_000_000; - /** * The minimum memory (in MB) that must be available for CodeQL to perform overlay analysis. If * CodeQL will be given less memory than this threshold, then the action will not perform overlay @@ -117,148 +124,6 @@ const OVERLAY_MINIMUM_MEMORY_MB = 5 * 1024; */ const CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE = "2.24.3"; -export type RegistryConfigWithCredentials = RegistryConfigNoCredentials & { - // Token to use when downloading packs from this registry. - token: string; -}; - -/** - * The list of registries and the associated pack globs that determine where each - * pack can be downloaded from. - */ -export interface RegistryConfigNoCredentials { - // URL of a package registry, eg- https://ghcr.io/v2/ - url: string; - - // List of globs that determine which packs are associated with this registry. - packages: string[] | string; - - // Kind of registry, either "github" or "docker". Default is "docker". - // "docker" refers specifically to the GitHub Container Registry, which is the usual way of sharing CodeQL packs. - // "github" refers to packs published as content in a GitHub repository. This kind of registry is used in scenarios - // where GHCR is not available, such as certain GHES environments. - kind?: "github" | "docker"; -} - -/** - * Format of the parsed config file. - */ -export interface Config { - /** - * The version of the CodeQL Action that the configuration is for. - */ - version: string; - /** - * Set of analysis kinds that are enabled. - */ - analysisKinds: AnalysisKind[]; - /** - * Set of languages to run analysis for. - */ - languages: Language[]; - /** - * Build mode, if set. Currently only a single build mode is supported per job. - */ - buildMode: BuildMode | undefined; - /** - * A unaltered copy of the original user input. - * Mainly intended to be used for status reporting. - * If any field is useful for the actual processing - * of the action then consider pulling it out to a - * top-level field above. - */ - originalUserInput: UserConfig; - /** - * Directory to use for temporary files that should be - * deleted at the end of the job. - */ - tempDir: string; - /** - * Path of the CodeQL executable. - */ - codeQLCmd: string; - /** - * Version of GitHub we are talking to. - */ - gitHubVersion: GitHubVersion; - /** - * The location where CodeQL databases should be stored. - */ - dbLocation: string; - /** - * Specifies whether we are debugging mode and should try to produce extra - * output for debugging purposes when possible. - */ - debugMode: boolean; - /** - * Specifies the name of the debugging artifact if we are in debug mode. - */ - debugArtifactName: string; - /** - * Specifies the name of the database in the debugging artifact. - */ - debugDatabaseName: string; - /** - * The configuration we computed by combining `originalUserInput` with `augmentationProperties`, - * as well as adjustments made to it based on unsupported or required options. - */ - computedConfig: UserConfig; - - /** - * Partial map from languages to locations of TRAP caches for that language. - * If a key is omitted, then TRAP caching should not be used for that language. - */ - trapCaches: { [language: Language]: string }; - - /** - * Time taken to download TRAP caches. Used for status reporting. - */ - trapCacheDownloadTime: number; - - /** A value indicating how dependency caching should be used. */ - dependencyCachingEnabled: CachingKind; - - /** The keys of caches that we restored, if any. */ - dependencyCachingRestoredKeys: string[]; - - /** - * Extra query exclusions to append to the config. - */ - extraQueryExclusions: ExcludeQueryFilter[]; - - /** - * The overlay database mode to use. - */ - overlayDatabaseMode: OverlayDatabaseMode; - - /** - * Whether to use caching for overlay databases. If it is true, the action - * will upload the created overlay-base database to the actions cache, and - * download an overlay-base database from the actions cache before it creates - * a new overlay database. If it is false, the action assumes that the - * workflow will be responsible for managing database storage and retrieval. - * - * This property has no effect unless `overlayDatabaseMode` is `Overlay` or - * `OverlayBase`. - */ - useOverlayDatabaseCaching: boolean; - - /** - * Whether the overlay database mode was set explicitly. - */ - overlayModeSetExplicitly: boolean; - - /** - * A partial mapping from repository properties that affect us to their values. - */ - repositoryProperties: RepositoryProperties; - - /** - * Whether to enable file coverage information. - */ - enableFileCoverageInformation: boolean; -} - async function getSupportedLanguageMap( codeql: CodeQL, logger: Logger, @@ -598,13 +463,22 @@ async function downloadCacheWithTime( return { trapCaches, trapCacheDownloadTime }; } -async function loadUserConfig( - logger: Logger, +/** + * Loads a CLI configuration file from `configFile`. + * + * @param actionState The Action state. + * @param configFile The address of the configuration file. + * @param workspacePath The workspace path, used to check that the configuration file exists relative to it. + * @param apiDetails Information for how to access the API to fetch remote files. + * @param tempDir The temporary directory which may contain a CodeQL Action-generated configuration file. + * @returns The loaded configuration file, if successful. + */ +export async function loadUserConfig( + actionState: ActionState<["Logger", "Env", "FeatureFlags"]>, configFile: string, workspacePath: string, apiDetails: api.GitHubApiCombinedDetails, tempDir: string, - validateConfig: boolean, ): Promise { if (isLocal(configFile)) { if (configFile !== userConfigFromActionPath(tempDir)) { @@ -617,14 +491,18 @@ async function loadUserConfig( ); } } - return getLocalConfig(logger, configFile, validateConfig); - } else { - return await getRemoteConfig( - logger, - configFile, - apiDetails, - validateConfig, + const validateConfig = await actionState.features.getValue( + Feature.ValidateDbConfig, ); + return getLocalConfig(actionState.logger, configFile, validateConfig); + } else { + // Drop the explicit prefix if it is present. Since `REMOTE_PATH_PREFIX` is chosen + // to not conflict with permissible characters in "owner" or "repo" components, + // this does not risk removing valid parts of either component by accident. + if (isExplicitRemotePath(configFile)) { + configFile = configFile.substring(REMOTE_PATH_PREFIX.length); + } + return await getRemoteConfig(actionState, configFile, apiDetails); } } @@ -705,11 +583,8 @@ async function checkOverlayAnalysisFeatureEnabled( function runnerHasSufficientDiskSpace( diskUsage: DiskUsage, logger: Logger, - useV2ResourceChecks: boolean, ): boolean { - const minimumDiskSpaceBytes = useV2ResourceChecks - ? OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_V2_BYTES - : OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_BYTES; + const minimumDiskSpaceBytes = OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_BYTES; if (diskUsage.numAvailableBytes < minimumDiskSpaceBytes) { const diskSpaceMb = Math.round(diskUsage.numAvailableBytes / 1_000_000); const minimumDiskSpaceMb = Math.round(minimumDiskSpaceBytes / 1_000_000); @@ -764,9 +639,8 @@ async function checkRunnerResources( diskUsage: DiskUsage, ramInput: string | undefined, logger: Logger, - useV2ResourceChecks: boolean, ): Promise> { - if (!runnerHasSufficientDiskSpace(diskUsage, logger, useV2ResourceChecks)) { + if (!runnerHasSufficientDiskSpace(diskUsage, logger)) { return new Failure(OverlayDisabledReason.InsufficientDiskSpace); } if (!(await runnerHasSufficientMemory(codeql, ramInput, logger))) { @@ -865,9 +739,6 @@ export async function checkOverlayEnablement( Feature.OverlayAnalysisSkipResourceChecks, codeql, )); - const useV2ResourceChecks = await features.getValue( - Feature.OverlayAnalysisResourceChecksV2, - ); const checkOverlayStatus = await features.getValue( Feature.OverlayAnalysisStatusCheck, ); @@ -881,13 +752,7 @@ export async function checkOverlayEnablement( } const resourceResult = performResourceChecks && diskUsage !== undefined - ? await checkRunnerResources( - codeql, - diskUsage, - ramInput, - logger, - useV2ResourceChecks, - ) + ? await checkRunnerResources(codeql, diskUsage, ramInput, logger) : new Success(undefined); if (resourceResult.isFailure()) { return resourceResult; @@ -1071,7 +936,11 @@ function dbLocationOrDefault( return dbLocation || path.resolve(tempDir, "codeql_databases"); } -function userConfigFromActionPath(tempDir: string): string { +/** + * Gets the path for the CodeQL Action-generated configuration file, + * which is used to store the `config` input. + */ +export function userConfigFromActionPath(tempDir: string): string { return path.resolve(tempDir, "user-config-from-action.yml"); } @@ -1132,44 +1001,123 @@ export async function applyIncrementalAnalysisSettings( } /** - * Load and return the config. + * Determines where to load the `UserConfig` for the CLI from and loads it. * - * This will parse the config from the user input if present, or generate - * a default config. The parsed config is then stored to a known location. + * @param inputs The Action inputs. The `configFile` value will be mutated + * if a CodeQL Action-generated file should be used. + * + * @returns The loaded `UserConfig`, which might be empty if no configuration + * was specified. */ -export async function initConfig( - features: FeatureEnablement, +export async function determineUserConfig( + action: ActionState<["Logger", "Env", "FeatureFlags"]>, + tempDir: string, inputs: InitConfigInputs, -): Promise { - const { logger, tempDir } = inputs; +): Promise { + const validateConfig = await action.features.getValue( + Feature.ValidateDbConfig, + ); - // if configInput is set, it takes precedence over configFile + // We have the following cases: + // 1. A `config` or `config-file` input is provided, but not both: use the provided one. + // 2. Both are provided and we are in an advanced workflow: ignore the `config-file` input. + // 3. Both are provided and we are in Default Setup: the `config` input uses a limited + // set of options, which are supported by `mergeDefaultSetupAndUserConfigs`, + // and we merge the two configs. if (inputs.configInput) { - if (inputs.configFile) { - logger.warning( - `Both a config file and config input were provided. Ignoring config file.`, + const computedConfigPath = userConfigFromActionPath(tempDir); + + // Get a function which enables us to determine whether the FF that allows us to + // merge supported configuration file properties is enabled. We only execute + // this lazily if the other checks pass. + const allowMergeConfigs = () => + action.features.getValue(Feature.AllowMergeConfigFiles); + + // Check whether we also have a `config-file` input and decide what to do. + if ( + inputs.configFile && + isDefaultSetup(action.env) && + (await allowMergeConfigs()) + ) { + // If the FF is enabled and we are in Default Setup, combine the supported + // configuration file properties and write the result to disk. + const fromConfigInput = parseUserConfig( + action.logger, + "`config` input", + inputs.configInput, + validateConfig, + ); + const fromConfigFile = await loadUserConfig( + action, + inputs.configFile, + inputs.workspacePath, + inputs.apiDetails, + tempDir, + ); + + // Write the merged configuration to disk so that it can be loaded subsequently by + // the CLI or other CodeQL Action steps. + const mergedConfig = mergeDefaultSetupAndUserConfigs( + action.logger, + fromConfigInput, + fromConfigFile, + ); + fs.writeFileSync(computedConfigPath, yaml.dump(mergedConfig)); + action.logger.debug( + `Using merged configurations from 'config' input with configuration from '${inputs.configFile}': ${computedConfigPath}`, + ); + + inputs.configFile = computedConfigPath; + return mergedConfig; + } else { + // If we are in this branch and there is a `config-file` input, then it means + // we didn't meet the conditions for merging the configurations. Warn the user + // that the configuration file will be ignored. + if (inputs.configFile) { + action.logger.warning( + `Both a config file and config input were provided. Ignoring config file.`, + ); + } + + // Write the `config` input straight to disk. + fs.writeFileSync(computedConfigPath, inputs.configInput); + inputs.configFile = computedConfigPath; + action.logger.debug( + `Using config from action input: ${inputs.configFile}`, ); } - inputs.configFile = userConfigFromActionPath(tempDir); - fs.writeFileSync(inputs.configFile, inputs.configInput); - logger.debug(`Using config from action input: ${inputs.configFile}`); } - let userConfig: UserConfig = {}; + // Load whatever configuration file we have, if any. if (!inputs.configFile) { - logger.debug("No configuration file was provided"); + action.logger.debug("No configuration file was provided"); + return {}; } else { - logger.debug(`Using configuration file: ${inputs.configFile}`); - const validateConfig = await features.getValue(Feature.ValidateDbConfig); - userConfig = await loadUserConfig( - logger, + action.logger.debug(`Using configuration file: ${inputs.configFile}`); + return await loadUserConfig( + action, inputs.configFile, inputs.workspacePath, inputs.apiDetails, tempDir, - validateConfig, ); } +} + +/** + * Load and return the config. + * + * This will parse the config from the user input if present, or generate + * a default config. The parsed config is then stored to a known location. + */ +export async function initConfig( + actionState: ActionState<["Logger", "Env", "FeatureFlags"]>, + inputs: InitConfigInputs, +): Promise { + const { logger, features } = actionState; + const { tempDir } = inputs; + + const userConfig = await determineUserConfig(actionState, tempDir, inputs); const config = await initActionState(inputs, userConfig); @@ -1196,7 +1144,6 @@ export async function initConfig( try { gitVersion = await getGitVersionOrThrow(); logger.info(`Using Git version ${gitVersion.fullVersion}`); - await logGitVersionTelemetry(config, gitVersion); } catch (e) { logger.warning(`Could not determine Git version: ${getErrorMessage(e)}`); // Throw the error in test mode so it's more visible, unless the environment @@ -1317,39 +1264,61 @@ export async function initConfig( return config; } -function parseRegistries( - registriesInput: string | undefined, -): RegistryConfigWithCredentials[] | undefined { - try { - return registriesInput - ? (yaml.load(registriesInput) as RegistryConfigWithCredentials[]) - : undefined; - } catch { - throw new ConfigurationError( - "Invalid registries input. Must be a YAML string.", - ); - } +/** + * Determines if `configPath` is explicitly local. That is, it starts with `LOCAL_PATH_PREFIX`. + * A configuration file path that starts with `LOCAL_PATH_PREFIX` is always treated as a local path. + * + * @param configPath The path to test. + */ +function isExplicitLocalPath(configPath: string): boolean { + return configPath.startsWith(LOCAL_PATH_PREFIX); } -export function parseRegistriesWithoutCredentials( - registriesInput?: string, -): RegistryConfigNoCredentials[] | undefined { - return parseRegistries(registriesInput)?.map((r) => { - const { url, packages, kind } = r; - return { url, packages, kind }; - }); +/** + * Determines if `configPath` starts with the prefix used to explicitly mark a path + * as a remote path (`REMOTE_PATH_PREFIX`). + * + * @param configPath The path to test. + */ +function isExplicitRemotePath(configPath: string): boolean { + return configPath.startsWith(REMOTE_PATH_PREFIX); } +/** + * Determines if `configPath` contains a '@' character. + * + * @param configPath The path to test. + */ +function containsAtRef(configPath: string): boolean { + return configPath.includes("@"); +} + +/** + * Determines if `configPath` refers to a local configuration file. + * + * @param configPath The path to test. + * @returns True if it is local, or false otherwise. + */ function isLocal(configPath: string): boolean { - // If the path starts with ./, look locally - if (configPath.indexOf("./") === 0) { + // If the path starts with `LOCAL_PATH_PREFIX`, it is explicitly local. + // This allows local paths that would otherwise contain '@' + // to be used with a `LOCAL_PATH_PREFIX` prefix. + if (isExplicitLocalPath(configPath)) { return true; } + // If the path starts with `REMOTE_PATH_PREFIX`, it is explicitly remote. + // This allows users to resolve ambiguity by specifying `REMOTE_PATH_PREFIX`. + if (isExplicitRemotePath(configPath)) { + return false; + } - return configPath.indexOf("@") === -1; + // Otherwise, the path is also local if it does not contain '@'. + // This assumes the `OLD_REMOTE_ADDRESS_FORMAT` which must contain a '@' + // character for remote addresses. + return !containsAtRef(configPath); } -function getLocalConfig( +export function getLocalConfig( logger: Logger, configFile: string, validateConfig: boolean, @@ -1369,54 +1338,6 @@ function getLocalConfig( ); } -async function getRemoteConfig( - logger: Logger, - configFile: string, - apiDetails: api.GitHubApiCombinedDetails, - validateConfig: boolean, -): Promise { - // retrieve the various parts of the config location, and ensure they're present - const format = new RegExp( - "(?[^/]+)/(?[^/]+)/(?[^@]+)@(?.*)", - ); - const pieces = format.exec(configFile); - // 5 = 4 groups + the whole expression - if (pieces?.groups === undefined || pieces.length < 5) { - throw new ConfigurationError( - errorMessages.getConfigFileRepoFormatInvalidMessage(configFile), - ); - } - - const response = await api - .getApiClientWithExternalAuth(apiDetails) - .rest.repos.getContent({ - owner: pieces.groups.owner, - repo: pieces.groups.repo, - path: pieces.groups.path, - ref: pieces.groups.ref, - }); - - let fileContents: string; - if ("content" in response.data && response.data.content !== undefined) { - fileContents = response.data.content; - } else if (Array.isArray(response.data)) { - throw new ConfigurationError( - errorMessages.getConfigFileDirectoryGivenMessage(configFile), - ); - } else { - throw new ConfigurationError( - errorMessages.getConfigFileFormatInvalidMessage(configFile), - ); - } - - return parseUserConfig( - logger, - configFile, - Buffer.from(fileContents, "base64").toString("binary"), - validateConfig, - ); -} - /** * Get the file path where the parsed config will be stored. */ @@ -1700,26 +1621,6 @@ export function getPrimaryAnalysisConfig(config: Config): AnalysisConfig { return getAnalysisConfig(getPrimaryAnalysisKind(config)); } -/** Logs the Git version as a telemetry diagnostic. */ -async function logGitVersionTelemetry( - config: Config, - gitVersion: GitVersionInfo, -): Promise { - if (config.languages.length > 0) { - addNoLanguageDiagnostic( - config, - makeTelemetryDiagnostic( - "codeql-action/git-version-telemetry", - "Git version telemetry", - { - fullVersion: gitVersion.fullVersion, - truncatedVersion: gitVersion.truncatedVersion, - }, - ), - ); - } -} - /** * Logs the time it took to identify generated files and how many were discovered as * a telemetry diagnostic. diff --git a/src/config/action-config.ts b/src/config/action-config.ts new file mode 100644 index 0000000000..de6882e77e --- /dev/null +++ b/src/config/action-config.ts @@ -0,0 +1,128 @@ +import type { AnalysisKind } from "../analyses"; +import type { CachingKind } from "../caching-utils"; +import type { RepositoryProperties } from "../feature-flags/properties"; +import type { Language } from "../languages"; +import type { OverlayDatabaseMode } from "../overlay/overlay-database-mode"; +import type { BuildMode, GitHubVersion } from "../util"; + +import type { ExcludeQueryFilter, UserConfig } from "./db-config"; + +/** + * Format of the CodeQL Action configuration state that is persisted + * between steps of the CodeQL Action in a CodeQL workflow. + */ +export interface Config { + /** + * The version of the CodeQL Action that the configuration is for. + */ + version: string; + /** + * Set of analysis kinds that are enabled. + */ + analysisKinds: AnalysisKind[]; + /** + * Set of languages to run analysis for. + */ + languages: Language[]; + /** + * Build mode, if set. Currently only a single build mode is supported per job. + */ + buildMode: BuildMode | undefined; + /** + * A unaltered copy of the original user input. + * Mainly intended to be used for status reporting. + * If any field is useful for the actual processing + * of the action then consider pulling it out to a + * top-level field above. + */ + originalUserInput: UserConfig; + /** + * Directory to use for temporary files that should be + * deleted at the end of the job. + */ + tempDir: string; + /** + * Path of the CodeQL executable. + */ + codeQLCmd: string; + /** + * Version of GitHub we are talking to. + */ + gitHubVersion: GitHubVersion; + /** + * The location where CodeQL databases should be stored. + */ + dbLocation: string; + /** + * Specifies whether we are debugging mode and should try to produce extra + * output for debugging purposes when possible. + */ + debugMode: boolean; + /** + * Specifies the name of the debugging artifact if we are in debug mode. + */ + debugArtifactName: string; + /** + * Specifies the name of the database in the debugging artifact. + */ + debugDatabaseName: string; + /** + * The configuration we computed by combining `originalUserInput` with `augmentationProperties`, + * as well as adjustments made to it based on unsupported or required options. + */ + computedConfig: UserConfig; + + /** + * Partial map from languages to locations of TRAP caches for that language. + * If a key is omitted, then TRAP caching should not be used for that language. + */ + trapCaches: { [language: Language]: string }; + + /** + * Time taken to download TRAP caches. Used for status reporting. + */ + trapCacheDownloadTime: number; + + /** A value indicating how dependency caching should be used. */ + dependencyCachingEnabled: CachingKind; + + /** The keys of caches that we restored, if any. */ + dependencyCachingRestoredKeys: string[]; + + /** + * Extra query exclusions to append to the config. + */ + extraQueryExclusions: ExcludeQueryFilter[]; + + /** + * The overlay database mode to use. + */ + overlayDatabaseMode: OverlayDatabaseMode; + + /** + * Whether to use caching for overlay databases. If it is true, the action + * will upload the created overlay-base database to the actions cache, and + * download an overlay-base database from the actions cache before it creates + * a new overlay database. If it is false, the action assumes that the + * workflow will be responsible for managing database storage and retrieval. + * + * This property has no effect unless `overlayDatabaseMode` is `Overlay` or + * `OverlayBase`. + */ + useOverlayDatabaseCaching: boolean; + + /** + * Whether the overlay database mode was set explicitly. + */ + overlayModeSetExplicitly: boolean; + + /** + * A partial mapping from repository properties that affect us to their values. + */ + repositoryProperties: RepositoryProperties; + + /** + * Whether to enable file coverage information. + */ + enableFileCoverageInformation: boolean; +} diff --git a/src/config/db-config.test.ts b/src/config/db-config.test.ts index ca0061e136..63d9d2ffec 100644 --- a/src/config/db-config.test.ts +++ b/src/config/db-config.test.ts @@ -8,6 +8,7 @@ import { getRecordingLogger, LoggedMessage, makeMacro, + RecordingLogger, } from "../testing-utils"; import { ConfigurationError, prettyPrintPack } from "../util"; @@ -488,3 +489,139 @@ test("parseUserConfig - throws no ConfigurationError if validation should fail, ), ); }); + +test("mergeDefaultSetupAndUserConfigs - combines threat models", async (t) => { + const logger = new RecordingLogger(); + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { "threat-models": ["a", "b"] }, + { "threat-models": ["local", "remote"] }, + ); + + const threatModels = result["threat-models"]; + + if (t.truthy(threatModels)) { + t.deepEqual(threatModels, ["a", "b", "local", "remote"]); + } +}); + +test("mergeDefaultSetupAndUserConfigs - warns if user-supplied config contains default setup key", async (t) => { + const logger = new RecordingLogger(); + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + {}, + { "default-setup": {} }, + ); + + // User-supplied value is ignored. + t.deepEqual(result, {}); + + // Warning is logged. + t.true( + logger.hasMessage( + "The 'default-setup' configuration key is not supported in user-supplied configuration files", + ), + ); +}); + +test("mergeDefaultSetupAndUserConfigs - keeps default setup key from 'config' input", async (t) => { + const logger = new RecordingLogger(); + const expected: dbConfig.DefaultSetupConfig = { + org: { "model-packs": ["some-pack"] }, + }; + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { "default-setup": expected }, + {}, + ); + + // Result matches the input. + t.deepEqual(result["default-setup"], expected); + + // No warning is logged. + t.false( + logger.hasMessage( + "The 'default-setup' configuration key is not supported in user-supplied configuration files", + ), + ); +}); + +test("mergeDefaultSetupAndUserConfigs - keeps other properties from user-supplied configuration", async (t) => { + const logger = new RecordingLogger(); + const configFile: dbConfig.UserConfig = { + "query-filters": [{ exclude: { a: "b" } }], + "paths-ignore": ["path"], + }; + + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + {}, + configFile, + ); + + t.deepEqual(result, configFile); +}); + +test("mergeDefaultSetupAndUserConfigs - ignores, but warns about, unknown keys from Default Setup", async (t) => { + const logger = new RecordingLogger(); + const configFile: dbConfig.UserConfig = { + "query-filters": [{ exclude: { a: "b" } }], + "paths-ignore": ["path"], + }; + + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { + "default-setup": { + borg: [], + org: { + unknown: "foo", + "model-packs": [], + }, + } as unknown as dbConfig.DefaultSetupConfig, + "paths-ignore": ["other-path"], + }, + configFile, + ); + + t.deepEqual(result, { + ...configFile, + "default-setup": { org: { "model-packs": [] } }, + }); + + const expectedUnrecognisedKeys = [ + ".default-setup.org.unknown", + ".default-setup.borg", + ".paths-ignore", + ].join(", "); + checkExpectedLogMessages(t, logger.messages, [ + `Unrecognised keys in Default Setup configuration: ${expectedUnrecognisedKeys}`, + ]); +}); + +test("mergeDefaultSetupAndUserConfigs - warns about invalid keys from Default Setup", async (t) => { + const logger = new RecordingLogger(); + const configFile: dbConfig.UserConfig = {}; + + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { + "default-setup": { + org: { + "model-packs": [123], + }, + } as unknown as dbConfig.DefaultSetupConfig, + }, + configFile, + ); + + t.deepEqual(result, { + ...configFile, + "default-setup": { org: { "model-packs": [123] } }, + }); + + const expectedInvalidKeys = [".default-setup.org.model-packs[0]"].join(", "); + checkExpectedLogMessages(t, logger.messages, [ + `Invalid keys in Default Setup configuration: ${expectedInvalidKeys}`, + ]); +}); diff --git a/src/config/db-config.ts b/src/config/db-config.ts index a84a20f247..7b5bdbd8ce 100644 --- a/src/config/db-config.ts +++ b/src/config/db-config.ts @@ -4,11 +4,16 @@ import * as yaml from "js-yaml"; import * as jsonschema from "jsonschema"; import * as semver from "semver"; +import { + addNoLanguageDiagnostic, + makeTelemetryDiagnostic, +} from "../diagnostics"; import * as errorMessages from "../error-messages"; import { RepositoryProperties, RepositoryPropertyName, } from "../feature-flags/properties"; +import * as json from "../json"; import { Language } from "../languages"; import { Logger } from "../logging"; import { cloneObject, ConfigurationError, prettyPrintPack } from "../util"; @@ -28,6 +33,21 @@ export interface QuerySpec { uses: string; } +const ORG_SCHEMA = { + /** An array of model pack names. */ + "model-packs": json.optional(json.array(json.string)), +} as const satisfies json.Schema; + +/** Not intended to be provided directly by a user. */ +export type OrgType = json.FromSchema; + +const DEFAULT_SETUP_SCHEMA = { + org: json.optional(json.object(ORG_SCHEMA)), +} as const satisfies json.Schema; + +/** Not intended to be provided directly by a user. */ +export type DefaultSetupConfig = json.FromSchema; + /** * Format of the config file supplied by the user. */ @@ -46,6 +66,119 @@ export interface UserConfig { // Set of query filters to include and exclude extra queries based on // codeql query suite `include` and `exclude` properties "query-filters"?: QueryFilter[]; + + /** An array (possibly empty or absent) of threat models to use. */ + "threat-models"?: string[]; + + /** + * Configuration options that are reserved for us in Default Setup and + * not intended to be supplied directly by users. + */ + "default-setup"?: DefaultSetupConfig; +} + +/** A subset of the `UserConfig` schema that is used by Default Setup. */ +const DEFAULT_SETUP_CONFIG_SCHEMA = { + "threat-models": json.optional(json.array(json.string)), + "default-setup": json.optional( + json.object(DEFAULT_SETUP_SCHEMA), + ), +} as const satisfies json.Schema; + +/** + * Merges supported properties from two configuration files. This is intended only for + * use with merging the `config` input provided by Default Setup with a potentially + * richer configuration file provided by a user. + * + * @param logger The logger to use. + * @param fromConfigInput The configuration from Default Setup. + * @param fromConfigFile The user-supplied configuration. + * @returns The combination of both configuration files. + */ +export function mergeDefaultSetupAndUserConfigs( + logger: Logger, + fromConfigInput: UserConfig, + fromConfigFile: UserConfig, +): UserConfig { + logger.debug( + "Combining configuration files from 'config' and 'config-file' inputs", + ); + + // Check for unexpected keys in the configuration from the `config` input + // that was provided by Default Setup. This should only contain the keys + // we would expect to receive from Default Setup. + const schemaCheckResult = json.checkSchema( + DEFAULT_SETUP_CONFIG_SCHEMA, + fromConfigInput as json.UnvalidatedObject, + ); + + // Report any invalid or unrecognised keys. + if (schemaCheckResult.invalidKeys.length > 0) { + logger.warning( + `Invalid keys in Default Setup configuration: ${schemaCheckResult.invalidKeys.join(", ")}`, + ); + addNoLanguageDiagnostic( + undefined, + makeTelemetryDiagnostic( + "codeql-action/invalid-default-setup-config-keys", + "Invalid Default Setup configuration keys", + { + invalidKeys: schemaCheckResult.invalidKeys, + }, + ["internal-error"], + ), + ); + } + if (schemaCheckResult.unknownKeys.length > 0) { + logger.warning( + `Unrecognised keys in Default Setup configuration: ${schemaCheckResult.unknownKeys.join(", ")}`, + ); + addNoLanguageDiagnostic( + undefined, + makeTelemetryDiagnostic( + "codeql-action/unrecognised-default-setup-config-keys", + "Unrecognised Default Setup configuration keys", + { + unrecognisedKeys: schemaCheckResult.unknownKeys, + }, + ["internal-error"], + ), + ); + } + + // Combine all specified threat models from both sources. + const threatModels = new Set(fromConfigInput["threat-models"] || []); + for (const configFileThreatModel of fromConfigFile["threat-models"] || []) { + threatModels.add(configFileThreatModel); + } + + // Warn if there is a 'default-setup' configuration key in the user-supplied configuration, + // since it is not meant to be used and we therefore ignore it here. + if (fromConfigFile["default-setup"]) { + logger.warning( + `The 'default-setup' configuration key is not supported in user-supplied configuration files and will be ignored.`, + ); + } + + // Since we expect the `fromConfigInput` configuration to be provided by Default Setup, + // we expect a limited set of options. Therefore, we base the overall configuration on + // the one provided via the `config-file` input, which may be richer. + const result = { ...fromConfigFile }; + delete result["threat-models"]; + delete result["default-setup"]; + + if (fromConfigInput["default-setup"]?.org?.["model-packs"]) { + result["default-setup"] = { + org: { + "model-packs": fromConfigInput["default-setup"].org["model-packs"], + }, + }; + } + if (threatModels.size > 0) { + result["threat-models"] = Array.from(threatModels); + } + + return result; } /** diff --git a/src/config/file.test.ts b/src/config/file.test.ts index fef6088297..0833ad3d06 100644 --- a/src/config/file.test.ts +++ b/src/config/file.test.ts @@ -1,22 +1,27 @@ +import * as github from "@actions/github"; import test from "ava"; import sinon from "sinon"; +import { AnalysisKind } from "../analyses"; +import * as api from "../api-client"; +import { RegistryProxyVars } from "../environment"; +import { Feature } from "../feature-flags"; import { RepositoryPropertyName } from "../feature-flags/properties"; import { - getTestActionsEnv, - RecordingLogger, + callee, + SAMPLE_DOTCOM_API_DETAILS, setupTests, } from "../testing-utils"; -import { getConfigFileInput } from "./file"; +import { getConfigFileInput, getRemoteConfig } from "./file"; setupTests(test); test("getConfigFileInput returns undefined by default", async (t) => { - const logger = new RecordingLogger(); - const actionsEnv = getTestActionsEnv(); - const result = getConfigFileInput(logger, actionsEnv, {}, true); - t.is(result, undefined); + await callee(getConfigFileInput) + .withArgs({}, undefined) + .withFeatures([Feature.ConfigFileRepositoryProperty]) + .passes(t.is, undefined); }); const repositoryProperties = { @@ -24,86 +29,138 @@ const repositoryProperties = { }; test("getConfigFileInput returns input value", async (t) => { - const logger = new RecordingLogger(); - const actionsEnv = getTestActionsEnv(); const testInput = "/some/path"; - sinon - .stub(actionsEnv, "getOptionalInput") - .withArgs("config-file") - .returns(testInput); // Even though both an input and repository property are configured, // we prefer the direct input to the Action. - const result = getConfigFileInput( - logger, - actionsEnv, - repositoryProperties, - true, - ); - t.is(result, testInput); - - // Check for the expected log message. - t.true(logger.hasMessage("Using configuration file input from workflow")); + await callee(getConfigFileInput) + .withFeatures([Feature.ConfigFileRepositoryProperty]) + .withActions((actionsEnv) => { + sinon + .stub(actionsEnv, "getOptionalInput") + .withArgs("config-file") + .returns(testInput); + }) + .withArgs(repositoryProperties, undefined) + .logs(t, "Using configuration file input from workflow") + .passes(t.is, testInput); }); test("getConfigFileInput returns repository property value", async (t) => { - const logger = new RecordingLogger(); - const actionsEnv = getTestActionsEnv(); + // Since there is no direct input, we should use the repository property. + await callee(getConfigFileInput) + .withFeatures([Feature.ConfigFileRepositoryProperty]) + .withArgs(repositoryProperties, undefined) + .logs(t, "Using configuration file input from repository property") + .passes(t.is, repositoryProperties[RepositoryPropertyName.CONFIG_FILE]); +}); +test("getConfigFileInput returns repository property value for Code Scanning", async (t) => { // Since there is no direct input, we should use the repository property. - const result = getConfigFileInput( - logger, - actionsEnv, - repositoryProperties, - true, - ); - t.is(result, repositoryProperties[RepositoryPropertyName.CONFIG_FILE]); - - // Check for the expected log message. - t.true( - logger.hasMessage( - "Using configuration file input from repository property", - ), - ); + await callee(getConfigFileInput) + .withFeatures([Feature.ConfigFileRepositoryProperty]) + .withArgs(repositoryProperties, [AnalysisKind.CodeScanning]) + .logs(t, "Using configuration file input from repository property") + .passes(t.is, repositoryProperties[RepositoryPropertyName.CONFIG_FILE]); }); -test("getConfigFileInput ignores empty repository property value", async (t) => { - const logger = new RecordingLogger(); - const actionsEnv = getTestActionsEnv(); +test("getConfigFileInput ignores repository property for other analysis kinds", async (t) => { + const unsupportedCases = [ + [AnalysisKind.CodeQuality], + [AnalysisKind.RiskAssessment], + [AnalysisKind.CodeScanning, AnalysisKind.CodeQuality], + ]; + + const target = callee(getConfigFileInput).withFeatures([ + Feature.ConfigFileRepositoryProperty, + ]); + + for (const unsupportedCase of unsupportedCases) { + // Since the analysis kind is unsupported, we should ignore the repository property. + await target + .withArgs(repositoryProperties, unsupportedCase) + .logs( + t, + "Ignoring configuration file input from repository property, because it is unsupported for the current analysis kind.", + ) + .passes(t.is, undefined); + } +}); +test("getConfigFileInput ignores empty repository property value", async (t) => { // Since the repository property value is an empty/whitespace string, we should ignore it. - const result = getConfigFileInput( - logger, - actionsEnv, - { - [RepositoryPropertyName.CONFIG_FILE]: " ", - }, - true, - ); - t.is(result, undefined); + await callee(getConfigFileInput) + .withFeatures([Feature.ConfigFileRepositoryProperty]) + .withArgs({ [RepositoryPropertyName.CONFIG_FILE]: " " }, undefined) + .passes(t.is, undefined); }); test("getConfigFileInput ignores repository property value when FF is off", async (t) => { - const logger = new RecordingLogger(); - const actionsEnv = getTestActionsEnv(); - // Since the FF is off, we should ignore the repository property value. - const result = getConfigFileInput( - logger, - actionsEnv, - repositoryProperties, - false, - ); - t.is(result, undefined); - - t.false( - logger.hasMessage( - "Using configuration file input from repository property", - ), - ); - t.true( - logger.hasMessage( + await callee(getConfigFileInput) + .withFeatures([]) + .withArgs(repositoryProperties, undefined) + .notLogs(t, "Using configuration file input from repository property") + .logs( + t, "Ignoring configuration file input from repository property, because the corresponding feature flag is disabled.", - ), - ); + ) + .passes(t.is, undefined); +}); + +test.serial("getRemoteConfig uses proxy when it is supposed to", async (t) => { + const client = github.getOctokit("123"); + const response = { + data: { + content: Buffer.from("disable-default-queries: false").toString("base64"), + }, + }; + sinon + .stub(client.rest.repos, "getContent") + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + .resolves(response as any); + + // We stub `getApiClientWithExternalAuth` so that it throws if no + // proxy is provided and returns the client otherwise. This allows us + // to verify the result in the following test cases. + const errorMessage = "No `proxy` was provided by the caller."; + sinon + .stub(api, "getApiClientWithExternalAuth") + .callsFake((_details, proxy) => { + // Throw if proxy isn't defined. + if (proxy === undefined) { + throw new Error(errorMessage); + } + // Otherwise return the client object. + return client; + }); + + const target = callee(getRemoteConfig) + .withDefaultActionsEnv() + .withArgs("file.yml", SAMPLE_DOTCOM_API_DETAILS); + + // Should use it when the FF is enabled and the environment variables are set. + await target + .withFeatures([Feature.ProxyApiRequests]) + .withEnv((env) => { + env.set(RegistryProxyVars.PROXY_HOST, "localhost"); + env.set(RegistryProxyVars.PROXY_PORT, "1234"); + }) + .logs(t, "Using private registry proxy at 'http://localhost:1234'") + .passes(t.truthy); + + // But not when the FF is not enabled. + await target + .withEnv((env) => { + env.set(RegistryProxyVars.PROXY_HOST, "localhost"); + env.set(RegistryProxyVars.PROXY_PORT, "1234"); + }) + .notLogs(t, "Using private registry proxy at 'http://localhost:1234'") + .throws(t, { message: errorMessage }); + + // And not when the environment variables aren't set. + await target + .withFeatures([Feature.ProxyApiRequests]) + .notLogs(t, "Using private registry proxy at 'http://localhost:1234'") + .throws(t, { message: errorMessage }); }); diff --git a/src/config/file.ts b/src/config/file.ts index 6b8dfdcdcb..be0e415a38 100644 --- a/src/config/file.ts +++ b/src/config/file.ts @@ -1,19 +1,42 @@ -import { ActionsEnv } from "../actions-util"; +import { ActionState } from "../action-common"; +import { AnalysisKind } from "../analyses"; +import * as api from "../api-client"; +import * as errorMessages from "../error-messages"; +import { Feature } from "../feature-flags"; import { RepositoryProperties, RepositoryPropertyName, } from "../feature-flags/properties"; -import { Logger } from "../logging"; +import { ConfigurationError } from "../util"; + +import { parseUserConfig, UserConfig } from "./db-config"; +import { parseRemoteFileAddress } from "./remote-file"; + +/** + * The prefix that can be specified to indicate that a path should be treated as a local file address. + */ +export const LOCAL_PATH_PREFIX = "./"; + +/** + * The prefix that can be specified to indicate that a path should be treated as a remote file address. + * The new remote file address format must start with either an owner or repository name. Both + * are restricted to ASCII characters, '.', and '-'. The prefix chosen here does not interfere with + * those (since it contains an `=`) and is _unlikely_ (but not impossible) to appear in a local file path. + */ +export const REMOTE_PATH_PREFIX = "remote="; /** * Gets the value that is configured for the configuration file, if any. */ -export function getConfigFileInput( - logger: Logger, - actions: ActionsEnv, +export async function getConfigFileInput( + { + logger, + actions, + features, + }: ActionState<["Logger", "Actions", "FeatureFlags"]>, repositoryProperties: Partial, - useRepositoryProperty: boolean, -): string | undefined { + analysisKinds: AnalysisKind[] | undefined, +): Promise { const input = actions.getOptionalInput("config-file"); if (input !== undefined) { @@ -24,13 +47,29 @@ export function getConfigFileInput( const propertyValue = repositoryProperties[RepositoryPropertyName.CONFIG_FILE]; + // Only allow the repository property to be used for standard Code Scanning analyses, + // since we don't currently support some customisation options for Code Quality. + // We don't expect customisations for Risk Assessments either. + const analysisKindSupported = + analysisKinds === undefined || + (analysisKinds.includes(AnalysisKind.CodeScanning) && + analysisKinds.length === 1); + if (propertyValue !== undefined && propertyValue.trim().length > 0) { // Only use the repository property value if the FF is enabled. - if (useRepositoryProperty) { + const useRepositoryProperty = await features.getValue( + Feature.ConfigFileRepositoryProperty, + ); + + if (analysisKindSupported && useRepositoryProperty) { logger.info( `Using configuration file input from repository property: ${propertyValue}`, ); return propertyValue; + } else if (!analysisKindSupported) { + logger.info( + "Ignoring configuration file input from repository property, because it is unsupported for the current analysis kind.", + ); } else { logger.info( "Ignoring configuration file input from repository property, because the corresponding feature flag is disabled.", @@ -40,3 +79,59 @@ export function getConfigFileInput( return undefined; } + +/** + * Attempts to fetch a `UserConfig` from a remote `address`. + * + * @param actionState The current Action state. + * @param configFile The remote address of the configuration file. + * @param apiDetails Information about how to connect to the API. + * + * @returns The `UserConfig`, if it could be fetched and parsed successfully. + */ +export async function getRemoteConfig( + actionState: ActionState<["Logger", "Env", "FeatureFlags"]>, + configFile: string, + apiDetails: api.GitHubApiCombinedDetails, +): Promise { + const address = await parseRemoteFileAddress(actionState, configFile); + + const shouldProxyRequest = await actionState.features.getValue( + Feature.ProxyApiRequests, + ); + const proxy = shouldProxyRequest + ? api.getRegistryProxy(actionState) + : undefined; + + const response = await api + .getApiClientWithExternalAuth(apiDetails, proxy) + .rest.repos.getContent({ + owner: address.owner, + repo: address.repo, + path: address.path, + ref: address.ref, + }); + + let fileContents: string; + if ("content" in response.data && response.data.content !== undefined) { + fileContents = response.data.content; + } else if (Array.isArray(response.data)) { + throw new ConfigurationError( + errorMessages.getConfigFileDirectoryGivenMessage(configFile), + ); + } else { + throw new ConfigurationError( + errorMessages.getConfigFileFormatInvalidMessage(configFile), + ); + } + + const validateConfig = await actionState.features.getValue( + Feature.ValidateDbConfig, + ); + return parseUserConfig( + actionState.logger, + configFile, + Buffer.from(fileContents, "base64").toString("binary"), + validateConfig, + ); +} diff --git a/src/config/inputs.test.ts b/src/config/inputs.test.ts new file mode 100644 index 0000000000..851dd72e2f --- /dev/null +++ b/src/config/inputs.test.ts @@ -0,0 +1,89 @@ +import test from "ava"; +import sinon from "sinon"; + +import { ActionsEnv } from "../actions-util"; +import { Feature } from "../feature-flags"; +import { RepositoryPropertyName } from "../feature-flags/properties"; +import { callee } from "../testing-utils"; + +import { ComputedInput, getToolsInput, InputName, InputSource } from "./inputs"; + +test("getToolsInput - undefined if there's no input", async (t) => { + await callee(getToolsInput).withArgs({}).passes(t.is, undefined); +}); + +const expectedWorkflowResult: ComputedInput = { + source: InputSource.Workflow, + value: "workflow-input-value", +}; + +const expectedRepositoryPropertyResult: ComputedInput = { + source: InputSource.RepositoryProperty, + value: "repo-property-input-value", +}; + +function stubGetToolsInput(actions: ActionsEnv) { + sinon + .stub(actions, "getOptionalInput") + .withArgs(InputName.Tools) + .returns(expectedWorkflowResult.value); +} + +const workflowLogMessage = `Using ${InputName.Tools} input from workflow:`; + +test("getToolsInput - returns workflow input if available", async (t) => { + await callee(getToolsInput) + .withActions(stubGetToolsInput) + .withArgs({}) + .logs(t, workflowLogMessage) + .passes(t.deepEqual, expectedWorkflowResult); +}); + +test("getToolsInput - returns repository property value if enforced", async (t) => { + const target = callee(getToolsInput) + .withActions(stubGetToolsInput) + .withArgs({ + [RepositoryPropertyName.TOOLS]: `!${expectedRepositoryPropertyResult.value}`, + }); + + // We expect the repository value if provided and the FF is enabled. + const enforcedLogMessage = `Using ${InputName.Tools} input from repository property (enforced):`; + await target + .withFeatures([Feature.ToolsRepositoryProperty]) + .logs(t, enforcedLogMessage) + .passes(t.deepEqual, expectedRepositoryPropertyResult); + await target + .notLogs(t, enforcedLogMessage) + .logs(t, workflowLogMessage) + .passes(t.deepEqual, expectedWorkflowResult); +}); + +test("getToolsInput - prefers workflow input", async (t) => { + const target = callee(getToolsInput) + .withActions(stubGetToolsInput) + .withArgs({ + [RepositoryPropertyName.TOOLS]: expectedRepositoryPropertyResult.value, + }); + + // We expect the workflow input regardless of the FF state. + await target + .withFeatures([Feature.ToolsRepositoryProperty]) + .logs(t, workflowLogMessage) + .passes(t.deepEqual, expectedWorkflowResult); + await target + .logs(t, workflowLogMessage) + .passes(t.deepEqual, expectedWorkflowResult); +}); + +test("getToolsInput - returns repository property", async (t) => { + const target = callee(getToolsInput).withArgs({ + [RepositoryPropertyName.TOOLS]: expectedRepositoryPropertyResult.value, + }); + + // We expect the repository property if the FF is enabled or undefined otherwise. + await target + .withFeatures([Feature.ToolsRepositoryProperty]) + .logs(t, `Using ${InputName.Tools} input from repository property:`) + .passes(t.deepEqual, expectedRepositoryPropertyResult); + await target.passes(t.is, undefined); +}); diff --git a/src/config/inputs.ts b/src/config/inputs.ts new file mode 100644 index 0000000000..32a8dfd6f6 --- /dev/null +++ b/src/config/inputs.ts @@ -0,0 +1,80 @@ +import { ActionState } from "../action-common"; +import { Feature } from "../feature-flags"; +import { + RepositoryProperties, + RepositoryPropertyName, +} from "../feature-flags/properties"; + +/** Enumerates input names. */ +export enum InputName { + Tools = "tools", +} + +/** Enumerates input sources. */ +export enum InputSource { + Workflow = "workflow", + RepositoryProperty = "repository-property", +} + +/** + * Represents an effective input to the CodeQL Action. That is, + * the input value that was computed or selected from multiple sources. + */ +export type ComputedInput = { + /** The value of the property. */ + value: string; + /** The source of the property. */ + source: InputSource; +}; + +/** + * Gets the computed `tools` input. This comes from either the workflow or + * the repository property. + * + * @param action The Action state. + * @param repositoryProperties The values of known repository properties. + * @returns The computed input or `undefined` if there is no input. + */ +export async function getToolsInput( + action: ActionState<["Logger", "Actions", "FeatureFlags"]>, + repositoryProperties: Partial, +): Promise { + const name = InputName.Tools; + const input = action.actions.getOptionalInput(name); + const propertyValue = repositoryProperties[RepositoryPropertyName.TOOLS]; + const allowRepositoryProperty = await action.features.getValue( + Feature.ToolsRepositoryProperty, + ); + + // The repository property takes precedence if it starts with an '!'. + if (allowRepositoryProperty && propertyValue?.startsWith("!")) { + action.logger.info( + `Using ${name} input from repository property (enforced): ${propertyValue}`, + ); + return { + // Drop the '!' from the value. + value: propertyValue.substring(1), + source: InputSource.RepositoryProperty, + }; + } + + // Otherwise, the input from the workflow takes precedence. + if (input !== undefined) { + action.logger.info(`Using ${name} input from workflow: ${input}`); + return { value: input, source: InputSource.Workflow }; + } + + // Use the repository property if there's no workflow input. + if (allowRepositoryProperty && propertyValue !== undefined) { + action.logger.info( + `Using ${name} input from repository property: ${propertyValue}`, + ); + return { + value: propertyValue, + source: InputSource.RepositoryProperty, + }; + } + + // There's no input. + return undefined; +} diff --git a/src/config/pack-registries.ts b/src/config/pack-registries.ts new file mode 100644 index 0000000000..76d2f6cd47 --- /dev/null +++ b/src/config/pack-registries.ts @@ -0,0 +1,49 @@ +import * as yaml from "js-yaml"; + +import { ConfigurationError } from "../util"; + +export type RegistryConfigWithCredentials = RegistryConfigNoCredentials & { + // Token to use when downloading packs from this registry. + token: string; +}; + +/** + * The list of registries and the associated pack globs that determine where each + * pack can be downloaded from. + */ +export interface RegistryConfigNoCredentials { + // URL of a package registry, eg- https://ghcr.io/v2/ + url: string; + + // List of globs that determine which packs are associated with this registry. + packages: string[] | string; + + // Kind of registry, either "github" or "docker". Default is "docker". + // "docker" refers specifically to the GitHub Container Registry, which is the usual way of sharing CodeQL packs. + // "github" refers to packs published as content in a GitHub repository. This kind of registry is used in scenarios + // where GHCR is not available, such as certain GHES environments. + kind?: "github" | "docker"; +} + +export function parseRegistries( + registriesInput: string | undefined, +): RegistryConfigWithCredentials[] | undefined { + try { + return registriesInput + ? (yaml.load(registriesInput) as RegistryConfigWithCredentials[]) + : undefined; + } catch { + throw new ConfigurationError( + "Invalid registries input. Must be a YAML string.", + ); + } +} + +export function parseRegistriesWithoutCredentials( + registriesInput?: string, +): RegistryConfigNoCredentials[] | undefined { + return parseRegistries(registriesInput)?.map((r) => { + const { url, packages, kind } = r; + return { url, packages, kind }; + }); +} diff --git a/src/config/remote-file.test.ts b/src/config/remote-file.test.ts new file mode 100644 index 0000000000..e263e6d79a --- /dev/null +++ b/src/config/remote-file.test.ts @@ -0,0 +1,229 @@ +import test from "ava"; +import sinon from "sinon"; + +import { ActionsEnvVars } from "../environment"; +import { callee } from "../testing-utils"; +import { ConfigurationError } from "../util"; + +import { + DEFAULT_CONFIG_FILE_NAME, + DEFAULT_CONFIG_FILE_REF, + parseRemoteFileAddress, + RemoteFileAddress, +} from "./remote-file"; + +type ParseRemoteFileAddressTest = { + input: string; + expected: RemoteFileAddress; +}; + +test("parseRemoteFileAddress accepts full remote addresses", async (t) => { + const target = callee(parseRemoteFileAddress); + + const expected: RemoteFileAddress = { + owner: "owner", + repo: "repo", + path: "path", + ref: "ref", + }; + + const oldFormatInputs: ParseRemoteFileAddressTest[] = [ + { input: "owner/repo/path@ref", expected }, + { input: "owner /repo/path@ref", expected }, + { input: "owner/ repo/path@ref", expected }, + { input: "owner/repo /path@ref", expected }, + { input: "owner/repo/ path@ref", expected }, + { input: "owner/repo/path @ref", expected }, + { input: "owner/repo/path@ ref", expected }, + { + input: "owner/repo/path/to/codeql.yml@ref/feature", + expected: { ...expected, path: "path/to/codeql.yml", ref: "ref/feature" }, + }, + { + input: " owner/repo/path/to/codeql.yml@ref/feature ", + expected: { ...expected, path: "path/to/codeql.yml", ref: "ref/feature" }, + }, + ]; + + for (const oldFormatInput of oldFormatInputs) { + await target + .withArgs(oldFormatInput.input) + .passes(t.deepEqual, oldFormatInput.expected); + } + + // New format. + const newFormatInputs: ParseRemoteFileAddressTest[] = [ + { input: "owner/repo@ref:path", expected }, + { input: "owner /repo@ref:path", expected }, + { input: "owner/ repo@ref:path", expected }, + { input: "owner/repo @ref:path", expected }, + { input: "owner/repo@ ref:path", expected }, + { input: "owner/repo@ref :path", expected }, + { input: "owner/repo@ref: path", expected }, + { + input: "owner/repo@ref/feature:path/to/codeql.yml", + expected: { ...expected, path: "path/to/codeql.yml", ref: "ref/feature" }, + }, + { + input: " owner/repo@ref/feature:path/to/codeql.yml ", + expected: { ...expected, path: "path/to/codeql.yml", ref: "ref/feature" }, + }, + ]; + + for (const newFormatInput of newFormatInputs) { + const targetWithArgs = target.withArgs(newFormatInput.input); + + await targetWithArgs.passes(t.deepEqual, newFormatInput.expected); + } +}); + +test("parseRemoteFileAddress accepts remote address without an owner", async (t) => { + const owner = "test-owner"; + const target = callee(parseRemoteFileAddress).withEnv((env) => { + const getRequired = sinon.stub(env, "getRequired"); + getRequired + .withArgs(ActionsEnvVars.GITHUB_REPOSITORY) + .returns(`${owner}/current-repo`); + }); + + const testCases: ParseRemoteFileAddressTest[] = [ + { + input: "repo@ref:path.yml", + expected: { + owner, + repo: "repo", + path: "path.yml", + ref: "ref", + }, + }, + { + input: "repo@ref", + expected: { + owner, + repo: "repo", + path: DEFAULT_CONFIG_FILE_NAME, + ref: "ref", + }, + }, + { + input: "repo:path.yml", + expected: { + owner, + repo: "repo", + path: "path.yml", + ref: DEFAULT_CONFIG_FILE_REF, + }, + }, + { + input: "repo", + expected: { + owner, + repo: "repo", + path: DEFAULT_CONFIG_FILE_NAME, + ref: DEFAULT_CONFIG_FILE_REF, + }, + }, + ]; + + for (const testCase of testCases) { + const targetWithArgs = target.withArgs(testCase.input); + + await targetWithArgs.passes(t.deepEqual, testCase.expected); + } +}); + +test("parseRemoteFileAddress throws for invalid `GITHUB_REPOSITORY`", async (t) => { + const getRequired: sinon.SinonStub = sinon.stub(); + getRequired.withArgs(ActionsEnvVars.GITHUB_REPOSITORY).returns(`not-valid`); + + const target = callee(parseRemoteFileAddress) + .withArgs("repo@ref") + .withEnv((env) => { + sinon.define(env, "getRequired", getRequired); + }); + + await target.throws(t, { instanceOf: Error }); + + t.assert(getRequired.calledOnceWith(ActionsEnvVars.GITHUB_REPOSITORY)); +}); + +test("parseRemoteFileAddress accepts remote address without a path", async (t) => { + const target = callee(parseRemoteFileAddress); + + const testCases: ParseRemoteFileAddressTest[] = [ + { + input: "owner/repo@ref", + expected: { + owner: "owner", + repo: "repo", + path: DEFAULT_CONFIG_FILE_NAME, + ref: "ref", + }, + }, + { + input: "owner/repo", + expected: { + owner: "owner", + repo: "repo", + path: DEFAULT_CONFIG_FILE_NAME, + ref: DEFAULT_CONFIG_FILE_REF, + }, + }, + ]; + + for (const testCase of testCases) { + const targetWithArgs = target.withArgs(testCase.input); + + await targetWithArgs.passes(t.deepEqual, testCase.expected); + } +}); + +test("parseRemoteFileAddress accepts remote address without a ref", async (t) => { + const target = callee(parseRemoteFileAddress).withArgs("owner/repo:path"); + + await target.passes(t.deepEqual, { + owner: "owner", + repo: "repo", + path: "path", + ref: DEFAULT_CONFIG_FILE_REF, + } satisfies RemoteFileAddress); +}); + +test("parseRemoteFileAddress rejects invalid values", async (t) => { + const owner = "owner"; + const target = callee(parseRemoteFileAddress).withEnv((env) => { + const getRequired = sinon.stub(env, "getRequired"); + getRequired + .withArgs(ActionsEnvVars.GITHUB_REPOSITORY) + .returns(`${owner}/current-repo`); + }); + + const testInputs = [ + " ", + "repo//absolute", + "repo:/absolute", + "/repo@ref", + " /repo@ref", + "repo@", + "repo:", + "repo/", + "/repo", + ":path", + "@ref", + "@ref:path", + "owner/@ref:path", + "owner/@ref", + "owner/:path", + ]; + + for (const testInput of testInputs) { + const targetWithArgs = target.withArgs(testInput); + + await targetWithArgs.throws(t, { + // When the new format is accepted, there are some more specific + // errors in some cases. It is sufficient for us to check that + // an exception is thrown. + instanceOf: ConfigurationError, + }); + } +}); diff --git a/src/config/remote-file.ts b/src/config/remote-file.ts new file mode 100644 index 0000000000..1052072a28 --- /dev/null +++ b/src/config/remote-file.ts @@ -0,0 +1,153 @@ +import { ActionState } from "../action-common"; +import { ActionsEnvVars, ReadOnlyEnv } from "../environment"; +import * as errorMessages from "../error-messages"; +import { ConfigurationError, Failure, Result, Success } from "../util"; + +/** Represents remote file addresses. */ +export interface RemoteFileAddress { + /** The owner of the repository. */ + owner: string; + /** The repository name. */ + repo: string; + /** The path of the file. */ + path: string; + /** The ref of the repository. */ + ref: string; +} + +/** The default file path to use in configuration file shorthands. */ +export const DEFAULT_CONFIG_FILE_NAME = ".github/codeql-config.yml"; + +/** The default ref to use in configuration file shorthands. */ +export const DEFAULT_CONFIG_FILE_REF = "main"; + +/** Extracts the owner from the `GITHUB_REPOSITORY` environment variable. */ +function getDefaultOwner(env: ReadOnlyEnv): string { + const currentRepoNwo = env.getRequired(ActionsEnvVars.GITHUB_REPOSITORY); + const nwoParts = currentRepoNwo.split("/"); + + if (nwoParts.length !== 2 || nwoParts[0].trim().length === 0) { + // This shouldn't happen, so we should throw if `GITHUB_REPOSITORY` doesn't match + // our expectations. + throw new Error( + `Expected ${ActionsEnvVars.GITHUB_REPOSITORY} to contain a name with owner, but got '${currentRepoNwo}'.`, + ); + } + + return nwoParts[0].trim(); +} + +/** + * The old remote address format that's always been supported for the `config-file` input. + * All the components are required. Unchanged from the previous implementation. + */ +const OLD_REMOTE_ADDRESS_FORMAT = new RegExp( + "(?[^/]+)/(?[^/]+)/(?[^@]+)@(?.*)", +); + +/** + * Attempts to parse `input` as a `RemoteFileAddress` using the old format. + * + * @param input The input to try and parse. + * @returns A `RemoteFileAddress` value if successful or `undefined` otherwise. + */ +function parseOldRemoteFileAddress( + input: string, +): Result { + const pieces = OLD_REMOTE_ADDRESS_FORMAT.exec(input); + + // 5 = 4 groups + the whole expression + if (pieces?.groups === undefined || pieces.length < 5) { + return new Failure(undefined); + } + + return new Success({ + owner: pieces.groups.owner.trim(), + repo: pieces.groups.repo.trim(), + path: pieces.groups.path.trim(), + ref: pieces.groups.ref.trim(), + }); +} + +/** + * Attempts to parse `input` as a `RemoteFileAddress` using the new format. + * + * @param env The read-only environment to obtain the owner name from if needed. + * @param configFile The input to try and parse. + * @returns A `RemoteFileAddress` value if successful or `undefined` otherwise. + */ +export function parseNewRemoteFileAddress( + env: ReadOnlyEnv, + configFile: string, +): Result { + // retrieve the various parts of the config location, and ensure they're present + const format = new RegExp( + "^((?[^:@/]+)/)?(?[^:@/]+)(@(?[^:]+))?(:(?.+))?$", + ); + const pieces = format.exec(configFile.trim()); + + const repo: string | undefined = pieces?.groups?.repo?.trim(); + + // Check that the regular expression matched and that we have at least the repo name. + if (!pieces?.groups || !repo || repo.length === 0) { + return new Failure(undefined); + } + + const owner: string | undefined = pieces.groups.owner?.trim(); + const path: string | undefined = pieces.groups.path?.trim(); + const ref: string | undefined = pieces.groups.ref?.trim(); + + return new Success({ + owner: owner || getDefaultOwner(env), + repo, + path: path || DEFAULT_CONFIG_FILE_NAME, + ref: ref || DEFAULT_CONFIG_FILE_REF, + }); +} + +/** + * Attempts to parse `configFile` into an array of `RemoteFileAddress` components. + * + * @param actionState The current Action state. + * @param configFile The string to try and parse. + * @returns The successful result of executing the regex. + * @throws `ConfigurationError` if the format of `configFile` is not valid. + */ +export async function parseRemoteFileAddress( + actionState: ActionState<["FeatureFlags", "Env"]>, + configFile: string, +): Promise { + // Try to parse the input using the old format. If successful, return the + // resulting `RemoteFileAddress`. Otherwise, continue using the new format. + const oldFormatAddressResult = parseOldRemoteFileAddress(configFile); + + if (oldFormatAddressResult.isSuccess()) { + return oldFormatAddressResult.value; + } + + // retrieve the various parts of the config location, and ensure they're present + const newFormatAddressResult = parseNewRemoteFileAddress( + actionState.env, + configFile, + ); + + if (newFormatAddressResult.isFailure()) { + // Neither the old format nor the new format worked. Throw an error that + // explains the format we accept. We only mention the new format, since that's + // what we want to be used going forward. + throw new ConfigurationError( + errorMessages.getConfigFileRepoFormatInvalidMessage(configFile), + ); + } + + const address = newFormatAddressResult.value; + + // Ensure that the path is a relative path. + if (address.path.startsWith("/")) { + throw new ConfigurationError( + `The path component of '${configFile}' cannot be an absolute path.`, + ); + } + + return address; +} diff --git a/src/defaults.json b/src/defaults.json index 7c82ff2a6e..b5d9f13644 100644 --- a/src/defaults.json +++ b/src/defaults.json @@ -1,6 +1,6 @@ { - "bundleVersion": "codeql-bundle-v2.25.6", - "cliVersion": "2.25.6", - "priorBundleVersion": "codeql-bundle-v2.25.5", - "priorCliVersion": "2.25.5" + "bundleVersion": "codeql-bundle-v2.26.3", + "cliVersion": "2.26.3", + "priorBundleVersion": "codeql-bundle-v2.26.2", + "priorCliVersion": "2.26.2" } diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 65e82ce1af..fa0e87c046 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -6,24 +6,45 @@ import { Language } from "./languages"; import { getActionsLogger } from "./logging"; import { getCodeQLDatabasePath } from "./util"; -/** Represents a diagnostic message for the tool status page, etc. */ -export interface DiagnosticMessage { +/** + * Known tags for diagnostics. There is currently only "internal-error", + * but others may be added in the future. + */ +export type DiagnosticTag = "internal-error"; + +/** Optional information about the origin of a diagnostic. */ +export type DiagnosticSourceOptions = { + /** + * Name of the CodeQL extractor. This is used to identify which tool component the reporting + * descriptor object should be nested under in SARIF. + */ + extractorName?: string; + /** An array of tags for the diagnostic. */ + tags?: DiagnosticTag[]; +}; + +/** Represents information about the origin of a diagnostic. */ +export type DiagnosticSource = { + /** + * An identifier under which it makes sense to group this diagnostic message. + * This is used to build the SARIF reporting descriptor object. + */ + id: string; + /** Display name for the ID. This is used to build the SARIF reporting descriptor object. */ + name: string; +} & DiagnosticSourceOptions; + +/** + * Represents a diagnostic message for the tool status page, etc. + * + * Unlike {@link DiagnosticMessage}, properties which can automatically + * be populated are optional in this type. + */ +export type DiagnosticMessageOptions = { /** ISO 8601 timestamp */ - timestamp: string; - source: { - /** - * An identifier under which it makes sense to group this diagnostic message. - * This is used to build the SARIF reporting descriptor object. - */ - id: string; - /** Display name for the ID. This is used to build the SARIF reporting descriptor object. */ - name: string; - /** - * Name of the CodeQL extractor. This is used to identify which tool component the reporting - * descriptor object should be nested under in SARIF. - */ - extractorName?: string; - }; + timestamp?: string; + /** Information about the origin of the diagnostic. */ + source?: DiagnosticSourceOptions; /** GitHub flavored Markdown formatted message. Should include inline links to any help pages. */ markdownMessage?: string; /** Plain text message. Used by components where the string processing needed to support Markdown is cumbersome. */ @@ -53,7 +74,15 @@ export interface DiagnosticMessage { }; /** Structured metadata about the diagnostic message */ attributes?: { [key: string]: any }; -} +}; + +/** Represents a diagnostic message for the tool status page, etc. */ +export type DiagnosticMessage = DiagnosticMessageOptions & { + /** ISO 8601 timestamp */ + timestamp: string; + /** Information about the origin of the diagnostic. */ + source: DiagnosticSource; +}; /** Represents a diagnostic message that has not yet been written to the database. */ interface UnwrittenDiagnostic { @@ -90,7 +119,7 @@ let diagnosticCounter = 0; export function makeDiagnostic( id: string, name: string, - data: Partial | undefined = undefined, + data: DiagnosticMessageOptions | undefined = undefined, ): DiagnosticMessage { return { ...data, @@ -243,6 +272,7 @@ export function makeTelemetryDiagnostic( id: string, name: string, attributes: { [key: string]: any }, + tags?: DiagnosticTag[], ): DiagnosticMessage { return makeDiagnostic(id, name, { attributes, @@ -251,5 +281,8 @@ export function makeTelemetryDiagnostic( statusPage: false, telemetry: true, }, + source: { + tags, + }, }); } diff --git a/src/environment.ts b/src/environment.ts index c3f54ebd27..d6ff20391a 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -1,3 +1,13 @@ +/** + * Environment variables used by Default Setup to communicate the private registry proxy configuration. + */ +export enum RegistryProxyVars { + PROXY_HOST = "CODEQL_PROXY_HOST", + PROXY_PORT = "CODEQL_PROXY_PORT", + PROXY_CA_CERTIFICATE = "CODEQL_PROXY_CA_CERTIFICATE", + PROXY_URLS = "CODEQL_PROXY_URLS", +} + /** * Environment variables used by the CodeQL Action. * @@ -17,6 +27,18 @@ export enum EnvVar { */ CLI_VERBOSITY = "CODEQL_VERBOSITY", + /** + * Set by Default Setup to the base branch of the PR being analysed, if analysing a PR. + * This is needed because the `pull_request` context is not available for `dynamic` events. + */ + CODE_SCANNING_BASE_BRANCH = "CODE_SCANNING_BASE_BRANCH", + + /** + * Set by Default Setup to the full ref being analysed, if analysing a PR. + * This is needed because the `pull_request` context is not available for `dynamic` events. + */ + CODE_SCANNING_REF = "CODE_SCANNING_REF", + /** * `PersistedVersionInfo` for the CodeQL CLI, so later Actions steps can reuse it instead of * invoking `codeql version` again. @@ -66,7 +88,7 @@ export enum EnvVar { LOG_VERSION_DEPRECATION = "CODEQL_ACTION_DID_LOG_VERSION_DEPRECATION", /** UUID representing the current job run. */ - JOB_RUN_UUID = "JOB_RUN_UUID", + JOB_RUN_UUID = "CODEQL_ACTION_JOB_RUN_UUID", /** Status for the entire job, submitted to the status report in `init-post` */ JOB_STATUS = "CODEQL_ACTION_JOB_STATUS", @@ -83,6 +105,9 @@ export enum EnvVar { /** Whether to suppress the warning if the current CLI will soon be unsupported. */ SUPPRESS_DEPRECATED_SOON_WARNING = "CODEQL_ACTION_SUPPRESS_DEPRECATED_SOON_WARNING", + /** Used to dictate or persist the temporary directory used by the CodeQL Action. */ + TEMP = "CODEQL_ACTION_TEMP", + /** Whether to disable uploading SARIF results or status reports to the GitHub API */ TEST_MODE = "CODEQL_ACTION_TEST_MODE", @@ -160,3 +185,135 @@ export enum EnvVar { /** Used by Code Scanning Risk Assessment to communicate the assessment ID to the CodeQL Action. */ RISK_ASSESSMENT_ID = "CODEQL_ACTION_RISK_ASSESSMENT_ID", } + +/** + * Enumerates known GitHub Actions environment variables that we expect + * to be set in a GitHub Actions environment. + */ +export enum ActionsEnvVars { + GITHUB_ACTION_REPOSITORY = "GITHUB_ACTION_REPOSITORY", + GITHUB_API_URL = "GITHUB_API_URL", + GITHUB_EVENT_NAME = "GITHUB_EVENT_NAME", + GITHUB_EVENT_PATH = "GITHUB_EVENT_PATH", + GITHUB_JOB = "GITHUB_JOB", + GITHUB_REF = "GITHUB_REF", + GITHUB_REPOSITORY = "GITHUB_REPOSITORY", + GITHUB_RUN_ATTEMPT = "GITHUB_RUN_ATTEMPT", + GITHUB_RUN_ID = "GITHUB_RUN_ID", + GITHUB_SERVER_URL = "GITHUB_SERVER_URL", + GITHUB_SHA = "GITHUB_SHA", + GITHUB_WORKFLOW = "GITHUB_WORKFLOW", + GITHUB_WORKSPACE = "GITHUB_WORKSPACE", + RUNNER_ENVIRONMENT = "RUNNER_ENVIRONMENT", + RUNNER_NAME = "RUNNER_NAME", + RUNNER_OS = "RUNNER_OS", + RUNNER_TEMP = "RUNNER_TEMP", + RUNNER_TOOL_CACHE = "RUNNER_TOOL_CACHE", +} + +/** A type representing all known environment variables. */ +export type KnownEnvVar = EnvVar | ActionsEnvVars | RegistryProxyVars; + +/** + * Gets an environment variable, but throws an error if it is not set. + */ +function getRequiredEnvVar(env: NodeJS.ProcessEnv, paramName: string): string { + const value = env[paramName]; + if (value === undefined || value.length === 0) { + throw new Error(`${paramName} environment variable must be set`); + } + return value; +} + +/** + * Get an environment parameter, but throw an error if it is not set. + * + * @deprecated Use `getRequired` of a `ReadOnlyEnv` or `Env` instance instead. + */ +export function getRequiredEnvParam(paramName: string): string { + return getRequiredEnvVar(process.env, paramName); +} + +/** + * Gets an environment variable, but returns `undefined` if it is not set or empty. + */ +function getOptionalEnvVarFrom( + env: NodeJS.ProcessEnv, + paramName: string, +): string | undefined { + const value = env[paramName]; + if (value?.trim().length === 0) { + return undefined; + } + return value; +} + +/** + * Get an environment variable, but return `undefined` if it is not set or empty. + * + * @deprecated Use `getOptional` of a `ReadOnlyEnv` or `Env` instance instead. + */ +export function getOptionalEnvVar(paramName: string): string | undefined { + return getOptionalEnvVarFrom(process.env, paramName); +} + +/** + * An abstraction around read-only environment variables, to allow abstracting away from `process.env` + * in tests, while clearly signalling in regular code that the consumer of the `ReadOnlyEnv` instance + * will only read from it. + */ +export class ReadOnlyEnv { + constructor(protected readonly vars: Record) {} + + /** Clones the object while detaching the underlying environment from the original. */ + public clone(): this { + return Object.create(this, { vars: { value: { ...this.vars } } }) as this; + } + + /** Gets a copy of the underlying environment. */ + public get(): Record { + return { ...this.vars }; + } + + /** Tries to get the value for `name` and throws if there isn't one. */ + public getRequired(name: string): string { + return getRequiredEnvVar(this.vars, name); + } + + /** Gets the value for `name`, or `undefined` if it isn't set or empty. */ + public getOptional(name: string): string | undefined { + return getOptionalEnvVarFrom(this.vars, name); + } + + /** Gets the entries of the underlying `ProcessEnv`. */ + public entries(): Array<[string, T]> { + return Object.entries(this.vars); + } +} + +/** + * A wrapper around an environment, to allow abstracting away from `process.env` in tests. + * Use `ReadOnlyEnv` instead if you only plan to read from the environment. + * This type allows writing to the environment. + */ +export class Env< + T extends string | undefined = string | undefined, +> extends ReadOnlyEnv { + private changed: boolean = false; + + /** Sets an environment variable. */ + public set(name: string, value: T): void { + this.vars[name] = value; + this.changed = true; + } + + /** Gets a value indicating whether `set` was called at least once. */ + public hasChanged(): boolean { + return this.changed; + } +} + +/** Gets an `Env` instance for `env`, which is `process.env` by default. */ +export function getEnv(env: NodeJS.ProcessEnv = process.env): Env { + return new Env(env); +} diff --git a/src/error-messages.ts b/src/error-messages.ts index 578ec69733..bd32a6a04a 100644 --- a/src/error-messages.ts +++ b/src/error-messages.ts @@ -30,7 +30,7 @@ export function getInvalidConfigFileMessage( return `The configuration file "${configFile}" is invalid: ${messages.slice(0, 10).join(", ")}${andMore}`; } -export function getConfigFileRepoFormatInvalidMessage( +export function getConfigFileRepoOldFormatInvalidMessage( configFile: string, ): string { let error = `The configuration file "${configFile}" is not a supported remote file reference.`; @@ -39,6 +39,15 @@ export function getConfigFileRepoFormatInvalidMessage( return error; } +export function getConfigFileRepoFormatInvalidMessage( + configFile: string, +): string { + let error = `The configuration file "${configFile}" is not a supported remote file reference.`; + error += " Expected format [/][@][:]"; + + return error; +} + export function getConfigFileFormatInvalidMessage(configFile: string): string { return `The configuration file "${configFile}" could not be read`; } diff --git a/src/feature-flags.ts b/src/feature-flags.ts index f71ecab57b..fff7ef0440 100644 --- a/src/feature-flags.ts +++ b/src/feature-flags.ts @@ -70,9 +70,10 @@ export interface CodeQLDefaultVersionInfo { * Legacy features should end with `_enabled`. */ export enum Feature { + /** Allows supported properties of configuration files to be merged. */ + AllowMergeConfigFiles = "allow_merge_config_files", /** Controls whether we allow multiple values for the `analysis-kinds` input. */ AllowMultipleAnalysisKinds = "allow_multiple_analysis_kinds", - AllowToolcacheInput = "allow_toolcache_input", CleanupTrapCaches = "cleanup_trap_caches", /** Whether to allow the `config-file` input to be specified via a repository property. */ ConfigFileRepositoryProperty = "config_file_repository_property", @@ -121,11 +122,6 @@ export enum Feature { */ OverlayAnalysisMatchCodeqlVersionDryRun = "overlay_analysis_match_codeql_version_dry_run", OverlayAnalysisPython = "overlay_analysis_python", - /** - * Controls whether lower disk space requirements are used for overlay hardware checks. - * Has no effect if `OverlayAnalysisSkipResourceChecks` is enabled. - */ - OverlayAnalysisResourceChecksV2 = "overlay_analysis_resource_checks_v2", OverlayAnalysisRuby = "overlay_analysis_ruby", /** Controls whether hardware checks are skipped for overlay analysis. */ OverlayAnalysisSkipResourceChecks = "overlay_analysis_skip_resource_checks", @@ -134,9 +130,13 @@ export enum Feature { /** Controls whether overlay build failures on the default branch are stored in the Actions cache. */ OverlayAnalysisStatusSave = "overlay_analysis_status_save", QaTelemetryEnabled = "qa_telemetry_enabled", + /** Routes (some) API requests through the registry proxy. */ + ProxyApiRequests = "proxy_api_requests", /** Note that this currently only disables baseline file coverage information. */ SkipFileCoverageOnPrs = "skip_file_coverage_on_prs", StartProxyUseFeaturesRelease = "start_proxy_use_features_release", + /** Whether to allow the `tools` input to be specified via a repository property. */ + ToolsRepositoryProperty = "tools_repository_property", UploadOverlayDbToApi = "upload_overlay_db_to_api", ValidateDbConfig = "validate_db_config", } @@ -171,14 +171,14 @@ export type FeatureConfig = { }; export const featureConfig = { - [Feature.AllowMultipleAnalysisKinds]: { + [Feature.AllowMergeConfigFiles]: { defaultValue: false, - envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS", + envVar: "CODEQL_ACTION_ALLOW_MERGE_CONFIG_FILES", minimumVersion: undefined, }, - [Feature.AllowToolcacheInput]: { + [Feature.AllowMultipleAnalysisKinds]: { defaultValue: false, - envVar: "CODEQL_ACTION_ALLOW_TOOLCACHE_INPUT", + envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS", minimumVersion: undefined, }, [Feature.CleanupTrapCaches]: { @@ -349,11 +349,6 @@ export const featureConfig = { envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION_DRY_RUN", minimumVersion: undefined, }, - [Feature.OverlayAnalysisResourceChecksV2]: { - defaultValue: false, - envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_RESOURCE_CHECKS_V2", - minimumVersion: undefined, - }, [Feature.OverlayAnalysisStatusCheck]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_STATUS_CHECK", @@ -375,6 +370,11 @@ export const featureConfig = { legacyApi: true, minimumVersion: undefined, }, + [Feature.ProxyApiRequests]: { + defaultValue: false, + envVar: "CODEQL_ACTION_PROXY_API_REQUESTS", + minimumVersion: undefined, + }, [Feature.SkipFileCoverageOnPrs]: { defaultValue: false, envVar: "CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS", @@ -386,6 +386,11 @@ export const featureConfig = { envVar: "CODEQL_ACTION_START_PROXY_USE_FEATURES_RELEASE", minimumVersion: undefined, }, + [Feature.ToolsRepositoryProperty]: { + defaultValue: false, + envVar: "CODEQL_ACTION_TOOLS_REPOSITORY_PROPERTY", + minimumVersion: undefined, + }, [Feature.UploadOverlayDbToApi]: { defaultValue: false, envVar: "CODEQL_ACTION_UPLOAD_OVERLAY_DB_TO_API", diff --git a/src/feature-flags/properties.test.ts b/src/feature-flags/properties.test.ts index 66526b1fb2..d3094a8d1c 100644 --- a/src/feature-flags/properties.test.ts +++ b/src/feature-flags/properties.test.ts @@ -72,13 +72,17 @@ test.serial( ); test.serial("loadPropertiesFromApi loads known properties", async (t) => { + const knownProperties = [ + { property_name: "github-codeql-config-file", value: "owner/repo" }, + { property_name: "github-codeql-extra-queries", value: "+queries" }, + { property_name: "github-codeql-tools", value: "nightly" }, + ]; sinon.stub(api, "getRepositoryProperties").resolves({ headers: {}, status: 200, url: "", data: [ - { property_name: "github-codeql-config-file", value: "owner/repo" }, - { property_name: "github-codeql-extra-queries", value: "+queries" }, + ...knownProperties, { property_name: "unknown-property", value: "something" }, ] satisfies properties.GitHubPropertiesResponse, }); @@ -88,10 +92,12 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => { logger, mockRepositoryNwo, ); - t.deepEqual(response, { - "github-codeql-config-file": "owner/repo", - "github-codeql-extra-queries": "+queries", - }); + t.deepEqual( + response, + Object.fromEntries( + knownProperties.map((prop) => [prop.property_name, prop.value]), + ), + ); }); test.serial("loadPropertiesFromApi parses true boolean property", async (t) => { diff --git a/src/feature-flags/properties.ts b/src/feature-flags/properties.ts index e239c71947..4c888bd5ec 100644 --- a/src/feature-flags/properties.ts +++ b/src/feature-flags/properties.ts @@ -1,7 +1,10 @@ +import * as github from "@actions/github"; + import { isDynamicWorkflow } from "../actions-util"; import { getRepositoryProperties } from "../api-client"; import { Logger } from "../logging"; import { RepositoryNwo } from "../repository"; +import { Failure, getErrorMessage, Result, Success } from "../util"; /** The common prefix that we expect all of our repository properties to have. */ export const GITHUB_CODEQL_PROPERTY_PREFIX = "github-codeql-"; @@ -14,6 +17,7 @@ export enum RepositoryPropertyName { DISABLE_OVERLAY = "github-codeql-disable-overlay", EXTRA_QUERIES = "github-codeql-extra-queries", FILE_COVERAGE_ON_PRS = "github-codeql-file-coverage-on-prs", + TOOLS = "github-codeql-tools", } /** Parsed types of the known repository properties. */ @@ -22,6 +26,7 @@ export type AllRepositoryProperties = { [RepositoryPropertyName.DISABLE_OVERLAY]: boolean; [RepositoryPropertyName.EXTRA_QUERIES]: string; [RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: boolean; + [RepositoryPropertyName.TOOLS]: string; }; /** Parsed repository properties. */ @@ -33,6 +38,7 @@ export type RepositoryPropertyApiType = { [RepositoryPropertyName.DISABLE_OVERLAY]: string; [RepositoryPropertyName.EXTRA_QUERIES]: string; [RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: string; + [RepositoryPropertyName.TOOLS]: string; }; /** The type of functions which take the `value` from the API and try to convert it to the type we want. */ @@ -81,6 +87,7 @@ const repositoryPropertyParsers: { [RepositoryPropertyName.DISABLE_OVERLAY]: booleanProperty, [RepositoryPropertyName.EXTRA_QUERIES]: stringProperty, [RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: booleanProperty, + [RepositoryPropertyName.TOOLS]: stringProperty, }; /** @@ -230,3 +237,35 @@ const KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( function isKnownPropertyName(name: string): name is RepositoryPropertyName { return KNOWN_REPOSITORY_PROPERTY_NAMES.has(name); } + +/** + * Loads [repository properties](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization) if applicable. + */ +export async function loadRepositoryProperties( + repositoryNwo: RepositoryNwo, + logger: Logger, +): Promise> { + // See if we can skip loading repository properties early. In particular, + // repositories owned by users cannot have repository properties, so we can + // skip the API call entirely in that case. + const repositoryOwnerType = github.context.payload.repository?.owner.type; + logger.debug( + `Repository owner type is '${repositoryOwnerType ?? "unknown"}'.`, + ); + if (repositoryOwnerType === "User") { + logger.debug( + "Skipping loading repository properties because the repository is owned by a user and " + + "therefore cannot have repository properties.", + ); + return new Success({}); + } + + try { + return new Success(await loadPropertiesFromApi(logger, repositoryNwo)); + } catch (error) { + logger.warning( + `Failed to load repository properties: ${getErrorMessage(error)}`, + ); + return new Failure(error); + } +} diff --git a/src/init-action-post-helper.ts b/src/init-action-post-helper.ts index 23695b6d1c..7b7b056a1c 100644 --- a/src/init-action-post-helper.ts +++ b/src/init-action-post-helper.ts @@ -123,6 +123,7 @@ async function prepareFailedSarif( const category = `/language:${language}`; const checkoutPath = "."; const result = await generateFailedSarif( + logger, features, config, category, @@ -146,6 +147,7 @@ async function prepareFailedSarif( const checkoutPath = getCheckoutPathInputOrThrow(workflow, jobName, matrix); const result = await generateFailedSarif( + logger, features, config, category, @@ -156,6 +158,7 @@ async function prepareFailedSarif( } async function generateFailedSarif( + logger: Logger, features: FeatureEnablement, config: Config, category: string | undefined, @@ -163,7 +166,7 @@ async function generateFailedSarif( sarifFile?: string, ) { const databasePath = config.dbLocation; - const codeql = await getCodeQL(config.codeQLCmd); + const codeql = await getCodeQL(logger, config.codeQLCmd); // Set the filename for the SARIF file if not already set. if (sarifFile === undefined) { diff --git a/src/init-action-post.ts b/src/init-action-post.ts index b407cfb99e..2261b56ea6 100644 --- a/src/init-action-post.ts +++ b/src/init-action-post.ts @@ -75,7 +75,7 @@ async function run(startedAt: Date) { "Debugging artifacts are unavailable since the 'init' Action failed before it could produce any.", ); } else { - const codeql = await getCodeQL(config.codeQLCmd); + const codeql = await getCodeQL(logger, config.codeQLCmd); uploadFailedSarifResult = await initActionPostHelper.uploadFailureInfo( debugArtifacts.tryUploadAllAvailableDebugArtifacts, diff --git a/src/init-action.ts b/src/init-action.ts index ec711cdf0b..6b5ed392ef 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -2,14 +2,12 @@ import * as fs from "fs"; import * as path from "path"; import * as core from "@actions/core"; -import * as github from "@actions/github"; import * as io from "@actions/io"; import * as semver from "semver"; -import { v4 as uuidV4 } from "uuid"; +import { Action, ActionState, runInActions } from "./action-common"; import { FileCmdNotFoundError, - getActionsEnv, getActionVersion, getFileType, getOptionalInput, @@ -26,6 +24,7 @@ import { } from "./caching-utils"; import { CodeQL } from "./codeql"; import { getConfigFileInput } from "./config/file"; +import { ComputedInput, getToolsInput } from "./config/inputs"; import * as configUtils from "./config-utils"; import { DependencyCacheRestoreStatusReport, @@ -41,10 +40,7 @@ import { } from "./diagnostics"; import { EnvVar } from "./environment"; import { Feature, FeatureEnablement, initFeatures } from "./feature-flags"; -import { - loadPropertiesFromApi, - RepositoryProperties, -} from "./feature-flags/properties"; +import { loadRepositoryProperties } from "./feature-flags/properties"; import { checkInstallPython311, checkPacksForOverlayCompatibility, @@ -56,13 +52,13 @@ import { runDatabaseInitCluster, } from "./init"; import { JavaEnvVars, BuiltInLanguage } from "./languages"; -import { getActionsLogger, Logger, withGroupAsync } from "./logging"; +import { Logger, withGroupAsync } from "./logging"; import { downloadOverlayBaseDatabaseFromCache, OverlayBaseDatabaseDownloadStats, } from "./overlay/caching"; import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; -import { getRepositoryNwo, RepositoryNwo } from "./repository"; +import { getRepositoryNwo } from "./repository"; import { ToolsSource } from "./setup-codeql"; import { ActionName, @@ -73,9 +69,7 @@ import { createStatusReportBase, getActionsStatus, sendStatusReport, - sendUnhandledErrorStatusReport, } from "./status-report"; -import { ZstdAvailability } from "./tar"; import { ToolsDownloadStatusReport } from "./tools-download"; import { ToolsFeature } from "./tools-features"; import { getCombinedTracerConfig } from "./tracer-config"; @@ -95,10 +89,7 @@ import { checkActionVersion, getErrorMessage, BuildMode, - Result, getOptionalEnvVar, - Success, - Failure, } from "./util"; import { checkWorkflow } from "./workflow"; @@ -138,6 +129,7 @@ async function sendCompletedStatusReport( startedAt: Date, config: configUtils.Config | undefined, configFile: string | undefined, + toolsInput: ComputedInput | undefined, toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined, toolsFeatureFlagsValid: boolean | undefined, toolsSource: ToolsSource, @@ -166,12 +158,16 @@ async function sendCompletedStatusReport( const initStatusReport: InitStatusReport = { ...statusReportBase, - tools_input: getOptionalInput("tools") || "", + tools_input: toolsInput?.value || "", tools_resolved_version: toolsVersion, tools_source: toolsSource || ToolsSource.Unknown, workflow_languages: workflowLanguages || "", }; + if (toolsInput !== undefined) { + initStatusReport.computed_inputs.tools = toolsInput; + } + const initToolsDownloadFields: InitToolsDownloadFields = {}; if (toolsDownloadStatusReport?.downloadDurationMs !== undefined) { @@ -204,12 +200,14 @@ async function sendCompletedStatusReport( } } -async function run(startedAt: Date) { +async function run( + actionState: ActionState<["Base", "Logger", "Env", "Actions"]>, +) { // To capture errors appropriately, keep as much code within the try-catch as // possible, and only use safe functions outside. - const logger = getActionsLogger(); - const actionsEnv = getActionsEnv(); + const startedAt = actionState.startedAt; + const logger = actionState.logger; let apiDetails: GitHubApiCombinedDetails; let config: configUtils.Config | undefined; @@ -217,11 +215,11 @@ async function run(startedAt: Date) { let codeql: CodeQL; let features: FeatureEnablement; let sourceRoot: string; + let toolsInput: ComputedInput | undefined; let toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined; let toolsFeatureFlagsValid: boolean | undefined; let toolsSource: ToolsSource; let toolsVersion: string; - let zstdAvailability: ZstdAvailability | undefined; try { initializeEnvironment(getActionVersion()); @@ -256,23 +254,8 @@ async function run(startedAt: Date) { ); const repositoryProperties = repositoryPropertiesResult.orElse({}); - // Create a unique identifier for this run. - const jobRunUuid = uuidV4(); - logger.info(`Job run UUID is ${jobRunUuid}.`); - core.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid); - core.exportVariable(EnvVar.INIT_ACTION_HAS_RUN, "true"); - const useConfigFileProperty = await features.getValue( - Feature.ConfigFileRepositoryProperty, - ); - configFile = getConfigFileInput( - logger, - actionsEnv, - repositoryProperties, - useConfigFileProperty, - ); - // path.resolve() respects the intended semantics of source-root. If // source-root is relative, it is relative to the GITHUB_WORKSPACE. If // source-root is absolute, it is used as given. @@ -295,6 +278,14 @@ async function run(startedAt: Date) { ); } + // Compute the value of the `config-file` input. + const actionStateWithFeatures = { ...actionState, features }; + configFile = await getConfigFileInput( + actionStateWithFeatures, + repositoryProperties, + analysisKinds, + ); + // Send a status report indicating that an analysis is starting. await sendStartingStatusReport(startedAt, { analysisKinds }, logger); @@ -305,6 +296,12 @@ async function run(startedAt: Date) { ); } + // Get the computed `tools` input. + toolsInput = await getToolsInput( + actionStateWithFeatures, + repositoryProperties, + ); + const codeQLDefaultVersionInfo = await features.getEnabledDefaultCliVersions(gitHubVersion.type); toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid; @@ -315,7 +312,7 @@ async function run(startedAt: Date) { analysisKinds?.length === 1 && analysisKinds[0] === AnalysisKind.CodeScanning; const initCodeQLResult = await initCodeQL( - getOptionalInput("tools"), + toolsInput?.value, apiDetails, getTemporaryDirectory(), gitHubVersion.type, @@ -329,7 +326,6 @@ async function run(startedAt: Date) { toolsDownloadStatusReport = initCodeQLResult.toolsDownloadStatusReport; toolsVersion = initCodeQLResult.toolsVersion; toolsSource = initCodeQLResult.toolsSource; - zstdAvailability = initCodeQLResult.zstdAvailability; // Check the workflow for problems. If there are any problems, they are reported // to the workflow log. No exceptions are thrown. @@ -369,7 +365,7 @@ async function run(startedAt: Date) { repositoryProperties, ); - config = await initConfig(features, { + config = await initConfig(actionStateWithFeatures, { analysisKinds, languagesInput: getOptionalInput("languages"), queriesInput: getOptionalInput("queries"), @@ -500,22 +496,6 @@ async function run(startedAt: Date) { cleanupDatabaseClusterDirectory(config, logger); } - if (zstdAvailability) { - await recordZstdAvailability(config, zstdAvailability); - } - - // Log CodeQL download telemetry, if appropriate - if (toolsDownloadStatusReport) { - addNoLanguageDiagnostic( - config, - makeTelemetryDiagnostic( - "codeql-action/bundle-download-telemetry", - "CodeQL bundle download telemetry", - toolsDownloadStatusReport, - ), - ); - } - // Forward Go flags const goFlags = process.env["GOFLAGS"]; if (goFlags) { @@ -709,7 +689,6 @@ async function run(startedAt: Date) { sourceRoot, "Runner.Worker.exe", qlconfigFile, - logger, ); // To check custom query packs for compatibility with overlay analysis, we @@ -738,7 +717,6 @@ async function run(startedAt: Date) { sourceRoot, "Runner.Worker.exe", qlconfigFile, - logger, ); } @@ -781,6 +759,7 @@ async function run(startedAt: Date) { startedAt, config, undefined, // We only report config info on success. + toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, @@ -798,6 +777,7 @@ async function run(startedAt: Date) { startedAt, config, configFile, + toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, @@ -808,65 +788,13 @@ async function run(startedAt: Date) { ); } -/** - * Loads [repository properties](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization) if applicable. - */ -async function loadRepositoryProperties( - repositoryNwo: RepositoryNwo, - logger: Logger, -): Promise> { - // See if we can skip loading repository properties early. In particular, - // repositories owned by users cannot have repository properties, so we can - // skip the API call entirely in that case. - const repositoryOwnerType = github.context.payload.repository?.owner.type; - logger.debug( - `Repository owner type is '${repositoryOwnerType ?? "unknown"}'.`, - ); - if (repositoryOwnerType === "User") { - logger.debug( - "Skipping loading repository properties because the repository is owned by a user and " + - "therefore cannot have repository properties.", - ); - return new Success({}); - } - - try { - return new Success(await loadPropertiesFromApi(logger, repositoryNwo)); - } catch (error) { - logger.warning( - `Failed to load repository properties: ${getErrorMessage(error)}`, - ); - return new Failure(error); - } -} - -async function recordZstdAvailability( - config: configUtils.Config, - zstdAvailability: ZstdAvailability, -) { - addNoLanguageDiagnostic( - config, - makeTelemetryDiagnostic( - "codeql-action/zstd-availability", - "Zstandard availability", - zstdAvailability, - ), - ); -} +/** Defines the `init` Action. */ +const init: Action = { + name: ActionName.Init, + run, +}; export async function runWrapper() { - const startedAt = new Date(); - const logger = getActionsLogger(); - try { - await run(startedAt); - } catch (error) { - core.setFailed(`init action failed: ${getErrorMessage(error)}`); - await sendUnhandledErrorStatusReport( - ActionName.Init, - startedAt, - error, - logger, - ); - } + await runInActions(init); await checkForTimeout(); } diff --git a/src/init.test.ts b/src/init.test.ts index 88ad0c9b18..1f0d2c701c 100644 --- a/src/init.test.ts +++ b/src/init.test.ts @@ -8,6 +8,7 @@ import * as sinon from "sinon"; import * as actionsUtil from "./actions-util"; import { createStubCodeQL } from "./codeql"; +import { ActionsEnvVars } from "./environment"; import { Feature } from "./feature-flags"; import { checkPacksForOverlayCompatibility, @@ -84,7 +85,7 @@ for (const { runnerEnv, ErrorConstructor, message } of [ `cleanupDatabaseClusterDirectory throws a ${ErrorConstructor.name} when cleanup fails on ${runnerEnv} runner`, async (t) => { await withTmpDir(async (tmpDir: string) => { - process.env["RUNNER_ENVIRONMENT"] = runnerEnv; + process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = runnerEnv; const dbLocation = path.resolve(tmpDir, "dbs"); fs.mkdirSync(dbLocation, { recursive: true }); diff --git a/src/init.ts b/src/init.ts index 2533d9a894..c6a258e58c 100644 --- a/src/init.ts +++ b/src/init.ts @@ -7,6 +7,7 @@ import * as github from "@actions/github"; import * as io from "@actions/io"; import * as yaml from "js-yaml"; +import { ActionState } from "./action-common"; import { getOptionalInput, isAnalyzingPullRequest, @@ -29,7 +30,6 @@ import { import { BuiltInLanguage, Language } from "./languages"; import { Logger, withGroupAsync } from "./logging"; import { ToolsSource } from "./setup-codeql"; -import { ZstdAvailability } from "./tar"; import { ToolsDownloadStatusReport } from "./tools-download"; import * as util from "./util"; @@ -48,27 +48,21 @@ export async function initCodeQL( toolsDownloadStatusReport?: ToolsDownloadStatusReport; toolsSource: ToolsSource; toolsVersion: string; - zstdAvailability: ZstdAvailability; }> { logger.startGroup("Setup CodeQL tools"); - const { - codeql, - toolsDownloadStatusReport, - toolsSource, - toolsVersion, - zstdAvailability, - } = await setupCodeQL( - toolsInput, - apiDetails, - tempDir, - variant, - defaultCliVersion, - rawLanguages, - useOverlayAwareDefaultCliVersion, - features, - logger, - true, - ); + const { codeql, toolsDownloadStatusReport, toolsSource, toolsVersion } = + await setupCodeQL( + toolsInput, + apiDetails, + tempDir, + variant, + defaultCliVersion, + rawLanguages, + useOverlayAwareDefaultCliVersion, + features, + logger, + true, + ); await codeql.printVersion(); logger.endGroup(); return { @@ -76,16 +70,15 @@ export async function initCodeQL( toolsDownloadStatusReport, toolsSource, toolsVersion, - zstdAvailability, }; } export async function initConfig( - features: FeatureEnablement, + actionState: ActionState<["Logger", "Env", "FeatureFlags"]>, inputs: configUtils.InitConfigInputs, ): Promise { return await withGroupAsync("Load language configuration", async () => { - return await configUtils.initConfig(features, inputs); + return await configUtils.initConfig(actionState, inputs); }); } @@ -96,7 +89,6 @@ export async function runDatabaseInitCluster( sourceRoot: string, processName: string | undefined, qlconfigFile: string | undefined, - logger: Logger, ): Promise { fs.mkdirSync(config.dbLocation, { recursive: true }); await configUtils.wrapEnvironment( @@ -107,7 +99,6 @@ export async function runDatabaseInitCluster( sourceRoot, processName, qlconfigFile, - logger, ), ); } diff --git a/src/json/index.test.ts b/src/json/index.test.ts index 825bbc0e70..80edbedece 100644 --- a/src/json/index.test.ts +++ b/src/json/index.test.ts @@ -10,8 +10,8 @@ const testSchema = { requiredKey: json.string, }; -const optionalSchema = { - optionalKey: json.optional(json.string), +const optionalOrNullSchema = { + optionalKey: json.optionalOrNull(json.string), }; test("validateSchema - required properties are required", async (t) => { @@ -28,13 +28,36 @@ test("validateSchema - required properties are required", async (t) => { t.true(json.validateSchema(testSchema, { requiredKey: "foo" })); }); -test("validateSchema - optional properties are optional", async (t) => { +test("validateSchema - optionalOrNullSchema properties are optional or null", async (t) => { // Optional fields may be absent + t.true(json.validateSchema(optionalOrNullSchema, {})); + t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: undefined })); + t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: null })); + + // But, if present, should have the expected type + t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: 0 })); + t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: 123 })); + t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: false })); + t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: true })); + t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: [] })); + t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: {} })); + t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: "" })); + t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: "foo" })); +}); + +const optionalSchema = { + optionalKey: json.optional(json.string), +}; + +test("validateSchema - optional properties are optional", async (t) => { + // Optional fields may be absent or explicitly undefined t.true(json.validateSchema(optionalSchema, {})); t.true(json.validateSchema(optionalSchema, { optionalKey: undefined })); - t.true(json.validateSchema(optionalSchema, { optionalKey: null })); - // But, if present, should have the expected type + // But should reject null + t.false(json.validateSchema(optionalSchema, { optionalKey: null })); + + // And, if present, should have the expected type t.false(json.validateSchema(optionalSchema, { optionalKey: 0 })); t.false(json.validateSchema(optionalSchema, { optionalKey: 123 })); t.false(json.validateSchema(optionalSchema, { optionalKey: false })); @@ -44,3 +67,76 @@ test("validateSchema - optional properties are optional", async (t) => { t.true(json.validateSchema(optionalSchema, { optionalKey: "" })); t.true(json.validateSchema(optionalSchema, { optionalKey: "foo" })); }); + +const arraySchema = { + arrayKey: json.array(json.number), +}; + +test("validateSchema - validates arrays", async (t) => { + // Arrays of numeric elements are accepted. + t.true(json.validateSchema(arraySchema, { arrayKey: [] })); + t.true(json.validateSchema(arraySchema, { arrayKey: [4] })); + t.true(json.validateSchema(arraySchema, { arrayKey: [4, 8] })); + t.true(json.validateSchema(arraySchema, { arrayKey: [4, 8, 15] })); + + // Other array elements are not accepted. + t.false(json.validateSchema(arraySchema, { arrayKey: [4, 8, 15, "bar"] })); + t.false(json.validateSchema(arraySchema, { arrayKey: [4, 8, undefined] })); + t.false(json.validateSchema(arraySchema, { arrayKey: [4, 8, 15, null] })); +}); + +const objectSchema = { + objectKey: json.object(arraySchema), +}; + +test("validateSchema - validates objects", async (t) => { + // Objects of the given schema are accepted. + t.true(json.validateSchema(objectSchema, { objectKey: { arrayKey: [] } })); + t.true(json.validateSchema(objectSchema, { objectKey: { arrayKey: [4] } })); + + // Other values are not accepted. + t.false(json.validateSchema(objectSchema, {})); + t.false(json.validateSchema(objectSchema, { objectKey: [] })); + t.false(json.validateSchema(objectSchema, { objectKey: undefined })); + t.false(json.validateSchema(objectSchema, { objectKey: null })); + t.false(json.validateSchema(objectSchema, { objectKey: "foo" })); + t.false(json.validateSchema(objectSchema, { objectKey: 123 })); +}); + +const checkSchemaTestSchema = { + rootKey: json.object(objectSchema), +}; + +test("checkSchema - reports unknown keys", async (t) => { + const result = json.checkSchema(checkSchemaTestSchema, { + rootKey: { + objectKey: { + arrayKey: [], + }, + nestedExtraKey: "foo", + }, + extraKey: "bar", + }); + + t.true(result.valid); + t.deepEqual( + result.unknownKeys.sort(), + [".extraKey", ".rootKey.nestedExtraKey"].sort(), + ); +}); + +test("checkSchema - reports invalid keys", async (t) => { + const result = json.checkSchema(checkSchemaTestSchema, { + rootKey: { + objectKey: { + arrayKey: ["foo"], + }, + }, + }); + + t.false(result.valid); + t.deepEqual( + result.invalidKeys.sort(), + [".rootKey.objectKey.arrayKey[0]"].sort(), + ); +}); diff --git a/src/json/index.ts b/src/json/index.ts index 8a1b60a178..d8764ec478 100644 --- a/src/json/index.ts +++ b/src/json/index.ts @@ -30,6 +30,16 @@ export function isString(value: unknown): value is string { return typeof value === "string"; } +/** Asserts that `value` is a number. */ +export function isNumber(value: unknown): value is number { + return typeof value === "number"; +} + +/** Asserts that `value` is a boolean. */ +export function isBoolean(value: unknown): value is boolean { + return typeof value === "boolean"; +} + /** Asserts that `value` is either a string or undefined. */ export function isStringOrUndefined( value: unknown, @@ -43,28 +53,146 @@ export function isStringOrUndefined( */ export type Validator = { validate: (val: unknown) => val is T; + check: ( + val: unknown, + opts: CheckSchemaOptions, + path: string, + ) => CheckSchemaResult; required: boolean; }; +function defaultCheck( + validate: (val: unknown) => val is any, +): (arg: unknown) => CheckSchemaResult { + return (arg) => ({ unknownKeys: [], invalidKeys: [], valid: validate(arg) }); +} + +function makeValidator(validate: (arg: unknown) => arg is T) { + return { + validate, + check: defaultCheck(validate), + required: true, + } as const satisfies Validator; +} + /** Extracts `T` from `Validator`. */ export type UnwrapValidator = V extends Validator ? A : never; /** A validator for string fields in schemas. */ -export const string = { - validate: isString, - required: true, -} as const satisfies Validator; +export const string = makeValidator(isString); -/** Transforms a validator to be optional. */ -export function optional(validator: Validator) { +/** A validator for number fields in schemas. */ +export const number = makeValidator(isNumber); + +/** A validator for boolean fields in schemas. */ +export const boolean = makeValidator(isBoolean); + +/** A validator for arrays. */ +export function array(validator: Validator) { + const validate = (val: unknown) => { + return isArray(val) && val.every((e) => validator.validate(e)); + }; + return { + validate, + check: (val: unknown, opts: CheckSchemaOptions, path: string) => { + const result: CheckSchemaResult = successfulCheckSchema(); + + // The value must be an array. + if (!isArray(val)) { + result.valid = false; + return result; + } + + // Validate all elements of the array. + let index = 0; + for (const e of val) { + const elementPath = `${path}[${index}]`; + const eResult = validator.check(e, opts, `${elementPath}`); + + result.invalidKeys.push(...eResult.invalidKeys); + result.unknownKeys.push(...eResult.unknownKeys); + index++; + + if (!eResult.valid) { + result.valid = false; + + // Add the element path to `invalidKeys` if we didn't get + // any more specific ones from the element validator. + if (eResult.invalidKeys.length === 0) { + result.invalidKeys.push(elementPath); + } + + if (opts.failFast) { + return result; + } + + continue; + } + } + + return result; + }, + required: true, + } as const satisfies Validator; +} + +/** A validator for objects. */ +export function object< + S extends Schema, + T extends UnvalidatedObject = FromSchema, +>(schema: S) { + return { + validate: (val: unknown) => { + return isObject(val) && validateSchema(schema, val); + }, + check: (val, opts, path) => { + if (!isObject(val)) { + return invalidCheckSchema(); + } + return checkSchema(schema, val, opts, path); + }, + required: true, + } as const satisfies Validator; +} + +/** + * Transforms a validator to be optional, accepting `undefined` or `null` for an + * absent value. + */ +export function optionalOrNull(validator: Validator) { return { validate: (val: unknown) => { return val === undefined || val === null || validator.validate(val); }, + check: (val, opts, path) => { + if (val === undefined || val === null) { + return successfulCheckSchema(); + } + return validator.check(val, opts, path); + }, required: false, } as const satisfies Validator; } +/** + * Transforms a validator to be optional, accepting `undefined` for an absent + * value but, unlike `optionalOrNull`, rejecting `null`. + */ +export function optional(validator: Validator) { + return { + validate: (val: unknown): val is T | undefined => { + return val === undefined || validator.validate(val); + }, + check: (val, opts, path) => { + if (val === undefined) { + return successfulCheckSchema(); + } + return validator.check(val, opts, path); + }, + required: false, + } as const satisfies Validator; +} + /** Represents an arbitrary object schema. */ export type Schema = Record>; @@ -90,28 +218,150 @@ export type FromSchema = { * @param obj The object to validate. * @returns Asserts that `obj` is of the `schema`'s type if validation is successful. */ -export function validateSchema( +export function validateSchema< + S extends Schema, + T extends UnvalidatedObject = FromSchema, +>(schema: S, obj: UnvalidatedObject): obj is T { + const result = checkSchema(schema, obj, { failFast: true }); + return result.valid; +} + +/** + * Validates that `arr` is an array whose elements satisfy at least `elementSchema`. + * Additional keys are accepted in each element. + * + * @param elementSchema The schema to validate the elements against. + * @param arr The array to validate. + * @returns Asserts that `arr` has elements of `schema`'s type if validation is successful. + */ +export function validateArray< + S extends Schema, + T extends UnvalidatedArray = Array>, +>(elementSchema: S, arr: UnvalidatedArray): arr is T { + const elementValidator = object(elementSchema); + + return array(elementValidator).validate(arr); +} + +export interface CheckSchemaOptions { + /** Whether to stop validation after the first error. */ + failFast?: boolean; +} + +export interface CheckSchemaResult { + /** Whether the `obj` satisfies the schema. */ + valid: boolean; + /** Unknown keys that were found during validation. */ + unknownKeys: string[]; + /** Known keys that failed validation. */ + invalidKeys: string[]; +} + +/** + * Convenience function to produce a `CheckSchemaResult` where `valid: true`. + */ +function successfulCheckSchema(): CheckSchemaResult { + return { + valid: true, + unknownKeys: [], + invalidKeys: [], + }; +} + +/** + * Convenience function to produce a `CheckSchemaResult` where `valid: false`. + */ +function invalidCheckSchema(): CheckSchemaResult { + return { + valid: false, + unknownKeys: [], + invalidKeys: [], + }; +} + +export function checkSchema( schema: S, obj: UnvalidatedObject, -): obj is FromSchema { + options: CheckSchemaOptions = {}, + path: string = "", +): CheckSchemaResult { + const result: CheckSchemaResult = successfulCheckSchema(); + + // Track the set of input keys. We remove keys from this set as we recognise them + // during validation. + const inputKeys = new Set(Object.keys(obj)); + + // Track keys that have failed validation, starting with the empty set. + const invalidKeys = new Set(); + + // Loop through all keys in the object schema and validate that the given object + // satisfies the schema key. for (const [key, validator] of Object.entries(schema)) { const hasKey = key in obj; + // Remove key from set of unrecognised keys. + inputKeys.delete(key); + + // Add the key to the set of invalid keys. We remove it later once + // it passes validation. + invalidKeys.add(key); + // If the property is required, but absent, fail. if (validator.required && !hasKey) { - return false; + result.valid = false; + + if (options.failFast) { + break; + } + continue; } // If the property is required, but undefined or null, fail. if (validator.required && (obj[key] === undefined || obj[key] === null)) { - return false; + result.valid = false; + + if (options.failFast) { + break; + } + continue; } // If the property is present, validate it. - if (hasKey && !validator.validate(obj[key])) { - return false; + if (hasKey) { + const checkResult = validator.check(obj[key], options, `${path}.${key}`); + + result.unknownKeys.push(...checkResult.unknownKeys); + result.invalidKeys.push(...checkResult.invalidKeys); + + // If we have invalid keys from the validator, then that means that + // we have a more specific key than `key`. Remove `key` from the results. + if (checkResult.invalidKeys.length > 0) { + invalidKeys.delete(key); + } + + if (!checkResult.valid) { + result.valid = false; + + if (options.failFast) { + break; + } + continue; + } } + + // If we reach this point, the key has been successfully validated. + invalidKeys.delete(key); + } + + // If there are any remaining keys in `inputKeys`, add them to `unknownKeys`. + for (const remainingKey of inputKeys) { + result.unknownKeys.push(`${path}.${remainingKey}`); + } + + // If there are any remaining keys in `invalidKeys`, add them to the result. + for (const invalidKey of invalidKeys) { + result.invalidKeys.push(`${path}.${invalidKey}`); } - return true; + return result; } diff --git a/src/resolve-environment.ts b/src/resolve-environment.ts index d202efa83e..3a1a6ca6bf 100644 --- a/src/resolve-environment.ts +++ b/src/resolve-environment.ts @@ -9,7 +9,7 @@ export async function runResolveBuildEnvironment( ) { logger.startGroup(`Attempting to resolve build environment for ${language}`); - const codeql = await getCodeQL(cmd); + const codeql = await getCodeQL(logger, cmd); if (workingDir !== undefined) { logger.info(`Using ${workingDir} as the working directory.`); diff --git a/src/setup-codeql-action.ts b/src/setup-codeql-action.ts index d3e0e7dbcc..7873449f9c 100644 --- a/src/setup-codeql-action.ts +++ b/src/setup-codeql-action.ts @@ -1,6 +1,6 @@ import * as core from "@actions/core"; -import { v4 as uuidV4 } from "uuid"; +import { Action, ActionState, runInActions } from "./action-common"; import { getActionVersion, getOptionalInput, @@ -10,11 +10,13 @@ import { import { AnalysisKind, getAnalysisKinds } from "./analyses"; import { getGitHubVersion } from "./api-client"; import { CodeQL } from "./codeql"; +import { ComputedInput, getToolsInput } from "./config/inputs"; import { getRawLanguagesNoAutodetect } from "./config-utils"; import { EnvVar } from "./environment"; import { initFeatures } from "./feature-flags"; +import { loadRepositoryProperties } from "./feature-flags/properties"; import { initCodeQL } from "./init"; -import { getActionsLogger, Logger } from "./logging"; +import { Logger } from "./logging"; import { getRepositoryNwo } from "./repository"; import { ToolsSource } from "./setup-codeql"; import { @@ -24,7 +26,6 @@ import { createStatusReportBase, getActionsStatus, sendStatusReport, - sendUnhandledErrorStatusReport, } from "./status-report"; import { ToolsDownloadStatusReport } from "./tools-download"; import { @@ -36,7 +37,6 @@ import { ConfigurationError, wrapError, checkActionVersion, - getErrorMessage, } from "./util"; /** @@ -44,6 +44,7 @@ import { */ async function sendCompletedStatusReport( startedAt: Date, + toolsInput: ComputedInput | undefined, toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined, toolsFeatureFlagsValid: boolean | undefined, toolsSource: ToolsSource, @@ -68,12 +69,16 @@ async function sendCompletedStatusReport( const initStatusReport: InitStatusReport = { ...statusReportBase, - tools_input: getOptionalInput("tools") || "", + tools_input: toolsInput?.value || "", tools_resolved_version: toolsVersion, tools_source: toolsSource || ToolsSource.Unknown, workflow_languages: "", }; + if (toolsInput !== undefined) { + initStatusReport.computed_inputs.tools = toolsInput; + } + const initToolsDownloadFields: InitToolsDownloadFields = {}; if (toolsDownloadStatusReport?.downloadDurationMs !== undefined) { @@ -88,13 +93,15 @@ async function sendCompletedStatusReport( } /** The main behaviour of this action. */ -async function run(startedAt: Date): Promise { +async function run( + actionState: ActionState<["Base", "Logger", "Env", "Actions"]>, +): Promise { // To capture errors appropriately, keep as much code within the try-catch as // possible, and only use safe functions outside. - - const logger = getActionsLogger(); + const { logger, startedAt } = actionState; let codeql: CodeQL; + let toolsInput: ComputedInput | undefined; let toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined; let toolsFeatureFlagsValid: boolean | undefined; let toolsSource: ToolsSource; @@ -123,9 +130,14 @@ async function run(startedAt: Date): Promise { logger, ); - const jobRunUuid = uuidV4(); - logger.info(`Job run UUID is ${jobRunUuid}.`); - core.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid); + // Fetch the values of known repository properties that affect us. + const repositoryPropertiesResult = await loadRepositoryProperties( + repositoryNwo, + logger, + ); + const repositoryProperties = repositoryPropertiesResult.orElse({}); + + const actionStateWithFeatures = { ...actionState, features }; const statusReportBase = await createStatusReportBase( ActionName.SetupCodeQL, @@ -138,6 +150,13 @@ async function run(startedAt: Date): Promise { if (statusReportBase !== undefined) { await sendStatusReport(statusReportBase); } + + // Get the computed `tools` input. + toolsInput = await getToolsInput( + actionStateWithFeatures, + repositoryProperties, + ); + const codeQLDefaultVersionInfo = await features.getEnabledDefaultCliVersions(gitHubVersion.type); toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid; @@ -146,7 +165,7 @@ async function run(startedAt: Date): Promise { ); const analysisKinds = await getAnalysisKinds(logger, features); const initCodeQLResult = await initCodeQL( - getOptionalInput("tools"), + toolsInput?.value, apiDetails, getTemporaryDirectory(), gitHubVersion.type, @@ -187,6 +206,7 @@ async function run(startedAt: Date): Promise { await sendCompletedStatusReport( startedAt, + toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, @@ -195,20 +215,14 @@ async function run(startedAt: Date): Promise { ); } +/** Defines the `setup-codeql` Action. */ +const setupCodeQL: Action = { + name: ActionName.SetupCodeQL, + run, +}; + /** Run the action and catch any unhandled errors. */ export async function runWrapper(): Promise { - const startedAt = new Date(); - const logger = getActionsLogger(); - try { - await run(startedAt); - } catch (error) { - core.setFailed(`setup-codeql action failed: ${getErrorMessage(error)}`); - await sendUnhandledErrorStatusReport( - ActionName.SetupCodeQL, - startedAt, - error, - logger, - ); - } + await runInActions(setupCodeQL); await checkForTimeout(); } diff --git a/src/setup-codeql.test.ts b/src/setup-codeql.test.ts index 49d4d66aad..219e39984c 100644 --- a/src/setup-codeql.test.ts +++ b/src/setup-codeql.test.ts @@ -7,6 +7,7 @@ import * as sinon from "sinon"; import * as actionsUtil from "./actions-util"; import * as api from "./api-client"; +import { EnvVar } from "./environment"; import { Feature } from "./feature-flags"; import { getRunnerLogger } from "./logging"; import { getCacheRestoreKeyPrefix } from "./overlay/caching"; @@ -116,30 +117,69 @@ test.serial( }, ); -test.serial( - "getCodeQLSource correctly returns bundled CLI version when tools == linked", - async (t) => { - const features = createFeatures([]); - - await withTmpDir(async (tmpDir) => { - setupActionsVars(tmpDir, tmpDir); - const source = await setupCodeql.getCodeQLSource( - "linked", - SAMPLE_DEFAULT_CLI_VERSION, - undefined, // rawLanguages - false, // useOverlayAwareDefaultCliVersion - SAMPLE_DOTCOM_API_DETAILS, - GitHubVariant.DOTCOM, - false, - features, - getRunnerLogger(true), - ); - - t.is(source.toolsVersion, LINKED_CLI_VERSION.cliVersion); - t.is(source.sourceType, "download"); - }); +const LINKED_BUNDLE_TEST_CASES = [ + { + platform: "linux", + tarSupportsZstd: true, + expectedBundleName: "codeql-bundle-linux64.tar.zst", + expectedCompressionMethod: "zstd", }, -); + { + platform: "darwin", + tarSupportsZstd: true, + expectedBundleName: "codeql-bundle-osx64.tar.zst", + expectedCompressionMethod: "zstd", + }, + { + platform: "win32", + tarSupportsZstd: true, + expectedBundleName: "codeql-bundle-win64.tar.gz", + expectedCompressionMethod: "gzip", + }, + { + platform: "linux", + tarSupportsZstd: false, + expectedBundleName: "codeql-bundle-linux64.tar.gz", + expectedCompressionMethod: "gzip", + }, +] as const; + +for (const { + platform, + tarSupportsZstd, + expectedBundleName, + expectedCompressionMethod, +} of LINKED_BUNDLE_TEST_CASES) { + test.serial( + `getCodeQLSource selects ${expectedBundleName} for linked tools`, + async (t) => { + const features = createFeatures([]); + sinon.stub(process, "platform").value(platform); + + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + const source = await setupCodeql.getCodeQLSource( + "linked", + SAMPLE_DEFAULT_CLI_VERSION, + undefined, // rawLanguages + false, // useOverlayAwareDefaultCliVersion + SAMPLE_DOTCOM_API_DETAILS, + GitHubVariant.DOTCOM, + tarSupportsZstd, + features, + getRunnerLogger(true), + ); + + t.is(source.toolsVersion, LINKED_CLI_VERSION.cliVersion); + t.is(source.sourceType, "download"); + if (source.sourceType === "download") { + t.is(source.compressionMethod, expectedCompressionMethod); + t.true(source.codeqlURL.endsWith(`/${expectedBundleName}`)); + } + }); + }, + ); +} test.serial( "getCodeQLSource correctly returns bundled CLI version when tools == latest", @@ -193,12 +233,7 @@ test.serial( sinon.stub(setupCodeql, "downloadCodeQL").resolves({ codeqlFolder: "codeql", statusReport: { - combinedDurationMs: 500, - compressionMethod: "gzip", downloadDurationMs: 200, - extractionDurationMs: 300, - streamExtraction: false, - toolsUrl: "toolsUrl", }, toolsVersion: LINKED_CLI_VERSION.cliVersion, }); @@ -250,12 +285,7 @@ test.serial( sinon.stub(setupCodeql, "downloadCodeQL").resolves({ codeqlFolder: "codeql", statusReport: { - combinedDurationMs: 500, - compressionMethod: "gzip", downloadDurationMs: 200, - extractionDurationMs: 300, - streamExtraction: false, - toolsUrl: bundleUrl, }, toolsVersion: expectedVersion, }); @@ -421,7 +451,7 @@ test.serial( async (t) => { const loggedMessages: LoggedMessage[] = []; const logger = getRecordingLogger(loggedMessages); - const features = createFeatures([Feature.AllowToolcacheInput]); + const features = createFeatures([]); const latestToolcacheVersion = "3.2.1"; const latestVersionPath = "/path/to/latest"; @@ -550,7 +580,7 @@ const toolcacheInputFallbackMacro = makeMacro({ toolcacheInputFallbackMacro.serial( "the toolcache doesn't have a CodeQL CLI when tools == toolcache", - [Feature.AllowToolcacheInput], + [], { GITHUB_EVENT_NAME: "dynamic" }, [], [ @@ -561,7 +591,7 @@ toolcacheInputFallbackMacro.serial( toolcacheInputFallbackMacro.serial( "the workflow trigger is not `dynamic`", - [Feature.AllowToolcacheInput], + [], { GITHUB_EVENT_NAME: "pull_request" }, [], [ @@ -569,14 +599,6 @@ toolcacheInputFallbackMacro.serial( ], ); -toolcacheInputFallbackMacro.serial( - "the feature flag is not enabled", - [], - { GITHUB_EVENT_NAME: "dynamic" }, - [], - [`Ignoring 'tools: toolcache' because the feature is not enabled.`], -); - test.serial( 'tryGetTagNameFromUrl extracts the right tag name for a repo name containing "codeql-bundle"', (t) => { @@ -637,8 +659,8 @@ test.serial( async (t) => { await withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); - process.env["CODE_SCANNING_REF"] = "refs/heads/feature-branch"; - process.env["CODE_SCANNING_BASE_BRANCH"] = "main"; + process.env[EnvVar.CODE_SCANNING_REF] = "refs/heads/feature-branch"; + process.env[EnvVar.CODE_SCANNING_BASE_BRANCH] = "main"; sinon.stub(api, "getAutomationID").resolves("test/"); const listStub = sinon.stub(api, "listActionsCaches").resolves([ diff --git a/src/setup-codeql.ts b/src/setup-codeql.ts index 3db0b6ca4d..8d374585aa 100644 --- a/src/setup-codeql.ts +++ b/src/setup-codeql.ts @@ -533,11 +533,7 @@ export async function getCodeQLSource( // We only allow `toolsInput === "toolcache"` for `dynamic` events. In general, using `toolsInput === "toolcache"` // can lead to alert wobble and so it shouldn't be used for an analysis where results are intended to be uploaded. // We also allow this in test mode. - const allowToolcacheValueFF = await features.getValue( - Feature.AllowToolcacheInput, - ); - const allowToolcacheValue = - allowToolcacheValueFF && (isDynamicWorkflow() || util.isInTestMode()); + const allowToolcacheValue = isDynamicWorkflow() || util.isInTestMode(); if (allowToolcacheValue) { // If `toolsInput === "toolcache"`, try to find the latest version of the CLI that's available in the toolcache // and use that. We perform this check here since we can set `cliVersion` directly and don't want to default to @@ -558,15 +554,9 @@ export async function getCodeQLSource( `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...`, ); } else { - if (allowToolcacheValueFF) { - logger.warning( - `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.`, - ); - } else { - logger.info( - `Ignoring 'tools: ${toolsInput}' because the feature is not enabled.`, - ); - } + logger.warning( + `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.`, + ); } const version = await resolveDefaultCliVersion( @@ -921,7 +911,6 @@ interface SetupCodeQLResult { toolsDownloadStatusReport?: ToolsDownloadStatusReport; toolsSource: ToolsSource; toolsVersion: string; - zstdAvailability: tar.ZstdAvailability; } /** @@ -1005,7 +994,6 @@ export async function setupCodeQLBundle( toolsDownloadStatusReport, toolsSource, toolsVersion, - zstdAvailability, }; } diff --git a/src/start-proxy-action.ts b/src/start-proxy-action.ts index 3e376ec64f..e8b89732f7 100644 --- a/src/start-proxy-action.ts +++ b/src/start-proxy-action.ts @@ -3,11 +3,12 @@ import * as path from "path"; import * as core from "@actions/core"; +import { Action, ActionState, runInActions } from "./action-common"; import * as actionsUtil from "./actions-util"; import { getGitHubVersion } from "./api-client"; import { FeatureEnablement, initFeatures } from "./feature-flags"; import { BuiltInLanguage, parseBuiltInLanguage } from "./languages"; -import { getActionsLogger, Logger } from "./logging"; +import { Logger } from "./logging"; import { getRepositoryNwo } from "./repository"; import { credentialToStr, @@ -23,14 +24,14 @@ import { import { generateCertificateAuthority } from "./start-proxy/ca"; import { checkProxyEnvironment } from "./start-proxy/environment"; import { checkConnections } from "./start-proxy/reachability"; -import { ActionName, sendUnhandledErrorStatusReport } from "./status-report"; +import { ActionName } from "./status-report"; import * as util from "./util"; -async function run(startedAt: Date) { +async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) { // To capture errors appropriately, keep as much code within the try-catch as // possible, and only use safe functions outside. - - const logger = getActionsLogger(); + const startedAt = action.startedAt; + const logger = action.logger; let features: FeatureEnablement | undefined; let language: BuiltInLanguage | undefined; @@ -122,21 +123,15 @@ async function run(startedAt: Date) { } } -export async function runWrapper() { - const startedAt = new Date(); - const logger = getActionsLogger(); +/** Defines the `start-proxy` Action. */ +const startProxyAction: Action = { + name: ActionName.StartProxy, + run, + transformTelemetryError: getSafeErrorMessage, +}; - try { - await run(startedAt); - } catch (error) { - core.setFailed(`start-proxy action failed: ${util.getErrorMessage(error)}`); - await sendUnhandledErrorStatusReport( - ActionName.StartProxy, - startedAt, - getSafeErrorMessage(util.wrapError(error)), - logger, - ); - } +export async function runWrapper() { + await runInActions(startProxyAction); } async function startProxy( diff --git a/src/start-proxy.test.ts b/src/start-proxy.test.ts index 6a905f16b7..ee953798b8 100644 --- a/src/start-proxy.test.ts +++ b/src/start-proxy.test.ts @@ -120,9 +120,20 @@ const mixedCredentials = [ { type: "maven_repository", host: "maven.pkg.github.com", token: "def" }, { type: "nuget_feed", host: "nuget.pkg.github.com", token: "ghi" }, { type: "goproxy_server", host: "goproxy.example.com", token: "jkl" }, - { type: "git_source", host: "github.com/github", token: "mno" }, ]; +const gitSourceCredential = { + type: "git_source", + host: "github.com/github", + token: "mno", +}; + +const dockerRegistryCredential = { + type: "docker_registry", + host: "https://registry.example.com", + token: "pqr", +}; + test("getCredentials prefers registriesCredentials over registrySecrets", async (t) => { const registryCredentials = Buffer.from( JSON.stringify([ @@ -241,7 +252,7 @@ test("getCredentials returns all for a language when specified", async (t) => { const credentials = startProxyExports.getCredentials( getRunnerLogger(true), undefined, - toEncodedJSON(mixedCredentials), + toEncodedJSON([...mixedCredentials, gitSourceCredential]), BuiltInLanguage.go, ); t.is(credentials.length, 2); @@ -284,7 +295,7 @@ test("getCredentials returns all maven_repositories for Java when specified", as host: "maven2.pkg.github.com", token: "token2", }, - { type: "git_source", host: "github.com/github", token: "mno" }, + { type: "goproxy_server", host: "github.com/github", token: "mno" }, ]; const credentials = startProxyExports.getCredentials( @@ -624,8 +635,12 @@ test("getCredentials validates 'replaces-base' correctly", async (t) => { ); }); -test("getCredentials returns no credentials for Actions", async (t) => { - const credentialsInput = toEncodedJSON(mixedCredentials); +test("getCredentials returns only ALWAYS_ENABLED_REGISTRY_TYPE credentials for Actions", async (t) => { + const credentialsInput = toEncodedJSON([ + ...mixedCredentials, + gitSourceCredential, + dockerRegistryCredential, + ]); const credentials = startProxyExports.getCredentials( getRunnerLogger(true), @@ -633,7 +648,41 @@ test("getCredentials returns no credentials for Actions", async (t) => { credentialsInput, BuiltInLanguage.actions, ); - t.deepEqual(credentials, []); + + for (const credential of credentials) { + t.true( + startProxyExports.ALWAYS_ENABLED_REGISTRY_TYPE.some( + (ty) => ty === credential.type, + ), + ); + } +}); + +test("getCredentials always returns ALWAYS_ENABLED_REGISTRY_TYPE credentials for all languages", async (t) => { + const alwaysEnabledCredentials: startProxyExports.Credential[] = []; + + for (const alwaysEnabled of startProxyExports.ALWAYS_ENABLED_REGISTRY_TYPE) { + alwaysEnabledCredentials.push({ + type: alwaysEnabled, + host: `host-${alwaysEnabled}`, + token: `bar-${alwaysEnabled}`, + url: `url-${alwaysEnabled}`, + }); + } + + const credentialsInput = toEncodedJSON(alwaysEnabledCredentials); + + // Test all languages. + for (const language of Object.values(BuiltInLanguage)) { + const credentials = startProxyExports.getCredentials( + getRunnerLogger(true), + undefined, + credentialsInput, + language, + ); + + t.deepEqual(credentials, alwaysEnabledCredentials); + } }); function mockGetApiClient(endpoints: any) { diff --git a/src/start-proxy.ts b/src/start-proxy.ts index 6b956d6473..caa1b3054a 100644 --- a/src/start-proxy.ts +++ b/src/start-proxy.ts @@ -83,12 +83,6 @@ export class StartProxyError extends Error { } } -interface StartProxyStatus extends StatusReportBase { - // A comma-separated list of registry types which are configured for CodeQL. - // This only includes registry types we support, not all that are configured. - registry_types: string; -} - /** * Sends a status report for the `start-proxy` action indicating a successful outcome. * @@ -112,7 +106,7 @@ export async function sendSuccessStatusReport( logger, ); if (statusReportBase !== undefined) { - const statusReport: StartProxyStatus = { + const statusReport: StatusReportBase = { ...statusReportBase, registry_types: registry_types.join(","), }; @@ -187,9 +181,19 @@ function isPAT(value: string) { ]); } +/** + * A list of always-enabled registry types. The registry types in this list are always + * enabled, because generic CodeQL workflow components may use them rather than just + * language-specific components. + */ +export const ALWAYS_ENABLED_REGISTRY_TYPE = [ + "git_source", + "docker_registry", +] as const; + type RegistryMapping = Partial>; -const LANGUAGE_TO_REGISTRY_TYPE: Required = { +export const LANGUAGE_TO_REGISTRY_TYPE: Required = { actions: [], cpp: [], java: ["maven_repository"], @@ -233,9 +237,11 @@ function getRegistryAddress( } } -// getCredentials returns registry credentials from action inputs. -// It prefers `registries_credentials` over `registry_secrets`. -// If neither is set, it returns an empty array. +/** + * Returns registry credentials from action inputs. + * It prefers `registriesCredentials` over `registrySecrets`. + * If neither is set, it returns an empty array. + */ export function getCredentials( logger: Logger, registrySecrets: string | undefined, @@ -291,8 +297,11 @@ export function getCredentials( const address = getRegistryAddress(e); // Filter credentials based on language if specified. `type` is the registry type. - // E.g., "maven_feed" for Java/Kotlin, "nuget_repository" for C#. + // E.g., "maven_repository" for Java/Kotlin, "nuget_feed" for C#. + // We always allow types in `ALWAYS_ENABLED_REGISTRY_TYPE` since they can be used by + // other parts of the workflow. if ( + !ALWAYS_ENABLED_REGISTRY_TYPE.some((t) => t === e.type) && registryTypeForLanguage && !registryTypeForLanguage.some((t) => t === e.type) ) { diff --git a/src/start-proxy/types.ts b/src/start-proxy/types.ts index 13a4ce0e8f..17803e9126 100644 --- a/src/start-proxy/types.ts +++ b/src/start-proxy/types.ts @@ -12,7 +12,7 @@ export type RawCredential = UnvalidatedObject; /** A schema for credential objects with a username. */ export const usernameSchema = { /** The username needed to authenticate to the package registry, if any. */ - username: json.optional(json.string), + username: json.optionalOrNull(json.string), } as const satisfies json.Schema; /** Usernames may be present for both authentication with tokens or passwords. */ @@ -29,7 +29,7 @@ export function hasUsername(config: AuthConfig): config is Username { /** A schema for credential objects with a username and password. */ export const usernamePasswordSchema = { /** The password needed to authenticate to the package registry, if any. */ - password: json.optional(json.string), + password: json.optionalOrNull(json.string), ...usernameSchema, } as const satisfies json.Schema; @@ -52,7 +52,7 @@ export function hasUsernameAndPassword( /** A schema for credential objects for token-based authentication. */ export const tokenSchema = { /** The token needed to authenticate to the package registry, if any. */ - token: json.optional(json.string), + token: json.optionalOrNull(json.string), ...usernameSchema, } as const satisfies json.Schema; @@ -100,7 +100,7 @@ export const awsConfigSchema = { "role-name": json.string, domain: json.string, "domain-owner": json.string, - audience: json.optional(json.string), + audience: json.optionalOrNull(json.string), } as const satisfies json.Schema; /** Configuration for AWS OIDC. */ @@ -116,8 +116,8 @@ export function isAWSConfig( /** A schema for JFrog OIDC configurations. */ export const jfrogConfigSchema = { "jfrog-oidc-provider-name": json.string, - audience: json.optional(json.string), - "identity-mapping-name": json.optional(json.string), + audience: json.optionalOrNull(json.string), + "identity-mapping-name": json.optionalOrNull(json.string), } as const satisfies json.Schema; /** Configuration for JFrog OIDC. */ @@ -150,8 +150,8 @@ export function isCloudsmithConfig( /** A schema for GCP OIDC configurations. */ export const gcpConfigSchema = { "workload-identity-provider": json.string, - "service-account": json.optional(json.string), - audience: json.optional(json.string), + "service-account": json.optionalOrNull(json.string), + audience: json.optionalOrNull(json.string), } as const satisfies json.Schema; /** Configuration for GCP OIDC. */ @@ -254,13 +254,19 @@ export function credentialToStr(credential: Credential): string { return result; } -/** A package registry is identified by its type and address. */ -export type Registry = { +/** The schema for `RegistryBase` objects. */ +export const registryBaseSchema = { /** The type of the package registry. */ - type: string; + type: json.string, /** Whether the registry replaces the base registry for the ecosystem. */ - "replaces-base"?: boolean; -} & Address; + "replaces-base": json.optional(json.boolean), +} as const satisfies json.Schema; + +/** Information about a registry, other than its address. */ +export type RegistryBase = json.FromSchema; + +/** A package registry is identified by its type and address. */ +export type Registry = RegistryBase & Address; // If a registry has an `url`, then that takes precedence over the `host` which may or may // not be defined. diff --git a/src/status-report.test.ts b/src/status-report.test.ts index 52132b7649..2b763da700 100644 --- a/src/status-report.test.ts +++ b/src/status-report.test.ts @@ -1,17 +1,21 @@ import test from "ava"; import * as sinon from "sinon"; +import * as uuid from "uuid"; import * as actionsUtil from "./actions-util"; import { Config } from "./config-utils"; -import { EnvVar } from "./environment"; +import { EnvVar, RegistryProxyVars } from "./environment"; import { BuiltInLanguage } from "./languages"; import { getRunnerLogger } from "./logging"; import { ToolsSource } from "./setup-codeql"; +import type { Registry } from "./start-proxy"; import { ActionName, createInitWithConfigStatusReport, createStatusReportBase, getActionsStatus, + getRegistryTypesFromEnv, + getJobUUID, InitStatusReport, InitWithConfigStatusReport, } from "./status-report"; @@ -20,11 +24,106 @@ import { setupActionsVars, createTestConfig, makeMacro, + getTestEnv, + RecordingLogger, + callee, } from "./testing-utils"; import { BuildMode, ConfigurationError, withTmpDir, wrapError } from "./util"; setupTests(test); +test("getRegistryTypesFromEnv - gets unique registry types from environment", async (t) => { + const logger = new RecordingLogger(true); + const env = getTestEnv({ + [RegistryProxyVars.PROXY_URLS]: JSON.stringify([ + { type: "git_source", url: "https://example.com" }, + { type: "git_source", url: "https://github.com" }, + { type: "docker_registry", url: "https://registry.example.com" }, + ] satisfies Array>), + }); + + const result = getRegistryTypesFromEnv(logger, env); + t.deepEqual(result, ["git_source", "docker_registry"].sort().join(",")); +}); + +test("getRegistryTypesFromEnv - returns undefined if the env var is not set", async (t) => { + const logger = new RecordingLogger(true); + const env = getTestEnv({}); + + const result = getRegistryTypesFromEnv(logger, env); + t.is(result, undefined); +}); + +test("getRegistryTypesFromEnv - returns undefined if the env var is not valid JSON", async (t) => { + const logger = new RecordingLogger(true); + const env = getTestEnv({ [RegistryProxyVars.PROXY_URLS]: "[" }); + + const result = getRegistryTypesFromEnv(logger, env); + t.is(result, undefined); +}); + +test("getRegistryTypesFromEnv - returns undefined if the env var is unexpected JSON", async (t) => { + const logger = new RecordingLogger(true); + + t.is( + getRegistryTypesFromEnv( + logger, + getTestEnv({ + // Top-level object rather than an array of objects. + [RegistryProxyVars.PROXY_URLS]: JSON.stringify({ type: "git_source" }), + }), + ), + undefined, + ); + t.is( + getRegistryTypesFromEnv( + logger, + getTestEnv({ + // Object has no "type" key. + [RegistryProxyVars.PROXY_URLS]: JSON.stringify([{}]), + }), + ), + undefined, + ); +}); + +test("getJobUUID - generates valid UUIDs", async (t) => { + await callee(getJobUUID) + .withArgs() + .logs(t, "Job run UUID is ") + .hasEnv(t, (val) => { + return { + [EnvVar.JOB_RUN_UUID]: val, + }; + }) + .passes((val) => { + t.true(uuid.validate(val)); + }); +}); + +test("getJobUUID - retrieves existing job UUIDs", async (t) => { + const existingJobUuid = uuid.v4(); + await callee(getJobUUID) + .withArgs() + .withEnv((env) => { + env.set(EnvVar.JOB_RUN_UUID, existingJobUuid); + }) + .logs(t, `Existing job run UUID is ${existingJobUuid}.`) + .passes(t.deepEqual, existingJobUuid); +}); + +test("getJobUUID - doesn't retrieve invalid UUIDs", async (t) => { + const existingJobUuid = "not-a-uuid"; + await callee(getJobUUID) + .withArgs() + .withEnv((env) => { + env.set(EnvVar.JOB_RUN_UUID, existingJobUuid); + }) + .logs(t, `Job run UUID is `) + .notLogs(t, `Existing job run UUID is ${existingJobUuid}.`) + .passes(t.notDeepEqual, existingJobUuid); +}); + function setupEnvironmentAndStub(tmpDir: string) { setupActionsVars(tmpDir, tmpDir, { GITHUB_EVENT_NAME: "dynamic", @@ -34,6 +133,9 @@ function setupEnvironmentAndStub(tmpDir: string) { process.env[EnvVar.ANALYSIS_KEY] = "analysis-key"; process.env["ImageVersion"] = "2023.05.19.1"; + process.env[RegistryProxyVars.PROXY_URLS] = JSON.stringify([ + { type: "maven_repository" }, + ] satisfies Array>); const getRequiredInput = sinon.stub(actionsUtil, "getRequiredInput"); getRequiredInput.withArgs("matrix").resolves("input/matrix"); @@ -71,11 +173,13 @@ test.serial("createStatusReportBase", async (t) => { t.is(statusReport.build_mode, BuildMode.None); t.is(statusReport.cause, "failure cause"); t.is(statusReport.commit_oid, process.env["GITHUB_SHA"]!); + t.deepEqual(statusReport.computed_inputs, {}); t.is(statusReport.exception, "exception stack trace"); t.is(statusReport.job_name, process.env["GITHUB_JOB"] || ""); t.is(typeof statusReport.job_run_uuid, "string"); t.is(statusReport.languages, "java,swift"); t.is(statusReport.ref, process.env["GITHUB_REF"]!); + t.is(statusReport.registry_types, "maven_repository"); t.is(statusReport.runner_available_disk_space_bytes, 100); t.is(statusReport.runner_image_version, process.env["ImageVersion"]); t.is(statusReport.runner_os, process.env["RUNNER_OS"]!); diff --git a/src/status-report.ts b/src/status-report.ts index b3e3628b36..b471bfa971 100644 --- a/src/status-report.ts +++ b/src/status-report.ts @@ -1,7 +1,9 @@ import * as os from "os"; import * as core from "@actions/core"; +import * as uuid from "uuid"; +import type { ActionState } from "./action-common"; import { getWorkflowEventName, getOptionalInput, @@ -12,15 +14,19 @@ import { isSelfHostedRunner, } from "./actions-util"; import { getAnalysisKey, getApiClient } from "./api-client"; -import { parseRegistriesWithoutCredentials, type Config } from "./config-utils"; -import { DependencyCacheRestoreStatusReport } from "./dependency-caching"; +import type { Config } from "./config/action-config"; +import type { ComputedInput, InputName } from "./config/inputs"; +import { parseRegistriesWithoutCredentials } from "./config/pack-registries"; +import type { DependencyCacheRestoreStatusReport } from "./dependency-caching"; import { DocUrl } from "./doc-url"; -import { EnvVar } from "./environment"; +import { EnvVar, getEnv, ReadOnlyEnv, RegistryProxyVars } from "./environment"; import { getRef } from "./git-utils"; -import { Logger } from "./logging"; -import { OverlayBaseDatabaseDownloadStats } from "./overlay/caching"; +import * as json from "./json"; +import type { Logger } from "./logging"; +import type { OverlayBaseDatabaseDownloadStats } from "./overlay/caching"; import { getRepositoryNwo } from "./repository"; -import { ToolsSource } from "./setup-codeql"; +import type { ToolsSource } from "./setup-codeql"; +import { registryBaseSchema } from "./start-proxy/types"; import { ConfigurationError, getRequiredEnvParam, @@ -46,6 +52,41 @@ export enum ActionName { UploadSarif = "upload-sarif", } +/** + * Maps an `ActionName` to its display name. Usually that is the same, except + * for `ActionName.Analyze` where it is `"analyze"` instead of `"finish"`. + */ +export function getDisplayActionName(actionName: ActionName): string { + if (actionName === ActionName.Analyze) { + return "analyze"; + } + return actionName; +} + +/** + * Either creates a UUIDv4 for the analysis or retrieves an existing one from the + * environment and returns it. + * If a new UUID is generated, it is also exported as an environment variable. + */ +export function getJobUUID( + action: ActionState<["Logger", "ReadOnlyEnv", "Actions"]>, +) { + // Check if we already have a UUID for the analysis and return it if so. + const existingJobRunUuid = action.env.getOptional(EnvVar.JOB_RUN_UUID); + + if (existingJobRunUuid !== undefined && uuid.validate(existingJobRunUuid)) { + action.logger.info(`Existing job run UUID is ${existingJobRunUuid}.`); + return existingJobRunUuid; + } + + // Otherwise generate a new UUID. + const jobRunUuid = uuid.v4(); + action.logger.info(`Job run UUID is ${jobRunUuid}.`); + + action.actions.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid); + return jobRunUuid; +} + /** * @returns a boolean indicating whether the analysis is considered to be first party. * @@ -110,6 +151,8 @@ export interface StatusReportBase { commit_oid: string; /** Time this action completed, or undefined if not yet completed. */ completed_at?: string; + /** A mapping of input names to their computed values. */ + computed_inputs: Partial>; /** Stack trace of the failure (or undefined if status is not failure). */ exception?: string; /** Whether this is a first-party (CodeQL) run of the action. */ @@ -144,6 +187,12 @@ export interface StatusReportBase { ml_powered_javascript_queries?: string; /** Ref that the workflow was triggered on. */ ref: string; + /** + * A comma-separated list of private registry types which are configured for CodeQL. + * This only includes registry types we support (as determined by the `start-proxy` action), + * not all that are configured. + */ + registry_types?: string; /** Action runner hardware architecture (context runner.arch). */ runner_arch?: string; /** Available disk space on the runner, in bytes. */ @@ -247,6 +296,50 @@ export interface EventReport { started_at: string; } +/** + * Attempts to retrieve a list of private registry types from the `CODEQL_PROXY_URLS` environment + * variable and returns it as a comma-separated string if successful. Returns `undefined` otherwise. + */ +export function getRegistryTypesFromEnv( + logger: Logger, + env: ReadOnlyEnv = getEnv(), +): string | undefined { + // Try to get the value of the environment variable. + const value = env.getOptional(RegistryProxyVars.PROXY_URLS); + + if (value === undefined) { + return undefined; + } + + // Try to parse the JSON we expect to find in it and return the comma-separated list of + // (unique) registry types. + try { + const data = JSON.parse(value) as unknown; + + // Check that the parsed JSON meets our expectations. + if (!json.isArray(data)) { + logger.debug( + `Expected '${RegistryProxyVars.PROXY_URLS}' to contain a JSON array, but got '${typeof data}'.`, + ); + return undefined; + } + if (!json.validateArray(registryBaseSchema, data)) { + logger.debug( + `Expected '${RegistryProxyVars.PROXY_URLS}' to contain a JSON array of registry objects, but got something else.`, + ); + return undefined; + } + + const types = new Set(data.map((r) => r.type)); + return Array.from(types).sort().join(","); + } catch (err) { + logger.debug( + `Failed to parse '${RegistryProxyVars.PROXY_URLS}': ${getErrorMessage(err)}.`, + ); + return undefined; + } +} + /** * Compose a StatusReport. * @@ -304,10 +397,12 @@ export async function createStatusReportBase( analysis_key, build_mode: config?.buildMode, commit_oid: commitOid, + computed_inputs: {}, first_party_analysis: isFirstPartyAnalysis(actionName), job_name: jobName, job_run_uuid: jobRunUUID, ref, + registry_types: getRegistryTypesFromEnv(logger), runner_os: runnerOs, started_at: workflowStartedAt, status, diff --git a/src/tar.test.ts b/src/tar.test.ts new file mode 100644 index 0000000000..48f4e866d3 --- /dev/null +++ b/src/tar.test.ts @@ -0,0 +1,33 @@ +import * as path from "path"; +import * as stream from "stream"; + +import test from "ava"; + +import { getRunnerLogger } from "./logging"; +import { extractTarZst } from "./tar"; +import { setupTests } from "./testing-utils"; +import { withTmpDir } from "./util"; + +setupTests(test); + +test("extractTarZst rejects if the input stream errors", async (t) => { + await withTmpDir(async (tmpDir) => { + const archive = new stream.PassThrough(); + const promise = extractTarZst( + archive, + path.join(tmpDir, "dest"), + { type: "gnu", version: "1.34" }, + getRunnerLogger(true), + ); + + archive.destroy( + Object.assign(new Error("socket hang up"), { + code: "ECONNRESET", + }), + ); + + await t.throwsAsync(promise, { + message: /Error while downloading and extracting tar/, + }); + }); +}); diff --git a/src/tar.ts b/src/tar.ts index 723716b016..3a0d79cc64 100644 --- a/src/tar.ts +++ b/src/tar.ts @@ -194,10 +194,15 @@ export async function extractTarZst( }); if (tar instanceof stream.Readable) { - tar.pipe(tarProcess.stdin).on("error", (err) => { - reject( - new Error(`Error while downloading and extracting tar: ${err}`), - ); + // Use `pipeline` rather than `pipe` so that an error on either stream is reported here + // rather than being emitted as an unhandled `error` event, and so that `tar`'s standard + // input is closed if the download fails partway through. + stream.pipeline(tar, tarProcess.stdin, (err) => { + if (err) { + reject( + new Error(`Error while downloading and extracting tar: ${err}`), + ); + } }); } diff --git a/src/testing-utils.ts b/src/testing-utils.ts index 2660c21a69..279459275d 100644 --- a/src/testing-utils.ts +++ b/src/testing-utils.ts @@ -3,6 +3,8 @@ import path from "path"; import * as github from "@actions/github"; import test, { + type ThrownError, + type ThrowsExpectation, type ExecutionContext, type MacroDeclarationOptions, type TestFn, @@ -10,6 +12,7 @@ import test, { import nock from "nock"; import * as sinon from "sinon"; +import { ActionState, StateFeature } from "./action-common"; import { ActionsEnv, getActionVersion } from "./actions-util"; import { AnalysisKind } from "./analyses"; import * as apiClient from "./api-client"; @@ -18,6 +21,7 @@ import { CachingKind } from "./caching-utils"; import * as codeql from "./codeql"; import { Config } from "./config-utils"; import * as defaults from "./defaults.json"; +import { Env, ActionsEnvVars } from "./environment"; import { CodeQLDefaultVersionInfo, Feature, @@ -26,13 +30,18 @@ import { } from "./feature-flags"; import { Logger } from "./logging"; import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; +import { ActionName } from "./status-report"; import { DEFAULT_DEBUG_ARTIFACT_NAME, DEFAULT_DEBUG_DATABASE_NAME, + Failure, + getEnv, GitHubVariant, GitHubVersion, HTTPError, resetCachedCodeQlVersion, + Result, + Success, } from "./util"; export const SAMPLE_DOTCOM_API_DETAILS = { @@ -172,15 +181,365 @@ export function makeMacro( return wrapper; } +export function getTestEnv(testEnv: NodeJS.ProcessEnv = {}): Env { + return getEnv(testEnv); +} + +/** An implementation of `ActionsEnv` for use in tests. */ +class TestActionsEnv implements ActionsEnv { + constructor(private readonly env: Env) {} + + public clone(env: Env): this { + return Object.create(this, { env: { value: env } }) as this; + } + + public getRequiredInput(name: string): string { + throw new Error(`Input required and not supplied: ${name}`); + } + + public getOptionalInput(_name: string): string | undefined { + return undefined; + } + + public exportVariable(name: string, value: string): void { + this.env.set(name, value); + } +} + /** * Gets an `ActionsEnv` instance for use in tests. */ -export function getTestActionsEnv(): ActionsEnv { +export function getTestActionsEnv(env: Env): TestActionsEnv { + return new TestActionsEnv(env); +} + +/** For testing purposes, we make all available state features accessible in `TestEnv`. */ +type AllState = [ + "Base", + "Logger", + "Env", + "ReadOnlyEnv", + "Actions", + "Api", + "FeatureFlags", +]; + +/** Initialise a fresh `ActionState` value. */ +export function initAllState( + overrides?: Partial>, +): ActionState { + const env = getTestEnv(); return { - getOptionalInput: () => undefined, + name: ActionName.Init, + startedAt: new Date(), + logger: new RecordingLogger(), + env, + actions: getTestActionsEnv(env), + apiClient: github.getOctokit("123"), + features: createFeatures([]), + ...overrides, }; } +type DelayedCheck< + Args extends readonly any[], + R, + Fs extends ReadonlyArray, +> = ( + env: Readonly>, + result: Result, ThrownError>, +) => Promise; + +export type Mutation = (val: T) => void; +export type ValueOrMutation = T | Mutation; + +/** + * Wraps a function that accepts an `ActionState` for testing in different environments. + */ +abstract class BaseEnvBuilder< + Args extends readonly any[], + R, + Fs extends ReadonlyArray, +> { + protected readonly fn: (state: ActionState, ...args: Args) => R; + private logger: RecordingLogger; + private actions: TestActionsEnv; + protected state: ActionState; + protected checks: Array>; + + constructor( + fn: (state: ActionState, ...args: Args) => R, + cloneFrom?: BaseEnvBuilder, + ) { + this.fn = fn; + this.logger = new RecordingLogger(); + + if (cloneFrom !== undefined) { + const env = cloneFrom.state.env.clone(); + this.actions = cloneFrom.actions.clone(env); + this.state = { + ...cloneFrom.state, + env, + actions: this.actions, + logger: this.logger, + } satisfies ActionState; + } else { + const env = getTestEnv(); + this.actions = getTestActionsEnv(env); + this.state = initAllState({ + logger: this.logger, + env, + actions: this.actions, + }); + } + + this.checks = [...(cloneFrom?.checks ?? [])]; + } + + /** + * Creates a clone of this object. Used internally. + * Must be overridden by subclasses. + */ + protected abstract clone(): this; + + public getLogger(): RecordingLogger { + return this.logger; + } + + public getState(): ActionState { + return this.state; + } + + public withArgs(...args: Args): CallableEnvBuilder { + const result = new CallableEnvBuilder(this.fn, args, this.clone()); + return result; + } + + public withFeatures(enabled: Feature[]): this { + const result = this.clone(); + result.state.features = createFeatures(enabled); + return result; + } + + /** + * Sets environment variables that are always available to GitHub Actions, + * excluding some that are expected to be set to paths. + * + * @param overrides Overrides for the defaults. + */ + public withDefaultActionsEnv(overrides?: ActionVarOverrides): this { + const result = this.clone(); + setupBaseActionsVars(overrides, result.state.env); + return result; + } + + /** + * Sets environment variables that are always available to GitHub Actions. + * @param tempDir A value for `RUNNER_TEMP` and `GITHUB_WORKSPACE`. + * @param toolsDir A value for `RUNNER_TOOL_CACHE`. + * @param overrides Overrides for the defaults. + */ + public withActionsEnv( + tempDir: string, + toolsDir: string, + overrides?: ActionVarOverrides, + ): this { + const result = this.clone(); + setupActionsVars(tempDir, toolsDir, overrides, result.state.env); + return result; + } + + public withEnv(arg: ValueOrMutation): this { + const result = this.clone(); + if (typeof arg === "function") { + arg(result.state.env); + } else { + result.state.env = arg; + } + return result; + } + + /** Applies `fn` to the `ActionsEnv`. */ + public withActions(fn: Mutation): this { + const result = this.clone(); + fn(result.state.actions); + return result; + } + + /** + * Adds a delayed check that `messages` are logged. The check will be + * performed after the main assertion passes. + */ + public logs(t: ExecutionContext, ...messages: string[]): this { + const result = this.clone(); + result.checks.push(async (env) => { + checkExpectedLogMessages(t, env.getLogger().messages, messages); + }); + return result; + } + + /** + * Adds a delayed check that the environment variables returned by `fn` + * are present in the environment after the main assertion passes. + */ + public hasEnv( + t: ExecutionContext, + fn: ( + value: Awaited | undefined, + error: ThrownError | undefined, + ) => Record, + ): this { + const result = this.clone(); + result.checks.push(async (env, r) => { + const value = r.orElse(undefined); + const error = r.isFailure() ? r.value : undefined; + const expected = fn(value, error); + + t.like(env.getState().env.get(), expected); + }); + return result; + } + + /** + * Adds a delayed check that `messages` are not logged. The check will be + * performed after the main assertion passes. + */ + public notLogs(t: ExecutionContext, ...messages: string[]): this { + const result = this.clone(); + result.checks.push(async (env) => { + checkUnexpectedLogMessages(t, env.getLogger().messages, messages); + }); + return result; + } +} + +class EnvBuilder< + Args extends readonly any[], + R, + Fs extends ReadonlyArray, +> extends BaseEnvBuilder { + protected clone(): this { + return new EnvBuilder(this.fn, this) as this; + } +} + +export interface PassedAssertion { + result: Awaited; + assertionResult: T; +} + +/** + * A more minimal, exported interface for `CallableEnvBuilder`. This makes it easier to + * define helper functions in tests which expect a value of a compatible type. + */ +export interface AssertableTarget { + passes( + assertion: (val: Awaited, ...assertionArgs: AArgs) => AResult, + ...assertionArgs: AArgs + ): Promise>; + + throws( + t: ExecutionContext, + expectations?: ThrowsExpectation, + ): Promise>; +} + +class CallableEnvBuilder< + Args extends readonly any[], + R, + Fs extends ReadonlyArray, + > + extends BaseEnvBuilder + implements AssertableTarget +{ + private args: Args; + + constructor( + fn: (state: ActionState, ...args: Args) => R, + args: Args, + cloneFrom?: BaseEnvBuilder, + ) { + super(fn, cloneFrom); + this.args = args; + } + + protected clone(): this { + return new CallableEnvBuilder(this.fn, this.args, this) as this; + } + + public getArgs(): Args { + return this.args; + } + + call(): R { + return this.fn(this.state as unknown as ActionState, ...this.args); + } + + /** + * Calls the underlying function in the configured environment and passes + * the result to `assertion` along with extra `assertionArgs`. + * + * @param assertion The assertion to apply to the result. + * @param assertionArgs Extra arguments for the assertion. + * @returns The result of the assertion. + */ + public async passes( + assertion: (val: Awaited, ...assertionArgs: AArgs) => AResult, + ...assertionArgs: AArgs + ): Promise> { + // this.call() may or may not return a promise, + // `Promise.resolve` turns the result into one if it isn't already, + // and we then await it. That ensures that `result` is an `Awaited`. + const result = await Promise.resolve(this.call()); + + // Run the main assertion on the `result`. + const assertionResult = await assertion(result, ...assertionArgs); + + // Run other delayed checks. + for (const delayedCheck of this.checks) { + await delayedCheck(this, new Success(result)); + } + + // Return the results of the function call and the main assertion. + return { result, assertionResult }; + } + + /** + * Asserts that calling the underlying function should throw an exception. + * + * @param t The execution context for the assertion. + * @param expectations Expectations for the error. + * @returns The error that was thrown. + */ + public async throws( + t: ExecutionContext, + expectations?: ThrowsExpectation, + ): Promise> { + // Run the main assertion. + const error = await t.throwsAsync( + async () => Promise.resolve(this.call()), + expectations, + ); + + // Run other delayed checks. + for (const delayedCheck of this.checks) { + await delayedCheck(this, new Failure(error)); + } + + // Return the error. + return error; + } +} + +/** Utility function to construct a `TestEnv`. */ +export function callee< + Args extends readonly any[], + R, + Fs extends readonly StateFeature[], +>(fn: (state: ActionState, ...args: Args) => R): EnvBuilder { + return new EnvBuilder(fn); +} + /** * Default values for environment variables typically set in an Actions * environment. Tests can override individual variables by passing them in the @@ -200,7 +559,7 @@ export const DEFAULT_ACTIONS_VARS = { GITHUB_WORKFLOW: "test-workflow", RUNNER_NAME: "my-runner", RUNNER_OS: "Linux", -} as const satisfies Record; +} as const satisfies Partial>; /** Partial mappings from GitHub Actions environment variables to values. */ export type ActionVarOverrides = Partial< @@ -212,11 +571,15 @@ export type ActionVarOverrides = Partial< * excluding some that are expected to be set to paths. See `setupActionsVars`. * * @param overrides Overrides for the defaults. + * @param env The environment to set the variables for. */ -export function setupBaseActionsVars(overrides?: ActionVarOverrides) { +export function setupBaseActionsVars( + overrides?: ActionVarOverrides, + env: Env = getEnv(), +) { const vars = { ...DEFAULT_ACTIONS_VARS, ...overrides }; for (const [key, value] of Object.entries(vars)) { - process.env[key] = value; + env.set(key, value); } } @@ -226,16 +589,18 @@ export function setupBaseActionsVars(overrides?: ActionVarOverrides) { * @param tempDir A value for `RUNNER_TEMP` and `GITHUB_WORKSPACE`. * @param toolsDir A value for `RUNNER_TOOL_CACHE`. * @param overrides Overrides for the defaults. + * @param env The environment to set the variables for. */ export function setupActionsVars( tempDir: string, toolsDir: string, overrides?: ActionVarOverrides, + env: Env = getEnv(), ) { - setupBaseActionsVars(overrides); - process.env["RUNNER_TEMP"] = tempDir; - process.env["RUNNER_TOOL_CACHE"] = toolsDir; - process.env["GITHUB_WORKSPACE"] = tempDir; + setupBaseActionsVars(overrides, env); + env.set(ActionsEnvVars.RUNNER_TEMP, tempDir); + env.set(ActionsEnvVars.RUNNER_TOOL_CACHE, toolsDir); + env.set(ActionsEnvVars.GITHUB_WORKSPACE, tempDir); } type LogLevel = "debug" | "info" | "warning" | "error"; @@ -369,6 +734,34 @@ export function checkExpectedLogMessages( } } +/** + * Checks that `messages` contains none of `unexpectedMessages`. + */ +export function checkUnexpectedLogMessages( + t: ExecutionContext, + messages: LoggedMessage[], + unexpectedMessages: string[], +) { + const presentMessages: string[] = []; + + for (const unexpectedMessage of unexpectedMessages) { + if (hasLoggedMessage(messages, unexpectedMessage)) { + presentMessages.push(unexpectedMessage); + } + } + + if (presentMessages.length > 0) { + const listify = (lines: string[]) => + lines.map((m) => ` - '${m}'`).join("\n"); + + t.fail( + `Did not expect\n\n${listify(presentMessages)}\n\nin the logger output, but found them in:\n\n${messages.map((m) => ` - '${m.message}'`).join("\n")}`, + ); + } else { + t.pass(); + } +} + /** * Asserts that `message` should not have been logged to `logger`. */ diff --git a/src/tools-download.test.ts b/src/tools-download.test.ts new file mode 100644 index 0000000000..66fe0e72e4 --- /dev/null +++ b/src/tools-download.test.ts @@ -0,0 +1,115 @@ +import { once } from "events"; +import * as path from "path"; + +import * as toolcache from "@actions/tool-cache"; +import test from "ava"; +import nock from "nock"; +import * as sinon from "sinon"; + +import { getRunnerLogger } from "./logging"; +import * as tar from "./tar"; +import { setupTests } from "./testing-utils"; +import { downloadAndExtract } from "./tools-download"; +import { withTmpDir } from "./util"; + +setupTests(test); + +test.serial( + "downloadAndExtract reports the duration when downloading before extracting", + async (t) => { + await withTmpDir(async (tmpDir) => { + const archivePath = path.join(tmpDir, "codeql-bundle.tar.gz"); + const destination = path.join(tmpDir, "codeql"); + sinon.stub(toolcache, "downloadTool").resolves(archivePath); + sinon.stub(tar, "extract").resolves(destination); + + const statusReport = await downloadAndExtract( + "https://example.com/codeql-bundle.tar.gz", + "gzip", + destination, + undefined, + {}, + undefined, + getRunnerLogger(true), + ); + + t.assert(Number.isInteger(statusReport.downloadDurationMs)); + }); + }, +); + +test.serial( + "downloadAndExtract falls back to downloading before extracting if streaming fails", + async (t) => { + await withTmpDir(async (tmpDir) => { + sinon.stub(process, "platform").value("linux"); + const archivePath = path.join(tmpDir, "codeql-bundle.tar.zst"); + const destination = path.join(tmpDir, "codeql"); + const downloadTool = sinon + .stub(toolcache, "downloadTool") + .resolves(archivePath); + const extract = sinon.stub(tar, "extract").resolves(destination); + const extractTarZst = sinon.stub(tar, "extractTarZst").resolves(); + const request = nock("https://example.com") + .get("/codeql-bundle.tar.zst") + .replyWithError( + Object.assign(new Error("socket hang up"), { code: "ECONNRESET" }), + ); + + const statusReport = await downloadAndExtract( + "https://example.com/codeql-bundle.tar.zst", + "zstd", + destination, + undefined, + {}, + { type: "gnu", version: "1.34" }, + getRunnerLogger(true), + ); + + t.assert(Number.isInteger(statusReport.downloadDurationMs)); + t.true(request.isDone()); + t.false(extractTarZst.called); + t.true(downloadTool.calledOnce); + t.true(extract.calledOnce); + }); + }, +); + +test.serial( + "downloadAndExtract omits the download duration when streaming extraction", + async (t) => { + await withTmpDir(async (tmpDir) => { + sinon.stub(process, "platform").value("linux"); + const downloadTool = sinon.stub(toolcache, "downloadTool"); + const extractTarZst = sinon + .stub(tar, "extractTarZst") + .callsFake(async (archive) => { + if (typeof archive === "string") { + t.fail("Expected the Zstandard archive to be streamed."); + return; + } + const end = once(archive, "end"); + archive.resume(); + await end; + }); + const request = nock("https://example.com") + .get("/codeql-bundle.tar.zst") + .reply(200, "archive"); + + const statusReport = await downloadAndExtract( + "https://example.com/codeql-bundle.tar.zst", + "zstd", + path.join(tmpDir, "codeql"), + undefined, + {}, + { type: "gnu", version: "1.34" }, + getRunnerLogger(true), + ); + + t.deepEqual(statusReport, {}); + t.false(downloadTool.called); + t.true(extractTarZst.calledOnce); + t.true(request.isDone()); + }); + }, +); diff --git a/src/tools-download.ts b/src/tools-download.ts index 5d8a4c5fb9..9b2fa8723a 100644 --- a/src/tools-download.ts +++ b/src/tools-download.ts @@ -20,65 +20,19 @@ import { cleanUpPath, getErrorMessage, getRequiredEnvParam } from "./util"; const STREAMING_HIGH_WATERMARK_BYTES = 4 * 1024 * 1024; // 4 MiB /** - * The name of the tool cache directory for the CodeQL tools. + * How long the streaming download of the CodeQL tools may stall for before we abort it. This + * applies both to establishing the connection and to gaps between chunks of the response body. */ -const TOOLCACHE_TOOL_NAME = "CodeQL"; +const STREAMING_STALL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes /** - * Timing information for the download and extraction of the CodeQL tools when - * we fully download the bundle before extracting. - */ -type DownloadFirstToolsDownloadDurations = { - combinedDurationMs: number; - downloadDurationMs: number; - extractionDurationMs: number; - streamExtraction: false; -}; - -function makeDownloadFirstToolsDownloadDurations( - downloadDurationMs: number, - extractionDurationMs: number, -): DownloadFirstToolsDownloadDurations { - return { - combinedDurationMs: downloadDurationMs + extractionDurationMs, - downloadDurationMs, - extractionDurationMs, - streamExtraction: false, - }; -} - -/** - * Timing information for the download and extraction of the CodeQL tools when - * we stream the download and extraction of the bundle. + * The name of the tool cache directory for the CodeQL tools. */ -type StreamedToolsDownloadDurations = { - combinedDurationMs: number; - downloadDurationMs: undefined; - extractionDurationMs: undefined; - streamExtraction: true; -}; - -function makeStreamedToolsDownloadDurations( - combinedDurationMs: number, -): StreamedToolsDownloadDurations { - return { - combinedDurationMs, - downloadDurationMs: undefined, - extractionDurationMs: undefined, - streamExtraction: true, - }; -} - -type ToolsDownloadDurations = - | DownloadFirstToolsDownloadDurations - | StreamedToolsDownloadDurations; +const TOOLCACHE_TOOL_NAME = "CodeQL"; export type ToolsDownloadStatusReport = { - cacheDurationMs?: number; - compressionMethod: tar.CompressionMethod; - toolsUrl: string; - zstdFailureReason?: string; -} & ToolsDownloadDurations; + downloadDurationMs?: number; +}; export async function downloadAndExtract( codeqlURL: string, @@ -116,11 +70,7 @@ export async function downloadAndExtract( )}).`, ); - return { - compressionMethod, - toolsUrl: sanitizeUrlForStatusReport(codeqlURL), - ...makeStreamedToolsDownloadDurations(combinedDurationMs), - }; + return {}; } } catch (e) { core.warning( @@ -170,14 +120,7 @@ export async function downloadAndExtract( await cleanUpPath(archivedBundlePath, "CodeQL bundle archive", logger); } - return { - compressionMethod, - toolsUrl: sanitizeUrlForStatusReport(codeqlURL), - ...makeDownloadFirstToolsDownloadDurations( - downloadDurationMs, - extractionDurationMs, - ), - }; + return { downloadDurationMs }; } async function downloadAndExtractZstdWithStreaming( @@ -200,8 +143,8 @@ async function downloadAndExtractZstdWithStreaming( authorization ? { authorization } : {}, headers, ); - const response = await new Promise((resolve) => - https.get( + const response = await new Promise((resolve, reject) => { + const request = https.get( codeqlURL, { headers, @@ -211,10 +154,24 @@ async function downloadAndExtractZstdWithStreaming( agent, } as unknown as RequestOptions, (r) => resolve(r), - ), - ); + ); + // Without this listener, connection failures such as `ECONNRESET` are emitted as unhandled + // `error` events, which terminate the process instead of letting us fall back to downloading + // the bundle before extracting it. This listener stays attached after the response arrives, so + // it also handles errors that occur while the response is being streamed. + request.on("error", reject); + request.setTimeout(STREAMING_STALL_TIMEOUT_MS, () => { + request.destroy( + new Error( + `No data received for ${formatDuration(STREAMING_STALL_TIMEOUT_MS)}.`, + ), + ); + }); + }); if (response.statusCode !== 200) { + // Discard the response body so that the connection can be released. + response.resume(); throw new Error( `Failed to download CodeQL bundle from ${codeqlURL}. HTTP status code: ${response.statusCode}.`, ); @@ -241,11 +198,3 @@ export function writeToolcacheMarkerFile( fs.writeFileSync(markerFilePath, ""); logger.info(`Created toolcache marker file ${markerFilePath}`); } - -function sanitizeUrlForStatusReport(url: string): string { - return ["github/codeql-action", "dsp-testing/codeql-cli-nightlies"].some( - (repo) => url.startsWith(`https://github.com/${repo}/releases/download/`), - ) - ? url - : "sanitized-value"; -} diff --git a/src/upload-lib.ts b/src/upload-lib.ts index 83d1eaffb0..da5552cf24 100644 --- a/src/upload-lib.ts +++ b/src/upload-lib.ts @@ -140,7 +140,7 @@ async function combineSarifFilesUsingCLI( const config = await getConfig(tempDir, logger); if (config !== undefined) { - codeQL = await getCodeQL(config.codeQLCmd); + codeQL = await getCodeQL(logger, config.codeQLCmd); tempDir = config.tempDir; } else { logger.info( diff --git a/src/upload-sarif-action.ts b/src/upload-sarif-action.ts index bd190f0318..d3437510ce 100644 --- a/src/upload-sarif-action.ts +++ b/src/upload-sarif-action.ts @@ -1,17 +1,17 @@ import * as core from "@actions/core"; +import { Action, ActionState, runInActions } from "./action-common"; import * as actionsUtil from "./actions-util"; import { getActionVersion, getTemporaryDirectory } from "./actions-util"; import * as analyses from "./analyses"; import { getGitHubVersion } from "./api-client"; import { initFeatures } from "./feature-flags"; -import { Logger, getActionsLogger } from "./logging"; +import { Logger } from "./logging"; import { getRepositoryNwo } from "./repository"; import { InvalidSarifUploadError } from "./sarif"; import { createStatusReportBase, sendStatusReport, - sendUnhandledErrorStatusReport, StatusReportBase, getActionsStatus, ActionName, @@ -23,7 +23,6 @@ import { ConfigurationError, checkActionVersion, checkDiskUsage, - getErrorMessage, initializeEnvironment, shouldSkipSarifUpload, wrapError, @@ -55,12 +54,9 @@ async function sendSuccessStatusReport( } } -async function run(startedAt: Date) { +async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) { // To capture errors appropriately, keep as much code within the try-catch as // possible, and only use safe functions outside. - - const logger = getActionsLogger(); - try { initializeEnvironment(getActionVersion()); @@ -165,20 +161,12 @@ async function run(startedAt: Date) { } } +/** Defines the `upload-sarif` Action. */ +const uploadSarif: Action = { + name: ActionName.UploadSarif, + run, +}; + export async function runWrapper() { - const startedAt = new Date(); - const logger = getActionsLogger(); - try { - await run(startedAt); - } catch (error) { - core.setFailed( - `codeql/upload-sarif action failed: ${getErrorMessage(error)}`, - ); - await sendUnhandledErrorStatusReport( - ActionName.UploadSarif, - startedAt, - error, - logger, - ); - } + await runInActions(uploadSarif); } diff --git a/src/util.ts b/src/util.ts index fc0553b1b7..b7d27afae3 100644 --- a/src/util.ts +++ b/src/util.ts @@ -13,11 +13,14 @@ import * as apiCompatibility from "./api-compatibility.json"; import type { CodeQL, VersionInfo } from "./codeql"; import type { Pack } from "./config/db-config"; import type { Config } from "./config-utils"; -import { EnvVar } from "./environment"; +import { EnvVar, getRequiredEnvParam } from "./environment"; import * as json from "./json"; import { Language } from "./languages"; import { Logger } from "./logging"; +// Re-export for backwards compatibility to avoid updating a lot of imports elsewhere. +export { getRequiredEnvParam, getOptionalEnvVar, getEnv } from "./environment"; + /** * The name of the file containing the base database OIDs, as stored in the * root of the database location. @@ -566,28 +569,6 @@ export function initializeEnvironment(version: string) { core.exportVariable(EnvVar.VERSION, version); } -/** - * Get an environment parameter, but throw an error if it is not set. - */ -export function getRequiredEnvParam(paramName: string): string { - const value = process.env[paramName]; - if (value === undefined || value.length === 0) { - throw new Error(`${paramName} environment variable must be set`); - } - return value; -} - -/** - * Get an environment variable, but return `undefined` if it is not set or empty. - */ -export function getOptionalEnvVar(paramName: string): string | undefined { - const value = process.env[paramName]; - if (value?.trim().length === 0) { - return undefined; - } - return value; -} - export class HTTPError extends Error { public status: number;