Coder Social home page Coder Social logo

corgibytes / freshli-cli Goto Github PK

View Code? Open in Web Editor NEW
8.0 7.0 5.0 4.07 MB

A tool for displaying historical metrics about a project's dependencies. Run the Freshli CLI on your project to see how its dependency freshness changes over time.

Home Page: https://freshli.app

License: GNU Affero General Public License v3.0

C# 92.96% Dockerfile 0.77% Ruby 2.58% Gherkin 2.48% Shell 1.20%
dependencies libyear sbom-tool metrics

freshli-cli's Introduction

Freshli Logo

Freshli Command Line

Maintainability Test Coverage

A tool for displaying historical metrics about a project's dependencies. Run the Freshli CLI on your project to see how its dependency freshness changes over time.

Installing and Running

freshli is only distributed in binary form via a Docker container image. The Docker container image includes the freshli-agent-java executable. As new language agents are released, these will be added to the container image as well.

If you don’t want to use Docker, you’ll need to build from source and configure your environment appropriately.

Using Docker

Use the docker pull command to retrieve the latest version of the container image.

docker pull corgibytes/freshli-cli:latest

It’s a good idea to run this command periodically to check for new versions.

Next pass the --version option to check the version that’s been retrieved.

docker run --rm corgibytes/freshli-cli --version

analyze command

The analyze command is used to compute LibYear metrics from a local or remote Git repository. The LibYear metric is computed for every package found in every dependency manifest file for the entire history of the repository branch that is being analyzed.

Data is sent to the Freshli app website via API calls. You need to visit the link that’s provided in the analyze command’s output to view the results. There is no other supported way to access the collected data at this time.

❗ Warning

The analyze command may take a long time to complete. For some projects, it may take well over an hour. How long it takes is dependent on several variables, such as the amount of history in the Git repository and the number of packages that are found at each point in time. The performance characteristics of the computer running the command is also a factor.

📝 Suggestion

On Windows, you will see performance benefits if you exclude the cache directory (default $HOME/.freshli) from real-time security analysis by the malware engine that's built into Windows. You can do this by opening a PowerShell session as an administrator and running the following command:

  Add-MpPreference -ExclusionPath $HOME/.freshli

If you're working with this repository as a developer, then you'll also want to add an exclusion for the ./tmp directory, which is used by the test suite.

Analyzing a remote Git repository

To analyze a remote Git repository, provide a Git URL that can be used to clone the repository, such as https://github.com/corgibytes/freshli-fixture-java-test.

docker run --rm corgibytes/freshli-cli analyze https://github.com/corgibytes/freshli-fixture-java-test

Unless a branch name is provided with the --branch option, the default branch will be used.

Analyzing a local Git repository

To analyze a local Git repository, one that has already been cloned, provide a file system path to the location of the repository. The branch that is checked out is the one that will be analyzed.

git clone https://github.com/corgibytes/freshli-fixture-java-test
docker run --rm corgibytes/freshli-cli analyze freshli-fixture-java-test

Adjusting the history interval

By default, the analyze command computes metrics at one month intervals. This value can be changed with the --history-interval option. Valid values are in the form of <number><unit>, where <number> is a positive integer and <unit> is either y for years, m for months, w for weeks, or d for days.

The following command sets the history interval to 2 weeks.

docker run --rm corgibytes/freshli-cli analyze --history-interval=2w https://github.com/corgibytes/freshli-fixture-java-test

Analyzing every commit

It is possible for some commits to get skipped depending on the history interval that is selected. Specifying the --commit-history option will instruct the analyze to compute metrics for every commit regardless of the history interval value.

Example:

docker run --rm corgibytes/freshli-cli analyze --commit-history https://github.com/corgibytes/freshli-fixture-java-test

Analyzing only the latest commit

For times when you would like to prevent the collection of historical metrics, use the --latest-only option.

Example:

docker run --rm corgibytes/freshli-cli analyze --latest-only https://github.com/corgibytes/freshli-fixture-java-test

Adjusting the number of workers

The analyze command, like many freshli commands, employs background workers to make full use of available CPU resources. You can use the --workers option to control the number of background workers that are used.

Example:

docker run --rm corgibytes/freshli-cli --workers=2 analyze https://github.com/corgibytes/freshli-fixture-java-test

agents detect command

This command is used to determine the language agents that are available to freshli.

docker run --rm corgibytes/freshli-cli agents detect

agents verify command

This command is used to determine if the the language agents behave in the way that Freshli expects.

docker run --rm corgibytes/freshli-cli agents verify

Supported Dependency Managers

The freshli executable does not have built-in support for processing dependency manifest files. Language-specific agent programs, executables with names starting with freshli-agent-, provide the ability to process dependency manifests from different language ecosystems.

Here is a list of language agents that have been developed so far and are included in the Docker container image mentioned above.

Language Agent Dependency Manager
Java freshli-agent-java Maven

Please let us know what other dependency managers and/or manifest files you would like use to support via the contact information in the Contributing section.

Metrics

The freshli analyze command computes the LibYear metric.

Libyear

The libyear for a dependency is calculated by dividing the days between the current version and latest version by 365. Yes we know we shouldn't always use 365, we will fix it in a future release. For example, if the days between the current dependency and the latest is 42 days then the libyear is:

42 / 365 = 0.1151

Say you have 4 dependencies that are 128, 256, 512, and 1024 the libyear would be:

(128 / 365) + (256 / 365) + (512 / 365) + (1024 / 365) =
0.3507 + 0.7014 + 1.4027 + 2.8055 =
5.2603

That means you dependencies are 5.3 libyears out of date or 5 libyears and 109.5 libdays.

Note: The latest dependency is determined based on date the check is run. For example, if a dependency has the following release dates:

Jan 1, 2019 (v1.0.0)
Jan 26, 2019 (v1.0.1)
Apr 3, 2019 (v1.1.0)
Sep 15, 2019 (v1.2.0)
Oct 31, 2019 (v1.2.1)

When checking the libyear on May 1, 2019 Freshli will use v1.1.0 (Apr 3rd, 2019) as the latest dependency. So if as of May 1, 2019 your project uses v1.1.0 your libyear is zero as v1.2.0 was not released until Sep. If on May 1st your project is using v1.0.0 then your libyear is days between Apr 3, 2019 and Jan 1, 2019 which is 93 days so you get a libyear of:

93 / 365 = 0.2548

If you have v1.0.1 installed then your libyear when checking on May 1, 2019 is 68 days for a libyear of:

68 / 365 = 0.1863

Culture and Language Support

The headings for column output are localized such that the culture settings of the user's computer are used. (This is found in the CurrentUICulture). Currently there are English and Spanish translations with English being the default.

Data (such as dates and numeric formatting) are NOT localized. Dates and numeric formats use the CurrentCulture which is explicitly set to the invariant culture.

We are not sure how to handle documentation, such as this ReadMe, in different languages. If you have any suggestions or would like to help with translations please let us know using the contact information in the Contributing section.

Logging

By default all logs are set to WARN level and sent to the console output.

Log levels can be adjusted by using the --logLevel <level> option when running the application. The level can be any level that is supported by NLog:

  • Trace
  • Debug
  • Info
  • Warn
  • Error
  • Fatal

Logs can be redirected to a file instead by using the --logfile <file_path_and_name> option when running the application.

Building

