Skip to content

Commit

Permalink
Project Edit Command (#1395)
Browse files Browse the repository at this point in the history
* project edit implementation
  • Loading branch information
JeffreyHuynh1 authored Apr 26, 2024
1 parent 0479bae commit e4d3d6d
Show file tree
Hide file tree
Showing 40 changed files with 1,707 additions and 365 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## v3.9.14
- Update cargo strategy to parse new `cargo metadata` format for cargo >= 1.77.0 ([#1416](https://github.com/fossas/fossa-cli/pull/1416)).
- `fossa release-group`: Add command to create a FOSSA release group release (`fossa release-group create-release`) [#1409](https://github.com/fossas/fossa-cli/pull/1409).
- `fossa project`: Adds commands to interact with FOSSA projects (`fossa project edit`) [#1394](https://github.com/fossas/fossa-cli/pull/1395).

## v3.9.13
- Support GIT dependencies in Bundler projects ([#1403](https://github.com/fossas/fossa-cli/pull/1403/files))
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ Concept guides explain the nuances behind how basic FOSSA primitives work. If yo
- [`fossa snippets`](./references/subcommands/snippets.md): Analyze snippets of a project and check if they exist in other open source projects FOSSA knows about.
- [`fossa test`](./references/subcommands/test.md): View the results of the most recent scan of a project.
- [`fossa release-group`](./references/subcommands/release-group.md): Interact with FOSSA release groups.
- [`fossa project`](./references/subcommands/project.md): Interact with FOSSA projects.

#### Configuration

Expand Down
15 changes: 15 additions & 0 deletions docs/references/files/fossa-yml.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ server: https://app.fossa.com
apiKey: a1b2c3

project:
locator: custom+1/github.com/fossas/fossa-cli
id: github.com/fossas/fossa-cli
name: fossa-cli
team: cli-team
teams:
- cli-team-1
- cli-team-2
policy: custom-cli-policy
link: fossa.com
url: github.com/fossas/fossa-cli
Expand Down Expand Up @@ -123,6 +127,11 @@ The project fields allow you to configure settings for the project you are inter

> Note: `name`, `team`, `policy`, `link`, and `jiraProjectKey` can only be set when creating a project (running `fossa analyze` for the first time). Otherwise, they will be silently ignored (we would like to make this a visible warning in the future).

#### `project.locator:`
The project Locator defines a unique ID that the FOSSA API will use to reference this project within FOSSA. The project locator can be found in the UI on the project `Settings` page listed as the `Project Locator` underneath the `Project Title` setting.

<img src="../../assets/project-locator-example.png">

#### `project.id:`
The project ID defines an ID that is used to reference a project within your FOSSA organization. The project ID is a specific portion of the project locator and can be found in the UI on the project `Settings` page listed as the "Project Locator" underneath the "Project Title" setting. For example, if the "Project Locator" value of `custom+1/foo` is provided in the FOSSA UI, use `foo` for the `project.id`.

Expand All @@ -144,6 +153,12 @@ The name field sets the projects visible name in the FOSSA dashboard. By default
#### `project.team:`
The name of the team in your FOSSA organization to associate this project with.

#### `project.teams:`
The name of the teams in your FOSSA organization to associate this project with.

>NOTE:
Currently, ONLY `fossa project edit` utilizes this field. Use [fossa project edit](../subcommands/project/edit.md) to add a project to all teams in the list.

#### `project.policy:`
The name of the policy in your FOSSA organization to associate this project with.
Mutually excludes `project.policyId`.
Expand Down
14 changes: 13 additions & 1 deletion docs/references/files/fossa-yml.v3.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@
"type": "object",
"description": "The project fields allow you to configure settings for the project you are interacting with through the FOSSA API.",
"properties": {
"locator": {
"type": "string",
"minLength": 1,
"description": "The project Locator defines a unique ID that the FOSSA API will use to reference this project within FOSSA. The project locator can be found in the UI on the project `Settings` page listed as the `Project Locator` underneath the `Project Title` setting."
},
"id": {
"type": "string",
"minLength": 1,
"description": "The project ID defines a unique ID that the FOSSA API will use to reference this project. The project ID can be found in the UI on the project settings page listed as the `Project Locator` underneath the `Project Title` setting.\n\nBy default, it will use git remote origin url as project id if it's git repository. If it does not recognize version control system (vcs), project directory's name will be used."
"description": "The project ID defines a unique ID that the FOSSA API will use to reference this project within your organization. The project ID is a specific portion of the project locator and can be found in the UI on the project `Settings` page listed as the `Project Locator` underneath the `Project Title` setting.\n\nBy default, it will use git remote origin url as project id if it's git repository. If it does not recognize version control system (vcs), project directory's name will be used."
},
"name": {
"type": "string",
Expand All @@ -22,6 +27,13 @@
"minLength": 1,
"description": "The name of the team in your FOSSA organization to associate this project with."
},
"teams": {
"type": "array",
"description": "A list of team names in your FOSSA organization to associate this project with.",
"items": {
"type": "string"
}
},
"policy": {
"type": "string",
"minLength": 1,
Expand Down
27 changes: 27 additions & 0 deletions docs/references/subcommands/project.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## `fossa project`

This `fossa project` subcommand allows users to interact with FOSSA projects.

It has the following subcommand:

- [`fossa project edit`](./project/edit.md)

See the pages linked above for more details.

### `fossa project edit`

Edits a FOSSA project's settings and configurations.

Example:

```bash
fossa project edit --project-locator custom+1/example --title example-title --project-url github.com/fossas/fossa-cli --jira-project-key example-jira-key --link fossa.com --team example-team-1 --team example-team-2 --policy example-policy --project-label example-label-1 --project-label example-label-2
```

### F.A.Q.

1. Where can I find my project locator?

The project Locator defines a unique ID that the FOSSA API will use to reference this project within FOSSA. The project locator can be found in the UI on the project `Settings` page listed as the "Project Locator" underneath the "Project Title" setting.

<img src="../../assets/project-locator-example.png">
113 changes: 113 additions & 0 deletions docs/references/subcommands/project/edit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
## `fossa project edit`

`fossa project edit` allows you to edit a FOSSA project's settings and configuration.

## Options

Argument | Required | Description
-----------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------
`--config` / `-c` | No | The to your path to your `.fossa.yml`.
`--project-locator` | Yes | The project Locator defines a unique ID that the FOSSA API will use to reference this project within FOSSA. The project locator can be found in the UI on the project `Settings` page listed as the "Project Locator" underneath the "Project Title" setting.
`--project-id` | Yes | The project ID defines an ID that is used to reference a project within your FOSSA organization. The project ID is a specific portion of the project locator and can be found in the UI on the project `Settings` page listed as the "Project Locator" underneath the "Project Title" setting. For example, if the "Project Locator" value of `custom+1/foo` is provided in the FOSSA UI, use `foo`. Project ID defaults to the .git/config file or project's remote "origin" URL (Git), "Repository Root" obtained using 'svn info' (SVN), or the name of the project's directory (No VCS), if project ID wasn't explicityly set during project creation.
`--title` / `-t` | No | The title of the FOSSA project.
`--project-url` | No | The url of the project's repository.
`--jira-project-key` / `-j` | No | The JIRA project key to associate to the FOSSA project.
`--link` / `-L` | No | A link to attach to the FOSSA project.
`--team` / `T` | No | The name of the team that will be associated with the FOSSA project. Specify multiple options by providing this argument multiple times.
`--project-label` | No | The labels associated with the FOSSA project. Assign up to 5 labels for a project. Specify multiple options by providing this argument multiple times.

> NOTE: The arguments listed as `Required` need to be provided through CLI options OR through your `.fossa.yml` configuration.
> NOTE: Either project ID OR project locator needs to be set. Project ID takes precedence over project locator. For more details on the differences between project ID and project locator refer to [documentation](../../files/fossa-yml.md#what-is-the-difference-between-project-id-and-project-locator).
## .fossa.yml Configuration

All of the previously mentioned CLI options can be provided through a `.fossa.yml`. Refer to [fossa configuration](../../files/fossa-yml.md) to set up your `.fossa.yml`.

> NOTE: CLI options take precedence over the configurations in `.fossa.yml`.
## `fossa project edit` usage and guidance

### Updating a project's labels

When updating project labels through `fossa project edit`, the transaction is all or nothing. This means that the project labels specified through this command will overwrite the existing labels that are associated with the project. Be sure to include all the labels that you want to be associated with the project, even if some labels are already currently set.

### Updating the teams associated with a project

Currently, `fossa project edit` only supports adding a project to the teams that are specified through the command. There will be support to remove a project from the provided teams in the future.

Providing teams for `fossa project edit` takes the following precdence:

1. CLI options - Adds the project to teams (1 or many)
2. `project.teams` in `.fossa.yml` - Adds the project to teams (1 or many)
3. `project.team` in `.fossa.yml` - Adds the project to a team

There is support for `project.team` due to backwards compatability as `project.teams` is a newly added field in `.fossa.yml`.

## Example

### Project locator example
Given a project with project locator: `custom+1/example`, the following command:

- Sets the project's title to `example-title`
- Set the the project's url to `github.com/fossas/fossa-cli`
- Sets the project's JIRA key to `example-jira-key`
- Attaches link: `fossa.com` to the project
- Adds the project to teams: `example-team-1`, `example-team-2`
- Attaches policy: `example-policy` to the project
- Attaches labels: `example-label-1` , `example-label-2` to the project

```bash
fossa project edit --project-locator custom+1/example --title example-title --project-url github.com/fossas/fossa-cli --jira-project-key example-jira-key --link fossa.com --team example-team --team example-team-2 --policy example-policy --project-label example-label-1 --project-label example-label-2
```

Similarly, you can you achieve the same result by running the following command with the given `.fossa.yml` configuration:

```bash
fossa project edit --config /path/to/config
```

```yaml
project:
locator: custom+1/example
name: example-title
teams:
- example-team-1
- example-team-2
policy: example-policy
link: fossa.com
url: github.com/fossas/fossa-cli
jiraProjectKey: example-jira-key
labels:
- example-label-1
- example-label-2
```
### Project ID example
Achieve the same result as defined above (for projects created through the CLI) using project ID:
```bash
fossa project edit --project-id example --title example-title --project-url github.com/fossas/fossa-cli --jira-project-key example-jira-key --link fossa.com --team example-team --team example-team-2 --policy example-policy --project-label example-label-1 --project-label example-label-2
```

Similarly, you can you achieve the same result by running the following command with the given `.fossa.yml` configuration:

```bash
fossa project edit --config /path/to/config
```

```yaml
project:
id: example
name: example-title
teams:
- example-team-1
- example-team-2
policy: example-policy
link: fossa.com
url: github.com/fossas/fossa-cli
jiraProjectKey: example-jira-key
labels:
- example-label-1
- example-label-2
```
8 changes: 8 additions & 0 deletions spectrometer.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ library
App.Fossa.Analyze.Upload
App.Fossa.API.BuildLink
App.Fossa.API.BuildWait
App.Fossa.ApiUtils
App.Fossa.ArchiveUploader
App.Fossa.BinaryDeps
App.Fossa.BinaryDeps.Jar
Expand All @@ -205,6 +206,8 @@ library
App.Fossa.Config.LicenseScan
App.Fossa.Config.LinkUserBinaries
App.Fossa.Config.ListTargets
App.Fossa.Config.Project
App.Fossa.Config.Project.Edit
App.Fossa.Config.ReleaseGroup
App.Fossa.Config.ReleaseGroup.AddProjects
App.Fossa.Config.ReleaseGroup.Common
Expand Down Expand Up @@ -238,6 +241,8 @@ library
App.Fossa.ManualDeps
App.Fossa.PathDependency
App.Fossa.PreflightChecks
App.Fossa.Project
App.Fossa.Project.Edit
App.Fossa.ProjectInference
App.Fossa.Reachability.Gradle
App.Fossa.Reachability.Jar
Expand Down Expand Up @@ -379,6 +384,7 @@ library
Effect.Grapher
Effect.Logger
Effect.ReadFS
Fossa.API.CoreTypes
Fossa.API.Types
Graphing
Graphing.Debug
Expand Down Expand Up @@ -541,6 +547,7 @@ test-suite unit-tests
App.Fossa.AnalyzeSpec
App.Fossa.API.BuildLinkSpec
App.Fossa.API.BuildWaitSpec
App.Fossa.ApiUtilsSpec
App.Fossa.ArchiveUploaderSpec
App.Fossa.BinaryDeps.JarSpec
App.Fossa.Config.AnalyzeSpec
Expand All @@ -558,6 +565,7 @@ test-suite unit-tests
App.Fossa.ManualDepsSpec
App.Fossa.PathDependencySpec
App.Fossa.PreflightChecksSpec
App.Fossa.Project.EditSpec
App.Fossa.ProjectInferenceSpec
App.Fossa.ReleaseGroup.AddProjectsSpec
App.Fossa.ReleaseGroup.CommonSpec
Expand Down
66 changes: 66 additions & 0 deletions src/App/Fossa/ApiUtils.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module App.Fossa.ApiUtils (
retrievePolicyId,
retrieveTeamIds,
retrieveLabelIds,
retrieveTeamIdsWithMaybe,
) where

import Control.Algebra (Has)
import Control.Effect.Diagnostics (Diagnostics, errHelp, fatalText)
import Data.Map qualified as Map
import Data.Maybe (mapMaybe)
import Data.Text (Text, intercalate)
import Effect.Logger (pretty, renderIt, vsep)
import Fossa.API.CoreTypes (Label (..), Labels (..), Policy (..), PolicyType (..), Team (..))

retrievePolicyId :: Has Diagnostics sig m => Maybe Text -> PolicyType -> [Policy] -> m (Maybe Int)
retrievePolicyId maybeTitle targetType policies = case maybeTitle of
Nothing -> pure Nothing
Just targetTitle -> do
let filteredPolicies = filter (\p -> policyTitle p == targetTitle && (policyType p == targetType)) policies
case filteredPolicies of
[] -> fatalText $ "Policy `" <> targetTitle <> "` not found"
[policy] -> pure . Just $ policyId policy
(_ : _ : _) ->
errHelp ("Navigate to the FOSSA web UI to rename your policies so that they are unqiue" :: Text)
. fatalText
$ "Multiple policies with title `" <> targetTitle <> "` found. Unable to determine which policy to use."

retrieveTeamIdsWithMaybe :: Has Diagnostics sig m => Maybe [Text] -> [Team] -> m (Maybe [Int])
retrieveTeamIdsWithMaybe maybeTeamNames teams = case maybeTeamNames of
Nothing -> pure Nothing
Just teamNames -> Just <$> retrieveTeamIds teamNames teams

retrieveTeamIds :: Has Diagnostics sig m => [Text] -> [Team] -> m [Int]
retrieveTeamIds teamNames teams = do
let teamMap = Map.fromList $ map (\team -> (teamName team, teamId team)) teams
validTeamIds = mapMaybe (`Map.lookup` teamMap) teamNames

if length teamNames == length validTeamIds
then pure validTeamIds
else do
let missingTeamNames = filter (`Map.notMember` teamMap) teamNames
fatalText $ "Teams " <> intercalate "," missingTeamNames <> " not found"

retrieveLabelIds :: Has Diagnostics sig m => [Text] -> Labels -> m ([Int], Maybe [Text])
retrieveLabelIds projectLabels (Labels orgLabels) = do
let orgLabelMap = Map.fromList $ map (\label -> (labelName label, labelId label)) orgLabels
go orgLabelMap projectLabels []
where
go :: Has Diagnostics sig m => Map.Map Text Int -> [Text] -> [Int] -> m ([Int], Maybe [Text])
go _ [] acc = pure (acc, Nothing)
go labelMap (x : xs) acc = do
case Map.lookup x labelMap of
Just labelId' -> go labelMap xs (labelId' : acc)
Nothing -> do
(labelIds, maybeWarnings) <- go labelMap xs acc
let warning =
renderIt $
vsep
[ "Label `" <> pretty x <> "` does not exist"
, "Navigate to `Organization Settings` in the FOSSA web UI to create new labels: https://app.fossa.com/account/settings/organization"
]
let updatedWarnings = case maybeWarnings of
Just warnings -> Just (warning : warnings)
Nothing -> Just [warning]
pure (labelIds, updatedWarnings)
Loading

0 comments on commit e4d3d6d

Please sign in to comment.