diff --git a/.phylum_project b/.phylum_project index d6c540b3..feb63ef4 100644 --- a/.phylum_project +++ b/.phylum_project @@ -1,7 +1,7 @@ id: 56f7f1b0-7f63-47a4-9f5e-8194772b2e13 name: phylum-ci -created_at: 2023-12-07T16:29:55.527161-06:00 +created_at: 2024-06-28T10:11:12.990575-05:00 group_name: phylum_bot -lockfiles: +depfiles: - path: ./poetry.lock type: poetry diff --git a/docs/integrations/azure_pipelines.md b/docs/integrations/azure_pipelines.md index ca0d0a66..595e1493 100644 --- a/docs/integrations/azure_pipelines.md +++ b/docs/integrations/azure_pipelines.md @@ -315,6 +315,10 @@ view the [script options output][script_options] for the latest release. # for *workspace* manifest files where there is no companion lockfile (e.g., libraries). - script: phylum-ci --force-analysis --all-deps --depfile Cargo.toml + # Perform analysis as part of a group-owned project. + # A paid account is needed to use groups: https://phylum.io/pricing + - script: phylum-ci --group my_group + # Analyze all dependencies in audit mode, to gain insight without failing builds. - script: phylum-ci --all-deps --audit diff --git a/docs/integrations/bitbucket_pipelines.md b/docs/integrations/bitbucket_pipelines.md index 7ed0bc1d..6f54a9a2 100644 --- a/docs/integrations/bitbucket_pipelines.md +++ b/docs/integrations/bitbucket_pipelines.md @@ -313,6 +313,10 @@ view the [script options output][script_options] for the latest release. # for *workspace* manifest files where there is no companion lockfile (e.g., libraries). - phylum-ci --force-analysis --all-deps --depfile Cargo.toml + # Perform analysis as part of a group-owned project. + # A paid account is needed to use groups: https://phylum.io/pricing + - phylum-ci --group my_group + # Analyze all dependencies in audit mode, to gain insight without failing builds. - phylum-ci --all-deps --audit diff --git a/docs/integrations/git_precommit.md b/docs/integrations/git_precommit.md index 02f7afc0..597113f7 100644 --- a/docs/integrations/git_precommit.md +++ b/docs/integrations/git_precommit.md @@ -153,6 +153,10 @@ with `--help` output as specified in the [Usage section of the top-level README. # for *workspace* manifest files where there is no companion lockfile (e.g., libraries). args: [--force-analysis, --all-deps, --depfile=Cargo.toml] + # Perform analysis as part of a group-owned project. + # A paid account is needed to use groups: https://phylum.io/pricing + args: [--group, my_group] + # Ensure the latest Phylum CLI is installed. args: [--force-install] diff --git a/docs/integrations/gitlab_ci.md b/docs/integrations/gitlab_ci.md index 7ab031b7..3503b30d 100644 --- a/docs/integrations/gitlab_ci.md +++ b/docs/integrations/gitlab_ci.md @@ -301,6 +301,10 @@ view the [script options output][script_options] for the latest release. # for *workspace* manifest files where there is no companion lockfile (e.g., libraries). - phylum-ci --force-analysis --all-deps --depfile Cargo.toml + # Perform analysis as part of a group-owned project. + # A paid account is needed to use groups: https://phylum.io/pricing + - phylum-ci --group my_group + # Analyze all dependencies in audit mode, to gain insight without failing builds. - phylum-ci --all-deps --audit diff --git a/docs/integrations/jenkins.md b/docs/integrations/jenkins.md index 6f22ce5b..734da8ca 100644 --- a/docs/integrations/jenkins.md +++ b/docs/integrations/jenkins.md @@ -8,25 +8,25 @@ The credential ID should be specified since it will be used in the `Jenkinsfile` 1. Add the following "Phylum" stage to an existing `Jenkinsfile` declarative pipeline configuration: ```groovy - stage('Phylum') { - agent { - docker { - image 'phylumio/phylum-ci:latest' - alwaysPull true - } - } - environment { - // The environment variable must be named like - // this but the credential ID can be different. - PHYLUM_API_KEY = credentials('phylum-token') - } - steps { - // The `--force-analysis` and `--all-deps` flags - // are needed because this configuration has no - // history available for computing changes. - sh 'phylum-ci -vv --force-analysis --all-deps' - } + stage('Phylum') { + agent { + docker { + image 'phylumio/phylum-ci:latest' + alwaysPull true } + } + environment { + // The environment variable must be named like + // this but the credential ID can be different. + PHYLUM_API_KEY = credentials('phylum-token') + } + steps { + // The `--force-analysis` and `--all-deps` flags + // are needed because this configuration has no + // history available for computing changes. + sh 'phylum-ci -vv --force-analysis --all-deps' + } + } ``` > ⚠️ **INFO** ⚠️ @@ -78,46 +78,46 @@ pipeline configuration: ```groovy pipeline { - agent none - stages { - stage('Phylum') { - agent { - docker { - image 'phylumio/phylum-ci:latest' - alwaysPull true - } - } - environment { - PHYLUM_API_KEY = credentials('phylum-token') - } - options { - // This is optional but may save time since - // a full checkout is needed later. - skipDefaultCheckout() - } - steps { - checkout scmGit( - branches: [[name: '**']], - extensions: [cleanBeforeCheckout()], - // Change to match your repository URL and creds. - userRemoteConfigs: [[ - credentialsId: 'CHANGEME', - url: 'https://github.com/CHANGEME/CHANGEME.git' - ]] - ) - withCredentials([gitUsernamePassword(credentialsId: 'CHANGEME', gitToolName: 'Default')]) { - sh 'phylum-ci -vv' - } - } - post { - always { - // Cleaning the workspace ensures the git - // checkout is valid for future runs. - cleanWs() - } - } + agent none + stages { + stage('Phylum') { + agent { + docker { + image 'phylumio/phylum-ci:latest' + alwaysPull true + } + } + environment { + PHYLUM_API_KEY = credentials('phylum-token') + } + options { + // This is optional but may save time since + // a full checkout is needed later. + skipDefaultCheckout() + } + steps { + checkout scmGit( + branches: [[name: '**']], + extensions: [cleanBeforeCheckout()], + // Change to match your repository URL and creds. + userRemoteConfigs: [[ + credentialsId: 'CHANGEME', + url: 'https://github.com/CHANGEME/CHANGEME.git' + ]] + ) + withCredentials([gitUsernamePassword(credentialsId: 'CHANGEME', gitToolName: 'Default')]) { + sh 'phylum-ci -vv' + } + } + post { + always { + // Cleaning the workspace ensures the git + // checkout is valid for future runs. + cleanWs() } + } } + } } ``` @@ -146,23 +146,23 @@ anymore...use the SHA256 digest of the tag. The digest can be found by looking a For instance, at the time of this writing, all of these tag references pointed to the same image: ```groovy - agent { - docker { - // NOTE: These are examples. Only one image line for `phylum-ci` is expected. - // - // Not specifying a tag means a default of `latest` - image 'phylumio/phylum-ci' - // Be more explicit about wanting the `latest` tag - image 'phylumio/phylum-ci:latest' - // Use a specific release version of the `phylum-ci` package - image 'phylumio/phylum-ci:0.42.4-CLIv6.1.2' - // Use a specific image with it's SHA256 digest - image 'phylumio/phylum-ci@sha256:77b761ccef10edc28b0f009a40fbeab240bf004522edaaea05572dc3728b6ca6' - - // This option is useful when image is NOT specified by hash - alwaysPull true - } - } + agent { + docker { + // NOTE: These are examples. Only one image line for `phylum-ci` is expected. + // + // Not specifying a tag means a default of `latest` + image 'phylumio/phylum-ci' + // Be more explicit about wanting the `latest` tag + image 'phylumio/phylum-ci:latest' + // Use a specific release version of the `phylum-ci` package + image 'phylumio/phylum-ci:0.42.4-CLIv6.1.2' + // Use a specific image with it's SHA256 digest + image 'phylumio/phylum-ci@sha256:77b761ccef10edc28b0f009a40fbeab240bf004522edaaea05572dc3728b6ca6' + + // This option is useful when image is NOT specified by hash + alwaysPull true + } + } ``` Only the last tag reference, by SHA256 digest, is guaranteed to not have the underlying image it points to change. @@ -184,19 +184,19 @@ manifest files are present and/or **only** lockfiles are used. Here are examples of using the slim image tags: ```groovy - agent { - docker { - // NOTE: These are examples. Only one image line for `phylum-ci` is expected. - // - // Use the most current release of *both* `phylum-ci` and the Phylum CLI - image 'phylumio/phylum-ci:slim' - // Use the `slim` image with a specific release version of `phylum-ci` and Phylum CLI - image 'phylumio/phylum-ci:0.42.4-CLIv6.1.2-slim' - - // This option is useful when image is NOT specified by hash - alwaysPull true - } - } + agent { + docker { + // NOTE: These are examples. Only one image line for `phylum-ci` is expected. + // + // Use the most current release of *both* `phylum-ci` and the Phylum CLI + image 'phylumio/phylum-ci:slim' + // Use the `slim` image with a specific release version of `phylum-ci` and Phylum CLI + image 'phylumio/phylum-ci:0.42.4-CLIv6.1.2-slim' + + // This option is useful when image is NOT specified by hash + alwaysPull true + } + } ``` See the documentation for [using Docker with Pipeline][docker_pipeline] more information. @@ -216,11 +216,11 @@ higher. The value for this variable is sensitive and should be set as a secret t **Care should be taken to protect it appropriately**. ```groovy - environment { - // Variable must be named `PHYLUM_API_KEY` - // but the credential ID can be different. - PHYLUM_API_KEY = credentials('phylum-token') - } + environment { + // Variable must be named `PHYLUM_API_KEY` + // but the credential ID can be different. + PHYLUM_API_KEY = credentials('phylum-token') + } ``` [user_vars]: https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#setting-environment-variables @@ -233,19 +233,19 @@ repository is required to ensure that the local working copy is always pristine requested information. ```groovy - steps { - // A full checkout is needed to provide history and proper diffs. - // A `checkout scm` step is not enough here. - checkout scmGit( - branches: [[name: '**']], - extensions: [cleanBeforeCheckout()], - // Change to match your repository URL and creds. - userRemoteConfigs: [[ - credentialsId: 'CHANGEME', - url: 'https://github.com/CHANGEME/CHANGEME.git' - ]] - ) - } + steps { + // A full checkout is needed to provide history and proper diffs. + // A `checkout scm` step is not enough here. + checkout scmGit( + branches: [[name: '**']], + extensions: [cleanBeforeCheckout()], + // Change to match your repository URL and creds. + userRemoteConfigs: [[ + credentialsId: 'CHANGEME', + url: 'https://github.com/CHANGEME/CHANGEME.git' + ]] + ) + } ``` See the [checkout step][checkout_step] and [checkout plugin][scm_plugin] documentation for more information. @@ -265,61 +265,65 @@ release. [script_options]: https://github.com/phylum-dev/phylum-ci/blob/main/docs/script_options.md ```groovy - steps { - // This block is needed for change detection and using - // authenticated git commands. Change the `credentialsId`. - withCredentials([gitUsernamePassword(credentialsId: 'CHANGEME', gitToolName: 'Default')]) { - // NOTE: These are examples. Only one `phylum-ci` entry is expected. - // - // Use the defaults for all the arguments. - // The default behavior is to only analyze newly added dependencies - // against the active policy set at the Phylum project level. - sh 'phylum-ci' - - // Provide debug level output. Highly recommended. - sh 'phylum-ci -vv' - - // Consider all dependencies in analysis results instead of just the newly added ones. - // The default is to only analyze newly added dependencies, which can be useful for - // existing code bases that may not meet established policy rules yet, - // but don't want to make things worse. Specifying `--all-deps` can be useful for - // casting the widest net for strict adherence to Quality Assurance (QA) standards. - sh 'phylum-ci --all-deps' - - // Some lockfile types (e.g., Python/pip `requirements.txt`) are ambiguous in that - // they can be named differently and may or may not contain strict dependencies. - // In these cases it is best to specify an explicit path, either with the `--depfile` - // option or in a `.phylum_project` file. The easiest way to do that is with the - // Phylum CLI, using `phylum init` command (docs.phylum.io/cli/commands/phylum_init) - // and committing the generated `.phylum_project` file. - sh 'phylum-ci --depfile requirements-prod.txt' - - // Specify multiple explicit dependency file paths. - sh 'phylum-ci --depfile requirements-prod.txt Cargo.toml path/to/dependency.file' - - // Force analysis for all dependencies in a manifest file. This is especially useful - // for *workspace* manifest files where there is no companion lockfile (e.g., libraries). - sh 'phylum-ci --force-analysis --all-deps --depfile Cargo.toml' - - // Analyze all dependencies in audit mode, to gain insight without failing builds. - sh 'phylum-ci --all-deps --audit' - - // Ensure the latest Phylum CLI is installed. - sh 'phylum-ci --force-install' - - // Install a specific version of the Phylum CLI. - sh 'phylum-ci --phylum-release 6.4.0 --force-install' - - // Mix and match for your specific use case. - sh 'phylum-ci \ - -vv \ - --depfile requirements-dev.txt \ - --depfile requirements-prod.txt path/to/dependency.file \ - --depfile Cargo.toml \ - --force-analysis \ - --all-deps' - } - } + steps { + // This block is needed for change detection and using + // authenticated git commands. Change the `credentialsId`. + withCredentials([gitUsernamePassword(credentialsId: 'CHANGEME', gitToolName: 'Default')]) { + // NOTE: These are examples. Only one `phylum-ci` entry is expected. + // + // Use the defaults for all the arguments. + // The default behavior is to only analyze newly added dependencies + // against the active policy set at the Phylum project level. + sh 'phylum-ci' + + // Provide debug level output. Highly recommended. + sh 'phylum-ci -vv' + + // Consider all dependencies in analysis results instead of just the newly added ones. + // The default is to only analyze newly added dependencies, which can be useful for + // existing code bases that may not meet established policy rules yet, + // but don't want to make things worse. Specifying `--all-deps` can be useful for + // casting the widest net for strict adherence to Quality Assurance (QA) standards. + sh 'phylum-ci --all-deps' + + // Some lockfile types (e.g., Python/pip `requirements.txt`) are ambiguous in that + // they can be named differently and may or may not contain strict dependencies. + // In these cases it is best to specify an explicit path, either with the `--depfile` + // option or in a `.phylum_project` file. The easiest way to do that is with the + // Phylum CLI, using `phylum init` command (docs.phylum.io/cli/commands/phylum_init) + // and committing the generated `.phylum_project` file. + sh 'phylum-ci --depfile requirements-prod.txt' + + // Specify multiple explicit dependency file paths. + sh 'phylum-ci --depfile requirements-prod.txt Cargo.toml path/to/dependency.file' + + // Force analysis for all dependencies in a manifest file. This is especially useful + // for *workspace* manifest files where there is no companion lockfile (e.g., libraries). + sh 'phylum-ci --force-analysis --all-deps --depfile Cargo.toml' + + // Perform analysis as part of a group-owned project. + // A paid account is needed to use groups: https://phylum.io/pricing + sh 'phylum-ci --group my_group' + + // Analyze all dependencies in audit mode, to gain insight without failing builds. + sh 'phylum-ci --all-deps --audit' + + // Ensure the latest Phylum CLI is installed. + sh 'phylum-ci --force-install' + + // Install a specific version of the Phylum CLI. + sh 'phylum-ci --phylum-release 6.4.0 --force-install' + + // Mix and match for your specific use case. + sh 'phylum-ci \ + -vv \ + --depfile requirements-dev.txt \ + --depfile requirements-prod.txt path/to/dependency.file \ + --depfile Cargo.toml \ + --force-analysis \ + --all-deps' + } + } ``` ## Alternatives @@ -330,10 +334,10 @@ or "standalone" pipeline configuration, the solution is to force analysis of all is needed. This is done with the following flags: ```groovy - steps { - // Force analysis for all current dependencies. - sh 'phylum-ci --force-analysis --all-deps' - } + steps { + // Force analysis for all current dependencies. + sh 'phylum-ci -vv --force-analysis --all-deps' + } ``` It is also possible to make direct use of the [`phylum` Python package][pypi] within CI. diff --git a/src/phylum/ci/ci_base.py b/src/phylum/ci/ci_base.py index ba9e1ec5..f8aafd15 100644 --- a/src/phylum/ci/ci_base.py +++ b/src/phylum/ci/ci_base.py @@ -316,36 +316,54 @@ def phylum_project(self) -> str: LOG.debug("Project name provided as argument: %s", project_name) return project_name - LOG.info("Project name not provided as argument. Checking the `.phylum_project` file ...") + LOG.info("Project name not provided as argument. Checking `.phylum_project` file ...") project_name = self._project_settings.get("project") if project_name: LOG.debug("Project name provided in `.phylum_project` file: %s", project_name) return project_name - LOG.info("Project name not found in the `.phylum_project` file or file does not exist. Detecting instead ...") + LOG.info("Project name not found in `.phylum_project` file or file does not exist. Detecting instead ...") project_name = git_repo_name() LOG.debug("Project name detected from git repository name: %s", project_name) return project_name - @property + @cached_property def phylum_group(self) -> Optional[str]: """Get the effective Phylum group in use. The Phylum group name can be specified as an option or contained in the `.phylum_project` file. A group name provided as an input option will be preferred over an entry in the `.phylum_project` file. + Generate a warning when the possibility of unintended project/group pairings exist. This happens when + one of a project or group (but not both) is explicitly specified by argument and the other is specified + in the `.phylum_project` file. + Return `None` when the group name is not available. """ - # Group supplied on command-line - # A group can not be specified without a project - if self.args.group and self.args.project: - return self.args.group - - # Group supplied in `.phylum_project` - # Don't "mix" a project supplied as an option with a group that wasn't - if not self.args.project: - return self._project_settings.get("group") + group_name = self.args.group + if group_name: + LOG.debug("Group name provided as argument: %s", group_name) + if not self.args.project and self._project_file_already_existed: + msg = """ + Group name was explicitly specified but without a matching + project argument. This can result in creation of an unexpected + project/group pairing. Please check if this was intended.""" + LOG.warning(cleandoc(msg)) + return group_name + + LOG.debug("Group name not provided as argument. Checking `.phylum_project` file...") + group_name = self._project_settings.get("group") + if group_name: + LOG.debug("Group name provided in `.phylum_project` file: %s", group_name) + if self.args.project: + msg = """ + Project name was explicitly specified but without a matching + group argument. This can result in creation of an unexpected + project/group pairing. Please check if this was intended.""" + LOG.warning(cleandoc(msg)) + return group_name + LOG.debug("Group name not found in `.phylum_project` file or file does not exist. Assuming no group ...") return None @cached_property @@ -489,10 +507,10 @@ def update_depfiles_change_status(self, commit: str, err_msg: Optional[str] = No cmd = ["git", "diff", "--exit-code", "--quiet", commit, "--", str(depfile.path)] ret = subprocess.run(cmd, check=False) # noqa: S603 if ret.returncode == 0: - LOG.debug("The dependency file [code]%r[/] has [b]NOT[/] changed", depfile, extra=MARKUP) + LOG.debug("Dependency file [code]%r[/] has [b]NOT[/] changed", depfile, extra=MARKUP) depfile.is_depfile_changed = False elif ret.returncode == 1: - LOG.debug("The dependency file [code]%r[/] has changed", depfile, extra=MARKUP) + LOG.debug("Dependency file [code]%r[/] has changed", depfile, extra=MARKUP) depfile.is_depfile_changed = True else: if err_msg: @@ -526,10 +544,10 @@ def _ensure_project_exists(self) -> None: A project may or may not already exist. Attempt to create the project, possibly overwriting a `.phylum_project` file that already exists. Continue on without error when the specified project already exists. """ - LOG.info("Attempting to create a Phylum project with the name: %s ...", self.phylum_project) + LOG.info("Attempting to create a Phylum project with name: %s ...", self.phylum_project) cmd = [str(self.cli_path), "project", "create"] if self.phylum_group: - LOG.debug("Using Phylum group: %s", self.phylum_group) + LOG.info("Using Phylum group: %s", self.phylum_group) cmd.extend(["--group", self.phylum_group]) if self.repo_url: LOG.debug("Using repository URL: %s", self.repo_url) @@ -537,25 +555,86 @@ def _ensure_project_exists(self) -> None: cmd.append(self.phylum_project) if not Path.cwd().joinpath(".git").is_dir(): LOG.warning("Attempting to create a Phylum project outside the top level of a `git` repository") + try: subprocess.run(cmd, check=True, capture_output=True, text=True) # noqa: S603 - except subprocess.CalledProcessError as err: - # The Phylum CLI will return a unique error code when a project that already - # exists is attempted to be created. This situation is recognized and allowed to happen - # since it means the project exists as expected. Any other exit code is an error. - if err.returncode == CLIExitCode.PROJECT_ALREADY_EXISTS.value: - LOG.info("Project %s already exists. Continuing with it ...", self.phylum_project) + except subprocess.CalledProcessError as outer_err: + # The Phylum CLI will return a unique error code when a project/group pairing + # that already exists is attempted to be created. This situation is recognized + # and allowed to happen since it means the project exists as expected. + project_exists_msg = f""" + Project/group pairing already exists. Continuing with it. + Project: {self.phylum_project} + Group: {self.phylum_group or '(no group)'}""" + if outer_err.returncode == CLIExitCode.ALREADY_EXISTS.value: + LOG.info(cleandoc(project_exists_msg)) self._set_repo_url() return - msg = """ - There was a problem creating the project. - A PRO account is needed to create a project with a group. + + err_msg = """ + There was a problem creating the project. A paid account is needed to + use groups or create more than five projects: https://phylum.io/pricing If the command was expected to succeed, please report this as a bug.""" - raise PhylumCalledProcessError(err, cleandoc(msg)) from err - LOG.info("Project %s created successfully", self.phylum_project) + + # The problem may be that a group was specified but does not exist yet. Check for that case and create it, + # if needed. This is done here instead of pre-emptively in an effort to avoid extra group creation calls. + if not self._created_group(): + # A missing group was not the problem, which means the project creation attempt is an error. + raise PhylumCalledProcessError(outer_err, cleandoc(err_msg)) from outer_err + + # A missing group was created, which means we need to try to create the project again. + try: + subprocess.run(cmd, check=True, capture_output=True, text=True) # noqa: S603 + except subprocess.CalledProcessError as inner_err: + # Check for this error code again because it is possible the same + # project/group pairing was created elsewhere since the last check. + if inner_err.returncode == CLIExitCode.ALREADY_EXISTS.value: + LOG.info(cleandoc(project_exists_msg)) + self._set_repo_url() + return + # Any other exit code is an error. + raise PhylumCalledProcessError(inner_err, cleandoc(err_msg)) from inner_err + + project_created_msg = f""" + Project/group pairing created successfully. + Project: {self.phylum_project} + Group: {self.phylum_group or '(no group)'}""" + LOG.info(cleandoc(project_created_msg)) if self._project_file_already_existed: LOG.warning("Overwrote previous `.phylum_project` file found at: %s", self._phylum_project_file) + def _created_group(self) -> bool: + """Ensure a Phylum group is created and in place, when specified. + + A group may or may not already exist. Attempt to create the group when one is specified. + Continue on without error when the specified group already exists. + + Return True if a group was created and False otherwise. + """ + if not self.phylum_group: + LOG.debug("No Phylum group specified. Nothing to do.") + return False + + LOG.info("Attempting to create a Phylum group with name: %s ...", self.phylum_group) + cmd = [str(self.cli_path), "group", "create", self.phylum_group] + try: + subprocess.run(cmd, check=True, capture_output=True, text=True) # noqa: S603 + except subprocess.CalledProcessError as err: + # The Phylum CLI will return a unique error code when a group that already + # exists is attempted to be created. This situation is recognized and allowed to happen + # since it means the group exists as expected. Any other exit code is an error. + if err.returncode == CLIExitCode.ALREADY_EXISTS.value: + LOG.info("Group %s already exists. Continuing with it ...", self.phylum_group) + return False + msg = """ + There was a problem creating the group. + A paid account is needed to use groups: https://phylum.io/pricing + If the command was expected to succeed, please report this as a bug.""" + raise PhylumCalledProcessError(err, cleandoc(msg)) from err + + LOG.info("Successfully created group: %s", self.phylum_group) + return True + def _set_repo_url(self) -> None: """Set the repository URL for the project. @@ -698,7 +777,7 @@ def analyze(self) -> None: else: msg = """ There was a problem analyzing the project. - A PRO account is needed to use groups. + A paid account is needed to use groups: https://phylum.io/pricing If the command was expected to succeed, please report this as a bug.""" raise PhylumCalledProcessError(err, cleandoc(msg)) from err @@ -757,10 +836,10 @@ def _parse_analysis_result(self, analysis_result: str) -> None: # https://github.com/phylum-dev/phylum-ci/issues/357 if analysis.incomplete_count == 0: if analysis.is_failure: - LOG.error("The analysis is complete and there were failures") + LOG.error("Analysis is complete and there were failures") self.returncode = ReturnCode.POLICY_FAILURE else: - LOG.info("The analysis is complete and there were NO failures") + LOG.info("Analysis is complete and there were NO failures") self.returncode = ReturnCode.SUCCESS elif analysis.is_failure: LOG.error("There were failures in one or more completed packages") diff --git a/src/phylum/ci/cli.py b/src/phylum/ci/cli.py index f99c2cc6..34bd55ec 100644 --- a/src/phylum/ci/cli.py +++ b/src/phylum/ci/cli.py @@ -151,7 +151,9 @@ def get_args(args: Optional[Sequence[str]] = None) -> tuple[argparse.Namespace, analysis_group.add_argument( "-g", "--group", - help="Optional group name, which will be the owner of the project. Only used when a project is also specified.", + help="""Optional group name, which will be the owner of the project. Can also specify this option's value in the + `.phylum_project` file. The value specified with this option takes precedence when both are provided. Group + will be created if it does not already exist. Groups require a paid account: https://phylum.io/pricing""", ) analysis_group.add_argument( "-s", diff --git a/src/phylum/ci/common.py b/src/phylum/ci/common.py index 3595988e..587f21f0 100644 --- a/src/phylum/ci/common.py +++ b/src/phylum/ci/common.py @@ -116,8 +116,8 @@ class ReturnCode(IntEnum): class CLIExitCode(IntEnum): """Integer enumeration to track the Phylum CLI exit codes.""" - # A project that already exists is attempted to be created - PROJECT_ALREADY_EXISTS = 14 + # A project or group that already exists is attempted to be created + ALREADY_EXISTS = 14 # A manifest is attempted to be parsed but lockfile generation has been disabled MANIFEST_WITHOUT_GENERATION = 20