⚙️ Prerequisites

  • The scripts in the bin/ directory require ruby version 3.1 or later to be installed.
  • Make sure you have the latest .NET 7.0 SDK installed before attempting to run any of the commands below.

The project can be built using the bin/build.rb script.

To build manually, you first need to install the DotNet tools that are used by the project with:

dotnet tool restore

Then you can build the freshli executable and place it in the exe directory (where the acceptance tests expect it to be located) with:

dotnet build -o exe

Linting

We use a few different automatted tools tools to help us keep the code in this repository in compliance with the Freshli project style guide.

All of the following linters (with the exception of codeclimate) can be run together by running the bin/lint.rb script. You can also run the bin/format.rb script if you want to instruct the linters to correct any issues that are found. (Note: not all of the linters provide an auto-correct mechanism.)

The eclint project helps us validate that the files in the project are formatted consistently with respect to the rules that have been defined in the .editorconfig file.

After making sure the eclint executable's in your path, it can be run with:

eclint

The rubocop project help us validate that the Ruby code we're writing conforms with the Ruby Style Guide that it is based on.

After running bundle install, the following will run rubocop:

bundle exec rubocop

The dotnet format command helps us make sure that our code is formatted consistent with the .NET/C# specific settings that are present in the .editorconfig file along with sets of validation rules that the project has been configured to use.

To determine if any style changes are needed, you can run:

dotnet format --verify-no-changes --severity info

To instruct dotnet format to attempt to correct the issues that it has found, you can run:

dotnet format --severity info

dotnet jb

Known Issue

If you encounter linter errors that persist despite being explicitly suppressed, try clearing the cache for JetBrains inspect code. On Windows the cache is located here %LocalAppData%\JetBrains\Transient\InspectCode\v212\SolutionCaches On Linux and macOS the cache is located here ~/.local/share/JetBrains/Transient/InspectCode/v221/SolutionCaches

codeclimate

There are two ways to run the codeclimate linter, by using the codeclimate CLI or by using docker. For both options, you'll need docker installed, because the codeclimate CLI is just a wrapper that makes it easy to run the codeclimate Docker image.

  1. Using the codeclimate CLI

    Note: This option will not work if you're working with the DevContainer.

    With the codeclimate CLI in your path, simply run the following to execute the CodeClimate analysis:

    codeclimate analyze
  2. Using docker

    Since codeclimate CLI is a wrapper around the codeclimate docker image the following command can be used to run the analysis:

    docker run \
        --interactive --tty --rm \
        --env CODECLIMATE_CODE="$PWD" \
        --volume "$PWD":/code \
        --volume /var/run/docker.sock:/var/run/docker.sock \
        --volume /tmp/cc:/tmp/cc \
        codeclimate/codeclimate analyze

    The above command will need to be changed if you're attempting to run codeclimate from within the DevContainer. This is because $PWD in the command above will expand to be the path to the project source code as it is mounted in the container. The docker command needs the path to the source code on your host system.

    To address this you'll need to start the DevContainer with an environment variable that contains the path to the source code on the host system. Here, we'll use $CODE_FOLDER.

    Another thing that needs to be done is to mount the socket that's used for communicating with Docker on the host system.

    docker build -t freshli-cli-dev .devcontainer
    docker run \
        --interactive --tty --rm \
        --env CODE_FOLDER=$PWD \
        --volume $PWD:/code \
        --volume /var/run/docker.sock:/var/run/docker.sock \
        --user vscode \
        --workdir /code \
        freshli-cli-dev bash

    And then from within that shell environment you can run codeclimate with:

    sudo docker run \
        --interactive --tty --rm \
        --env CODECLIMATE_CODE="$CODE_FOLDER" \
        --volume "$CODE_FOLDER":/code \
        --volume /var/run/docker.sock:/var/run/docker.sock \
        --volume /tmp/cc:/tmp/cc \
        codeclimate/codeclimate analyze

    Also, note in the above command that we're using sudo to run the docker command. This is because of the permissions that are required to access the Docker socket from the host system.

docker run \
    --interactive --tty --rm \
    --env CODECLIMATE_CODE="$CODE_FOLDER" \
    --volume "$CODE_FOLDER":/code \
    --volume /var/run/docker.sock:/var/run/docker.sock \
    --volume /tmp/cc:/tmp/cc \
    codeclimate/codeclimate analyze

Testing

You can run the unit, integration, and acceptance tests by running the bin/test.rb script.

Unit and Integration Tests

Installing freshli-agent-java into the path

⚠️ Important Note

Some of the integration tests require freshli-agent-java to be correctly installed in the path.

You'll need to have the Eclipse Temurin version of Java 17 installed before running the following commands. And you'll need Maven for some of the agent commands.

