From f3a4ad9c21c7b876bd2cc362021771ee54c98485 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Wed, 25 Dec 2024 22:29:20 -0500 Subject: [PATCH 01/10] ci: Fix a11y and other Action workflows (#1299) --- .github/workflows/deploy-package.yml | 116 ++++++++++++++--------- .github/workflows/deploy-website.yml | 24 ++--- .github/workflows/lint-pull-request.yml | 31 +++--- .github/workflows/lock-issues-pr.yml | 2 + .github/workflows/test-website-a11y.yml | 45 +++++---- .github/workflows/test-website-links.yml | 54 +++++------ docs/.pa11yci | 12 ++- docs/{GemFile => Gemfile} | 0 docs/{GemFile.lock => Gemfile.lock} | 30 +++--- docs/_config.yml | 2 +- src/Indicators.csproj | 1 - 11 files changed, 170 insertions(+), 147 deletions(-) rename docs/{GemFile => Gemfile} (100%) rename docs/{GemFile.lock => Gemfile.lock} (95%) diff --git a/.github/workflows/deploy-package.yml b/.github/workflows/deploy-package.yml index e8bcf4d7d..43dff6ee8 100644 --- a/.github/workflows/deploy-package.yml +++ b/.github/workflows/deploy-package.yml @@ -7,15 +7,20 @@ on: description: Deployment environment type: choice options: - - staging + - pkg.github.com - nuget.org - default: staging + default: pkg.github.com required: true preview: description: Append preview suffix type: boolean default: true required: true + dry_run: + description: 'Dry-run only (no deploy)' + type: boolean + default: true + required: true concurrency: group: ${{ inputs.environment }} @@ -34,6 +39,12 @@ jobs: with: fetch-depth: 0 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "9.x" + dotnet-quality: "ga" + - name: Setup GitVersion uses: gittools/actions/gitversion/setup@v1.2.0 with: @@ -51,21 +62,19 @@ jobs: - name: Compose version id: compose run: | - ver=${{ steps.gitversion.outputs.majorMinorPatch }}${{ inputs.preview && '-preview.' || '' }}${{ inputs.preview && steps.gitversion.outputs.preReleaseNumber || inputs.preview && github.run_number || '' }} - echo "version=$ver" >> "$GITHUB_OUTPUT" + # get base version + base="${{ steps.gitversion.outputs.majorMinorPatch }}" - - name: Update release notes URL - uses: jacobtomlinson/gha-find-replace@v3 - with: - find: "https://github.com/DaveSkender/Stock.Indicators/releases" - replace: "https://github.com/DaveSkender/Stock.Indicators/releases/tag/${{ steps.compose.outputs.version }}" - regex: false + # determine preview suffix + preview_tag="${{ inputs.preview && '-preview.' || '' }}" - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: "9.x" - dotnet-quality: "ga" + # determine preview number + preview_num="${{ inputs.preview && steps.gitversion.outputs.preReleaseNumber || '' }}" + + # combine all parts + ver="${base}${preview_tag}${preview_num}" + + echo "version=$ver" >> "$GITHUB_OUTPUT" - name: Build library run: > @@ -106,28 +115,18 @@ jobs: deploy: needs: build runs-on: ubuntu-latest + if: success() permissions: contents: write packages: write environment: - name: ${{ inputs.environment }} - url: "${{ vars.NUGET_DOWNLOAD_PREFIX }}${{ needs.build.outputs.version }}" + name: ${{ !inputs.dry_run && inputs.environment || '' }} + url: ${{ steps.package_info.outputs.url }} steps: - - name: Pre-flight summary - run: | - { - echo "| Parameter | Value |" - echo "| :-------------- | :--------------------------------- |" - echo "| Environment | ${{ inputs.environment }} |" - echo "| Publishing URL | ${{ vars.NUGET_PUBLISH_URL }} |" - echo "| Download URL | ${{ vars.NUGET_DOWNLOAD_PREFIX }} |" - echo "| Version ID | ${{ needs.build.outputs.version }} |" - } >> $GITHUB_STEP_SUMMARY - - name: Setup .NET uses: actions/setup-dotnet@v4 with: @@ -146,28 +145,59 @@ jobs: name: packages path: NuGet - - name: Publish to GitHub Packages (staging) - if: inputs.environment == 'staging' - run: | - dotnet nuget push NuGet/*.nupkg --source "https://nuget.pkg.github.com/DaveSkender/index.json" --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate - - - name: Publish to NuGet.org - if: inputs.environment == 'nuget.org' + - name: Compose package info + id: package_info run: | - dotnet new nugetconfig --force - nuget setApiKey ${{ secrets.NUGET_TOKEN }} -src nuget -ConfigFile nuget.config - nuget push NuGet/*.nupkg -src nuget -NonInteractive -ConfigFile nuget.config -Verbosity Detailed + PACKAGE_NAME=$(xmllint --xpath "//PropertyGroup/PackageId/text()" Directory.Build.props || xmllint --xpath "//PropertyGroup/PackageId/text()" **/*.csproj) + echo "pkg_name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT + if [[ "${{ inputs.environment }}" == "nuget.org" ]]; then + echo "pkg_url=https://www.nuget.org/packages/${PACKAGE_NAME}/${{ needs.build.outputs.version }}" >> $GITHUB_OUTPUT + else + echo "pkg_url=https://github.com/${{ github.repository }}/packages/nuget/${PACKAGE_NAME}/${{ needs.build.outputs.version }}" >> $GITHUB_OUTPUT + fi + + - name: Publish package + if: ${{ !inputs.dry_run }} + env: + API_KEY: ${{ inputs.environment == 'nuget.org' && secrets.NUGET_TOKEN || secrets.GITHUB_TOKEN }} + run: > + dotnet nuget push NuGet/*.nupkg + --source "${{ vars.NUGET_PUBLISH_URL }}" + --api-key "$API_KEY" + --skip-duplicate - name: Tag and draft release note uses: ncipollo/release-action@v1 - if: inputs.environment == 'nuget.org' + if: ${{ !inputs.dry_run && inputs.environment == 'nuget.org' }} with: body: | - Weโ€™ve released a new Stock Indicators for .NET NuGet package. - See [Skender.Stock.Indicators @ NuGet.org](${{ vars.NUGET_DOWNLOAD_PREFIX }}${{ needs.build.outputs.version }}) for more information. + ## Release ${{ needs.build.outputs.version }} + + ๐Ÿ“ฆ Package deployed to [${{ inputs.environment }}](${{ steps.package_info.outputs.pkg_url }}) + + ### Package Details + - **Name**: ${{ steps.package_info.outputs.pkg_name }} + - **Version**: ${{ needs.build.outputs.version }} + - **Preview**: ${{ inputs.preview && 'Yes' || 'No' }} generateReleaseNotes: true draft: true makeLatest: ${{ !inputs.preview }} prerelease: ${{ inputs.preview }} - tag: ${{ needs.build.outputs.version }} - commit: ${{ github.ref_name }} + tag: v${{ needs.build.outputs.version }} + commit: ${{ github.sha }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Deployment summary + if: always() + run: | + { + echo "## Package Deployment" + echo "| Parameter | Value |" + echo "|:------------|:------|" + echo "| Mode | ${{ inputs.dry_run && '๐Ÿ” DRY RUN' || '๐Ÿš€ DEPLOY' }} |" + echo "| Status | ${{ job.status == 'success' && 'โœ… Success' || 'โŒ Failed' }} |" + echo "| Environment | ${{ inputs.environment }} |" + echo "| Version | ${{ needs.build.outputs.version }} |" + echo "| Package | [${{ steps.package_info.outputs.pkg_name }}](${{ steps.package_info.outputs.pkg_url }}) |" + echo "| Preview | ${{ inputs.preview && 'โœ“' || 'โœ—' }} |" + } >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml index 58058c1a0..13e627773 100644 --- a/.github/workflows/deploy-website.yml +++ b/.github/workflows/deploy-website.yml @@ -12,10 +12,16 @@ jobs: deploy: name: Cloudflare Pages runs-on: ubuntu-latest + defaults: + run: + working-directory: docs + env: + BUNDLE_GEMFILE: ${{github.workspace}}/docs/Gemfile + JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} environment: name: stockindicators.dev - url: ${{ steps.deploy.outputs.deployment-alias-url }} + url: ${{ steps.deploy.outputs.pages-deployment-alias-url }} steps: - name: Checkout source @@ -24,9 +30,11 @@ jobs: - name: Setup Ruby uses: ruby/setup-ruby@v1 with: - working-directory: docs ruby-version: 3.3 + - name: Install dependencies + run: bundle install + - name: Define tag id: tag run: echo "version=$(date +'%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_OUTPUT @@ -38,28 +46,16 @@ jobs: replace: "${{ steps.tag.outputs.version }}" regex: false - - name: Install GEMs - working-directory: docs - env: - BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile - run: | - pwd - bundle install - - name: Build site (production) if: github.ref == 'refs/heads/main' - working-directory: docs env: JEKYLL_ENV: production - BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile run: bundle exec jekyll build - name: Build site (preview) if: github.ref != 'refs/heads/main' - working-directory: docs env: JEKYLL_ENV: preview - BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile run: bundle exec jekyll build - name: Publish to Cloudflare Pages diff --git a/.github/workflows/lint-pull-request.yml b/.github/workflows/lint-pull-request.yml index dd068cb0c..48eb9a37c 100644 --- a/.github/workflows/lint-pull-request.yml +++ b/.github/workflows/lint-pull-request.yml @@ -14,6 +14,7 @@ jobs: main: name: lint PR title runs-on: ubuntu-latest + steps: - uses: amannn/action-semantic-pull-request@v5 id: lint_pr_title @@ -29,9 +30,8 @@ jobs: # the default error message that is shown when the pattern doesn't match. # The variables `subject` and `title` can be used within the message. subjectPatternError: > - The subject "**{subject}**" found in the pull request title "*{title}*" - didn't match the configured pattern. Please ensure that the subject - starts with an uppercase character. + The subject "**{subject}**" in pull request "*{title}*" + needs to start with an uppercase character. # If the PR contains one of these newline-delimited labels, the # validation is skipped. If you want to rerun the validation when @@ -48,24 +48,21 @@ jobs: with: header: pr-title-lint-error message: | - ### Hey there and thank you for opening this pull request! ๐Ÿ‘‹๐Ÿผ - - It looks like your proposed **_Pull request title_** needs to be adjusted. - - >๐Ÿšฉ **Error** ยป ${{ steps.lint_pr_title.outputs.error_message }} + ### โš ๏ธ Pull Request title needs adjustment - #### Pull request title naming convention + Your PR title doesn't match our naming convention: `type: Subject` - Our PR title name taxonomy is `type: Subject`, where **type** is typically - *feat*, *fix*, or *chore*, and **subject** is a phrase (proper noun) that starts - with a capitalized letter. The *chore* type usually has a subject that starts - with an action verb like *Add* or *Update*. Examples: + > [!CAUTION] + > ${{ steps.lint_pr_title.outputs.error_message }} - - _feat: Admin portal login_ - - _fix: Divide by zero in X calculation_ - - _chore: Update packages_ - - _docs: Improve setup guidance_ + #### Valid examples + - `feat: Add new RSI indicator` + - `fix: Correct MACD calculation` + - `chore: Update documentation` + - `test: Add unit tests for EMA` + - `refactor: Optimize moving average logic` + See the [Conventional Commits specification](https://www.conventionalcommits.org) for more information. # Delete a previous comment when the issue has been resolved diff --git a/.github/workflows/lock-issues-pr.yml b/.github/workflows/lock-issues-pr.yml index ffb20549b..000093ade 100644 --- a/.github/workflows/lock-issues-pr.yml +++ b/.github/workflows/lock-issues-pr.yml @@ -16,6 +16,8 @@ concurrency: jobs: lock: runs-on: ubuntu-latest + timeout-minutes: 10 + steps: - uses: dessant/lock-threads@v5 with: diff --git a/.github/workflows/test-website-a11y.yml b/.github/workflows/test-website-a11y.yml index 510726e8a..ad193a5d5 100644 --- a/.github/workflows/test-website-a11y.yml +++ b/.github/workflows/test-website-a11y.yml @@ -4,19 +4,23 @@ on: pull_request: branches: ["*"] paths: - - 'docs/**' - - ".github/workflows/test-website-a11y.yml" + - docs/** + - .github/workflows/test-website-a11y.yml -env: - JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# Prevent multiple concurrent runs +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: test: - name: test runs-on: ubuntu-latest - + defaults: + run: + working-directory: docs env: - BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile + BUNDLE_GEMFILE: ${{github.workspace}}/docs/Gemfile + JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Checkout source @@ -25,33 +29,34 @@ jobs: - name: Setup Ruby uses: ruby/setup-ruby@v1 with: - working-directory: docs ruby-version: 3.3 - - name: Install GEMs - working-directory: docs + - name: Install dependencies run: | - pwd bundle install + npm install -g pa11y-ci + + - name: Use 'localhost' + uses: jacobtomlinson/gha-find-replace@v3 + with: + find: "https://dotnet.stockindicators.dev" + replace: "http://127.0.0.1:4000" + regex: false - name: Build site - working-directory: docs run: bundle exec jekyll build - name: Serve site - working-directory: docs - run: bundle exec jekyll serve --detach + run: bundle exec jekyll serve --port 4000 --detach - name: Show environment - working-directory: docs run: npx pa11y --environment - name: Test accessibility - working-directory: docs - run: | - npx pa11y-ci \ - --sitemap http://127.0.0.1:4000/sitemap.xml \ - --sitemap-exclude "/*.pdf" + run: > + pa11y-ci + --sitemap http://127.0.0.1:4000/sitemap.xml + --config ./.pa11yci - name: Kill site (failsafe) if: always() diff --git a/.github/workflows/test-website-links.yml b/.github/workflows/test-website-links.yml index c79ebcea2..0c40dbd16 100644 --- a/.github/workflows/test-website-links.yml +++ b/.github/workflows/test-website-links.yml @@ -4,16 +4,23 @@ on: pull_request: branches: ["*"] paths: - - 'docs/**' - - ".github/workflows/test-website-links.yml" + - docs/** + - .github/workflows/test-website-links.yml -env: - JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# Prevent multiple concurrent runs +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: test: - name: test runs-on: ubuntu-latest + defaults: + run: + working-directory: docs + env: + BUNDLE_GEMFILE: ${{github.workspace}}/docs/Gemfile + JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Checkout source @@ -22,21 +29,12 @@ jobs: - name: Setup Ruby uses: ruby/setup-ruby@v1 with: - working-directory: docs ruby-version: 3.3 - - name: Install GEMs - working-directory: docs - env: - BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile + - name: Install dependencies run: | - pwd bundle install - - - name: Setup HTML Proofer - run: | gem install html-proofer - htmlproofer --help - name: Replace "data-src" uses: jacobtomlinson/gha-find-replace@v3 @@ -45,7 +43,7 @@ jobs: replace: "src" regex: false - - name: Replace "dotnet.stockindicators.dev" + - name: Use 'localhost' uses: jacobtomlinson/gha-find-replace@v3 with: find: "https://dotnet.stockindicators.dev" @@ -53,27 +51,19 @@ jobs: regex: false - name: Build site - working-directory: docs - env: - BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile - run: bundle exec jekyll build --trace + run: bundle exec jekyll build - name: Serve site - working-directory: docs - env: - BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile - run: bundle exec jekyll serve --detach + run: bundle exec jekyll serve --port 4000 --detach # see help in setup step - name: Test for broken URLs - working-directory: docs - run: | - htmlproofer _site \ - --no-enforce-https \ - --no-check-external-hash \ - --ignore-status-codes "0,302,403,406,408,429,503,999" \ - --swap-urls "https\://dotnet.stockindicators.dev:http\://127.0.0.1:4000" \ - --ignore-urls "/fonts.gstatic.com/" + run: > + htmlproofer _site + --no-enforce-https + --no-check-external-hash + --ignore-status-codes "0,302,403,406,408,429,503,999" + --ignore-urls "/fonts.gstatic.com/" - name: Kill site (failsafe) if: always() diff --git a/docs/.pa11yci b/docs/.pa11yci index 026b63583..58454b2d5 100644 --- a/docs/.pa11yci +++ b/docs/.pa11yci @@ -1,8 +1,16 @@ { "defaults": { "concurrency": 1, + "timeout": 30000, + "wait": 1000, "standard": "WCAG2AA", - "runners": ["htmlcs"], - "hideElements": "#searchzone" + "hideElements": "#searchzone", + "port": 4000, + "chromeLaunchConfig": { + "args": [ + "--no-sandbox", + "--disable-setuid-sandbox" + ] + } } } diff --git a/docs/GemFile b/docs/Gemfile similarity index 100% rename from docs/GemFile rename to docs/Gemfile diff --git a/docs/GemFile.lock b/docs/Gemfile.lock similarity index 95% rename from docs/GemFile.lock rename to docs/Gemfile.lock index 800569ef8..e9ca2ba73 100644 --- a/docs/GemFile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (8.0.0) + activesupport (8.0.1) base64 benchmark (>= 0.3) bigdecimal @@ -18,7 +18,7 @@ GEM public_suffix (>= 2.0.2, < 7.0) base64 (0.2.0) benchmark (0.4.0) - bigdecimal (3.1.8) + bigdecimal (3.1.9) coffee-script (2.4.1) coffee-script-source execjs @@ -28,7 +28,7 @@ GEM concurrent-ruby (1.3.4) connection_pool (2.4.1) cssminify2 (2.0.1) - csv (3.3.0) + csv (3.3.2) dnsruby (1.72.3) base64 (~> 0.2.0) simpleidn (~> 0.2.1) @@ -40,14 +40,13 @@ GEM ffi (>= 1.15.0) eventmachine (1.2.7) execjs (2.10.0) - faraday (2.12.1) + faraday (2.12.2) faraday-net_http (>= 2.0, < 3.5) json logger faraday-net_http (3.4.0) net-http (>= 0.5.0) ffi (1.17.0-x64-mingw-ucrt) - ffi (1.17.0-x86_64-linux-gnu) forwardable-extended (2.6.0) gemoji (4.1.0) github-pages (232) @@ -227,7 +226,7 @@ GEM gemoji (>= 3, < 5) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) - json (2.8.2) + json (2.9.1) json-minify (0.0.3) json (> 0) kramdown (2.4.0) @@ -238,18 +237,16 @@ GEM listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - logger (1.6.1) + logger (1.6.4) mercenary (0.3.6) minima (2.5.1) jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.25.2) - net-http (0.5.0) + minitest (5.25.4) + net-http (0.6.0) uri - nokogiri (1.16.7-x64-mingw-ucrt) - racc (~> 1.4) - nokogiri (1.16.7-x86_64-linux) + nokogiri (1.18.0-x64-mingw-ucrt) racc (~> 1.4) octokit (4.25.1) faraday (>= 1, < 3) @@ -261,7 +258,7 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rexml (3.3.9) + rexml (3.4.0) rouge (3.30.0) rubyzip (2.3.2) safe_yaml (1.0.5) @@ -273,7 +270,7 @@ GEM sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - securerandom (0.3.2) + securerandom (0.4.1) simpleidn (0.2.3) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) @@ -286,11 +283,10 @@ GEM unicode-display_width (1.8.0) uri (1.0.2) wdm (0.2.0) - webrick (1.9.0) + webrick (1.9.1) PLATFORMS x64-mingw-ucrt - x86_64-linux DEPENDENCIES faraday @@ -305,4 +301,4 @@ DEPENDENCIES webrick BUNDLED WITH - 2.4.7 + 2.5.18 diff --git a/docs/_config.yml b/docs/_config.yml index 0f941f5fd..d5f0d975b 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -55,7 +55,7 @@ exclude: "docs/README.md", "examples/Backtest", "examples/ConsoleApp", - "GemFile", + "Gemfile", "node_modules", "vendor", ".devcontainer" diff --git a/src/Indicators.csproj b/src/Indicators.csproj index 907e5dad4..98ef2f687 100644 --- a/src/Indicators.csproj +++ b/src/Indicators.csproj @@ -34,7 +34,6 @@ Alligator;Gator;Fractal;Chaos;Choppiness;Endpoint;WMA;ZigZag;.NET;C#;Library;Package; README.md - https://github.com/DaveSkender/Stock.Indicators/releases Apache-2.0 icon.png From 5beb677068098bbc7101b1299cf30632de4ec0dc Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Thu, 26 Dec 2024 01:59:42 -0500 Subject: [PATCH 02/10] ci: Add `libxml2-utils` to packager (#1300) --- .github/workflows/deploy-package.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/deploy-package.yml b/.github/workflows/deploy-package.yml index 43dff6ee8..4b684e016 100644 --- a/.github/workflows/deploy-package.yml +++ b/.github/workflows/deploy-package.yml @@ -139,6 +139,9 @@ jobs: nuget-api-key: ${{ secrets.NUGET_TOKEN }} nuget-version: '6.x' + - name: Install dependencies + run: sudo apt-get update && sudo apt-get install -y libxml2-utils + - name: Download package uses: actions/download-artifact@v4 with: From 933e018694a13b1ca81768947f0273606d8b54df Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Thu, 26 Dec 2024 02:41:02 -0500 Subject: [PATCH 03/10] ci: Fix XML parser (#1301) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .github/workflows/deploy-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-package.yml b/.github/workflows/deploy-package.yml index 4b684e016..fc7401080 100644 --- a/.github/workflows/deploy-package.yml +++ b/.github/workflows/deploy-package.yml @@ -151,7 +151,7 @@ jobs: - name: Compose package info id: package_info run: | - PACKAGE_NAME=$(xmllint --xpath "//PropertyGroup/PackageId/text()" Directory.Build.props || xmllint --xpath "//PropertyGroup/PackageId/text()" **/*.csproj) + PACKAGE_NAME=$(xmllint --xpath "//PropertyGroup/PackageId/text()" src/Indicators.csproj) echo "pkg_name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT if [[ "${{ inputs.environment }}" == "nuget.org" ]]; then echo "pkg_url=https://www.nuget.org/packages/${PACKAGE_NAME}/${{ needs.build.outputs.version }}" >> $GITHUB_OUTPUT From 73fd1a788382cf498483f751dd719d34f8e04878 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Thu, 26 Dec 2024 03:07:02 -0500 Subject: [PATCH 04/10] ci: Fix XML parser (#1302) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .github/workflows/deploy-package.yml | 47 +++++++++++++++------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/.github/workflows/deploy-package.yml b/.github/workflows/deploy-package.yml index fc7401080..31435929f 100644 --- a/.github/workflows/deploy-package.yml +++ b/.github/workflows/deploy-package.yml @@ -30,7 +30,9 @@ jobs: build: runs-on: ubuntu-latest outputs: - version: ${{ steps.compose.outputs.version }} + version: ${{ steps.version_info.outputs.version }} + pkg_url: ${{ steps.package_info.outputs.pkg_url }} + pkg_name: ${{ steps.package_info.outputs.pkg_name }} steps: @@ -50,6 +52,9 @@ jobs: with: versionSpec: "5.x" preferLatestVersion: true + + - name: Install dependencies + run: sudo apt-get update && sudo apt-get install -y libxml2-utils - name: Determine version id: gitversion @@ -60,7 +65,7 @@ jobs: configFilePath: src/gitversion.yml - name: Compose version - id: compose + id: version_info run: | # get base version base="${{ steps.gitversion.outputs.majorMinorPatch }}" @@ -76,11 +81,22 @@ jobs: echo "version=$ver" >> "$GITHUB_OUTPUT" + - name: Compose package info + id: package_info + run: | + PACKAGE_NAME=$(xmllint --xpath "//PropertyGroup/PackageId/text()" src/Indicators.csproj) + echo "pkg_name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT + if [[ "${{ inputs.environment }}" == "nuget.org" ]]; then + echo "pkg_url=https://www.nuget.org/packages/${PACKAGE_NAME}/${{ steps.version_info.outputs.version }}" >> $GITHUB_OUTPUT + else + echo "pkg_url=https://github.com/${{ github.repository }}/packages/nuget/${PACKAGE_NAME}/${{ steps.version_info.outputs.version }}" >> $GITHUB_OUTPUT + fi + - name: Build library run: > dotnet build src/Indicators.csproj --configuration Release - --property:Version=${{ steps.compose.outputs.version }} + --property:Version=${{ steps.version_info.outputs.version }} --property:ContinuousIntegrationBuild=true -warnAsError @@ -91,7 +107,7 @@ jobs: --no-build --include-symbols --output NuGet - -p:PackageVersion=${{ steps.compose.outputs.version }} + -p:PackageVersion=${{ steps.version_info.outputs.version }} - name: Save package uses: actions/upload-artifact@v4 @@ -109,7 +125,7 @@ jobs: echo "| Minor | ${{ steps.gitversion.outputs.minor }} |" echo "| Patch | ${{ steps.gitversion.outputs.patch }} |" echo "| Base | ${{ steps.gitversion.outputs.majorMinorPatch }} |" - echo "| Composed | ${{ steps.compose.outputs.version }} |" + echo "| Composed | ${{ steps.version_info.outputs.version }} |" } >> $GITHUB_STEP_SUMMARY deploy: @@ -123,7 +139,7 @@ jobs: environment: name: ${{ !inputs.dry_run && inputs.environment || '' }} - url: ${{ steps.package_info.outputs.url }} + url: ${{ needs.build.outputs.pkg_url }} steps: @@ -139,8 +155,6 @@ jobs: nuget-api-key: ${{ secrets.NUGET_TOKEN }} nuget-version: '6.x' - - name: Install dependencies - run: sudo apt-get update && sudo apt-get install -y libxml2-utils - name: Download package uses: actions/download-artifact@v4 @@ -148,17 +162,6 @@ jobs: name: packages path: NuGet - - name: Compose package info - id: package_info - run: | - PACKAGE_NAME=$(xmllint --xpath "//PropertyGroup/PackageId/text()" src/Indicators.csproj) - echo "pkg_name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT - if [[ "${{ inputs.environment }}" == "nuget.org" ]]; then - echo "pkg_url=https://www.nuget.org/packages/${PACKAGE_NAME}/${{ needs.build.outputs.version }}" >> $GITHUB_OUTPUT - else - echo "pkg_url=https://github.com/${{ github.repository }}/packages/nuget/${PACKAGE_NAME}/${{ needs.build.outputs.version }}" >> $GITHUB_OUTPUT - fi - - name: Publish package if: ${{ !inputs.dry_run }} env: @@ -176,10 +179,10 @@ jobs: body: | ## Release ${{ needs.build.outputs.version }} - ๐Ÿ“ฆ Package deployed to [${{ inputs.environment }}](${{ steps.package_info.outputs.pkg_url }}) + ๐Ÿ“ฆ Package deployed to [${{ inputs.environment }}](${{ needs.build.outputs.pkg_url }}) ### Package Details - - **Name**: ${{ steps.package_info.outputs.pkg_name }} + - **Name**: ${{ needs.build.outputs.pkg_name }} - **Version**: ${{ needs.build.outputs.version }} - **Preview**: ${{ inputs.preview && 'Yes' || 'No' }} generateReleaseNotes: true @@ -201,6 +204,6 @@ jobs: echo "| Status | ${{ job.status == 'success' && 'โœ… Success' || 'โŒ Failed' }} |" echo "| Environment | ${{ inputs.environment }} |" echo "| Version | ${{ needs.build.outputs.version }} |" - echo "| Package | [${{ steps.package_info.outputs.pkg_name }}](${{ steps.package_info.outputs.pkg_url }}) |" + echo "| Package | [${{ needs.build.outputs.pkg_name }}](${{ needs.build.outputs.pkg_url }}) |" echo "| Preview | ${{ inputs.preview && 'โœ“' || 'โœ—' }} |" } >> $GITHUB_STEP_SUMMARY From db1128d8df7b688c16207c333557ca2aa18f071e Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sat, 4 Jan 2025 00:43:21 -0500 Subject: [PATCH 05/10] chore: Simplify PR lint trigger (#1309) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .github/workflows/lint-pull-request.yml | 63 +++++++++++++++---------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/.github/workflows/lint-pull-request.yml b/.github/workflows/lint-pull-request.yml index 48eb9a37c..4e9507808 100644 --- a/.github/workflows/lint-pull-request.yml +++ b/.github/workflows/lint-pull-request.yml @@ -1,11 +1,11 @@ name: Pull request on: - pull_request_target: + pull_request: types: - opened - edited - - synchronize + - unlabeled permissions: pull-requests: write @@ -21,49 +21,60 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - - # Configure additional validation for the subject based on a regex. - # We enforce that the subject starts with an uppercase character. subjectPattern: ^([A-Z]).+$ - - # If `subjectPattern` is configured, you can use this property to override - # the default error message that is shown when the pattern doesn't match. - # The variables `subject` and `title` can be used within the message. subjectPatternError: > - The subject "**{subject}**" in pull request "*{title}*" - needs to start with an uppercase character. - - # If the PR contains one of these newline-delimited labels, the - # validation is skipped. If you want to rerun the validation when - # labels change, you might want to use the `labeled` and `unlabeled` - # event triggers in your workflow. + The subject "**{subject}**" must start with an uppercase character. + Example: "Add feature" instead of "add feature" ignoreLabels: | bot dependencies - uses: marocchino/sticky-pull-request-comment@v2 - # When the previous steps fails, the workflow would stop. By adding this - # condition you can continue the execution with the populated error message. if: always() && (steps.lint_pr_title.outputs.error_message != null) with: header: pr-title-lint-error message: | - ### โš ๏ธ Pull Request title needs adjustment + ### โš ๏ธ Invalid Pull Request title - Your PR title doesn't match our naming convention: `type: Subject` + Your PR title must follow the format: `type: Subject` where: + - `type` is one of the conventional commit types (in lowercase) + - `Subject` starts with an uppercase letter > [!CAUTION] > ${{ steps.lint_pr_title.outputs.error_message }} #### Valid examples - - `feat: Add new RSI indicator` - - `fix: Correct MACD calculation` - - `chore: Update documentation` - - `test: Add unit tests for EMA` - - `refactor: Optimize moving average logic` + - `feat: Add API endpoint for market data` + - `fix: Resolve WebSocket connection issues` + - `chore: Update NuGet dependencies` + +
+ see more valid examples + + #### Features & fixes + - `feat: Add API endpoint for market data` + - `fix: Resolve WebSocket connection issues` + + #### Code quality + - `style: Format trading strategy classes` + - `refactor: Restructure trading engine components` + - `perf: Optimize trade order execution flow` + + #### Documentation & testing + - `docs: Update API documentation` + - `test: Add unit tests for sign-in flow` + + #### Infrastructure + - `build: Update .NET SDK version to 8.0` + - `ci: Add workflow for performance testing` + - `chore: Update NuGet dependencies` + + #### Other + - `revert: Remove faulty market data provider` - See the [Conventional Commits specification](https://www.conventionalcommits.org) for more information. + See [Conventional Commits](https://www.conventionalcommits.org) for more details. +
# Delete a previous comment when the issue has been resolved - if: ${{ steps.lint_pr_title.outputs.error_message == null }} From afb43005857e407d6ea7b6a734d5310e05488dad Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 5 Jan 2025 16:11:27 -0500 Subject: [PATCH 06/10] chore: Use `auto/lf` line endings (#1313) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 462913965..9a5d6f371 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ # Normalize line endings. -* text=lf +* text=auto eol=lf From 49d6ddddd98f7f9cadf3af5027bdd22cdfb46f6b Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 5 Jan 2025 17:38:45 -0500 Subject: [PATCH 07/10] revert: Restore .NET Standard 2.0 (#1312) We'd prematurely removed .NET Standard 2.0 We do plan on removing it for version 3 since it will limit use of newer SDK features with better overall performance. --- .github/workflows/test-indicators.yml | 32 +++++++++------------- .gitignore | 11 +++++--- docs/pages/home.md | 2 +- src/Indicators.csproj | 2 +- tests/indicators/_Initialize.cs | 39 +++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 26 deletions(-) diff --git a/.github/workflows/test-indicators.yml b/.github/workflows/test-indicators.yml index 65b447340..c44e5832c 100644 --- a/.github/workflows/test-indicators.yml +++ b/.github/workflows/test-indicators.yml @@ -1,11 +1,11 @@ -name: Indicators +name: Test Indicators on: push: branches: ["main"] - pull_request: branches: ["*"] + workflow_dispatch: jobs: test: @@ -20,18 +20,17 @@ jobs: strategy: matrix: os: [windows-latest, ubuntu-latest, macos-latest] - dotnet-version: ["9.x"] - # TODO: restore when runners have 9.x on all - # ["2.1.x", "6.x", "9.x"] + dotnet-version: ["6.0.x", "9.0.x"] env: # identifying primary configuration so only one reports coverage - IS_PRIMARY: ${{ matrix.os == 'ubuntu-latest' && matrix.dotnet-version == '9.x' }} + IS_PRIMARY: ${{ matrix.os == 'ubuntu-latest' && matrix.dotnet-version == '9.0.x' }} - # .NET SDK versions in the matrix that support `ga` quality spec - # versions before 5.x do not support it - SUPPORT_GA: ${{ contains(fromJson('["6.x", "9.x"]'), matrix.dotnet-version) }} + # Identifies the current target framework + TARGET_FRAMEWORK: > + ${{ matrix.dotnet-version == '6.0.x' && 'net6.0' || + matrix.dotnet-version == '9.0.x' && 'net9.0' }} steps: @@ -39,19 +38,11 @@ jobs: uses: actions/checkout@v4 - name: Setup .NET - id: dotnet-new uses: actions/setup-dotnet@v4 - if: env.SUPPORT_GA == 'true' with: - dotnet-version: ${{ matrix.dotnet-version }} + dotnet-version: "9.x" dotnet-quality: "ga" - - name: Setup .NET (older) - uses: actions/setup-dotnet@v4 - if: env.SUPPORT_GA == 'false' - with: - dotnet-version: ${{ matrix.dotnet-version }} - - name: Build library run: > dotnet build @@ -66,6 +57,7 @@ jobs: run: > dotnet test tests/indicators/Tests.Indicators.csproj --configuration Release + --property:TestFramework="${{ env.TARGET_FRAMEWORK }}" --no-build --verbosity normal --logger trx @@ -84,8 +76,8 @@ jobs: --logger trx --results-directory ./test-other - - name: Post test summary - uses: dorny/test-reporter@v1 + - name: Post test results + uses: dorny/test-reporter@v1.9.1 if: env.IS_PRIMARY == 'true' && always() with: name: Test results diff --git a/.gitignore b/.gitignore index 7a97cd5dc..b06c8feb4 100644 --- a/.gitignore +++ b/.gitignore @@ -133,6 +133,9 @@ _TeamCity* *.coverage *.coveragexml +# Visual Studio live unit testing +*.lutconfig + # NCrunch _NCrunch_* .*crunch*.local.xml @@ -218,7 +221,7 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -314,7 +317,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -323,11 +326,11 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ # Jekyll site _site/ # zip artifacts -.DS_Store \ No newline at end of file +.DS_Store diff --git a/docs/pages/home.md b/docs/pages/home.md index 7915db21e..ce548079c 100644 --- a/docs/pages/home.md +++ b/docs/pages/home.md @@ -70,7 +70,7 @@ See the [guide]({{site.baseurl}}/guide/#content) and the [full list of indicator Our [NuGet library](https://www.nuget.org/packages/Skender.Stock.Indicators) directly targets all current frameworks for peak performance, including the .NET Standard for older framework compatibility. - .NET 9.0, 8.0, 6.0 -- .NET Standard 2.1 +- .NET Standard 2.1, 2.0 The compiled library package is [Common Language Specification (CLS) compliant](https://docs.microsoft.com/en-us/dotnet/standard/common-type-system) and can be used in other programming languages, including Python and everything in the .NET universe. diff --git a/src/Indicators.csproj b/src/Indicators.csproj index 98ef2f687..5ae3ef76b 100644 --- a/src/Indicators.csproj +++ b/src/Indicators.csproj @@ -1,7 +1,7 @@ - net9.0;net8.0;net6.0;netstandard2.1 + net9.0;net8.0;net6.0;netstandard2.1;netstandard2.0 Dave Skender Stock Indicators for .NET diff --git a/tests/indicators/_Initialize.cs b/tests/indicators/_Initialize.cs index 3b09c9e0f..bdbe00b75 100644 --- a/tests/indicators/_Initialize.cs +++ b/tests/indicators/_Initialize.cs @@ -1,5 +1,7 @@ using System.Globalization; +using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.Versioning; // GLOBALS & INITIALIZATION OF TEST DATA @@ -8,6 +10,9 @@ [assembly: InternalsVisibleTo("Tests.Performance")] namespace Tests.Common; +/// +/// Base class for all test classes, providing common test data and utilities. +/// [TestClass] public abstract class TestBase { @@ -27,3 +32,37 @@ public abstract class TestBase internal static readonly IEnumerable zeroesQuotes = TestData.GetZeros(); internal static readonly IEnumerable<(DateTime, double)> tupleNanny = TestData.GetTupleNaN(); } + +/// +/// Test class for the startup of the test project. +/// +[TestClass] +public class Startup +{ + /// + /// Displays the assembly location, name, version, and target framework + /// as a sanity check for test runner targeting. + /// + [TestMethod] + [TestCategory("ShowMe")] + public void ShowFramework() + { + // Get the assembly of any type from your Indicators project + Assembly assembly = typeof(Indicator).Assembly; + + Console.WriteLine($"Assembly Location: {assembly.Location}"); + Console.WriteLine($"Assembly Name: {assembly.GetName().Name}"); + Console.WriteLine($"Assembly Version: {assembly.GetName().Version}"); + + // Get the target framework the assembly was built for + TargetFrameworkAttribute targetFrameworkAttribute = assembly + .GetCustomAttribute(); + + string frameworkName + = targetFrameworkAttribute?.FrameworkName ?? "Unknown"; + + Console.WriteLine($"Target Framework: {frameworkName}"); + + frameworkName.Should().NotBe("Unknown"); + } +} From 7029415d56e33827850d1e383122c771ec7f6ac0 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 5 Jan 2025 18:33:21 -0500 Subject: [PATCH 08/10] fix: Add language version (#1315) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- src/Indicators.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Indicators.csproj b/src/Indicators.csproj index 5ae3ef76b..12e488c58 100644 --- a/src/Indicators.csproj +++ b/src/Indicators.csproj @@ -2,6 +2,7 @@ net9.0;net8.0;net6.0;netstandard2.1;netstandard2.0 + 13.0 Dave Skender Stock Indicators for .NET From 27ce78b7531b53d1828a4600f2bd8ef2674d6b6e Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 5 Jan 2025 19:06:53 -0500 Subject: [PATCH 09/10] ci: Remove unneeded NuGet setup (#1316) --- .github/workflows/deploy-package.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy-package.yml b/.github/workflows/deploy-package.yml index 31435929f..0a754c26d 100644 --- a/.github/workflows/deploy-package.yml +++ b/.github/workflows/deploy-package.yml @@ -52,7 +52,7 @@ jobs: with: versionSpec: "5.x" preferLatestVersion: true - + - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y libxml2-utils @@ -89,7 +89,7 @@ jobs: if [[ "${{ inputs.environment }}" == "nuget.org" ]]; then echo "pkg_url=https://www.nuget.org/packages/${PACKAGE_NAME}/${{ steps.version_info.outputs.version }}" >> $GITHUB_OUTPUT else - echo "pkg_url=https://github.com/${{ github.repository }}/packages/nuget/${PACKAGE_NAME}/${{ steps.version_info.outputs.version }}" >> $GITHUB_OUTPUT + echo "pkg_url=https://github.com/${{ github.repository }}/pkgs/nuget/${PACKAGE_NAME}" >> $GITHUB_OUTPUT fi - name: Build library @@ -149,13 +149,6 @@ jobs: dotnet-version: "9.x" dotnet-quality: "ga" - - name: Setup NuGet - uses: nuget/setup-nuget@v2 - with: - nuget-api-key: ${{ secrets.NUGET_TOKEN }} - nuget-version: '6.x' - - - name: Download package uses: actions/download-artifact@v4 with: From d3593f59563d9cb43407dd5cda87ec559d84bac2 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 5 Jan 2025 19:44:55 -0500 Subject: [PATCH 10/10] ci: Remove custom CodeQL (#1317) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .github/workflows/test-codeql.yml | 52 ------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 .github/workflows/test-codeql.yml diff --git a/.github/workflows/test-codeql.yml b/.github/workflows/test-codeql.yml deleted file mode 100644 index 9a585cb6e..000000000 --- a/.github/workflows/test-codeql.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: CodeQL - -on: - push: - branches: ["main"] - - pull_request: - branches: ["*"] - - schedule: - - cron: '37 14 * * 3' - -jobs: - - analyze: - name: analyze - runs-on: 'ubuntu-latest' - - permissions: - security-events: write - packages: read - actions: read - contents: read - - steps: - - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: csharp - build-mode: manual - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 9.x - dotnet-quality: "ga" - - - name: Build library - run: > - dotnet build - --configuration Release - --property:ContinuousIntegrationBuild=true - -warnAsError - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:csharp"