diff --git a/.cspell.json b/.cspell.json new file mode 100644 index 0000000..88532b0 --- /dev/null +++ b/.cspell.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "caseSensitive": false, + "language": "en-GB", + "useGitignore": true, + "dictionaries": [ + "project-dictionary" + ], + "ignorePaths": [ + "LICENSE", + ".gitignore", + ".editorconfig", + "./.githooks/**", + "./.github/dependabot.yml", + "./.github/workflows/**", + "./.vscode/extensions.json", + "./dictionary.dic", + "./Directory.*.props", + "./Directory.*.targets", + "**/*.csproj" + ], + "dictionaryDefinitions": [ + { + "name": "project-dictionary", + "path": "./dictionary.dic", + "description": "Words used in this project", + "addWords": true + } + ], +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a0f398b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,168 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 120 +spelling_languages = en-GB +spelling_error_severity = warning +spelling_exclusion_path = .\dictionary.dic + +[*.json] +insert_final_newline = false + +[*.md] +max_line_length = off +trim_trailing_whitespace = false + +[*.{yml,yaml}] +max_line_length = off + +[*.sln, *.csproj, *.ruleset, *.props, *.targets] +max_line_length = off + +[*.cs] +indent_size = 4 + +################# .NET Coding Conventions ################# +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = true +file_header_template = unset + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:warning + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + + +################# C# Coding Conventions ################# + +# var preferences +csharp_style_var_elsewhere = true:silent +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_switch_expression = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:warning +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +# namespace preferences +csharp_style_namespace_declarations = file_scoped + +################## C# Formatting Rules ################## + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true diff --git a/.githooks/commit-msg b/.githooks/commit-msg new file mode 100755 index 0000000..74ee9d4 --- /dev/null +++ b/.githooks/commit-msg @@ -0,0 +1,22 @@ +#!/bin/sh + +rootDirectory="$(dirname -- "$0")/.." +source "$rootDirectory/scripts/colours.sh" +export ROOT_DIR="$rootDirectory" + +echo "${CYAN}Running commit-msg hooks${NC}" + +# Read the commit message from the file +commit_message_file=$1 +commit_message=$(cat "$commit_message_file") + +"$rootDirectory/scripts/validate-commit-message.sh" "$commit_message" + +# Check the exit code of the script +if [ $? -ne 0 ]; then + echo "${RED}Error: Script failed, aborting commit.${NC}" + exit 1 +fi + +echo "" +exit 0 diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..90e02d5 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,24 @@ +#!/bin/sh + +rootDirectory="$(dirname -- "$0")/.." +source "$rootDirectory/scripts/colours.sh" +export ROOT_DIR="$rootDirectory" + +echo "${CYAN}Running pre-commit hooks${NC}" + +staged_files=$(git diff --cached --name-only --diff-filter=ACMR) + +"$rootDirectory/scripts/validate-branch-name.sh" +"$rootDirectory/scripts/lint-dotnet.sh" "$staged_files" + +# Check the exit code of the script +if [ $? -ne 0 ]; then + echo "${RED}Error: Script failed, aborting commit.${NC}" + exit 1 +fi + +# Add any modified files to the staging area +git add $staged_files + +echo "" +exit 0 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..2fe9996 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +### Describe the bug + +A clear and concise description of what the bug is. + +### Actual behaviour + +A clear and concise description of what is currently happening. + +##### Screenshots + +If applicable, add screenshots to help explain your problem. + +### Expected behaviour +A clear and concise description of what you expected to happen. + +### How to reproduce +Steps to reproduce the behaviour: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +### Environment + +##### Desktop (please complete the following information): + - OS: [e.g. Mac, Windows] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +##### Smartphone (please complete the following information): + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +### Additional context +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..dfcbce4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +### What problem are you trying to solve? +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +### Describe the solution you'd like +A clear and concise description of what you want to happen. + +### Describe alternatives you've considered +A clear and concise description of any alternative solutions or features you've considered. + +### Additional context +Add any other context, diagrams or screenshots about the feature request here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f96529c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,35 @@ +--- +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: daily + day: friday + time: "05:00" + timezone: "Australia/Perth" + commit-message: + prefix: "dependencies(github-actions): " + + - package-ecosystem: nuget + directory: "/" + groups: + microsoft: + patterns: + - Microsoft* + update-types: ["minor", "patch"] + testing: + patterns: + - AutoFixture* + - coverlet* + - FluentAssertions* + - GitHubActionsTestLogger + - xunit* + update-types: ["minor", "patch"] + schedule: + interval: weekly + day: friday + time: "05:00" + timezone: "Australia/Perth" + commit-message: + prefix: "dependencies(nuget): " diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b665c2c --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +### :spiral_notepad: Description + + + +### :white_check_mark: Checklist + + + +- [ ] Linked to any related issues in the PR description +- [ ] Added or updated documentation +- [ ] Tests for new functionality and regression tests for bug fixes +- [ ] Breaking changes were made diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..2e67fb6 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,61 @@ +name: build-and-test + +on: + pull_request: + push: + branches: + - main + - releases/* + paths: + - "src/**" + +permissions: + contents: read + +jobs: + build-and-test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + + permissions: + checks: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Install .NET Core + uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1 + with: + dotnet-version: 8.0 + + - name: Restore packages + run: dotnet restore + + - name: Build + run: dotnet build --no-restore --configuration Release + + - name: Run unit tests + run: > + dotnet test + --no-restore + --configuration Release + --logger "GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true;annotations.titleFormat=@test;annotations.messageFormat=@error\n@trace" + /p:CollectCoverage=true + -- + RunConfiguration.CollectSourceInformation=true + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + directory: ./reports/coverage/ + fail_ci_if_error: false + flags: unittests + slug: saan800/saansoft-correlationId diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..556f3c3 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,88 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + schedule: + - cron: '36 3 * * 1' + +permissions: + contents: read + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners + # Consider using larger runners for possible analysis time improvements. + runs-on: ${{ matrix.os }} + timeout-minutes: ${{ matrix.timeout-minutes }} + permissions: + # required for all workflows + security-events: write + + strategy: + fail-fast: false + matrix: + include: + - language: csharp + build-mode: autobuild + os: ubuntu-latest + timeout-minutes: 360 + + steps: + - name: Harden Runner + uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..e919c6c --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,56 @@ +name: dependabot-auto-approve-and-merge + +on: pull_request + +permissions: + contents: read + pull-requests: read + +jobs: + dependabot-auto-approve-and-merge: + runs-on: ubuntu-latest + + permissions: + contents: write + pull-requests: write + + # Checking the author and fork will prevent your Action run failing on non-Dependabot PRs + if: | + github.event.repository.fork == false && + github.event.pull_request.user.login == 'dependabot[bot]' + steps: + - name: Harden Runner + uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + with: + egress-policy: audit + + - name: Checkout code + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Dependabot metadata + uses: dependabot/fetch-metadata@dbb049abf0d677abbd7f7eee0375145b417fdd34 # v2.2.0 + id: dependabot-metadata + + - name: Check for merge conflicts + uses: sv-tools/block-merge-conflicts@2fb5852d0efb87ecceed7a3d24ad657dccf47a64 # v1.1.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Approve a PR if not already approved + if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} + run: | + gh pr checkout "$PR_URL" # sets the upstream metadata for `gh pr status` + if [ "$(gh pr status --json reviewDecision -q .currentBranch.reviewDecision)" != "APPROVED" ]; + then gh pr review --approve "$PR_URL" && gh pr merge --auto --squash "${PR_URL}" + else echo "PR already approved, skipping additional approvals to minimize emails/notification noise."; + fi + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge + if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} + run: gh pr merge --auto --squash "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/dependabot-validate.yml b/.github/workflows/dependabot-validate.yml new file mode 100644 index 0000000..b97898d --- /dev/null +++ b/.github/workflows/dependabot-validate.yml @@ -0,0 +1,27 @@ +name: dependabot-validate + +on: + pull_request: + paths: + - ".github/dependabot.yml" + - ".github/workflows/dependabot-validate.yml" + +permissions: + contents: read + +jobs: + dependabot-validate: + runs-on: ubuntu-latest + steps: + + - name: Harden Runner + uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + with: + egress-policy: audit + + - name: Checkout code + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: validate dependabot + uses: marocchino/validate-dependabot@d8ae5c0d03dd75fbd0ad5f8ab4ba8101ebbd4b37 # v3.0.0 + diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..6fe9acc --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,21 @@ +name: dependency-review + +on: pull_request + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + with: + egress-policy: audit + + - name: Checkout code + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Run dependency review + uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..c2db7ab --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,81 @@ +name: lint + +on: pull_request + +permissions: + contents: read + +jobs: + super-linter: + runs-on: ubuntu-latest + + permissions: + packages: read + # To report GitHub Actions status checks + statuses: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + with: + egress-policy: audit + + - name: Checkout code + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + # super-linter needs the full git history to get the + # list of files that changed across commits + fetch-depth: 0 + + - name: Get changed c# files + id: changed-csharp-files + uses: tj-actions/changed-files@6b2903bdce6310cfbddd87c418f253cf29b2dec9 # v44.5.6 + with: + # Avoid using single or double quotes for multiline patterns + files: | + **.cs + + - name: Setup linting config + id: config + env: + hasChangedCSharpFiles: ${{ steps.changed-csharp-files.outputs.any_changed == 'true' }} + run: | + if [ "${hasChangedCSharpFiles}" == "true" ]; then + echo "VALIDATE_ALL_CODEBASE=false" >> "$GITHUB_OUTPUT" + else + echo "VALIDATE_ALL_CODEBASE=true" >> "$GITHUB_OUTPUT" + fi + + - name: Run super-linter + uses: super-linter/super-linter@3fe03abab2eafb293ace16d4a3b07aeabcb3f1a0 # v6.7.0 + env: + # To report GitHub Actions status checks + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FILTER_REGEX_EXCLUDE: (./\.editorconfig|\.idea/*|\.vscode/*) + IGNORE_GENERATED_FILES: true + # false -> only checks changed files + VALIDATE_ALL_CODEBASE: ${{ steps.config.outputs.VALIDATE_ALL_CODEBASE == 'true'}} + VALIDATE_CSHARP: true + VALIDATE_GITHUB_ACTIONS: true + VALIDATE_YAML: true + + spellcheck: + permissions: + contents: read # for streetsidesoftware/cspell-action to fetch files for commit + pull-requests: read # for streetsidesoftware/cspell-action to fetch commits for PR + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + with: + egress-policy: audit + + - name: Checkout code + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Spellcheck + uses: streetsidesoftware/cspell-action@245201e3f58019204d99920deeb78aade6724230 # v6.6.0 + with: + config: .cspell.json + check_dot_files: false + incremental_files_only: true diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml new file mode 100644 index 0000000..8a625dc --- /dev/null +++ b/.github/workflows/ossf-scorecard.yml @@ -0,0 +1,78 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '39 19 * * 6' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: Harden Runner + uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + with: + egress-policy: audit + + - name: "Checkout code" + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard (optional). + # Commenting out will disable upload of results to your repo's Code Scanning dashboard + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0821451 --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +# ********************************************** # +# ******************** IDEs ******************** # +# ********************************************** # + +############### Visual Studio Code ############### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +################## Visual Studio ################# +# For more VS gitignore rules: https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +.vs/ + +*.DotSettings +*.suo +*.user +*.userosscache +*.sln.docstates + +################# JetBrains Rider ################ +# For more Rider gitignore rules: https://github.com/JetBrains/resharper-rider-samples/blob/master/.gitignore +#.idea/ + +################## Visual Studio ################# +.mono/ + + +# ********************************************** # +# ******************** CODE ******************** # +# ********************************************** # + +# compiled / generated output +dist +tmp +/out-tsc + +# dependencies +node_modules +packages + +# .NET +bin +obj + + +# LOGS, TESTS AND COVERAGE + +/coverage +/reports +npm-debug.log +yarn-error.log +testem.log +bundle-stats.html diff --git a/.idea/.idea.SaanSoft.CorrelationId/.idea/.gitignore b/.idea/.idea.SaanSoft.CorrelationId/.idea/.gitignore new file mode 100644 index 0000000..731c243 --- /dev/null +++ b/.idea/.idea.SaanSoft.CorrelationId/.idea/.gitignore @@ -0,0 +1,14 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/projectSettingsUpdater.xml +/modules.xml +/.idea.SaanSoft.CorrelationId.iml +/contentModel.xml +# Editor-based HTTP Client requests +/httpRequests/ +/dictionaries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.SaanSoft.CorrelationId/.idea/.name b/.idea/.idea.SaanSoft.CorrelationId/.idea/.name new file mode 100644 index 0000000..551e836 --- /dev/null +++ b/.idea/.idea.SaanSoft.CorrelationId/.idea/.name @@ -0,0 +1 @@ +SaanSoft.CorrelationId \ No newline at end of file diff --git a/.idea/.idea.SaanSoft.CorrelationId/.idea/indexLayout.xml b/.idea/.idea.SaanSoft.CorrelationId/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.SaanSoft.CorrelationId/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.SaanSoft.CorrelationId/.idea/vcs.xml b/.idea/.idea.SaanSoft.CorrelationId/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/.idea.SaanSoft.CorrelationId/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..cdb4074 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "EditorConfig.EditorConfig", + "ms-dotnettools.csdevkit", + "streetsidesoftware.code-spell-checker", + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6af2d4c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.formatOnPaste": true, + "editor.formatOnSave": true, +} \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..1636f5e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behaviour that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behaviour include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behaviour and will take appropriate and fair corrective action in +response to any behaviour that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behaviour may be +reported to the community leaders responsible for enforcement via raising a +[GitHub issue](https://github.com/saan800/saansoft-correlationid/issues/new/choose). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behaviour deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behaviour was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behaviour. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behaviour. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behaviour, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..fea0994 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# Contributing to SaanSoft.CorrelationId + +## Recommended Git Setup + +### Git Hooks + +All PRs will be verified against linting scripts to ensure styling consistency. +For a faster feedback cycle, we recommend configuring git hooks. + +Run the following to enable git hooks for this repository. + +```shell +git config core.hooksPath .githooks +``` + +### Signed commits + +So we can attribute changes to the people involved we like to have signed commits + +```shell +git config user.name "John Doe" +git config user.email johndoe@example.com + +# Or to configure globally for all repositories +git config --global user.name "John Doe" +git config --global user.email johndoe@example.com +``` + +Then use the `-s` or `--signoff` flag when committing. + + +## Spelling + +SaanSoft.CorrelationId uses the `en-GB` dictionary for spell checking. + +The `dictionary.dic` file at the root level of the repository contains project specific words. + +### VS Code + +Add the [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) plugin and enable it. + +The `.cspell.json` file in the root directory is configured to use and add any unknown words to the dictionary. + +The `en-GB` dictionary comes packaged with the plugin, so no further setup is required. + +### JetBrains Rider + +#### Configure project to use `dictionary.dic + +Open the project then go to: + +* `Settings` > `Editor` > `Spelling` +* Check `Use single dictionary for saving words` and select `application-level` +* Under `Custom dictionaries (plain text word lists, hunspell)` + * Click the `+` button + * In the root directory of the repository, select `dictionary.dic` + * Save the changes + + +### Visual Studio + +Read more [here](https://learn.microsoft.com/en-us/visualstudio/ide/text-spell-checker) + +## Coding guidelines + +A consistent coding style is included via [EditorConfig](https://editorconfig.org/) with the file [.editorconfig](./.editorconfig) at the root of the repo. Depending on your editor of choice, it will either support it out of the box or you can [download a plugin](https://editorconfig.org/#download) for the config to be applied. + diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..ca9f9d3 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,20 @@ + + + enable + default + true + enable + + + true + true + CS1591 + + + true + true + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000..d8a3bf9 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,9 @@ + + + true + $([System.IO.Path]::Combine($(MSBuildProjectDirectory), "../../", 'reports', 'coverage'))/ + opencover,json + $(CoverletOutput)/coverage.json + [SaanSoft.Tests.*]*,[xunit.*]* + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..5f689d3 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/README.md b/README.md index 2311a51..5ff7e57 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ -# saansoft-correlationid -Generate correlation Ids +# SaanSoft.CorrelationId + +In a distributed system it can be a challenge to trace HTTP requests and messages through multiple microservices. + +A `CorrelationId` bundles each logical transaction as it moves through multiple processors. + +With this system, your client's requests are collected under one value for easier tracking and troubleshooting. + +Depending on the `ICorrelationIdProvider` configured you can extract the value from HTTP request headers or OpenTelemetry tracing for greater ease of tracking the transaction, or simply default to a generated GUID string. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..a6be393 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Reporting Security Issues + +To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/saan800/saansoft-correlationid/security/advisories/new) tab. + +More information can be found in the [GitHub Documentation](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) diff --git a/SaanSoft.CorrelationId.sln b/SaanSoft.CorrelationId.sln new file mode 100644 index 0000000..2e84f7e --- /dev/null +++ b/SaanSoft.CorrelationId.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_solution", "_solution", "{63401EBF-A03B-47CD-B5C2-99E334B312E1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaanSoft.CorrelationId", "src\SaanSoft.CorrelationId\SaanSoft.CorrelationId.csproj", "{82101F7B-641B-4F7B-9446-57DDFA66A694}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A1F9D39E-7FAB-4D65-8DE7-C29511D4C024}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaanSoft.Tests.CorrelationId", "test\SaanSoft.Tests.CorrelationId\SaanSoft.Tests.CorrelationId.csproj", "{31889625-3724-44C6-B426-FF0C031C2FC7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaanSoft.Tests.CorrelationId.Common", "test\SaanSoft.Tests.CorrelationId.Common\SaanSoft.Tests.CorrelationId.Common.csproj", "{ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {82101F7B-641B-4F7B-9446-57DDFA66A694}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {82101F7B-641B-4F7B-9446-57DDFA66A694}.Debug|Any CPU.Build.0 = Debug|Any CPU + {82101F7B-641B-4F7B-9446-57DDFA66A694}.Release|Any CPU.ActiveCfg = Release|Any CPU + {82101F7B-641B-4F7B-9446-57DDFA66A694}.Release|Any CPU.Build.0 = Release|Any CPU + {31889625-3724-44C6-B426-FF0C031C2FC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31889625-3724-44C6-B426-FF0C031C2FC7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31889625-3724-44C6-B426-FF0C031C2FC7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31889625-3724-44C6-B426-FF0C031C2FC7}.Release|Any CPU.Build.0 = Release|Any CPU + {ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {31889625-3724-44C6-B426-FF0C031C2FC7} = {A1F9D39E-7FAB-4D65-8DE7-C29511D4C024} + {ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B} = {A1F9D39E-7FAB-4D65-8DE7-C29511D4C024} + EndGlobalSection +EndGlobal diff --git a/dictionary.dic b/dictionary.dic new file mode 100644 index 0000000..dd4df45 --- /dev/null +++ b/dictionary.dic @@ -0,0 +1,12 @@ +abbrev +bson +codecov +color +nojournal +opencover +Saan +SaanSoft +Specflow +signoff +unittests +Xunit diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..38ac8e7 --- /dev/null +++ b/nuget.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/scripts/colours.sh b/scripts/colours.sh new file mode 100755 index 0000000..92aeb6b --- /dev/null +++ b/scripts/colours.sh @@ -0,0 +1,12 @@ +# More colours in top answer on +# - https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux +# Add colours to echo console statements. +# Ensure you reset console colours after using them with ${NC} +# > echo ${BLUE}Something is happening...${NC} +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +PURPLE='\033[0;35m' +CYAN='\033[0;36m' +NC='\033[0m' # No Colour diff --git a/scripts/lint-dotnet.sh b/scripts/lint-dotnet.sh new file mode 100755 index 0000000..4bdd381 --- /dev/null +++ b/scripts/lint-dotnet.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +source "$ROOT_DIR/scripts/colours.sh" +echo "${YELLOW}Running lint-dotnet...${NC}" + +# Check if the correct number of arguments are provided +if [ $# -lt 1 ]; then + echo "${RED}Usage: $0 [options]${NC}" + echo " - options: see ${PURPLE}dotnet format --help${NC} for details" + echo " eg ${PURPLE}--verify-no-changes${NC} for verification in CI pipeline" + exit 1 +fi + +# Assign arguments to variables +files="$1" +staged_cs_files=$(echo "$files" | grep -E ".cs$") +# Get additional arguments if provided +if [ $# -gt 1 ]; then + shift # Shift the arguments to remove the first one + additional_args="$@" + echo "Additional arguments: $additional_args" +fi + +if [ -n "$staged_cs_files" ]; then + dotnet format --verbosity normal --no-restore --include $staged_cs_files $additional_args +fi diff --git a/scripts/validate-branch-name.sh b/scripts/validate-branch-name.sh new file mode 100755 index 0000000..f8985c0 --- /dev/null +++ b/scripts/validate-branch-name.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# A simple regex linter for branch naming strategy +# based off https://www.devwithimagination.com/2020/04/13/git-commit-hooks-for-branch-naming-using-husky/ + +source "$ROOT_DIR/scripts/colours.sh" +echo "${YELLOW}Running validate-branch-name...${NC}" + +local_branch_name="$(git rev-parse --abbrev-ref HEAD)" + +validTypes="bugfix|chore|docs|feature|hotfix" +valid_branch_regex="^(($validTypes)\/[a-zA-Z0-9_\-]{4,})$" + +if [[ ! $local_branch_name =~ $valid_branch_regex ]]; then + commaSeparatedList=$(echo "$validTypes" | tr '|' ', ') + + echo "${RED}Invalid branch name: $local_branch_name${NC}" + echo " Branch names must be in the format:" + echo " /" + echo " /-" + echo " Where:" + echo " - : ${commaSeparatedList}" + echo " - : (optional) usually used for a story or issue number" + echo " - : at least 4 characters long" + echo " Examples:" + echo " - feature/ABC-123_subject" + echo " - hotfix/something_is_wrong" + exit 1 +fi + +exit 0 diff --git a/scripts/validate-commit-message.sh b/scripts/validate-commit-message.sh new file mode 100755 index 0000000..0943997 --- /dev/null +++ b/scripts/validate-commit-message.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +# A simple regex commit linter example +# https://www.conventionalcommits.org/en/v1.0.0/ +# https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type + +source "$ROOT_DIR/scripts/colours.sh" +echo "${YELLOW}Running validate-commit-message...${NC}" + +# Check if the correct number of arguments are provided +if [ $# -ne 1 ]; then + echo "${RED}Usage: $0 ${NC}" + exit 1 +fi + +# Read the commit message from the arguments +commit_message="$1" + + +validTypes="breaking change|bugfix|build|ci|chore|docs|feat|feature|fix|hotfix|perf|refactor|revert|style|test" +system_msg_regex="^(Merge remote-tracking branch|Revert) .*" +valid_msg_regex="^($validTypes)(\(.{3,}\))?!?: .{4,}" + +if [[ ! $commit_message =~ $valid_msg_regex ]] && [[ ! $commit_message =~ $system_msg_regex ]]; then + commaSeparatedList=$(echo "$validTypes" | tr '|' ', ') + + echo "${RED}Invalid commit message: $commit_message${NC}" + echo " Commit messages must be in the Conventional Commits format:" + echo " : " + echo " (): " + echo " Where:" + echo " - : $commaSeparatedList" + echo " - : (optional) usually used for story or issue number" + echo " - : at least 4 characters long" + echo " Examples:" + echo " - feature(ABC-123): subject" + echo " - fix: subject" + echo " More info: https://www.conventionalcommits.org/en/v1.0.0/" + echo "" + echo " ${GREEN}HELP:${NC}" + echo " - If you are using zhs command line, and the '!' character for breaking changes. You may get the error:" + echo " > zsh: illegal modifier:" + echo " In your commit message use single quotes around the commit message." + + + exit 1 +fi + +exit 0 diff --git a/src/SaanSoft.CorrelationId/ICorrelationIdProvider.cs b/src/SaanSoft.CorrelationId/ICorrelationIdProvider.cs new file mode 100644 index 0000000..857d9a8 --- /dev/null +++ b/src/SaanSoft.CorrelationId/ICorrelationIdProvider.cs @@ -0,0 +1,16 @@ +namespace SaanSoft.CorrelationId; + +public interface ICorrelationIdProvider +{ + /// + /// Set the correlationId to a specific value + /// + /// + void Set(string correlationId); + + /// + /// Get the correlationId + /// + /// + string? Get(); +} diff --git a/src/SaanSoft.CorrelationId/SaanSoft.CorrelationId.csproj b/src/SaanSoft.CorrelationId/SaanSoft.CorrelationId.csproj new file mode 100644 index 0000000..c513b8a --- /dev/null +++ b/src/SaanSoft.CorrelationId/SaanSoft.CorrelationId.csproj @@ -0,0 +1,8 @@ + + + + netstandard2.1;net7.0;net8.0 + true + + + diff --git a/src/SaanSoft.CorrelationId/SimpleCorrelationIdProvider.cs b/src/SaanSoft.CorrelationId/SimpleCorrelationIdProvider.cs new file mode 100644 index 0000000..bd59d2b --- /dev/null +++ b/src/SaanSoft.CorrelationId/SimpleCorrelationIdProvider.cs @@ -0,0 +1,20 @@ +namespace SaanSoft.CorrelationId; + +/// +/// If no correlationId is set, it will default to `Guid.NewGuid().ToString()` to ensure a value is always provided. +/// +public class SimpleCorrelationIdProvider : ICorrelationIdProvider +{ + private string? _correlationId; + + public void Set(string correlationId) + { + if (!string.IsNullOrWhiteSpace(correlationId)) _correlationId = correlationId.Trim(); + } + + public string? Get() + { + if (string.IsNullOrWhiteSpace(_correlationId)) _correlationId = Guid.NewGuid().ToString(); + return _correlationId; + } +} diff --git a/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFakeDataAttribute.cs b/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFakeDataAttribute.cs new file mode 100644 index 0000000..620ad4d --- /dev/null +++ b/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFakeDataAttribute.cs @@ -0,0 +1,6 @@ +using AutoFixture.Xunit2; + +namespace SaanSoft.Tests.CorrelationId.Common.AutoFixture; + +[AttributeUsage(AttributeTargets.Method)] +public class AutoFakeDataAttribute() : AutoDataAttribute(AutoFixtureExtensions.Create); diff --git a/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFixtureExtensions.cs b/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFixtureExtensions.cs new file mode 100644 index 0000000..0fd80d1 --- /dev/null +++ b/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFixtureExtensions.cs @@ -0,0 +1,36 @@ +using AutoFixture; +using AutoFixture.AutoFakeItEasy; + +namespace SaanSoft.Tests.CorrelationId.Common.AutoFixture; + +public static class AutoFixtureExtensions +{ + public static Fixture Create() + { + var fixture = new Fixture(); + + fixture.Customize(new AutoFakeItEasyCustomization + { + ConfigureMembers = true, + GenerateDelegates = true + } + ); + + fixture.Register(A.Fake); + fixture.Freeze(); + + fixture.Register(() => + { + var httpMessageHandler = fixture.Freeze(); + return new HttpClient(httpMessageHandler) + { + // inject a default baseUri for httpClient, this is required for dependencies that expect + // HttpClientFactory to configure their defaults correctly, in that case even if you mock out the + // message handler things will fail when generating the target uri + BaseAddress = new Uri("http://localhost") + }; + }); + + return fixture; + } +} diff --git a/test/SaanSoft.Tests.CorrelationId.Common/FakeTest.cs b/test/SaanSoft.Tests.CorrelationId.Common/FakeTest.cs new file mode 100644 index 0000000..93f8377 --- /dev/null +++ b/test/SaanSoft.Tests.CorrelationId.Common/FakeTest.cs @@ -0,0 +1,13 @@ +namespace SaanSoft.Tests.CorrelationId.Common; + +/// +/// Here just to make this project have a test file, and ignore in coverage +/// +public class FakeTest +{ + [Fact] + public void Run() + { + Assert.True(true); + } +} diff --git a/test/SaanSoft.Tests.CorrelationId.Common/GlobalUsings.cs b/test/SaanSoft.Tests.CorrelationId.Common/GlobalUsings.cs new file mode 100644 index 0000000..257b210 --- /dev/null +++ b/test/SaanSoft.Tests.CorrelationId.Common/GlobalUsings.cs @@ -0,0 +1 @@ +global using FakeItEasy; diff --git a/test/SaanSoft.Tests.CorrelationId.Common/SaanSoft.Tests.CorrelationId.Common.csproj b/test/SaanSoft.Tests.CorrelationId.Common/SaanSoft.Tests.CorrelationId.Common.csproj new file mode 100644 index 0000000..c55b5c3 --- /dev/null +++ b/test/SaanSoft.Tests.CorrelationId.Common/SaanSoft.Tests.CorrelationId.Common.csproj @@ -0,0 +1,19 @@ + + + + net7.0;net8.0 + false + true + CS1591;CS8602;CS8625;CS8618 + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/test/SaanSoft.Tests.CorrelationId/GlobalUsings.cs b/test/SaanSoft.Tests.CorrelationId/GlobalUsings.cs new file mode 100644 index 0000000..9f9f398 --- /dev/null +++ b/test/SaanSoft.Tests.CorrelationId/GlobalUsings.cs @@ -0,0 +1,4 @@ +global using FakeItEasy; +global using FluentAssertions; +global using SaanSoft.CorrelationId; + diff --git a/test/SaanSoft.Tests.CorrelationId/SaanSoft.Tests.CorrelationId.csproj b/test/SaanSoft.Tests.CorrelationId/SaanSoft.Tests.CorrelationId.csproj new file mode 100644 index 0000000..9261795 --- /dev/null +++ b/test/SaanSoft.Tests.CorrelationId/SaanSoft.Tests.CorrelationId.csproj @@ -0,0 +1,23 @@ + + + net7.0;net8.0 + false + true + CS1591;CS8602;CS8625;CS8618 + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/test/SaanSoft.Tests.CorrelationId/SimpleCorrelationIdProviderTests.cs b/test/SaanSoft.Tests.CorrelationId/SimpleCorrelationIdProviderTests.cs new file mode 100644 index 0000000..51df717 --- /dev/null +++ b/test/SaanSoft.Tests.CorrelationId/SimpleCorrelationIdProviderTests.cs @@ -0,0 +1,27 @@ +using AutoFixture.Xunit2; + +namespace SaanSoft.Tests.CorrelationId; + +public class SimpleCorrelationIdProviderTests +{ + private readonly SimpleCorrelationIdProvider _provider = new(); + + [Fact] + public void Get_returns_non_empty_string() + { + var result = _provider.Get(); + + result.Should().NotBeNullOrWhiteSpace(); + result.Length.Should().Be(36); // length of a guid + } + + [Theory] + [InlineAutoData] + public void Set_and_Get_returns_given_value(string val) + { + _provider.Set(val); + + var result = _provider.Get(); + result.Should().Be(val); + } +}