cd /tmp
git clone https://github.com/corgibytes/freshli-agent-java
cd freshli-agent-java
./gradlew installDist
mkdir -p /usr/local/share/freshli-agent-java
cp -r build/install/freshli-agent-java/* /usr/local/share/freshli-agent-java
ln -s /usr/local/share/freshli-agent-java/bin/freshli-agent-java /usr/local/bin/freshli-agent-java
cd ~
rm -rf /tmp/freshli-agent-java

Running the Unit and Integration Tests

The project's unit and integration tests can be run with:

dotnet test

Acceptance Tests

Freshli's acceptance test suite, built using Aruba and Cucumber, is pre-configured in the repository.

You will need Ruby installed on your system, and then run:

gem install bundler
bundle

From then on, you can run the Aruba tests with:

dotnet build -o exe && bundle exec cucumber

Collecting Code Coverage for the Acceptance Tests

Code coverage data can be collected for the acceptance tests. This activity is performed by the project's continuous integration environment where the collected data is sent to CodeClimate for further tracking. You can also run the code coverage collection locally.

First you'll need to make sure that the correct version of the Coverlet code coverage tool is installed:

dotnet tool restore

📙 Take Note

Make sure you run bin/build.rb before running any of the following commands.

Collecting Coverage for the Entire Test Suite

The following command can be used to compute the total test coverage across the .NET-based unit and integration tests combined with the Cucumber-based acceptance tests.

dotnet coverlet --target "./bin/test.rb" --targetargs "--skip-build" ./exe

Collecting Coverage for .NET-Based Test Suite

The following command will report the code coverage of the tests that are authored using the .NET-based testing tools.

dotnet coverlet --target "dotnet" --targetargs "test exe/Corgibytes.Freshli.Cli.Test.dll" ./exe

Collecting Coverage for the Cucumber-based Acceptance Test Suite

The following command will list the code coverage for the Cucumber-based tests.

dotnet coverlet --target "bundle" --targetargs "exec cucumber" ./exe

Working with the DevContainer

This project uses DevContainer to assist with creating a full configured development environment.

There are two paths to working with this DevContainer setup.

  1. Install the devcontainer CLI and then run devcontainer build followed by devcontainer open. That will open Visual Studio Code running from inside of a container with everything needed to build the project.

  2. Run docker directly. Run docker build -t freshli-cli-dev .devcontainer/ to build the container. Then you'll be able to run docker run --rm -it -v $PWD:/code -w /code freshli-cli-dev bash to create a shell session inside of a running container with everything set up for you. (Note, you may need to run bundle install when you first start the container to install the ruby-based dependencies. This step is performed for you if you use the devcontainer CLI to open a Visual Studio Code instance.)

Production Docker container

Building for local use

A production-ready container can be created from the Dockerfile in this project by running:

docker build -t freshli-cli .

You can then run the container with:

docker run --rm freshli-cli agents detect

You should see output that looks similar to:

❯ docker run --rm freshli-cli agents detect
+------------------+---------------------------------+
|Agent file        |Agent path                       |
+------------------+---------------------------------+
|freshli-agent-java|/usr/local/bin/freshli-agent-java|
+------------------+---------------------------------+

Manually publishing to DockerHub

Docker images are built and published to DockerHub by the CI process whenever a commit is added to the main or release/* branches (assuming that all of the tests have passed).

Follow these instructions if you need to produce a build manually.

  1. Log into DockerHub

    The account that you login with will need to have write permissions for the corgibytes/freshli project.

    docker login
    
  2. Create a local buildx node

    docker buildx create --use
    
  3. Build and publish

    This will create images that can run on Intel/AMD 64-bit or ARM 64-bit processors.

    You'll need to update the tag list with the specific tag list to include the specific tags that you want to publish.

    docker buildx build \
       --push \
       --platform linux/arm64/v8,linux/amd64 \
       --tag corgibytes/freshli-cli:latest \
       .
    

Cache Database Migrations

This project uses C#'s Code First Migrations: https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/migrations/ Migrations allow us to keep track of changes we make to models saved in a database, and it keeps our databases up-to-date.

Creating a new migration

From the CLI:

  1. dotnet ef migrations add [name]

Running migrations

By default it'll update to the latest available migration.

From the CLI:

  1. dotnet ef database update

Reverting migrations

Reverting a migration is done similarly as to updating the database though we are now specifying til what point in time we want to revert.

From the CLI:

  1. dotnet ef database update [specific migration name]

Activities and Events

See the documentation for Activities and Events, which includes a diagram showing the relationships between the current set of activities and events.

The diagram referenced above is generated as part of our continuous integration process. This ensures that it stays up-to-date as changes are made to the application.

Generating an Activity/Event diagram on demand

To update the contents of docs/activities-and-events.md with an updated diagram, you can simply run:

bin/generate-diagram.rb

If you want just the raw diagram text, you can run:

dotnet run --project diagram-generator

The text that is output can be pasted into mermaid.live to see the rendered diagram.

Contributing

If you have any questions, notice a bug, or have a suggestion/enhancment please let us know by opening a issue or pull request.

See the Contributing guide guide for developer documentation.

freshli-cli's People

Contributors

codemouse92 avatar dan-hein avatar dependabot[bot] avatar dfar-io avatar donabp avatar dotnet-updater[bot] avatar doughertym avatar edwinkortman avatar github-actions[bot] avatar klhoot avatar lizcorgi avatar mairadanielaferrari avatar mfcorgi avatar mrbiggred avatar mscottford avatar nickiemc avatar repocorp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

freshli-cli's Issues

Add `analyze` command

NOTE: This issue is still in a draft state. Its contents will likely change as new commands are identified as part of the pipeline.

analyze

The primary user-facing command. This command will delegate to other freshli commands to accomplish its work. It will manage work queues to enable parallelization.

freshli [global options] analyze [command options] <repo url>|<local dir>

What this command does?

With no options specified, performs analysis locally, and then sends the results to the Freshli web app so that the results can be viewed at at URL that will be provided in the command output.

Global Options

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Command Options

  • --git-path
    • Path to the git binary
    • default value: git
  • --branch <name>
    • The branch to checkout after cloning the repository
    • If the option is not specified, then no checkout command will be issued. The remote server’s default branch will be used instead.
  • --commit-history (this might be moved to the "build later" category per #188)
    • Analyzes only the points in time when files have changed in the commit history. This can be combined with --history-invertal to make sure that every commit is included in the result set. If --history-interval is not used, then only the values from each commit will be present in the output, meaning that the data will show the metrics at the time that the files were changed, but will not show how the metrics changed between commits.
  • --history-interval <duration> (this might be moved to the "build later" category per #187)
    • As the analyze command moves backwards in time through the history of the project, what time interval should it use to determine the points in time that are sampled
    • default value: 1m
    • valid values: /\d+[ymwd]?/
      • y suffix – specifies duration in years
      • m suffix – specifies duration in months
      • w suffix – specifies duration in weeks
      • d suffix – specifies duration in days
      • If no suffix is provided, then the duration is assumed to be a number of months
    • When walking backward in time the following approach will be taken: given current time T, subtract the smallest amount of time to produce time T' such that T' when be an even interval of the specified duration suffix.
      • Example:
        • Assume
          • T = 2022-02-01 23:11:43Z
          • Interval duration — 1d
          • Timezone offset — -04:00
          • Then T' would be set to 2022-02-01 00:00:00-04:00
          • The value of T' would be decremented by one calendar day until the earliest commit date is reached
  • --workers — the number of worker processes that should be running at any given time. This defaults to twice the number of CPU cores.

Running this command will dispatch a StartAnalysisActivity. Additionally, this command will be responsible for adjusting the number of application engine workers to match the provided value of the --workers parameter.

Tasks:

  • Add Cucumber/Aruba-based acceptance tests for this command
  • Implement handling analyze command
    • Adjust number of workers (this could be considered a nice to have if it turns out to be difficult)
    • Pass command line parameters to StartAnalysisActivity (all except --workers should be passed in)
  • Remove the following commands
    • git checkout-history
    • git compute-history
    • git clone
    • compute-libyear
  • Write results to freshli-web database
    • TBD columns

Add `bom generate-histories` command

bom generate-histories

Used to generate CycloneDX BOM files for each each historical point for the given repository.

freshli [global options] bom generate-histories [command options] <repository-id>

What this command does?

Iterates over the recorded historical intervals that are defined for the repository and invokes the bom generate-history command for each one. The number of copies of the bom generate-history command that are allowed to run at one time is controlled by the --workers parameter.

Global Options

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Command Options

  • --workers
    • the number of worker processes that should be running at any given time. This defaults to twice the number of CPU cores.
    • default: N+1 where N is the number of cores that are detected in the CPU

Add Scan and Help commands

Spawned from #23. Currently the CLI does not command line parsing and just assume the first argument passed in is the repository. We need to create a framework/design for parsing more complicated command lines.

This issue only encompasses adding the framework and adding the scan and help commands. No other commands should be added.

Currently the CLI supports the following command which scans the repository:

freshli-cli http://url/to/git/repo

After this issue is implemented the above existing command should still work but with the scan command:

freshli-cli scan <git-repo>

There should also be a help command:

freshli-cli help [command]

The help should display something like the below. Will have to work with someone smarter then myself to create a nice looking help message.

Freshli checks your projects dependency Freshness (i.e. how out of up to date are your dependencies). 

Supported commands:
  scan: Scan the given repository for freshness history.
    freshli-cli scan <git-repo>
    
  help: Display the help for commands.  If not arguments are supplies will show all the high level commands (i.e. what you are viewing now).
    freshli-clie help [command]

Examples:

Scan the Freshli Ruby test repository:
  freshli-cli scan https://github.com/corgibytes/freshli-fixture-ruby-nokotest

Help for the scan command:
  freshli-cli help scan

The help command for scan:

freshli-cli help scan

Should display something like:

Computes the historical Freshness of your repository.  Data is returned in <some format>.

scan: freshli-cli scan <git-repo>

  Arguments:
    git-repo: The Git repository to parse.  Supports `git://` or `https://` paths.

Examples:

Scan the Freshli Ruby test repository:
  freshli-cli scan https://github.com/corgibytes/freshli-fixture-ruby-nokotest

Workflows are referencing vulnerable actions

Hello, there!

As part of the university research we are currently doing regarding the security of Github Actions, we noticed that one or many of the workflows that are part of this repository are referencing vulnerable versions of the third-party actions. As part of a disclosure process, we decided to open issues to notify GitHub Community.

Please note that there are could be some false positives in our methodology, thus not all of the open issues could be valid. If that is the case, please let us know, so that we can improve on our approach. You can contact me directly using an email: ikoishy [at] ncsu.edu

Thanks in advance

  1. The workflow ci.yml is referencing action gittools/actions/gitversion/setup using references v0.9.9. However this reference is missing the commit 90150b4 which may contain fix to the vulnerability.
  2. The workflow ci.yml is referencing action gittools/actions/gitreleasemanager/setup using references v0.9.9. However this reference is missing the commit 90150b4 which may contain fix to the vulnerability.
  3. The workflow ci.yml is referencing action gittools/actions/gitversion/execute using references v0.9.9. However this reference is missing the commit 90150b4 which may contain fix to the vulnerability.
  4. The workflow ci.yml is referencing action gittools/actions/gitversion/execute using references v0.9.9. However this reference is missing the commit 90150b4 which may contain fix to the vulnerability.
  5. The workflow ci.yml is referencing action gittools/actions/gitversion/execute using references v0.9.9. However this reference is missing the commit 90150b4 which may contain fix to the vulnerability.
  6. The workflow ci.yml is referencing action gittools/actions/gitreleasemanager/addasset using references v0.9.9. However this reference is missing the commit 90150b4 which may contain fix to the vulnerability.

The vulnerability fix that is missing by actions' versions could be related to:
(1) CVE fix
(2) upgrade of vulnerable dependency
(3) fix to secret leak and others.
Please consider updating the reference to the action.

If you end up updating the reference, please let us know. We need the stats for the paper :-)

Add `bom detect-all-manifests` command

bom detect-all-manifests

Used to detect all manifest files that are present in the directory associated with the provided repository id and date-time pair.

freshli [global options] bom detect-all-manifests [command options] <repository-id> <date-time>

What this command does?

For each language-specific agent that is detected (by delegating to the agents detect command), delegates to that language agent’s detect-manifests command.

Each detected manifest will be stored in the cache database with a reference to the repository history that it belongs to (determined by the parameters passed in on the command line), the name of the language agent that detected it, an auto-generated manifest id (sha1 of a guid), a sha hash of the manifest file contents, and a relative path to the manifest file from the root of the checked out history directory. This cached information will be used by the bom process-all-manifests command. If the directory associated with the provided date-time has already been scanned for manifest files, then the cached information will be duplicated for the provided date-time.

Language agents are detected by searching the $PATH for a program name that starts with freshli-agent-. It is assumed that anything after this prefix is the language name, and would be used whenever referring to the agent that directly. It is assumed that the language agent knows how to detect all manifest files for that languages ecosystem, even in the case where the language ecosystem uses a variety of manifest file formats. For example, Maven and Gradle competing dependency managers in the Java ecosystem, and each one has its own manifest file format for documenting dependencies. It is assumed that the freshli-agent-java executable knows how to detect both file formats.

Global Options

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Command Options

  • --workers
    • the number of worker processes that should be running at any given time. This defaults to twice the number of CPU cores.
    • default: N+1 where N is the number of cores that are detected in the CPU

Modify `Cache` to use a logger for writing output

Inspired by a conversation in pull request #81.

The Cache class is throwing exceptions containing information that needs to be written to the console. An exception class is best used for "exceptional" or unexcepted scenarios. The scenarios in question are expected to happen from time to time. A cleaner way of passing these messages to the console would be to use a logger instance that is wired up to output to the console, either to STDERR or STDOUT based on the method that is called on the logger.

This should simplify the calling code because it will not need to have knowledge about special kinds of exceptions that the Cache class might throw and route those to the correct output stream.

[EPIC] Implement CycloneDX-based pipeline

What’s this?

A re-imagining of Freshli that takes advantage of the ecosystem of tools that generate CycloneDX BOM files. The current idea is to drive this from the Freshli CLI.

Architecture

The CLI is going to be split into user-facing commands and internal commands. This is similar to the approach that’s taken by the git set of tools. In that ecosystem, there are a set of commands that are meant to be user-facing (called porcelain commands), and there are other commands that are in support of internal operations and other commands (called plumbing commands).

We’ll do our best to follow the Unix philosophy for building out these commands:

  • Do one thing, and do it well
  • The output of each command can be used as input for another command, with the ability to read information from STDIN and write information to STDOUT - sometimes this point will be deviated from when it makes more sense to persist output to the file system so that it can be used by multiple programs reading the same information at the same time

One reason for taking this approach is that it allows us to use the operating system’s process scheduling to get parallelization of activities. Another reason is that it allows us to interact with tools that we did not write, such as git for source code integration or mvn for build system integration or a command line tool that knows how to generate or manipulate CycloneDX BOM files.

It’s expected that many of the commands below will permit running multiple workers, with work to be completed stored in queue tables in the local database. The local database will also be used to cache information from various external sources and to cache the results of expensive computations.

One goal with caching is to make the analyze command run quickly against commits that it has already analyzed. This would be particularly useful when running the CLI from a CI environment, since most of them support storing cached information between builds.

flowchart LR
    start(analyze start)
    subgraph cache
        prepare
    end
    subgraph git
        clone --> compute-history
        compute-history --> checkout-histories
        checkout-histories --> compute-remaining-histories{{Histories\nRemain?}}
        compute-remaining-histories -->|Yes| checkout-history
        checkout-history --> compute-remaining-histories
        compute-remaining-histories -->|No| history-done(Done)
    end
    
    subgraph bom
        generate(generate start) --> generate-histories

        generate-histories --> generate-remaining-histories{{Histories\nRemain?}}
        generate-remaining-histories -->|Yes| generate-history
        generate-history --> generate-remaining-histories
        generate-remaining-histories -->|No| generate-done(Done)				
    end
    subgraph compute
        libyear
    end
    subgraph persist
        save-to-database
    end
    subgraph output
        create-local-report
    end
    subgraph publish
        send-to-freshli-web
    end
    start --> cache
    cache --> git
    git --> bom
    bom --> compute
    compute --> persist
    compute --> output
    compute --> publish
flowchart
    subgraph agent
        subgraph lang
            lang-detect-manifests[detect-manifests]
        end
    end

    subgraph agents
        agents-detect[detect]
    end

    subgraph bom
	generate-history --> detect-all-manifests
        detect-all-manifests --> agents-detect
	detect-all-manifests --> all-manifests-detected{{All\nManifests\nDetected}}
        all-manifests-detected -->|No| lang-detect-manifests
        lang-detect-manifests --> all-manifests-detected
        all-manifests-detected -->|Yes| process-all-manifests
    end
flowchart
    subgraph agent
        subgraph lang
            lang-process-manifest[process-manifest]
        end
    end

    subgraph bom
        process-all-manifests --> all-manifests-processed{{All\nManifests\nProcessed}}

        all-manifests-processed -->|No| lang-process-manifest
        lang-process-manifest --> all-manifests-processed
        all-manifests-processed -->|Yes| process-manifests-done(Done)
    end

The above graphs will be updated as more commands are identified and added to the pipeline.

The following issues are associated with this epic.

Make the command line executable downloadable as a GitHub release asset

Compile and make the command line executable downloadable. Not sure where the best place to put the download is? For alpha releases my first thought would be GitHub either in the releases or possibly packages (if even possible). We will need to discuss where to put beta and production versions of the executables.

Add `agents detect` command

agents detect

Used to detect all of the language agents that are available for use.

freshli agents detect

What this command does?

Language agents are detected by searching the $PATH for a program name that starts with freshli-agent-. It is assumed that anything after this prefix is the language name, and would be used whenever referring to the agent that directly.

This command outputs the detected language name and the path to the language agent binary in a tabular format. One line of output is created for each language agent that is detected.

How to deal with negative libyear

Continuing the conversation from this comment: #112 (comment)
My initial idea was to just look at the absolute value of libyear:

// .Duration() will always return an absolute value.
// So even if the latest version was released before the current version you'll end up with a positive number.

Given a package with two releases. Version 8.0 was released on 11/29/2020, Version 7.4 was released on 11/29/2021. If your application is on version 7.4, and If you'd follow the guidelines than that would mean a libyear of -1. That doesn't make sense because the number should tell you how up-to-date you are. That can't be negative, you can only be behind or on-top. So what the number to means is that it's an indication of how far behind you are on the latest release. Since version 7.4 was released in 2021, you had a year already to upgrade to version 8. So a libyear of 1 as you are behind with updates.

@mscottford I'm continuing the conversation here asynchronously. I think that's better so we have some documentation later as well as it's a pretty important topic of the whole app. 😅

Add `agents verify` command

agents verify

Used to test all of the language agents that are available for use. It tests if all the agents are adhering to the contract. e.g. does it have the expected output for validating-repositories.

freshli [global options] agents verify [command options] [language-name]

What this command does?

When no language name is specified, delegates to the agents detect to detect each available for agent, and then operates as if the agents verify command had been called once for each available language.

When a language is specified invokes the language agent’s validating-repositories command to get the urls of a publicly available repositories that the agent can be validated against. Each provided repository is then cloned as a child of CACHE_ROOT/<lang>/ directory. It is up to the language agent to determine the correct number of repositories to validate against, but the expectation is that at least one repository url is provided for each manifest file format that the language agent knows how to detect and process. If any of the repositories fails to clone, then an error is generated.

For each repository that has been checked out, the language agent’s detect-manifests command is invoked. The command output is then validated to ensure that at least one manifest file has been detected. If no manifest files are detected, then then an error is generated.

For each repository, the language agent’s process-manifests command is then invoked. It is expected that the output of the command contains the following for each manifest file that was processed:

  • manifest file location in the cache tree
  • resulting bom file location in the cache tree
  • optionally, if a modified manifest file was generated as part of the processing, the location in the cache tree of the original manifest file

If the language agent’s process-manifests command does not process the same number of manifests as were detected by the language agent’s detect-manifests command, then an error is generated.

If the output of the process-manifests command lists files that don’t exist or are empty, then an error is generated.

If the resulting CycloneDX bom file is not a valid JSON-formatted CycloneDX bom file, then an error is generated.

If the any residual modifications to the cloned source code repository are detected after process-manifests command has completed, then an error is thrown. This is the same validation that is performed when running the bom process-all-manifests command with the --verify-directories option.

For each language that this command evaluates, the result of the verification is listed along with the number of repositories that were tested and the amount of time that it took to perform the verification.

Global Options

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Command Options

  • --workers
    • the number of worker processes that should be running at any given time. This defaults to twice the number of CPU cores.
    • default: N+1 where N is the number of cores that are detected in the CPU

Upgrade GitVersion and GitReleaseManager to latest versions

There's a warning message in the build output when the build step ".NET Core 3 (GitReleaseManager)" runs.

Warning: .NET Core 3.0 is no longer supported and will not receive security updates in the future. Please refer to https://aka.ms/dotnet-core-support for more information about the .NET support policy.

I'm assuming that this is because the GitHub actions for GitReleaseManager (and perhaps GitVersion) are not running the latest versions.

Add `compute libyear` command

compute libyear

freshli compute libyear [filepath]

What does this command do?

The CycloneDX(SBOM) file passed to this command is generated by the bom:generate command. This file contains the Bill of Materials including the package URL.

This command will use the CycloneDX file to check each package what the current version is. It will use the packge URL for finding out what the current version is and which date it was released.

graph LR;
 subgraph what it should query
  queries-.-latest[What's the latest version? And it's release date?]
  queries-.-current[What's the currents version release date?]
 end

  subgraph different repositories
   Repositories-.-Composer
   Repositories-.-Bundler
   Repositories-.-Carton
   Repositories-.-Pip
   Repositories-.-NuGet
  end

 subgraph the flow
  purl[Package URL]-->Repository
  Repository-->query[Query info, find out where the code lives github.com etc.]
  query-->git[Ask git for the repository's tags and what date they were published]
  git-->calc[Calculate lib year]
 end

Command parameters

  • filepath: Filepath of CycloneDX file containing the BOM history.

Notes

Fetching repository info with bundler:

root ➜ /code (calculate-libyear) $ bundle info sqlite3

  * sqlite3 (1.4.2)

        Summary: This module allows Ruby programs to interface with the SQLite3 database engine (http://www.sqlite.org)

        Homepage: https://github.com/sparklemotion/sqlite3-ruby

        Path: /usr/local/bundle/gems/sqlite3-1.4.2

Fetching repository info with carton (https://metacpan.org/dist/carton/view/lib/Carton/Doc/Show.pod):

carton show Module

Add `bom process-all-manifests` command

bom process-all-manifests

Used to produce a CycloneDX file for each manifest file that has been detected for the provided repository-id and date-time pair.

freshli [global options] bom process-all-manifests [command options] <repository-id> <date-time>

What this command does?

For each language-specific agent that is detected, delegates to that language agent’s process-manifests command.

The same language agent that detected the manifest file will be used to process it. It is assumed that the language agent knows how to process any manifest file that it has detected, even in the case where the language ecosystem uses a variety of manifest file formats. For example, Maven and Gradle competing dependency managers in the Java ecosystem, and each one has its own manifest file format for documenting dependencies. It is assumed that the freshli-agent-java executable knows how to process both file formats.

Some language agent processors will generate a modified or translated version of the manifest file that it operated on. This modified or translated version of the manifest file is needed to replace version range expressions with specific versions that were available at the provided date-time value. Entries are created in the cache database for these modified manifests in a way that associates it with the manifest file that it originated from. The translated file is also stored in the cache for easy future retrieval.

Generating these modified manifest files requires exclusive access to a history directory tree. This means that no two instances of the same language agent processor is permitted to operate on a the same directory tree at the same time. This is regardless of the number of workers that have been specified via the --workers parameter.

An entry is created in the cache database for each CycloneDX bom.json file that is produced by this operation. The bom file’s entry includes a reference to the manifest file that it was generated from. These files are also stored in the cache file tree for examination or future processing by other commands.

It is expected that after a language agent’s process-manifests command has completed, the provided directory tree will appear unchanged. The --verify-directories option can be specified to enforce this rule.

Global Options

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Command Options

  • --workers
    • the number of worker processes that should be running at any given time. This defaults to twice the number of CPU cores.
    • default: N+1 where N is the number of cores that are detected in the CPU
  • --verify-directories
    • When specified, the contents of each processed history directory is validate before and after a language agent is run. This ensures that no residual changes were made to the directory tree by the language agent.

Warning when adding assets to GitHub release in CI

When running the CI the following warning is displayed:

##[warning]Unexpected input(s) 'tagName', valid inputs are ['owner', 'repository', 'token', 'milestone', 'assets', 'targetDirectory']

This is linked to the following CI step in ci.yml:

- name: "[Post Build] - Add Assets to Release Draft"
  if: ${{ github.event_name == 'push' }}
  uses: gittools/actions/gitreleasemanager/[email protected]
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    owner: 'corgibytes'
    repository: 'freshli-cli'
    milestone: 'v${{ steps.gitversion.outputs.majorMinorPatch }}'
    tagName: 'v${{ steps.gitversion.outputs.majorMinorPatch }}'
    assets: 'freshli-cli-${{ steps.gitversion.outputs.majorMinorPatch }}-win-x64.zip,freshli-cli-${{ steps.gitversion.outputs.majorMinorPatch }}-linux-x64.zip,freshli-cli-${{ steps.gitversion.outputs.majorMinorPatch }}-osx-x64.zip'

I don't think the tagName is valid argument, at least not anymore.

https://github.com/GitTools/actions/blob/main/gitreleasemanager/addasset/action.yml

Unexpected input for CLA action

The CLA build process works, at least it appears too, but generates the below warning:

Warning: Unexpected input(s) 'path-to-cla-document', valid inputs are ['path-to-signatures', 'branch', 'allowlist', 'remote-repository-name', 'remote-organization-name', 'path-to-document', 'signed-commit-message', 'signed-empty-commit-message', 'create-file-commit-message', 'custom-notsigned-prcomment', 'custom-pr-sign-comment', 'custom-allsigned-prcomment', 'use-dco-flag']
Run cla-assistant/[email protected]
  with:
    path-to-signatures: CLA/signatures/v1.0/cla.json
    path-to-cla-document: https://github.com/corgibytes/freshli-cli/blob/main/CLA/CLA-v1.0.md
    branch: main
    allowlist: dependabot[bot]
    create-file-commit-message: Adds file for storing CLA Signatures
    signed-commit-message: Adds CLA signature for $contributorName (PR #$pullRequestNo)
    custom-notsigned-prcomment: Thank you for your submission to Freshli! Like many open-source projects, we ask that you agree to our [Contributor License Agreement](https://github.com/corgibytes/freshli-cli/blob/main/CLA/CLA-v1.0.md) before we can accept your contribution. You can sign the CLA by posting a comment in this PR matching the text below:
    use-dco-flag: false
  env:
    GITHUB_TOKEN: ***
    PERSONAL_ACCESS_TOKEN: ***
CLA Assistant GitHub Action bot has started the process
Locking the Pull Request to safe guard the Pull Request CLA Signatures
successfully locked the pull request 29

I think the fix is the change the path-to-cla-document to path-to-document. See issue contributor-assistant/github-action#66 for more details.

Add `git compute-history` command

git compute-history

Used to examine the history of the specified repository and determine the sha hashes that will need to be checked out to complete a historical analysis at the intervals specified.

freshli [global options] git compute-history [command options] <respository-id>

What this command does?

Creates a row in the repository_histories table for each history interval stop and records the sha hash that is detected for that point in time. Each date/time and sha hash pair is also output to STDOUT on a single line per row.

Global Options

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Command Options

  • --git-path

    • Path to the git binary
    • default value: git
  • --commit-history

    • Analyzes only the points in time when files have changed in the commit history. This can be combined with --history-invertal to make sure that every commit is included in the result set. If --history-interval is not used, then only the values from each commit will be present in the output, meaning that the data will show the metrics at the time that the files were changed, but will not show how the metrics changed between commits.
  • --history-interval <duration>

    • As the analyze command moves backwards in time through the history of the project, what time interval should it use to determine the points in time that are sampled
    • default value: 1m
    • valid values: /\d+[ymwd]?/
      • y suffix – specifies duration in years
      • m suffix – specifies duration in months
      • w suffix – specifies duration in weeks
      • d suffix – specifies duration in days
      • If no suffix is provided, then the duration is assumed to be a number of months
    • When walking backward in time the following approach will be taken: given current time T, subtract the smallest amount of time to produce time T' such that T' when be an even interval of the specified duration suffix.
      • Example:
        • Assume
          • T = 2022-02-01 23:11:43Z
          • Interval duration — 1d
          • Timezone offset — -04:00
          • Then T' would be set to 2022-02-01 00:00:00-04:00
          • The value of T' would be decremented by one calendar day until the earliest commit date is reached

Add `bom generate-history` command

bom generate-history

Used to generate CycloneDX BOM files for the specified historical point for the given repository.

freshli [global options] bom generate-history [command options] <repository-id> <date-time>

What this command does?

Delegates to the bom detect-all-manifests command to detect all of the manifest files that are available for the git history that’s been checked out for the provided date-time value. After bom detect-all-manifests has completed, delegates to the bom process-all-manifests command for processing all of the manifests that have been detected.
Global Options

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Localize the CLI

Currently the messages displayed by the Freshli-CLI are not localized but are just hard coded English strings. This case is to localize any messages displayed by the CLI including help. The command line arguments won't be localized.

This issue only covers the CLI. If any messages are displayed from Lib they will either still be displayed unfocalized (i.e. in English) or the CLI will have to override the message.

Also this issue only covers localizing the current English messages. New cases will be created to actually adding new languages.

Some documentation on how to localize .NET applications can be found here

Auto generate the release notes

Auto generate the change log and GitHub releases using GitReleaseManager which is made by the same people who made GitVersion.

GitReleaseManager works by having creating a milestone with version number (e.g. v0.3.0 and assigning issues to that milestone. Then in the CI we run the GitReleaseMangaer to generate both the GitHub Release and the change log.

I tested this in another project and it seemed to work pretty well. An example of the GitHub action file is:

# Assume we have the next version number via GitVersion.

- uses: gittools/actions/gitreleasemanager/[email protected]
name: Install GitReleaseManager
with:
  versionSpec: '0.11.0'

- uses: gittools/actions/gitreleasemanager/[email protected]
name: Create GitHub Release for ${{ steps.gitversion.outputs.majorMinorPatch }}  if it Does not Exist
continue-on-error: true
with:
  token: ${{ secrets.GITHUB_TOKEN }}
  owner: 'repo-owner'
  repository: 'my-repo'
  milestone: 'v${{ steps.gitversion.outputs.majorMinorPatch }}' 

# This will generate the change log for all the GitHub Releases.
- name: Generate Change Log (no GitHub Action)
run: |
  dotnet-gitreleasemanager export --token ${{ secrets.GITHUB_TOKEN }} -o 'repo-owner' -r 'my-repo' -f 'CHANGELOG.md'
  cat CHANGELOG.md

- uses: stefanzweifel/git-auto-commit-action@v4
name: Commit Change Log if it Changed
with:
  commit_message: Committing auto generated change log.
  file_pattern: CHANGELOG.md

Spike - Is it possible to change property names of command options without having to change the command line parameter?

This spike originates here: #112 (comment)

The problem is as follows. Given a class that extends the CommandOptions class. For example:

public class ComputeLibYearCommandOptions : CommandOptions
{
    public FileInfo FilePath { get; set; }
}

There's some magic going on behind the scene to connect a command line parameter to the CommandOptions object.
E.g. passing --filepath=/opt/home/things is set to the FilePath property of ComputeLibYearCommandOptions behind the scene.
However, can we change it so that FilePath can have a different name? In this example it's not descriptive, and tells us nothing about the context. We want to know it's about a CycloneDX file here. Can we rename FilePath?

Spike: Enable Nullable Across Project

At present, the project's nullable setting is disabled, necessitating the use of #nullable enable whenever we need that. We should inspect whether it would be practical to set nullable to enabled across the project. (I already know this will require some code changes.)

Create custom FluentAssertion matchers for validating `Option` and `Argument` configuration

You didn't create this, so I think this little bit of clean-up can be pushed into a code clean-up issue.

Since we're using the FluentAssertions library, it would be cleanest to rely on its support for adding new matchers.

So this would end up looking like something like this: (I haven't tried to compile this.)

public static class Command
{
    public static CommandOptionAssertions ShouldContainOptionAlias(this Command command, string alias)
    {
        return new CommandAssertions(command, alias);
    }
}

public class CommandOptionAssertions: ReferenceTypeAssertions<Option, CommandOptionAssertions>
{
    private string alias;
    private Command command;
    public CommandOptionAssertions(Command command, string alias): base(command)
    {
        this.command = command;
        this.alias = alias;
    }

    public AndConstraint<CommandAssertions> DoesAllowMultipleArgumentsPerToken()
    {
        Option option = command.Options.FirstOrDefault(x => x.Aliases.Contains(alias));
        Execute.Assertion
            .ForCondition(option != null)
            .FailWith("Alias with name {0} was not found", _ => alias)
            .Then
            .ForCondition(option.AllMultipleArgumentsPerToken)
            .FailWith("Expected to support multiple arguments per token")

        return new AndConstraint<CommandAssertions>(this);
    }

    public AndConstraint<CommandAssertions> DoesNotAllowMultipleArgumentsPerToken()
    {
        Option option = command.Options.FirstOrDefault(x => x.Aliases.Contains(alias));
        Execute.Assertion
            .ForCondition(option != null)
            .FailWith("Alias with name {0} was not found", _ => alias)
            .Then
            .ForCondition(!option.AllMultipleArgumentsPerToken)
            .FailWith("Expected to _not_ support multiple arguments per token")

        return new AndConstraint<CommandAssertions>(this);
    }

    public AndConstraint<CommandAssertions> HavingArity(int arity)
    {
        Option option = command.Options.FirstOrDefault(x => x.Aliases.Contains(alias));
        Execute.Assertion
            .ForCondition(option != null)
            .FailWith("Alias with name {0} was not found", _ => alias)
            .Then
            .BecauseOf(arity)
            .ForCondition(option.Arity == arity)
        return new AndConstraint<CommandAssertions>(this);
    }
}

And then I'm pretty sure it would be invoked like this:

[Theory]
[InlineData("--output")]
[InlineData("-o")]
public void Verify_output_options_configuration(string alias)
{
    (new ScanCommand())
        .ShouldHaveOptionWithAlias("-o")
        .HavingArity(ArgumentArity.OneOrMore)
        .DoesAllowMultipleArgumentsPerToken();
}

Originally posted by @mscottford in #67 (comment)

Add `git checkout-histories` command

git checkout-histories

Used to checkout all of the histories that have been detected for a given repository.

freshli [global options] git checkout-histories [command options] <repository-id>

What this command does?

Determines if there are histories that have not been checked out for the given repository and then delegates to the cache checkout-history command to perform the actual work of doing the checkout. The number of copies of the cache checkout-history command that are allowed to run at one time is controlled by the --workers parameter.

Global Otions

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Command Options

  • --workers
    • the number of worker processes that should be running at any given time. This defaults to twice the number of CPU cores.
    • default: N+1 where N is the number of cores that are detected in the CPU

Add `cache destroy` command

cache destroy

Used to completely remove the cache directory and all of its contents, including the cache database.

freshli [global options] cache destroy [command options]

What this command does?

Deletes the cache directory and all files that it contains.

Global Otions

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Command Options

  • --force
    • Skips the prompt confirming that operation should proceed

Add `git clone` command

git clone

Used to clone the project into the cache directory.

freshli [global options] git clone [command options] <repository-url>

What this command does?

Uses the git command to clone the repository under the $CACHE_ROOT/repositories/ directory. A unique ID will also be generated for the repository. Repository ID, url, and path will be added to the repositories table in the cache database. The command’s only output to STDOUT is the repository id.

Global Otions

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Command Otions

  • --git-path
    • Path to the git binary
    • default value: git
  • --branch <name>
    • The branch to checkout after cloning the repository
    • If the option is not specified, then no checkout command will be issued. The remote server’s default branch will be used instead.

Add `git checkout-history` command

git checkout-history

Used to checkout a specific historical point for a given repository.

freshli [global options] git checkout-history [command options] <repository-id> <sha>

What this command does?

Delegates to the git archive command to create an archive of the specified sha, and then extract that archive into the $CACHE_ROOT/histories/<repository id>/<sha> directory.

Global Options

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Command Options

  • --git-path
    • Path to the git binary
    • default value: git

Create automated build for the executable

Edit: Changed this case to create an automated build for the CLI. Didn't notice one did not exist yet. Leaving the below notes on GitVersion as using GitVersion will be part of the build process to determine the exe version.

Use GitVersion to generate the version number for this project. Actually the plan is to use the GitHub Action. GitVersion will be setup to support the Cactus Branching model (see below for details) which requires a setup like:

# GitVersion.yml
branches:
  master:
    mode: ContinuousDeployment
    tag: 'alpha'
    increment: Minor
    prevent-increment-of-merged-branch-version: false
    track-merge-target: true
  release:
    tag: 'beta'
    increment: Patch
    prevent-increment-of-merged-branch-version: true
    track-merge-target: false

The idea is that only release-X.Y branches are tagged something like vX.Y.Z. Then this tag is merged back to main GitVersion will calculate the next version number as vX.Y+1.0.0.

When a feature branch is merged to release-X.Y for a bug fix only the patch number will be incremented and the major, minor, and patch numbers left as is.

GitVersion generates several different version number formats such as:

  • AssemblySemVer: 0.8.0.0
  • NuGetVersionV2: 0.8.0-alpha0001
  • MajorMinorPatch: 0.8.0

GitVersion can set the version in C# Assembly Version files now that they have fixed this issue.


An example of the Cactus or Trident Branching model:

image

Create Score command

Spawned from #23 and depends on #25 being completed first.

We would like to use Freshli in continuous integration (CI) builds to calculate the Freshness score of the project for the commit being built. To do this we will need a Freshli-CLI to return a Freshness score for the current commit which the Freshli-CLI does not support.

The command should look something like:

freshli-cli score [threshold] [--directory directory]

The score command will analysis dependencies in the current directly, or in the directory argument, and return a Freshness score. The Freshness score will be a single number that represents the Freshness for the entire directory. For example if the directory has NuGet and NPM packages it will return the score both files combined.

If the Freshness score is greater then then threshold argument the command will fail. For example, if the a project always wants a Freshness score of 10 or less then they would run a command like:

freshli-cli score 10

In a GitHub Action I see using it as:

# Assume .NET 5.0 is installed.
- name: Run Freshli
  run: |
    dotnet tools install freshli-cli
    freshli-cli score 10

If you want the Freshness value to use in a different GitHub action:

- name: Run Freshli
  id: freshli
  run: |
      dotnet tools install freshli-cli
      echo "::set-output name=FRESHNESS::$(freshli-cli score)"

- name: Display Freshness
  run: echo "Freshness: ${{ steps.freshli.outputs.FRESHNESS }}"

I don't think Freshli-Lib currently supports computing just the Freshness score for the currently checked out commit. If not we will have to add the API methods.

Set up Dev Containers for Freshli CLI

Would like to set up Dev Containers to allow for easy development for the project, including pulling down the Freshli core.

I do want to extend this to other IDEs, but going to focus this task on just setting up VSCode, since the Dockerfile created will probably extend to other IDEs.

Update ReadMe with how to use CLI

Update the ReadMe with steps on how to get the Freshli CLI (i.e. where to download, install use dotnet tool, etc). Should include examples of using the CLI and example results. It should also list the languages Freshli supports as well as a summary of known issues.

Update GitReleaseManager support labels

GitReleaseManager will fail if it finds an issue in the GitHub Milestone that has a label it does not recognize. A list of labels GitReleaseManager recognizes can be found here and include:

- Breaking Change
- Bug
- Documentation
- Feature
- Good First Issue
- Help Wanted
- Improvement
- Question

To fix the issue the GitReleaseManager.yaml file needs to be updated to support labels such as dev-ops. You can find documentation about GitReleaseManager issue to include here and label aliases here. You can also see an example in the GitReleaseManager.yaml in Freshli-Lib.

P.S. - Note the GitReleaseManager.yaml file must end in yaml. This is a known issue.

Using the CLI in automated builds design

Try to use the CLI in automated builds and find out what features would be useful. Trying to use the CLI in automated builds will hopefully create some discussion in this case and also issue/PRs for both the CLI and Lib.

On example of using Freshli CLI in an automated build is to get a Freshness score and fail the build the score is too high (i.e. dependencies our too out of date). How high is too high? Can we compare it with the previous builds Freshness score?

Another example is can the CLI produce data that can be used by the Continuous Integration platform in a graph? Can it upload the data somewhere useful to the user? Perhaps upload it to Freshli Web?

Any other ideas?

Note: Currently this issue is assigned to @mfcorgi but I'm hoping that others will try the CLI in their automated build and give us feedback.

Add `cache prepare` command

cache prepare

Used to ensure that the cache directory exists and that it contains a valid cache database instance. If an existing cache database is found, then the database will be checked to determine if migrations need to be run against it.

freshli [global options] cache prepare

What this command does?

Ensures that the cache directory exists and that the database that it contains is valid and up-to-date.

Global Otions

  • --cache-dir <path>
    • the location where the freshli command will write temporary files as part of it’s processing
    • default value: $HOME/.freshli

Specify level of debug information

After merging #25 the CLI now spits out a bunch of debug information. This debug information should not be displayed when running the CLI unless specifically asked for. For example running the CLI with no arguments produces:

> Corgibytes.Freshli.Cli.exe
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\Repos\freshli-cli\Corgibytes.Freshli.Cli\bin\Debug\net5.0
[Command Execution Invocation Started  - ParseResult: ![ Corgibytes.Freshli.Cli ] ]
[Command Execution Invocation Ended - ParseResult: ![ Corgibytes.Freshli.Cli ] ]
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
Required command was not provided.
Corgibytes.Freshli.Cli
  Root Command

Usage:
  Corgibytes.Freshli.Cli [options] [command]

Options:
  -?, -h, --help  Show help and usage information


Commands:
  scan <path>  Scan command returns metrics results for given local repository path

The info: and [Command Execution Invocation] should not be displayed when running the application. You will see similar info lines when running the scan command:

> Corgibytes.Freshli.Cli.exe scan https://github.com/corgibytes/freshli-fixture-ruby-nokotest.git
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\Repos\freshli-cli\Corgibytes.Freshli.Cli\bin\Debug\net5.0
[Command Execution Invocation Started  - ParseResult: [ Corgibytes.Freshli.Cli [ scan <https://github.com/corgibytes/freshli-fixture-ruby-nokotest.git> *[ --format <Json> ] *[ --output <Console> ] ] ] ]
CliOutput.ScanCommand_ScanCommand_Executing_scan_command_handler
Sending metrics to Console
[
  {
    "Filename": "Gemfile.lock",
    "MetricResults": [
      {
        "Date": "2017-01-01T23:59:59.9999999",
        "LibYear": [
          {
 <... More JSON...>

The main issue to remove the debug information when running the application. Time permitting it would be nice to add a verbose argument. Something like:

 Corgibytes.Freshli.Cli.exe  -verbose scan https://github.com/corgibytes/freshli-fixture-ruby-nokotest.git

This argument would be apply to all commands and display the info information. If need be we can also set a verbose level, something like:

 Corgibytes.Freshli.Cli.exe -verbose [debug|info] scan https://github.com/corgibytes/freshli-fixture-ruby-nokotest.git

The above are just examples, feel free to suggest better ways to display debug/verbose information.

Creating empty GitHub Release fails

When trying to create a GitHub Release using GitReleaseManager it will fail if there are no closed issues. Error occurs in this action:

https://github.com/corgibytes/freshli-cli/actions/runs/719767690

[Draft Release] - Create/Update GitHub Release 0.4.0
2s
Run gittools/actions/gitreleasemanager/[email protected]
Command: dotnet-gitreleasemanager create --owner corgibytes --repository freshli-cli --token *** --targetDirectory /home/runner/work/freshli-cli/freshli-cli --milestone v0.4.0
/opt/hostedtoolcache/GitReleaseManager.Tool/0.11.0/x64/dotnet-gitreleasemanager create --owner corgibytes --repository freshli-cli --token *** --targetDirectory /home/runner/work/freshli-cli/freshli-cli --milestone v0.4.0

   ____ ____  __  __
  / ___|  _ \|  \/  |
 | |  _| |_) | |\/| |
 | |_| |  _ <| |  | |
  \____|_| \_\_|  |_|
               0.11.0

Creating release...
[WRN] Yaml not found, that's ok! Learn more at https://gittools.github.io/GitReleaseManager/docs/yaml
Using GitHub as VCS Provider
[WRN] Unable to find tag for milestone, so commit count will be returned as zero
[FTL] No closed issues have been found for milestone v0.4.0, or all assigned issues are meant to be excluded from release notes, aborting creation of release.
System.InvalidOperationException: No closed issues have been found for milestone v0.4.0, or all assigned issues are meant to be excluded from release notes, aborting creation of release.
   at GitReleaseManager.Core.ReleaseNotesBuilder.BuildReleaseNotes()
   at GitReleaseManager.Core.GitHubProvider.CreateReleaseFromMilestone(String owner, String repository, String milestone, String releaseName, String targetCommitish, IList`1 assets, Boolean prerelease)
   at GitReleaseManager.Cli.Program.CreateReleaseAsync(CreateSubOptions subOptions) in C:\projects\gitreleasemanager\Source\GitReleaseManager.Cli\Program.cs:line 164
   at GitReleaseManager.Cli.Program.Main(String[] args) in C:\projects\gitreleasemanager\Source\GitReleaseManager.Cli\Program.cs:line 41
Error: The process '/opt/hostedtoolcache/GitReleaseManager.Tool/0.11.0/x64/dotnet-gitreleasemanager' failed with exit code 1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.