Skip to content

Commit

Permalink
feat(release): Add new tasks for release and tests (#33)
Browse files Browse the repository at this point in the history
* Add new tasks for release and tests
  • Loading branch information
juancgalvis authored Apr 8, 2024
1 parent 76b77f6 commit ab8f41b
Show file tree
Hide file tree
Showing 23 changed files with 321 additions and 80 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ jobs:
issues: true
issuesWoLabels: true
stripGeneratorNotice: true
- name: Publish
run: mix hex.publish --replace --yes
env:
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
- name: Save version
uses: github-actions-x/commit@v2.9
with:
Expand All @@ -47,7 +51,3 @@ jobs:
files: mix.exs CHANGELOG.md
name: Release Bot
email: release-bot@bancolombia.com.co
- name: Publish
run: mix hex.publish --replace --yes
env:
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
elixir 1.15.7-otp-24
erlang 24.3.4.14
erlang 26.1.1
elixir 1.15.6-otp-26
1 change: 0 additions & 1 deletion lib/core/apply_template.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ defmodule ElixirStructureManager.Core.ApplyTemplate do
defp resolve_behaviour(:asynceventhandler), do: EP.AsyncEventHandlers

defp resolve_behaviour(:metrics), do: Config.Metrics
defp resolve_behaviour(:distillery), do: Config.Distillery
defp resolve_behaviour(:sonar), do: Config.Sonar

defp resolve_behaviour(_other) do
Expand Down
22 changes: 0 additions & 22 deletions lib/core/config/distillery.ex

This file was deleted.

2 changes: 1 addition & 1 deletion lib/core/config/sonar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ defmodule Config.Sonar do
"test/support/test_stubs.exs" => @base <> "test_stubs.exs"
},
transformations: [
{:inject_dependency, ~s|{:sobelow, "~> 0.13", only: :dev}|},
{:inject_dependency, ~s|{:credo_sonarqube, "~> 0.1"}|},
{:inject_dependency, ~s|{:sobelow, "~> 0.11", only: :dev}|},
{:inject_dependency, ~s|{:ex_unit_sonarqube, "~> 0.1", only: :test}|},
{:append_end, ".gitignore", ignore},
{:run_task, :install_deps}
Expand Down
6 changes: 3 additions & 3 deletions lib/mix/tasks/ca.apply.config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ defmodule Mix.Tasks.Ca.Apply.Config do
Type param options:
* distillery
* sonar
* metrics
Examples:
$ mix ca.apply.config --type distillery
$ mix ca.apply.config -t distillery
$ mix ca.apply.config --type sonar
$ mix ca.apply.config -t metrics
"""

alias ElixirStructureManager.Core.ApplyTemplate
Expand Down
8 changes: 4 additions & 4 deletions lib/mix/tasks/ca.new.structure.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ defmodule Mix.Tasks.Ca.New.Structure do
use Mix.Task

@version Mix.Project.config()[:version]
@switches [metrics: :boolean, distillery: :boolean]
@aliases [m: :metrics, d: :distillery]
@switches [metrics: :boolean, sonar: :boolean]
@aliases [m: :metrics, s: :sonar]

def run([]), do: run(["-h"])

Expand All @@ -39,8 +39,8 @@ defmodule Mix.Tasks.Ca.New.Structure do

CommonCommands.install_deps(root_dir)

if opts[:distillery] do
CommonCommands.config_distillery(root_dir)
if opts[:sonar] do
CommonCommands.config_sonar(root_dir)
end

if opts[:metrics] do
Expand Down
42 changes: 42 additions & 0 deletions lib/mix/tasks/ca.release.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
defmodule Mix.Tasks.Ca.Release do
@moduledoc """
Mix release wrapper that moves compressed artifact to _build/release/artifact folder
Examples:
$ mix ca.release
It generates the following files:
* _build/release/artifact/<app-name>.tar.gz
"""

alias ElixirStructureManager.Utils.{FileGenerator, TokenHelper}
alias Mix.Tasks.Ca.BaseTask

use BaseTask,
name: "ca.release",
description:
"Mix release wrapper that moves compressed artifact to _build/release/artifact folder",
switches: [],
aliases: []

def execute(_any) do
Mix.shell().info([:green, "* Generating release artifact"])

args = %{
folders: [
"_build/prod/rel/{app_snake}/releases/RELEASES",
"_build/release/artifact"
],
transformations: [
{:cmd, "mix release --overwrite"},
{:cmd,
"mv _build/prod/{app_snake}-{version}.tar.gz _build/release/artifact/{app_snake}.tar.gz"},
{:cmd, "ls -lR _build/release"}
]
}

FileGenerator.execute_actions(args, TokenHelper.default_tokens())

Mix.shell().info([:green, "* Artifact generated"])
end
end
40 changes: 40 additions & 0 deletions lib/mix/tasks/ca.sobelow.sonar.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule Mix.Tasks.Ca.Sobelow.Sonar do
@moduledoc """
Translates the sobelow json report to sonar issues
Examples:
$ mix ca.sobelow.sonar -i sobelow.json -o sobelow_sonarqube.json
"""

alias ElixirStructureManager.Reports.Sobelow
alias Mix.Tasks.Ca.BaseTask

use BaseTask,
name: "ca.sobelow.sonar",
description: "Translates the sobelow json report to sonar issues",
switches: [input: :string, output: :string],
aliases: [i: :input, o: :output]

def execute({opts, []}) do
Mix.shell().info([:green, "* Translating sobelow report"])

input = Keyword.get(opts, :input, "sobelow.json")
output = Keyword.get(opts, :output, "sobelow_sonarqube.json")

sonar_base_folder = Application.get_env(:elixir_structure_manager, :sonar_base_folder, "")

File.exists?(input) || Mix.shell().error([:red, "* Input file not found"])

report =
File.read!(input)
|> Poison.decode!()

sonar_report = Sobelow.translate(report, sonar_base_folder)
json = Poison.encode!(sonar_report, %{pretty: true})
File.write!(output, json)

Mix.shell().info([:green, "* Sonarqube report generated"])
end

def execute(_any), do: run(["-h"])
end
51 changes: 51 additions & 0 deletions lib/mix/tasks/ca.test.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
defmodule Mix.Tasks.Ca.Test do
@moduledoc """
Run common static code analysis tools and tests for the project
Examples:
$ mix ca.test
It generates the following files:
* _build/release/credo_sonarqube.json
* _build/release/excoveralls.xml
* _build/release/generic_test_execution_sonarqube.xml
* _build/release/sobelow.json
* _build/release/sobelow_sonarqube.json
* _build/release/test-junit-report.xml
"""

alias ElixirStructureManager.Utils.{FileGenerator, TokenHelper}
alias Mix.Tasks.Ca.BaseTask

use BaseTask,
name: "ca.test",
description: "Run common static code analysis tools and tests for the project",
switches: [],
aliases: []

def execute(_any) do
Mix.shell().info([:green, "* Executing analysis and tests"])

sonar_base_folder = Application.get_env(:elixir_structure_manager, :sonar_base_folder, "")

args = %{
folders: [
"_build/release"
],
transformations: [
{:cmd,
"mix credo --sonarqube-base-folder {sonar_base_folder} --sonarqube-file _build/release/credo_sonarqube.json --mute-exit-status"},
{:cmd, "mix sobelow -f json --out _build/release/sobelow.json"},
{:cmd,
"mix ca.sobelow.sonar -i _build/release/sobelow.json -o _build/release/sobelow_sonarqube.json"},
{:cmd, "mix coveralls.xml"},
{:cmd,
"mv generic_test_execution_sonarqube.xml _build/release/generic_test_execution_sonarqube.xml"}
]
}

FileGenerator.execute_actions(args, TokenHelper.add("sonar_base_folder", sonar_base_folder))

Mix.shell().info([:green, "* Analysis executed"])
end
end
61 changes: 61 additions & 0 deletions lib/reports/sobelow.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
defmodule ElixirStructureManager.Reports.Sobelow do
@moduledoc """
Sobelow report generator
"""

def translate(
%{
"findings" => %{
"high_confidence" => highs,
"medium_confidence" => meds,
"low_confidence" => lows
},
"sobelow_version" => vsn
},
sonar_base_folder
) do
issues =
Enum.map(highs, &format(&1, :high, vsn, sonar_base_folder))
|> Enum.concat(Enum.map(meds, &format(&1, :medium, vsn, sonar_base_folder)))
|> Enum.concat(Enum.map(lows, &format(&1, :low, vsn, sonar_base_folder)))

%{issues: issues}
end

defp format(
%{"type" => type, "file" => file, "line" => line} = finding,
confidence,
vsn,
prefix
) do
variable = Map.get(finding, "variable", "")
[mod_id, _] = String.split(type, ":", parts: 2)
# Code.ensure_loaded(Sobelow)
rule = Sobelow.get_mod(mod_id)

location = %{
filePath: "#{prefix}#{file}",
message: "#{type} #{variable} \n Help: #{rule.details()}"
}

location = with_text_range(location, line)

%{
ruleId: rule.id(),
severity: confidence_to_severity(confidence),
type: "VULNERABILITY",
engineId: "sobelow-#{vsn}",
primaryLocation: location
}
end

defp with_text_range(%{} = location, line) when line > 0 do
Map.put(location, :textRange, %{startLine: line})
end

defp with_text_range(%{} = location, _line), do: location

defp confidence_to_severity(:high), do: "CRITICAL"
defp confidence_to_severity(:medium), do: "MAJOR"
defp confidence_to_severity(:low), do: "MINOR"
end
22 changes: 13 additions & 9 deletions lib/utils/common_commands.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ defmodule ElixirStructureManager.Utils.CommonCommands do

def install_deps(cwd \\ nil), do: run_no_ci("mix", ["deps.get"], cwd)

def distillery_init(cwd \\ nil), do: run_no_ci("mix", ["distillery.init"], cwd)

def config_distillery(cwd \\ nil), do: run("mix", ["ca.apply.config", "-t", "distillery"], cwd)
def config_sonar(cwd \\ nil), do: run("mix", ["ca.apply.config", "-t", "sonar"], cwd)

def config_metrics(cwd \\ nil), do: run("mix", ["ca.apply.config", "-t", "metrics"], cwd)

defp run_no_ci(cmd, args, cwd) do
def run_no_ci(cmd, args, cwd) do
ci_env = System.get_env("CI_ENV", "false")
Logger.info("Resolved ci_env #{ci_env}")

Expand All @@ -21,11 +19,17 @@ defmodule ElixirStructureManager.Utils.CommonCommands do
end
end

defp run(cmd, args, cwd) do
Mix.shell().info([:green, "Running '#{cmd} #{Enum.join(args, " ")}'"])
System.cmd(cmd, args, into: IO.stream(), cd: resolve_dir(cwd))
def run(cmd, args, cwd) do
log_cms = "#{cmd} #{Enum.join(args, " ")}"
Mix.shell().info([:green, "Running '#{log_cms}'"])
{_, exit_code} = System.cmd(cmd, args, into: IO.stream(), cd: resolve_dir(cwd))

if exit_code != 0 do
Mix.shell().error([:red, "Command '#{log_cms}' failed with exit code #{exit_code}"])
exit(exit_code)
end
end

defp resolve_dir(nil), do: File.cwd!()
defp resolve_dir(dir), do: dir
def resolve_dir(nil), do: File.cwd!()
def resolve_dir(dir), do: dir
end
5 changes: 5 additions & 0 deletions lib/utils/file_generator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ defmodule ElixirStructureManager.Utils.FileGenerator do
apply(CommonCommands, task, [])
end

defp transformation({:cmd, cmd}, tokens) do
[cmd | args] = String.split(resolve_content(cmd, tokens), " ")
CommonCommands.run(cmd, args, nil)
end

defp transformation({:inject_dependency = operation, dependency}, tokens) do
transformation({operation, _dest_file = "mix.exs", dependency}, tokens)
end
Expand Down
Loading

0 comments on commit ab8f41b

Please sign in to comment.